| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/frame/web_frame_widget_base.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #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/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_settings.h" |
| #include "third_party/blink/public/web/web_widget_client.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/events/web_input_event_conversion.h" |
| #include "third_party/blink/renderer/core/events/wheel_event.h" |
| #include "third_party/blink/renderer/core/exported/web_view_impl.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_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/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_object.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/page.h" |
| #include "third_party/blink/renderer/core/page/pointer_lock_controller.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/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" |
| |
| #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 { |
| |
| void ForEachWebLocalFrameControlledByWidget( |
| WebLocalFrame* frame, |
| const base::RepeatingCallback<void(WebLocalFrame*)>& callback) { |
| callback.Run(frame); |
| for (WebFrame* child = frame->FirstChild(); child; |
| child = child->NextSibling()) { |
| if (child->IsWebLocalFrame()) { |
| ForEachWebLocalFrameControlledByWidget(child->ToWebLocalFrame(), |
| callback); |
| } |
| } |
| } |
| |
| // Iterate the remote children that will be controlled by the widget. Skip over |
| // any RemoteFrames have have another LocalFrame 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); |
| } |
| } |
| |
| // The first call to ForEachRemoteFrameChildrenControlledByWidget will be |
| // with a LocalFrame. Iterate on any portals owned by that frame. Portals |
| // on descendant LocalFrame will be owned by that widget so we don't need |
| // to descend into LocalFrames. |
| 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); |
| } |
| } |
| } |
| } |
| |
| } // namespace |
| |
| // Ensure that the WebDragOperation enum values stay in sync with the original |
| // DragOperation constants. |
| STATIC_ASSERT_ENUM(kDragOperationNone, kWebDragOperationNone); |
| STATIC_ASSERT_ENUM(kDragOperationCopy, kWebDragOperationCopy); |
| STATIC_ASSERT_ENUM(kDragOperationLink, kWebDragOperationLink); |
| STATIC_ASSERT_ENUM(kDragOperationGeneric, kWebDragOperationGeneric); |
| STATIC_ASSERT_ENUM(kDragOperationPrivate, kWebDragOperationPrivate); |
| STATIC_ASSERT_ENUM(kDragOperationMove, kWebDragOperationMove); |
| STATIC_ASSERT_ENUM(kDragOperationDelete, kWebDragOperationDelete); |
| STATIC_ASSERT_ENUM(kDragOperationEvery, kWebDragOperationEvery); |
| |
| bool WebFrameWidgetBase::ignore_input_events_ = false; |
| |
| WebFrameWidgetBase::WebFrameWidgetBase( |
| WebWidgetClient& client, |
| CrossVariantMojoAssociatedRemote<mojom::blink::FrameWidgetHostInterfaceBase> |
| frame_widget_host, |
| CrossVariantMojoAssociatedReceiver<mojom::blink::FrameWidgetInterfaceBase> |
| frame_widget, |
| CrossVariantMojoAssociatedRemote<mojom::blink::WidgetHostInterfaceBase> |
| widget_host, |
| CrossVariantMojoAssociatedReceiver<mojom::blink::WidgetInterfaceBase> |
| widget) |
| : widget_base_(std::make_unique<WidgetBase>(this, |
| std::move(widget_host), |
| std::move(widget))), |
| client_(&client) { |
| frame_widget_host_.Bind( |
| std::move(frame_widget_host), |
| ThreadScheduler::Current()->DeprecatedDefaultTaskRunner()); |
| receiver_.Bind(std::move(frame_widget), |
| ThreadScheduler::Current()->DeprecatedDefaultTaskRunner()); |
| } |
| |
| WebFrameWidgetBase::~WebFrameWidgetBase() = default; |
| |
| void WebFrameWidgetBase::BindLocalRoot(WebLocalFrame& local_root) { |
| local_root_ = To<WebLocalFrameImpl>(local_root); |
| local_root_->SetFrameWidget(this); |
| request_animation_after_delay_timer_.reset( |
| new TaskRunnerTimer<WebFrameWidgetBase>( |
| local_root.GetTaskRunner(TaskType::kInternalDefault), this, |
| &WebFrameWidgetBase::RequestAnimationAfterDelayTimerFired)); |
| } |
| |
| void WebFrameWidgetBase::Close( |
| scoped_refptr<base::SingleThreadTaskRunner> cleanup_runner) { |
| mutator_dispatcher_ = nullptr; |
| local_root_->SetFrameWidget(nullptr); |
| local_root_ = nullptr; |
| client_ = nullptr; |
| request_animation_after_delay_timer_.reset(); |
| widget_base_->Shutdown(std::move(cleanup_runner)); |
| widget_base_.reset(); |
| receiver_.reset(); |
| } |
| |
| WebLocalFrame* WebFrameWidgetBase::LocalRoot() const { |
| return local_root_; |
| } |
| |
| WebRect WebFrameWidgetBase::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 WebRect(); |
| |
| // 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 WebRect(); |
| } |
| |
| WebDragOperation WebFrameWidgetBase::DragTargetDragEnter( |
| const WebDragData& web_drag_data, |
| const gfx::PointF& point_in_viewport, |
| const gfx::PointF& screen_point, |
| WebDragOperationsMask operations_allowed, |
| uint32_t key_modifiers) { |
| DCHECK(!current_drag_data_); |
| |
| current_drag_data_ = DataObject::Create(web_drag_data); |
| operations_allowed_ = operations_allowed; |
| |
| return DragTargetDragEnterOrOver(point_in_viewport, screen_point, kDragEnter, |
| key_modifiers); |
| } |
| |
| void WebFrameWidgetBase::DragTargetDragOver( |
| const gfx::PointF& point_in_viewport, |
| const gfx::PointF& screen_point, |
| WebDragOperationsMask operations_allowed, |
| uint32_t key_modifiers, |
| DragTargetDragOverCallback callback) { |
| operations_allowed_ = operations_allowed; |
| |
| blink::WebDragOperation operation = DragTargetDragEnterOrOver( |
| point_in_viewport, screen_point, kDragOver, key_modifiers); |
| std::move(callback).Run(operation); |
| } |
| |
| void WebFrameWidgetBase::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), |
| static_cast<DragOperation>(operations_allowed_)); |
| |
| GetPage()->GetDragController().DragExited(&drag_data, |
| *local_root_->GetFrame()); |
| |
| // FIXME: why is the drag scroll timer not stopped here? |
| |
| drag_operation_ = kWebDragOperationNone; |
| current_drag_data_ = nullptr; |
| } |
| |
| void WebFrameWidgetBase::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 m_dragOperation state is not DragOperationNone. |
| |
| if (drag_operation_ == kWebDragOperationNone) { |
| // 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), |
| static_cast<DragOperation>(operations_allowed_)); |
| |
| GetPage()->GetDragController().PerformDrag(&drag_data, |
| *local_root_->GetFrame()); |
| } |
| drag_operation_ = kWebDragOperationNone; |
| current_drag_data_ = nullptr; |
| } |
| |
| void WebFrameWidgetBase::DragSourceEndedAt(const gfx::PointF& point_in_viewport, |
| const gfx::PointF& screen_point, |
| WebDragOperation 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, static_cast<DragOperation>(operation)); |
| } |
| |
| void WebFrameWidgetBase::DragSourceSystemDragEnded() { |
| CancelDrag(); |
| } |
| |
| void WebFrameWidgetBase::SetBackgroundOpaque(bool opaque) { |
| if (opaque) { |
| View()->ClearBaseBackgroundColorOverride(); |
| View()->ClearBackgroundColorOverride(); |
| } else { |
| View()->SetBaseBackgroundColorOverride(SK_ColorTRANSPARENT); |
| View()->SetBackgroundColorOverride(SK_ColorTRANSPARENT); |
| } |
| } |
| |
| void WebFrameWidgetBase::SetTextDirection(base::i18n::TextDirection direction) { |
| LocalFrame* focusedFrame = FocusedLocalFrameInWidget(); |
| if (focusedFrame) |
| focusedFrame->SetTextDirection(direction); |
| } |
| |
| #if defined(OS_MAC) |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::BindWidgetCompositor( |
| mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) { |
| widget_base_->BindWidgetCompositor(std::move(receiver)); |
| } |
| |
| void WebFrameWidgetBase::SetActive(bool active) { |
| View()->SetIsActive(active); |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::StartDragging(const WebDragData& data, |
| WebDragOperationsMask mask, |
| const SkBitmap& drag_image, |
| const gfx::Point& drag_image_offset) { |
| doing_drag_and_drop_ = true; |
| Client()->StartDragging(data, mask, drag_image, drag_image_offset); |
| } |
| |
| WebDragOperation WebFrameWidgetBase::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 kWebDragOperationNone; |
| } |
| |
| 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), |
| static_cast<DragOperation>(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 (!(drag_operation & drag_data.DraggingSourceOperationMask())) |
| drag_operation = kDragOperationNone; |
| |
| drag_operation_ = static_cast<WebDragOperation>(drag_operation); |
| |
| return drag_operation_; |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::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); |
| } |
| |
| gfx::PointF WebFrameWidgetBase::ViewportToRootFrame( |
| const gfx::PointF& point_in_viewport) const { |
| return GetPage()->GetVisualViewport().ViewportToRootFrame( |
| FloatPoint(point_in_viewport)); |
| } |
| |
| WebViewImpl* WebFrameWidgetBase::View() const { |
| return local_root_->ViewImpl(); |
| } |
| |
| Page* WebFrameWidgetBase::GetPage() const { |
| return View()->GetPage(); |
| } |
| |
| mojom::blink::FrameWidgetHost* |
| WebFrameWidgetBase::GetAssociatedFrameWidgetHost() const { |
| return frame_widget_host_.get(); |
| } |
| |
| void WebFrameWidgetBase::DidAcquirePointerLock() { |
| GetPage()->GetPointerLockController().DidAcquirePointerLock(); |
| |
| LocalFrame* focusedFrame = FocusedLocalFrameInWidget(); |
| if (focusedFrame) { |
| focusedFrame->GetEventHandler().ReleaseMousePointerCapture(); |
| } |
| } |
| |
| void WebFrameWidgetBase::DidNotAcquirePointerLock() { |
| GetPage()->GetPointerLockController().DidNotAcquirePointerLock(); |
| } |
| |
| void WebFrameWidgetBase::DidLosePointerLock() { |
| GetPage()->GetPointerLockController().DidLosePointerLock(); |
| } |
| |
| void WebFrameWidgetBase::RequestDecode( |
| const PaintImage& image, |
| base::OnceCallback<void(bool)> callback) { |
| Client()->RequestDecode(image, std::move(callback)); |
| } |
| |
| void WebFrameWidgetBase::Trace(Visitor* visitor) const { |
| visitor->Trace(local_root_); |
| visitor->Trace(current_drag_data_); |
| visitor->Trace(frame_widget_host_); |
| visitor->Trace(receiver_); |
| } |
| |
| void WebFrameWidgetBase::SetNeedsRecalculateRasterScales() { |
| if (!View()->does_composite()) |
| return; |
| widget_base_->LayerTreeHost()->SetNeedsRecalculateRasterScales(); |
| } |
| |
| void WebFrameWidgetBase::SetBackgroundColor(SkColor color) { |
| if (!View()->does_composite()) |
| return; |
| widget_base_->LayerTreeHost()->set_background_color(color); |
| } |
| |
| void WebFrameWidgetBase::SetOverscrollBehavior( |
| const cc::OverscrollBehavior& overscroll_behavior) { |
| if (!View()->does_composite()) |
| return; |
| widget_base_->LayerTreeHost()->SetOverscrollBehavior(overscroll_behavior); |
| } |
| |
| void WebFrameWidgetBase::RegisterSelection(cc::LayerSelection selection) { |
| if (!View()->does_composite()) |
| return; |
| widget_base_->LayerTreeHost()->RegisterSelection(selection); |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::RequestBeginMainFrameNotExpected(bool request) { |
| if (!View()->does_composite()) |
| return; |
| widget_base_->LayerTreeHost()->RequestBeginMainFrameNotExpected(request); |
| } |
| |
| void WebFrameWidgetBase::EndCommitCompositorFrame( |
| base::TimeTicks commit_start_time) { |
| Client()->DidCommitCompositorFrame(commit_start_time); |
| } |
| |
| void WebFrameWidgetBase::DidCommitAndDrawCompositorFrame() { |
| Client()->DidCommitAndDrawCompositorFrame(); |
| } |
| |
| void WebFrameWidgetBase::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); |
| } |
| } |
| |
| void WebFrameWidgetBase::RequestNewLayerTreeFrameSink( |
| LayerTreeFrameSinkCallback callback) { |
| Client()->RequestNewLayerTreeFrameSink(std::move(callback)); |
| } |
| |
| void WebFrameWidgetBase::DidCompletePageScaleAnimation() { |
| Client()->DidCompletePageScaleAnimation(); |
| } |
| |
| void WebFrameWidgetBase::DidBeginMainFrame() { |
| Client()->DidBeginMainFrame(); |
| } |
| |
| void WebFrameWidgetBase::WillBeginMainFrame() { |
| Client()->WillBeginMainFrame(); |
| } |
| |
| void WebFrameWidgetBase::SubmitThroughputData( |
| ukm::SourceId source_id, |
| int aggregated_percent, |
| int impl_percent, |
| base::Optional<int> main_percent) { |
| local_root_->Client()->SubmitThroughputData(source_id, aggregated_percent, |
| impl_percent, main_percent); |
| } |
| |
| void WebFrameWidgetBase::ScheduleAnimation() { |
| Client()->ScheduleAnimation(); |
| } |
| |
| bool WebFrameWidgetBase::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 WebFrameWidgetBase::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 WebFrameWidgetBase via the |
| // VisualProperties waterfall, instead of coming to each WebFrameWidgetBase |
| // independently. |
| // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/display-mode |
| SetDisplayMode(visual_properties.display_mode); |
| |
| 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->Client()->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 = widget_base_->VisibleViewportSize(); |
| auto* emulator = DeviceEmulator(); |
| if (emulator) { |
| emulator->UpdateVisualProperties(visual_properties); |
| } else { |
| SetWindowSegments(visual_properties.root_widget_window_segments); |
| } |
| |
| Client()->UpdateVisualProperties(/*emulator_enabled=*/!!emulator, |
| visual_properties); |
| |
| if (old_visible_viewport_size != widget_base_->VisibleViewportSize()) { |
| ForEachWebLocalFrameControlledByWidget( |
| local_root_, WTF::BindRepeating([](WebLocalFrame* local_frame) { |
| local_frame->Client()->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->Client()->DidChangeVisibleViewportSize( |
| visible_viewport_size); |
| }, |
| widget_base_->VisibleViewportSize())); |
| } |
| |
| // 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 (!ForTopLevelFrame()) { |
| // The main frame controls the page scale factor, from blink. For other |
| // frame widgets, the page 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. |
| widget_base_->LayerTreeHost()->SetExternalPageScaleFactor( |
| visual_properties.page_scale_factor, |
| visual_properties.is_pinch_gesture_active); |
| |
| NotifyPageScaleFactorChanged(visual_properties.page_scale_factor, |
| visual_properties.is_pinch_gesture_active); |
| } 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 WebFrameWidgetBase::ScheduleAnimationForWebTests() { |
| Client()->ScheduleAnimationForWebTests(); |
| } |
| |
| int WebFrameWidgetBase::GetLayerTreeId() { |
| if (!View()->does_composite()) |
| return 0; |
| return widget_base_->LayerTreeHost()->GetId(); |
| } |
| |
| void WebFrameWidgetBase::SetHaveScrollEventHandlers(bool has_handlers) { |
| widget_base_->LayerTreeHost()->SetHaveScrollEventHandlers(has_handlers); |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::EventListenerProperties( |
| cc::EventListenerClass listener_class) const { |
| return widget_base_->LayerTreeHost()->event_listener_properties( |
| listener_class); |
| } |
| |
| mojom::blink::DisplayMode WebFrameWidgetBase::DisplayMode() const { |
| return display_mode_; |
| } |
| |
| const WebVector<gfx::Rect>& WebFrameWidgetBase::WindowSegments() const { |
| return window_segments_; |
| } |
| |
| void WebFrameWidgetBase::StartDeferringCommits(base::TimeDelta timeout) { |
| if (!View()->does_composite()) |
| return; |
| widget_base_->LayerTreeHost()->StartDeferringCommits(timeout); |
| } |
| |
| void WebFrameWidgetBase::StopDeferringCommits( |
| cc::PaintHoldingCommitTrigger triggger) { |
| if (!View()->does_composite()) |
| return; |
| widget_base_->LayerTreeHost()->StopDeferringCommits(triggger); |
| } |
| |
| std::unique_ptr<cc::ScopedDeferMainFrameUpdate> |
| WebFrameWidgetBase::DeferMainFrameUpdate() { |
| return widget_base_->LayerTreeHost()->DeferMainFrameUpdate(); |
| } |
| |
| void WebFrameWidgetBase::SetBrowserControlsShownRatio(float top_ratio, |
| float bottom_ratio) { |
| widget_base_->LayerTreeHost()->SetBrowserControlsShownRatio(top_ratio, |
| bottom_ratio); |
| } |
| |
| void WebFrameWidgetBase::SetBrowserControlsParams( |
| cc::BrowserControlsParams params) { |
| widget_base_->LayerTreeHost()->SetBrowserControlsParams(params); |
| } |
| |
| cc::LayerTreeDebugState WebFrameWidgetBase::GetLayerTreeDebugState() { |
| return widget_base_->LayerTreeHost()->GetDebugState(); |
| } |
| |
| void WebFrameWidgetBase::SetLayerTreeDebugState( |
| const cc::LayerTreeDebugState& state) { |
| widget_base_->LayerTreeHost()->SetDebugState(state); |
| } |
| |
| void WebFrameWidgetBase::SynchronouslyCompositeForTesting( |
| base::TimeTicks frame_time) { |
| widget_base_->LayerTreeHost()->Composite(frame_time, false); |
| } |
| |
| // TODO(665924): Remove direct dispatches of mouse events from |
| // PointerLockController, instead passing them through EventHandler. |
| void WebFrameWidgetBase::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); |
| } |
| } |
| |
| void WebFrameWidgetBase::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(); |
| } |
| |
| base::Optional<gfx::Point> |
| WebFrameWidgetBase::GetAndResetContextMenuLocation() { |
| return std::move(host_context_menu_location_); |
| } |
| |
| void WebFrameWidgetBase::SetZoomLevel(double zoom_level) { |
| 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->Client()->ZoomLevelChanged(zoom_level); |
| }, |
| zoom_level)); |
| } |
| |
| LocalFrame* WebFrameWidgetBase::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; |
| } |
| |
| WebLocalFrame* WebFrameWidgetBase::FocusedWebLocalFrameInWidget() const { |
| return WebLocalFrameImpl::FromFrame(FocusedLocalFrameInWidget()); |
| } |
| |
| cc::LayerTreeHost* WebFrameWidgetBase::InitializeCompositing( |
| bool never_composited, |
| scheduler::WebThreadScheduler* main_thread_scheduler, |
| cc::TaskGraphRunner* task_graph_runner, |
| bool for_child_local_root_frame, |
| const ScreenInfo& screen_info, |
| std::unique_ptr<cc::UkmRecorderFactory> ukm_recorder_factory, |
| const cc::LayerTreeSettings* settings) { |
| widget_base_->InitializeCompositing( |
| never_composited, main_thread_scheduler, task_graph_runner, |
| for_child_local_root_frame, screen_info, std::move(ukm_recorder_factory), |
| settings); |
| GetPage()->AnimationHostInitialized(*AnimationHost(), |
| GetLocalFrameViewForAnimationScrolling()); |
| return widget_base_->LayerTreeHost(); |
| } |
| |
| void WebFrameWidgetBase::SetCompositorVisible(bool visible) { |
| widget_base_->SetCompositorVisible(visible); |
| } |
| |
| void WebFrameWidgetBase::RecordTimeToFirstActivePaint( |
| base::TimeDelta duration) { |
| Client()->RecordTimeToFirstActivePaint(duration); |
| } |
| |
| void WebFrameWidgetBase::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()); |
| } |
| } |
| |
| bool WebFrameWidgetBase::WillHandleGestureEvent(const WebGestureEvent& event) { |
| return Client()->WillHandleGestureEvent(event); |
| } |
| |
| bool WebFrameWidgetBase::WillHandleMouseEvent(const WebMouseEvent& event) { |
| return Client()->WillHandleMouseEvent(event); |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::DidHandleKeyEvent() { |
| ClearEditCommands(); |
| } |
| |
| WebTextInputType WebFrameWidgetBase::GetTextInputType() { |
| if (Client()->ShouldDispatchImeEventsToPepper()) |
| return Client()->GetPepperTextInputType(); |
| |
| WebInputMethodController* controller = GetActiveWebInputMethodController(); |
| if (!controller) |
| return WebTextInputType::kWebTextInputTypeNone; |
| return controller->TextInputType(); |
| } |
| |
| void WebFrameWidgetBase::ApplyViewportChangesForTesting( |
| const ApplyViewportChangesArgs& args) { |
| widget_base_->ApplyViewportChanges(args); |
| } |
| |
| void WebFrameWidgetBase::SetDisplayMode(mojom::blink::DisplayMode mode) { |
| if (mode != display_mode_) { |
| display_mode_ = mode; |
| LocalFrame* frame = LocalRootImpl()->GetFrame(); |
| frame->MediaQueryAffectingValueChangedForLocalSubtree( |
| MediaValueChange::kOther); |
| } |
| } |
| |
| void WebFrameWidgetBase::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->Client()->DidChangeRootWindowSegments(window_segments); |
| }, |
| window_segments_param)); |
| } |
| } |
| |
| void WebFrameWidgetBase::SetCursor(const ui::Cursor& cursor) { |
| widget_base_->SetCursor(cursor); |
| } |
| |
| bool WebFrameWidgetBase::HandlingInputEvent() { |
| return widget_base_->input_handler().handling_input_event(); |
| } |
| |
| void WebFrameWidgetBase::SetHandlingInputEvent(bool handling) { |
| widget_base_->input_handler().set_handling_input_event(handling); |
| } |
| |
| void WebFrameWidgetBase::ProcessInputEventSynchronously( |
| const WebCoalescedInputEvent& event, |
| HandledEventCallback callback) { |
| widget_base_->input_handler().HandleInputEvent(event, std::move(callback)); |
| } |
| |
| void WebFrameWidgetBase::UpdateTextInputState() { |
| widget_base_->UpdateTextInputState(); |
| } |
| |
| void WebFrameWidgetBase::UpdateSelectionBounds() { |
| widget_base_->UpdateSelectionBounds(); |
| } |
| |
| void WebFrameWidgetBase::ShowVirtualKeyboard() { |
| widget_base_->ShowVirtualKeyboard(); |
| } |
| |
| void WebFrameWidgetBase::FlushInputProcessedCallback() { |
| widget_base_->FlushInputProcessedCallback(); |
| } |
| |
| void WebFrameWidgetBase::CancelCompositionForPepper() { |
| widget_base_->CancelCompositionForPepper(); |
| } |
| |
| void WebFrameWidgetBase::RequestMouseLock( |
| bool has_transient_user_activation, |
| bool priviledged, |
| bool request_unadjusted_movement, |
| base::OnceCallback<void( |
| mojom::blink::PointerLockResult, |
| CrossVariantMojoRemote<mojom::blink::PointerLockContextInterfaceBase>)> |
| callback) { |
| widget_base_->RequestMouseLock(has_transient_user_activation, priviledged, |
| request_unadjusted_movement, |
| std::move(callback)); |
| } |
| |
| #if defined(OS_ANDROID) |
| SynchronousCompositorRegistry* |
| WebFrameWidgetBase::GetSynchronousCompositorRegistry() { |
| return widget_base_->widget_input_handler_manager() |
| ->GetSynchronousCompositorRegistry(); |
| } |
| #endif |
| |
| void WebFrameWidgetBase::ApplyVisualProperties( |
| const VisualProperties& visual_properties) { |
| widget_base_->UpdateVisualProperties(visual_properties); |
| } |
| |
| bool WebFrameWidgetBase::IsFullscreenGranted() { |
| return is_fullscreen_granted_; |
| } |
| |
| bool WebFrameWidgetBase::PinchGestureActiveInMainFrame() { |
| return is_pinch_gesture_active_in_mainframe_; |
| } |
| |
| float WebFrameWidgetBase::PageScaleInMainFrame() { |
| return page_scale_factor_in_mainframe_; |
| } |
| |
| void WebFrameWidgetBase::UpdateSurfaceAndScreenInfo( |
| const viz::LocalSurfaceIdAllocation& new_local_surface_id_allocation, |
| const gfx::Rect& compositor_viewport_pixel_rect, |
| const ScreenInfo& new_screen_info) { |
| widget_base_->UpdateSurfaceAndScreenInfo(new_local_surface_id_allocation, |
| compositor_viewport_pixel_rect, |
| new_screen_info); |
| } |
| |
| void WebFrameWidgetBase::UpdateScreenInfo(const ScreenInfo& new_screen_info) { |
| widget_base_->UpdateScreenInfo(new_screen_info); |
| } |
| |
| void WebFrameWidgetBase::UpdateCompositorViewportAndScreenInfo( |
| const gfx::Rect& compositor_viewport_pixel_rect, |
| const ScreenInfo& new_screen_info) { |
| widget_base_->UpdateCompositorViewportAndScreenInfo( |
| compositor_viewport_pixel_rect, new_screen_info); |
| } |
| |
| void WebFrameWidgetBase::UpdateCompositorViewportRect( |
| const gfx::Rect& compositor_viewport_pixel_rect) { |
| widget_base_->UpdateCompositorViewportRect(compositor_viewport_pixel_rect); |
| } |
| |
| const ScreenInfo& WebFrameWidgetBase::GetScreenInfo() { |
| return widget_base_->GetScreenInfo(); |
| } |
| |
| gfx::Rect WebFrameWidgetBase::WindowRect() { |
| return widget_base_->WindowRect(); |
| } |
| |
| gfx::Rect WebFrameWidgetBase::ViewRect() { |
| return widget_base_->ViewRect(); |
| } |
| |
| void WebFrameWidgetBase::SetScreenRects(const gfx::Rect& widget_screen_rect, |
| const gfx::Rect& window_screen_rect) { |
| widget_base_->SetScreenRects(widget_screen_rect, window_screen_rect); |
| } |
| |
| void WebFrameWidgetBase::SetVisibleViewportSize( |
| const gfx::Size& visible_viewport_size) { |
| widget_base_->SetVisibleViewportSize(visible_viewport_size); |
| } |
| |
| const gfx::Size& WebFrameWidgetBase::VisibleViewportSize() { |
| return widget_base_->VisibleViewportSize(); |
| } |
| |
| void WebFrameWidgetBase::SetPendingWindowRect( |
| const gfx::Rect* window_screen_rect) { |
| widget_base_->SetPendingWindowRect(window_screen_rect); |
| } |
| |
| void WebFrameWidgetBase::AutoscrollStart(const gfx::PointF& position) { |
| GetAssociatedFrameWidgetHost()->AutoscrollStart(std::move(position)); |
| } |
| |
| void WebFrameWidgetBase::AutoscrollFling(const gfx::Vector2dF& velocity) { |
| GetAssociatedFrameWidgetHost()->AutoscrollFling(std::move(velocity)); |
| } |
| |
| void WebFrameWidgetBase::AutoscrollEnd() { |
| GetAssociatedFrameWidgetHost()->AutoscrollEnd(); |
| } |
| |
| void WebFrameWidgetBase::DidMeaningfulLayout(WebMeaningfulLayout layout_type) { |
| if (layout_type == blink::WebMeaningfulLayout::kVisuallyNonEmpty) { |
| NotifySwapAndPresentationTime( |
| base::NullCallback(), |
| WTF::Bind(&WebFrameWidgetBase::PresentationCallbackForMeaningfulLayout, |
| WrapPersistent(this))); |
| } |
| |
| if (client_) |
| client_->DidMeaningfulLayout(layout_type); |
| } |
| |
| void WebFrameWidgetBase::PresentationCallbackForMeaningfulLayout( |
| blink::WebSwapResult, |
| base::TimeTicks) { |
| GetAssociatedFrameWidgetHost()->DidFirstVisuallyNonEmptyPaint(); |
| } |
| |
| void WebFrameWidgetBase::RequestAnimationAfterDelay( |
| const base::TimeDelta& delay) { |
| DCHECK(request_animation_after_delay_timer_.get()); |
| if (request_animation_after_delay_timer_->IsActive() && |
| request_animation_after_delay_timer_->NextFireInterval() > delay) { |
| request_animation_after_delay_timer_->Stop(); |
| } |
| if (!request_animation_after_delay_timer_->IsActive()) { |
| request_animation_after_delay_timer_->StartOneShot(delay, FROM_HERE); |
| } |
| } |
| |
| void WebFrameWidgetBase::RequestAnimationAfterDelayTimerFired(TimerBase*) { |
| if (client_) |
| client_->ScheduleAnimation(); |
| } |
| |
| base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> |
| WebFrameWidgetBase::EnsureCompositorMutatorDispatcher( |
| scoped_refptr<base::SingleThreadTaskRunner>* mutator_task_runner) { |
| if (!mutator_task_runner_) { |
| widget_base_->LayerTreeHost()->SetLayerTreeMutator( |
| AnimationWorkletMutatorDispatcherImpl::CreateCompositorThreadClient( |
| &mutator_dispatcher_, &mutator_task_runner_)); |
| } |
| |
| DCHECK(mutator_task_runner_); |
| *mutator_task_runner = mutator_task_runner_; |
| return mutator_dispatcher_; |
| } |
| |
| cc::AnimationHost* WebFrameWidgetBase::AnimationHost() const { |
| return widget_base_->AnimationHost(); |
| } |
| |
| base::WeakPtr<PaintWorkletPaintDispatcher> |
| WebFrameWidgetBase::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 WebFrameWidgetBase::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, |
| WebFrameWidgetBase* 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( |
| WebFrameWidgetBase* 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<WebFrameWidgetBase> widget_; |
| uint32_t frame_token_ = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(ReportTimeSwapPromise); |
| }; |
| |
| void WebFrameWidgetBase::NotifySwapAndPresentationTimeInBlink( |
| WebReportTimeCallback swap_time_callback, |
| WebReportTimeCallback presentation_time_callback) { |
| NotifySwapAndPresentationTime(std::move(swap_time_callback), |
| std::move(presentation_time_callback)); |
| } |
| |
| void WebFrameWidgetBase::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* |
| WebFrameWidgetBase::RendererWidgetSchedulingState() { |
| return widget_base_->RendererWidgetSchedulingState(); |
| } |
| |
| void WebFrameWidgetBase::WaitForDebuggerWhenShown() { |
| local_root_->WaitForDebuggerWhenShown(); |
| } |
| |
| void WebFrameWidgetBase::SetTextZoomFactor(float text_zoom_factor) { |
| local_root_->GetFrame()->SetTextZoomFactor(text_zoom_factor); |
| } |
| |
| float WebFrameWidgetBase::TextZoomFactor() { |
| return local_root_->GetFrame()->TextZoomFactor(); |
| } |
| |
| void WebFrameWidgetBase::SetMainFrameOverlayColor(SkColor color) { |
| DCHECK(!local_root_->Parent()); |
| local_root_->GetFrame()->SetMainFrameColorOverlay(color); |
| } |
| |
| void WebFrameWidgetBase::AddEditCommandForNextKeyEvent(const WebString& name, |
| const WebString& value) { |
| edit_commands_.push_back(mojom::blink::EditCommand::New(name, value)); |
| } |
| |
| bool WebFrameWidgetBase::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 WebFrameWidgetBase::ClearEditCommands() { |
| edit_commands_ = Vector<mojom::blink::EditCommandPtr>(); |
| } |
| |
| WebTextInputInfo WebFrameWidgetBase::TextInputInfo() { |
| WebInputMethodController* controller = GetActiveWebInputMethodController(); |
| if (!controller) |
| return WebTextInputInfo(); |
| return controller->TextInputInfo(); |
| } |
| |
| ui::mojom::blink::VirtualKeyboardVisibilityRequest |
| WebFrameWidgetBase::GetLastVirtualKeyboardVisibilityRequest() { |
| WebInputMethodController* controller = GetActiveWebInputMethodController(); |
| if (!controller) |
| return ui::mojom::blink::VirtualKeyboardVisibilityRequest::NONE; |
| return controller->GetLastVirtualKeyboardVisibilityRequest(); |
| } |
| |
| bool WebFrameWidgetBase::ShouldSuppressKeyboardForFocusedElement() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return false; |
| return focused_frame->ShouldSuppressKeyboardForFocusedElement(); |
| } |
| |
| void WebFrameWidgetBase::GetEditContextBoundsInWindow( |
| base::Optional<gfx::Rect>* edit_context_control_bounds, |
| base::Optional<gfx::Rect>* edit_context_selection_bounds) { |
| WebInputMethodController* controller = GetActiveWebInputMethodController(); |
| if (!controller) |
| return; |
| WebRect control_bounds; |
| WebRect selection_bounds; |
| controller->GetLayoutBounds(&control_bounds, &selection_bounds); |
| client_->ConvertViewportToWindow(&control_bounds); |
| edit_context_control_bounds->emplace(control_bounds); |
| if (controller->IsEditContextActive()) { |
| client_->ConvertViewportToWindow(&selection_bounds); |
| edit_context_selection_bounds->emplace(selection_bounds); |
| } |
| } |
| |
| int32_t WebFrameWidgetBase::ComputeWebTextInputNextPreviousFlags() { |
| WebInputMethodController* controller = GetActiveWebInputMethodController(); |
| if (!controller) |
| return 0; |
| return controller->ComputeWebTextInputNextPreviousFlags(); |
| } |
| |
| void WebFrameWidgetBase::ResetVirtualKeyboardVisibilityRequest() { |
| WebInputMethodController* controller = GetActiveWebInputMethodController(); |
| if (!controller) |
| return; |
| controller->SetVirtualKeyboardVisibilityRequest( |
| ui::mojom::blink::VirtualKeyboardVisibilityRequest::NONE); |
| ; |
| } |
| |
| bool WebFrameWidgetBase::GetSelectionBoundsInWindow( |
| gfx::Rect* focus, |
| gfx::Rect* anchor, |
| base::i18n::TextDirection* focus_dir, |
| base::i18n::TextDirection* anchor_dir, |
| bool* is_anchor_first) { |
| if (Client()->ShouldDispatchImeEventsToPepper()) { |
| // 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 = Client()->GetPepperCaretBounds(); |
| if (pepper_caret == *focus && pepper_caret == *anchor) |
| return false; |
| *focus = pepper_caret; |
| *anchor = *focus; |
| return true; |
| } |
| WebRect focus_webrect; |
| WebRect anchor_webrect; |
| SelectionBounds(focus_webrect, anchor_webrect); |
| client_->ConvertViewportToWindow(&focus_webrect); |
| client_->ConvertViewportToWindow(&anchor_webrect); |
| |
| // if the bounds are the same return false. |
| if (gfx::Rect(focus_webrect) == *focus && |
| gfx::Rect(anchor_webrect) == *anchor) |
| return false; |
| *focus = gfx::Rect(focus_webrect); |
| *anchor = gfx::Rect(anchor_webrect); |
| |
| 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 WebFrameWidgetBase::ClearTextInputState() { |
| widget_base_->ClearTextInputState(); |
| } |
| |
| bool WebFrameWidgetBase::IsPasting() { |
| return widget_base_->is_pasting(); |
| } |
| |
| bool WebFrameWidgetBase::HandlingSelectRange() { |
| return widget_base_->handling_select_range(); |
| } |
| |
| void WebFrameWidgetBase::SetFocus(bool focus) { |
| widget_base_->SetFocus(focus); |
| } |
| |
| bool WebFrameWidgetBase::HasFocus() { |
| return widget_base_->has_focus(); |
| } |
| |
| void WebFrameWidgetBase::SetToolTipText(const String& tooltip_text, |
| TextDirection dir) { |
| widget_base_->SetToolTipText(tooltip_text, dir); |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::InjectGestureScrollEvent( |
| blink::WebGestureDevice device, |
| const gfx::Vector2dF& delta, |
| ui::ScrollGranularity granularity, |
| cc::ElementId scrollable_area_element_id, |
| blink::WebInputEvent::Type injected_type) { |
| widget_base_->input_handler().InjectGestureScrollEvent( |
| device, delta, granularity, scrollable_area_element_id, injected_type); |
| } |
| |
| void WebFrameWidgetBase::DidChangeCursor(const ui::Cursor& cursor) { |
| widget_base_->SetCursor(cursor); |
| Client()->DidChangeCursor(cursor); |
| } |
| |
| bool WebFrameWidgetBase::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 WebFrameWidgetBase::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 WebFrameWidgetBase::FinishComposingText(bool keep_selection) { |
| WebInputMethodController* controller = GetActiveWebInputMethodController(); |
| if (!controller) |
| return; |
| controller->FinishComposingText( |
| keep_selection ? WebInputMethodController::kKeepSelection |
| : WebInputMethodController::kDoNotKeepSelection); |
| } |
| |
| bool WebFrameWidgetBase::IsProvisional() { |
| return LocalRoot()->IsProvisional(); |
| } |
| |
| uint64_t WebFrameWidgetBase::GetScrollableContainerIdAt( |
| const gfx::PointF& point) { |
| gfx::PointF point_in_pixel = Client()->ConvertWindowPointToViewport(point); |
| return HitTestResultAt(point_in_pixel).GetScrollableContainerId(); |
| } |
| |
| void WebFrameWidgetBase::SetEditCommandsForNextKeyEvent( |
| Vector<mojom::blink::EditCommandPtr> edit_commands) { |
| edit_commands_ = std::move(edit_commands); |
| } |
| |
| void WebFrameWidgetBase::FocusChangeComplete() { |
| blink::WebLocalFrame* focused = LocalRoot()->View()->FocusedFrame(); |
| |
| if (focused && focused->AutofillClient()) |
| focused->AutofillClient()->DidCompleteFocusChangeInFrame(); |
| } |
| |
| void WebFrameWidgetBase::ShowVirtualKeyboardOnElementFocus() { |
| widget_base_->ShowVirtualKeyboardOnElementFocus(); |
| } |
| |
| void WebFrameWidgetBase::ProcessTouchAction(WebTouchAction touch_action) { |
| widget_base_->ProcessTouchAction(touch_action); |
| } |
| |
| void WebFrameWidgetBase::DidHandleGestureEvent(const WebGestureEvent& event, |
| bool event_cancelled) { |
| if (event_cancelled) { |
| // The delegate() doesn't need to hear about cancelled events. |
| return; |
| } |
| |
| #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 WebFrameWidgetBase::SetHasPointerRawUpdateEventHandlers( |
| bool has_handlers) { |
| widget_base_->widget_input_handler_manager() |
| ->input_event_queue() |
| ->HasPointerRawUpdateEventHandlers(has_handlers); |
| } |
| |
| void WebFrameWidgetBase::SetNeedsLowLatencyInput(bool needs_low_latency) { |
| widget_base_->widget_input_handler_manager() |
| ->input_event_queue() |
| ->SetNeedsLowLatency(needs_low_latency); |
| } |
| |
| void WebFrameWidgetBase::RequestUnbufferedInputEvents() { |
| widget_base_->widget_input_handler_manager() |
| ->input_event_queue() |
| ->RequestUnbufferedInputEvents(); |
| } |
| |
| void WebFrameWidgetBase::SetNeedsUnbufferedInputForDebugger(bool unbuffered) { |
| widget_base_->widget_input_handler_manager() |
| ->input_event_queue() |
| ->SetNeedsUnbufferedInputForDebugger(unbuffered); |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::SetMouseCapture(bool capture) { |
| if (mojom::blink::WidgetInputHandlerHost* host = |
| widget_base_->widget_input_handler_manager() |
| ->GetWidgetInputHandlerHost()) { |
| host->SetMouseCapture(capture); |
| } |
| } |
| |
| gfx::Range WebFrameWidgetBase::CompositionRange() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| 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 WebFrameWidgetBase::GetCompositionCharacterBoundsInWindow( |
| Vector<gfx::Rect>* bounds) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| blink::WebInputMethodController* controller = |
| focused_frame->GetInputMethodController(); |
| blink::WebVector<blink::WebRect> bounds_from_blink; |
| if (!controller->GetCompositionCharacterBounds(bounds_from_blink)) |
| return; |
| |
| for (size_t i = 0; i < bounds_from_blink.size(); ++i) { |
| Client()->ConvertViewportToWindow(&bounds_from_blink[i]); |
| bounds->push_back(bounds_from_blink[i]); |
| } |
| } |
| |
| |
| void WebFrameWidgetBase::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> |
| WebFrameWidgetBase::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) { |
| WebRect webrect; |
| unsigned length = ime_text_span.end_offset - ime_text_span.start_offset; |
| focused_frame->FirstRectForCharacterRange(ime_text_span.start_offset, |
| length, webrect); |
| Client()->ConvertViewportToWindow(&webrect); |
| |
| ime_text_spans_info.push_back(ui::mojom::blink::ImeTextSpanInfo::New( |
| ime_text_span, gfx::Rect(webrect))); |
| } |
| return ime_text_spans_info; |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::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 WebFrameWidgetBase::ExtendSelectionAndDelete(int32_t before, |
| int32_t after) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExtendSelectionAndDelete(before, after); |
| } |
| |
| void WebFrameWidgetBase::DeleteSurroundingText(int32_t before, int32_t after) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->DeleteSurroundingText(before, after); |
| } |
| |
| void WebFrameWidgetBase::DeleteSurroundingTextInCodePoints(int32_t before, |
| int32_t after) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->DeleteSurroundingTextInCodePoints(before, after); |
| } |
| |
| void WebFrameWidgetBase::SetEditableSelectionOffsets(int32_t start, |
| int32_t end) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->SetEditableSelectionOffsets(start, end); |
| } |
| |
| void WebFrameWidgetBase::ExecuteEditCommand(const String& command, |
| const String& value) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExecuteCommand(command, value); |
| } |
| |
| void WebFrameWidgetBase::Undo() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExecuteCommand(WebString::FromLatin1("Undo")); |
| } |
| |
| void WebFrameWidgetBase::Redo() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExecuteCommand(WebString::FromLatin1("Redo")); |
| } |
| |
| void WebFrameWidgetBase::Cut() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExecuteCommand(WebString::FromLatin1("Cut")); |
| } |
| |
| void WebFrameWidgetBase::Copy() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExecuteCommand(WebString::FromLatin1("Copy")); |
| } |
| |
| void WebFrameWidgetBase::CopyToFindPboard() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| To<WebLocalFrameImpl>(focused_frame)->CopyToFindPboard(); |
| } |
| |
| void WebFrameWidgetBase::Paste() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExecuteCommand(WebString::FromLatin1("Paste")); |
| } |
| |
| void WebFrameWidgetBase::PasteAndMatchStyle() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExecuteCommand(WebString::FromLatin1("PasteAndMatchStyle")); |
| } |
| |
| void WebFrameWidgetBase::Delete() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExecuteCommand(WebString::FromLatin1("Delete")); |
| } |
| |
| void WebFrameWidgetBase::SelectAll() { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->ExecuteCommand(WebString::FromLatin1("SelectAll")); |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::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 WebFrameWidgetBase::ReplaceMisspelling(const String& word) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| if (!focused_frame->HasSelection()) |
| return; |
| focused_frame->ReplaceMisspelledRange(word); |
| } |
| |
| void WebFrameWidgetBase::SelectRange(const gfx::Point& base, |
| const gfx::Point& extent) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->SelectRange(Client()->ConvertWindowPointToViewport(base), |
| Client()->ConvertWindowPointToViewport(extent)); |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::MoveRangeSelectionExtent(const gfx::Point& extent) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->MoveRangeSelectionExtent( |
| Client()->ConvertWindowPointToViewport(extent)); |
| } |
| |
| void WebFrameWidgetBase::ScrollFocusedEditableNodeIntoRect( |
| const gfx::Rect& rect) { |
| WebLocalFrame* 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->Client()->ResetHasScrolledFocusedEditableIntoView(); |
| local_frame->Client()->ScrollFocusedEditableElementIntoRect(rect); |
| } |
| |
| void WebFrameWidgetBase::MoveCaret(const gfx::Point& point) { |
| WebLocalFrame* focused_frame = FocusedWebLocalFrameInWidget(); |
| if (!focused_frame) |
| return; |
| focused_frame->MoveCaretSelection( |
| Client()->ConvertWindowPointToViewport(point)); |
| } |
| |
| #if defined(OS_ANDROID) |
| void WebFrameWidgetBase::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 WebFrameWidgetBase::ForEachRemoteFrameControlledByWidget( |
| const base::RepeatingCallback<void(RemoteFrame*)>& callback) { |
| ForEachRemoteFrameChildrenControlledByWidget(local_root_->GetFrame(), |
| callback); |
| } |
| |
| void WebFrameWidgetBase::BatterySavingsChanged(WebBatterySavingsFlags savings) { |
| widget_base_->LayerTreeHost()->SetEnableFrameRateThrottling( |
| savings & kAllowReducedFrameRate); |
| } |
| |
| const viz::LocalSurfaceIdAllocation& |
| WebFrameWidgetBase::LocalSurfaceIdAllocationFromParent() { |
| return widget_base_->local_surface_id_allocation_from_parent(); |
| } |
| |
| cc::LayerTreeHost* WebFrameWidgetBase::LayerTreeHost() { |
| return widget_base_->LayerTreeHost(); |
| } |
| |
| void WebFrameWidgetBase::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 WebFrameWidgetBase, so that it can bounce |
| // the value down to its WebFrameWidgetBase. 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 WebFrameWidgetBase |
| // from there. |
| ForEachRemoteFrameControlledByWidget(WTF::BindRepeating( |
| [](float page_scale_factor, bool is_pinch_gesture_active, |
| RemoteFrame* remote_frame) { |
| remote_frame->Client()->PageScaleFactorChanged(page_scale_factor, |
| is_pinch_gesture_active); |
| }, |
| page_scale_factor, is_pinch_gesture_active)); |
| } |
| |
| void WebFrameWidgetBase::SetPageScaleStateAndLimits( |
| float page_scale_factor, |
| bool is_pinch_gesture_active, |
| float minimum, |
| float maximum) { |
| widget_base_->LayerTreeHost()->SetPageScaleFactorAndLimits(page_scale_factor, |
| minimum, maximum); |
| } |
| |
| void WebFrameWidgetBase::OrientationChanged() { |
| LocalRoot()->SendOrientationChangeEvent(); |
| } |
| |
| void WebFrameWidgetBase::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 (Client()->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 |
| // WebFrameWidgetBase::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. |
| if (previous_original_screen_info.device_scale_factor != |
| screen_info.device_scale_factor) { |
| View()->CancelPagePopup(); |
| } |
| |
| // Propagate changes down to child local root RenderWidgets and BrowserPlugins |
| // in other frame trees/processes. |
| ScreenInfo original_screen_info = GetOriginalScreenInfo(); |
| if (previous_original_screen_info != original_screen_info) { |
| ForEachRemoteFrameControlledByWidget(WTF::BindRepeating( |
| [](const ScreenInfo& original_screen_info, RemoteFrame* remote_frame) { |
| remote_frame->Client()->DidChangeScreenInfo(original_screen_info); |
| }, |
| original_screen_info)); |
| } |
| } |
| |
| const ScreenInfo& WebFrameWidgetBase::GetOriginalScreenInfo() { |
| return widget_base_->GetScreenInfo(); |
| } |
| |
| base::Optional<blink::mojom::ScreenOrientation> |
| WebFrameWidgetBase::ScreenOrientationOverride() { |
| return View()->ScreenOrientationOverride(); |
| } |
| |
| } // namespace blink |