blob: 77ad9591f422184bfe6e89fb263272591017b1b9 [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 "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
#include "build/build_config.h"
#include "third_party/blink/public/common/input/web_input_event.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_drag_data.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/sanitize_script_errors.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/dom_node_ids.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/core/scroll/scroll_animator_base.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.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/graphics/paint/scroll_hit_test_display_item.h"
#include "third_party/blink/renderer/platform/heap/heap.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/wtf/assertions.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/mojom/cursor_type.mojom-blink.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(!IsAttached());
SetAttached(true);
SetParentVisible(true);
}
void WebPluginContainerImpl::DetachFromLayout() {
DCHECK(IsAttached());
SetParentVisible(false);
SetAttached(false);
}
void WebPluginContainerImpl::UpdateAllLifecyclePhases() {
if (!web_plugin_)
return;
web_plugin_->UpdateAllLifecyclePhases(DocumentUpdateReason::kPlugin);
}
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.Intersects(FrameRect()))
return;
if (WantsWheelEvents()) {
ScrollHitTestDisplayItem::Record(
context, *GetLayoutEmbeddedContent(), DisplayItem::kPluginScrollHitTest,
nullptr, GetLayoutEmbeddedContent()->FirstFragment().VisualRect());
}
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && layer_) {
layer_->SetBounds(gfx::Size(Size()));
layer_->SetIsDrawable(true);
layer_->SetHitTestable(true);
auto offset = GetLayoutEmbeddedContent()->ReplacedContentRect().offset;
// When compositing is after paint, composited plugins should have their
// layers inserted rather than invoking WebPlugin::paint.
RecordForeignLayer(context, *element_->GetLayoutObject(),
DisplayItem::kForeignLayerPlugin, layer_,
FloatPoint(offset));
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());
cc::PaintCanvas* 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 = 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 (!IsAttached())
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(PhysicalRect(dirty_rect));
}
void WebPluginContainerImpl::SetFocused(bool focused,
mojom::blink::FocusType focus_type) {
web_plugin_->UpdateFocus(focused, focus_type);
}
bool WebPluginContainerImpl::IsErrorplaceholder() {
if (!web_plugin_)
return false;
return web_plugin_->IsErrorPlaceholder();
}
void WebPluginContainerImpl::Show() {
SetSelfVisible(true);
web_plugin_->UpdateVisibility(true);
}
void WebPluginContainerImpl::Hide() {
SetSelfVisible(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 (auto* mouse_event = DynamicTo<MouseEvent>(event))
HandleMouseEvent(*mouse_event);
else if (auto* wheel_event = DynamicTo<WheelEvent>(event))
HandleWheelEvent(*wheel_event);
else if (auto* keyboard_event = DynamicTo<KeyboardEvent>(event))
HandleKeyboardEvent(*keyboard_event);
else if (auto* touch_event = DynamicTo<TouchEvent>(event))
HandleTouchEvent(*touch_event);
else if (auto* gesture_event = DynamicTo<GestureEvent>(event))
HandleGestureEvent(*gesture_event);
else if (IsA<DragEvent>(event) && web_plugin_->CanProcessDrag())
HandleDragEvent(To<DragEvent>(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::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::ParentVisibleChanged() {
// 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 is ignored. This
// function is called when the plugin eventually gets a parent.
if (web_plugin_ && IsSelfVisible())
web_plugin_->UpdateVisibility(IsVisible());
}
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)
return;
frame->GetFindInPage()->SetPluginFindHandler(this);
}
void WebPluginContainerImpl::ReportFindInPageMatchCount(int identifier,
int total,
bool final_update) {
WebLocalFrameImpl* frame =
WebLocalFrameImpl::FromFrame(element_->GetDocument().GetFrame());
if (!frame)
return;
frame->GetFindInPage()->ReportFindInPageMatchCount(identifier, total,
final_update);
}
void WebPluginContainerImpl::ReportFindInPageSelection(int identifier,
int index) {
WebLocalFrameImpl* frame =
WebLocalFrameImpl::FromFrame(element_->GetDocument().GetFrame());
if (!frame)
return;
frame->GetFindInPage()->ReportFindInPageSelection(
identifier, index, gfx::Rect(), false /* final_update */);
}
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;
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::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();
cc::PaintCanvas* 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;
LocalFrame* frame = element_->GetDocument().GetFrame();
frame->GetSystemClipboard()->WriteHTML(
web_plugin_->SelectionAsMarkup(), KURL(), web_plugin_->SelectionAsText());
frame->GetSystemClipboard()->CommitWrite();
}
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,
uint64_t loaded,
uint64_t total,
const WebString& url) {
ProgressEvent* event;
if (url.IsEmpty()) {
event = ProgressEvent::Create(type, length_computable, loaded, total);
} else {
event = MakeGarbageCollected<ResourceProgressEvent>(type, length_computable,
loaded, total, url);
}
element_->DispatchEvent(*event);
}
void WebPluginContainerImpl::EnqueueMessageEvent(
const WebDOMMessageEvent& event) {
if (!element_->GetExecutionContext())
return;
element_->EnqueueEvent(*event, TaskType::kInternalDefault);
}
void WebPluginContainerImpl::Invalidate() {
InvalidateRect(IntRect(0, 0, Size().Width(), Size().Height()));
}
void WebPluginContainerImpl::InvalidateRect(const WebRect& rect) {
InvalidateRect(static_cast<IntRect>(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 (!IsAttached())
return;
IntRect window_rect, clip_rect, unobscured_rect;
CalculateGeometry(window_rect, clip_rect, unobscured_rect);
web_plugin_->UpdateGeometry(window_rect, clip_rect, unobscured_rect,
IsSelfVisible());
}
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(),
DecodeURLMode::kUTF8OrIsomorphic);
if (!element_->GetDocument().GetContentSecurityPolicy()->AllowInline(
ContentSecurityPolicy::InlineType::kNavigation, element_, script,
String() /* nonce */, element_->GetDocument().Url(),
OrdinalNumber())) {
return WebString();
}
script = script.Substring(strlen("javascript:"));
if (popups_allowed)
LocalFrame::NotifyUserActivation(frame);
v8::HandleScope handle_scope(ToIsolate(frame));
v8::Local<v8::Value> result =
frame->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
ScriptSourceCode(script, ScriptSourceLocationType::kJavascriptUrl),
KURL(), SanitizeScriptErrors::kSanitize);
// 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());
Frame* target_frame =
frame->Tree().FindOrCreateFrameForNavigation(frame_request, target).frame;
if (target_frame)
target_frame->Navigate(frame_request, WebFrameLoadType::kStandard);
}
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 (!IsAttached() || !element_)
return false;
LocalFrame* frame = element_->GetDocument().GetFrame();
if (!frame)
return false;
IntRect frame_rect = rect;
frame_rect.MoveBy(Location());
HitTestLocation location((PhysicalRect(frame_rect)));
HitTestResult result = frame->GetEventHandler().HitTestResultAtLocation(
location, 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 (IsAttached()) {
LocalFrameView* frame_view = element_->GetDocument().GetFrame()->View();
scrolling_coordinator->NotifyGeometryChanged(frame_view);
// Scroll hit test display items depend on wheel events. The scroll
// hit test display items paint in the background phase.
GetLayoutEmbeddedContent()->SetBackgroundNeedsFullPaintInvalidation();
}
}
}
}
gfx::Point WebPluginContainerImpl::RootFrameToLocalPoint(
const gfx::Point& point_in_root_frame) {
gfx::Point point_in_content =
ParentFrameView()->ConvertFromRootFrame(IntPoint(point_in_root_frame));
return RoundedIntPoint(element_->GetLayoutObject()->AbsoluteToLocalPoint(
PhysicalOffset(point_in_content)));
}
gfx::Point WebPluginContainerImpl::LocalToRootFramePoint(
const gfx::Point& point_in_local) {
IntPoint absolute_point =
RoundedIntPoint(element_->GetLayoutObject()->LocalToAbsolutePoint(
PhysicalOffset(point_in_local)));
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.
ReportGeometry();
WrappedResourceResponse url_response(response);
web_plugin_->DidReceiveResponse(url_response);
}
void WebPluginContainerImpl::DidReceiveData(const char* data,
size_t 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() const {
return wants_wheel_events_;
}
// Private methods -------------------------------------------------------------
WebPluginContainerImpl::WebPluginContainerImpl(HTMLPlugInElement& element,
WebPlugin* web_plugin)
: EmbeddedContentView(IntRect()),
ExecutionContextClient(element.GetDocument().GetFrame()),
element_(element),
web_plugin_(web_plugin),
layer_(nullptr),
touch_event_request_type_(kTouchEventRequestTypeNone),
prevent_contents_opaque_changes_(false),
wants_wheel_events_(false) {}
WebPluginContainerImpl::~WebPluginContainerImpl() {
// The plugin container must have been disposed of by now.
DCHECK(!web_plugin_);
}
LocalFrameView* WebPluginContainerImpl::ParentFrameView() const {
DCHECK(IsAttached());
return element_->GetDocument().GetFrame()->View();
}
LayoutEmbeddedContent* WebPluginContainerImpl::GetLayoutEmbeddedContent()
const {
return element_->GetLayoutEmbeddedContent();
}
void WebPluginContainerImpl::Dispose() {
SetAttached(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;
}
layer_ = nullptr;
}
void WebPluginContainerImpl::SetFrameRect(const IntRect& rect) {
IntRect old_rect(FrameRect());
EmbeddedContentView::SetFrameRect(rect);
// We need to report every time SetFrameRect is called, even if there is no
// change (if there is a change, FrameRectsChanged will do the reporting).
if (old_rect == FrameRect())
PropagateFrameRects();
}
void WebPluginContainerImpl::Trace(Visitor* visitor) {
visitor->Trace(element_);
ExecutionContextClient::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() == event_type_names::kMousedown)
FocusPlugin();
ui::Cursor cursor(ui::mojom::blink::CursorType::kPointer);
if (web_plugin_ && web_plugin_->HandleInputEvent(
WebCoalescedInputEvent(transformed_event), &cursor) !=
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, &parent->GetFrame().LocalFrameRoot());
}
void WebPluginContainerImpl::HandleDragEvent(MouseEvent& event) {
DCHECK(IsA<DragEvent>(event));
WebDragStatus drag_status = kWebDragStatusUnknown;
if (event.type() == event_type_names::kDragenter)
drag_status = kWebDragStatusEnter;
else if (event.type() == event_type_names::kDragleave)
drag_status = kWebDragStatusLeave;
else if (event.type() == event_type_names::kDragover)
drag_status = kWebDragStatusOver;
else if (event.type() == event_type_names::kDrop)
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());
gfx::PointF drag_screen_location(event.screenX(), event.screenY());
IntPoint location(Location());
gfx::PointF 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) {
FloatPoint absolute_location =
FloatPoint(event.NativeEvent().PositionInRootFrame());
// Translate the root frame position to content coordinates.
absolute_location =
ParentFrameView()->ConvertFromRootFrame(absolute_location);
FloatPoint local_point =
element_->GetLayoutObject()->AbsoluteToLocalFloatPoint(absolute_location);
WebMouseWheelEvent translated_event = event.NativeEvent().FlattenTransform();
translated_event.SetPositionInWidget(local_point.X(), local_point.Y());
ui::Cursor dummy_cursor;
if (web_plugin_->HandleInputEvent(WebCoalescedInputEvent(translated_event),
&dummy_cursor) !=
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();
ui::Cursor dummy_cursor;
if (web_plugin_->HandleInputEvent(WebCoalescedInputEvent(web_event),
&dummy_cursor) !=
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) {
FloatPoint absolute_location =
FloatPoint(transformed_event.touches[i].PositionInWidget());
// Translate the root frame position to content coordinates.
absolute_location = parent->ConvertFromRootFrame(absolute_location);
FloatPoint local_point =
element_->GetLayoutObject()->AbsoluteToLocalFloatPoint(
absolute_location);
transformed_event.touches[i].SetPositionInWidget(local_point);
}
return transformed_event;
}
WebCoalescedInputEvent WebPluginContainerImpl::TransformCoalescedTouchEvent(
const WebCoalescedInputEvent& coalesced_event) {
WebCoalescedInputEvent transformed_event(
TransformTouchEvent(coalesced_event.Event()), {}, {});
for (size_t i = 0; i < coalesced_event.CoalescedEventSize(); ++i) {
transformed_event.AddCoalescedEvent(
TransformTouchEvent(coalesced_event.CoalescedEvent(i)));
}
for (size_t i = 0; i < coalesced_event.PredictedEventSize(); ++i) {
transformed_event.AddPredictedEvent(
TransformTouchEvent(coalesced_event.PredictedEvent(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() == event_type_names::kTouchstart)
FocusPlugin();
WebCoalescedInputEvent transformed_event =
TransformCoalescedTouchEvent(*event.NativeEvent());
ui::Cursor dummy_cursor;
if (web_plugin_->HandleInputEvent(transformed_event, &dummy_cursor) !=
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();
gfx::PointF absolute_root_frame_location =
event.NativeEvent().PositionInRootFrame();
FloatPoint local_point =
element_->GetLayoutObject()->AbsoluteToLocalFloatPoint(
FloatPoint(absolute_root_frame_location));
translated_event.FlattenTransform();
translated_event.SetPositionInWidget(local_point);
ui::Cursor dummy_cursor;
if (web_plugin_->HandleInputEvent(WebCoalescedInputEvent(translated_event),
&dummy_cursor) !=
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;
ui::Cursor dummy_cursor;
if (web_plugin_->HandleInputEvent(WebCoalescedInputEvent(web_event),
&dummy_cursor) !=
WebInputEventResult::kNotHandled)
event.SetDefaultHandled();
}
void WebPluginContainerImpl::FocusPlugin() {
LocalFrame* frame = element_->GetDocument().GetFrame();
DCHECK(IsAttached() && 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.
PhysicalRect unclipped_root_frame_rect = box->PhysicalContentBoxRect();
box->MapToVisualRectInAncestorSpace(root_view, unclipped_root_frame_rect);
unclipped_root_frame_rect =
root_view->GetFrameView()->DocumentToFrame(unclipped_root_frame_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();
PhysicalRect layout_window_rect =
element_->GetDocument().View()->GetLayoutView()->LocalToAbsoluteRect(
PhysicalRect(window_rect), kTraverseDocumentBoundaries);
window_rect = PixelSnappedIntRect(layout_window_rect);
PhysicalRect clipped_root_frame_rect = unclipped_root_frame_rect;
clipped_root_frame_rect.Intersect(PhysicalRect(
PhysicalOffset(), PhysicalSize(root_view->GetFrameView()->Size())));
unclipped_int_local_rect = EnclosingIntRect(box->AbsoluteToLocalRect(
unclipped_root_frame_rect, kTraverseDocumentBoundaries));
// As a performance optimization, map the clipped rect separately if is
// different than the unclipped rect.
if (clipped_root_frame_rect != unclipped_root_frame_rect) {
clipped_local_rect = EnclosingIntRect(box->AbsoluteToLocalRect(
clipped_root_frame_rect, kTraverseDocumentBoundaries));
} else {
clipped_local_rect = unclipped_int_local_rect;
}
}
void WebPluginContainerImpl::CalculateGeometry(IntRect& window_rect,
IntRect& clip_rect,
IntRect& unobscured_rect) {
// GetDocument().LayoutView() can be null when we receive messages from the
// plugins while we are destroying a frame.
// TODO: Can we just check element_->GetDocument().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