blob: e3e764c59b0df3d7686fdfe9eb5e65627ed46f33 [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/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/swap_promise.h"
#include "cc/trees/ukm_manager.h"
#include "third_party/blink/public/mojom/input/input_handler.mojom-blink.h"
#include "third_party/blink/public/mojom/input/touch_event.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/scheduler/web_render_widget_scheduling_state.h"
#include "third_party/blink/public/web/web_autofill_client.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_non_composited_widget_client.h"
#include "third_party/blink/public/web/web_performance.h"
#include "third_party/blink/public/web/web_plugin.h"
#include "third_party/blink/public/web/web_settings.h"
#include "third_party/blink/public/web/web_view_client.h"
#include "third_party/blink/renderer/core/content_capture/content_capture_manager.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
#include "third_party/blink/renderer/core/editing/frame_selection.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/events/wheel_event.h"
#include "third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h"
#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
#include "third_party/blink/renderer/core/exported/web_settings_impl.h"
#include "third_party/blink/renderer/core/exported/web_view_impl.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/remote_frame_client.h"
#include "third_party/blink/renderer/core/frame/screen.h"
#include "third_party/blink/renderer/core/frame/screen_metrics_emulator.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/html/battery_savings.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/html/html_plugin_element.h"
#include "third_party/blink/renderer/core/html/portal/document_portals.h"
#include "third_party/blink/renderer/core/html/portal/portal_contents.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/hit_test_location.h"
#include "third_party/blink/renderer/core/layout/hit_test_request.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_object.h"
#include "third_party/blink/renderer/core/layout/layout_shift_tracker.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/loader/interactive_detector.h"
#include "third_party/blink/renderer/core/page/context_menu_controller.h"
#include "third_party/blink/renderer/core/page/drag_actions.h"
#include "third_party/blink/renderer/core/page/drag_controller.h"
#include "third_party/blink/renderer/core/page/drag_data.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/link_highlight.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/pointer_lock_controller.h"
#include "third_party/blink/renderer/core/page/scrolling/fragment_anchor.h"
#include "third_party/blink/renderer/core/page/validation_message_client.h"
#include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h"
#include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h"
#include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h"
#include "third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h"
#include "third_party/blink/renderer/platform/keyboard_codes.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h"
#include "third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h"
#include "third_party/blink/renderer/platform/widget/widget_base.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-blink.h"
#include "ui/gfx/geometry/point_conversions.h"
#if defined(OS_MAC)
#include "third_party/blink/renderer/core/editing/substring_util.h"
#include "third_party/blink/renderer/platform/fonts/mac/attributed_string_type_converter.h"
#include "ui/base/mojom/attributed_string.mojom-blink.h"
#include "ui/gfx/geometry/point.h"
#endif
namespace WTF {
template <>
struct CrossThreadCopier<blink::WebReportTimeCallback>
: public CrossThreadCopierByValuePassThrough<blink::WebReportTimeCallback> {
STATIC_ONLY(CrossThreadCopier);
};
} // namespace WTF
namespace blink {
namespace {
using ::ui::mojom::blink::DragOperation;
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;
}
void ForEachLocalFrameControlledByWidget(
LocalFrame* frame,
const base::RepeatingCallback<void(WebLocalFrameImpl*)>& callback) {
callback.Run(WebLocalFrameImpl::FromFrame(frame));
for (Frame* child = frame->FirstChild(); child;
child = child->NextSibling()) {
if (child->IsLocalFrame()) {
ForEachLocalFrameControlledByWidget(DynamicTo<LocalFrame>(child),
callback);
}
}
}
// Iterate the remote children that will be controlled by the widget. Skip over
// any RemoteFrames have have another LocalFrame root as their parent.
void ForEachRemoteFrameChildrenControlledByWidget(
Frame* frame,
const base::RepeatingCallback<void(RemoteFrame*)>& callback) {
for (Frame* child = frame->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
if (auto* remote_frame = DynamicTo<RemoteFrame>(child)) {
callback.Run(remote_frame);
ForEachRemoteFrameChildrenControlledByWidget(remote_frame, callback);
} else if (auto* local_frame = DynamicTo<LocalFrame>(child)) {
// If iteration arrives at a local root then don't descend as it will be
// controlled by another widget.
if (!local_frame->IsLocalRoot()) {
ForEachRemoteFrameChildrenControlledByWidget(local_frame, callback);
}
}
}
// Iterate on any portals owned by a local frame.
if (auto* local_frame = DynamicTo<LocalFrame>(frame)) {
if (Document* document = local_frame->GetDocument()) {
for (PortalContents* portal :
DocumentPortals::From(*document).GetPortals()) {
if (RemoteFrame* remote_frame = portal->GetFrame())
callback.Run(remote_frame);
}
}
}
}
viz::FrameSinkId GetRemoteFrameSinkId(const HitTestResult& result) {
Node* node = result.InnerNode();
auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(node);
if (!frame_owner || !frame_owner->ContentFrame() ||
!frame_owner->ContentFrame()->IsRemoteFrame())
return viz::FrameSinkId();
RemoteFrame* remote_frame = To<RemoteFrame>(frame_owner->ContentFrame());
if (remote_frame->IsIgnoredForHitTest())
return viz::FrameSinkId();
LayoutObject* object = node->GetLayoutObject();
DCHECK(object);
if (!object->IsBox())
return viz::FrameSinkId();
IntPoint local_point = RoundedIntPoint(result.LocalPoint());
if (!To<LayoutBox>(object)->ComputedCSSContentBoxRect().Contains(local_point))
return viz::FrameSinkId();
return remote_frame->GetFrameSinkId();
}
} // namespace
// WebFrameWidget ------------------------------------------------------------
bool WebFrameWidgetImpl::ignore_input_events_ = false;
WebFrameWidgetImpl::WebFrameWidgetImpl(
base::PassKey<WebLocalFrame>,
CrossVariantMojoAssociatedRemote<mojom::blink::FrameWidgetHostInterfaceBase>
frame_widget_host,
CrossVariantMojoAssociatedReceiver<mojom::blink::FrameWidgetInterfaceBase>
frame_widget,
CrossVariantMojoAssociatedRemote<mojom::blink::WidgetHostInterfaceBase>
widget_host,
CrossVariantMojoAssociatedReceiver<mojom::blink::WidgetInterfaceBase>
widget,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const viz::FrameSinkId& frame_sink_id,
bool hidden,
bool never_composited,
bool is_for_child_local_root,
bool is_for_nested_main_frame)
: widget_base_(std::make_unique<WidgetBase>(/*widget_base_client=*/this,
std::move(widget_host),
std::move(widget),
task_runner,
hidden,
never_composited,
is_for_child_local_root)),
frame_sink_id_(frame_sink_id),
is_for_child_local_root_(is_for_child_local_root) {
DCHECK(task_runner);
if (is_for_nested_main_frame)
main_data().is_for_nested_main_frame = is_for_nested_main_frame;
frame_widget_host_.Bind(std::move(frame_widget_host), task_runner);
receiver_.Bind(std::move(frame_widget), task_runner);
}
WebFrameWidgetImpl::~WebFrameWidgetImpl() {
// Ensure that Close is called and we aren't releasing |widget_base_| in the
// destructor.
// TODO(crbug.com/1139104): This CHECK can be changed to a DCHECK once
// the issue is solved.
CHECK(!widget_base_);
}
void WebFrameWidgetImpl::BindLocalRoot(WebLocalFrame& local_root) {
local_root_ = To<WebLocalFrameImpl>(local_root);
}
bool WebFrameWidgetImpl::ForTopMostMainFrame() const {
return ForMainFrame() && !main_data().is_for_nested_main_frame;
}
void WebFrameWidgetImpl::SetIsNestedMainFrameWidget(bool is_nested) {
main_data().is_for_nested_main_frame = is_nested;
}
void WebFrameWidgetImpl::Close() {
LocalFrameView* frame_view;
if (is_for_child_local_root_) {
frame_view = LocalRootImpl()->GetFrame()->View();
} else {
// Scrolling for the root frame is special we need to pass null indicating
// we are at the top of the tree when setting up the Animation. Which will
// cause ownership of the timeline and animation host.
// See ScrollingCoordinator::AnimationHostInitialized.
frame_view = nullptr;
}
GetPage()->WillCloseAnimationHost(frame_view);
if (ForMainFrame()) {
// Closing the WebFrameWidgetImpl happens in response to the local main
// frame being detached from the Page/WebViewImpl.
View()->SetMainFrameViewWidget(nullptr);
}
mutator_dispatcher_ = nullptr;
local_root_ = nullptr;
widget_base_->Shutdown();
widget_base_.reset();
// These WeakPtrs must be invalidated for WidgetInputHandlerManager at the
// same time as the WidgetBase is.
input_handler_weak_ptr_factory_.InvalidateWeakPtrs();
receiver_.reset();
input_target_receiver_.reset();
}
WebLocalFrame* WebFrameWidgetImpl::LocalRoot() const {
return local_root_;
}
gfx::Rect WebFrameWidgetImpl::ComputeBlockBound(
const gfx::Point& point_in_root_frame,
bool ignore_clipping) const {
HitTestLocation location(local_root_->GetFrameView()->ConvertFromRootFrame(
PhysicalOffset(IntPoint(point_in_root_frame))));
HitTestRequest::HitTestRequestType hit_type =
HitTestRequest::kReadOnly | HitTestRequest::kActive |
(ignore_clipping ? HitTestRequest::kIgnoreClipping : 0);
HitTestResult result =
local_root_->GetFrame()->GetEventHandler().HitTestResultAtLocation(
location, hit_type);
result.SetToShadowHostIfInRestrictedShadowRoot();
Node* node = result.InnerNodeOrImageMapImage();
if (!node)
return gfx::Rect();
// Find the block type node based on the hit node.
// FIXME: This wants to walk flat tree with
// LayoutTreeBuilderTraversal::parent().
while (node &&
(!node->GetLayoutObject() || node->GetLayoutObject()->IsInline()))
node = LayoutTreeBuilderTraversal::Parent(*node);
// Return the bounding box in the root frame's coordinate space.
if (node) {
IntRect absolute_rect = node->GetLayoutObject()->AbsoluteBoundingBoxRect();
LocalFrame* frame = node->GetDocument().GetFrame();
return frame->View()->ConvertToRootFrame(absolute_rect);
}
return gfx::Rect();
}
void WebFrameWidgetImpl::DragTargetDragEnter(
const WebDragData& web_drag_data,
const gfx::PointF& point_in_viewport,
const gfx::PointF& screen_point,
DragOperationsMask operations_allowed,
uint32_t key_modifiers,
DragTargetDragEnterCallback callback) {
DCHECK(!current_drag_data_);
current_drag_data_ = DataObject::Create(web_drag_data);
operations_allowed_ = operations_allowed;
DragOperation operation = DragTargetDragEnterOrOver(
point_in_viewport, screen_point, kDragEnter, key_modifiers);
std::move(callback).Run(operation);
}
void WebFrameWidgetImpl::DragTargetDragOver(
const gfx::PointF& point_in_viewport,
const gfx::PointF& screen_point,
DragOperationsMask operations_allowed,
uint32_t key_modifiers,
DragTargetDragOverCallback callback) {
operations_allowed_ = operations_allowed;
DragOperation operation = DragTargetDragEnterOrOver(
point_in_viewport, screen_point, kDragOver, key_modifiers);
std::move(callback).Run(operation);
}
void WebFrameWidgetImpl::DragTargetDragLeave(
const gfx::PointF& point_in_viewport,
const gfx::PointF& screen_point) {
DCHECK(current_drag_data_);
// TODO(paulmeyer): It shouldn't be possible for |current_drag_data_| to be
// null here, but this is somehow happening (rarely). This suggests that in
// some cases drag-leave is happening before drag-enter, which should be
// impossible. This needs to be investigated further. Once fixed, the extra
// check for |!current_drag_data_| should be removed. (crbug.com/671152)
if (IgnoreInputEvents() || !current_drag_data_) {
CancelDrag();
return;
}
gfx::PointF point_in_root_frame(ViewportToRootFrame(point_in_viewport));
DragData drag_data(current_drag_data_.Get(), FloatPoint(point_in_root_frame),
FloatPoint(screen_point), operations_allowed_);
GetPage()->GetDragController().DragExited(&drag_data,
*local_root_->GetFrame());
// FIXME: why is the drag scroll timer not stopped here?
drag_operation_ = DragOperation::kNone;
current_drag_data_ = nullptr;
}
void WebFrameWidgetImpl::DragTargetDrop(const WebDragData& web_drag_data,
const gfx::PointF& point_in_viewport,
const gfx::PointF& screen_point,
uint32_t key_modifiers) {
gfx::PointF point_in_root_frame(ViewportToRootFrame(point_in_viewport));
DCHECK(current_drag_data_);
current_drag_data_ = DataObject::Create(web_drag_data);
// If this webview transitions from the "drop accepting" state to the "not
// accepting" state, then our IPC message reply indicating that may be in-
// flight, or else delayed by javascript processing in this webview. If a
// drop happens before our IPC reply has reached the browser process, then
// the browser forwards the drop to this webview. So only allow a drop to
// proceed if our webview drag_operation_ state is not DragOperation::kNone.
if (drag_operation_ == DragOperation::kNone) {
// IPC RACE CONDITION: do not allow this drop.
DragTargetDragLeave(point_in_viewport, screen_point);
return;
}
if (!IgnoreInputEvents()) {
current_drag_data_->SetModifiers(key_modifiers);
DragData drag_data(current_drag_data_.Get(),
FloatPoint(point_in_root_frame),
FloatPoint(screen_point), operations_allowed_);
GetPage()->GetDragController().PerformDrag(&drag_data,
*local_root_->GetFrame());
}
drag_operation_ = DragOperation::kNone;
current_drag_data_ = nullptr;
}
void WebFrameWidgetImpl::DragSourceEndedAt(const gfx::PointF& point_in_viewport,
const gfx::PointF& screen_point,
DragOperation operation) {
if (!local_root_) {
// We should figure out why |local_root_| could be nullptr
// (https://crbug.com/792345).
return;
}
if (IgnoreInputEvents()) {
CancelDrag();
return;
}
gfx::PointF point_in_root_frame(
GetPage()->GetVisualViewport().ViewportToRootFrame(
FloatPoint(point_in_viewport)));
WebMouseEvent fake_mouse_move(
WebInputEvent::Type::kMouseMove, point_in_root_frame, screen_point,
WebPointerProperties::Button::kLeft, 0, WebInputEvent::kNoModifiers,
base::TimeTicks::Now());
fake_mouse_move.SetFrameScale(1);
local_root_->GetFrame()->GetEventHandler().DragSourceEndedAt(fake_mouse_move,
operation);
}
void WebFrameWidgetImpl::DragSourceSystemDragEnded() {
CancelDrag();
}
void WebFrameWidgetImpl::SetBackgroundOpaque(bool opaque) {
if (opaque) {
View()->ClearBaseBackgroundColorOverride();
View()->ClearBackgroundColorOverride();
} else {
View()->SetBaseBackgroundColorOverride(SK_ColorTRANSPARENT);
View()->SetBackgroundColorOverride(SK_ColorTRANSPARENT);
}
}
void WebFrameWidgetImpl::SetTextDirection(base::i18n::TextDirection direction) {
LocalFrame* focusedFrame = FocusedLocalFrameInWidget();
if (focusedFrame)
focusedFrame->SetTextDirection(direction);
}
void WebFrameWidgetImpl::SetInheritedEffectiveTouchActionForSubFrame(
TouchAction touch_action) {
DCHECK(ForSubframe());
LocalRootImpl()->GetFrame()->SetInheritedEffectiveTouchAction(touch_action);
}
void WebFrameWidgetImpl::UpdateRenderThrottlingStatusForSubFrame(
bool is_throttled,
bool subtree_throttled,
bool display_locked) {
DCHECK(ForSubframe());
// TODO(szager,vmpstr): The parent render process currently rolls up
// display_locked into the value of subtree throttled here; display_locked
// should be maintained as a separate bit and transmitted between render
// processes.
LocalRootImpl()->GetFrameView()->UpdateRenderThrottlingStatus(
is_throttled, subtree_throttled, display_locked, /*recurse=*/true);
}
#if defined(OS_MAC)
void WebFrameWidgetImpl::GetStringAtPoint(const gfx::Point& point_in_local_root,
GetStringAtPointCallback callback) {
gfx::Point baseline_point;
ui::mojom::blink::AttributedStringPtr attributed_string = nullptr;
NSAttributedString* string = SubstringUtil::AttributedWordAtPoint(
this, point_in_local_root, baseline_point);
if (string)
attributed_string = ui::mojom::blink::AttributedString::From(string);
std::move(callback).Run(std::move(attributed_string), baseline_point);
}
#endif
void WebFrameWidgetImpl::BindWidgetCompositor(
mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) {
widget_base_->BindWidgetCompositor(std::move(receiver));
}
void WebFrameWidgetImpl::BindInputTargetClient(
mojo::PendingReceiver<viz::mojom::blink::InputTargetClient> receiver) {
DCHECK(!input_target_receiver_.is_bound());
input_target_receiver_.Bind(
std::move(receiver),
local_root_->GetTaskRunner(TaskType::kInternalDefault));
}
void WebFrameWidgetImpl::FrameSinkIdAt(const gfx::PointF& point,
const uint64_t trace_id,
FrameSinkIdAtCallback callback) {
TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Event.Pipeline",
TRACE_ID_GLOBAL(trace_id),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"step", "FrameSinkIdAt");
gfx::PointF local_point;
viz::FrameSinkId id = GetFrameSinkIdAtPoint(point, &local_point);
std::move(callback).Run(id, local_point);
}
viz::FrameSinkId WebFrameWidgetImpl::GetFrameSinkIdAtPoint(
const gfx::PointF& point_in_dips,
gfx::PointF* local_point_in_dips) {
HitTestResult result =
CoreHitTestResultAt(widget_base_->DIPsToBlinkSpace(point_in_dips));
Node* result_node = result.InnerNode();
*local_point_in_dips = gfx::PointF(point_in_dips);
// TODO(crbug.com/797828): When the node is null the caller may
// need to do extra checks. Like maybe update the layout and then
// call the hit-testing API. Either way it might be better to have
// a DCHECK for the node rather than a null check here.
if (!result_node) {
return frame_sink_id_;
}
viz::FrameSinkId remote_frame_sink_id = GetRemoteFrameSinkId(result);
if (remote_frame_sink_id.is_valid()) {
FloatPoint local_point = FloatPoint(result.LocalPoint());
LayoutObject* object = result_node->GetLayoutObject();
if (auto* box = DynamicTo<LayoutBox>(object))
local_point.MoveBy(-FloatPoint(box->PhysicalContentBoxOffset()));
*local_point_in_dips =
widget_base_->BlinkSpaceToDIPs(gfx::PointF(local_point));
return remote_frame_sink_id;
}
// Return the FrameSinkId for the current widget if the point did not hit
// test to a remote frame, or the point is outside of the remote frame's
// content box, or the remote frame doesn't have a valid FrameSinkId yet.
return frame_sink_id_;
}
gfx::RectF WebFrameWidgetImpl::BlinkSpaceToDIPs(const gfx::RectF& rect) {
return widget_base_->BlinkSpaceToDIPs(rect);
}
gfx::Rect WebFrameWidgetImpl::BlinkSpaceToEnclosedDIPs(const gfx::Rect& rect) {
return widget_base_->BlinkSpaceToEnclosedDIPs(rect);
}
gfx::Size WebFrameWidgetImpl::BlinkSpaceToFlooredDIPs(const gfx::Size& size) {
return widget_base_->BlinkSpaceToFlooredDIPs(size);
}
gfx::RectF WebFrameWidgetImpl::DIPsToBlinkSpace(const gfx::RectF& rect) {
return widget_base_->DIPsToBlinkSpace(rect);
}
gfx::PointF WebFrameWidgetImpl::DIPsToBlinkSpace(const gfx::PointF& point) {
return widget_base_->DIPsToBlinkSpace(point);
}
gfx::Point WebFrameWidgetImpl::DIPsToRoundedBlinkSpace(
const gfx::Point& point) {
return widget_base_->DIPsToRoundedBlinkSpace(point);
}
float WebFrameWidgetImpl::DIPsToBlinkSpace(float scalar) {
return widget_base_->DIPsToBlinkSpace(scalar);
}
gfx::Size WebFrameWidgetImpl::DIPsToCeiledBlinkSpace(const gfx::Size& size) {
return widget_base_->DIPsToCeiledBlinkSpace(size);
}
void WebFrameWidgetImpl::SetActive(bool active) {
View()->SetIsActive(active);
}
WebInputEventResult WebFrameWidgetImpl::HandleKeyEvent(
const WebKeyboardEvent& event) {
DCHECK((event.GetType() == WebInputEvent::Type::kRawKeyDown) ||
(event.GetType() == WebInputEvent::Type::kKeyDown) ||
(event.GetType() == WebInputEvent::Type::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;
// If there is a popup open, it should be the one processing the event,
// not the page.
scoped_refptr<WebPagePopupImpl> page_popup = View()->GetPagePopup();
if (page_popup) {
page_popup->HandleKeyEvent(event);
if (event.GetType() == WebInputEvent::Type::kRawKeyDown) {
suppress_next_keypress_event_ = true;
}
return WebInputEventResult::kHandledSystem;
}
auto* frame = DynamicTo<LocalFrame>(FocusedCoreFrame());
if (!frame)
return WebInputEventResult::kNotHandled;
WebInputEventResult result = frame->GetEventHandler().KeyEvent(event);
if (result != WebInputEventResult::kNotHandled) {
if (WebInputEvent::Type::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()) {
if (event.windows_key_code == VKEY_TAB) {
// If the plugin supports keyboard focus then we should not send a tab
// keypress event.
WebPluginContainerImpl* plugin_view =
To<LayoutEmbeddedContent>(element->GetLayoutObject())->Plugin();
if (plugin_view && plugin_view->SupportsKeyboardFocus()) {
suppress_next_keypress_event_ = true;
}
}
} else {
suppress_next_keypress_event_ = true;
}
}
return result;
}
#if !defined(OS_MAC)
const WebInputEvent::Type kContextMenuKeyTriggeringEventType =
#if defined(OS_WIN)
WebInputEvent::Type::kKeyUp;
#else
WebInputEvent::Type::kRawKeyDown;
#endif
const WebInputEvent::Type kShiftF10TriggeringEventType =
WebInputEvent::Type::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_MAC)
return WebInputEventResult::kNotHandled;
}
void WebFrameWidgetImpl::HandleMouseDown(LocalFrame& local_root,
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.
PhysicalOffset point(LayoutUnit(event.PositionInWidget().x()),
LayoutUnit(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();
auto* html_element = DynamicTo<HTMLElement>(hit_node);
if (!result.GetScrollbar() && hit_node && hit_node->GetLayoutObject() &&
hit_node->GetLayoutObject()->IsEmbeddedObject() && html_element &&
html_element->IsPluginElement()) {
mouse_capture_element_ = To<HTMLPlugInElement>(hit_node);
SetMouseCapture(true);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("input", "capturing mouse",
TRACE_ID_LOCAL(this));
}
}
PageWidgetEventHandler::HandleMouseDown(local_root, event);
// PageWidgetEventHandler may have detached the frame.
if (!LocalRootImpl())
return;
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_MAC)
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::HandleMouseLeave(LocalFrame& local_root,
const WebMouseEvent& event) {
View()->SetMouseOverURL(WebURL());
PageWidgetEventHandler::HandleMouseLeave(local_root, event);
// PageWidgetEventHandler may have detached the frame.
}
void WebFrameWidgetImpl::MouseContextMenu(const WebMouseEvent& event) {
GetPage()->GetContextMenuController().ClearContextMenu();
WebMouseEvent transformed_event =
TransformWebMouseEvent(LocalRootImpl()->GetFrameView(), event);
transformed_event.menu_source_type = kMenuSourceMouse;
// Find the right target frame. See issue 1186900.
HitTestResult result = HitTestResultForRootFramePos(
FloatPoint(transformed_event.PositionInRootFrame()));
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.
LocalFrame* target_local_frame = DynamicTo<LocalFrame>(target_frame);
if (!target_local_frame)
return;
{
ContextMenuAllowedScope scope;
target_local_frame->GetEventHandler().SendContextMenuEvent(
transformed_event);
}
// Actually showing the context menu is handled by the ContextMenuClient
// implementation...
}
WebInputEventResult WebFrameWidgetImpl::HandleMouseUp(
LocalFrame& local_root,
const WebMouseEvent& event) {
WebInputEventResult result =
PageWidgetEventHandler::HandleMouseUp(local_root, event);
// PageWidgetEventHandler may have detached the frame.
if (!LocalRootImpl())
return result;
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);
}
return result;
}
WebInputEventResult WebFrameWidgetImpl::HandleGestureEvent(
const WebGestureEvent& event) {
WebInputEventResult event_result = WebInputEventResult::kNotHandled;
// Fling events are not sent to the renderer.
CHECK(event.GetType() != WebInputEvent::Type::kGestureFlingStart);
CHECK(event.GetType() != WebInputEvent::Type::kGestureFlingCancel);
WebViewImpl* web_view = View();
LocalFrame* frame = LocalRootImpl()->GetFrame();
WebGestureEvent scaled_event = TransformWebGestureEvent(frame->View(), event);
// Special handling for double tap and scroll events as we don't want to
// hit test for them.
switch (event.GetType()) {
case WebInputEvent::Type::kGestureDoubleTap:
if (web_view->SettingsImpl()->DoubleTapToZoomEnabled() &&
web_view->MinimumPageScaleFactor() !=
web_view->MaximumPageScaleFactor()) {
IntPoint pos_in_local_frame_root =
FlooredIntPoint(scaled_event.PositionInRootFrame());
auto block_bounds =
gfx::Rect(ComputeBlockBound(pos_in_local_frame_root, false));
if (ForMainFrame()) {
web_view->AnimateDoubleTapZoom(pos_in_local_frame_root, block_bounds);
} else {
// 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.
GetAssociatedFrameWidgetHost()->AnimateDoubleTapZoomInMainFrame(
pos_in_local_frame_root, block_bounds);
}
}
event_result = WebInputEventResult::kHandledSystem;
DidHandleGestureEvent(event);
return event_result;
case WebInputEvent::Type::kGestureScrollBegin:
case WebInputEvent::Type::kGestureScrollEnd:
case WebInputEvent::Type::kGestureScrollUpdate:
// If we are getting any scroll toss close any page popup that is open.
web_view->CancelPagePopup();
// Scrolling-related gesture events invoke EventHandler recursively for
// each frame down the chain, doing a single-frame hit-test per frame.
// This matches handleWheelEvent. Perhaps we could simplify things by
// rewriting scroll handling to work inner frame out, and then unify with
// other gesture events.
event_result =
frame->GetEventHandler().HandleGestureScrollEvent(scaled_event);
DidHandleGestureEvent(event);
return event_result;
default:
break;
}
// Hit test across all frames and do touch adjustment as necessary for the
// event type.
GestureEventWithHitTestResults targeted_event =
frame->GetEventHandler().TargetGestureEvent(scaled_event);
// Link highlight animations are only for the main frame.
if (ForMainFrame()) {
// Handle link highlighting outside the main switch to avoid getting lost in
// the complicated set of cases handled below.
switch (scaled_event.GetType()) {
case WebInputEvent::Type::kGestureShowPress:
// Queue a highlight animation, then hand off to regular handler.
web_view->EnableTapHighlightAtPoint(targeted_event);
break;
case WebInputEvent::Type::kGestureTapCancel:
case WebInputEvent::Type::kGestureTap:
case WebInputEvent::Type::kGestureLongPress:
GetPage()->GetLinkHighlight().StartHighlightAnimationIfNeeded();
break;
default:
break;
}
}
switch (scaled_event.GetType()) {
case WebInputEvent::Type::kGestureTap: {
{
ContextMenuAllowedScope scope;
event_result =
frame->GetEventHandler().HandleGestureEvent(targeted_event);
}
if (web_view->GetPagePopup() && last_hidden_page_popup_ &&
web_view->GetPagePopup()->HasSamePopupClient(
last_hidden_page_popup_.get())) {
// The tap triggered a page popup that is the same as the one we just
// closed. It needs to be closed.
web_view->CancelPagePopup();
}
// Don't have this value persist outside of a single tap gesture, plus
// we're done with it now.
last_hidden_page_popup_ = nullptr;
break;
}
case WebInputEvent::Type::kGestureTwoFingerTap:
case WebInputEvent::Type::kGestureLongPress:
case WebInputEvent::Type::kGestureLongTap:
if (scaled_event.GetType() == WebInputEvent::Type::kGestureLongTap) {
if (LocalFrame* inner_frame =
targeted_event.GetHitTestResult().InnerNodeFrame()) {
if (!inner_frame->GetEventHandler().LongTapShouldInvokeContextMenu())
break;
} else if (!frame->GetEventHandler().LongTapShouldInvokeContextMenu()) {
break;
}
}
GetPage()->GetContextMenuController().ClearContextMenu();
{
ContextMenuAllowedScope scope;
event_result =
frame->GetEventHandler().HandleGestureEvent(targeted_event);
}
break;
case WebInputEvent::Type::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.
// When we close a popup because of a GestureTapDown, we also save it so
// we can prevent the following GestureTap from immediately reopening the
// same popup.
// This value should not persist outside of a gesture, so is cleared by
// GestureTap (where it is used) and by GestureCancel.
last_hidden_page_popup_ = web_view->GetPagePopup();
web_view->CancelPagePopup();
event_result =
frame->GetEventHandler().HandleGestureEvent(targeted_event);
break;
case WebInputEvent::Type::kGestureTapCancel:
// Don't have this value persist outside of a single tap gesture.
last_hidden_page_popup_ = nullptr;
event_result =
frame->GetEventHandler().HandleGestureEvent(targeted_event);
break;
case WebInputEvent::Type::kGestureShowPress:
case WebInputEvent::Type::kGestureTapUnconfirmed:
event_result =
frame->GetEventHandler().HandleGestureEvent(targeted_event);
break;
default:
NOTREACHED();
}
DidHandleGestureEvent(event);
return event_result;
}
WebInputEventResult WebFrameWidgetImpl::HandleMouseWheel(
LocalFrame& frame,
const WebMouseWheelEvent& event) {
View()->CancelPagePopup();
return PageWidgetEventHandler::HandleMouseWheel(frame, event);
// PageWidgetEventHandler may have detached the frame.
}
WebInputEventResult WebFrameWidgetImpl::HandleCharEvent(
const WebKeyboardEvent& event) {
DCHECK_EQ(event.GetType(), WebInputEvent::Type::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;
// If there is a popup open, it should be the one processing the event,
// not the page.
scoped_refptr<WebPagePopupImpl> page_popup = View()->GetPagePopup();
if (page_popup)
return page_popup->HandleKeyEvent(event);
LocalFrame* frame = To<LocalFrame>(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;
}
void WebFrameWidgetImpl::CancelDrag() {
// It's possible for this to be called while we're not doing a drag if
// it's from a previous page that got unloaded.
if (!doing_drag_and_drop_)
return;
GetPage()->GetDragController().DragEnded();
doing_drag_and_drop_ = false;
}
void WebFrameWidgetImpl::StartDragging(const WebDragData& drag_data,
DragOperationsMask operations_allowed,
const SkBitmap& drag_image,
const gfx::Point& drag_image_offset) {
doing_drag_and_drop_ = true;
if (drag_and_drop_disabled_) {
DragSourceSystemDragEnded();
return;
}
gfx::Point offset_in_dips =
widget_base_->BlinkSpaceToFlooredDIPs(drag_image_offset);
GetAssociatedFrameWidgetHost()->StartDragging(
drag_data, operations_allowed, drag_image,
gfx::Vector2d(offset_in_dips.x(), offset_in_dips.y()),
possible_drag_event_info_.Clone());
}
DragOperation WebFrameWidgetImpl::DragTargetDragEnterOrOver(
const gfx::PointF& point_in_viewport,
const gfx::PointF& screen_point,
DragAction drag_action,
uint32_t key_modifiers) {
DCHECK(current_drag_data_);
// TODO(paulmeyer): It shouldn't be possible for |m_currentDragData| to be
// null here, but this is somehow happening (rarely). This suggests that in
// some cases drag-over is happening before drag-enter, which should be
// impossible. This needs to be investigated further. Once fixed, the extra
// check for |!m_currentDragData| should be removed. (crbug.com/671504)
if (IgnoreInputEvents() || !current_drag_data_) {
CancelDrag();
return DragOperation::kNone;
}
FloatPoint point_in_root_frame(ViewportToRootFrame(point_in_viewport));
current_drag_data_->SetModifiers(key_modifiers);
DragData drag_data(current_drag_data_.Get(), FloatPoint(point_in_root_frame),
FloatPoint(screen_point), operations_allowed_);
DragOperation drag_operation =
GetPage()->GetDragController().DragEnteredOrUpdated(
&drag_data, *local_root_->GetFrame());
// Mask the drag operation against the drag source's allowed
// operations.
if (!(static_cast<int>(drag_operation) &
drag_data.DraggingSourceOperationMask()))
drag_operation = DragOperation::kNone;
drag_operation_ = drag_operation;
return drag_operation_;
}
void WebFrameWidgetImpl::SendOverscrollEventFromImplSide(
const gfx::Vector2dF& overscroll_delta,
cc::ElementId scroll_latched_element_id) {
if (!RuntimeEnabledFeatures::OverscrollCustomizationEnabled())
return;
Node* target_node = View()->FindNodeFromScrollableCompositorElementId(
scroll_latched_element_id);
if (target_node) {
target_node->GetDocument().EnqueueOverscrollEventForNode(
target_node, overscroll_delta.x(), overscroll_delta.y());
}
}
void WebFrameWidgetImpl::SendScrollEndEventFromImplSide(
cc::ElementId scroll_latched_element_id) {
if (!RuntimeEnabledFeatures::OverscrollCustomizationEnabled())
return;
Node* target_node = View()->FindNodeFromScrollableCompositorElementId(
scroll_latched_element_id);
if (target_node)
target_node->GetDocument().EnqueueScrollEndEventForNode(target_node);
}
void WebFrameWidgetImpl::UpdateCompositorScrollState(
const cc::CompositorCommitData& commit_data) {
if (WebDevToolsAgentImpl* devtools = LocalRootImpl()->DevToolsAgentImpl())
devtools->SetPageIsScrolling(commit_data.is_scroll_active);
RecordManipulationTypeCounts(commit_data.manipulation_info);
if (commit_data.scroll_latched_element_id == cc::ElementId())
return;
if (!commit_data.overscroll_delta.IsZero()) {
SendOverscrollEventFromImplSide(commit_data.overscroll_delta,
commit_data.scroll_latched_element_id);
}
// TODO(bokan): If a scroll ended and a new one began in the same Blink frame
// (e.g. during a long running main thread task), this will erroneously
// dispatch the scroll end to the latter (still-scrolling) element.
// https://crbug.com/1116780.
if (commit_data.scroll_gesture_did_end)
SendScrollEndEventFromImplSide(commit_data.scroll_latched_element_id);
}
WebInputMethodController*
WebFrameWidgetImpl::GetActiveWebInputMethodController() const {
WebLocalFrameImpl* local_frame =
WebLocalFrameImpl::FromFrame(FocusedLocalFrameInWidget());
return local_frame ? local_frame->GetInputMethodController() : nullptr;
}
void WebFrameWidgetImpl::DisableDragAndDrop() {
drag_and_drop_disabled_ = true;
}
gfx::PointF WebFrameWidgetImpl::ViewportToRootFrame(
const gfx::PointF& point_in_viewport) const {
return GetPage()->GetVisualViewport().ViewportToRootFrame(
FloatPoint(point_in_viewport));
}
WebViewImpl* WebFrameWidgetImpl::View() const {
return local_root_->ViewImpl();
}
Page* WebFrameWidgetImpl::GetPage() const {
return View()->GetPage();
}
mojom::blink::FrameWidgetHost*
WebFrameWidgetImpl::GetAssociatedFrameWidgetHost() const {
return frame_widget_host_.get();
}
void WebFrameWidgetImpl::RequestDecode(
const PaintImage& image,
base::OnceCallback<void(bool)> callback) {
widget_base_->LayerTreeHost()->QueueImageDecode(image, std::move(callback));
}
void WebFrameWidgetImpl::Trace(Visitor* visitor) const {
visitor->Trace(local_root_);
visitor->Trace(current_drag_data_);
visitor->Trace(frame_widget_host_);
visitor->Trace(receiver_);
visitor->Trace(input_target_receiver_);
visitor->Trace(mouse_capture_element_);
visitor->Trace(device_emulator_);
}
void WebFrameWidgetImpl::SetNeedsRecalculateRasterScales() {
if (!View()->does_composite())
return;
widget_base_->LayerTreeHost()->SetNeedsRecalculateRasterScales();
}
void WebFrameWidgetImpl::SetBackgroundColor(SkColor color) {
if (!View()->does_composite())
return;
widget_base_->LayerTreeHost()->set_background_color(color);
}
void WebFrameWidgetImpl::SetOverscrollBehavior(
const cc::OverscrollBehavior& overscroll_behavior) {
if (!View()->does_composite())
return;
widget_base_->LayerTreeHost()->SetOverscrollBehavior(overscroll_behavior);
}
void WebFrameWidgetImpl::RegisterSelection(cc::LayerSelection selection) {
if (!View()->does_composite())
return;
widget_base_->LayerTreeHost()->RegisterSelection(selection);
}
void WebFrameWidgetImpl::StartPageScaleAnimation(
const gfx::Vector2d& destination,
bool use_anchor,
float new_page_scale,
base::TimeDelta duration) {
widget_base_->LayerTreeHost()->StartPageScaleAnimation(
destination, use_anchor, new_page_scale, duration);
}
void WebFrameWidgetImpl::RequestBeginMainFrameNotExpected(bool request) {
if (!View()->does_composite())
return;
widget_base_->LayerTreeHost()->RequestBeginMainFrameNotExpected(request);
}
void WebFrameWidgetImpl::DidCommitAndDrawCompositorFrame() {
ForEachLocalFrameControlledByWidget(
local_root_->GetFrame(),
WTF::BindRepeating([](WebLocalFrameImpl* local_frame) {
local_frame->Client()->DidCommitAndDrawCompositorFrame();
}));
}
void WebFrameWidgetImpl::DidObserveFirstScrollDelay(
base::TimeDelta first_scroll_delay,
base::TimeTicks first_scroll_timestamp) {
if (!local_root_ || !(local_root_->GetFrame()) ||
!(local_root_->GetFrame()->GetDocument())) {
return;
}
InteractiveDetector* interactive_detector =
InteractiveDetector::From(*(local_root_->GetFrame()->GetDocument()));
if (interactive_detector) {
interactive_detector->DidObserveFirstScrollDelay(first_scroll_delay,
first_scroll_timestamp);
}
}
std::unique_ptr<cc::LayerTreeFrameSink>
WebFrameWidgetImpl::AllocateNewLayerTreeFrameSink() {
return nullptr;
}
void WebFrameWidgetImpl::DidBeginMainFrame() {
DCHECK(LocalRootImpl()->GetFrame());
PageWidgetDelegate::DidBeginFrame(*LocalRootImpl()->GetFrame());
}
void WebFrameWidgetImpl::UpdateLifecycle(WebLifecycleUpdate requested_update,
DocumentUpdateReason reason) {
TRACE_EVENT0("blink", "WebFrameWidgetImpl::UpdateLifecycle");
if (!LocalRootImpl())
return;
PageWidgetDelegate::UpdateLifecycle(*GetPage(), *LocalRootImpl()->GetFrame(),
requested_update, reason);
if (requested_update != WebLifecycleUpdate::kAll)
return;
View()->UpdatePagePopup();
// Meaningful layout events and background colors only apply to main frames.
if (ForMainFrame()) {
MainFrameData& data = main_data();
// There is no background color for non-composited WebViews (eg
// printing).
if (View()->does_composite()) {
SkColor background_color = View()->BackgroundColor();
SetBackgroundColor(background_color);
if (background_color != data.last_background_color) {
LocalRootImpl()->GetFrame()->DidChangeBackgroundColor(
background_color, false /* color_adjust */);
data.last_background_color = background_color;
}
}
if (LocalFrameView* view = LocalRootImpl()->GetFrameView()) {
LocalFrame* frame = LocalRootImpl()->GetFrame();
if (data.should_dispatch_first_visually_non_empty_layout &&
view->IsVisuallyNonEmpty()) {
data.should_dispatch_first_visually_non_empty_layout = false;
// TODO(esprehn): Move users of this callback to something
// better, the heuristic for "visually non-empty" is bad.
DidMeaningfulLayout(WebMeaningfulLayout::kVisuallyNonEmpty);
}
if (data.should_dispatch_first_layout_after_finished_parsing &&
frame->GetDocument()->HasFinishedParsing()) {
data.should_dispatch_first_layout_after_finished_parsing = false;
DidMeaningfulLayout(WebMeaningfulLayout::kFinishedParsing);
}
if (data.should_dispatch_first_layout_after_finished_loading &&
frame->GetDocument()->IsLoadCompleted()) {
data.should_dispatch_first_layout_after_finished_loading = false;
DidMeaningfulLayout(WebMeaningfulLayout::kFinishedLoading);
}
}
}
}
void WebFrameWidgetImpl::DidCompletePageScaleAnimation() {
// Page scale animations only happen on the main frame.
DCHECK(ForMainFrame());
if (auto* focused_frame = View()->FocusedFrame()) {
if (focused_frame->AutofillClient())
focused_frame->AutofillClient()->DidCompleteFocusChangeInFrame();
}
}
void WebFrameWidgetImpl::ScheduleAnimation() {
if (!View()->does_composite()) {
non_composited_client_->ScheduleNonCompositedAnimation();
return;
}
widget_base_->LayerTreeHost()->SetNeedsAnimate();
}
void WebFrameWidgetImpl::FocusChanged(bool enable) {
// TODO(crbug.com/689777): FocusChange events are only sent to the MainFrame
// these maybe should goto the local root so that the rest of input messages
// sent to those are preserved in order.
DCHECK(ForMainFrame());
View()->SetPageFocus(enable);
}
bool WebFrameWidgetImpl::ShouldAckSyntheticInputImmediately() {
// TODO(bokan): The RequestPresentation API appears not to function in VR. As
// a short term workaround for https://crbug.com/940063, ACK input
// immediately rather than using RequestPresentation.
if (GetPage()->GetSettings().GetImmersiveModeEnabled())
return true;
return false;
}
void WebFrameWidgetImpl::UpdateVisualProperties(
const VisualProperties& visual_properties) {
SetZoomLevel(visual_properties.zoom_level);
// TODO(danakj): In order to synchronize updates between local roots, the
// display mode should be propagated to RenderFrameProxies and down through
// their RenderWidgetHosts to child WebFrameWidgetImpl via the
// VisualProperties waterfall, instead of coming to each WebFrameWidgetImpl
// independently.
// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/display-mode
SetDisplayMode(visual_properties.display_mode);
if (ForMainFrame()) {
SetAutoResizeMode(visual_properties.auto_resize_enabled,
visual_properties.min_size_for_auto_resize,
visual_properties.max_size_for_auto_resize,
visual_properties.screen_info.device_scale_factor);
}
bool capture_sequence_number_changed =
visual_properties.capture_sequence_number !=
last_capture_sequence_number_;
if (capture_sequence_number_changed) {
last_capture_sequence_number_ = visual_properties.capture_sequence_number;
// Send the capture sequence number to RemoteFrames that are below the
// local root for this widget.
ForEachRemoteFrameControlledByWidget(WTF::BindRepeating(
[](uint32_t capture_sequence_number, RemoteFrame* remote_frame) {
remote_frame->UpdateCaptureSequenceNumber(capture_sequence_number);
},
visual_properties.capture_sequence_number));
}
if (!View()->AutoResizeMode()) {
if (visual_properties.is_fullscreen_granted != is_fullscreen_granted_) {
is_fullscreen_granted_ = visual_properties.is_fullscreen_granted;
if (is_fullscreen_granted_)
View()->DidEnterFullscreen();
else
View()->DidExitFullscreen();
}
}
gfx::Size old_visible_viewport_size_in_dips =
widget_base_->VisibleViewportSizeInDIPs();
ApplyVisualPropertiesSizing(visual_properties);
if (old_visible_viewport_size_in_dips !=
widget_base_->VisibleViewportSizeInDIPs()) {
ForEachLocalFrameControlledByWidget(
local_root_->GetFrame(),
WTF::BindRepeating([](WebLocalFrameImpl* local_frame) {
local_frame->ResetHasScrolledFocusedEditableIntoView();
}));
// Propagate changes down to child local root RenderWidgets and
// BrowserPlugins in other frame trees/processes.
ForEachRemoteFrameControlledByWidget(WTF::BindRepeating(
[](const gfx::Size& visible_viewport_size, RemoteFrame* remote_frame) {
remote_frame->DidChangeVisibleViewportSize(visible_viewport_size);
},
widget_base_->VisibleViewportSizeInDIPs()));
}
// All non-top-level Widgets (child local-root frames, Portals, GuestViews,
// etc.) propagate and consume the page scale factor as "external", meaning
// that it comes from the top level widget's page scale.
if (!ForTopMostMainFrame()) {
// The main frame controls the page scale factor, from blink. For other
// frame widgets, the page scale from pinch zoom and compositing scale is
// received from its parent as part of the visual properties here. While
// blink doesn't need to know this page scale factor outside the main frame,
// the compositor does in order to produce its output at the correct scale.
float combined_scale_factor = visual_properties.page_scale_factor *
visual_properties.compositing_scale_factor;
widget_base_->LayerTreeHost()->SetExternalPageScaleFactor(
combined_scale_factor, visual_properties.is_pinch_gesture_active);
NotifyPageScaleFactorChanged(visual_properties.page_scale_factor,
visual_properties.is_pinch_gesture_active);
NotifyCompositingScaleFactorChanged(
visual_properties.compositing_scale_factor);
} else {
// Ensure the external scale factor in top-level widgets is reset as it may
// be leftover from when a widget was nested and was promoted to top level
// (e.g. portal activation).
widget_base_->LayerTreeHost()->SetExternalPageScaleFactor(
1.f,
/*is_pinch_gesture_active=*/false);
}
// TODO(crbug.com/939118): ScrollFocusedNodeIntoViewForWidget does not work
// when the focused node is inside an OOPIF. This code path where
// scroll_focused_node_into_view is set is used only for WebView, crbug
// 939118 tracks fixing webviews to not use scroll_focused_node_into_view.
if (visual_properties.scroll_focused_node_into_view)
ScrollFocusedEditableElementIntoView();
}
void WebFrameWidgetImpl::ApplyVisualPropertiesSizing(
const VisualProperties& visual_properties) {
gfx::Rect new_compositor_viewport_pixel_rect =
visual_properties.compositor_viewport_pixel_rect;
if (ForMainFrame()) {
if (size_ !=
widget_base_->DIPsToCeiledBlinkSpace(visual_properties.new_size)) {
// Only hide popups when the size changes. Eg https://crbug.com/761908.
View()->CancelPagePopup();
}
if (auto* device_emulator = DeviceEmulator()) {
device_emulator->UpdateVisualProperties(visual_properties);
return;
}
if (AutoResizeMode()) {
new_compositor_viewport_pixel_rect = gfx::Rect(gfx::ScaleToCeiledSize(
widget_base_->BlinkSpaceToFlooredDIPs(size_.value_or(gfx::Size())),
visual_properties.screen_info.device_scale_factor));
}
}
SetWindowSegments(visual_properties.root_widget_window_segments);
widget_base_->UpdateSurfaceAndScreenInfo(
visual_properties.local_surface_id.value_or(viz::LocalSurfaceId()),
new_compositor_viewport_pixel_rect, visual_properties.screen_info);
// Store this even when auto-resizing, it is the size of the full viewport
// used for clipping, and this value is propagated down the Widget
// hierarchy via the VisualProperties waterfall.
widget_base_->SetVisibleViewportSizeInDIPs(
visual_properties.visible_viewport_size);
if (ForMainFrame()) {
if (!AutoResizeMode()) {
size_ = widget_base_->DIPsToCeiledBlinkSpace(visual_properties.new_size);
View()->ResizeWithBrowserControls(
size_.value(),
widget_base_->DIPsToCeiledBlinkSpace(
widget_base_->VisibleViewportSizeInDIPs()),
visual_properties.browser_controls_params);
}
} else {
// Widgets in a WebView's frame tree without a local main frame
// set the size of the WebView to be the |visible_viewport_size|, in order
// to limit compositing in (out of process) child frames to what is visible.
//
// Note that child frames in the same process/WebView frame tree as the
// main frame do not do this in order to not clobber the source of truth in
// the main frame.
if (!View()->MainFrameImpl()) {
View()->Resize(widget_base_->DIPsToCeiledBlinkSpace(
widget_base_->VisibleViewportSizeInDIPs()));
}
Resize(widget_base_->DIPsToCeiledBlinkSpace(visual_properties.new_size));
}
}
int WebFrameWidgetImpl::GetLayerTreeId() {
if (!View()->does_composite())
return 0;
return widget_base_->LayerTreeHost()->GetId();
}
const cc::LayerTreeSettings& WebFrameWidgetImpl::GetLayerTreeSettings() {
return widget_base_->LayerTreeHost()->GetSettings();
}
void WebFrameWidgetImpl::UpdateBrowserControlsState(
cc::BrowserControlsState constraints,
cc::BrowserControlsState current,
bool animate) {
DCHECK(View()->does_composite());
widget_base_->LayerTreeHost()->UpdateBrowserControlsState(constraints,
current, animate);
}
void WebFrameWidgetImpl::SetHaveScrollEventHandlers(bool has_handlers) {
widget_base_->LayerTreeHost()->SetHaveScrollEventHandlers(has_handlers);
}
void WebFrameWidgetImpl::SetEventListenerProperties(
cc::EventListenerClass listener_class,
cc::EventListenerProperties listener_properties) {
widget_base_->LayerTreeHost()->SetEventListenerProperties(
listener_class, listener_properties);
if (listener_class == cc::EventListenerClass::kTouchStartOrMove ||
listener_class == cc::EventListenerClass::kTouchEndOrCancel) {
bool has_touch_handlers =
EventListenerProperties(cc::EventListenerClass::kTouchStartOrMove) !=
cc::EventListenerProperties::kNone ||
EventListenerProperties(cc::EventListenerClass::kTouchEndOrCancel) !=
cc::EventListenerProperties::kNone;
if (!has_touch_handlers_ || *has_touch_handlers_ != has_touch_handlers) {
has_touch_handlers_ = has_touch_handlers;
// Can be null when running tests.
if (auto* scheduler_state = widget_base_->RendererWidgetSchedulingState())
scheduler_state->SetHasTouchHandler(has_touch_handlers);
// Set touch event consumers based on whether there are touch event
// handlers or the page has hit testable scrollbars.
auto touch_event_consumers = mojom::blink::TouchEventConsumers::New(
has_touch_handlers, GetPage()->GetScrollbarTheme().AllowsHitTest());
frame_widget_host_->SetHasTouchEventConsumers(
std::move(touch_event_consumers));
}
} else if (listener_class == cc::EventListenerClass::kPointerRawUpdate) {
SetHasPointerRawUpdateEventHandlers(listener_properties !=
cc::EventListenerProperties::kNone);
}
}
cc::EventListenerProperties WebFrameWidgetImpl::EventListenerProperties(
cc::EventListenerClass listener_class) const {
return widget_base_->LayerTreeHost()->event_listener_properties(
listener_class);
}
mojom::blink::DisplayMode WebFrameWidgetImpl::DisplayMode() const {
return display_mode_;
}
const WebVector<gfx::Rect>& WebFrameWidgetImpl::WindowSegments() const {
return window_segments_;
}
void WebFrameWidgetImpl::StartDeferringCommits(base::TimeDelta timeout) {
if (!View()->does_composite())
return;
widget_base_->LayerTreeHost()->StartDeferringCommits(timeout);
}
void WebFrameWidgetImpl::StopDeferringCommits(
cc::PaintHoldingCommitTrigger triggger) {
if (!View()->does_composite())
return;
widget_base_->LayerTreeHost()->StopDeferringCommits(triggger);
}
std::unique_ptr<cc::ScopedDeferMainFrameUpdate>
WebFrameWidgetImpl::DeferMainFrameUpdate() {
return widget_base_->LayerTreeHost()->DeferMainFrameUpdate();
}
void WebFrameWidgetImpl::SetBrowserControlsShownRatio(float top_ratio,
float bottom_ratio) {
widget_base_->LayerTreeHost()->SetBrowserControlsShownRatio(top_ratio,
bottom_ratio);
}
void WebFrameWidgetImpl::SetBrowserControlsParams(
cc::BrowserControlsParams params) {
widget_base_->LayerTreeHost()->SetBrowserControlsParams(params);
}
void WebFrameWidgetImpl::SynchronouslyCompositeForTesting(
base::TimeTicks frame_time) {
widget_base_->LayerTreeHost()->CompositeForTest(frame_time, false);
}
void WebFrameWidgetImpl::UseSynchronousResizeModeForTesting(bool enable) {
main_data().synchronous_resize_mode_for_testing = enable;
}
void WebFrameWidgetImpl::SetDeviceColorSpaceForTesting(
const gfx::ColorSpace& color_space) {
DCHECK(ForMainFrame());
// We are changing the device color space from the renderer, so allocate a
// new viz::LocalSurfaceId to avoid surface invariants violations in tests.
widget_base_->LayerTreeHost()->RequestNewLocalSurfaceId();
blink::ScreenInfo info = widget_base_->GetScreenInfo();
info.display_color_spaces = gfx::DisplayColorSpaces(color_space);
widget_base_->UpdateScreenInfo(info);
}
// TODO(665924): Remove direct dispatches of mouse events from
// PointerLockController, instead passing them through EventHandler.
void WebFrameWidgetImpl::PointerLockMouseEvent(
const WebCoalescedInputEvent& coalesced_event) {
const WebInputEvent& input_event = coalesced_event.Event();
const WebMouseEvent& mouse_event =
static_cast<const WebMouseEvent&>(input_event);
WebMouseEvent transformed_event =
TransformWebMouseEvent(local_root_->GetFrameView(), mouse_event);
AtomicString event_type;
switch (input_event.GetType()) {
case WebInputEvent::Type::kMouseDown:
event_type = event_type_names::kMousedown;
if (!GetPage() || !GetPage()->GetPointerLockController().GetElement())
break;
LocalFrame::NotifyUserActivation(
GetPage()
->GetPointerLockController()
.GetElement()
->GetDocument()
.GetFrame(),
mojom::blink::UserActivationNotificationType::kInteraction);
break;
case WebInputEvent::Type::kMouseUp:
event_type = event_type_names::kMouseup;
break;
case WebInputEvent::Type::kMouseMove:
event_type = event_type_names::kMousemove;
break;
default:
NOTREACHED() << input_event.GetType();
}
if (GetPage()) {
GetPage()->GetPointerLockController().DispatchLockedMouseEvent(
transformed_event,
TransformWebMouseEventVector(
local_root_->GetFrameView(),
coalesced_event.GetCoalescedEventsPointers()),
TransformWebMouseEventVector(
local_root_->GetFrameView(),
coalesced_event.GetPredictedEventsPointers()),
event_type);
}
}
bool WebFrameWidgetImpl::IsPointerLocked() {
if (GetPage()) {
return GetPage()->GetPointerLockController().IsPointerLocked();
}
return false;
}
void WebFrameWidgetImpl::ShowContextMenu(
ui::mojom::blink::MenuSourceType source_type,
const gfx::Point& location) {
host_context_menu_location_ = location;
if (!GetPage())
return;
GetPage()->GetContextMenuController().ClearContextMenu();
{
ContextMenuAllowedScope scope;
if (LocalFrame* focused_frame =
GetPage()->GetFocusController().FocusedFrame()) {
focused_frame->GetEventHandler().ShowNonLocatedContextMenu(
nullptr, static_cast<blink::WebMenuSourceType>(source_type));
}
}
host_context_menu_location_.reset();
}
void WebFrameWidgetImpl::SetViewportIntersection(
mojom::blink::ViewportIntersectionStatePtr intersection_state,
const base::Optional<VisualProperties>& visual_properties) {
// Remote viewports are only applicable to local frames with remote ancestors.
// TODO(https://crbug.com/1148960): Should this deal with portals?
DCHECK(ForSubframe());
if (visual_properties.has_value())
UpdateVisualProperties(visual_properties.value());
ApplyViewportIntersection(std::move(intersection_state));
}
void WebFrameWidgetImpl::ApplyViewportIntersectionForTesting(
mojom::blink::ViewportIntersectionStatePtr intersection_state) {
ApplyViewportIntersection(std::move(intersection_state));
}
void WebFrameWidgetImpl::ApplyViewportIntersection(
mojom::blink::ViewportIntersectionStatePtr intersection_state) {
child_data().compositor_visible_rect =
intersection_state->compositor_visible_rect;
widget_base_->LayerTreeHost()->SetVisualDeviceViewportIntersectionRect(
intersection_state->compositor_visible_rect);
LocalRootImpl()->GetFrame()->SetViewportIntersectionFromParent(
*intersection_state);
}
void WebFrameWidgetImpl::EnableDeviceEmulation(
const DeviceEmulationParams& parameters) {
// Device Emaulation is only supported for the main frame.
DCHECK(ForMainFrame());
if (!device_emulator_) {
gfx::Size size_in_dips = widget_base_->BlinkSpaceToFlooredDIPs(Size());
device_emulator_ = MakeGarbageCollected<ScreenMetricsEmulator>(
this, widget_base_->GetScreenInfo(), size_in_dips,
widget_base_->VisibleViewportSizeInDIPs(),
widget_base_->WidgetScreenRect(), widget_base_->WindowScreenRect());
}
device_emulator_->ChangeEmulationParams(parameters);
}
void WebFrameWidgetImpl::DisableDeviceEmulation() {
if (!device_emulator_)
return;
device_emulator_->DisableAndApply();
device_emulator_ = nullptr;
}
void WebFrameWidgetImpl::SetIsInertForSubFrame(bool inert) {
DCHECK(ForSubframe());
LocalRootImpl()->GetFrame()->SetIsInert(inert);
}
base::Optional<gfx::Point>
WebFrameWidgetImpl::GetAndResetContextMenuLocation() {
return std::move(host_context_menu_location_);
}
void WebFrameWidgetImpl::SetZoomLevel(double zoom_level) {
// Override the zoom level with the testing one if necessary.
if (zoom_level_for_testing_ != -INFINITY)
zoom_level = zoom_level_for_testing_;
View()->SetZoomLevel(zoom_level);
// Part of the UpdateVisualProperties dance we send the zoom level to
// RemoteFrames that are below the local root for this widget.
ForEachRemoteFrameControlledByWidget(WTF::BindRepeating(
[](double zoom_level, RemoteFrame* remote_frame) {
remote_frame->ZoomLevelChanged(zoom_level);
},
zoom_level));
}
void WebFrameWidgetImpl::SetAutoResizeMode(bool auto_resize,
const gfx::Size& min_window_size,
const gfx::Size& max_window_size,
float device_scale_factor) {
// Auto resize only applies to main frames.
DCHECK(ForMainFrame());
if (auto_resize) {
if (!Platform::Current()->IsUseZoomForDSFEnabled())
device_scale_factor = 1.f;
View()->EnableAutoResizeMode(
gfx::ScaleToCeiledSize(min_window_size, device_scale_factor),
gfx::ScaleToCeiledSize(max_window_size, device_scale_factor));
} else if (AutoResizeMode()) {
View()->DisableAutoResizeMode();
}
}
void WebFrameWidgetImpl::DidAutoResize(const gfx::Size& size) {
DCHECK(ForMainFrame());
gfx::Size size_in_dips = widget_base_->BlinkSpaceToFlooredDIPs(size);
size_ = size;
if (main_data().synchronous_resize_mode_for_testing) {
gfx::Rect new_pos(widget_base_->WindowRect());
new_pos.set_size(size_in_dips);
SetScreenRects(new_pos, new_pos);
}
// TODO(ccameron): Note that this destroys any information differentiating
// |size| from the compositor's viewport size.
gfx::Rect size_with_dsf = gfx::Rect(gfx::ScaleToCeiledSize(
gfx::Rect(size_in_dips).size(),
widget_base_->GetScreenInfo().device_scale_factor));
widget_base_->LayerTreeHost()->RequestNewLocalSurfaceId();
widget_base_->UpdateCompositorViewportRect(size_with_dsf);
}
LocalFrame* WebFrameWidgetImpl::FocusedLocalFrameInWidget() const {
if (!local_root_) {
// WebFrameWidget is created in the call to CreateFrame. The corresponding
// RenderWidget, however, might not swap in right away (InstallNewDocument()
// will lead to it swapping in). During this interval local_root_ is nullptr
// (see https://crbug.com/792345).
return nullptr;
}
LocalFrame* frame = GetPage()->GetFocusController().FocusedFrame();
return (frame && frame->LocalFrameRoot() == local_root_->GetFrame())
? frame
: nullptr;
}
WebLocalFrameImpl* WebFrameWidgetImpl::FocusedWebLocalFrameInWidget() const {
return WebLocalFrameImpl::FromFrame(FocusedLocalFrameInWidget());
}
bool WebFrameWidgetImpl::ScrollFocusedEditableElementIntoView() {
Element* element = FocusedElement();
if (!element || !WebElement(element).IsEditable())
return false;
element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kSelection);
if (!element->GetLayoutObject())
return false;
PhysicalRect rect_to_scroll;
auto params =
GetScrollParamsForFocusedEditableElement(*element, rect_to_scroll);
element->GetLayoutObject()->ScrollRectToVisible(rect_to_scroll,
std::move(params));
// Second phase for main frames is to schedule a zoom animation.
if (ForMainFrame()) {
LocalFrameView* main_frame_view = LocalRootImpl()->GetFrame()->View();
View()->ZoomAndScrollToFocusedEditableElementRect(
main_frame_view->RootFrameToDocument(
element->GetDocument().View()->ConvertToRootFrame(
element->GetLayoutObject()->AbsoluteBoundingBoxRect())),
main_frame_view->RootFrameToDocument(
element->GetDocument().View()->ConvertToRootFrame(
element->GetDocument()
.GetFrame()
->Selection()
.ComputeRectToScroll(kDoNotRevealExtent))),
View()->ShouldZoomToLegibleScale(*element));
}
return true;
}
void WebFrameWidgetImpl::ResetMeaningfulLayoutStateForMainFrame() {
MainFrameData& data = main_data();
data.should_dispatch_first_visually_non_empty_layout = true;
data.should_dispatch_first_layout_after_finished_parsing = true;
data.should_dispatch_first_layout_after_finished_loading = true;
}
void WebFrameWidgetImpl::InitializeCompositing(
scheduler::WebAgentGroupScheduler& agent_group_scheduler,
cc::TaskGraphRunner* task_graph_runner,
const ScreenInfo& screen_info,
std::unique_ptr<cc::UkmRecorderFactory> ukm_recorder_factory,
const cc::LayerTreeSettings* settings) {
DCHECK(View()->does_composite());
DCHECK(!non_composited_client_); // Assure only one initialize is called.
widget_base_->InitializeCompositing(
agent_group_scheduler, task_graph_runner, is_for_child_local_root_,
screen_info, std::move(ukm_recorder_factory), settings,
input_handler_weak_ptr_factory_.GetWeakPtr());
LocalFrameView* frame_view;
if (is_for_child_local_root_) {
frame_view = LocalRootImpl()->GetFrame()->View();
} else {
// Scrolling for the root frame is special we need to pass null indicating
// we are at the top of the tree when setting up the Animation. Which will
// cause ownership of the timeline and animation host.
// See ScrollingCoordinator::AnimationHostInitialized.
frame_view = nullptr;
}
GetPage()->AnimationHostInitialized(*AnimationHost(), frame_view);
}
void WebFrameWidgetImpl::InitializeNonCompositing(
WebNonCompositedWidgetClient* client) {
DCHECK(!non_composited_client_);
DCHECK(client);
DCHECK(!View()->does_composite());
// Assure only one initialize is called.
DCHECK(!widget_base_->IsComposited());
non_composited_client_ = client;
}
void WebFrameWidgetImpl::SetCompositorVisible(bool visible) {
widget_base_->SetCompositorVisible(visible);
}
gfx::Size WebFrameWidgetImpl::Size() {
return size_.value_or(gfx::Size());
}
void WebFrameWidgetImpl::Resize(const gfx::Size& new_size) {
if (size_ && *size_ == new_size)
return;
if (ForMainFrame()) {
size_ = new_size;
View()->Resize(new_size);
return;
}
if (child_data().did_suspend_parsing) {
child_data().did_suspend_parsing = false;
LocalRootImpl()->GetFrame()->Loader().GetDocumentLoader()->ResumeParser();
}
LocalFrameView* view = LocalRootImpl()->GetFrameView();
DCHECK(view);
size_ = new_size;
view->SetLayoutSize(IntSize(*size_));
view->Resize(IntSize(*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()) {
// 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.
LocalRootImpl()->GetFrame()->GetDocument()->EnqueueResizeEvent();
// Pass the limits even though this is for subframes, as the limits will
// be needed in setting the raster scale. We set this value when setting
// up the compositor, but need to update it when the limits of the
// WebViewImpl have changed.
// TODO(wjmaclean): This is updating when the size of the *child frame*
// have changed which are completely independent of the WebView, and in an
// OOPIF where the main frame is remote, are these limits even useful?
SetPageScaleStateAndLimits(1.f, false /* is_pinch_gesture_active */,
View()->MinimumPageScaleFactor(),
View()->MaximumPageScaleFactor());
}
}
void WebFrameWidgetImpl::BeginMainFrame(base::TimeTicks last_frame_time) {
TRACE_EVENT1("blink", "WebFrameWidgetImpl::BeginMainFrame", "frameTime",
last_frame_time);
DCHECK(!last_frame_time.is_null());
CHECK(LocalRootImpl());
// Dirty bit on MouseEventManager is not cleared in OOPIFs after scroll
// or layout changes. Ensure the hover state is recomputed if necessary.
LocalRootImpl()
->GetFrame()
->GetEventHandler()
.RecomputeMouseHoverStateIfNeeded();
// Adjusting frame anchor only happens on the main frame.
if (ForMainFrame()) {
if (LocalFrameView* view = LocalRootImpl()->GetFrameView()) {
if (FragmentAnchor* anchor = view->GetFragmentAnchor())
anchor->PerformPreRafActions();
}
}
base::Optional<LocalFrameUkmAggregator::ScopedUkmHierarchicalTimer> ukm_timer;
if (WidgetBase::ShouldRecordBeginMainFrameMetrics()) {
ukm_timer.emplace(LocalRootImpl()
->GetFrame()
->View()
->EnsureUkmAggregator()
.GetScopedTimer(LocalFrameUkmAggregator::kAnimate));
}
PageWidgetDelegate::Animate(*GetPage(), last_frame_time);
// Animate can cause the local frame to detach.
if (!LocalRootImpl())
return;
GetPage()->GetValidationMessageClient().LayoutOverlay();
}
void WebFrameWidgetImpl::BeginCommitCompositorFrame() {
commit_compositor_frame_start_time_.emplace(base::TimeTicks::Now());
}
void WebFrameWidgetImpl::EndCommitCompositorFrame(
base::TimeTicks commit_start_time) {
DCHECK(commit_compositor_frame_start_time_.has_value());
if (ForMainFrame()) {
View()->DidCommitCompositorFrameForLocalMainFrame();
View()->UpdatePreferredSize();
if (!View()->MainFrameImpl()) {
// Trying to track down why the view's idea of the main frame varies
// from LocalRootImpl's.
// TODO(https://crbug.com/1139104): Remove this.
std::string reason = View()->GetNullFrameReasonForBug1139104();
DCHECK(false) << reason;
SCOPED_CRASH_KEY_STRING32("Crbug1139104", "NullFrameReason", reason);
base::debug::DumpWithoutCrashing();
}
}
LocalRootImpl()
->GetFrame()
->View()
->EnsureUkmAggregator()
.RecordImplCompositorSample(commit_compositor_frame_start_time_.value(),
commit_start_time, base::TimeTicks::Now());
commit_compositor_frame_start_time_.reset();
}
void WebFrameWidgetImpl::ApplyViewportChanges(
const ApplyViewportChangesArgs& args) {
// Viewport changes only change the main frame.
if (!ForMainFrame())
return;
WebViewImpl* web_view = View();
// TODO(https://crbug.com/1160652): Figure out if View is null.
CHECK(widget_base_);
CHECK(web_view);
web_view->ApplyViewportChanges(args);
}
void WebFrameWidgetImpl::RecordManipulationTypeCounts(
cc::ManipulationInfo info) {
// Manipulation counts are only recorded for the main frame.
if (!ForMainFrame())
return;
if ((info & cc::kManipulationInfoWheel) == cc::kManipulationInfoWheel) {
UseCounter::Count(LocalRootImpl()->GetDocument(),
WebFeature::kScrollByWheel);
}
if ((info & cc::kManipulationInfoTouch) == cc::kManipulationInfoTouch) {
UseCounter::Count(LocalRootImpl()->GetDocument(),
WebFeature::kScrollByTouch);
}
if ((info & cc::kManipulationInfoPinchZoom) ==
cc::kManipulationInfoPinchZoom) {
UseCounter::Count(LocalRootImpl()->GetDocument(), WebFeature::kPinchZoom);
}
if ((info & cc::kManipulationInfoPrecisionTouchPad) ==
cc::kManipulationInfoPrecisionTouchPad) {
UseCounter::Count(LocalRootImpl()->GetDocument(),
WebFeature::kScrollByPrecisionTouchPad);
}
}
void WebFrameWidgetImpl::RecordDispatchRafAlignedInputTime(
base::TimeTicks raf_aligned_input_start_time) {
if (LocalRootImpl()) {
LocalRootImpl()->GetFrame()->View()->EnsureUkmAggregator().RecordSample(
LocalFrameUkmAggregator::kHandleInputEvents,
raf_aligned_input_start_time, base::TimeTicks::Now());
}
}
void WebFrameWidgetImpl::SetSuppressFrameRequestsWorkaroundFor704763Only(
bool suppress_frame_requests) {
GetPage()->Animator().SetSuppressFrameRequestsWorkaroundFor704763Only(
suppress_frame_requests);
}
std::unique_ptr<cc::BeginMainFrameMetrics>
WebFrameWidgetImpl::GetBeginMainFrameMetrics() {
if (!LocalRootImpl())
return nullptr;
return LocalRootImpl()
->GetFrame()
->View()
->EnsureUkmAggregator()
.GetBeginMainFrameMetrics();
}
std::unique_ptr<cc::WebVitalMetrics> WebFrameWidgetImpl::GetWebVitalMetrics() {
if (!LocalRootImpl())
return nullptr;
// This class should be called at most once per commit.
WebPerformance perf = LocalRootImpl()->Performance();
auto metrics = std::make_unique<cc::WebVitalMetrics>();
if (perf.FirstInputDelay().has_value()) {
metrics->first_input_delay = perf.FirstInputDelay().value();
metrics->has_fid = true;
}
base::TimeTicks start = perf.NavigationStartAsMonotonicTime();
base::TimeTicks largest_contentful_paint =
perf.LargestContentfulPaintAsMonotonicTime();
if (largest_contentful_paint >= start) {
metrics->largest_contentful_paint = largest_contentful_paint - start;
metrics->has_lcp = true;
}
double layout_shift = LocalRootImpl()
->GetFrame()
->View()
->GetLayoutShiftTracker()
.WeightedScore();
if (layout_shift > 0.f) {
metrics->layout_shift = layout_shift;
metrics->has_cls = true;
}
if (!metrics->HasValue())
return nullptr;
return metrics;
}
void WebFrameWidgetImpl::BeginUpdateLayers() {
if (LocalRootImpl())
update_layers_start_time_.emplace(base::TimeTicks::Now());
}
void WebFrameWidgetImpl::EndUpdateLayers() {
if (LocalRootImpl()) {
DCHECK(update_layers_start_time_);
LocalRootImpl()->GetFrame()->View()->EnsureUkmAggregator().RecordSample(
LocalFrameUkmAggregator::kUpdateLayers,
update_layers_start_time_.value(), base::TimeTicks::Now());
probe::LayerTreeDidChange(LocalRootImpl()->GetFrame());
}
update_layers_start_time_.reset();
}
void WebFrameWidgetImpl::RecordStartOfFrameMetrics() {
if (!LocalRootImpl())
return;
LocalRootImpl()->GetFrame()->View()->EnsureUkmAggregator().BeginMainFrame();
}
void WebFrameWidgetImpl::RecordEndOfFrameMetrics(
base::TimeTicks frame_begin_time,
cc::ActiveFrameSequenceTrackers trackers) {
if (!LocalRootImpl())
return;
LocalRootImpl()
->GetFrame()
->View()
->EnsureUkmAggregator()
.RecordEndOfFrameMetrics(frame_begin_time, base::TimeTicks::Now(),
trackers);
}
bool WebFrameWidgetImpl::WillHandleGestureEvent(const WebGestureEvent& event) {
possible_drag_event_info_.source = ui::mojom::blink::DragEventSource::kTouch;
possible_drag_event_info_.location =
gfx::ToFlooredPoint(event.PositionInScreen());
bool move_cursor = false;
switch (event.GetType()) {
case WebInputEvent::Type::kGestureScrollBegin: {
if (event.data.scroll_begin.cursor_control) {
swipe_to_move_cursor_activated_ = true;
move_cursor = true;
}
break;
}
case WebInputEvent::Type::kGestureScrollUpdate: {
if (swipe_to_move_cursor_activated_)
move_cursor = true;
break;
}
case WebInputEvent::Type::kGestureScrollEnd: {
if (swipe_to_move_cursor_activated_) {
swipe_to_move_cursor_activated_ = false;
}
break;
}
default:
break;
}
// TODO(crbug.com/1140106): Place cursor for scroll begin other than just move
// cursor.
if (move_cursor) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (focused_frame) {
gfx::Point base(event.PositionInWidget().x(),
event.PositionInWidget().y());
focused_frame->MoveCaretSelection(base);
}
return true;
}
return false;
}
void WebFrameWidgetImpl::WillHandleMouseEvent(const WebMouseEvent& event) {
possible_drag_event_info_.source = ui::mojom::blink::DragEventSource::kMouse;
possible_drag_event_info_.location =
gfx::Point(event.PositionInScreen().x(), event.PositionInScreen().y());
}
void WebFrameWidgetImpl::ObserveGestureEventAndResult(
const WebGestureEvent& gesture_event,
const gfx::Vector2dF& unused_delta,
const cc::OverscrollBehavior& overscroll_behavior,
bool event_processed) {
if (!widget_base_->LayerTreeHost()->GetSettings().enable_elastic_overscroll)
return;
cc::InputHandlerScrollResult scroll_result;
scroll_result.did_scroll = event_processed;
scroll_result.did_overscroll_root = !unused_delta.IsZero();
scroll_result.unused_scroll_delta = unused_delta;
scroll_result.overscroll_behavior = overscroll_behavior;
widget_base_->widget_input_handler_manager()->ObserveGestureEventOnMainThread(
gesture_event, scroll_result);
}
void WebFrameWidgetImpl::DidHandleKeyEvent() {
ClearEditCommands();
}
WebTextInputType WebFrameWidgetImpl::GetTextInputType() {
if (ShouldDispatchImeEventsToPlugin()) {
return GetFocusedPluginContainer()->GetPluginTextInputType();
}
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller)
return WebTextInputType::kWebTextInputTypeNone;
return controller->TextInputType();
}
void WebFrameWidgetImpl::SetCursorVisibilityState(bool is_visible) {
GetPage()->SetIsCursorVisible(is_visible);
}
void WebFrameWidgetImpl::ApplyViewportChangesForTesting(
const ApplyViewportChangesArgs& args) {
widget_base_->ApplyViewportChanges(args);
}
void WebFrameWidgetImpl::SetDisplayMode(mojom::blink::DisplayMode mode) {
if (mode != display_mode_) {
display_mode_ = mode;
LocalFrame* frame = LocalRootImpl()->GetFrame();
frame->MediaQueryAffectingValueChangedForLocalSubtree(
MediaValueChange::kOther);
}
}
void WebFrameWidgetImpl::SetWindowSegments(
const std::vector<gfx::Rect>& window_segments_param) {
WebVector<gfx::Rect> window_segments(window_segments_param);
if (!window_segments_.Equals(window_segments)) {
window_segments_ = window_segments;
LocalFrame* frame = LocalRootImpl()->GetFrame();
frame->WindowSegmentsChanged(window_segments_);
ForEachRemoteFrameControlledByWidget(WTF::BindRepeating(
[](const std::vector<gfx::Rect>& window_segments,
RemoteFrame* remote_frame) {
remote_frame->DidChangeRootWindowSegments(window_segments);
},
window_segments_param));
}
}
void WebFrameWidgetImpl::SetCursor(const ui::Cursor& cursor) {
widget_base_->SetCursor(cursor);
}
bool WebFrameWidgetImpl::HandlingInputEvent() {
return widget_base_->input_handler().handling_input_event();
}
void WebFrameWidgetImpl::SetHandlingInputEvent(bool handling) {
widget_base_->input_handler().set_handling_input_event(handling);
}
void WebFrameWidgetImpl::ProcessInputEventSynchronouslyForTesting(
const WebCoalescedInputEvent& event,
HandledEventCallback callback) {
widget_base_->input_handler().HandleInputEvent(event, nullptr,
std::move(callback));
}
WebInputEventResult WebFrameWidgetImpl::DispatchBufferedTouchEvents() {
CHECK(LocalRootImpl());
if (WebDevToolsAgentImpl* devtools = LocalRootImpl()->DevToolsAgentImpl())
devtools->DispatchBufferedTouchEvents();
return LocalRootImpl()
->GetFrame()
->GetEventHandler()
.DispatchBufferedTouchEvents();
}
WebInputEventResult WebFrameWidgetImpl::HandleInputEvent(
const WebCoalescedInputEvent& coalesced_event) {
const WebInputEvent& input_event = coalesced_event.Event();
TRACE_EVENT1("input,rail", "WebFrameWidgetImpl::HandleInputEvent", "type",
WebInputEvent::GetName(input_event.GetType()));
DCHECK(!WebInputEvent::IsTouchEventType(input_event.GetType()));
CHECK(LocalRootImpl());
// Only record metrics for the main frame.
if (ForMainFrame()) {
GetPage()->GetVisualViewport().StartTrackingPinchStats();
}
// If a drag-and-drop operation is in progress, ignore input events except
// PointerCancel.
if (doing_drag_and_drop_ &&
input_event.GetType() != WebInputEvent::Type::kPointerCancel)
return WebInputEventResult::kHandledSuppressed;
// Don't handle events once we've started shutting down.
if (!GetPage())
return WebInputEventResult::kNotHandled;
if (WebDevToolsAgentImpl* devtools = LocalRootImpl()->DevToolsAgentImpl()) {
auto result = devtools->HandleInputEvent(input_event);
if (result != WebInputEventResult::kNotHandled)
return result;
}
// Report the event to be NOT processed by WebKit, so that the browser can
// handle it appropriately.
if (IgnoreInputEvents())
return WebInputEventResult::kNotHandled;
base::AutoReset<const WebInputEvent*> current_event_change(
&CurrentInputEvent::current_input_event_, &input_event);
UIEventWithKeyState::ClearNewTabModifierSetFromIsolatedWorld();
if (GetPage()->GetPointerLockController().IsPointerLocked() &&
WebInputEvent::IsMouseEventType(input_event.GetType())) {
PointerLockMouseEvent(coalesced_event);
return WebInputEventResult::kHandledSystem;
}
/// These metrics are only captured for the main frame.
if (ForMainFrame()) {
Document& main_frame_document = *LocalRootImpl()->GetFrame()->GetDocument();
if (input_event.GetType() != WebInputEvent::Type::kMouseMove) {
FirstMeaningfulPaintDetector::From(main_frame_document)
.NotifyInputEvent();
}
if (input_event.GetType() != WebInputEvent::Type::kMouseMove &&
input_event.GetType() != WebInputEvent::Type::kMouseEnter &&
input_event.GetType() != WebInputEvent::Type::kMouseLeave) {
InteractiveDetector* interactive_detector(
InteractiveDetector::From(main_frame_document));
if (interactive_detector) {
interactive_detector->OnInvalidatingInputEvent(input_event.TimeStamp());
}
}
}
NotifyInputObservers(coalesced_event);
// Notify the focus frame of the input. Note that the other frames are not
// notified as input is only handled by the focused frame.
Frame* frame = FocusedCoreFrame();
if (auto* local_frame = DynamicTo<LocalFrame>(frame)) {
if (auto* content_capture_manager =
local_frame->LocalFrameRoot().GetContentCaptureManager()) {
content_capture_manager->NotifyInputEvent(input_event.GetType(),
*local_frame);
}
}
// Skip the pointerrawupdate for mouse capture case.
if (mouse_capture_element_ &&
input_event.GetType() == WebInputEvent::Type::kPointerRawUpdate)
return WebInputEventResult::kHandledSystem;
if (mouse_capture_element_ &&
WebInputEvent::IsMouseEventType(input_event.GetType()))
return HandleCapturedMouseEvent(coalesced_event);
// FIXME: This should take in the intended frame, not the local frame
// root.
return PageWidgetDelegate::HandleInputEvent(*this, coalesced_event,
LocalRootImpl()->GetFrame());
}
WebInputEventResult WebFrameWidgetImpl::HandleCapturedMouseEvent(
const WebCoalescedInputEvent& coalesced_event) {
const WebInputEvent& input_event = coalesced_event.Event();
TRACE_EVENT1("input", "captured mouse event", "type", input_event.GetType());
// Save |mouse_capture_element_| since |MouseCaptureLost()| will clear it.
HTMLPlugInElement* element = mouse_capture_element_;
// Not all platforms call mouseCaptureLost() directly.
if (input_event.GetType() == WebInputEvent::Type::kMouseUp) {
SetMouseCapture(false);
MouseCaptureLost();
}
AtomicString event_type;
switch (input_event.GetType()) {
case WebInputEvent::Type::kMouseEnter:
event_type = event_type_names::kMouseover;
break;
case WebInputEvent::Type::kMouseMove:
event_type = event_type_names::kMousemove;
break;
case WebInputEvent::Type::kPointerRawUpdate:
// There will be no mouse event for rawupdate events.
event_type = event_type_names::kPointerrawupdate;
break;
case WebInputEvent::Type::kMouseLeave:
event_type = event_type_names::kMouseout;
break;
case WebInputEvent::Type::kMouseDown:
event_type = event_type_names::kMousedown;
LocalFrame::NotifyUserActivation(
element->GetDocument().GetFrame(),
mojom::blink::UserActivationNotificationType::kInteraction);
break;
case WebInputEvent::Type::kMouseUp:
event_type = event_type_names::kMouseup;
break;
default:
NOTREACHED();
}
WebMouseEvent transformed_event =
TransformWebMouseEvent(LocalRootImpl()->GetFrameView(),
static_cast<const WebMouseEvent&>(input_event));
if (LocalFrame* frame = element->GetDocument().GetFrame()) {
frame->GetEventHandler().HandleTargetedMouseEvent(
element, transformed_event, event_type,
TransformWebMouseEventVector(
LocalRootImpl()->GetFrameView(),
coalesced_event.GetCoalescedEventsPointers()),
TransformWebMouseEventVector(
LocalRootImpl()->GetFrameView(),
coalesced_event.GetPredictedEventsPointers()));
}
return WebInputEventResult::kHandledSystem;
}
void WebFrameWidgetImpl::UpdateTextInputState() {
widget_base_->UpdateTextInputState();
}
void WebFrameWidgetImpl::UpdateSelectionBounds() {
widget_base_->UpdateSelectionBounds();
}
void WebFrameWidgetImpl::ShowVirtualKeyboard() {
widget_base_->ShowVirtualKeyboard();
}
void WebFrameWidgetImpl::FlushInputProcessedCallback() {
widget_base_->FlushInputProcessedCallback();
}
void WebFrameWidgetImpl::CancelCompositionForPepper() {
widget_base_->CancelCompositionForPepper();
}
void WebFrameWidgetImpl::RequestMouseLock(
bool has_transient_user_activation,
bool request_unadjusted_movement,
mojom::blink::WidgetInputHandlerHost::RequestMouseLockCallback callback) {
mojom::blink::WidgetInputHandlerHost* host =
widget_base_->widget_input_handler_manager()->GetWidgetInputHandlerHost();
// If we don't have a host just leave the callback uncalled. This simulates
// the browser indefinitely postponing the mouse request which is valid.
// Note that |callback| is not a mojo bound callback (until it is passed
// into the mojo interface) and can be destructed without invoking the
// callback. It does share the same signature as the mojo definition
// for simplicity.
if (host) {
host->RequestMouseLock(has_transient_user_activation,
request_unadjusted_movement, std::move(callback));
}
}
void WebFrameWidgetImpl::MouseCaptureLost() {
TRACE_EVENT_NESTABLE_ASYNC_END0("input", "capturing mouse",
TRACE_ID_LOCAL(this));
mouse_capture_element_ = nullptr;
}
void WebFrameWidgetImpl::ApplyVisualProperties(
const VisualProperties& visual_properties) {
widget_base_->UpdateVisualProperties(visual_properties);
}
bool WebFrameWidgetImpl::IsFullscreenGranted() {
return is_fullscreen_granted_;
}
bool WebFrameWidgetImpl::PinchGestureActiveInMainFrame() {
return is_pinch_gesture_active_in_mainframe_;
}
float WebFrameWidgetImpl::PageScaleInMainFrame() {
return page_scale_factor_in_mainframe_;
}
void WebFrameWidgetImpl::UpdateSurfaceAndScreenInfo(
const viz::LocalSurfaceId& new_local_surface_id,
const gfx::Rect& compositor_viewport_pixel_rect,
const ScreenInfo& new_screen_info) {
widget_base_->UpdateSurfaceAndScreenInfo(
new_local_surface_id, compositor_viewport_pixel_rect, new_screen_info);
}
void WebFrameWidgetImpl::UpdateScreenInfo(const ScreenInfo& new_screen_info) {
widget_base_->UpdateScreenInfo(new_screen_info);
}
void WebFrameWidgetImpl::UpdateSurfaceAndCompositorRect(
const viz::LocalSurfaceId& new_local_surface_id,
const gfx::Rect& compositor_viewport_pixel_rect) {
widget_base_->UpdateSurfaceAndCompositorRect(new_local_surface_id,
compositor_viewport_pixel_rect);
}
void WebFrameWidgetImpl::UpdateCompositorViewportRect(
const gfx::Rect& compositor_viewport_pixel_rect) {
widget_base_->UpdateCompositorViewportRect(compositor_viewport_pixel_rect);
}
const ScreenInfo& WebFrameWidgetImpl::GetScreenInfo() {
return widget_base_->GetScreenInfo();
}
gfx::Rect WebFrameWidgetImpl::WindowRect() {
return widget_base_->WindowRect();
}
gfx::Rect WebFrameWidgetImpl::ViewRect() {
return widget_base_->ViewRect();
}
void WebFrameWidgetImpl::SetScreenRects(const gfx::Rect& widget_screen_rect,
const gfx::Rect& window_screen_rect) {
widget_base_->SetScreenRects(widget_screen_rect, window_screen_rect);
}
gfx::Size WebFrameWidgetImpl::VisibleViewportSizeInDIPs() {
return widget_base_->VisibleViewportSizeInDIPs();
}
void WebFrameWidgetImpl::SetPendingWindowRect(
const gfx::Rect& window_screen_rect) {
widget_base_->SetPendingWindowRect(window_screen_rect);
}
void WebFrameWidgetImpl::AckPendingWindowRect() {
widget_base_->AckPendingWindowRect();
}
bool WebFrameWidgetImpl::IsHidden() const {
return widget_base_->is_hidden();
}
WebString WebFrameWidgetImpl::GetLastToolTipTextForTesting() const {
return GetPage()->GetChromeClient().GetLastToolTipTextForTesting();
}
float WebFrameWidgetImpl::GetEmulatorScale() {
if (device_emulator_)
return device_emulator_->scale();
return 1.0f;
}
void WebFrameWidgetImpl::IntrinsicSizingInfoChanged(
mojom::blink::IntrinsicSizingInfoPtr sizing_info) {
DCHECK(ForSubframe());
GetAssociatedFrameWidgetHost()->IntrinsicSizingInfoChanged(
std::move(sizing_info));
}
void WebFrameWidgetImpl::AutoscrollStart(const gfx::PointF& position) {
GetAssociatedFrameWidgetHost()->AutoscrollStart(std::move(position));
}
void WebFrameWidgetImpl::AutoscrollFling(const gfx::Vector2dF& velocity) {
GetAssociatedFrameWidgetHost()->AutoscrollFling(std::move(velocity));
}
void WebFrameWidgetImpl::AutoscrollEnd() {
GetAssociatedFrameWidgetHost()->AutoscrollEnd();
}
void WebFrameWidgetImpl::DidMeaningfulLayout(WebMeaningfulLayout layout_type) {
if (layout_type == blink::WebMeaningfulLayout::kVisuallyNonEmpty) {
NotifySwapAndPresentationTime(
base::NullCallback(),
WTF::Bind(&WebFrameWidgetImpl::PresentationCallbackForMeaningfulLayout,
WrapPersistent(this)));
}
ForEachLocalFrameControlledByWidget(
local_root_->GetFrame(),
WTF::BindRepeating(
[](WebMeaningfulLayout layout_type, WebLocalFrameImpl* local_frame) {
local_frame->Client()->DidMeaningfulLayout(layout_type);
},
layout_type));
}
void WebFrameWidgetImpl::PresentationCallbackForMeaningfulLayout(
blink::WebSwapResult,
base::TimeTicks) {
GetAssociatedFrameWidgetHost()->DidFirstVisuallyNonEmptyPaint();
}
void WebFrameWidgetImpl::RequestAnimationAfterDelay(
const base::TimeDelta& delay) {
widget_base_->RequestAnimationAfterDelay(delay);
}
void WebFrameWidgetImpl::SetRootLayer(scoped_refptr<cc::Layer> layer) {
if (!View()->does_composite()) {
DCHECK(ForMainFrame());
DCHECK(!layer);
return;
}
// Set up some initial state before we are setting the layer.
if (ForSubframe() && layer) {
// Child local roots will always have a transparent background color.
widget_base_->LayerTreeHost()->set_background_color(SK_ColorTRANSPARENT);
// Pass the limits even though this is for subframes, as the limits will
// be needed in setting the raster scale.
SetPageScaleStateAndLimits(1.f, false /* is_pinch_gesture_active */,
View()->MinimumPageScaleFactor(),
View()->MaximumPageScaleFactor());
}
bool root_layer_exists = !!layer;
widget_base_->LayerTreeHost()->SetRootLayer(std::move(layer));
// Notify the WebView that we did set a layer.
if (ForMainFrame()) {
View()->DidChangeRootLayer(root_layer_exists);
}
}
base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>
WebFrameWidgetImpl::EnsureCompositorMutatorDispatcher(
scoped_refptr<base::SingleThreadTaskRunner> mutator_task_runner) {
if (!mutator_task_runner_) {
mutator_task_runner_ = std::move(mutator_task_runner);
widget_base_->LayerTreeHost()->SetLayerTreeMutator(
AnimationWorkletMutatorDispatcherImpl::CreateCompositorThreadClient(
mutator_dispatcher_, mutator_task_runner_));
}
DCHECK(mutator_task_runner_);
return mutator_dispatcher_;
}
HitTestResult WebFrameWidgetImpl::CoreHitTestResultAt(
const gfx::PointF& point_in_viewport) {
LocalFrameView* view = LocalRootImpl()->GetFrameView();
FloatPoint point_in_root_frame(
view->ViewportToFrame(FloatPoint(point_in_viewport)));
return HitTestResultForRootFramePos(point_in_root_frame);
}
cc::AnimationHost* WebFrameWidgetImpl::AnimationHost() const {
return widget_base_->AnimationHost();
}
base::WeakPtr<PaintWorkletPaintDispatcher>
WebFrameWidgetImpl::EnsureCompositorPaintDispatcher(
scoped_refptr<base::SingleThreadTaskRunner>* paint_task_runner) {
// We check paint_task_runner_ not paint_dispatcher_ because the dispatcher is
// a base::WeakPtr that should only be used on the compositor thread.
if (!paint_task_runner_) {
widget_base_->LayerTreeHost()->SetPaintWorkletLayerPainter(
PaintWorkletPaintDispatcher::CreateCompositorThreadPainter(
&paint_dispatcher_));
paint_task_runner_ = Thread::CompositorThread()->GetTaskRunner();
}
DCHECK(paint_task_runner_);
*paint_task_runner = paint_task_runner_;
return paint_dispatcher_;
}
void WebFrameWidgetImpl::SetDelegatedInkMetadata(
std::unique_ptr<viz::DelegatedInkMetadata> metadata) {
widget_base_->LayerTreeHost()->SetDelegatedInkMetadata(std::move(metadata));
}
// Enables measuring and reporting both presentation times and swap times in
// swap promises.
class ReportTimeSwapPromise : public cc::SwapPromise {
public:
ReportTimeSwapPromise(WebReportTimeCallback swap_time_callback,
WebReportTimeCallback presentation_time_callback,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
WebFrameWidgetImpl* widget)
: swap_time_callback_(std::move(swap_time_callback)),
presentation_time_callback_(std::move(presentation_time_callback)),
task_runner_(std::move(task_runner)),
widget_(widget) {}
~ReportTimeSwapPromise() override = default;
void DidActivate() override {}
void WillSwap(viz::CompositorFrameMetadata* metadata) override {
DCHECK_GT(metadata->frame_token, 0u);
// The interval between the current swap and its presentation time is
// reported in UMA (see corresponding code in DidSwap() below).
frame_token_ = metadata->frame_token;
}
void DidSwap() override {
DCHECK_GT(frame_token_, 0u);
PostCrossThreadTask(
*task_runner_, FROM_HERE,
CrossThreadBindOnce(
&RunCallbackAfterSwap, widget_, base::TimeTicks::Now(),
std::move(swap_time_callback_),
std::move(presentation_time_callback_), frame_token_));
}
cc::SwapPromise::DidNotSwapAction DidNotSwap(
DidNotSwapReason reason) override {
WebSwapResult result;
switch (reason) {
case cc::SwapPromise::DidNotSwapReason::SWAP_FAILS:
result = WebSwapResult::kDidNotSwapSwapFails;
break;
case cc::SwapPromise::DidNotSwapReason::COMMIT_FAILS:
result = WebSwapResult::kDidNotSwapCommitFails;
break;
case cc::SwapPromise::DidNotSwapReason::COMMIT_NO_UPDATE:
result = WebSwapResult::kDidNotSwapCommitNoUpdate;
break;
case cc::SwapPromise::DidNotSwapReason::ACTIVATION_FAILS:
result = WebSwapResult::kDidNotSwapActivationFails;
break;
}
// During a failed swap, return the current time regardless of whether we're
// using presentation or swap timestamps.
PostCrossThreadTask(
*task_runner_, FROM_HERE,
CrossThreadBindOnce(
[](WebSwapResult result, base::TimeTicks swap_time,
WebReportTimeCallback swap_time_callback,
WebReportTimeCallback presentation_time_callback) {
ReportTime(std::move(swap_time_callback), result, swap_time);
ReportTime(std::move(presentation_time_callback), result,
swap_time);
},
result, base::TimeTicks::Now(), std::move(swap_time_callback_),
std::move(presentation_time_callback_)));
return DidNotSwapAction::BREAK_PROMISE;
}
int64_t TraceId() const override { return 0; }
private:
static void RunCallbackAfterSwap(
WebFrameWidgetImpl* widget,
base::TimeTicks swap_time,
WebReportTimeCallback swap_time_callback,
WebReportTimeCallback presentation_time_callback,
int frame_token) {
// If the widget was collected or the widget wasn't collected yet, but
// it was closed don't schedule a presentation callback.
if (widget && widget->widget_base_) {
widget->widget_base_->AddPresentationCallback(
frame_token,
WTF::Bind(&RunCallbackAfterPresentation,
std::move(presentation_time_callback), swap_time));
ReportTime(std::move(swap_time_callback), WebSwapResult::kDidSwap,
swap_time);
} else {
ReportTime(std::move(swap_time_callback), WebSwapResult::kDidSwap,
swap_time);
ReportTime(std::move(presentation_time_callback), WebSwapResult::kDidSwap,
swap_time);
}
}
static void RunCallbackAfterPresentation(
WebReportTimeCallback presentation_time_callback,
base::TimeTicks swap_time,
base::TimeTicks presentation_time) {
DCHECK(!swap_time.is_null());
bool presentation_time_is_valid =
!presentation_time.is_null() && (presentation_time > swap_time);
UMA_HISTOGRAM_BOOLEAN("PageLoad.Internal.Renderer.PresentationTime.Valid",
presentation_time_is_valid);
if (presentation_time_is_valid) {
// This measures from 1ms to 10seconds.
UMA_HISTOGRAM_TIMES(
"PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime",
presentation_time - swap_time);
}
ReportTime(std::move(presentation_time_callback), WebSwapResult::kDidSwap,
presentation_time_is_valid ? presentation_time : swap_time);
}
static void ReportTime(WebReportTimeCallback callback,
WebSwapResult result,
base::TimeTicks time) {
if (callback)
std::move(callback).Run(result, time);
}
WebReportTimeCallback swap_time_callback_;
WebReportTimeCallback presentation_time_callback_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
CrossThreadWeakPersistent<WebFrameWidgetImpl> widget_;
uint32_t frame_token_ = 0;
DISALLOW_COPY_AND_ASSIGN(ReportTimeSwapPromise);
};
void WebFrameWidgetImpl::NotifyPresentationTimeInBlink(
WebReportTimeCallback presentation_time_callback) {
NotifySwapAndPresentationTime(base::NullCallback(),
std::move(presentation_time_callback));
}
void WebFrameWidgetImpl::NotifySwapAndPresentationTime(
WebReportTimeCallback swap_time_callback,
WebReportTimeCallback presentation_time_callback) {
if (!View()->does_composite())
return;
widget_base_->LayerTreeHost()->QueueSwapPromise(
std::make_unique<ReportTimeSwapPromise>(
std::move(swap_time_callback), std::move(presentation_time_callback),
widget_base_->LayerTreeHost()
->GetTaskRunnerProvider()
->MainThreadTaskRunner(),
this));
}
scheduler::WebRenderWidgetSchedulingState*
WebFrameWidgetImpl::RendererWidgetSchedulingState() {
return widget_base_->RendererWidgetSchedulingState();
}
void WebFrameWidgetImpl::WaitForDebuggerWhenShown() {
local_root_->WaitForDebuggerWhenShown();
}
void WebFrameWidgetImpl::SetTextZoomFactor(float text_zoom_factor) {
local_root_->GetFrame()->SetTextZoomFactor(text_zoom_factor);
}
float WebFrameWidgetImpl::TextZoomFactor() {
return local_root_->GetFrame()->TextZoomFactor();
}
void WebFrameWidgetImpl::SetMainFrameOverlayColor(SkColor color) {
DCHECK(!local_root_->Parent());
local_root_->GetFrame()->SetMainFrameColorOverlay(color);
}
void WebFrameWidgetImpl::AddEditCommandForNextKeyEvent(const WebString& name,
const WebString& value) {
edit_commands_.push_back(mojom::blink::EditCommand::New(name, value));
}
bool WebFrameWidgetImpl::HandleCurrentKeyboardEvent() {
bool did_execute_command = false;
WebLocalFrame* frame = FocusedWebLocalFrameInWidget();
if (!frame)
frame = local_root_;
for (const auto& command : edit_commands_) {
// In gtk and cocoa, it's possible to bind multiple edit commands to one
// key (but it's the exception). Once one edit command is not executed, it
// seems safest to not execute the rest.
if (!frame->ExecuteCommand(command->name, command->value))
break;
did_execute_command = true;
}
return did_execute_command;
}
void WebFrameWidgetImpl::ClearEditCommands() {
edit_commands_ = Vector<mojom::blink::EditCommandPtr>();
}
WebTextInputInfo WebFrameWidgetImpl::TextInputInfo() {
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller)
return WebTextInputInfo();
return controller->TextInputInfo();
}
ui::mojom::blink::VirtualKeyboardVisibilityRequest
WebFrameWidgetImpl::GetLastVirtualKeyboardVisibilityRequest() {
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller)
return ui::mojom::blink::VirtualKeyboardVisibilityRequest::NONE;
return controller->GetLastVirtualKeyboardVisibilityRequest();
}
bool WebFrameWidgetImpl::ShouldSuppressKeyboardForFocusedElement() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return false;
return focused_frame->ShouldSuppressKeyboardForFocusedElement();
}
void WebFrameWidgetImpl::GetEditContextBoundsInWindow(
base::Optional<gfx::Rect>* edit_context_control_bounds,
base::Optional<gfx::Rect>* edit_context_selection_bounds) {
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller)
return;
gfx::Rect control_bounds;
gfx::Rect selection_bounds;
controller->GetLayoutBounds(&control_bounds, &selection_bounds);
*edit_context_control_bounds =
widget_base_->BlinkSpaceToEnclosedDIPs(control_bounds);
if (controller->IsEditContextActive()) {
*edit_context_selection_bounds =
widget_base_->BlinkSpaceToEnclosedDIPs(selection_bounds);
}
}
int32_t WebFrameWidgetImpl::ComputeWebTextInputNextPreviousFlags() {
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller)
return 0;
return controller->ComputeWebTextInputNextPreviousFlags();
}
void WebFrameWidgetImpl::ResetVirtualKeyboardVisibilityRequest() {
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller)
return;
controller->SetVirtualKeyboardVisibilityRequest(
ui::mojom::blink::VirtualKeyboardVisibilityRequest::NONE);
;
}
bool WebFrameWidgetImpl::GetSelectionBoundsInWindow(
gfx::Rect* focus,
gfx::Rect* anchor,
base::i18n::TextDirection* focus_dir,
base::i18n::TextDirection* anchor_dir,
bool* is_anchor_first) {
if (ShouldDispatchImeEventsToPlugin()) {
// TODO(kinaba) http://crbug.com/101101
// Current Pepper IME API does not handle selection bounds. So we simply
// use the caret position as an empty range for now. It will be updated
// after Pepper API equips features related to surrounding text retrieval.
gfx::Rect pepper_caret_in_dips = widget_base_->BlinkSpaceToEnclosedDIPs(
GetFocusedPluginContainer()->GetPluginCaretBounds());
if (pepper_caret_in_dips == *focus && pepper_caret_in_dips == *anchor)
return false;
*focus = pepper_caret_in_dips;
*anchor = *focus;
return true;
}
gfx::Rect focus_root_frame;
gfx::Rect anchor_root_frame;
CalculateSelectionBounds(focus_root_frame, anchor_root_frame);
gfx::Rect focus_rect_in_dips =
widget_base_->BlinkSpaceToEnclosedDIPs(gfx::Rect(focus_root_frame));
gfx::Rect anchor_rect_in_dips =
widget_base_->BlinkSpaceToEnclosedDIPs(gfx::Rect(anchor_root_frame));
// if the bounds are the same return false.
if (focus_rect_in_dips == *focus && anchor_rect_in_dips == *anchor)
return false;
*focus = focus_rect_in_dips;
*anchor = anchor_rect_in_dips;
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return true;
focused_frame->SelectionTextDirection(*focus_dir, *anchor_dir);
*is_anchor_first = focused_frame->IsSelectionAnchorFirst();
return true;
}
void WebFrameWidgetImpl::ClearTextInputState() {
widget_base_->ClearTextInputState();
}
bool WebFrameWidgetImpl::IsPasting() {
return widget_base_->is_pasting();
}
bool WebFrameWidgetImpl::HandlingSelectRange() {
return widget_base_->handling_select_range();
}
void WebFrameWidgetImpl::SetFocus(bool focus) {
widget_base_->SetFocus(focus);
}
bool WebFrameWidgetImpl::HasFocus() {
return widget_base_->has_focus();
}
void WebFrameWidgetImpl::SetToolTipText(const String& tooltip_text,
TextDirection dir) {
widget_base_->SetToolTipText(tooltip_text, dir);
}
void WebFrameWidgetImpl::DidOverscroll(
const gfx::Vector2dF& overscroll_delta,
const gfx::Vector2dF& accumulated_overscroll,
const gfx::PointF& position,
const gfx::Vector2dF& velocity) {
#if defined(OS_MAC)
// On OSX the user can disable the elastic overscroll effect. If that's the
// case, don't forward the overscroll notification.
if (!widget_base_->LayerTreeHost()->GetSettings().enable_elastic_overscroll)
return;
#endif
cc::OverscrollBehavior overscroll_behavior =
widget_base_->LayerTreeHost()->overscroll_behavior();
if (!widget_base_->input_handler().DidOverscrollFromBlink(
overscroll_delta, accumulated_overscroll, position, velocity,
overscroll_behavior))
return;
// If we're currently handling an event, stash the overscroll data such that
// it can be bundled in the event ack.
if (mojom::blink::WidgetInputHandlerHost* host =
widget_base_->widget_input_handler_manager()
->GetWidgetInputHandlerHost()) {
host->DidOverscroll(mojom::blink::DidOverscrollParams::New(
accumulated_overscroll, overscroll_delta, velocity, position,
overscroll_behavior));
}
}
void WebFrameWidgetImpl::InjectGestureScrollEvent(
blink::WebGestureDevice device,
const gfx::Vector2dF& delta,
ui::ScrollGranularity granularity,
cc::ElementId scrollable_area_element_id,
blink::WebInputEvent::Type injected_type) {
if (RuntimeEnabledFeatures::ScrollUnificationEnabled()) {
// create a GestureScroll Event and post it to the compositor thread
// TODO(crbug.com/1126098) use original input event's timestamp.
// TODO(crbug.com/1082590) ensure continuity in scroll metrics collection
base::TimeTicks now = base::TimeTicks::Now();
std::unique_ptr<WebGestureEvent> gesture_event =
WebGestureEvent::GenerateInjectedScrollGesture(
injected_type, now, device, gfx::PointF(0, 0), delta, granularity);
if (injected_type == WebInputEvent::Type::kGestureScrollBegin) {
gesture_event->data.scroll_begin.scrollable_area_element_id =
scrollable_area_element_id.GetStableId();
gesture_event->data.scroll_begin.main_thread_hit_tested = true;
}
widget_base_->widget_input_handler_manager()
->DispatchScrollGestureToCompositor(std::move(gesture_event));
} else {
widget_base_->input_handler().InjectGestureScrollEvent(
device, delta, granularity, scrollable_area_element_id, injected_type);
}
}
void WebFrameWidgetImpl::DidChangeCursor(const ui::Cursor& cursor) {
widget_base_->SetCursor(cursor);
}
bool WebFrameWidgetImpl::SetComposition(
const String& text,
const Vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& replacement_range,
int selection_start,
int selection_end) {
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller)
return false;
return controller->SetComposition(
text, ime_text_spans,
replacement_range.IsValid()
? WebRange(replacement_range.start(), replacement_range.length())
: WebRange(),
selection_start, selection_end);
}
void WebFrameWidgetImpl::CommitText(
const String& text,
const Vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& replacement_range,
int relative_cursor_pos) {
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller)
return;
controller->CommitText(
text, ime_text_spans,
replacement_range.IsValid()
? WebRange(replacement_range.start(), replacement_range.length())
: WebRange(),
relative_cursor_pos);
}
void WebFrameWidgetImpl::FinishComposingText(bool keep_selection) {
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller)
return;
controller->FinishComposingText(
keep_selection ? WebInputMethodController::kKeepSelection
: WebInputMethodController::kDoNotKeepSelection);
}
bool WebFrameWidgetImpl::IsProvisional() {
return LocalRoot()->IsProvisional();
}
uint64_t WebFrameWidgetImpl::GetScrollableContainerIdAt(
const gfx::PointF& point_in_dips) {
gfx::PointF point = widget_base_->DIPsToBlinkSpace(point_in_dips);
return HitTestResultAt(point).GetScrollableContainerId();
}
bool WebFrameWidgetImpl::ShouldHandleImeEvents() {
if (ForMainFrame()) {
return HasFocus();
} else {
// TODO(ekaramad): main frame widget returns true only if it has focus.
// We track page focus in all WebViews on the page but the WebFrameWidgets
// corresponding to child local roots do not get the update. For now, this
// method returns true when the WebFrameWidget is for a child local frame,
// i.e., IME events will be processed regardless of page focus. We should
// revisit this after page focus for OOPIFs has been fully resolved
// (https://crbug.com/689777).
return LocalRootImpl();
}
}
void WebFrameWidgetImpl::SetEditCommandsForNextKeyEvent(
Vector<mojom::blink::EditCommandPtr> edit_commands) {
edit_commands_ = std::move(edit_commands);
}
void WebFrameWidgetImpl::FocusChangeComplete() {
blink::WebLocalFrame* focused = LocalRoot()->View()->FocusedFrame();
if (focused && focused->AutofillClient())
focused->AutofillClient()->DidCompleteFocusChangeInFrame();
}
void WebFrameWidgetImpl::ShowVirtualKeyboardOnElementFocus() {
widget_base_->ShowVirtualKeyboardOnElementFocus();
}
void WebFrameWidgetImpl::ProcessTouchAction(WebTouchAction touch_action) {
widget_base_->ProcessTouchAction(touch_action);
}
void WebFrameWidgetImpl::DidHandleGestureEvent(const WebGestureEvent& event) {
#if defined(OS_ANDROID) || defined(USE_AURA)
if (event.GetType() == WebInputEvent::Type::kGestureTap) {
widget_base_->ShowVirtualKeyboard();
} else if (event.GetType() == WebInputEvent::Type::kGestureLongPress) {
WebInputMethodController* controller = GetActiveWebInputMethodController();
if (!controller || controller->TextInputInfo().value.IsEmpty())
widget_base_->UpdateTextInputState();
else
widget_base_->ShowVirtualKeyboard();
}
#endif
}
void WebFrameWidgetImpl::SetHasPointerRawUpdateEventHandlers(
bool has_handlers) {
widget_base_->widget_input_handler_manager()
->input_event_queue()
->HasPointerRawUpdateEventHandlers(has_handlers);
}
void WebFrameWidgetImpl::SetNeedsLowLatencyInput(bool needs_low_latency) {
widget_base_->widget_input_handler_manager()
->input_event_queue()
->SetNeedsLowLatency(needs_low_latency);
}
void WebFrameWidgetImpl::RequestUnbufferedInputEvents() {
widget_base_->widget_input_handler_manager()
->input_event_queue()
->RequestUnbufferedInputEvents();
}
void WebFrameWidgetImpl::SetNeedsUnbufferedInputForDebugger(bool unbuffered) {
widget_base_->widget_input_handler_manager()
->input_event_queue()
->SetNeedsUnbufferedInputForDebugger(unbuffered);
}
void WebFrameWidgetImpl::DidNavigate() {
// The input handler wants to know about navigation so that it can
// suppress input until the newly navigated page has a committed frame.
// It also resets the state for UMA reporting of input arrival with respect
// to document lifecycle.
if (!widget_base_->widget_input_handler_manager())
return;
widget_base_->widget_input_handler_manager()->DidNavigate();
}
void WebFrameWidgetImpl::SetMouseCapture(bool capture) {
if (mojom::blink::WidgetInputHandlerHost* host =
widget_base_->widget_input_handler_manager()
->GetWidgetInputHandlerHost()) {
host->SetMouseCapture(capture);
}
}
gfx::Range WebFrameWidgetImpl::CompositionRange() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame || ShouldDispatchImeEventsToPlugin())
return gfx::Range::InvalidRange();
blink::WebInputMethodController* controller =
focused_frame->GetInputMethodController();
WebRange web_range = controller->CompositionRange();
if (web_range.IsNull())
return gfx::Range::InvalidRange();
return gfx::Range(web_range.StartOffset(), web_range.EndOffset());
}
void WebFrameWidgetImpl::GetCompositionCharacterBoundsInWindow(
Vector<gfx::Rect>* bounds_in_dips) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame || ShouldDispatchImeEventsToPlugin())
return;
blink::WebInputMethodController* controller =
focused_frame->GetInputMethodController();
blink::WebVector<gfx::Rect> bounds_from_blink;
if (!controller->GetCompositionCharacterBounds(bounds_from_blink))
return;
for (auto& rect : bounds_from_blink) {
bounds_in_dips->push_back(widget_base_->BlinkSpaceToEnclosedDIPs(rect));
}
}
void WebFrameWidgetImpl::AddImeTextSpansToExistingText(
uint32_t start,
uint32_t end,
const Vector<ui::ImeTextSpan>& ime_text_spans) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->AddImeTextSpansToExistingText(ime_text_spans, start, end);
}
Vector<ui::mojom::blink::ImeTextSpanInfoPtr>
WebFrameWidgetImpl::GetImeTextSpansInfo(
const WebVector<ui::ImeTextSpan>& ime_text_spans) {
auto* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return Vector<ui::mojom::blink::ImeTextSpanInfoPtr>();
Vector<ui::mojom::blink::ImeTextSpanInfoPtr> ime_text_spans_info;
for (const auto& ime_text_span : ime_text_spans) {
gfx::Rect rect;
unsigned length = ime_text_span.end_offset - ime_text_span.start_offset;
focused_frame->FirstRectForCharacterRange(ime_text_span.start_offset,
length, rect);
ime_text_spans_info.push_back(ui::mojom::blink::ImeTextSpanInfo::New(
ime_text_span, widget_base_->BlinkSpaceToEnclosedDIPs(rect)));
}
return ime_text_spans_info;
}
void WebFrameWidgetImpl::ClearImeTextSpansByType(uint32_t start,
uint32_t end,
ui::ImeTextSpan::Type type) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ClearImeTextSpansByType(type, start, end);
}
void WebFrameWidgetImpl::SetCompositionFromExistingText(
int32_t start,
int32_t end,
const Vector<ui::ImeTextSpan>& ime_text_spans) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->SetCompositionFromExistingText(start, end, ime_text_spans);
}
void WebFrameWidgetImpl::ExtendSelectionAndDelete(int32_t before,
int32_t after) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExtendSelectionAndDelete(before, after);
}
void WebFrameWidgetImpl::DeleteSurroundingText(int32_t before, int32_t after) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->DeleteSurroundingText(before, after);
}
void WebFrameWidgetImpl::DeleteSurroundingTextInCodePoints(int32_t before,
int32_t after) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->DeleteSurroundingTextInCodePoints(before, after);
}
void WebFrameWidgetImpl::SetEditableSelectionOffsets(int32_t start,
int32_t end) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->SetEditableSelectionOffsets(start, end);
}
void WebFrameWidgetImpl::ExecuteEditCommand(const String& command,
const String& value) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExecuteCommand(command, value);
}
void WebFrameWidgetImpl::Undo() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExecuteCommand(WebString::FromLatin1("Undo"));
}
void WebFrameWidgetImpl::Redo() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExecuteCommand(WebString::FromLatin1("Redo"));
}
void WebFrameWidgetImpl::Cut() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExecuteCommand(WebString::FromLatin1("Cut"));
}
void WebFrameWidgetImpl::Copy() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExecuteCommand(WebString::FromLatin1("Copy"));
}
void WebFrameWidgetImpl::CopyToFindPboard() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
To<WebLocalFrameImpl>(focused_frame)->CopyToFindPboard();
}
void WebFrameWidgetImpl::Paste() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExecuteCommand(WebString::FromLatin1("Paste"));
}
void WebFrameWidgetImpl::PasteAndMatchStyle() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExecuteCommand(WebString::FromLatin1("PasteAndMatchStyle"));
}
void WebFrameWidgetImpl::Delete() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExecuteCommand(WebString::FromLatin1("Delete"));
}
void WebFrameWidgetImpl::SelectAll() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->ExecuteCommand(WebString::FromLatin1("SelectAll"));
}
void WebFrameWidgetImpl::CollapseSelection() {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
const blink::WebRange& range =
focused_frame->GetInputMethodController()->GetSelectionOffsets();
if (range.IsNull())
return;
focused_frame->SelectRange(blink::WebRange(range.EndOffset(), 0),
blink::WebLocalFrame::kHideSelectionHandle,
mojom::blink::SelectionMenuBehavior::kHide);
}
void WebFrameWidgetImpl::Replace(const String& word) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
if (!focused_frame->HasSelection())
focused_frame->SelectWordAroundCaret();
focused_frame->ReplaceSelection(word);
focused_frame->Client()->SyncSelectionIfRequired();
}
void WebFrameWidgetImpl::ReplaceMisspelling(const String& word) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
if (!focused_frame->HasSelection())
return;
focused_frame->ReplaceMisspelledRange(word);
}
void WebFrameWidgetImpl::SelectRange(const gfx::Point& base_in_dips,
const gfx::Point& extent_in_dips) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->SelectRange(
widget_base_->DIPsToRoundedBlinkSpace(base_in_dips),
widget_base_->DIPsToRoundedBlinkSpace(extent_in_dips));
}
void WebFrameWidgetImpl::AdjustSelectionByCharacterOffset(
int32_t start,
int32_t end,
mojom::blink::SelectionMenuBehavior selection_menu_behavior) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
blink::WebRange range =
focused_frame->GetInputMethodController()->GetSelectionOffsets();
if (range.IsNull())
return;
// Sanity checks to disallow empty and out of range selections.
if (start - end > range.length() || range.StartOffset() + start < 0)
return;
// A negative adjust amount moves the selection towards the beginning of
// the document, a positive amount moves the selection towards the end of
// the document.
focused_frame->SelectRange(blink::WebRange(range.StartOffset() + start,
range.length() + end - start),
blink::WebLocalFrame::kPreserveHandleVisibility,
selection_menu_behavior);
}
void WebFrameWidgetImpl::MoveRangeSelectionExtent(
const gfx::Point& extent_in_dips) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->MoveRangeSelectionExtent(
widget_base_->DIPsToRoundedBlinkSpace(extent_in_dips));
}
void WebFrameWidgetImpl::ScrollFocusedEditableNodeIntoRect(
const gfx::Rect& rect_in_dips) {
WebLocalFrameImpl* local_frame = FocusedWebLocalFrameInWidget();
if (!local_frame)
return;
// OnSynchronizeVisualProperties does not call DidChangeVisibleViewport
// on OOPIFs. Since we are starting a new scroll operation now, call
// DidChangeVisibleViewport to ensure that we don't assume the element
// is already in view and ignore the scroll.
local_frame->ResetHasScrolledFocusedEditableIntoView();
local_frame->ScrollFocusedEditableElementIntoRect(rect_in_dips);
}
void WebFrameWidgetImpl::ZoomToFindInPageRect(
const gfx::Rect& rect_in_root_frame) {
if (ForMainFrame()) {
View()->ZoomToFindInPageRect(rect_in_root_frame);
} else {
GetAssociatedFrameWidgetHost()->ZoomToFindInPageRectInMainFrame(
rect_in_root_frame);
}
}
void WebFrameWidgetImpl::MoveCaret(const gfx::Point& point_in_dips) {
WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame)
return;
focused_frame->MoveCaretSelection(
widget_base_->DIPsToRoundedBlinkSpace(point_in_dips));
}
#if defined(OS_ANDROID)
void WebFrameWidgetImpl::SelectWordAroundCaret(
SelectWordAroundCaretCallback callback) {
auto* focused_frame = FocusedWebLocalFrameInWidget();
if (!focused_frame) {
std::move(callback).Run(false, 0, 0);
return;
}
bool did_select = false;
int start_adjust = 0;
int end_adjust = 0;
blink::WebRange initial_range = focused_frame->SelectionRange();
SetHandlingInputEvent(true);
if (!initial_range.IsNull())
did_select = focused_frame->SelectWordAroundCaret();
if (did_select) {
blink::WebRange adjusted_range = focused_frame->SelectionRange();
DCHECK(!adjusted_range.IsNull());
start_adjust = adjusted_range.StartOffset() - initial_range.StartOffset();
end_adjust = adjusted_range.EndOffset() - initial_range.EndOffset();
}
SetHandlingInputEvent(false);
std::move(callback).Run(did_select, start_adjust, end_adjust);
}
#endif
void WebFrameWidgetImpl::ForEachRemoteFrameControlledByWidget(
const base::RepeatingCallback<void(RemoteFrame*)>& callback) {
ForEachRemoteFrameChildrenControlledByWidget(local_root_->GetFrame(),
callback);
}
void WebFrameWidgetImpl::CalculateSelectionBounds(gfx::Rect& anchor_root_frame,
gfx::Rect& focus_root_frame) {
const LocalFrame* local_frame = FocusedLocalFrameInWidget();
if (!local_frame)
return;
IntRect anchor;
IntRect focus;
if (!local_frame->Selection().ComputeAbsoluteBounds(anchor, focus))
return;
// Apply the visual viewport for main frames this will apply the page scale.
// For subframes it will just be a 1:1 transformation and the browser
// will then apply later transformations to these rects.
VisualViewport& visual_viewport = GetPage()->GetVisualViewport();
anchor_root_frame = visual_viewport.RootFrameToViewport(
local_frame->View()->ConvertToRootFrame(anchor));
focus_root_frame = visual_viewport.RootFrameToViewport(
local_frame->View()->ConvertToRootFrame(focus));
}
void WebFrameWidgetImpl::BatterySavingsChanged(BatterySavingsFlags savings) {
widget_base_->LayerTreeHost()->SetEnableFrameRateThrottling(
savings & kAllowReducedFrameRate);
}
const viz::LocalSurfaceId& WebFrameWidgetImpl::LocalSurfaceIdFromParent() {
return widget_base_->local_surface_id_from_parent();
}
cc::LayerTreeHost* WebFrameWidgetImpl::LayerTreeHost() {
return widget_base_->LayerTreeHost();
}
cc::LayerTreeHost* WebFrameWidgetImpl::LayerTreeHostForTesting() const {
return widget_base_->LayerTreeHost();
}
ScreenMetricsEmulator* WebFrameWidgetImpl::DeviceEmulator() {
return device_emulator_;
}
bool WebFrameWidgetImpl::AutoResizeMode() {
return View()->AutoResizeMode();
}
void WebFrameWidgetImpl::SetScreenMetricsEmulationParameters(
bool enabled,
const DeviceEmulationParams& params) {
if (enabled)
View()->ActivateDevToolsTransform(params);
else
View()->DeactivateDevToolsTransform();
}
void WebFrameWidgetImpl::SetScreenInfoAndSize(
const ScreenInfo& screen_info,
const gfx::Size& widget_size_in_dips,
const gfx::Size& visible_viewport_size_in_dips) {
// Emulation happens on regular main frames which don't use auto-resize mode.
DCHECK(!AutoResizeMode());
UpdateScreenInfo(screen_info);
widget_base_->SetVisibleViewportSizeInDIPs(visible_viewport_size_in_dips);
Resize(widget_base_->DIPsToCeiledBlinkSpace(widget_size_in_dips));
}
float WebFrameWidgetImpl::GetCompositingScaleFactor() {
return compositing_scale_factor_;
}
const cc::LayerTreeDebugState& WebFrameWidgetImpl::GetLayerTreeDebugState() {
return widget_base_->LayerTreeHost()->GetDebugState();
}
void WebFrameWidgetImpl::SetLayerTreeDebugState(
const cc::LayerTreeDebugState& state) {
widget_base_->LayerTreeHost()->SetDebugState(state);
}
void WebFrameWidgetImpl::NotifyCompositingScaleFactorChanged(
float compositing_scale_factor) {
compositing_scale_factor_ = compositing_scale_factor;
// Update the scale factor for remote frames which in turn depends on the
// compositing scale factor set in the widget.
ForEachRemoteFrameControlledByWidget(
WTF::BindRepeating([](RemoteFrame* remote_frame) {
if (remote_frame->View())
remote_frame->View()->UpdateCompositingScaleFactor();
}));
}
void WebFrameWidgetImpl::NotifyPageScaleFactorChanged(
float page_scale_factor,
bool is_pinch_gesture_active) {
// Store the value to give to any new RemoteFrame that will be created as a
// descendant of this widget.
page_scale_factor_in_mainframe_ = page_scale_factor;
is_pinch_gesture_active_in_mainframe_ = is_pinch_gesture_active;
// Push the page scale factor down to any child RemoteFrames.
// TODO(danakj): This ends up setting the page scale factor in the
// RenderWidgetHost of the child WebFrameWidgetImpl, so that it can bounce
// the value down to its WebFrameWidgetImpl. Since this is essentially a
// global value per-page, we could instead store it once in the browser
// (such as in RenderViewHost) and distribute it to each WebFrameWidgetImpl
// from there.
ForEachRemoteFrameControlledByWidget(WTF::BindRepeating(
[](float page_scale_factor, bool is_pinch_gesture_active,
RemoteFrame* remote_frame) {
remote_frame->PageScaleFactorChanged(page_scale_factor,
is_pinch_gesture_active);
},
page_scale_factor, is_pinch_gesture_active));
}
void WebFrameWidgetImpl::SetPageScaleStateAndLimits(
float page_scale_factor,
bool is_pinch_gesture_active,
float minimum,
float maximum) {
widget_base_->LayerTreeHost()->SetPageScaleFactorAndLimits(page_scale_factor,
minimum, maximum);
// Only propagate page scale from the main frame.
if (ForMainFrame()) {
// If page scale hasn't changed, then just return without notifying
// the remote frames.
if (page_scale_factor == page_scale_factor_in_mainframe_ &&
is_pinch_gesture_active == is_pinch_gesture_active_in_mainframe_) {
return;
}
NotifyPageScaleFactorChanged(page_scale_factor, is_pinch_gesture_active);
}
}
bool WebFrameWidgetImpl::UpdateScreenRects(
const gfx::Rect& widget_screen_rect,
const gfx::Rect& window_screen_rect) {
if (!device_emulator_)
return false;
device_emulator_->OnUpdateScreenRects(widget_screen_rect, window_screen_rect);
return true;
}
void WebFrameWidgetImpl::OrientationChanged() {
local_root_->SendOrientationChangeEvent();
}
void WebFrameWidgetImpl::DidUpdateSurfaceAndScreen(
const ScreenInfo& previous_original_screen_info) {
ScreenInfo screen_info = widget_base_->GetScreenInfo();
if (Platform::Current()->IsUseZoomForDSFEnabled()) {
View()->SetZoomFactorForDeviceScaleFactor(screen_info.device_scale_factor);
} else {
View()->SetDeviceScaleFactor(screen_info.device_scale_factor);
}
if (ShouldAutoDetermineCompositingToLCDTextSetting()) {
// This causes compositing state to be modified which dirties the
// document lifecycle. Android Webview relies on the document
// lifecycle being clean after the RenderWidget is initialized, in
// order to send IPCs that query and change compositing state. So
// WebFrameWidgetImpl::Resize() must come after this call, as it runs the
// entire document lifecycle.
View()->GetSettings()->SetPreferCompositingToLCDTextEnabled(
widget_base_->ComputePreferCompositingToLCDText());
}
// When the device scale changes, the size and position of the popup would
// need to be adjusted, which we can't do. Just close the popup, which is
// also consistent with page zoom and resize behavior.
ScreenInfo original_screen_info = GetOriginalScreenInfo();
if (previous_original_screen_info.device_scale_factor !=
original_screen_info.device_scale_factor) {
View()->CancelPagePopup();
}
if (previous_original_screen_info != original_screen_info) {
local_root_->GetFrame()->DomWindow()->screen()->DispatchEvent(
*Event::Create(event_type_names::kChange));
// Propagate changes down to child local root RenderWidgets and
// BrowserPlugins in other frame trees/processes.
ForEachRemoteFrameControlledByWidget(WTF::BindRepeating(
[](const ScreenInfo& original_screen_info, RemoteFrame* remote_frame) {
remote_frame->DidChangeScreenInfo(original_screen_info);
},
original_screen_info));
}
}
gfx::Rect WebFrameWidgetImpl::ViewportVisibleRect() {
if (ForMainFrame()) {
return widget_base_->CompositorViewportRect();
} else {
return child_data().compositor_visible_rect;
}
}
const ScreenInfo& WebFrameWidgetImpl::GetOriginalScreenInfo() {
if (device_emulator_)
return device_emulator_->original_screen_info();
return widget_base_->GetScreenInfo();
}
base::Optional<blink::mojom::ScreenOrientation>
WebFrameWidgetImpl::ScreenOrientationOverride() {
return View()->ScreenOrientationOverride();
}
void WebFrameWidgetImpl::WasHidden() {
ForEachLocalFrameControlledByWidget(
local_root_->GetFrame(),
WTF::BindRepeating([](WebLocalFrameImpl* local_frame) {
local_frame->Client()->WasHidden();
}));
}
void WebFrameWidgetImpl::WasShown(bool was_evicted) {
ForEachLocalFrameControlledByWidget(
local_root_->GetFrame(),
WTF::BindRepeating([](WebLocalFrameImpl* local_frame) {
local_frame->Client()->WasShown();
}));
if (was_evicted) {
ForEachRemoteFrameControlledByWidget(
WTF::BindRepeating([](RemoteFrame* remote_frame) {
// On eviction, the last SurfaceId is invalidated. We need to
// allocate a new id.
remote_frame->ResendVisualProperties();
}));
}
}
void WebFrameWidgetImpl::RunPaintBenchmark(int repeat_count,
cc::PaintBenchmarkResult& result) {
if (!ForMainFrame())
return;
if (auto* frame_view = LocalRootImpl()->GetFrameView())
frame_view->RunPaintBenchmark(repeat_count, result);
}
void WebFrameWidgetImpl::NotifyInputObservers(
const WebCoalescedInputEvent& coalesced_event) {
LocalFrame* frame = FocusedLocalFrameInWidget();
if (!frame)
return;
LocalFrameView* frame_view = frame->View();
if (!frame_view)
return;
const WebInputEvent& input_event = coalesced_event.Event();
auto& paint_timing_detector = frame_view->GetPaintTimingDetector();
if (paint_timing_detector.NeedToNotifyInputOrScroll())
paint_timing_detector.NotifyInputEvent(input_event.GetType());
}
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();
}
HitTestResult WebFrameWidgetImpl::HitTestResultForRootFramePos(
const FloatPoint& pos_in_root_frame) {
FloatPoint doc_point =
LocalRootImpl()->GetFrame()->View()->ConvertFromRootFrame(
pos_in_root_frame);
HitTestLocation location(doc_point);
HitTestResult result =
LocalRootImpl()->GetFrame()->View()->HitTestWithThrottlingAllowed(
location, HitTestRequest::kReadOnly | HitTestRequest::kActive);
result.SetToShadowHostIfInRestrictedShadowRoot();
return result;
}
bool WebFrameWidgetImpl::SynchronousResizeModeForTestingEnabled() {
return main_data().synchronous_resize_mode_for_testing;
}
KURL WebFrameWidgetImpl::GetURLForDebugTrace() {
WebFrame* main_frame = View()->MainFrame();
if (main_frame->IsWebLocalFrame())
return main_frame->ToWebLocalFrame()->GetDocument().Url();
return {};
}
float WebFrameWidgetImpl::GetTestingDeviceScaleFactorOverride() {
return device_scale_factor_for_testing_;
}
void WebFrameWidgetImpl::ReleaseMouseLockAndPointerCaptureForTesting() {
GetPage()->GetPointerLockController().ExitPointerLock();
MouseCaptureLost();
}
const viz::FrameSinkId& WebFrameWidgetImpl::GetFrameSinkId() {
// It is valid to create a WebFrameWidget with an invalid frame sink id for
// printing and placeholders. But if we go to use it, it should be valid.
DCHECK(frame_sink_id_.is_valid());
return frame_sink_id_;
}
WebHitTestResult WebFrameWidgetImpl::HitTestResultAt(const gfx::PointF& point) {
return CoreHitTestResultAt(point);
}
void WebFrameWidgetImpl::SetZoomLevelForTesting(double zoom_level) {
DCHECK(ForMainFrame());
DCHECK_NE(zoom_level, -INFINITY);
zoom_level_for_testing_ = zoom_level;
SetZoomLevel(zoom_level);
}
void WebFrameWidgetImpl::ResetZoomLevelForTesting() {
DCHECK(ForMainFrame());
zoom_level_for_testing_ = -INFINITY;
SetZoomLevel(0);
}
void WebFrameWidgetImpl::SetDeviceScaleFactorForTesting(float factor) {
DCHECK(ForMainFrame());
DCHECK_GE(factor, 0.f);
// Stash the window size before we adjust the scale factor, as subsequent
// calls to convert will use the new scale factor.
gfx::Size size_in_dips = widget_base_->BlinkSpaceToFlooredDIPs(Size());
device_scale_factor_for_testing_ = factor;
// Receiving a 0 is used to reset between tests, it removes the override in
// order to listen to the browser for the next test.
if (!factor)
return;
// We are changing the device scale factor from the renderer, so allocate a
// new viz::LocalSurfaceId to avoid surface invariants violations in tests.
widget_base_->LayerTreeHost()->RequestNewLocalSurfaceId();
ScreenInfo info = widget_base_->GetScreenInfo();
info.device_scale_factor = factor;
gfx::Size size_with_dsf = gfx::ScaleToCeiledSize(size_in_dips, factor);
widget_base_->UpdateCompositorViewportAndScreenInfo(gfx::Rect(size_with_dsf),
info);
if (!AutoResizeMode()) {
// This picks up the new device scale factor as
// `UpdateCompositorViewportAndScreenInfo()` has applied a new value.
Resize(widget_base_->DIPsToCeiledBlinkSpace(size_in_dips));
}
}
FrameWidgetTestHelper*
WebFrameWidgetImpl::GetFrameWidgetTestHelperForTesting() {
return nullptr;
}
WebPlugin* WebFrameWidgetImpl::GetFocusedPluginContainer() {
LocalFrame* focused_frame = FocusedLocalFrameInWidget();
if (!focused_frame)
return nullptr;
if (auto* container = focused_frame->GetWebPluginContainer())
return container->Plugin();
return nullptr;
}
bool WebFrameWidgetImpl::HasPendingPageScaleAnimation() {
return LayerTreeHost()->HasPendingPageScaleAnimation();
}
void WebFrameWidgetImpl::SetSourceURLForCompositor(ukm::SourceId source_id,
const KURL& url) {
LayerTreeHost()->SetSourceURL(source_id, url);
}
base::ReadOnlySharedMemoryRegion
WebFrameWidgetImpl::CreateSharedMemoryForSmoothnessUkm() {
return LayerTreeHost()->CreateSharedMemoryForSmoothnessUkm();
}
bool WebFrameWidgetImpl::CanComposeInline() {
if (auto* plugin = GetFocusedPluginContainer())
return plugin->CanComposeInline();
return true;
}
bool WebFrameWidgetImpl::ShouldDispatchImeEventsToPlugin() {
if (auto* plugin = GetFocusedPluginContainer())
return plugin->ShouldDispatchImeEventsToPlugin();
return false;
}
void WebFrameWidgetImpl::ImeSetCompositionForPlugin(
const String& text,
const Vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& replacement_range,
int selection_start,
int selection_end) {
if (auto* plugin = GetFocusedPluginContainer()) {
plugin->ImeSetCompositionForPlugin(
text,
std::vector<ui::ImeTextSpan>(ime_text_spans.begin(),
ime_text_spans.end()),
replacement_range, selection_start, selection_end);
}
}
void WebFrameWidgetImpl::ImeCommitTextForPlugin(
const String& text,
const Vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& replacement_range,
int relative_cursor_pos) {
if (auto* plugin = GetFocusedPluginContainer()) {
plugin->ImeCommitTextForPlugin(
text,
std::vector<ui::ImeTextSpan>(ime_text_spans.begin(),
ime_text_spans.end()),
replacement_range, relative_cursor_pos);
}
}
void WebFrameWidgetImpl::ImeFinishComposingTextForPlugin(bool keep_selection) {
if (auto* plugin = GetFocusedPluginContainer())
plugin->ImeFinishComposingTextForPlugin(keep_selection);
}
void WebFrameWidgetImpl::SetWindowRect(const gfx::Rect& window_rect) {
DCHECK(ForMainFrame());
if (SynchronousResizeModeForTestingEnabled()) {
// This is a web-test-only path. At one point, it was planned to be
// removed. See https://crbug.com/309760.
SetWindowRectSynchronously(window_rect);
return;
}
SetPendingWindowRect(window_rect);
View()->SendWindowRectToMainFrameHost(
window_rect, WTF::Bind(&WebFrameWidgetImpl::AckPendingWindowRect,
WrapWeakPersistent(this)));
}
void WebFrameWidgetImpl::SetWindowRectSynchronouslyForTesting(
const gfx::Rect& new_window_rect) {
DCHECK(ForMainFrame());
SetWindowRectSynchronously(new_window_rect);
}
void WebFrameWidgetImpl::SetWindowRectSynchronously(
const gfx::Rect& new_window_rect) {
// This method is only call in tests, and it applies the |new_window_rect| to
// all three of:
// a) widget size (in |size_|)
// b) blink viewport (in |visible_viewport_size_|)
// c) compositor viewport (in cc::LayerTreeHost)
// Normally the browser controls these three things independently, but this is
// used in tests to control the size from the renderer.
// We are resizing the window from the renderer, so allocate a new
// viz::LocalSurfaceId to avoid surface invariants violations in tests.
widget_base_->LayerTreeHost()->RequestNewLocalSurfaceId();
gfx::Rect compositor_viewport_pixel_rect(gfx::ScaleToCeiledSize(
new_window_rect.size(),
widget_base_->GetScreenInfo().device_scale_factor));
widget_base_->UpdateSurfaceAndScreenInfo(
widget_base_->local_surface_id_from_parent(),
compositor_viewport_pixel_rect, widget_base_->GetScreenInfo());
Resize(new_window_rect.size());
widget_base_->SetScreenRects(new_window_rect, new_window_rect);
}
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 (ForSubframe() && !size_) {
child_data().did_suspend_parsing = true;
LocalRootImpl()->GetFrame()->Loader().GetDocumentLoader()->BlockParser();
}
}
mojom::blink::ScrollIntoViewParamsPtr
WebFrameWidgetImpl::GetScrollParamsForFocusedEditableElement(
const Element& element,
PhysicalRect& out_rect_to_scroll) {
// For main frames, scrolling takes place in two phases.
if (ForMainFrame()) {
// Since the page has been resized, the layout may have changed. The page
// scale animation started by ZoomAndScrollToFocusedEditableRect will scroll
// only the visual and layout viewports. We'll call ScrollRectToVisible with
// the stop_at_main_frame_layout_viewport param to ensure the element is
// actually visible in the page.
mojom::blink::ScrollIntoViewParamsPtr params =
ScrollAlignment::CreateScrollIntoViewParams(
ScrollAlignment::CenterIfNeeded(),
ScrollAlignment::CenterIfNeeded(),
mojom::blink::ScrollType::kProgrammatic, false,
mojom::blink::ScrollBehavior::kInstant);
params->stop_at_main_frame_layout_viewport = true;
out_rect_to_scroll =
PhysicalRect(element.GetLayoutObject()->AbsoluteBoundingBoxRect());
return 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 preferable 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());
}
}
mojom::blink::ScrollIntoViewParamsPtr params =
ScrollAlignment::CreateScrollIntoViewParams();
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 = mojom::blink::ScrollBehavior::kInstant;
out_rect_to_scroll = PhysicalRect(maximal_rect);
return params;
}
bool WebFrameWidgetImpl::ShouldAutoDetermineCompositingToLCDTextSetting() {
return true;
}
} // namespace blink