| /* |
| * 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 "third_party/blink/renderer/core/exported/web_plugin_container_impl.h" |
| |
| #include "build/build_config.h" |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/public/platform/web_coalesced_input_event.h" |
| #include "third_party/blink/public/platform/web_cursor_info.h" |
| #include "third_party/blink/public/platform/web_drag_data.h" |
| #include "third_party/blink/public/platform/web_input_event.h" |
| #include "third_party/blink/public/platform/web_rect.h" |
| #include "third_party/blink/public/platform/web_string.h" |
| #include "third_party/blink/public/platform/web_url.h" |
| #include "third_party/blink/public/platform/web_url_error.h" |
| #include "third_party/blink/public/platform/web_url_request.h" |
| #include "third_party/blink/public/web/web_document.h" |
| #include "third_party/blink/public/web/web_dom_message_event.h" |
| #include "third_party/blink/public/web/web_element.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/blink/public/web/web_local_frame_client.h" |
| #include "third_party/blink/public/web/web_plugin.h" |
| #include "third_party/blink/public/web/web_print_params.h" |
| #include "third_party/blink/public/web/web_print_preset_options.h" |
| #include "third_party/blink/public/web/web_view_client.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_controller.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_element.h" |
| #include "third_party/blink/renderer/core/clipboard/data_object.h" |
| #include "third_party/blink/renderer/core/clipboard/data_transfer.h" |
| #include "third_party/blink/renderer/core/clipboard/system_clipboard.h" |
| #include "third_party/blink/renderer/core/dom/events/event_queue_impl.h" |
| #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h" |
| #include "third_party/blink/renderer/core/events/drag_event.h" |
| #include "third_party/blink/renderer/core/events/gesture_event.h" |
| #include "third_party/blink/renderer/core/events/keyboard_event.h" |
| #include "third_party/blink/renderer/core/events/mouse_event.h" |
| #include "third_party/blink/renderer/core/events/progress_event.h" |
| #include "third_party/blink/renderer/core/events/resource_progress_event.h" |
| #include "third_party/blink/renderer/core/events/touch_event.h" |
| #include "third_party/blink/renderer/core/events/web_input_event_conversion.h" |
| #include "third_party/blink/renderer/core/events/wheel_event.h" |
| #include "third_party/blink/renderer/core/execution_context/execution_context.h" |
| #include "third_party/blink/renderer/core/exported/web_document_loader_impl.h" |
| #include "third_party/blink/renderer/core/exported/web_view_impl.h" |
| #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" |
| #include "third_party/blink/renderer/core/frame/event_handler_registry.h" |
| #include "third_party/blink/renderer/core/frame/find_in_page.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" |
| #include "third_party/blink/renderer/core/fullscreen/fullscreen.h" |
| #include "third_party/blink/renderer/core/html/forms/html_form_element.h" |
| #include "third_party/blink/renderer/core/html/html_plugin_element.h" |
| #include "third_party/blink/renderer/core/html_names.h" |
| #include "third_party/blink/renderer/core/input/event_handler.h" |
| #include "third_party/blink/renderer/core/layout/hit_test_result.h" |
| #include "third_party/blink/renderer/core/layout/layout_box.h" |
| #include "third_party/blink/renderer/core/layout/layout_embedded_content.h" |
| #include "third_party/blink/renderer/core/layout/layout_view.h" |
| #include "third_party/blink/renderer/core/loader/frame_load_request.h" |
| #include "third_party/blink/renderer/core/page/chrome_client.h" |
| #include "third_party/blink/renderer/core/page/focus_controller.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer.h" |
| #include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h" |
| #include "third_party/blink/renderer/platform/geometry/layout_rect.h" |
| #include "third_party/blink/renderer/platform/graphics/graphics_context.h" |
| #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h" |
| #include "third_party/blink/renderer/platform/keyboard_codes.h" |
| #include "third_party/blink/renderer/platform/runtime_enabled_features.h" |
| #include "third_party/blink/renderer/platform/scroll/scroll_animator_base.h" |
| #include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h" |
| #include "third_party/blink/renderer/platform/wtf/assertions.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| #if defined(OS_MACOSX) |
| const WebInputEvent::Modifiers kEditingModifier = WebInputEvent::kMetaKey; |
| #else |
| const WebInputEvent::Modifiers kEditingModifier = WebInputEvent::kControlKey; |
| #endif |
| |
| } // namespace |
| |
| // Public methods -------------------------------------------------------------- |
| |
| void WebPluginContainerImpl::AttachToLayout() { |
| DCHECK(!is_attached_); |
| is_attached_ = true; |
| SetParentVisible(true); |
| } |
| |
| void WebPluginContainerImpl::DetachFromLayout() { |
| DCHECK(is_attached_); |
| SetParentVisible(false); |
| is_attached_ = false; |
| } |
| |
| void WebPluginContainerImpl::UpdateAllLifecyclePhases() { |
| if (!web_plugin_) |
| return; |
| |
| web_plugin_->UpdateAllLifecyclePhases(); |
| } |
| |
| void WebPluginContainerImpl::SetFrameRect(const IntRect& frame_rect) { |
| frame_rect_ = frame_rect; |
| ReportGeometry(); |
| } |
| |
| IntRect WebPluginContainerImpl::FrameRect() const { |
| IntPoint location(frame_rect_.Location()); |
| |
| // As an optimization, we don't include the root layer's scroll offset in the |
| // frame rect. As a result, we don't need to recalculate the frame rect every |
| // time the root layer scrolls, but we need to add it in here. |
| LayoutEmbeddedContent* owner = element_->GetLayoutEmbeddedContent(); |
| if (owner) { |
| LayoutView* owner_layout_view = owner->View(); |
| DCHECK(owner_layout_view); |
| if (owner_layout_view->HasOverflowClip()) |
| location.Move(-owner_layout_view->ScrolledContentOffset()); |
| } |
| |
| return IntRect(location, frame_rect_.Size()); |
| } |
| |
| void WebPluginContainerImpl::Paint(GraphicsContext& context, |
| const GlobalPaintFlags, |
| const CullRect& cull_rect, |
| const IntSize& paint_offset) const { |
| // Don't paint anything if the plugin doesn't intersect. |
| if (!cull_rect.IntersectsCullRect(FrameRect())) |
| return; |
| |
| if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() && layer_) { |
| layer_->SetBounds(static_cast<gfx::Size>(frame_rect_.Size())); |
| layer_->SetIsDrawable(true); |
| // With Slimming Paint v2, composited plugins should have their layers |
| // inserted rather than invoking WebPlugin::paint. |
| RecordForeignLayer(context, *element_->GetLayoutObject(), |
| DisplayItem::kForeignLayerPlugin, layer_, |
| FrameRect().Location(), frame_rect_.Size()); |
| return; |
| } |
| |
| if (DrawingRecorder::UseCachedDrawingIfPossible( |
| context, *element_->GetLayoutObject(), DisplayItem::kWebPlugin)) |
| return; |
| |
| DrawingRecorder recorder(context, *element_->GetLayoutObject(), |
| DisplayItem::kWebPlugin); |
| context.Save(); |
| |
| // The plugin is positioned in the root frame's coordinates, so it needs to |
| // be painted in them too. |
| FloatPoint origin(ParentFrameView().ConvertToRootFrame(IntPoint())); |
| origin.Move(-paint_offset); |
| context.Translate(-origin.X(), -origin.Y()); |
| |
| WebCanvas* canvas = context.Canvas(); |
| |
| IntRect window_rect = ParentFrameView().ConvertToRootFrame(cull_rect.rect_); |
| web_plugin_->Paint(canvas, window_rect); |
| |
| context.Restore(); |
| } |
| |
| void WebPluginContainerImpl::UpdateGeometry() { |
| if (LayoutEmbeddedContent* layout = element_->GetLayoutEmbeddedContent()) |
| layout->UpdateGeometry(*this); |
| } |
| |
| void WebPluginContainerImpl::InvalidateRect(const IntRect& rect) { |
| // InvalidateRect can be called from Dispose when this plugin is no longer |
| // attached. In this case, we return immediately. |
| if (!is_attached_) |
| 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()); |
| |
| layout_object->InvalidatePaintRectangle(LayoutRect(dirty_rect)); |
| } |
| |
| 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 EmbeddedContentView::HandleEvent returned |
| // true/false and HTMLPluginElement called SetDefaultHandled or |
| // DefaultEventHandler. |
| if (!event->DefaultHandled()) |
| element_->Node::DefaultEventHandler(event); |
| } |
| |
| void WebPluginContainerImpl::FrameRectsChanged() { |
| 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; |
| } |
| |
| void WebPluginContainerImpl::UsePluginAsFindHandler() { |
| WebLocalFrameImpl* frame = |
| WebLocalFrameImpl::FromFrame(element_->GetDocument().GetFrame()); |
| if (frame) |
| frame->GetFindInPage()->SetPluginFindHandler(this); |
| } |
| |
| 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::SetCcLayer(cc::Layer* new_layer, |
| bool prevent_contents_opaque_changes) { |
| if (layer_ == new_layer && |
| prevent_contents_opaque_changes == prevent_contents_opaque_changes_) |
| return; |
| |
| if (layer_) |
| GraphicsLayer::UnregisterContentsLayer(layer_); |
| if (new_layer) |
| GraphicsLayer::RegisterContentsLayer(new_layer); |
| |
| layer_ = new_layer; |
| prevent_contents_opaque_changes_ = prevent_contents_opaque_changes; |
| |
| if (element_) |
| element_->SetNeedsCompositingUpdate(); |
| } |
| |
| void WebPluginContainerImpl::RequestFullscreen() { |
| Fullscreen::RequestFullscreen(*element_); |
| } |
| |
| bool WebPluginContainerImpl::IsFullscreenElement() const { |
| return Fullscreen::IsFullscreenElement(*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) { |
| if (DrawingRecorder::UseCachedDrawingIfPossible( |
| gc, *element_->GetLayoutObject(), DisplayItem::kWebPlugin)) |
| return; |
| |
| // TODO(wkorman): Do we still need print_rect at all? |
| DrawingRecorder recorder(gc, *element_->GetLayoutObject(), |
| DisplayItem::kWebPlugin); |
| 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; |
| |
| SystemClipboard::GetInstance().WriteHTML( |
| web_plugin_->SelectionAsMarkup(), KURL(), web_plugin_->SelectionAsText()); |
| } |
| |
| 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); |
| } |
| |
| // static |
| bool WebPluginContainerImpl::SupportsCommand(const WebString& name) { |
| return name == "Copy" || name == "Cut" || name == "Paste" || |
| name == "PasteAndMatchStyle" || name == "SelectAll" || |
| name == "Undo" || name == "Redo"; |
| } |
| |
| 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) { |
| if (!element_->GetExecutionContext()) |
| return; |
| event_queue_->EnqueueEvent(FROM_HERE, 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() { |
| // Ignore when SetFrameRect/ReportGeometry is called from |
| // UpdateOnEmbeddedContentViewChange before plugin is attached. |
| if (!is_attached_) |
| 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(); |
| } |
| |
| std::unique_ptr<UserGestureIndicator> gesture_indicator; |
| if (popups_allowed) { |
| gesture_indicator = |
| Frame::NotifyUserActivation(frame, UserGestureToken::kNewGesture); |
| } |
| |
| v8::HandleScope handle_scope(ToIsolate(frame)); |
| v8::Local<v8::Value> result = |
| frame->GetScriptController().ExecuteScriptInMainWorldAndReturnValue( |
| ScriptSourceCode(script, ScriptSourceLocationType::kJavascriptUrl)); |
| |
| // 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().StartNavigation(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_attached_ || !element_) |
| return false; |
| |
| LocalFrame* frame = element_->GetDocument().GetFrame(); |
| if (!frame) |
| return false; |
| |
| IntPoint location = FrameRect().Location(); |
| LayoutRect document_rect(location.X() + rect.x, location.Y() + rect.y, |
| rect.width, rect.height); |
| HitTestResult result = frame->GetEventHandler().HitTestResultAtRect( |
| document_rect, HitTestRequest::kReadOnly | HitTestRequest::kActive | |
| HitTestRequest::kListBased); |
| 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 (auto* frame = element_->GetDocument().GetFrame()) { |
| EventHandlerRegistry& registry = frame->GetEventHandlerRegistry(); |
| if (request_type == kTouchEventRequestTypeRawLowLatency) { |
| if (touch_event_request_type_ != kTouchEventRequestTypeNone) { |
| registry.DidRemoveEventHandler( |
| *element_, EventHandlerRegistry::kTouchStartOrMoveEventBlocking); |
| } |
| registry.DidAddEventHandler( |
| *element_, |
| EventHandlerRegistry::kTouchStartOrMoveEventBlockingLowLatency); |
| } else if (request_type != kTouchEventRequestTypeNone) { |
| if (touch_event_request_type_ == kTouchEventRequestTypeRawLowLatency) { |
| registry.DidRemoveEventHandler( |
| *element_, |
| EventHandlerRegistry::kTouchStartOrMoveEventBlockingLowLatency); |
| } |
| if (touch_event_request_type_ == kTouchEventRequestTypeNone || |
| touch_event_request_type_ == kTouchEventRequestTypeRawLowLatency) { |
| registry.DidAddEventHandler( |
| *element_, EventHandlerRegistry::kTouchStartOrMoveEventBlocking); |
| } |
| } else if (touch_event_request_type_ != kTouchEventRequestTypeNone) { |
| registry.DidRemoveEventHandler( |
| *element_, |
| touch_event_request_type_ == kTouchEventRequestTypeRawLowLatency |
| ? EventHandlerRegistry::kTouchStartOrMoveEventBlockingLowLatency |
| : EventHandlerRegistry::kTouchStartOrMoveEventBlocking); |
| } |
| } |
| touch_event_request_type_ = request_type; |
| } |
| |
| void WebPluginContainerImpl::SetWantsWheelEvents(bool wants_wheel_events) { |
| if (wants_wheel_events_ == wants_wheel_events) |
| return; |
| |
| if (auto* frame = element_->GetDocument().GetFrame()) { |
| EventHandlerRegistry& registry = frame->GetEventHandlerRegistry(); |
| if (wants_wheel_events) { |
| registry.DidAddEventHandler(*element_, |
| EventHandlerRegistry::kWheelEventBlocking); |
| } else { |
| registry.DidRemoveEventHandler(*element_, |
| EventHandlerRegistry::kWheelEventBlocking); |
| } |
| } |
| |
| wants_wheel_events_ = wants_wheel_events; |
| if (auto* page = element_->GetDocument().GetPage()) { |
| if (ScrollingCoordinator* scrolling_coordinator = |
| page->GetScrollingCoordinator()) { |
| // Only call scrolling_coordinator if attached. SetWantsWheelEvents can |
| // be called from Plugin Initialization when it is not yet attached. |
| if (is_attached_) { |
| LocalFrameView* frame_view = element_->GetDocument().GetFrame()->View(); |
| scrolling_coordinator->NotifyGeometryChanged(frame_view); |
| } |
| } |
| } |
| } |
| |
| WebPoint WebPluginContainerImpl::RootFrameToLocalPoint( |
| const WebPoint& point_in_root_frame) { |
| WebPoint point_in_content = |
| ParentFrameView().ConvertFromRootFrame(point_in_root_frame); |
| return RoundedIntPoint(element_->GetLayoutObject()->AbsoluteToLocal( |
| FloatPoint(point_in_content), kUseTransforms)); |
| } |
| |
| WebPoint WebPluginContainerImpl::LocalToRootFramePoint( |
| const WebPoint& point_in_local) { |
| IntPoint absolute_point = |
| RoundedIntPoint(element_->GetLayoutObject()->LocalToAbsolute( |
| FloatPoint(point_in_local), kUseTransforms)); |
| return ParentFrameView().ConvertToRootFrame(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); |
| } |
| |
| cc::Layer* WebPluginContainerImpl::CcLayer() const { |
| return layer_; |
| } |
| |
| bool WebPluginContainerImpl::PreventContentsOpaqueChangesToCcLayer() const { |
| return prevent_contents_opaque_changes_; |
| } |
| |
| 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), |
| event_queue_( |
| EventQueueImpl::Create(&element, TaskType::kInternalDefault)), |
| web_plugin_(web_plugin), |
| layer_(nullptr), |
| touch_event_request_type_(kTouchEventRequestTypeNone), |
| prevent_contents_opaque_changes_(false), |
| wants_wheel_events_(false), |
| self_visible_(false), |
| parent_visible_(false), |
| is_attached_(false) {} |
| |
| WebPluginContainerImpl::~WebPluginContainerImpl() { |
| // The plugin container must have been disposed of by now. |
| DCHECK(!web_plugin_); |
| } |
| |
| LocalFrameView& WebPluginContainerImpl::ParentFrameView() const { |
| DCHECK(is_attached_); |
| return *element_->GetDocument().GetFrame()->View(); |
| } |
| |
| void WebPluginContainerImpl::Dispose() { |
| is_attached_ = false; |
| |
| RequestTouchEventType(kTouchEventRequestTypeNone); |
| SetWantsWheelEvents(false); |
| |
| if (WebLocalFrameImpl* frame = |
| WebLocalFrameImpl::FromFrame(element_->GetDocument().GetFrame())) { |
| if (frame->GetFindInPage()->PluginFindHandler() == this) |
| frame->GetFindInPage()->SetPluginFindHandler(nullptr); |
| } |
| |
| if (web_plugin_) { |
| CHECK(web_plugin_->Container() == this); |
| web_plugin_->Destroy(); |
| web_plugin_ = nullptr; |
| } |
| |
| if (layer_) { |
| GraphicsLayer::UnregisterContentsLayer(layer_); |
| layer_ = nullptr; |
| } |
| } |
| |
| void WebPluginContainerImpl::Trace(blink::Visitor* visitor) { |
| visitor->Trace(element_); |
| visitor->Trace(event_queue_); |
| ContextClient::Trace(visitor); |
| } |
| |
| void WebPluginContainerImpl::HandleMouseEvent(MouseEvent* event) { |
| // We cache the parent LocalFrameView here as the plugin widget could be |
| // deleted in the call to HandleEvent. See http://b/issue?id=1362948 |
| LocalFrameView& parent = ParentFrameView(); |
| |
| // TODO(dtapuska): Move WebMouseEventBuilder into the anonymous namespace |
| // in this class. |
| WebMouseEventBuilder transformed_event(&parent, 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( |
| WebCoalescedInputEvent(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.GetFrame().GetPage(); |
| if (!page) |
| return; |
| page->GetChromeClient().SetCursorForPlugin( |
| cursor_info, &parent.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()); |
| WebFloatPoint drag_screen_location(event->screenX(), event->screenY()); |
| IntPoint location(FrameRect().Location()); |
| WebFloatPoint drag_location(event->AbsoluteLocation().X() - location.X(), |
| event->AbsoluteLocation().Y() - 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. |
| absolute_location = ParentFrameView().ConvertFromRootFrame(absolute_location); |
| |
| FloatPoint local_point = 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(WebCoalescedInputEvent(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 (HandleCutCopyPasteKeyboardEvent(web_event)) { |
| event->SetDefaultHandled(); |
| return; |
| } |
| |
| // Give the client a chance to issue edit comamnds. |
| WebLocalFrameImpl* web_frame = |
| WebLocalFrameImpl::FromFrame(element_->GetDocument().GetFrame()); |
| if (web_plugin_->SupportsEditCommands()) |
| web_frame->Client()->HandleCurrentKeyboardEvent(); |
| |
| WebCursorInfo cursor_info; |
| if (web_plugin_->HandleInputEvent(WebCoalescedInputEvent(web_event), |
| cursor_info) != |
| WebInputEventResult::kNotHandled) { |
| event->SetDefaultHandled(); |
| } |
| } |
| |
| bool WebPluginContainerImpl::HandleCutCopyPasteKeyboardEvent( |
| const WebKeyboardEvent& event) { |
| if (event.GetType() != WebInputEvent::kRawKeyDown && |
| event.GetType() != WebInputEvent::kKeyDown) { |
| return false; |
| } |
| |
| int input_modifiers = event.GetModifiers() & WebInputEvent::kInputModifiers; |
| if (input_modifiers == kEditingModifier) { |
| // Only copy/cut if there's a selection, so that we only ever do |
| // this for Pepper plugins that support copying/cutting. |
| if (web_plugin_->HasSelection()) { |
| if (event.windows_key_code == VKEY_C || |
| event.windows_key_code == VKEY_INSERT) { |
| Copy(); |
| return true; |
| } |
| if (event.windows_key_code == VKEY_X) |
| return ExecuteEditCommand("Cut", ""); |
| } |
| // Ask the plugin if it can edit text before executing "Paste". |
| if (event.windows_key_code == VKEY_V && web_plugin_->CanEditText()) |
| return ExecuteEditCommand("Paste", ""); |
| return false; |
| } |
| |
| if (input_modifiers == WebInputEvent::kShiftKey) { |
| // Alternate shortcuts for "Cut" and "Paste" are Shift + Delete and Shift + |
| // Insert, respectively. |
| if (event.windows_key_code == VKEY_DELETE && web_plugin_->HasSelection()) |
| return ExecuteEditCommand("Cut", ""); |
| if (event.windows_key_code == VKEY_INSERT && web_plugin_->CanEditText()) |
| return ExecuteEditCommand("Paste", ""); |
| return false; |
| } |
| |
| // Invoke "PasteAndMatchStyle" using Ctrl + Shift + V to paste as plain |
| // text. |
| if (input_modifiers == (kEditingModifier | WebInputEvent::kShiftKey) && |
| event.windows_key_code == VKEY_V && web_plugin_->CanEditText()) { |
| return ExecuteEditCommand("PasteAndMatchStyle", ""); |
| } |
| return false; |
| } |
| |
| WebTouchEvent WebPluginContainerImpl::TransformTouchEvent( |
| const WebInputEvent& event) { |
| DCHECK(blink::WebInputEvent::IsTouchEventType(event.GetType())); |
| const WebTouchEvent* touch_event = static_cast<const WebTouchEvent*>(&event); |
| WebTouchEvent transformed_event = touch_event->FlattenTransform(); |
| |
| LocalFrameView& parent = ParentFrameView(); |
| for (unsigned i = 0; i < transformed_event.touches_length; ++i) { |
| WebFloatPoint absolute_location = |
| transformed_event.touches[i].PositionInWidget(); |
| |
| // Translate the root frame position to content coordinates. |
| absolute_location = parent.ConvertFromRootFrame(absolute_location); |
| |
| FloatPoint local_point = element_->GetLayoutObject()->AbsoluteToLocal( |
| absolute_location, kUseTransforms); |
| transformed_event.touches[i].SetPositionInWidget(local_point); |
| } |
| return transformed_event; |
| } |
| |
| WebCoalescedInputEvent WebPluginContainerImpl::TransformCoalescedTouchEvent( |
| const WebCoalescedInputEvent& coalesced_event) { |
| WebCoalescedInputEvent transformed_event( |
| TransformTouchEvent(coalesced_event.Event()), |
| std::vector<const WebInputEvent*>()); |
| for (size_t i = 0; i < coalesced_event.CoalescedEventSize(); ++i) { |
| transformed_event.AddCoalescedEvent( |
| TransformTouchEvent(coalesced_event.CoalescedEvent(i))); |
| } |
| return transformed_event; |
| } |
| |
| void WebPluginContainerImpl::HandleTouchEvent(TouchEvent* event) { |
| switch (touch_event_request_type_) { |
| case kTouchEventRequestTypeNone: |
| return; |
| case kTouchEventRequestTypeRaw: |
| case kTouchEventRequestTypeRawLowLatency: { |
| if (!event->NativeEvent()) |
| return; |
| |
| if (event->type() == EventTypeNames::touchstart) |
| FocusPlugin(); |
| |
| WebCoalescedInputEvent transformed_event = |
| TransformCoalescedTouchEvent(*event->NativeEvent()); |
| |
| 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(); |
| FloatPoint local_point = element_->GetLayoutObject()->AbsoluteToLocal( |
| absolute_root_frame_location, kUseTransforms); |
| translated_event.FlattenTransform(); |
| translated_event.SetPositionInWidget(local_point); |
| |
| WebCursorInfo cursor_info; |
| if (web_plugin_->HandleInputEvent(WebCoalescedInputEvent(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(&ParentFrameView(), |
| element_->GetLayoutObject(), *event); |
| if (web_event.GetType() == WebInputEvent::kUndefined) |
| return; |
| |
| WebCursorInfo cursor_info; |
| if (web_plugin_->HandleInputEvent(WebCoalescedInputEvent(web_event), |
| cursor_info) != |
| WebInputEventResult::kNotHandled) |
| event->SetDefaultHandled(); |
| } |
| |
| void WebPluginContainerImpl::FocusPlugin() { |
| LocalFrame* frame = element_->GetDocument().GetFrame(); |
| DCHECK(is_attached_ && frame && frame->GetPage()); |
| frame->GetPage()->GetFocusController().SetFocusedElement(element_, frame); |
| } |
| |
| 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 |
| // LayoutEmbeddedContent::UpdateGeometry. To remove the lossy effect of |
| // rounding off, use contentBoxRect directly. |
| LayoutRect unclipped_absolute_rect(box->ContentBoxRect()); |
| box->MapToVisualRectInAncestorSpace(root_view, unclipped_absolute_rect); |
| unclipped_absolute_rect = |
| box->View()->GetFrameView()->DocumentToFrame(unclipped_absolute_rect); |
| |
| // The frameRect is already in absolute space of the local frame to the |
| // plugin so map it up to the root frame. |
| window_rect = FrameRect(); |
| LayoutRect layout_window_rect = |
| LayoutRect(element_->GetDocument() |
| .View() |
| ->GetLayoutView() |
| ->LocalToAbsoluteQuad(FloatQuad(FloatRect(window_rect)), |
| kTraverseDocumentBoundaries) |
| .BoundingBox()); |
| |
| 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().GetLayoutView()) { |
| // 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 |