blob: 263d6d337ef296a8abe446746a6d99c1ecfd89bc [file] [log] [blame]
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
* Copyright (C) 2014 Opera Software ASA. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "web/WebPluginContainerImpl.h"
#include "bindings/core/v8/ScriptController.h"
#include "bindings/core/v8/ScriptSourceCode.h"
#include "bindings/core/v8/V8BindingForCore.h"
#include "bindings/core/v8/V8Element.h"
#include "core/HTMLNames.h"
#include "core/clipboard/DataObject.h"
#include "core/clipboard/DataTransfer.h"
#include "core/dom/DocumentUserGestureToken.h"
#include "core/dom/ExecutionContext.h"
#include "core/dom/Fullscreen.h"
#include "core/events/DragEvent.h"
#include "core/events/EventQueue.h"
#include "core/events/GestureEvent.h"
#include "core/events/KeyboardEvent.h"
#include "core/events/MouseEvent.h"
#include "core/events/ProgressEvent.h"
#include "core/events/ResourceProgressEvent.h"
#include "core/events/TouchEvent.h"
#include "core/events/WheelEvent.h"
#include "core/exported/WebDataSourceImpl.h"
#include "core/exported/WebViewBase.h"
#include "core/frame/EventHandlerRegistry.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/WebLocalFrameBase.h"
#include "core/frame/csp/ContentSecurityPolicy.h"
#include "core/html/HTMLFormElement.h"
#include "core/html/HTMLPlugInElement.h"
#include "core/input/EventHandler.h"
#include "core/layout/HitTestResult.h"
#include "core/layout/LayoutBox.h"
#include "core/layout/LayoutView.h"
#include "core/layout/api/LayoutPartItem.h"
#include "core/layout/api/LayoutViewItem.h"
#include "core/loader/FrameLoadRequest.h"
#include "core/page/FocusController.h"
#include "core/page/Page.h"
#include "core/page/scrolling/ScrollingCoordinator.h"
#include "core/paint/LayoutObjectDrawingRecorder.h"
#include "core/paint/PaintLayer.h"
#include "platform/KeyboardCodes.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/UserGestureIndicator.h"
#include "platform/exported/WrappedResourceResponse.h"
#include "platform/geometry/LayoutRect.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/paint/CullRect.h"
#include "platform/graphics/paint/ForeignLayerDisplayItem.h"
#include "platform/scroll/ScrollAnimatorBase.h"
#include "platform/scroll/ScrollbarTheme.h"
#include "platform/wtf/Assertions.h"
#include "public/platform/Platform.h"
#include "public/platform/WebClipboard.h"
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebCursorInfo.h"
#include "public/platform/WebDragData.h"
#include "public/platform/WebExternalTextureLayer.h"
#include "public/platform/WebInputEvent.h"
#include "public/platform/WebRect.h"
#include "public/platform/WebString.h"
#include "public/platform/WebURL.h"
#include "public/platform/WebURLError.h"
#include "public/platform/WebURLRequest.h"
#include "public/web/WebDOMMessageEvent.h"
#include "public/web/WebDocument.h"
#include "public/web/WebElement.h"
#include "public/web/WebFrameClient.h"
#include "public/web/WebPlugin.h"
#include "public/web/WebPrintParams.h"
#include "public/web/WebPrintPresetOptions.h"
#include "public/web/WebViewClient.h"
#include "web/ChromeClientImpl.h"
#include "web/WebInputEventConversion.h"
namespace blink {
// Public methods --------------------------------------------------------------
void WebPluginContainerImpl::SetParent(FrameView* parent) {
DCHECK(!parent || !parent_);
if (!parent || !parent->IsVisible())
SetParentVisible(false);
parent_ = parent;
if (parent && parent->IsVisible())
SetParentVisible(true);
}
void WebPluginContainerImpl::UpdateAllLifecyclePhases() {
if (!web_plugin_)
return;
web_plugin_->UpdateAllLifecyclePhases();
}
void WebPluginContainerImpl::Paint(GraphicsContext& context,
const CullRect& cull_rect) const {
if (!parent_)
return;
// Don't paint anything if the plugin doesn't intersect.
if (!cull_rect.IntersectsCullRect(frame_rect_))
return;
if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() && web_layer_) {
// With Slimming Paint v2, composited plugins should have their layers
// inserted rather than invoking WebPlugin::paint.
RecordForeignLayer(context, *element_->GetLayoutObject(),
DisplayItem::kForeignLayerPlugin, web_layer_,
frame_rect_.Location(), frame_rect_.Size());
return;
}
if (LayoutObjectDrawingRecorder::UseCachedDrawingIfPossible(
context, *element_->GetLayoutObject(), DisplayItem::Type::kWebPlugin))
return;
LayoutObjectDrawingRecorder drawing_recorder(
context, *element_->GetLayoutObject(), DisplayItem::Type::kWebPlugin,
cull_rect.rect_);
context.Save();
// The plugin is positioned in the root frame's coordinates, so it needs to
// be painted in them too.
IntPoint origin = parent_->ContentsToRootFrame(IntPoint(0, 0));
context.Translate(static_cast<float>(-origin.X()),
static_cast<float>(-origin.Y()));
WebCanvas* canvas = context.Canvas();
IntRect window_rect = parent_->ContentsToRootFrame(cull_rect.rect_);
web_plugin_->Paint(canvas, window_rect);
context.Restore();
}
void WebPluginContainerImpl::InvalidateRect(const IntRect& rect) {
if (!parent_)
return;
LayoutBox* layout_object = ToLayoutBox(element_->GetLayoutObject());
if (!layout_object)
return;
IntRect dirty_rect = rect;
dirty_rect.Move(
(layout_object->BorderLeft() + layout_object->PaddingLeft()).ToInt(),
(layout_object->BorderTop() + layout_object->PaddingTop()).ToInt());
pending_invalidation_rect_.Unite(dirty_rect);
layout_object->SetMayNeedPaintInvalidation();
}
void WebPluginContainerImpl::SetFocused(bool focused, WebFocusType focus_type) {
web_plugin_->UpdateFocus(focused, focus_type);
}
bool WebPluginContainerImpl::IsErrorplaceholder() {
if (!web_plugin_)
return false;
return web_plugin_->IsErrorPlaceholder();
}
void WebPluginContainerImpl::Show() {
self_visible_ = true;
web_plugin_->UpdateVisibility(true);
}
void WebPluginContainerImpl::Hide() {
self_visible_ = false;
web_plugin_->UpdateVisibility(false);
}
void WebPluginContainerImpl::HandleEvent(Event* event) {
// The events we pass are defined at:
// http://devedge-temp.mozilla.org/library/manuals/2002/plugin/1.0/structures5.html#1000000
// Don't take the documentation as truth, however. There are many cases
// where mozilla behaves differently than the spec.
if (event->IsMouseEvent())
HandleMouseEvent(ToMouseEvent(event));
else if (event->IsWheelEvent())
HandleWheelEvent(ToWheelEvent(event));
else if (event->IsKeyboardEvent())
HandleKeyboardEvent(ToKeyboardEvent(event));
else if (event->IsTouchEvent())
HandleTouchEvent(ToTouchEvent(event));
else if (event->IsGestureEvent())
HandleGestureEvent(ToGestureEvent(event));
else if (event->IsDragEvent() && web_plugin_->CanProcessDrag())
HandleDragEvent(ToDragEvent(event));
// FIXME: it would be cleaner if FrameViewBase::handleEvent returned
// true/false and HTMLPluginElement called setDefaultHandled or
// defaultEventHandler.
if (!event->DefaultHandled())
element_->Node::DefaultEventHandler(event);
}
void WebPluginContainerImpl::FrameRectsChanged() {
ReportGeometry();
}
void WebPluginContainerImpl::GeometryMayHaveChanged() {
ReportGeometry();
}
void WebPluginContainerImpl::EventListenersRemoved() {
// We're no longer registered to receive touch events, so don't try to remove
// the touch event handlers in our destructor.
touch_event_request_type_ = kTouchEventRequestTypeNone;
}
void WebPluginContainerImpl::SetParentVisible(bool parent_visible) {
// We override this function to make sure that geometry updates are sent
// over to the plugin. For e.g. when a plugin is instantiated it does not
// have a valid parent. As a result the first geometry update from webkit
// is ignored. This function is called when the plugin eventually gets a
// parent.
if (parent_visible_ == parent_visible)
return; // No change.
parent_visible_ = parent_visible;
if (!self_visible_)
return; // This widget has explicitely been marked as not visible.
if (web_plugin_)
web_plugin_->UpdateVisibility(parent_visible_ && self_visible_);
}
void WebPluginContainerImpl::SetPlugin(WebPlugin* plugin) {
if (plugin == web_plugin_)
return;
element_->ResetInstance();
web_plugin_ = plugin;
is_disposed_ = false;
}
float WebPluginContainerImpl::DeviceScaleFactor() {
Page* page = element_->GetDocument().GetPage();
if (!page)
return 1.0;
return page->DeviceScaleFactorDeprecated();
}
float WebPluginContainerImpl::PageScaleFactor() {
Page* page = element_->GetDocument().GetPage();
if (!page)
return 1.0;
return page->PageScaleFactor();
}
float WebPluginContainerImpl::PageZoomFactor() {
LocalFrame* frame = element_->GetDocument().GetFrame();
if (!frame)
return 1.0;
return frame->PageZoomFactor();
}
void WebPluginContainerImpl::SetWebLayer(WebLayer* layer) {
if (web_layer_ == layer)
return;
if (web_layer_)
GraphicsLayer::UnregisterContentsLayer(web_layer_);
if (layer)
GraphicsLayer::RegisterContentsLayer(layer);
web_layer_ = layer;
if (element_)
element_->SetNeedsCompositingUpdate();
}
void WebPluginContainerImpl::RequestFullscreen() {
Fullscreen::RequestFullscreen(*element_);
}
bool WebPluginContainerImpl::IsFullscreenElement() const {
return Fullscreen::IsCurrentFullScreenElement(*element_);
}
void WebPluginContainerImpl::CancelFullscreen() {
Fullscreen::FullyExitFullscreen(element_->GetDocument());
}
bool WebPluginContainerImpl::SupportsPaginatedPrint() const {
return web_plugin_->SupportsPaginatedPrint();
}
bool WebPluginContainerImpl::IsPrintScalingDisabled() const {
return web_plugin_->IsPrintScalingDisabled();
}
bool WebPluginContainerImpl::GetPrintPresetOptionsFromDocument(
WebPrintPresetOptions* preset_options) const {
return web_plugin_->GetPrintPresetOptionsFromDocument(preset_options);
}
int WebPluginContainerImpl::PrintBegin(
const WebPrintParams& print_params) const {
return web_plugin_->PrintBegin(print_params);
}
void WebPluginContainerImpl::PrintPage(int page_number,
GraphicsContext& gc,
const IntRect& print_rect) {
if (LayoutObjectDrawingRecorder::UseCachedDrawingIfPossible(
gc, *element_->GetLayoutObject(), DisplayItem::Type::kWebPlugin))
return;
LayoutObjectDrawingRecorder drawing_recorder(gc, *element_->GetLayoutObject(),
DisplayItem::Type::kWebPlugin,
print_rect);
gc.Save();
WebCanvas* canvas = gc.Canvas();
web_plugin_->PrintPage(page_number, canvas);
gc.Restore();
}
void WebPluginContainerImpl::PrintEnd() {
web_plugin_->PrintEnd();
}
void WebPluginContainerImpl::Copy() {
if (!web_plugin_->HasSelection())
return;
Platform::Current()->Clipboard()->WriteHTML(
web_plugin_->SelectionAsMarkup(), WebURL(),
web_plugin_->SelectionAsText(), false);
}
bool WebPluginContainerImpl::ExecuteEditCommand(const WebString& name) {
if (web_plugin_->ExecuteEditCommand(name))
return true;
if (name != "Copy")
return false;
Copy();
return true;
}
bool WebPluginContainerImpl::ExecuteEditCommand(const WebString& name,
const WebString& value) {
return web_plugin_->ExecuteEditCommand(name, value);
}
WebElement WebPluginContainerImpl::GetElement() {
return WebElement(element_);
}
WebDocument WebPluginContainerImpl::GetDocument() {
return WebDocument(&element_->GetDocument());
}
void WebPluginContainerImpl::DispatchProgressEvent(const WebString& type,
bool length_computable,
unsigned long long loaded,
unsigned long long total,
const WebString& url) {
ProgressEvent* event;
if (url.IsEmpty()) {
event = ProgressEvent::Create(type, length_computable, loaded, total);
} else {
event = ResourceProgressEvent::Create(type, length_computable, loaded,
total, url);
}
element_->DispatchEvent(event);
}
void WebPluginContainerImpl::EnqueueMessageEvent(
const WebDOMMessageEvent& event) {
static_cast<Event*>(event)->SetTarget(element_);
element_->GetExecutionContext()->GetEventQueue()->EnqueueEvent(event);
}
void WebPluginContainerImpl::Invalidate() {
InvalidateRect(IntRect(0, 0, frame_rect_.Width(), frame_rect_.Height()));
}
void WebPluginContainerImpl::InvalidateRect(const WebRect& rect) {
InvalidateRect(static_cast<IntRect>(rect));
}
void WebPluginContainerImpl::ScrollRect(const WebRect& rect) {
InvalidateRect(rect);
}
void WebPluginContainerImpl::ScheduleAnimation() {
if (auto* frame_view = element_->GetDocument().View())
frame_view->ScheduleAnimation();
}
void WebPluginContainerImpl::ReportGeometry() {
// We cannot compute geometry without a parent or layoutObject.
if (!parent_ || !element_ || !element_->GetLayoutObject() || !web_plugin_)
return;
IntRect window_rect, clip_rect, unobscured_rect;
CalculateGeometry(window_rect, clip_rect, unobscured_rect);
web_plugin_->UpdateGeometry(window_rect, clip_rect, unobscured_rect,
self_visible_);
}
v8::Local<v8::Object> WebPluginContainerImpl::V8ObjectForElement() {
LocalFrame* frame = element_->GetDocument().GetFrame();
if (!frame)
return v8::Local<v8::Object>();
if (!element_->GetDocument().CanExecuteScripts(kNotAboutToExecuteScript))
return v8::Local<v8::Object>();
ScriptState* script_state = ToScriptStateForMainWorld(frame);
if (!script_state)
return v8::Local<v8::Object>();
v8::Local<v8::Value> v8value =
ToV8(element_.Get(), script_state->GetContext()->Global(),
script_state->GetIsolate());
if (v8value.IsEmpty())
return v8::Local<v8::Object>();
DCHECK(v8value->IsObject());
return v8::Local<v8::Object>::Cast(v8value);
}
WebString WebPluginContainerImpl::ExecuteScriptURL(const WebURL& url,
bool popups_allowed) {
LocalFrame* frame = element_->GetDocument().GetFrame();
if (!frame)
return WebString();
const KURL& kurl = url;
DCHECK(kurl.ProtocolIs("javascript"));
String script = DecodeURLEscapeSequences(
kurl.GetString().Substring(strlen("javascript:")));
if (!element_->GetDocument().GetContentSecurityPolicy()->AllowJavaScriptURLs(
element_, script, element_->GetDocument().Url(), OrdinalNumber())) {
return WebString();
}
UserGestureIndicator gesture_indicator(
popups_allowed ? DocumentUserGestureToken::Create(
frame->GetDocument(), UserGestureToken::kNewGesture)
: nullptr);
v8::HandleScope handle_scope(ToIsolate(frame));
v8::Local<v8::Value> result =
frame->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
ScriptSourceCode(script));
// Failure is reported as a null string.
if (result.IsEmpty() || !result->IsString())
return WebString();
return ToCoreString(v8::Local<v8::String>::Cast(result));
}
void WebPluginContainerImpl::LoadFrameRequest(const WebURLRequest& request,
const WebString& target) {
LocalFrame* frame = element_->GetDocument().GetFrame();
if (!frame || !frame->Loader().GetDocumentLoader())
return; // FIXME: send a notification in this case?
FrameLoadRequest frame_request(frame->GetDocument(),
request.ToResourceRequest(), target);
frame->Loader().Load(frame_request);
}
bool WebPluginContainerImpl::IsRectTopmost(const WebRect& rect) {
// Disallow access to the frame during dispose(), because it is not guaranteed
// to be valid memory once this object has started disposal. In particular,
// we might be being disposed because the frame has already be deleted and
// then something else dropped the
// last reference to the this object.
if (is_disposed_ || !element_)
return false;
LocalFrame* frame = element_->GetDocument().GetFrame();
if (!frame)
return false;
IntRect document_rect(frame_rect_.X() + rect.x, frame_rect_.Y() + rect.y,
rect.width, rect.height);
// hitTestResultAtPoint() takes a padding rectangle.
// FIXME: We'll be off by 1 when the width or height is even.
LayoutPoint center = document_rect.Center();
// Make the rect we're checking (the point surrounded by padding rects)
// contained inside the requested rect. (Note that -1/2 is 0.)
LayoutSize padding((document_rect.Width() - 1) / 2,
(document_rect.Height() - 1) / 2);
HitTestResult result = frame->GetEventHandler().HitTestResultAtPoint(
center,
HitTestRequest::kReadOnly | HitTestRequest::kActive |
HitTestRequest::kListBased,
padding);
const HitTestResult::NodeSet& nodes = result.ListBasedTestResult();
if (nodes.size() != 1)
return false;
return nodes.front().Get() == element_;
}
void WebPluginContainerImpl::RequestTouchEventType(
TouchEventRequestType request_type) {
if (touch_event_request_type_ == request_type || !element_)
return;
if (Page* page = element_->GetDocument().GetPage()) {
EventHandlerRegistry& registry = page->GetEventHandlerRegistry();
if (request_type != kTouchEventRequestTypeNone &&
touch_event_request_type_ == kTouchEventRequestTypeNone)
registry.DidAddEventHandler(
*element_, EventHandlerRegistry::kTouchStartOrMoveEventBlocking);
else if (request_type == kTouchEventRequestTypeNone &&
touch_event_request_type_ != kTouchEventRequestTypeNone)
registry.DidRemoveEventHandler(
*element_, EventHandlerRegistry::kTouchStartOrMoveEventBlocking);
}
touch_event_request_type_ = request_type;
}
void WebPluginContainerImpl::SetWantsWheelEvents(bool wants_wheel_events) {
if (wants_wheel_events_ == wants_wheel_events)
return;
if (Page* page = element_->GetDocument().GetPage()) {
EventHandlerRegistry& registry = page->GetEventHandlerRegistry();
if (wants_wheel_events)
registry.DidAddEventHandler(*element_,
EventHandlerRegistry::kWheelEventBlocking);
else
registry.DidRemoveEventHandler(*element_,
EventHandlerRegistry::kWheelEventBlocking);
}
wants_wheel_events_ = wants_wheel_events;
if (Page* page = element_->GetDocument().GetPage()) {
if (ScrollingCoordinator* scrolling_coordinator =
page->GetScrollingCoordinator()) {
if (parent_)
scrolling_coordinator->NotifyGeometryChanged();
}
}
}
WebPoint WebPluginContainerImpl::RootFrameToLocalPoint(
const WebPoint& point_in_root_frame) {
if (!parent_)
return point_in_root_frame;
WebPoint point_in_content = parent_->RootFrameToContents(point_in_root_frame);
return RoundedIntPoint(element_->GetLayoutObject()->AbsoluteToLocal(
FloatPoint(point_in_content), kUseTransforms));
}
WebPoint WebPluginContainerImpl::LocalToRootFramePoint(
const WebPoint& point_in_local) {
if (!parent_)
return point_in_local;
IntPoint absolute_point =
RoundedIntPoint(element_->GetLayoutObject()->LocalToAbsolute(
FloatPoint(point_in_local), kUseTransforms));
return parent_->ContentsToRootFrame(absolute_point);
}
void WebPluginContainerImpl::DidReceiveResponse(
const ResourceResponse& response) {
// Make sure that the plugin receives window geometry before data, or else
// plugins misbehave.
FrameRectsChanged();
WrappedResourceResponse url_response(response);
web_plugin_->DidReceiveResponse(url_response);
}
void WebPluginContainerImpl::DidReceiveData(const char* data, int data_length) {
web_plugin_->DidReceiveData(data, data_length);
}
void WebPluginContainerImpl::DidFinishLoading() {
web_plugin_->DidFinishLoading();
}
void WebPluginContainerImpl::DidFailLoading(const ResourceError& error) {
web_plugin_->DidFailLoading(error);
}
WebLayer* WebPluginContainerImpl::PlatformLayer() const {
return web_layer_;
}
v8::Local<v8::Object> WebPluginContainerImpl::ScriptableObject(
v8::Isolate* isolate) {
// With Oilpan, on plugin element detach dispose() will be called to safely
// clear out references, including the pre-emptive destruction of the plugin.
//
// It clearly has no scriptable object if in such a disposed state.
if (!web_plugin_)
return v8::Local<v8::Object>();
v8::Local<v8::Object> object = web_plugin_->V8ScriptableObject(isolate);
// If the plugin has been destroyed and the reference on the stack is the
// only one left, then don't return the scriptable object.
if (!web_plugin_)
return v8::Local<v8::Object>();
return object;
}
bool WebPluginContainerImpl::SupportsKeyboardFocus() const {
return web_plugin_->SupportsKeyboardFocus();
}
bool WebPluginContainerImpl::SupportsInputMethod() const {
return web_plugin_->SupportsInputMethod();
}
bool WebPluginContainerImpl::CanProcessDrag() const {
return web_plugin_->CanProcessDrag();
}
bool WebPluginContainerImpl::WantsWheelEvents() {
return wants_wheel_events_;
}
// Private methods -------------------------------------------------------------
WebPluginContainerImpl::WebPluginContainerImpl(HTMLPlugInElement* element,
WebPlugin* web_plugin)
: ContextClient(element->GetDocument().GetFrame()),
element_(element),
web_plugin_(web_plugin),
web_layer_(nullptr),
touch_event_request_type_(kTouchEventRequestTypeNone),
wants_wheel_events_(false),
self_visible_(false),
parent_visible_(false),
is_disposed_(false) {}
WebPluginContainerImpl::~WebPluginContainerImpl() {
// The plugin container must have been disposed of by now.
DCHECK(!web_plugin_);
}
void WebPluginContainerImpl::Dispose() {
is_disposed_ = true;
RequestTouchEventType(kTouchEventRequestTypeNone);
SetWantsWheelEvents(false);
if (web_plugin_) {
CHECK(web_plugin_->Container() == this);
web_plugin_->Destroy();
web_plugin_ = nullptr;
}
if (web_layer_) {
GraphicsLayer::UnregisterContentsLayer(web_layer_);
web_layer_ = nullptr;
}
}
DEFINE_TRACE(WebPluginContainerImpl) {
visitor->Trace(parent_);
visitor->Trace(element_);
ContextClient::Trace(visitor);
PluginView::Trace(visitor);
}
void WebPluginContainerImpl::HandleMouseEvent(MouseEvent* event) {
// We cache the parent FrameView here as the plugin widget could be deleted
// in the call to HandleEvent. See http://b/issue?id=1362948
FrameView* parent_view = parent_;
// TODO(dtapuska): Move WebMouseEventBuilder into the anonymous namespace
// in this class.
WebMouseEventBuilder transformed_event(
parent_, LayoutItem(element_->GetLayoutObject()), *event);
if (transformed_event.GetType() == WebInputEvent::kUndefined)
return;
if (event->type() == EventTypeNames::mousedown)
FocusPlugin();
WebCursorInfo cursor_info;
if (web_plugin_ &&
web_plugin_->HandleInputEvent(transformed_event, cursor_info) !=
WebInputEventResult::kNotHandled)
event->SetDefaultHandled();
// A windowless plugin can change the cursor in response to a mouse move
// event. We need to reflect the changed cursor in the frame view as the
// mouse is moved in the boundaries of the windowless plugin.
Page* page = parent_view->GetFrame().GetPage();
if (!page)
return;
ToChromeClientImpl(page->GetChromeClient())
.SetCursorForPlugin(cursor_info,
&parent_view->GetFrame().LocalFrameRoot());
}
void WebPluginContainerImpl::HandleDragEvent(MouseEvent* event) {
DCHECK(event->IsDragEvent());
WebDragStatus drag_status = kWebDragStatusUnknown;
if (event->type() == EventTypeNames::dragenter)
drag_status = kWebDragStatusEnter;
else if (event->type() == EventTypeNames::dragleave)
drag_status = kWebDragStatusLeave;
else if (event->type() == EventTypeNames::dragover)
drag_status = kWebDragStatusOver;
else if (event->type() == EventTypeNames::drop)
drag_status = kWebDragStatusDrop;
if (drag_status == kWebDragStatusUnknown)
return;
DataTransfer* data_transfer = event->getDataTransfer();
WebDragData drag_data = data_transfer->GetDataObject()->ToWebDragData();
WebDragOperationsMask drag_operation_mask =
static_cast<WebDragOperationsMask>(data_transfer->SourceOperation());
WebPoint drag_screen_location(event->screenX(), event->screenY());
WebPoint drag_location(
event->AbsoluteLocation().X() - frame_rect_.Location().X(),
event->AbsoluteLocation().Y() - frame_rect_.Location().Y());
web_plugin_->HandleDragStatusUpdate(drag_status, drag_data,
drag_operation_mask, drag_location,
drag_screen_location);
}
void WebPluginContainerImpl::HandleWheelEvent(WheelEvent* event) {
WebFloatPoint absolute_location = event->NativeEvent().PositionInRootFrame();
// Translate the root frame position to content coordinates.
if (parent_) {
absolute_location = parent_->RootFrameToContents(absolute_location);
}
IntPoint local_point =
RoundedIntPoint(element_->GetLayoutObject()->AbsoluteToLocal(
absolute_location, kUseTransforms));
WebMouseWheelEvent translated_event = event->NativeEvent().FlattenTransform();
translated_event.SetPositionInWidget(local_point.X(), local_point.Y());
WebCursorInfo cursor_info;
if (web_plugin_->HandleInputEvent(translated_event, cursor_info) !=
WebInputEventResult::kNotHandled)
event->SetDefaultHandled();
}
void WebPluginContainerImpl::HandleKeyboardEvent(KeyboardEvent* event) {
WebKeyboardEventBuilder web_event(*event);
if (web_event.GetType() == WebInputEvent::kUndefined)
return;
if (web_event.GetType() == WebInputEvent::kKeyDown) {
#if OS(MACOSX)
if ((web_event.GetModifiers() & WebInputEvent::kInputModifiers) ==
WebInputEvent::kMetaKey
#else
if ((web_event.GetModifiers() & WebInputEvent::kInputModifiers) ==
WebInputEvent::kControlKey
#endif
&& (web_event.windows_key_code == VKEY_C ||
web_event.windows_key_code == VKEY_INSERT)
// Only copy if there's a selection, so that we only ever do this
// for Pepper plugins that support copying. Windowless NPAPI
// plugins will get the event as before.
&& web_plugin_->HasSelection()) {
Copy();
event->SetDefaultHandled();
return;
}
}
// Give the client a chance to issue edit comamnds.
WebLocalFrameBase* web_frame =
WebLocalFrameBase::FromFrame(element_->GetDocument().GetFrame());
if (web_plugin_->SupportsEditCommands())
web_frame->Client()->HandleCurrentKeyboardEvent();
WebCursorInfo cursor_info;
if (web_plugin_->HandleInputEvent(web_event, cursor_info) !=
WebInputEventResult::kNotHandled)
event->SetDefaultHandled();
}
void WebPluginContainerImpl::HandleTouchEvent(TouchEvent* event) {
switch (touch_event_request_type_) {
case kTouchEventRequestTypeNone:
return;
case kTouchEventRequestTypeRaw: {
if (!event->NativeEvent())
return;
if (event->type() == EventTypeNames::touchstart)
FocusPlugin();
WebTouchEvent transformed_event =
event->NativeEvent()->FlattenTransform();
for (unsigned i = 0; i < transformed_event.touches_length; ++i) {
WebFloatPoint absolute_location = transformed_event.touches[i].position;
// Translate the root frame position to content coordinates.
if (parent_) {
absolute_location = parent_->RootFrameToContents(absolute_location);
}
IntPoint local_point =
RoundedIntPoint(element_->GetLayoutObject()->AbsoluteToLocal(
absolute_location, kUseTransforms));
transformed_event.touches[i].position.x = local_point.X();
transformed_event.touches[i].position.y = local_point.Y();
}
WebCursorInfo cursor_info;
if (web_plugin_->HandleInputEvent(transformed_event, cursor_info) !=
WebInputEventResult::kNotHandled)
event->SetDefaultHandled();
// FIXME: Can a plugin change the cursor from a touch-event callback?
return;
}
case kTouchEventRequestTypeSynthesizedMouse:
SynthesizeMouseEventIfPossible(event);
return;
}
}
void WebPluginContainerImpl::HandleGestureEvent(GestureEvent* event) {
if (event->NativeEvent().GetType() == WebInputEvent::kUndefined)
return;
if (event->NativeEvent().GetType() == WebInputEvent::kGestureTapDown)
FocusPlugin();
// Take a copy of the event and translate it into the coordinate
// system of the plugin.
WebGestureEvent translated_event = event->NativeEvent();
WebFloatPoint absolute_root_frame_location =
event->NativeEvent().PositionInRootFrame();
IntPoint local_point =
RoundedIntPoint(element_->GetLayoutObject()->AbsoluteToLocal(
absolute_root_frame_location, kUseTransforms));
translated_event.FlattenTransform();
translated_event.x = local_point.X();
translated_event.y = local_point.Y();
WebCursorInfo cursor_info;
if (web_plugin_->HandleInputEvent(translated_event, cursor_info) !=
WebInputEventResult::kNotHandled) {
event->SetDefaultHandled();
return;
}
// FIXME: Can a plugin change the cursor from a touch-event callback?
}
void WebPluginContainerImpl::SynthesizeMouseEventIfPossible(TouchEvent* event) {
WebMouseEventBuilder web_event(
parent_, LayoutItem(element_->GetLayoutObject()), *event);
if (web_event.GetType() == WebInputEvent::kUndefined)
return;
WebCursorInfo cursor_info;
if (web_plugin_->HandleInputEvent(web_event, cursor_info) !=
WebInputEventResult::kNotHandled)
event->SetDefaultHandled();
}
void WebPluginContainerImpl::FocusPlugin() {
LocalFrame& containing_frame = parent_->GetFrame();
if (Page* current_page = containing_frame.GetPage())
current_page->GetFocusController().SetFocusedElement(element_,
&containing_frame);
else
containing_frame.GetDocument()->SetFocusedElement(
element_, FocusParams(SelectionBehaviorOnFocus::kNone,
kWebFocusTypeNone, nullptr));
}
void WebPluginContainerImpl::IssuePaintInvalidations() {
if (pending_invalidation_rect_.IsEmpty())
return;
LayoutBox* layout_object = ToLayoutBox(element_->GetLayoutObject());
if (!layout_object)
return;
layout_object->InvalidatePaintRectangle(
LayoutRect(pending_invalidation_rect_));
pending_invalidation_rect_ = IntRect();
}
void WebPluginContainerImpl::ComputeClipRectsForPlugin(
const HTMLFrameOwnerElement* owner_element,
IntRect& window_rect,
IntRect& clipped_local_rect,
IntRect& unclipped_int_local_rect) const {
DCHECK(owner_element);
if (!owner_element->GetLayoutObject()) {
clipped_local_rect = IntRect();
unclipped_int_local_rect = IntRect();
return;
}
LayoutView* root_view = element_->GetDocument().View()->GetLayoutView();
while (root_view->GetFrame()->OwnerLayoutObject())
root_view = root_view->GetFrame()->OwnerLayoutObject()->View();
LayoutBox* box = ToLayoutBox(owner_element->GetLayoutObject());
// Note: FrameRect() for this plugin is equal to contentBoxRect, mapped to the
// containing view space, and rounded off.
// See LayoutPart.cpp::updateGeometryInternal. To remove the lossy
// effect of rounding off, use contentBoxRect directly.
LayoutRect unclipped_absolute_rect(box->ContentBoxRect());
box->MapToVisualRectInAncestorSpace(root_view, unclipped_absolute_rect);
// The frameRect is already in absolute space of the local frame to the
// plugin.
window_rect = frame_rect_;
// Map up to the root frame.
LayoutRect layout_window_rect =
LayoutRect(element_->GetDocument()
.View()
->GetLayoutViewItem()
.LocalToAbsoluteQuad(FloatQuad(FloatRect(frame_rect_)),
kTraverseDocumentBoundaries)
.BoundingBox());
// Finally, adjust for scrolling of the root frame, which the above does not
// take into account.
layout_window_rect.MoveBy(-root_view->ViewRect().Location());
window_rect = PixelSnappedIntRect(layout_window_rect);
LayoutRect layout_clipped_local_rect = unclipped_absolute_rect;
LayoutRect unclipped_layout_local_rect = layout_clipped_local_rect;
layout_clipped_local_rect.Intersect(
LayoutRect(root_view->GetFrameView()->VisibleContentRect()));
unclipped_int_local_rect =
box->AbsoluteToLocalQuad(FloatRect(unclipped_layout_local_rect),
kTraverseDocumentBoundaries | kUseTransforms)
.EnclosingBoundingBox();
// As a performance optimization, map the clipped rect separately if is
// different than the unclipped rect.
if (layout_clipped_local_rect != unclipped_layout_local_rect)
clipped_local_rect =
box->AbsoluteToLocalQuad(FloatRect(layout_clipped_local_rect),
kTraverseDocumentBoundaries | kUseTransforms)
.EnclosingBoundingBox();
else
clipped_local_rect = unclipped_int_local_rect;
}
void WebPluginContainerImpl::CalculateGeometry(IntRect& window_rect,
IntRect& clip_rect,
IntRect& unobscured_rect) {
// document().layoutView() can be null when we receive messages from the
// plugins while we are destroying a frame.
// FIXME: Can we just check m_element->document().isActive() ?
if (!element_->GetLayoutObject()
->GetDocument()
.GetLayoutViewItem()
.IsNull()) {
// Take our element and get the clip rect from the enclosing layer and
// frame view.
ComputeClipRectsForPlugin(element_, window_rect, clip_rect,
unobscured_rect);
}
}
} // namespace blink