blob: ded7dd5f9d5b083c9f7d72fdf590fc50ea730d4d [file] [log] [blame]
/*
* Copyright (C) 2014 Google Inc. 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/frame/web_frame_widget_impl.h"
#include <memory>
#include <utility>
#include "base/auto_reset.h"
#include "base/optional.h"
#include "build/build_config.h"
#include "cc/layers/picture_layer.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_scroll_into_view_params.h"
#include "third_party/blink/public/web/web_autofill_client.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_plugin.h"
#include "third_party/blink/public/web/web_range.h"
#include "third_party/blink/public/web/web_widget_client.h"
#include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
#include "third_party/blink/renderer/core/editing/plain_text_range.h"
#include "third_party/blink/renderer/core/editing/selection_template.h"
#include "third_party/blink/renderer/core/events/current_input_event.h"
#include "third_party/blink/renderer/core/events/web_input_event_conversion.h"
#include "third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h"
#include "third_party/blink/renderer/core/exported/web_page_popup_impl.h"
#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
#include "third_party/blink/renderer/core/exported/web_remote_frame_impl.h"
#include "third_party/blink/renderer/core/exported/web_view_impl.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/remote_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/frame/web_view_frame_widget.h"
#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
#include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/context_menu_controller.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/page_popup.h"
#include "third_party/blink/renderer/core/page/pointer_lock_controller.h"
#include "third_party/blink/renderer/core/page/validation_message_client.h"
#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
#include "third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h"
#include "third_party/blink/renderer/platform/graphics/color.h"
#include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h"
#include "third_party/blink/renderer/platform/keyboard_codes.h"
namespace blink {
namespace {
const int kCaretPadding = 10;
const float kIdealPaddingRatio = 0.3f;
// Returns a rect which is offset and scaled accordingly to |base_rect|'s
// location and size.
FloatRect NormalizeRect(const IntRect& to_normalize, const IntRect& base_rect) {
FloatRect result(to_normalize);
result.SetLocation(
FloatPoint(to_normalize.Location() + (-base_rect.Location())));
result.Scale(1.0 / base_rect.Width(), 1.0 / base_rect.Height());
return result;
}
} // namespace
// WebFrameWidget ------------------------------------------------------------
WebFrameWidget* WebFrameWidget::CreateForMainFrame(WebWidgetClient* client,
WebLocalFrame* main_frame) {
DCHECK(client) << "A valid WebWidgetClient must be supplied.";
DCHECK(!main_frame->Parent()); // This is the main frame.
// Grabs the WebViewImpl associated with the |main_frame|, which will then
// be wrapped by the WebViewFrameWidget, with calls being forwarded to the
// |main_frame|'s WebViewImpl.
// Note: this can't DCHECK that the view's main frame points to
// |main_frame|, as provisional frames violate this precondition.
WebLocalFrameImpl& main_frame_impl = ToWebLocalFrameImpl(*main_frame);
DCHECK(main_frame_impl.ViewImpl());
WebViewImpl& web_view_impl = *main_frame_impl.ViewImpl();
// Note: this isn't a leak, as the object has a self-reference that the
// caller needs to release by calling Close().
// TODO(dcheng): Remove the special bridge class for main frame widgets.
WebFrameWidgetBase* widget =
MakeGarbageCollected<WebViewFrameWidget>(*client, web_view_impl);
widget->BindLocalRoot(*main_frame);
return widget;
}
WebFrameWidget* WebFrameWidget::CreateForChildLocalRoot(
WebWidgetClient* client,
WebLocalFrame* local_root) {
DCHECK(client) << "A valid WebWidgetClient must be supplied.";
DCHECK(local_root->Parent()); // This is not the main frame.
// Frames whose direct ancestor is a remote frame are local roots. Verify this
// is one. Other frames should be using the widget for their nearest local
// root.
DCHECK(local_root->Parent()->IsWebRemoteFrame());
// Note: this isn't a leak, as the object has a self-reference that the
// caller needs to release by calling Close().
WebFrameWidgetBase* widget = WebFrameWidgetImpl::Create(*client);
widget->BindLocalRoot(*local_root);
return widget;
}
WebFrameWidgetImpl* WebFrameWidgetImpl::Create(WebWidgetClient& client) {
// Pass the WebFrameWidgetImpl's self-reference from SelfKeepAlive to the
// caller.
return MakeGarbageCollected<WebFrameWidgetImpl>(client);
}
WebFrameWidgetImpl::WebFrameWidgetImpl(WebWidgetClient& client)
: WebFrameWidgetBase(client),
mutator_dispatcher_(nullptr),
layer_tree_view_(nullptr),
root_layer_(nullptr),
root_graphics_layer_(nullptr),
is_accelerated_compositing_active_(false),
layer_tree_view_closed_(false),
suppress_next_keypress_event_(false),
ime_accept_events_(true),
self_keep_alive_(this) {}
WebFrameWidgetImpl::~WebFrameWidgetImpl() = default;
void WebFrameWidgetImpl::Trace(blink::Visitor* visitor) {
visitor->Trace(mouse_capture_node_);
WebFrameWidgetBase::Trace(visitor);
}
// WebWidget ------------------------------------------------------------------
void WebFrameWidgetImpl::Close() {
WebFrameWidgetBase::Close();
mutator_dispatcher_ = nullptr;
layer_tree_view_ = nullptr;
root_layer_ = nullptr;
root_graphics_layer_ = nullptr;
animation_host_ = nullptr;
self_keep_alive_.Clear();
}
WebSize WebFrameWidgetImpl::Size() {
return size_ ? *size_ : WebSize();
}
void WebFrameWidgetImpl::Resize(const WebSize& new_size) {
if (size_ && *size_ == new_size)
return;
if (did_suspend_parsing_) {
did_suspend_parsing_ = false;
LocalRootImpl()->GetFrame()->Loader().GetDocumentLoader()->ResumeParser();
}
LocalFrameView* view = LocalRootImpl()->GetFrameView();
if (!view)
return;
size_ = new_size;
UpdateMainFrameLayoutSize();
view->Resize(*size_);
// FIXME: In WebViewImpl this layout was a precursor to setting the minimum
// scale limit. It is not clear if this is necessary for frame-level widget
// resize.
if (view->NeedsLayout())
view->UpdateLayout();
// FIXME: Investigate whether this is needed; comment from eseidel suggests
// that this function is flawed.
// TODO(kenrb): It would probably make more sense to check whether lifecycle
// updates are throttled in the root's LocalFrameView, but for OOPIFs that
// doesn't happen. Need to investigate if OOPIFs can be throttled during
// load.
if (LocalRootImpl()->GetFrame()->GetDocument()->IsLoadCompleted())
SendResizeEventAndRepaint();
}
void WebFrameWidgetImpl::SendResizeEventAndRepaint() {
// FIXME: This is wrong. The LocalFrameView is responsible sending a
// resizeEvent as part of layout. Layout is also responsible for sending
// invalidations to the embedder. This method and all callers may be wrong. --
// eseidel.
if (LocalRootImpl()->GetFrameView()) {
// Enqueues the resize event.
LocalRootImpl()->GetFrame()->GetDocument()->EnqueueResizeEvent();
}
DCHECK(Client());
if (IsAcceleratedCompositingActive()) {
UpdateLayerTreeViewport();
} else {
WebRect damaged_rect(0, 0, size_->width, size_->height);
Client()->DidInvalidateRect(damaged_rect);
}
}
void WebFrameWidgetImpl::ResizeVisualViewport(const WebSize& new_size) {
if (!LocalRootImpl()) {
// We should figure out why we get here when there is no local root
// (https://crbug.com/792345).
return;
}
// TODO(alexmos, kenrb): resizing behavior such as this should be changed
// to use Page messages. This uses the visual viewport size to set size on
// both the WebViewImpl size and the Page's VisualViewport. If there are
// multiple OOPIFs on a page, this will currently be set redundantly by
// each of them. See https://crbug.com/599688.
View()->MainFrameWidget()->Resize(new_size);
View()->DidUpdateFullscreenSize();
}
void WebFrameWidgetImpl::UpdateMainFrameLayoutSize() {
if (!LocalRootImpl())
return;
LocalFrameView* view = LocalRootImpl()->GetFrameView();
if (!view)
return;
WebSize layout_size = *size_;
view->SetLayoutSize(layout_size);
}
void WebFrameWidgetImpl::DidEnterFullscreen() {
View()->MainFrameWidget()->DidEnterFullscreen();
}
void WebFrameWidgetImpl::DidExitFullscreen() {
View()->MainFrameWidget()->DidExitFullscreen();
}
void WebFrameWidgetImpl::SetSuppressFrameRequestsWorkaroundFor704763Only(
bool suppress_frame_requests) {
GetPage()->Animator().SetSuppressFrameRequestsWorkaroundFor704763Only(
suppress_frame_requests);
}
void WebFrameWidgetImpl::BeginFrame(base::TimeTicks last_frame_time) {
TRACE_EVENT1("blink", "WebFrameWidgetImpl::beginFrame", "frameTime",
last_frame_time);
DCHECK(!last_frame_time.is_null());
if (!LocalRootImpl())
return;
DocumentLifecycle::AllowThrottlingScope throttling_scope(
LocalRootImpl()->GetFrame()->GetDocument()->Lifecycle());
PageWidgetDelegate::Animate(*GetPage(), last_frame_time);
// Animate can cause the local frame to detach.
if (LocalRootImpl())
GetPage()->GetValidationMessageClient().LayoutOverlay();
}
void WebFrameWidgetImpl::RecordEndOfFrameMetrics(
base::TimeTicks frame_begin_time) {
if (!LocalRootImpl())
return;
LocalRootImpl()->GetFrame()->View()->RecordEndOfFrameMetrics(
frame_begin_time);
}
void WebFrameWidgetImpl::UpdateLifecycle(LifecycleUpdate requested_update,
LifecycleUpdateReason reason) {
TRACE_EVENT0("blink", "WebFrameWidgetImpl::updateAllLifecyclePhases");
if (!LocalRootImpl())
return;
DocumentLifecycle::AllowThrottlingScope throttling_scope(
LocalRootImpl()->GetFrame()->GetDocument()->Lifecycle());
PageWidgetDelegate::UpdateLifecycle(*GetPage(), *LocalRootImpl()->GetFrame(),
requested_update, reason);
}
void WebFrameWidgetImpl::PaintContent(cc::PaintCanvas* canvas,
const WebRect& rect) {
// Out-of-process iframes require compositing.
NOTREACHED();
}
void WebFrameWidgetImpl::UpdateLayerTreeViewport() {
if (!GetPage() || !layer_tree_view_)
return;
// Pass the limits even though this is for subframes, as the limits will be
// needed in setting the raster scale.
layer_tree_view_->SetPageScaleFactorAndLimits(
1, View()->MinimumPageScaleFactor(), View()->MaximumPageScaleFactor());
}
void WebFrameWidgetImpl::LayoutAndPaintAsync(base::OnceClosure callback) {
layer_tree_view_->LayoutAndPaintAsync(std::move(callback));
}
void WebFrameWidgetImpl::CompositeAndReadbackAsync(
base::OnceCallback<void(const SkBitmap&)> callback) {
layer_tree_view_->CompositeAndReadbackAsync(std::move(callback));
}
void WebFrameWidgetImpl::ThemeChanged() {
LocalFrameView* view = LocalRootImpl()->GetFrameView();
WebRect damaged_rect(0, 0, size_->width, size_->height);
view->InvalidateRect(damaged_rect);
}
WebHitTestResult WebFrameWidgetImpl::HitTestResultAt(const gfx::Point& point) {
return CoreHitTestResultAt(point);
}
WebInputEventResult WebFrameWidgetImpl::DispatchBufferedTouchEvents() {
if (doing_drag_and_drop_)
return WebInputEventResult::kHandledSuppressed;
if (!GetPage())
return WebInputEventResult::kNotHandled;
if (LocalRootImpl()) {
if (WebDevToolsAgentImpl* devtools = LocalRootImpl()->DevToolsAgentImpl())
devtools->DispatchBufferedTouchEvents();
}
if (IgnoreInputEvents())
return WebInputEventResult::kNotHandled;
return LocalRootImpl()
->GetFrame()
->GetEventHandler()
.DispatchBufferedTouchEvents();
}
WebInputEventResult WebFrameWidgetImpl::HandleInputEvent(
const WebCoalescedInputEvent& coalesced_event) {
const WebInputEvent& input_event = coalesced_event.Event();
TRACE_EVENT1("input", "WebFrameWidgetImpl::handleInputEvent", "type",
WebInputEvent::GetName(input_event.GetType()));
DCHECK(!WebInputEvent::IsTouchEventType(input_event.GetType()));
// If a drag-and-drop operation is in progress, ignore input events.
if (doing_drag_and_drop_)
return WebInputEventResult::kHandledSuppressed;
// Don't handle events once we've started shutting down.
if (!GetPage())
return WebInputEventResult::kNotHandled;
if (LocalRootImpl()) {
if (WebDevToolsAgentImpl* devtools = LocalRootImpl()->DevToolsAgentImpl()) {
if (devtools->HandleInputEvent(input_event))
return WebInputEventResult::kHandledSuppressed;
}
}
// Report the event to be NOT processed by WebKit, so that the browser can
// handle it appropriately.
if (IgnoreInputEvents())
return WebInputEventResult::kNotHandled;
// FIXME: pass event to m_localRoot's WebDevToolsAgentImpl once available.
base::AutoReset<const WebInputEvent*> current_event_change(
&CurrentInputEvent::current_input_event_, &input_event);
DCHECK(Client());
if (Client()->IsPointerLocked() &&
WebInputEvent::IsMouseEventType(input_event.GetType())) {
PointerLockMouseEvent(coalesced_event);
return WebInputEventResult::kHandledSystem;
}
if (mouse_capture_node_ &&
WebInputEvent::IsMouseEventType(input_event.GetType())) {
TRACE_EVENT1("input", "captured mouse event", "type",
input_event.GetType());
// Save m_mouseCaptureNode since mouseCaptureLost() will clear it.
Node* node = mouse_capture_node_;
// Not all platforms call mouseCaptureLost() directly.
if (input_event.GetType() == WebInputEvent::kMouseUp)
MouseCaptureLost();
std::unique_ptr<UserGestureIndicator> gesture_indicator;
AtomicString event_type;
switch (input_event.GetType()) {
case WebInputEvent::kMouseEnter:
event_type = event_type_names::kMouseover;
break;
case WebInputEvent::kMouseMove:
event_type = event_type_names::kMousemove;
break;
case WebInputEvent::kMouseLeave:
event_type = event_type_names::kMouseout;
break;
case WebInputEvent::kMouseDown:
event_type = event_type_names::kMousedown;
gesture_indicator = LocalFrame::NotifyUserActivation(
node->GetDocument().GetFrame(), UserGestureToken::kNewGesture);
mouse_capture_gesture_token_ = gesture_indicator->CurrentToken();
break;
case WebInputEvent::kMouseUp:
event_type = event_type_names::kMouseup;
gesture_indicator = std::make_unique<UserGestureIndicator>(
std::move(mouse_capture_gesture_token_));
break;
default:
NOTREACHED();
}
WebMouseEvent transformed_event =
TransformWebMouseEvent(LocalRootImpl()->GetFrameView(),
static_cast<const WebMouseEvent&>(input_event));
if (LocalFrame* frame = node->GetDocument().GetFrame()) {
frame->GetEventHandler().HandleTargetedMouseEvent(
node, transformed_event, event_type,
TransformWebMouseEventVector(
LocalRootImpl()->GetFrameView(),
coalesced_event.GetCoalescedEventsPointers()),
TransformWebMouseEventVector(
LocalRootImpl()->GetFrameView(),
coalesced_event.GetPredictedEventsPointers()));
}
return WebInputEventResult::kHandledSystem;
}
return PageWidgetDelegate::HandleInputEvent(*this, coalesced_event,
LocalRootImpl()->GetFrame());
}
void WebFrameWidgetImpl::SetCursorVisibilityState(bool is_visible) {
GetPage()->SetIsCursorVisible(is_visible);
}
WebInputMethodController*
WebFrameWidgetImpl::GetActiveWebInputMethodController() const {
WebLocalFrameImpl* local_frame =
WebLocalFrameImpl::FromFrame(FocusedLocalFrameInWidget());
return local_frame ? local_frame->GetInputMethodController() : nullptr;
}
bool WebFrameWidgetImpl::ScrollFocusedEditableElementIntoView() {
Element* element = FocusedElement();
if (!element || !WebElement(element).IsEditable())
return false;
if (!element->GetLayoutObject())
return false;
LayoutRect rect_to_scroll;
WebScrollIntoViewParams params;
GetScrollParamsForFocusedEditableElement(*element, rect_to_scroll, params);
element->GetLayoutObject()->ScrollRectToVisible(rect_to_scroll, params);
return true;
}
void WebFrameWidgetImpl::Initialize() {
}
void WebFrameWidgetImpl::IntrinsicSizingInfoChanged(
const IntrinsicSizingInfo& sizing_info) {
WebIntrinsicSizingInfo web_sizing_info;
web_sizing_info.size = sizing_info.size;
web_sizing_info.aspect_ratio = sizing_info.aspect_ratio;
web_sizing_info.has_width = sizing_info.has_width;
web_sizing_info.has_height = sizing_info.has_height;
Client()->IntrinsicSizingInfoChanged(web_sizing_info);
}
base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>
WebFrameWidgetImpl::EnsureCompositorMutatorDispatcher(
scoped_refptr<base::SingleThreadTaskRunner>* mutator_task_runner) {
if (!mutator_task_runner_) {
layer_tree_view_->SetMutatorClient(
AnimationWorkletMutatorDispatcherImpl::CreateCompositorThreadClient(
&mutator_dispatcher_, &mutator_task_runner_));
}
DCHECK(mutator_task_runner_);
*mutator_task_runner = mutator_task_runner_;
return mutator_dispatcher_;
}
void WebFrameWidgetImpl::ApplyViewportChanges(const ApplyViewportChangesArgs&) {
}
void WebFrameWidgetImpl::MouseCaptureLost() {
TRACE_EVENT_ASYNC_END0("input", "capturing mouse", this);
mouse_capture_node_ = nullptr;
}
void WebFrameWidgetImpl::SetFocus(bool enable) {
if (enable)
GetPage()->GetFocusController().SetActive(true);
GetPage()->GetFocusController().SetFocused(enable);
if (enable) {
LocalFrame* focused_frame = GetPage()->GetFocusController().FocusedFrame();
if (focused_frame) {
Element* element = focused_frame->GetDocument()->FocusedElement();
if (element && focused_frame->Selection()
.ComputeVisibleSelectionInDOMTreeDeprecated()
.IsNone()) {
// If the selection was cleared while the WebView was not
// focused, then the focus element shows with a focus ring but
// no caret and does respond to keyboard inputs.
focused_frame->GetDocument()->UpdateStyleAndLayoutTree();
if (element->IsTextControl()) {
element->UpdateFocusAppearance(SelectionBehaviorOnFocus::kRestore);
} else if (HasEditableStyle(*element)) {
// updateFocusAppearance() selects all the text of
// contentseditable DIVs. So we set the selection explicitly
// instead. Note that this has the side effect of moving the
// caret back to the beginning of the text.
Position position(element, 0);
focused_frame->Selection().SetSelectionAndEndTyping(
SelectionInDOMTree::Builder().Collapse(position).Build());
}
}
}
ime_accept_events_ = true;
} else {
LocalFrame* focused_frame = FocusedLocalFrameInWidget();
if (focused_frame) {
// Finish an ongoing composition to delete the composition node.
if (focused_frame->GetInputMethodController().HasComposition()) {
// TODO(editing-dev): The use of
// updateStyleAndLayoutIgnorePendingStylesheets needs to be audited.
// See http://crbug.com/590369 for more details.
focused_frame->GetDocument()
->UpdateStyleAndLayoutIgnorePendingStylesheets();
focused_frame->GetInputMethodController().FinishComposingText(
InputMethodController::kKeepSelection);
}
ime_accept_events_ = false;
}
}
}
bool WebFrameWidgetImpl::SelectionBounds(WebRect& anchor_web,
WebRect& focus_web) const {
const LocalFrame* local_frame = FocusedLocalFrameInWidget();
if (!local_frame)
return false;
IntRect anchor;
IntRect focus;
if (!local_frame->Selection().ComputeAbsoluteBounds(anchor, focus))
return false;
// FIXME: This doesn't apply page scale. This should probably be contents to
// viewport. crbug.com/459293.
anchor_web = local_frame->View()->ConvertToRootFrame(anchor);
focus_web = local_frame->View()->ConvertToRootFrame(focus);
return true;
}
bool WebFrameWidgetImpl::IsAcceleratedCompositingActive() const {
return is_accelerated_compositing_active_;
}
void WebFrameWidgetImpl::WillCloseLayerTreeView() {
if (layer_tree_view_) {
GetPage()->WillCloseLayerTreeView(*layer_tree_view_,
LocalRootImpl()->GetFrame()->View());
}
SetIsAcceleratedCompositingActive(false);
mutator_dispatcher_ = nullptr;
layer_tree_view_ = nullptr;
animation_host_ = nullptr;
layer_tree_view_closed_ = true;
}
void WebFrameWidgetImpl::SetRemoteViewportIntersection(
const WebRect& viewport_intersection,
bool occluded_or_obscured) {
// Remote viewports are only applicable to local frames with remote ancestors.
DCHECK(LocalRootImpl()->Parent() &&
LocalRootImpl()->Parent()->IsWebRemoteFrame() &&
LocalRootImpl()->GetFrame());
LocalRootImpl()->GetFrame()->SetViewportIntersectionFromParent(
viewport_intersection, occluded_or_obscured);
}
void WebFrameWidgetImpl::SetIsInert(bool inert) {
DCHECK(LocalRootImpl()->Parent());
DCHECK(LocalRootImpl()->Parent()->IsWebRemoteFrame());
LocalRootImpl()->GetFrame()->SetIsInert(inert);
}
void WebFrameWidgetImpl::SetInheritedEffectiveTouchAction(
TouchAction touch_action) {
DCHECK(LocalRootImpl()->Parent());
DCHECK(LocalRootImpl()->Parent()->IsWebRemoteFrame());
LocalRootImpl()->GetFrame()->SetInheritedEffectiveTouchAction(touch_action);
}
void WebFrameWidgetImpl::UpdateRenderThrottlingStatus(bool is_throttled,
bool subtree_throttled) {
DCHECK(LocalRootImpl()->Parent());
DCHECK(LocalRootImpl()->Parent()->IsWebRemoteFrame());
LocalRootImpl()->GetFrameView()->UpdateRenderThrottlingStatus(
is_throttled, subtree_throttled);
}
WebURL WebFrameWidgetImpl::GetURLForDebugTrace() {
WebFrame* main_frame = View()->MainFrame();
if (main_frame->IsWebLocalFrame())
return main_frame->ToWebLocalFrame()->GetDocument().Url();
return {};
}
void WebFrameWidgetImpl::HandleMouseLeave(LocalFrame& main_frame,
const WebMouseEvent& event) {
// FIXME: WebWidget doesn't have the method below.
// m_client->setMouseOverURL(WebURL());
PageWidgetEventHandler::HandleMouseLeave(main_frame, event);
}
void WebFrameWidgetImpl::HandleMouseDown(LocalFrame& main_frame,
const WebMouseEvent& event) {
WebViewImpl* view_impl = View();
// If there is a popup open, close it as the user is clicking on the page
// (outside of the popup). We also save it so we can prevent a click on an
// element from immediately reopening the same popup.
scoped_refptr<WebPagePopupImpl> page_popup;
if (event.button == WebMouseEvent::Button::kLeft) {
page_popup = view_impl->GetPagePopup();
view_impl->CancelPagePopup();
}
// Take capture on a mouse down on a plugin so we can send it mouse events.
// If the hit node is a plugin but a scrollbar is over it don't start mouse
// capture because it will interfere with the scrollbar receiving events.
LayoutPoint point(event.PositionInWidget().x, event.PositionInWidget().y);
if (event.button == WebMouseEvent::Button::kLeft) {
HitTestLocation location(
LocalRootImpl()->GetFrameView()->ConvertFromRootFrame(point));
HitTestResult result(
LocalRootImpl()->GetFrame()->GetEventHandler().HitTestResultAtLocation(
location));
result.SetToShadowHostIfInRestrictedShadowRoot();
Node* hit_node = result.InnerNode();
if (!result.GetScrollbar() && hit_node && hit_node->GetLayoutObject() &&
hit_node->GetLayoutObject()->IsEmbeddedObject()) {
mouse_capture_node_ = hit_node;
TRACE_EVENT_ASYNC_BEGIN0("input", "capturing mouse", this);
}
}
PageWidgetEventHandler::HandleMouseDown(main_frame, event);
if (event.button == WebMouseEvent::Button::kLeft && mouse_capture_node_) {
mouse_capture_gesture_token_ =
main_frame.GetEventHandler().TakeLastMouseDownGestureToken();
}
if (view_impl->GetPagePopup() && page_popup &&
view_impl->GetPagePopup()->HasSamePopupClient(page_popup.get())) {
// That click triggered a page popup that is the same as the one we just
// closed. It needs to be closed.
view_impl->CancelPagePopup();
}
// Dispatch the contextmenu event regardless of if the click was swallowed.
if (!GetPage()->GetSettings().GetShowContextMenuOnMouseUp()) {
#if defined(OS_MACOSX)
if (event.button == WebMouseEvent::Button::kRight ||
(event.button == WebMouseEvent::Button::kLeft &&
event.GetModifiers() & WebMouseEvent::kControlKey))
MouseContextMenu(event);
#else
if (event.button == WebMouseEvent::Button::kRight)
MouseContextMenu(event);
#endif
}
}
void WebFrameWidgetImpl::MouseContextMenu(const WebMouseEvent& event) {
GetPage()->GetContextMenuController().ClearContextMenu();
WebMouseEvent transformed_event =
TransformWebMouseEvent(LocalRootImpl()->GetFrameView(), event);
transformed_event.menu_source_type = kMenuSourceMouse;
IntPoint position_in_root_frame =
FlooredIntPoint(transformed_event.PositionInRootFrame());
// Find the right target frame. See issue 1186900.
HitTestResult result = HitTestResultForRootFramePos(position_in_root_frame);
Frame* target_frame;
if (result.InnerNodeOrImageMapImage())
target_frame = result.InnerNodeOrImageMapImage()->GetDocument().GetFrame();
else
target_frame = GetPage()->GetFocusController().FocusedOrMainFrame();
// This will need to be changed to a nullptr check when focus control
// is refactored, at which point focusedOrMainFrame will never return a
// RemoteFrame.
// See https://crbug.com/341918.
if (!target_frame->IsLocalFrame())
return;
LocalFrame* target_local_frame = ToLocalFrame(target_frame);
{
ContextMenuAllowedScope scope;
target_local_frame->GetEventHandler().SendContextMenuEvent(
transformed_event, nullptr);
}
// Actually showing the context menu is handled by the ContextMenuClient
// implementation...
}
void WebFrameWidgetImpl::HandleMouseUp(LocalFrame& main_frame,
const WebMouseEvent& event) {
PageWidgetEventHandler::HandleMouseUp(main_frame, event);
if (GetPage()->GetSettings().GetShowContextMenuOnMouseUp()) {
// Dispatch the contextmenu event regardless of if the click was swallowed.
// On Mac/Linux, we handle it on mouse down, not up.
if (event.button == WebMouseEvent::Button::kRight)
MouseContextMenu(event);
}
}
WebInputEventResult WebFrameWidgetImpl::HandleMouseWheel(
LocalFrame& frame,
const WebMouseWheelEvent& event) {
View()->CancelPagePopup();
return PageWidgetEventHandler::HandleMouseWheel(frame, event);
}
WebInputEventResult WebFrameWidgetImpl::HandleGestureEvent(
const WebGestureEvent& event) {
DCHECK(Client());
WebInputEventResult event_result = WebInputEventResult::kNotHandled;
bool event_cancelled = false;
base::Optional<ContextMenuAllowedScope> maybe_context_menu_scope;
WebViewImpl* view_impl = View();
switch (event.GetType()) {
case WebInputEvent::kGestureScrollBegin:
case WebInputEvent::kGestureScrollEnd:
case WebInputEvent::kGestureScrollUpdate:
case WebInputEvent::kGestureTap:
case WebInputEvent::kGestureTapUnconfirmed:
case WebInputEvent::kGestureTapDown:
// Touch pinch zoom and scroll on the page (outside of a popup) must hide
// the popup. In case of a touch scroll or pinch zoom, this function is
// called with GestureTapDown rather than a GSB/GSU/GSE or GPB/GPU/GPE.
// WebViewImpl takes additional steps to avoid the following GestureTap
// from re-opening the popup being closed here, but since GestureTap will
// unconditionally close the current popup here, it is not used/needed.
// TODO(wjmaclean): We should maybe mirror what WebViewImpl does, the
// HandleGestureEvent() needs to happen inside or before the GestureTap
// case to do so.
View()->CancelPagePopup();
break;
case WebInputEvent::kGestureTapCancel:
case WebInputEvent::kGestureShowPress:
break;
case WebInputEvent::kGestureDoubleTap:
if (GetPage()->GetChromeClient().DoubleTapToZoomEnabled() &&
view_impl->MinimumPageScaleFactor() !=
view_impl->MaximumPageScaleFactor()) {
LocalFrame* frame = LocalRootImpl()->GetFrame();
WebGestureEvent scaled_event =
TransformWebGestureEvent(frame->View(), event);
IntPoint pos_in_local_frame_root =
FlooredIntPoint(scaled_event.PositionInRootFrame());
WebRect block_bounds =
ComputeBlockBound(pos_in_local_frame_root, false);
// This sends the tap point and bounds to the main frame renderer via
// the browser, where their coordinates will be transformed into the
// main frame's coordinate space.
Client()->AnimateDoubleTapZoomInMainFrame(pos_in_local_frame_root,
block_bounds);
}
event_result = WebInputEventResult::kHandledSystem;
Client()->DidHandleGestureEvent(event, event_cancelled);
return event_result;
break;
case WebInputEvent::kGestureTwoFingerTap:
case WebInputEvent::kGestureLongPress:
case WebInputEvent::kGestureLongTap:
GetPage()->GetContextMenuController().ClearContextMenu();
maybe_context_menu_scope.emplace();
break;
default:
NOTREACHED();
}
LocalFrame* frame = LocalRootImpl()->GetFrame();
WebGestureEvent scaled_event = TransformWebGestureEvent(frame->View(), event);
event_result = frame->GetEventHandler().HandleGestureEvent(scaled_event);
Client()->DidHandleGestureEvent(event, event_cancelled);
return event_result;
}
PageWidgetEventHandler* WebFrameWidgetImpl::GetPageWidgetEventHandler() {
return this;
}
WebInputEventResult WebFrameWidgetImpl::HandleKeyEvent(
const WebKeyboardEvent& event) {
DCHECK((event.GetType() == WebInputEvent::kRawKeyDown) ||
(event.GetType() == WebInputEvent::kKeyDown) ||
(event.GetType() == WebInputEvent::kKeyUp));
// Please refer to the comments explaining the m_suppressNextKeypressEvent
// member.
// The m_suppressNextKeypressEvent is set if the KeyDown is handled by
// Webkit. A keyDown event is typically associated with a keyPress(char)
// event and a keyUp event. We reset this flag here as this is a new keyDown
// event.
suppress_next_keypress_event_ = false;
Frame* focused_frame = FocusedCoreFrame();
if (!focused_frame || !focused_frame->IsLocalFrame())
return WebInputEventResult::kNotHandled;
LocalFrame* frame = ToLocalFrame(focused_frame);
WebInputEventResult result = frame->GetEventHandler().KeyEvent(event);
if (result != WebInputEventResult::kNotHandled) {
if (WebInputEvent::kRawKeyDown == event.GetType()) {
// Suppress the next keypress event unless the focused node is a plugin
// node. (Flash needs these keypress events to handle non-US keyboards.)
Element* element = FocusedElement();
if (!element || !element->GetLayoutObject() ||
!element->GetLayoutObject()->IsEmbeddedObject())
suppress_next_keypress_event_ = true;
}
return result;
}
#if !defined(OS_MACOSX)
const WebInputEvent::Type kContextMenuKeyTriggeringEventType =
#if defined(OS_WIN)
WebInputEvent::kKeyUp;
#else
WebInputEvent::kRawKeyDown;
#endif
const WebInputEvent::Type kShiftF10TriggeringEventType =
WebInputEvent::kRawKeyDown;
bool is_unmodified_menu_key =
!(event.GetModifiers() & WebInputEvent::kInputModifiers) &&
event.windows_key_code == VKEY_APPS;
bool is_shift_f10 = (event.GetModifiers() & WebInputEvent::kInputModifiers) ==
WebInputEvent::kShiftKey &&
event.windows_key_code == VKEY_F10;
if ((is_unmodified_menu_key &&
event.GetType() == kContextMenuKeyTriggeringEventType) ||
(is_shift_f10 && event.GetType() == kShiftF10TriggeringEventType)) {
View()->SendContextMenuEvent();
return WebInputEventResult::kHandledSystem;
}
#endif // !defined(OS_MACOSX)
return WebInputEventResult::kNotHandled;
}
WebInputEventResult WebFrameWidgetImpl::HandleCharEvent(
const WebKeyboardEvent& event) {
DCHECK_EQ(event.GetType(), WebInputEvent::kChar);
// Please refer to the comments explaining the m_suppressNextKeypressEvent
// member. The m_suppressNextKeypressEvent is set if the KeyDown is
// handled by Webkit. A keyDown event is typically associated with a
// keyPress(char) event and a keyUp event. We reset this flag here as it
// only applies to the current keyPress event.
bool suppress = suppress_next_keypress_event_;
suppress_next_keypress_event_ = false;
LocalFrame* frame = ToLocalFrame(FocusedCoreFrame());
if (!frame) {
return suppress ? WebInputEventResult::kHandledSuppressed
: WebInputEventResult::kNotHandled;
}
EventHandler& handler = frame->GetEventHandler();
if (!event.IsCharacterKey())
return WebInputEventResult::kHandledSuppressed;
// Accesskeys are triggered by char events and can't be suppressed.
// It is unclear whether a keypress should be dispatched as well
// crbug.com/563507
if (handler.HandleAccessKey(event))
return WebInputEventResult::kHandledSystem;
// Safari 3.1 does not pass off windows system key messages (WM_SYSCHAR) to
// the eventHandler::keyEvent. We mimic this behavior on all platforms since
// for now we are converting other platform's key events to windows key
// events.
if (event.is_system_key)
return WebInputEventResult::kNotHandled;
if (suppress)
return WebInputEventResult::kHandledSuppressed;
WebInputEventResult result = handler.KeyEvent(event);
if (result != WebInputEventResult::kNotHandled)
return result;
return WebInputEventResult::kNotHandled;
}
Frame* WebFrameWidgetImpl::FocusedCoreFrame() const {
return GetPage() ? GetPage()->GetFocusController().FocusedOrMainFrame()
: nullptr;
}
Element* WebFrameWidgetImpl::FocusedElement() const {
LocalFrame* frame = GetPage()->GetFocusController().FocusedFrame();
if (!frame)
return nullptr;
Document* document = frame->GetDocument();
if (!document)
return nullptr;
return document->FocusedElement();
}
void WebFrameWidgetImpl::SetLayerTreeView(WebLayerTreeView* layer_tree_view) {
DCHECK(Client());
DCHECK(!mutator_dispatcher_);
layer_tree_view_ = layer_tree_view;
if (Platform::Current()->IsThreadedAnimationEnabled()) {
animation_host_ = std::make_unique<CompositorAnimationHost>(
layer_tree_view_->CompositorAnimationHost());
}
GetPage()->LayerTreeViewInitialized(*layer_tree_view_,
LocalRootImpl()->GetFrame()->View());
// TODO(kenrb): Currently GPU rasterization is always enabled for OOPIFs.
// This is okay because it is only necessarily to set the trigger to false
// for certain cases that affect the top-level frame, but it would be better
// to be consistent with the top-level frame. Ideally the logic should
// be moved from WebViewImpl into WebFrameWidget and used for all local
// frame roots. https://crbug.com/712794
layer_tree_view_->HeuristicsForGpuRasterizationUpdated(true);
// WebFrameWidgetImpl is used for child frames, which always have a
// transparent background color.
layer_tree_view_->SetBackgroundColor(SK_ColorTRANSPARENT);
}
void WebFrameWidgetImpl::SetIsAcceleratedCompositingActive(bool active) {
// In the middle of shutting down; don't try to spin back up a compositor.
// FIXME: compositing startup/shutdown should be refactored so that it
// turns on explicitly rather than lazily, which causes this awkwardness.
if (layer_tree_view_closed_)
return;
DCHECK(!active || layer_tree_view_);
if (is_accelerated_compositing_active_ == active)
return;
if (active) {
TRACE_EVENT0("blink",
"WebViewImpl::setIsAcceleratedCompositingActive(true)");
layer_tree_view_->SetRootLayer(root_layer_);
UpdateLayerTreeViewport();
is_accelerated_compositing_active_ = true;
}
}
PaintLayerCompositor* WebFrameWidgetImpl::Compositor() const {
LocalFrame* frame = LocalRootImpl()->GetFrame();
if (!frame || !frame->GetDocument() || !frame->GetDocument()->GetLayoutView())
return nullptr;
return frame->GetDocument()->GetLayoutView()->Compositor();
}
void WebFrameWidgetImpl::SetRootGraphicsLayer(GraphicsLayer* layer) {
root_graphics_layer_ = layer;
root_layer_ = layer ? layer->CcLayer() : nullptr;
SetIsAcceleratedCompositingActive(layer);
if (!layer_tree_view_)
return;
if (root_layer_)
layer_tree_view_->SetRootLayer(root_layer_);
else
layer_tree_view_->ClearRootLayer();
}
void WebFrameWidgetImpl::SetRootLayer(scoped_refptr<cc::Layer> layer) {
root_layer_ = layer;
SetIsAcceleratedCompositingActive(!!layer);
if (!layer_tree_view_)
return;
if (root_layer_)
layer_tree_view_->SetRootLayer(root_layer_);
else
layer_tree_view_->ClearRootLayer();
}
WebLayerTreeView* WebFrameWidgetImpl::GetLayerTreeView() const {
return layer_tree_view_;
}
CompositorAnimationHost* WebFrameWidgetImpl::AnimationHost() const {
return animation_host_.get();
}
HitTestResult WebFrameWidgetImpl::CoreHitTestResultAt(
const gfx::Point& point_in_viewport) {
DocumentLifecycle::AllowThrottlingScope throttling_scope(
LocalRootImpl()->GetFrame()->GetDocument()->Lifecycle());
LocalFrameView* view = LocalRootImpl()->GetFrameView();
IntPoint point_in_root_frame =
view->ViewportToFrame(IntPoint(point_in_viewport));
return HitTestResultForRootFramePos(point_in_root_frame);
}
void WebFrameWidgetImpl::ZoomToFindInPageRect(
const WebRect& rect_in_root_frame) {
Client()->ZoomToFindInPageRectInMainFrame(rect_in_root_frame);
}
HitTestResult WebFrameWidgetImpl::HitTestResultForRootFramePos(
const LayoutPoint& pos_in_root_frame) {
LayoutPoint doc_point(
LocalRootImpl()->GetFrame()->View()->ConvertFromRootFrame(
pos_in_root_frame));
HitTestLocation location(doc_point);
HitTestResult result =
LocalRootImpl()->GetFrame()->GetEventHandler().HitTestResultAtLocation(
location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
result.SetToShadowHostIfInRestrictedShadowRoot();
return result;
}
LocalFrame* WebFrameWidgetImpl::FocusedLocalFrameAvailableForIme() const {
if (!ime_accept_events_)
return nullptr;
return FocusedLocalFrameInWidget();
}
void WebFrameWidgetImpl::DidCreateLocalRootView() {
// If this WebWidget still hasn't received its size from the embedder, block
// the parser. This is necessary, because the parser can cause layout to
// happen, which needs to be done with the correct size.
if (!size_) {
did_suspend_parsing_ = true;
LocalRootImpl()->GetFrame()->Loader().GetDocumentLoader()->BlockParser();
}
}
void WebFrameWidgetImpl::GetScrollParamsForFocusedEditableElement(
const Element& element,
LayoutRect& rect_to_scroll,
WebScrollIntoViewParams& params) {
LocalFrameView& frame_view = *element.GetDocument().View();
IntRect absolute_element_bounds =
element.GetLayoutObject()->AbsoluteBoundingBoxRect();
IntRect absolute_caret_bounds =
element.GetDocument().GetFrame()->Selection().AbsoluteCaretBounds();
// Ideally, the chosen rectangle includes the element box and caret bounds
// plus some margin on the left. If this does not work (i.e., does not fit
// inside the frame view), then choose a subrect which includes the caret
// bounds. It is preferrable to also include element bounds' location and left
// align the scroll. If this cant be satisfied, the scroll will be right
// aligned.
IntRect maximal_rect =
UnionRect(absolute_element_bounds, absolute_caret_bounds);
// Set the ideal margin.
maximal_rect.ShiftXEdgeTo(
maximal_rect.X() -
static_cast<int>(kIdealPaddingRatio * absolute_element_bounds.Width()));
bool maximal_rect_fits_in_frame =
!(frame_view.Size() - maximal_rect.Size()).IsEmpty();
if (!maximal_rect_fits_in_frame) {
IntRect frame_rect(maximal_rect.Location(), frame_view.Size());
maximal_rect.Intersect(frame_rect);
IntPoint point_forced_to_be_visible =
absolute_caret_bounds.MaxXMaxYCorner() +
IntSize(kCaretPadding, kCaretPadding);
if (!maximal_rect.Contains(point_forced_to_be_visible)) {
// Move the rect towards the point until the point is barely contained.
maximal_rect.Move(point_forced_to_be_visible -
maximal_rect.MaxXMaxYCorner());
}
}
params.zoom_into_rect = View()->ShouldZoomToLegibleScale(element);
params.relative_element_bounds = NormalizeRect(
Intersection(absolute_element_bounds, maximal_rect), maximal_rect);
params.relative_caret_bounds = NormalizeRect(
Intersection(absolute_caret_bounds, maximal_rect), maximal_rect);
params.behavior = WebScrollIntoViewParams::kInstant;
rect_to_scroll = LayoutRect(maximal_rect);
}
} // namespace blink