2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights
4 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
5 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "third_party/blink/renderer/core/input/event_handler.h"
34 #include "build/build_config.h"
35 #include "third_party/blink/public/common/input/web_input_event.h"
36 #include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
37 #include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-blink.h"
38 #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-blink.h"
39 #include "third_party/blink/public/platform/task_type.h"
40 #include "third_party/blink/renderer/core/clipboard/data_transfer.h"
41 #include "third_party/blink/renderer/core/css/style_engine.h"
42 #include "third_party/blink/renderer/core/dom/document.h"
43 #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
44 #include "third_party/blink/renderer/core/dom/shadow_root.h"
45 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
46 #include "third_party/blink/renderer/core/editing/editor.h"
47 #include "third_party/blink/renderer/core/editing/frame_selection.h"
48 #include "third_party/blink/renderer/core/editing/local_caret_rect.h"
49 #include "third_party/blink/renderer/core/editing/selection_controller.h"
50 #include "third_party/blink/renderer/core/editing/selection_template.h"
51 #include "third_party/blink/renderer/core/editing/text_affinity.h"
52 #include "third_party/blink/renderer/core/editing/visible_selection.h"
53 #include "third_party/blink/renderer/core/events/keyboard_event.h"
54 #include "third_party/blink/renderer/core/events/mouse_event.h"
55 #include "third_party/blink/renderer/core/events/pointer_event.h"
56 #include "third_party/blink/renderer/core/events/pointer_event_factory.h"
57 #include "third_party/blink/renderer/core/events/text_event.h"
58 #include "third_party/blink/renderer/core/events/touch_event.h"
59 #include "third_party/blink/renderer/core/frame/event_handler_registry.h"
60 #include "third_party/blink/renderer/core/frame/local_frame.h"
61 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
62 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
63 #include "third_party/blink/renderer/core/frame/settings.h"
64 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
65 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
66 #include "third_party/blink/renderer/core/html/html_dialog_element.h"
67 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
68 #include "third_party/blink/renderer/core/html/html_frame_set_element.h"
69 #include "third_party/blink/renderer/core/html/portal/html_portal_element.h"
70 #include "third_party/blink/renderer/core/input/event_handling_util.h"
71 #include "third_party/blink/renderer/core/input/input_device_capabilities.h"
72 #include "third_party/blink/renderer/core/input_type_names.h"
73 #include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
74 #include "third_party/blink/renderer/core/layout/hit_test_request.h"
75 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
76 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
77 #include "third_party/blink/renderer/core/layout/layout_view.h"
78 #include "third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h"
79 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
80 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
81 #include "third_party/blink/renderer/core/page/chrome_client.h"
82 #include "third_party/blink/renderer/core/page/drag_state.h"
83 #include "third_party/blink/renderer/core/page/frame_tree.h"
84 #include "third_party/blink/renderer/core/page/page.h"
85 #include "third_party/blink/renderer/core/page/touch_adjustment.h"
86 #include "third_party/blink/renderer/core/paint/paint_layer.h"
87 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
88 #include "third_party/blink/renderer/core/scroll/scrollbar.h"
89 #include "third_party/blink/renderer/core/style/computed_style.h"
90 #include "third_party/blink/renderer/core/style/cursor_data.h"
91 #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
92 #include "third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h"
93 #include "third_party/blink/renderer/core/svg/svg_use_element.h"
94 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
95 #include "third_party/blink/renderer/platform/cursors.h"
96 #include "third_party/blink/renderer/platform/graphics/image_orientation.h"
97 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
98 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
99 #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
100 #include "third_party/blink/renderer/platform/windows_keyboard_codes.h"
101 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
102 #include "third_party/skia/include/core/SkBitmap.h"
103 #include "ui/base/cursor/cursor.h"
104 #include "ui/base/cursor/mojom/cursor_type.mojom-blink.h"
105 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-blink.h"
106 #include "ui/display/screen_info.h"
107 #include "ui/display/screen_infos.h"
108 #include "ui/gfx/geometry/point.h"
109 #include "ui/gfx/geometry/point_conversions.h"
110 #include "ui/gfx/geometry/point_f.h"
111 #include "ui/gfx/geometry/rect.h"
112 #include "ui/gfx/geometry/rect_conversions.h"
113 #include "ui/gfx/geometry/size.h"
114 #include "ui/gfx/geometry/size_conversions.h"
115 #include "ui/gfx/geometry/size_f.h"
119 using mojom::blink::FormControlType;
123 // Refetch the event target node if it is removed or currently is the shadow
124 // node inside an <input> element. If a mouse event handler changes the input
125 // element type to one that has a EmbeddedContentView associated, we'd like to
126 // EventHandler::handleMousePressEvent to pass the event to the
127 // EmbeddedContentView and thus the event target node can't still be the shadow
129 bool ShouldRefetchEventTarget(const MouseEventWithHitTestResults& mev) {
130 Node* target_node = mev.InnerNode();
131 if (!target_node || !target_node->parentNode())
133 if (auto* shadow_root = DynamicTo<ShadowRoot>(target_node))
134 return IsA<HTMLInputElement>(shadow_root->host());
138 gfx::Point GetMiddleSelectionCaretOfPosition(
139 const PositionWithAffinity& position) {
140 const LocalCaretRect& local_caret_rect = LocalCaretRectOfPosition(position);
141 if (local_caret_rect.IsEmpty())
143 const gfx::Rect rect = AbsoluteCaretBoundsOf(position);
144 // In a multiline edit, rect.bottom() would end up on the next line, so
145 // take the midpoint in order to use this corner point directly.
146 if (local_caret_rect.layout_object->IsHorizontalWritingMode())
147 return {rect.x(), (rect.y() + rect.bottom()) / 2};
149 // When text is vertical, rect.right() would end up on the next line, so
150 // take the midpoint in order to use this corner point directly.
151 return {(rect.x() + rect.right()) / 2, rect.y()};
154 bool ContainsEvenAtEdge(const gfx::Rect& rect, const gfx::Point& point) {
155 return point.x() >= rect.x() && point.x() <= rect.right() &&
156 point.y() >= rect.y() && point.y() <= rect.bottom();
159 gfx::Point DetermineHotSpot(const Image& image,
160 bool hot_spot_specified,
161 const gfx::Point& specified_hot_spot) {
162 if (hot_spot_specified) {
163 return specified_hot_spot;
166 // If hot spot is not specified externally, it can be extracted from some
167 // image formats (e.g. .cur).
168 gfx::Point intrinsic_hot_spot;
169 const bool image_has_intrinsic_hot_spot =
170 image.GetHotSpot(intrinsic_hot_spot);
171 const gfx::Rect image_rect = image.Rect();
172 if (image_has_intrinsic_hot_spot && image_rect.Contains(intrinsic_hot_spot))
173 return intrinsic_hot_spot;
175 // If neither is provided, use a default value of (0, 0).
179 // Returns whether the hit element contains a title and isn't a SVGUseElement or
180 // part of an SVGUseElement.
181 bool HasTitleAndNotSVGUseElement(const HitTestResult& hovered_node_result) {
182 // TODO(crbug.com/1473774): Remove flag check if no issues arise.
183 if (!RuntimeEnabledFeatures::SkipShadowHostWhenHoveringForTooltipEnabled()) {
186 Node* inner_node = hovered_node_result.InnerNode();
190 auto* element = DynamicTo<Element>(inner_node);
191 if (!element || element->title().IsNull()) {
194 ShadowRoot* containing_shadow_root = inner_node->ContainingShadowRoot();
195 if (IsA<SVGUseElement>(element) ||
196 (containing_shadow_root &&
197 IsA<SVGUseElement>(containing_shadow_root->host()))) {
205 // The amount of time to wait for a cursor update on style and layout changes
206 // Set to 50Hz, no need to be faster than common screen refresh rate
207 static constexpr base::TimeDelta kCursorUpdateInterval = base::Milliseconds(20);
209 // The maximum size a cursor can be without falling back to the default cursor
210 // when intersecting browser native UI.
211 // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#icon_size_limits.
212 static const int kMaximumCursorSizeWithoutFallback = 32;
214 // The minimum amount of time an element stays active after a ShowPress
215 // This is roughly 9 frames, which should be long enough to be noticeable.
216 constexpr base::TimeDelta kMinimumActiveInterval = base::Seconds(0.15);
218 EventHandler::EventHandler(LocalFrame& frame)
220 selection_controller_(MakeGarbageCollected<SelectionController>(frame)),
221 hover_timer_(frame.GetTaskRunner(TaskType::kUserInteraction),
223 &EventHandler::HoverTimerFired),
224 cursor_update_timer_(
225 frame.GetTaskRunner(TaskType::kInternalUserInteraction),
227 &EventHandler::CursorUpdateTimerFired),
228 should_only_fire_drag_over_event_(false),
229 event_handler_registry_(
230 frame_->IsLocalRoot()
231 ? MakeGarbageCollected<EventHandlerRegistry>(*frame_)
232 : &frame_->LocalFrameRoot().GetEventHandlerRegistry()),
233 scroll_manager_(MakeGarbageCollected<ScrollManager>(frame)),
234 mouse_event_manager_(
235 MakeGarbageCollected<MouseEventManager>(frame, *scroll_manager_)),
236 mouse_wheel_event_manager_(
237 MakeGarbageCollected<MouseWheelEventManager>(frame,
239 keyboard_event_manager_(
240 MakeGarbageCollected<KeyboardEventManager>(frame, *scroll_manager_)),
241 pointer_event_manager_(
242 MakeGarbageCollected<PointerEventManager>(frame,
243 *mouse_event_manager_)),
245 MakeGarbageCollected<GestureManager>(frame,
247 *mouse_event_manager_,
248 *pointer_event_manager_,
249 *selection_controller_)),
250 active_interval_timer_(frame.GetTaskRunner(TaskType::kUserInteraction),
252 &EventHandler::ActiveIntervalTimerFired) {}
254 void EventHandler::Trace(Visitor* visitor) const {
255 visitor->Trace(frame_);
256 visitor->Trace(selection_controller_);
257 visitor->Trace(hover_timer_);
258 visitor->Trace(cursor_update_timer_);
259 visitor->Trace(capturing_mouse_events_element_);
260 visitor->Trace(capturing_subframe_element_);
261 visitor->Trace(last_mouse_move_event_subframe_);
262 visitor->Trace(last_scrollbar_under_mouse_);
263 visitor->Trace(drag_target_);
264 visitor->Trace(frame_set_being_resized_);
265 visitor->Trace(event_handler_registry_);
266 visitor->Trace(scroll_manager_);
267 visitor->Trace(mouse_event_manager_);
268 visitor->Trace(mouse_wheel_event_manager_);
269 visitor->Trace(keyboard_event_manager_);
270 visitor->Trace(pointer_event_manager_);
271 visitor->Trace(gesture_manager_);
272 visitor->Trace(active_interval_timer_);
273 visitor->Trace(last_deferred_tap_element_);
276 void EventHandler::Clear() {
278 cursor_update_timer_.Stop();
279 active_interval_timer_.Stop();
280 last_mouse_move_event_subframe_ = nullptr;
281 last_scrollbar_under_mouse_ = nullptr;
282 frame_set_being_resized_ = nullptr;
283 drag_target_ = nullptr;
284 should_only_fire_drag_over_event_ = false;
285 capturing_mouse_events_element_ = nullptr;
286 capturing_subframe_element_ = nullptr;
287 pointer_event_manager_->Clear();
288 scroll_manager_->Clear();
289 gesture_manager_->Clear();
290 mouse_event_manager_->Clear();
291 mouse_wheel_event_manager_->Clear();
292 last_show_press_timestamp_.reset();
293 last_deferred_tap_element_ = nullptr;
294 should_use_touch_event_adjusted_point_ = false;
295 touch_adjustment_result_.unique_event_id = 0;
298 void EventHandler::UpdateSelectionForMouseDrag() {
299 mouse_event_manager_->UpdateSelectionForMouseDrag();
302 void EventHandler::StartMiddleClickAutoscroll(LayoutObject* layout_object) {
303 DCHECK(RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled());
304 if (!layout_object->IsBox())
306 AutoscrollController* controller = scroll_manager_->GetAutoscrollController();
310 LayoutBox* scrollable = LayoutBox::FindAutoscrollable(
311 layout_object, /*is_middle_click_autoscroll*/ true);
313 controller->StartMiddleClickAutoscroll(
314 layout_object->GetFrame(), scrollable,
315 LastKnownMousePositionInRootFrame(),
316 mouse_event_manager_->LastKnownMouseScreenPosition());
317 mouse_event_manager_->InvalidateClick();
320 void EventHandler::PerformHitTest(const HitTestLocation& location,
321 HitTestResult& result,
322 bool no_lifecycle_update) const {
323 // LayoutView::hitTest causes a layout, and we don't want to hit that until
324 // the first layout because until then, there is nothing shown on the screen -
325 // the user can't have intentionally clicked on something belonging to this
326 // page. Furthermore, mousemove events before the first layout should not
327 // lead to a premature layout() happening, which could show a flash of white.
328 // See also the similar code in Document::performMouseEventHitTest.
329 // The check to LifecycleUpdatesActive() prevents hit testing to frames
330 // that have already had layout but are throttled to prevent painting
331 // because the current Document isn't ready to render yet. In that case
332 // the lifecycle update prompted by HitTest() would fail.
333 if (!frame_->ContentLayoutObject() || !frame_->View() ||
334 !frame_->View()->DidFirstLayout() ||
335 !frame_->View()->LifecycleUpdatesActive())
338 if (no_lifecycle_update) {
339 frame_->ContentLayoutObject()->HitTestNoLifecycleUpdate(location, result);
341 frame_->ContentLayoutObject()->HitTest(location, result);
343 const HitTestRequest& request = result.GetHitTestRequest();
344 if (!request.ReadOnly()) {
345 frame_->GetDocument()->UpdateHoverActiveState(
346 request.Active(), !request.Move(), result.InnerElement());
350 HitTestResult EventHandler::HitTestResultAtLocation(
351 const HitTestLocation& location,
352 HitTestRequest::HitTestRequestType hit_type,
353 const LayoutObject* stop_node,
354 bool no_lifecycle_update) {
355 TRACE_EVENT0("blink", "EventHandler::HitTestResultAtLocation");
357 // We always send HitTestResultAtLocation to the main frame if we have one,
358 // otherwise we might hit areas that are obscured by higher frames.
359 if (frame_->GetPage()) {
360 LocalFrame& main_frame = frame_->LocalFrameRoot();
361 if (frame_ != &main_frame) {
362 LocalFrameView* frame_view = frame_->View();
363 LocalFrameView* main_view = main_frame.View();
364 if (frame_view && main_view) {
365 HitTestLocation adjusted_location;
366 if (location.IsRectBasedTest()) {
367 DCHECK(location.IsRectilinear());
368 if (hit_type & HitTestRequest::kHitTestVisualOverflow) {
369 // Apply ancestor transforms to location rect
370 PhysicalRect local_rect = location.BoundingBox();
371 PhysicalRect main_frame_rect =
372 frame_view->GetLayoutView()->LocalToAncestorRect(
373 local_rect, main_view->GetLayoutView(),
374 kTraverseDocumentBoundaries);
375 adjusted_location = HitTestLocation(main_frame_rect);
377 // Don't apply ancestor transforms to bounding box
378 PhysicalOffset main_content_point = main_view->ConvertFromRootFrame(
379 frame_view->ConvertToRootFrame(location.BoundingBox().offset));
380 adjusted_location = HitTestLocation(
381 PhysicalRect(main_content_point, location.BoundingBox().size));
384 adjusted_location = HitTestLocation(main_view->ConvertFromRootFrame(
385 frame_view->ConvertToRootFrame(location.Point())));
387 return main_frame.GetEventHandler().HitTestResultAtLocation(
388 adjusted_location, hit_type, stop_node, no_lifecycle_update);
392 // HitTestResultAtLocation is specifically used to hitTest into all frames,
393 // thus it always allows child frame content.
394 HitTestRequest request(hit_type | HitTestRequest::kAllowChildFrameContent,
396 HitTestResult result(request, location);
397 PerformHitTest(location, result, no_lifecycle_update);
401 void EventHandler::StopAutoscroll() {
402 scroll_manager_->StopMiddleClickAutoscroll();
403 scroll_manager_->StopAutoscroll();
406 // TODO(bokan): This should be merged with logicalScroll assuming
407 // defaultSpaceEventHandler's chaining scroll can be done crossing frames.
408 bool EventHandler::BubblingScroll(mojom::blink::ScrollDirection direction,
409 ui::ScrollGranularity granularity,
410 Node* starting_node) {
411 return scroll_manager_->BubblingScroll(
412 direction, granularity, starting_node,
413 mouse_event_manager_->MousePressNode());
416 gfx::PointF EventHandler::LastKnownMousePositionInRootFrame() const {
417 return frame_->GetPage()->GetVisualViewport().ViewportToRootFrame(
418 mouse_event_manager_->LastKnownMousePositionInViewport());
421 gfx::PointF EventHandler::LastKnownMouseScreenPosition() const {
422 return mouse_event_manager_->LastKnownMouseScreenPosition();
425 gfx::Point EventHandler::DragDataTransferLocationForTesting() {
426 if (mouse_event_manager_->GetDragState().drag_data_transfer_)
427 return mouse_event_manager_->GetDragState()
428 .drag_data_transfer_->DragLocation();
433 static bool IsSubmitImage(const Node* node) {
434 auto* html_input_element = DynamicTo<HTMLInputElement>(node);
435 return html_input_element &&
436 html_input_element->FormControlType() == FormControlType::kInputImage;
439 bool EventHandler::UsesHandCursor(const Node* node) {
442 return ((node->IsLink() || IsSubmitImage(node)) && !IsEditable(*node));
445 void EventHandler::CursorUpdateTimerFired(TimerBase*) {
447 DCHECK(frame_->GetDocument());
452 void EventHandler::UpdateCursor() {
453 TRACE_EVENT0("input", "EventHandler::updateCursor");
455 // We must do a cross-frame hit test because the frame that triggered the
456 // cursor update could be occluded by a different frame.
457 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
459 LocalFrameView* view = frame_->View();
460 if (!view || !view->ShouldSetCursor())
463 auto* layout_view = view->GetLayoutView();
467 frame_->GetDocument()->UpdateStyleAndLayout(DocumentUpdateReason::kInput);
469 HitTestRequest request(HitTestRequest::kReadOnly |
470 HitTestRequest::kAllowChildFrameContent);
471 HitTestLocation location(view->ViewportToFrame(
472 mouse_event_manager_->LastKnownMousePositionInViewport()));
473 HitTestResult result(request, location);
474 layout_view->HitTest(location, result);
476 if (LocalFrame* frame = result.InnerNodeFrame()) {
477 absl::optional<ui::Cursor> optional_cursor =
478 frame->GetEventHandler().SelectCursor(location, result);
479 if (optional_cursor.has_value()) {
480 view->SetCursor(optional_cursor.value());
485 bool EventHandler::ShouldShowResizeForNode(const LayoutObject& layout_object,
486 const HitTestLocation& location) {
487 const PaintLayer* layer = layout_object.EnclosingLayer();
488 const PaintLayerScrollableArea* scrollable_area = layer->GetScrollableArea();
489 return scrollable_area &&
490 scrollable_area->IsAbsolutePointInResizeControl(
491 ToRoundedPoint(location.Point()), kResizerForPointer);
494 bool EventHandler::IsSelectingLink(const HitTestResult& result) {
495 // If a drag may be starting or we're capturing mouse events for a particular
496 // node, don't treat this as a selection. Note calling
497 // ComputeVisibleSelectionInDOMTreeDeprecated may update layout.
498 const bool mouse_selection =
499 !capturing_mouse_events_element_ &&
500 mouse_event_manager_->MousePressed() &&
501 GetSelectionController().MouseDownMayStartSelect() &&
502 !mouse_event_manager_->MouseDownMayStartDrag() &&
504 .ComputeVisibleSelectionInDOMTreeDeprecated()
506 return mouse_selection && result.IsOverLink();
509 bool EventHandler::ShouldShowIBeamForNode(const Node* node,
510 const HitTestResult& result) {
514 if (node->IsTextNode() && (node->CanStartSelection() || result.IsOverLink()))
517 return IsEditable(*node);
520 absl::optional<ui::Cursor> EventHandler::SelectCursor(
521 const HitTestLocation& location,
522 const HitTestResult& result) {
523 if (scroll_manager_->InResizeMode())
524 return absl::nullopt;
526 Page* page = frame_->GetPage();
528 return absl::nullopt;
529 if (scroll_manager_->MiddleClickAutoscrollInProgress())
530 return absl::nullopt;
532 if (result.GetScrollbar() && !result.GetScrollbar()->IsCustomScrollbar())
533 return PointerCursor();
535 Node* node = result.InnerPossiblyPseudoNode();
536 if (!node || !node->GetLayoutObject()) {
537 return SelectAutoCursor(result, node, IBeamCursor());
540 const LayoutObject& layout_object = *node->GetLayoutObject();
541 if (ShouldShowResizeForNode(layout_object, location)) {
542 const LayoutBox* box = layout_object.EnclosingLayer()->GetLayoutBox();
543 EResize resize = box->StyleRef().UsedResize();
545 case EResize::kVertical:
546 return NorthSouthResizeCursor();
547 case EResize::kHorizontal:
548 return EastWestResizeCursor();
550 if (box->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
551 return SouthWestResizeCursor();
553 return SouthEastResizeCursor();
556 return PointerCursor();
561 ui::Cursor override_cursor;
562 switch (layout_object.GetCursor(result.LocalPoint(), override_cursor)) {
563 case kSetCursorBasedOnStyle:
566 return override_cursor;
567 case kDoNotSetCursor:
568 return absl::nullopt;
572 const ComputedStyle& style = layout_object.StyleRef();
573 if (const CursorList* cursors = style.Cursors()) {
574 for (const auto& cursor : *cursors) {
575 const StyleImage* style_image = cursor.GetImage();
576 if (!style_image || !style_image->CanRender()) {
579 // The 'cursor' property only allow url() and image-set(). Either of
580 // those will return false from their CanRender() implementation if they
581 // don't have an ImageResourceContent (and the former should always have
583 CHECK(style_image->CachedImage());
585 // Compute the concrete object size in DIP based on the
586 // default cursor size obtained from the OS.
588 style_image->ImageSize(1,
589 gfx::SizeF(page->GetChromeClient()
590 .GetScreenInfos(*frame_)
591 .system_cursor_size),
592 kRespectImageOrientation);
594 float scale = style_image->ImageScaleFactor();
595 Image* image = style_image->CachedImage()->GetImage();
596 if (image->IsSVGImage()) {
597 // `StyleImage::ImageSize` does not take `StyleImage::ImageScaleFactor`
598 // into account when computing the size for SVG images.
599 size.Scale(1 / scale);
602 if (size.IsEmpty() ||
603 !ui::Cursor::AreDimensionsValidForWeb(
604 gfx::ToCeiledSize(gfx::ScaleSize(size, scale)), scale)) {
608 const float device_scale_factor =
609 page->GetChromeClient().GetScreenInfo(*frame_).device_scale_factor;
611 // If the image is an SVG, then adjust the scale to reflect the device
612 // scale factor so that the SVG can be rasterized in the native
613 // resolution and scaled down to the correct size for the cursor.
614 scoped_refptr<Image> svg_image_holder;
615 if (auto* svg_image = DynamicTo<SVGImage>(image)) {
616 scale *= device_scale_factor;
617 // Re-scale back from DIP to device pixels.
620 // TODO(fs): Should pass proper URL. Use StyleImage::GetImage.
621 svg_image_holder = SVGImageForContainer::Create(
622 svg_image, size, device_scale_factor, NullURL(),
623 frame_->GetDocument()
625 .ResolveColorSchemeForEmbedding(&style));
626 image = svg_image_holder.get();
629 // Convert from DIP to physical pixels.
630 gfx::Point hot_spot = gfx::ScaleToRoundedPoint(cursor.HotSpot(), scale);
632 const bool hot_spot_specified = cursor.HotSpotSpecified();
633 ui::Cursor custom_cursor = ui::Cursor::NewCustom(
634 image->AsSkBitmapForCurrentFrame(kRespectImageOrientation),
635 DetermineHotSpot(*image, hot_spot_specified, hot_spot), scale);
637 // For large cursors below the max size, limit their ability to cover UI
638 // elements by removing them when they are not fully contained by the
639 // visual viewport. Careful, we need to make sure to translate coordinate
640 // spaces if we are in an OOPIF.
642 // TODO(csharrison): Consider sending a fallback cursor in the IPC to the
643 // browser process so we can do that calculation there instead, this would
644 // ensure even a compromised renderer could not obscure browser UI with a
645 // large cursor. Also, consider augmenting the intervention to drop the
646 // cursor for iframes if the cursor image obscures content in the parent
648 gfx::SizeF custom_bitmap_size(custom_cursor.custom_bitmap().width(),
649 custom_cursor.custom_bitmap().height());
650 custom_bitmap_size.Scale(1.f / custom_cursor.image_scale_factor());
651 if (custom_bitmap_size.width() > kMaximumCursorSizeWithoutFallback ||
652 custom_bitmap_size.height() > kMaximumCursorSizeWithoutFallback) {
653 PhysicalOffset ancestor_location =
654 frame_->ContentLayoutObject()->LocalToAncestorPoint(
656 nullptr, // no ancestor maps all the way up the hierarchy
657 kTraverseDocumentBoundaries | kApplyRemoteMainFrameTransform);
659 // Check the cursor rect with device and accessibility scaling applied.
660 const float scale_factor =
661 cursor_accessibility_scale_factor_ *
662 (image->IsSVGImage() ? 1.f : device_scale_factor);
663 gfx::SizeF scaled_size(custom_bitmap_size);
664 scaled_size.Scale(scale_factor);
665 gfx::PointF scaled_hot_spot(custom_cursor.custom_hotspot());
666 scaled_hot_spot.Scale(scale_factor /
667 custom_cursor.image_scale_factor());
668 PhysicalRect cursor_rect(
670 PhysicalOffset::FromPointFFloor(scaled_hot_spot),
671 PhysicalSize::FromSizeFFloor(scaled_size));
673 PhysicalRect frame_rect(page->GetVisualViewport().VisibleContentRect());
674 frame_->ContentLayoutObject()->MapToVisualRectInAncestorSpace(
675 nullptr, frame_rect);
677 if (!frame_rect.Contains(cursor_rect)) {
682 return custom_cursor;
686 const ui::Cursor& i_beam =
687 style.IsHorizontalWritingMode() ? IBeamCursor() : VerticalTextCursor();
689 switch (style.Cursor()) {
691 return SelectAutoCursor(result, node, i_beam);
692 case ECursor::kCrosshair:
693 return CrossCursor();
694 case ECursor::kPointer:
695 return IsSelectingLink(result) ? i_beam : HandCursor();
698 case ECursor::kAllScroll:
700 case ECursor::kEResize:
701 return EastResizeCursor();
702 case ECursor::kWResize:
703 return WestResizeCursor();
704 case ECursor::kNResize:
705 return NorthResizeCursor();
706 case ECursor::kSResize:
707 return SouthResizeCursor();
708 case ECursor::kNeResize:
709 return NorthEastResizeCursor();
710 case ECursor::kSwResize:
711 return SouthWestResizeCursor();
712 case ECursor::kNwResize:
713 return NorthWestResizeCursor();
714 case ECursor::kSeResize:
715 return SouthEastResizeCursor();
716 case ECursor::kNsResize:
717 return NorthSouthResizeCursor();
718 case ECursor::kEwResize:
719 return EastWestResizeCursor();
720 case ECursor::kNeswResize:
721 return NorthEastSouthWestResizeCursor();
722 case ECursor::kNwseResize:
723 return NorthWestSouthEastResizeCursor();
724 case ECursor::kColResize:
725 return ColumnResizeCursor();
726 case ECursor::kRowResize:
727 return RowResizeCursor();
734 case ECursor::kVerticalText:
735 return VerticalTextCursor();
738 case ECursor::kContextMenu:
739 return ContextMenuCursor();
740 case ECursor::kProgress:
741 return ProgressCursor();
742 case ECursor::kNoDrop:
743 return NoDropCursor();
744 case ECursor::kAlias:
745 return AliasCursor();
750 case ECursor::kNotAllowed:
751 return NotAllowedCursor();
752 case ECursor::kDefault:
753 return PointerCursor();
754 case ECursor::kZoomIn:
755 return ZoomInCursor();
756 case ECursor::kZoomOut:
757 return ZoomOutCursor();
760 case ECursor::kGrabbing:
761 return GrabbingCursor();
763 return PointerCursor();
766 absl::optional<ui::Cursor> EventHandler::SelectAutoCursor(
767 const HitTestResult& result,
769 const ui::Cursor& i_beam) {
770 if (ShouldShowIBeamForNode(node, result))
773 return PointerCursor();
776 WebInputEventResult EventHandler::DispatchBufferedTouchEvents() {
777 return pointer_event_manager_->FlushEvents();
780 WebInputEventResult EventHandler::HandlePointerEvent(
781 const WebPointerEvent& web_pointer_event,
782 const Vector<WebPointerEvent>& coalesced_events,
783 const Vector<WebPointerEvent>& predicted_events) {
784 return pointer_event_manager_->HandlePointerEvent(
785 web_pointer_event, coalesced_events, predicted_events);
788 WebInputEventResult EventHandler::HandleMousePressEvent(
789 const WebMouseEvent& mouse_event) {
790 TRACE_EVENT0("blink", "EventHandler::handleMousePressEvent");
792 // For 4th/5th button in the mouse since Chrome does not yet send
793 // button value to Blink but in some cases it does send the event.
794 // This check is needed to suppress such an event (crbug.com/574959)
795 if (mouse_event.button == WebPointerProperties::Button::kNoButton)
796 return WebInputEventResult::kHandledSuppressed;
798 capturing_mouse_events_element_ = nullptr;
799 mouse_event_manager_->HandleMousePressEventUpdateStates(mouse_event);
801 return WebInputEventResult::kNotHandled;
803 #if BUILDFLAG(IS_TIZEN_TV)
804 handled_mouse_left_button_press_event_ = false;
807 HitTestRequest request(HitTestRequest::kActive);
808 // Save the document point we generate in case the window coordinate is
809 // invalidated by what happens when we dispatch the event.
810 PhysicalOffset document_point = frame_->View()->ConvertFromRootFrame(
811 PhysicalOffset(gfx::ToFlooredPoint(mouse_event.PositionInRootFrame())));
812 MouseEventWithHitTestResults mev = GetMouseEventTarget(request, mouse_event);
813 if (!mev.InnerNode()) {
814 // An anonymous box can be scrollable.
815 if (PassMousePressEventToScrollbar(mev))
816 return WebInputEventResult::kHandledSystem;
818 mouse_event_manager_->InvalidateClick();
819 return WebInputEventResult::kNotHandled;
822 mouse_event_manager_->SetMousePressNode(mev.InnerNode());
823 frame_->GetDocument()->SetSequentialFocusNavigationStartingPoint(
826 LocalFrame* subframe = event_handling_util::GetTargetSubframe(mev);
828 WebInputEventResult result = PassMousePressEventToSubframe(mev, subframe);
829 // Start capturing future events for this frame. We only do this if we
830 // didn't clear the m_mousePressed flag, which may happen if an AppKit
831 // EmbeddedContentView entered a modal event loop. The capturing should be
832 // done only when the result indicates it has been handled. See
834 mouse_event_manager_->SetCapturesDragging(
835 subframe->GetEventHandler().mouse_event_manager_->CapturesDragging());
836 if (mouse_event_manager_->MousePressed() &&
837 mouse_event_manager_->CapturesDragging()) {
838 capturing_mouse_events_element_ = mev.InnerElement();
839 capturing_subframe_element_ = mev.InnerElement();
842 mouse_event_manager_->InvalidateClick();
846 if (discarded_events_.mouse_down_target != kInvalidDOMNodeId &&
847 discarded_events_.mouse_down_target == mev.InnerNode()->GetDomNodeId() &&
848 mouse_event.TimeStamp() - discarded_events_.mouse_down_time <
849 event_handling_util::kDiscardedEventMistakeInterval) {
850 mev.InnerNode()->GetDocument().CountUse(
851 WebFeature::kInputEventToRecentlyMovedIframeMistakenlyDiscarded);
853 if (event_handling_util::ShouldDiscardEventTargetingFrame(mev.Event(),
855 discarded_events_.mouse_down_target = mev.InnerNode()->GetDomNodeId();
856 discarded_events_.mouse_down_time = mouse_event.TimeStamp();
857 return WebInputEventResult::kHandledSuppressed;
859 discarded_events_.mouse_down_target = kInvalidDOMNodeId;
860 discarded_events_.mouse_down_time = base::TimeTicks();
863 LocalFrame::NotifyUserActivation(
864 frame_, mojom::blink::UserActivationNotificationType::kInteraction,
865 RuntimeEnabledFeatures::BrowserVerifiedUserActivationMouseEnabled());
867 if (RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled()) {
868 // We store whether middle click autoscroll is in progress before calling
869 // stopAutoscroll() because it will set m_autoscrollType to NoAutoscroll on
871 bool is_middle_click_autoscroll_in_progress =
872 scroll_manager_->MiddleClickAutoscrollInProgress();
873 scroll_manager_->StopMiddleClickAutoscroll();
874 if (is_middle_click_autoscroll_in_progress) {
875 // We invalidate the click when exiting middle click auto scroll so that
876 // we don't inadvertently navigate away from the current page (e.g. the
877 // click was on a hyperlink). See <rdar://problem/6095023>.
878 mouse_event_manager_->InvalidateClick();
879 return WebInputEventResult::kHandledSuppressed;
883 mouse_event_manager_->SetClickCount(mouse_event.click_count);
884 mouse_event_manager_->SetClickElement(mev.InnerElement());
886 if (!mouse_event.FromTouch())
887 frame_->Selection().SetCaretBlinkingSuspended(true);
889 WebInputEventResult event_result = DispatchMousePointerEvent(
890 WebInputEvent::Type::kPointerDown, mev.InnerElement(), mev.Event(),
891 Vector<WebMouseEvent>(), Vector<WebMouseEvent>());
893 // Disabled form controls still need to resize the scrollable area.
894 if ((event_result == WebInputEventResult::kNotHandled ||
895 event_result == WebInputEventResult::kHandledSuppressed) &&
897 LocalFrameView* view = frame_->View();
899 mev.InnerNode()->GetLayoutObject()
900 ? mev.InnerNode()->GetLayoutObject()->EnclosingLayer()
902 gfx::Point p = view->ConvertFromRootFrame(
903 gfx::ToFlooredPoint(mouse_event.PositionInRootFrame()));
904 if (layer && layer->GetScrollableArea() &&
905 layer->GetScrollableArea()->IsAbsolutePointInResizeControl(
906 p, kResizerForPointer)) {
907 scroll_manager_->SetResizeScrollableArea(layer, p);
908 return WebInputEventResult::kHandledSystem;
912 // m_selectionInitiationState is initialized after dispatching mousedown
913 // event in order not to keep the selection by DOM APIs because we can't
914 // give the user the chance to handle the selection by user action like
915 // dragging if we keep the selection in case of mousedown. FireFox also has
916 // the same behavior and it's more compatible with other browsers.
917 GetSelectionController().InitializeSelectionState();
918 HitTestResult hit_test_result = event_handling_util::HitTestResultInFrame(
919 frame_, HitTestLocation(document_point), HitTestRequest::kReadOnly);
920 InputDeviceCapabilities* source_capabilities =
921 frame_->DomWindow()->GetInputDeviceCapabilities()->FiresTouchEvents(
922 mouse_event.FromTouch());
924 if (event_result == WebInputEventResult::kNotHandled) {
925 event_result = mouse_event_manager_->HandleMouseFocus(hit_test_result,
926 source_capabilities);
929 if (event_result == WebInputEventResult::kNotHandled || mev.GetScrollbar()) {
930 mouse_event_manager_->SetCapturesDragging(true);
931 // Outermost main frames don't implicitly capture mouse input on MouseDown,
932 // all subframes do (regardless of whether local or remote or fenced).
933 if (frame_->IsAttached() && !frame_->IsOutermostMainFrame())
934 CaptureMouseEventsToWidget(true);
936 mouse_event_manager_->SetCapturesDragging(false);
938 #if BUILDFLAG(IS_TIZEN_TV)
939 if (frame_->GetSettings() && frame_->GetSettings()->GetUseArrowScroll() &&
940 mouse_event.button == WebPointerProperties::Button::kLeft) {
941 handled_mouse_left_button_press_event_ =
942 (SelectCursor(mev.GetHitTestLocation(), mev.GetHitTestResult())
944 .type() == ui::mojom::blink::CursorType::kPointer)
945 ? event_result != WebInputEventResult::kNotHandled
950 if (PassMousePressEventToScrollbar(mev))
951 event_result = WebInputEventResult::kHandledSystem;
953 if (event_result == WebInputEventResult::kNotHandled) {
954 if (ShouldRefetchEventTarget(mev)) {
955 HitTestRequest read_only_request(HitTestRequest::kReadOnly |
956 HitTestRequest::kActive);
957 mev = frame_->GetDocument()->PerformMouseEventHitTest(
958 read_only_request, document_point, mouse_event);
960 event_result = mouse_event_manager_->HandleMousePressEvent(mev);
963 if (mev.GetHitTestResult().InnerNode() &&
964 mouse_event.button == WebPointerProperties::Button::kLeft) {
965 DCHECK_EQ(WebInputEvent::Type::kMouseDown, mouse_event.GetType());
966 HitTestResult result = mev.GetHitTestResult();
967 result.SetToShadowHostIfInUAShadowRoot();
968 frame_->GetChromeClient().OnMouseDown(*result.InnerNode());
974 WebInputEventResult EventHandler::HandleMouseMoveEvent(
975 const WebMouseEvent& event,
976 const Vector<WebMouseEvent>& coalesced_events,
977 const Vector<WebMouseEvent>& predicted_events) {
978 TRACE_EVENT0("blink", "EventHandler::handleMouseMoveEvent");
979 DCHECK(event.GetType() == WebInputEvent::Type::kMouseMove);
980 HitTestResult hovered_node_result;
981 HitTestLocation location;
982 WebInputEventResult result =
983 HandleMouseMoveOrLeaveEvent(event, coalesced_events, predicted_events,
984 &hovered_node_result, &location);
986 Page* page = frame_->GetPage();
990 if (PaintLayer* layer =
991 event_handling_util::LayerForNode(hovered_node_result.InnerNode())) {
992 if (ScrollableArea* layer_scrollable_area =
993 event_handling_util::AssociatedScrollableArea(layer))
994 layer_scrollable_area->MouseMovedInContentArea();
997 // Should not convert the hit shadow element to its shadow host, so that
998 // tooltips in the shadow tree appear correctly.
999 if (!HasTitleAndNotSVGUseElement(hovered_node_result)) {
1000 hovered_node_result.SetToShadowHostIfInUAShadowRoot();
1002 page->GetChromeClient().MouseDidMoveOverElement(*frame_, location,
1003 hovered_node_result);
1008 void EventHandler::HandleMouseLeaveEvent(const WebMouseEvent& event) {
1009 TRACE_EVENT0("blink", "EventHandler::handleMouseLeaveEvent");
1010 DCHECK(event.GetType() == WebInputEvent::Type::kMouseLeave);
1012 Page* page = frame_->GetPage();
1014 page->GetChromeClient().ClearToolTip(*frame_);
1015 HandleMouseMoveOrLeaveEvent(event, Vector<WebMouseEvent>(),
1016 Vector<WebMouseEvent>());
1017 pointer_event_manager_->RemoveLastMousePosition();
1020 WebInputEventResult EventHandler::HandleMouseMoveOrLeaveEvent(
1021 const WebMouseEvent& mouse_event,
1022 const Vector<WebMouseEvent>& coalesced_events,
1023 const Vector<WebMouseEvent>& predicted_events,
1024 HitTestResult* hovered_node_result,
1025 HitTestLocation* hit_test_location) {
1027 DCHECK(frame_->View());
1028 DCHECK(mouse_event.GetType() == WebInputEvent::Type::kMouseMove ||
1029 mouse_event.GetType() == WebInputEvent::Type::kMouseLeave);
1030 mouse_event_manager_->SetLastKnownMousePosition(mouse_event);
1032 hover_timer_.Stop();
1033 cursor_update_timer_.Stop();
1035 mouse_event_manager_->HandleSvgPanIfNeeded(false);
1037 if (mouse_event.GetType() == WebInputEvent::Type::kMouseMove) {
1038 AnchorElementInteractionTracker* tracker =
1039 frame_->GetDocument()->GetAnchorElementInteractionTracker();
1041 tracker->OnMouseMoveEvent(mouse_event);
1045 // Mouse states need to be reset when mouse move with no button down.
1046 // This is for popup/context_menu opened at mouse_down event and
1047 // mouse_release is not handled in page.
1049 if (mouse_event.button == WebPointerProperties::Button::kNoButton &&
1050 !(mouse_event.GetModifiers() &
1051 WebInputEvent::Modifiers::kRelativeMotionEvent)) {
1052 mouse_event_manager_->ClearDragHeuristicState();
1053 capturing_mouse_events_element_ = nullptr;
1054 ReleaseMouseCaptureFromLocalRoot();
1056 // If the scrollbar still thinks it's being dragged, tell it to stop.
1057 // Can happen on Win if we lose focus (e.g. from Alt-Tab) mid-drag.
1058 if (last_scrollbar_under_mouse_ &&
1059 last_scrollbar_under_mouse_->PressedPart() != ScrollbarPart::kNoPart)
1060 last_scrollbar_under_mouse_->MouseUp(mouse_event);
1063 if (RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled()) {
1064 if (Page* page = frame_->GetPage()) {
1065 page->GetAutoscrollController().HandleMouseMoveForMiddleClickAutoscroll(
1066 frame_, mouse_event_manager_->LastKnownMouseScreenPosition(),
1067 mouse_event.button == WebPointerProperties::Button::kMiddle);
1071 if (frame_set_being_resized_) {
1072 return DispatchMousePointerEvent(
1073 WebInputEvent::Type::kPointerMove, frame_set_being_resized_.Get(),
1074 mouse_event, coalesced_events, predicted_events);
1077 // Send events right to a scrollbar if the mouse is pressed.
1078 if (last_scrollbar_under_mouse_ && mouse_event_manager_->MousePressed()) {
1079 last_scrollbar_under_mouse_->MouseMoved(mouse_event);
1080 return WebInputEventResult::kHandledSystem;
1083 HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kMove;
1084 if (mouse_event_manager_->MousePressed()) {
1085 hit_type |= HitTestRequest::kActive;
1088 // Treat any mouse move events as readonly if the user is currently touching
1090 if (pointer_event_manager_->IsAnyTouchActive() &&
1091 mouse_event.GetType() == WebInputEvent::Type::kMouseMove) {
1092 hit_type |= HitTestRequest::kActive | HitTestRequest::kReadOnly;
1094 HitTestRequest request(hit_type);
1095 HitTestLocation out_location((PhysicalOffset()));
1096 MouseEventWithHitTestResults mev = MouseEventWithHitTestResults(
1097 mouse_event, out_location, HitTestResult(request, out_location));
1099 // We don't want to do a hit-test in MouseLeave scenarios because there
1100 // might actually be some other frame above this one at the specified
1101 // coordinate. So we avoid the hit test but still clear the hover/active
1103 if (mouse_event.GetType() == WebInputEvent::Type::kMouseLeave) {
1104 frame_->GetDocument()->UpdateHoverActiveState(request.Active(),
1105 /*update_active_chain=*/false,
1108 mev = GetMouseEventTarget(request, mouse_event);
1111 if (hovered_node_result)
1112 *hovered_node_result = mev.GetHitTestResult();
1114 if (hit_test_location)
1115 *hit_test_location = mev.GetHitTestLocation();
1117 Scrollbar* scrollbar = nullptr;
1119 if (scroll_manager_->InResizeMode()) {
1120 scroll_manager_->Resize(mev.Event());
1122 scrollbar = mev.GetScrollbar();
1124 UpdateLastScrollbarUnderMouse(scrollbar,
1125 !mouse_event_manager_->MousePressed());
1128 WebInputEventResult event_result = WebInputEventResult::kNotHandled;
1130 mev.InnerElement() && IsA<HTMLPortalElement>(*mev.InnerElement());
1131 bool is_remote_frame = false;
1132 LocalFrame* current_subframe = event_handling_util::GetTargetSubframe(
1133 mev, capturing_mouse_events_element_, &is_remote_frame);
1135 // We want mouseouts to happen first, from the inside out. First send a
1136 // move event to the last subframe so that it will fire mouseouts.
1137 // TODO(lanwei): figure out here if we should call HandleMouseLeaveEvent on a
1138 // mouse move event.
1139 if (last_mouse_move_event_subframe_ &&
1140 last_mouse_move_event_subframe_->Tree().IsDescendantOf(frame_) &&
1141 last_mouse_move_event_subframe_ != current_subframe) {
1142 WebMouseEvent event = mev.Event();
1143 event.SetType(WebInputEvent::Type::kMouseLeave);
1144 last_mouse_move_event_subframe_->GetEventHandler().HandleMouseLeaveEvent(
1146 last_mouse_move_event_subframe_->GetEventHandler()
1147 .mouse_event_manager_->SetLastMousePositionAsUnknown();
1150 if (current_subframe) {
1151 // Update over/out state before passing the event to the subframe.
1152 pointer_event_manager_->SendMouseAndPointerBoundaryEvents(
1153 EffectiveMouseEventTargetElement(mev.InnerElement()), mev.Event());
1155 // Event dispatch in sendMouseAndPointerBoundaryEvents may have caused the
1156 // subframe of the target node to be detached from its LocalFrameView, in
1157 // which case the event should not be passed.
1158 if (current_subframe->View()) {
1160 PassMouseMoveEventToSubframe(mev, coalesced_events, predicted_events,
1161 current_subframe, hovered_node_result);
1164 if (scrollbar && !mouse_event_manager_->MousePressed()) {
1165 // Handle hover effects on platforms that support visual feedback on
1166 // scrollbar hovering.
1167 scrollbar->MouseMoved(mev.Event());
1170 // Set Effective pan action before Pointer cursor is updated.
1171 const WebPointerEvent web_pointer_event(WebInputEvent::Type::kPointerMove,
1172 mev.Event().FlattenTransform());
1173 pointer_event_manager_->SendEffectivePanActionAtPointer(web_pointer_event,
1176 LocalFrameView* view = frame_->View();
1177 if ((!is_remote_frame || is_portal) && view) {
1178 absl::optional<ui::Cursor> optional_cursor =
1179 SelectCursor(mev.GetHitTestLocation(), mev.GetHitTestResult());
1180 if (optional_cursor.has_value()) {
1181 view->SetCursor(optional_cursor.value());
1186 last_mouse_move_event_subframe_ = current_subframe;
1188 if (event_result != WebInputEventResult::kNotHandled)
1189 return event_result;
1191 event_result = DispatchMousePointerEvent(WebInputEvent::Type::kPointerMove,
1192 mev.InnerElement(), mev.Event(),
1193 coalesced_events, predicted_events);
1194 // TODO(crbug.com/346473): Since there is no default action for the mousemove
1195 // event we should consider doing drag&drop even when js cancels the
1196 // mouse move event.
1197 // https://w3c.github.io/uievents/#event-type-mousemove
1198 if (event_result != WebInputEventResult::kNotHandled)
1199 return event_result;
1201 return mouse_event_manager_->HandleMouseDraggedEvent(mev);
1204 WebInputEventResult EventHandler::HandleMouseReleaseEvent(
1205 const WebMouseEvent& mouse_event) {
1206 TRACE_EVENT0("blink", "EventHandler::handleMouseReleaseEvent");
1208 // For 4th/5th button in the mouse since Chrome does not yet send
1209 // button value to Blink but in some cases it does send the event.
1210 // This check is needed to suppress such an event (crbug.com/574959)
1211 if (mouse_event.button == WebPointerProperties::Button::kNoButton)
1212 return WebInputEventResult::kHandledSuppressed;
1214 if (!mouse_event.FromTouch())
1215 frame_->Selection().SetCaretBlinkingSuspended(false);
1217 if (RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled()) {
1218 if (Page* page = frame_->GetPage()) {
1219 page->GetAutoscrollController()
1220 .HandleMouseReleaseForMiddleClickAutoscroll(
1222 mouse_event.button == WebPointerProperties::Button::kMiddle);
1226 mouse_event_manager_->ReleaseMousePress();
1227 mouse_event_manager_->SetLastKnownMousePosition(mouse_event);
1228 mouse_event_manager_->HandleSvgPanIfNeeded(true);
1230 if (frame_set_being_resized_) {
1231 WebInputEventResult result =
1232 mouse_event_manager_->SetMousePositionAndDispatchMouseEvent(
1233 EffectiveMouseEventTargetElement(frame_set_being_resized_.Get()),
1234 event_type_names::kMouseup, mouse_event);
1235 // crbug.com/1053385 release mouse capture only if there are no more mouse
1236 // buttons depressed
1237 if (MouseEvent::WebInputEventModifiersToButtons(
1238 mouse_event.GetModifiers()) == 0)
1239 ReleaseMouseCaptureFromLocalRoot();
1243 if (last_scrollbar_under_mouse_) {
1244 mouse_event_manager_->InvalidateClick();
1245 last_scrollbar_under_mouse_->MouseUp(mouse_event);
1246 // crbug.com/1053385 release mouse capture only if there are no more mouse
1247 // buttons depressed
1248 if (MouseEvent::WebInputEventModifiersToButtons(
1249 mouse_event.GetModifiers()) == 0) {
1250 ReleaseMouseCaptureFromLocalRoot();
1252 return DispatchMousePointerEvent(
1253 WebInputEvent::Type::kPointerUp,
1254 mouse_event_manager_->GetElementUnderMouse(), mouse_event,
1255 Vector<WebMouseEvent>(), Vector<WebMouseEvent>());
1258 // Mouse events simulated from touch should not hit-test again.
1259 DCHECK(!mouse_event.FromTouch());
1260 HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kRelease;
1261 HitTestRequest request(hit_type);
1262 MouseEventWithHitTestResults mev = GetMouseEventTarget(request, mouse_event);
1263 LocalFrame* subframe = event_handling_util::GetTargetSubframe(
1264 mev, capturing_mouse_events_element_.Get());
1265 capturing_mouse_events_element_ = nullptr;
1267 return PassMouseReleaseEventToSubframe(mev, subframe);
1269 WebInputEventResult event_result = WebInputEventResult::kNotHandled;
1271 if (event_handling_util::ShouldDiscardEventTargetingFrame(mev.Event(),
1273 event_result = WebInputEventResult::kHandledSuppressed;
1275 event_result = DispatchMousePointerEvent(
1276 WebInputEvent::Type::kPointerUp, mev.InnerElement(), mev.Event(),
1277 Vector<WebMouseEvent>(), Vector<WebMouseEvent>(),
1278 (GetSelectionController().HasExtendedSelection() &&
1279 IsSelectionOverLink(mev)));
1281 scroll_manager_->ClearResizeScrollableArea(false);
1282 #if BUILDFLAG(IS_TIZEN_TV)
1283 if (frame_->GetSettings() && frame_->GetSettings()->GetUseArrowScroll() &&
1284 mouse_event.button == WebPointerProperties::Button::kLeft) {
1285 bool handled_mouse_left_button_release_event =
1286 (SelectCursor(mev.GetHitTestLocation(), mev.GetHitTestResult())
1288 .type() == ui::mojom::blink::CursorType::kPointer)
1289 ? (event_result != WebInputEventResult::kNotHandled)
1291 if (!handled_mouse_left_button_press_event_ &&
1292 !handled_mouse_left_button_release_event &&
1293 CanRunArrowScrolling(mev.GetHitTestResult())) {
1295 frame_->Client()->RunArrowScroll();
1301 if (event_result == WebInputEventResult::kNotHandled)
1302 event_result = mouse_event_manager_->HandleMouseReleaseEvent(mev);
1304 mouse_event_manager_->HandleMouseReleaseEventUpdateStates();
1306 // crbug.com/1053385 release mouse capture only if there are no more mouse
1307 // buttons depressed
1308 if (MouseEvent::WebInputEventModifiersToButtons(mouse_event.GetModifiers()) ==
1310 ReleaseMouseCaptureFromLocalRoot();
1312 return event_result;
1315 static LocalFrame* LocalFrameFromTargetNode(Node* target) {
1316 auto* html_frame_base_element = DynamicTo<HTMLFrameElementBase>(target);
1317 if (!html_frame_base_element)
1320 // Cross-process drag and drop is not yet supported.
1321 return DynamicTo<LocalFrame>(html_frame_base_element->ContentFrame());
1324 WebInputEventResult EventHandler::UpdateDragAndDrop(
1325 const WebMouseEvent& event,
1326 DataTransfer* data_transfer) {
1327 WebInputEventResult event_result = WebInputEventResult::kNotHandled;
1329 if (!frame_->View())
1330 return event_result;
1332 HitTestRequest request(HitTestRequest::kReadOnly);
1333 MouseEventWithHitTestResults mev =
1334 event_handling_util::PerformMouseEventHitTest(frame_, request, event);
1336 // Drag events should never go to text nodes (following IE, and proper
1337 // mouseover/out dispatch)
1338 Node* new_target = mev.InnerNode();
1339 if (new_target && new_target->IsTextNode())
1340 new_target = FlatTreeTraversal::Parent(*new_target);
1342 if (AutoscrollController* controller =
1343 scroll_manager_->GetAutoscrollController()) {
1344 controller->UpdateDragAndDrop(new_target, event.PositionInRootFrame(),
1348 if (drag_target_ != new_target) {
1349 // FIXME: this ordering was explicitly chosen to match WinIE. However,
1350 // it is sometimes incorrect when dragging within subframes, as seen with
1351 // web_tests/fast/events/drag-in-frames.html.
1353 // Moreover, this ordering conforms to section 7.9.4 of the HTML 5 spec.
1354 // <http://dev.w3.org/html5/spec/Overview.html#drag-and-drop-processing-model>.
1355 if (auto* target_frame = LocalFrameFromTargetNode(new_target)) {
1356 event_result = target_frame->GetEventHandler().UpdateDragAndDrop(
1357 event, data_transfer);
1358 } else if (new_target) {
1359 // As per section 7.9.4 of the HTML 5 spec., we must always fire a drag
1360 // event before firing a dragenter, dragleave, or dragover event.
1361 if (mouse_event_manager_->GetDragState().drag_src_) {
1362 // For now we don't care if event handler cancels default behavior,
1363 // since there is none.
1364 mouse_event_manager_->DispatchDragSrcEvent(event_type_names::kDrag,
1367 event_result = mouse_event_manager_->DispatchDragEvent(
1368 event_type_names::kDragenter, new_target, drag_target_, event,
1372 if (auto* target_frame = LocalFrameFromTargetNode(drag_target_.Get())) {
1373 event_result = target_frame->GetEventHandler().UpdateDragAndDrop(
1374 event, data_transfer);
1375 } else if (drag_target_) {
1376 mouse_event_manager_->DispatchDragEvent(event_type_names::kDragleave,
1377 drag_target_.Get(), new_target,
1378 event, data_transfer);
1382 // We do not explicitly call m_mouseEventManager->dispatchDragEvent here
1383 // because it could ultimately result in the appearance that two dragover
1384 // events fired. So, we mark that we should only fire a dragover event on
1385 // the next call to this function.
1386 should_only_fire_drag_over_event_ = true;
1389 if (auto* target_frame = LocalFrameFromTargetNode(new_target)) {
1390 event_result = target_frame->GetEventHandler().UpdateDragAndDrop(
1391 event, data_transfer);
1392 } else if (new_target) {
1393 // Note, when dealing with sub-frames, we may need to fire only a dragover
1394 // event as a drag event may have been fired earlier.
1395 if (!should_only_fire_drag_over_event_ &&
1396 mouse_event_manager_->GetDragState().drag_src_) {
1397 // For now we don't care if event handler cancels default behavior,
1398 // since there is none.
1399 mouse_event_manager_->DispatchDragSrcEvent(event_type_names::kDrag,
1402 event_result = mouse_event_manager_->DispatchDragEvent(
1403 event_type_names::kDragover, new_target, nullptr, event,
1405 should_only_fire_drag_over_event_ = false;
1408 drag_target_ = new_target;
1410 return event_result;
1413 void EventHandler::CancelDragAndDrop(const WebMouseEvent& event,
1414 DataTransfer* data_transfer) {
1415 if (auto* target_frame = LocalFrameFromTargetNode(drag_target_.Get())) {
1416 target_frame->GetEventHandler().CancelDragAndDrop(event, data_transfer);
1417 } else if (drag_target_.Get()) {
1418 if (mouse_event_manager_->GetDragState().drag_src_) {
1419 mouse_event_manager_->DispatchDragSrcEvent(event_type_names::kDrag,
1422 mouse_event_manager_->DispatchDragEvent(event_type_names::kDragleave,
1423 drag_target_.Get(), nullptr, event,
1429 WebInputEventResult EventHandler::PerformDragAndDrop(
1430 const WebMouseEvent& event,
1431 DataTransfer* data_transfer) {
1432 WebInputEventResult result = WebInputEventResult::kNotHandled;
1433 if (auto* target_frame = LocalFrameFromTargetNode(drag_target_.Get())) {
1434 result = target_frame->GetEventHandler().PerformDragAndDrop(event,
1436 } else if (drag_target_.Get()) {
1437 result = mouse_event_manager_->DispatchDragEvent(
1438 event_type_names::kDrop, drag_target_.Get(), nullptr, event,
1445 #if BUILDFLAG(IS_TIZEN)
1446 void EventHandler::EnterDragState() {
1447 gesture_manager_->EnterDragState();
1451 void EventHandler::ClearDragState() {
1452 scroll_manager_->StopAutoscroll();
1453 drag_target_ = nullptr;
1454 capturing_mouse_events_element_ = nullptr;
1455 ReleaseMouseCaptureFromLocalRoot();
1456 should_only_fire_drag_over_event_ = false;
1459 #if BUILDFLAG(IS_TIZEN_TV)
1460 bool EventHandler::ScrollNewPosition(const ScrollOffset& scroll_offset,
1461 LocalFrameView* frame_view) {
1462 ScrollableArea* const scrollable_area = frame_view->LayoutViewport();
1463 if (!scrollable_area)
1466 gfx::PointF ori_point = scrollable_area->ScrollPosition();
1467 scrollable_area->ScrollBy(scroll_offset, mojom::blink::ScrollType::kUser);
1468 gfx::PointF new_point = scrollable_area->ScrollPosition();
1469 return new_point != ori_point;
1472 bool EventHandler::ScrollWithMultiplier(const ScrollOffset& scroll_offset,
1474 LocalFrame* frame = frame_;
1476 mojom::blink::ScrollDirection vertical_direction =
1477 (scroll_offset.y() < 0)
1478 ? mojom::blink::ScrollDirection::kScrollUpIgnoringWritingMode
1479 : mojom::blink::ScrollDirection::kScrollDownIgnoringWritingMode;
1480 mojom::blink::ScrollDirection horizontal_direction =
1481 (scroll_offset.x() < 0)
1482 ? mojom::blink::ScrollDirection::kScrollLeftIgnoringWritingMode
1483 : mojom::blink::ScrollDirection::kScrollRightIgnoringWritingMode;
1484 if (scroll_manager_->LogicalScroll(
1485 vertical_direction, ui::ScrollGranularity::kScrollByPixel, start_node,
1486 nullptr, std::abs(scroll_offset.y())))
1489 if (scroll_manager_->LogicalScroll(
1490 horizontal_direction, ui::ScrollGranularity::kScrollByPixel, start_node,
1491 nullptr, std::abs(scroll_offset.x())))
1494 LocalFrameView* frame_view = frame->View();
1495 if (frame_view && ScrollNewPosition(scroll_offset, frame_view))
1498 frame = DynamicTo<LocalFrame>(frame->Tree().Parent());
1500 start_node = frame->DeprecatedLocalOwner();
1506 void EventHandler::RecomputeMouseHoverStateIfNeeded() {
1507 mouse_event_manager_->RecomputeMouseHoverStateIfNeeded();
1510 void EventHandler::MarkHoverStateDirty() {
1511 mouse_event_manager_->MarkHoverStateDirty();
1514 Element* EventHandler::EffectiveMouseEventTargetElement(
1515 Element* target_element) {
1516 Element* new_element_under_mouse = target_element;
1517 if (pointer_event_manager_->GetMouseCaptureTarget())
1518 new_element_under_mouse = pointer_event_manager_->GetMouseCaptureTarget();
1519 return new_element_under_mouse;
1522 Element* EventHandler::GetElementUnderMouse() {
1523 return mouse_event_manager_->GetElementUnderMouse();
1526 Element* EventHandler::CurrentTouchDownElement() {
1527 return pointer_event_manager_->CurrentTouchDownElement();
1530 void EventHandler::SetDelayedNavigationTaskHandle(TaskHandle task_handle) {
1531 delayed_navigation_task_handle_ = std::move(task_handle);
1534 TaskHandle& EventHandler::GetDelayedNavigationTaskHandle() {
1535 return delayed_navigation_task_handle_;
1538 bool EventHandler::IsPointerIdActiveOnFrame(PointerId pointer_id,
1539 LocalFrame* frame) const {
1540 DCHECK(frame_ == &frame_->LocalFrameRoot() || frame_ == frame);
1541 return pointer_event_manager_->IsPointerIdActiveOnFrame(pointer_id, frame);
1544 bool EventHandler::RootFrameTrackedActivePointerInCurrentFrame(
1545 PointerId pointer_id) const {
1546 return frame_ != &frame_->LocalFrameRoot() &&
1547 frame_->LocalFrameRoot().GetEventHandler().IsPointerIdActiveOnFrame(
1548 pointer_id, frame_);
1551 bool EventHandler::IsPointerEventActive(PointerId pointer_id) {
1552 return pointer_event_manager_->IsActive(pointer_id) ||
1553 RootFrameTrackedActivePointerInCurrentFrame(pointer_id);
1556 LocalFrame* EventHandler::DetermineActivePointerTrackerFrame(
1557 PointerId pointer_id) const {
1558 // If pointer_id is active on current |frame_|, pointer states are in
1559 // current frame's PEM; otherwise, check if it's a touch-like pointer that
1560 // have its active states in the local frame root's PEM.
1561 if (IsPointerIdActiveOnFrame(pointer_id, frame_))
1562 return frame_.Get();
1563 if (RootFrameTrackedActivePointerInCurrentFrame(pointer_id))
1564 return &frame_->LocalFrameRoot();
1568 void EventHandler::SetPointerCapture(PointerId pointer_id,
1570 bool explicit_capture) {
1571 // TODO(crbug.com/591387): This functionality should be per page not per
1573 LocalFrame* tracking_frame = DetermineActivePointerTrackerFrame(pointer_id);
1576 tracking_frame && tracking_frame->GetEventHandler()
1577 .pointer_event_manager_->SetPointerCapture(
1578 pointer_id, target, explicit_capture);
1580 if (captured && pointer_id == PointerEventFactory::kMouseId) {
1581 CaptureMouseEventsToWidget(true);
1585 void EventHandler::ReleasePointerCapture(PointerId pointer_id,
1587 LocalFrame* tracking_frame = DetermineActivePointerTrackerFrame(pointer_id);
1591 tracking_frame->GetEventHandler()
1592 .pointer_event_manager_->ReleasePointerCapture(pointer_id, target);
1594 if (released && pointer_id == PointerEventFactory::kMouseId) {
1595 CaptureMouseEventsToWidget(false);
1599 void EventHandler::ReleaseMousePointerCapture() {
1600 ReleaseMouseCaptureFromLocalRoot();
1603 bool EventHandler::HasPointerCapture(PointerId pointer_id,
1604 const Element* target) const {
1605 if (LocalFrame* tracking_frame =
1606 DetermineActivePointerTrackerFrame(pointer_id)) {
1607 return tracking_frame->GetEventHandler()
1608 .pointer_event_manager_->HasPointerCapture(pointer_id, target);
1613 void EventHandler::ElementRemoved(Element* target) {
1614 pointer_event_manager_->ElementRemoved(target);
1616 mouse_wheel_event_manager_->ElementRemoved(target);
1619 void EventHandler::ResetMousePositionForPointerUnlock() {
1620 pointer_event_manager_->RemoveLastMousePosition();
1623 bool EventHandler::LongTapShouldInvokeContextMenu() {
1624 return gesture_manager_->GestureContextMenuDeferred();
1627 WebInputEventResult EventHandler::DispatchMousePointerEvent(
1628 const WebInputEvent::Type event_type,
1629 Element* target_element,
1630 const WebMouseEvent& mouse_event,
1631 const Vector<WebMouseEvent>& coalesced_events,
1632 const Vector<WebMouseEvent>& predicted_events,
1633 bool skip_click_dispatch) {
1634 const auto& event_result = pointer_event_manager_->SendMousePointerEvent(
1635 EffectiveMouseEventTargetElement(target_element), event_type, mouse_event,
1636 coalesced_events, predicted_events, skip_click_dispatch);
1637 return event_result;
1640 WebInputEventResult EventHandler::HandleWheelEvent(
1641 const WebMouseWheelEvent& event) {
1642 return mouse_wheel_event_manager_->HandleWheelEvent(event);
1645 // TODO(crbug.com/665924): This function bypasses all Handle*Event path.
1646 // It should be using that flow instead of creating/sending events directly.
1647 WebInputEventResult EventHandler::HandleTargetedMouseEvent(
1649 const WebMouseEvent& event,
1650 const AtomicString& mouse_event_type,
1651 const Vector<WebMouseEvent>& coalesced_events,
1652 const Vector<WebMouseEvent>& predicted_events) {
1653 mouse_event_manager_->SetClickCount(event.click_count);
1654 return pointer_event_manager_->DirectDispatchMousePointerEvent(
1655 target, event, mouse_event_type, coalesced_events, predicted_events);
1658 WebInputEventResult EventHandler::HandleGestureEvent(
1659 const WebGestureEvent& gesture_event) {
1660 // Propagation to inner frames is handled below this function.
1661 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
1662 DCHECK_NE(0, gesture_event.FrameScale());
1664 // Gesture scroll events are handled on the compositor thread.
1665 DCHECK(!gesture_event.IsScrollEvent());
1667 // Hit test across all frames and do touch adjustment as necessary for the
1669 GestureEventWithHitTestResults targeted_event =
1670 TargetGestureEvent(gesture_event);
1672 return HandleGestureEvent(targeted_event);
1675 WebInputEventResult EventHandler::HandleGestureEvent(
1676 const GestureEventWithHitTestResults& targeted_event) {
1677 TRACE_EVENT0("input", "EventHandler::handleGestureEvent");
1678 if (!frame_->GetPage())
1679 return WebInputEventResult::kNotHandled;
1681 // Propagation to inner frames is handled below this function.
1682 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
1684 // Non-scrolling related gesture events do a single cross-frame hit-test and
1685 // jump directly to the inner most frame. This matches handleMousePressEvent
1687 DCHECK(!targeted_event.Event().IsScrollEvent());
1689 if (targeted_event.Event().GetType() ==
1690 WebInputEvent::Type::kGestureShowPress)
1691 last_show_press_timestamp_ = base::TimeTicks::Now();
1693 // Update mouseout/leave/over/enter events before jumping directly to the
1694 // inner most frame.
1695 if (targeted_event.Event().GetType() == WebInputEvent::Type::kGestureTap)
1696 UpdateGestureTargetNodeForMouseEvent(targeted_event);
1698 // Route to the correct frame.
1699 if (LocalFrame* inner_frame =
1700 targeted_event.GetHitTestResult().InnerNodeFrame())
1701 return inner_frame->GetEventHandler().HandleGestureEventInFrame(
1704 // No hit test result, handle in root instance. Perhaps we should just return
1706 return gesture_manager_->HandleGestureEventInFrame(targeted_event);
1709 WebInputEventResult EventHandler::HandleGestureEventInFrame(
1710 const GestureEventWithHitTestResults& targeted_event) {
1712 targeted_event.Event().GetType() == WebInputEvent::Type::kGestureTap;
1713 if (is_tap && discarded_events_.tap_target != kInvalidDOMNodeId &&
1714 discarded_events_.tap_target ==
1715 targeted_event.InnerNode()->GetDomNodeId() &&
1716 targeted_event.Event().TimeStamp() - discarded_events_.tap_time <
1717 event_handling_util::kDiscardedEventMistakeInterval) {
1718 targeted_event.InnerNode()->GetDocument().CountUse(
1719 WebFeature::kInputEventToRecentlyMovedIframeMistakenlyDiscarded);
1721 if (event_handling_util::ShouldDiscardEventTargetingFrame(
1722 targeted_event.Event(), *frame_)) {
1724 discarded_events_.tap_target = targeted_event.InnerNode()->GetDomNodeId();
1725 discarded_events_.tap_time = targeted_event.Event().TimeStamp();
1727 return WebInputEventResult::kHandledSuppressed;
1730 discarded_events_.tap_target = kInvalidDOMNodeId;
1731 discarded_events_.tap_time = base::TimeTicks();
1733 return gesture_manager_->HandleGestureEventInFrame(targeted_event);
1736 WebInputEventResult EventHandler::HandleGestureScrollEvent(
1737 const WebGestureEvent& gesture_event) {
1738 TRACE_EVENT0("input", "EventHandler::handleGestureScrollEvent");
1739 if (!frame_->GetPage())
1740 return WebInputEventResult::kNotHandled;
1742 return scroll_manager_->HandleGestureScrollEvent(gesture_event);
1745 void EventHandler::SetMouseDownMayStartAutoscroll() {
1746 mouse_event_manager_->SetMouseDownMayStartAutoscroll();
1749 bool EventHandler::IsScrollbarHandlingGestures() const {
1750 return scroll_manager_->IsScrollbarHandlingGestures();
1753 bool EventHandler::ShouldApplyTouchAdjustment(
1754 const WebGestureEvent& event) const {
1755 if (event.primary_pointer_type == WebPointerProperties::PointerType::kPen)
1758 return !event.TapAreaInRootFrame().IsEmpty();
1761 void EventHandler::CacheTouchAdjustmentResult(uint32_t id,
1762 gfx::PointF adjusted_point) {
1763 touch_adjustment_result_.unique_event_id = id;
1764 touch_adjustment_result_.adjusted_point = adjusted_point;
1767 bool EventHandler::GestureCorrespondsToAdjustedTouch(
1768 const WebGestureEvent& event) {
1769 // Gesture events start with a GestureTapDown. If GestureTapDown's unique id
1770 // matches stored adjusted touchstart event id, then we can use the stored
1771 // result for following gesture event.
1772 if (event.GetType() == WebInputEvent::Type::kGestureTapDown) {
1773 should_use_touch_event_adjusted_point_ =
1774 (event.unique_touch_event_id != 0 &&
1775 event.unique_touch_event_id ==
1776 touch_adjustment_result_.unique_event_id);
1779 // Check if the adjusted point is in the gesture event tap rect.
1780 // If not, should not use this touch point in following events.
1781 if (should_use_touch_event_adjusted_point_) {
1782 gfx::SizeF size = event.TapAreaInRootFrame();
1783 gfx::RectF tap_rect(
1784 event.PositionInRootFrame() -
1785 gfx::Vector2dF(size.width() * 0.5, size.height() * 0.5),
1787 should_use_touch_event_adjusted_point_ =
1788 tap_rect.InclusiveContains(touch_adjustment_result_.adjusted_point);
1791 return should_use_touch_event_adjusted_point_;
1794 bool EventHandler::BestNodeForHitTestResult(
1795 TouchAdjustmentCandidateType candidate_type,
1796 const HitTestLocation& location,
1797 const HitTestResult& result,
1798 gfx::Point& adjusted_point,
1799 Node*& adjusted_node) {
1800 TRACE_EVENT0("input", "EventHandler::BestNodeForHitTestResult");
1801 CHECK(location.IsRectBasedTest());
1803 // If the touch is over a scrollbar or a resizer, we don't adjust the touch
1804 // point. This is because touch adjustment only takes into account DOM nodes
1805 // so a touch over a scrollbar or a resizer would be adjusted towards a nearby
1806 // DOM node, making the scrollbar/resizer unusable.
1808 // Context-menu hittests are excluded from this consideration because a
1809 // right-click/long-press doesn't drag the scrollbar therefore prefers DOM
1810 // nodes with relevant contextmenu properties.
1811 if (candidate_type != TouchAdjustmentCandidateType::kContextMenu &&
1812 (result.GetScrollbar() || result.IsOverResizer())) {
1816 gfx::Point touch_hotspot =
1817 frame_->View()->ConvertToRootFrame(location.RoundedPoint());
1818 gfx::Rect touch_rect =
1819 frame_->View()->ConvertToRootFrame(location.ToEnclosingRect());
1821 if (touch_rect.IsEmpty()) {
1825 HeapVector<Member<Node>, 11> nodes(result.ListBasedTestResult());
1827 return FindBestTouchAdjustmentCandidate(candidate_type, adjusted_node,
1828 adjusted_point, touch_hotspot,
1832 // Update the hover and active state across all frames. This logic is
1833 // different than the mouse case because mice send MouseLeave events to frames
1834 // as they're exited. With gestures or manual applications, a single event
1835 // conceptually both 'leaves' whatever frame currently had hover and enters a
1836 // new frame so we need to update state in the old frame chain as well.
1837 void EventHandler::UpdateCrossFrameHoverActiveState(bool is_active,
1838 Element* inner_element) {
1839 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
1841 HeapVector<Member<LocalFrame>> new_hover_frame_chain;
1842 LocalFrame* new_hover_frame_in_document =
1843 inner_element ? inner_element->GetDocument().GetFrame() : nullptr;
1844 // Insert the ancestors of the frame having the new hovered element to the
1845 // frame chain. The frame chain doesn't include the main frame to avoid the
1846 // redundant work that cleans the hover state because the hover state for the
1847 // main frame is updated by calling Document::UpdateHoverActiveState.
1848 while (new_hover_frame_in_document && new_hover_frame_in_document != frame_) {
1849 new_hover_frame_chain.push_back(new_hover_frame_in_document);
1850 Frame* parent_frame = new_hover_frame_in_document->Tree().Parent();
1851 new_hover_frame_in_document = DynamicTo<LocalFrame>(parent_frame);
1854 Element* old_hover_element_in_cur_doc = frame_->GetDocument()->HoverElement();
1855 Element* new_innermost_hover_element = inner_element;
1857 if (new_innermost_hover_element != old_hover_element_in_cur_doc) {
1858 wtf_size_t index_frame_chain = new_hover_frame_chain.size();
1860 // Clear the hover state on any frames which are no longer in the frame
1861 // chain of the hovered element.
1862 while (old_hover_element_in_cur_doc &&
1863 old_hover_element_in_cur_doc->IsFrameOwnerElement()) {
1864 LocalFrame* new_hover_frame = nullptr;
1865 // If we can't get the frame from the new hover frame chain,
1866 // the newHoverFrame will be null and the old hover state will be cleared.
1867 if (index_frame_chain > 0)
1868 new_hover_frame = new_hover_frame_chain[--index_frame_chain];
1870 auto* owner = To<HTMLFrameOwnerElement>(old_hover_element_in_cur_doc);
1871 LocalFrame* old_hover_frame =
1872 DynamicTo<LocalFrame>(owner->ContentFrame());
1873 if (!old_hover_frame)
1876 Document* doc = old_hover_frame->GetDocument();
1880 old_hover_element_in_cur_doc = doc->HoverElement();
1881 // If the old hovered frame is different from the new hovered frame.
1882 // we should clear the old hovered element from the old hovered frame.
1883 if (new_hover_frame != old_hover_frame) {
1884 doc->UpdateHoverActiveState(is_active,
1885 /*update_active_chain=*/true, nullptr);
1890 // Recursively set the new active/hover states on every frame in the chain of
1892 frame_->GetDocument()->UpdateHoverActiveState(is_active,
1893 /*update_active_chain=*/true,
1897 // Update the mouseover/mouseenter/mouseout/mouseleave events across all frames
1898 // for this gesture, before passing the targeted gesture event directly to a hit
1900 void EventHandler::UpdateGestureTargetNodeForMouseEvent(
1901 const GestureEventWithHitTestResults& targeted_event) {
1902 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
1904 // Behaviour of this function is as follows:
1905 // - Create the chain of all entered frames.
1906 // - Compare the last frame chain under the gesture to newly entered frame
1907 // chain from the main frame one by one.
1908 // - If the last frame doesn't match with the entered frame, then create the
1909 // chain of exited frames from the last frame chain.
1910 // - Dispatch mouseout/mouseleave events of the exited frames from the inside
1912 // - Dispatch mouseover/mouseenter events of the entered frames into the
1915 // Insert the ancestors of the frame having the new target node to the entered
1917 HeapVector<Member<LocalFrame>, 2> entered_frame_chain;
1918 LocalFrame* entered_frame_in_document =
1919 targeted_event.GetHitTestResult().InnerNodeFrame();
1920 while (entered_frame_in_document) {
1921 entered_frame_chain.push_back(entered_frame_in_document);
1922 Frame* parent_frame = entered_frame_in_document->Tree().Parent();
1923 entered_frame_in_document = DynamicTo<LocalFrame>(parent_frame);
1926 wtf_size_t index_entered_frame_chain = entered_frame_chain.size();
1927 LocalFrame* exited_frame_in_document = frame_;
1928 HeapVector<Member<LocalFrame>, 2> exited_frame_chain;
1929 // Insert the frame from the disagreement between last frames and entered
1931 while (exited_frame_in_document) {
1932 Node* last_node_under_tap =
1933 exited_frame_in_document->GetEventHandler()
1934 .mouse_event_manager_->GetElementUnderMouse();
1935 if (!last_node_under_tap)
1938 LocalFrame* next_exited_frame_in_document = nullptr;
1939 if (auto* owner = DynamicTo<HTMLFrameOwnerElement>(last_node_under_tap)) {
1940 next_exited_frame_in_document =
1941 DynamicTo<LocalFrame>(owner->ContentFrame());
1944 if (exited_frame_chain.size() > 0) {
1945 exited_frame_chain.push_back(exited_frame_in_document);
1947 LocalFrame* last_entered_frame_in_document =
1948 index_entered_frame_chain
1949 ? entered_frame_chain[index_entered_frame_chain - 1]
1951 if (exited_frame_in_document != last_entered_frame_in_document)
1952 exited_frame_chain.push_back(exited_frame_in_document);
1953 else if (next_exited_frame_in_document && index_entered_frame_chain)
1954 --index_entered_frame_chain;
1956 exited_frame_in_document = next_exited_frame_in_document;
1959 const WebGestureEvent& gesture_event = targeted_event.Event();
1960 unsigned modifiers = gesture_event.GetModifiers();
1961 WebMouseEvent fake_mouse_move(
1962 WebInputEvent::Type::kMouseMove, gesture_event,
1963 WebPointerProperties::Button::kNoButton,
1965 modifiers | WebInputEvent::Modifiers::kIsCompatibilityEventForTouch,
1966 gesture_event.TimeStamp());
1968 // Update the mouseout/mouseleave event
1969 wtf_size_t index_exited_frame_chain = exited_frame_chain.size();
1970 while (index_exited_frame_chain) {
1971 LocalFrame* leave_frame = exited_frame_chain[--index_exited_frame_chain];
1972 leave_frame->GetEventHandler().mouse_event_manager_->SetElementUnderMouse(
1973 EffectiveMouseEventTargetElement(nullptr), fake_mouse_move);
1976 // update the mouseover/mouseenter event
1977 while (index_entered_frame_chain) {
1978 Frame* parent_frame =
1979 entered_frame_chain[--index_entered_frame_chain]->Tree().Parent();
1980 if (auto* parent_local_frame = DynamicTo<LocalFrame>(parent_frame)) {
1981 parent_local_frame->GetEventHandler()
1982 .mouse_event_manager_->SetElementUnderMouse(
1983 EffectiveMouseEventTargetElement(To<HTMLFrameOwnerElement>(
1984 entered_frame_chain[index_entered_frame_chain]->Owner())),
1990 GestureEventWithHitTestResults EventHandler::TargetGestureEvent(
1991 const WebGestureEvent& gesture_event,
1993 TRACE_EVENT0("input", "EventHandler::targetGestureEvent");
1995 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
1996 // Scrolling events get hit tested per frame (like wheel events do).
1997 DCHECK(!gesture_event.IsScrollEvent());
1999 HitTestRequest::HitTestRequestType hit_type =
2000 gesture_manager_->GetHitTypeForGestureType(gesture_event.GetType());
2001 base::TimeDelta active_interval;
2002 bool should_keep_active_for_min_interval = false;
2004 hit_type |= HitTestRequest::kReadOnly;
2006 #if BUILDFLAG(IS_EFL)
2007 !RuntimeEnabledFeatures::TizenCompatibilityModeEnabled() &&
2009 gesture_event.GetType() == WebInputEvent::Type::kGestureTap &&
2010 last_show_press_timestamp_) {
2011 // If the Tap is received very shortly after ShowPress, we want to
2012 // delay clearing of the active state so that it's visible to the user
2013 // for at least a couple of frames.
2015 base::TimeTicks::Now() - last_show_press_timestamp_.value();
2016 should_keep_active_for_min_interval =
2017 active_interval < kMinimumActiveInterval;
2018 if (should_keep_active_for_min_interval)
2019 hit_type |= HitTestRequest::kReadOnly;
2022 GestureEventWithHitTestResults event_with_hit_test_results =
2023 HitTestResultForGestureEvent(gesture_event, hit_type);
2024 // Now apply hover/active state to the final target.
2025 HitTestRequest request(hit_type | HitTestRequest::kAllowChildFrameContent);
2026 if (!request.ReadOnly()) {
2027 UpdateCrossFrameHoverActiveState(
2029 event_with_hit_test_results.GetHitTestResult().InnerElement());
2032 if (should_keep_active_for_min_interval) {
2033 last_deferred_tap_element_ =
2034 event_with_hit_test_results.GetHitTestResult().InnerElement();
2035 // TODO(https://crbug.com/668758): Use a normal BeginFrame update for this.
2036 active_interval_timer_.StartOneShot(
2037 kMinimumActiveInterval - active_interval, FROM_HERE);
2040 return event_with_hit_test_results;
2043 GestureEventWithHitTestResults EventHandler::HitTestResultForGestureEvent(
2044 const WebGestureEvent& gesture_event,
2045 HitTestRequest::HitTestRequestType hit_type) {
2046 // Perform the rect-based hit-test (or point-based if adjustment is
2047 // disabled). Note that we don't yet apply hover/active state here because
2048 // we need to resolve touch adjustment first so that we apply hover/active
2049 // it to the final adjusted node.
2050 hit_type |= HitTestRequest::kReadOnly;
2051 WebGestureEvent adjusted_event = gesture_event;
2052 PhysicalSize hit_rect_size;
2053 if (ShouldApplyTouchAdjustment(gesture_event)) {
2054 // If gesture_event unique id matches the stored touch event result, do
2055 // point-base hit test. Otherwise add padding and do rect-based hit test.
2056 if (GestureCorrespondsToAdjustedTouch(gesture_event)) {
2057 adjusted_event.ApplyTouchAdjustment(
2058 touch_adjustment_result_.adjusted_point);
2060 gfx::SizeF tap_area = adjusted_event.TapAreaInRootFrame();
2061 hit_rect_size = GetHitTestRectForAdjustment(
2062 *frame_, PhysicalSize(LayoutUnit(tap_area.width()),
2063 LayoutUnit(tap_area.height())));
2064 if (!hit_rect_size.IsEmpty())
2065 hit_type |= HitTestRequest::kListBased;
2069 HitTestLocation location;
2070 LocalFrame& root_frame = frame_->LocalFrameRoot();
2071 HitTestResult hit_test_result;
2072 if (hit_rect_size.IsEmpty()) {
2073 location = HitTestLocation(adjusted_event.PositionInRootFrame());
2074 hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
2075 location, hit_type);
2077 PhysicalOffset top_left =
2078 PhysicalOffset::FromPointFRound(adjusted_event.PositionInRootFrame());
2079 top_left -= PhysicalOffset(LayoutUnit(hit_rect_size.width * 0.5f),
2080 LayoutUnit(hit_rect_size.height * 0.5f));
2081 location = HitTestLocation(PhysicalRect(top_left, hit_rect_size));
2082 hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
2083 location, hit_type);
2085 // Adjust the location of the gesture to the most likely nearby node, as
2086 // appropriate for the type of event.
2087 ApplyTouchAdjustment(&adjusted_event, location, hit_test_result);
2088 // Do a new hit-test at the (adjusted) gesture coordinates. This is
2089 // necessary because rect-based hit testing and touch adjustment sometimes
2090 // return a different node than what a point-based hit test would return for
2092 // FIXME: Fix touch adjustment to avoid the need for a redundant hit test.
2093 // http://crbug.com/398914
2094 LocalFrame* hit_frame = hit_test_result.InnerNodeFrame();
2097 location = HitTestLocation(adjusted_event.PositionInRootFrame());
2098 hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
2100 (hit_type | HitTestRequest::kReadOnly) & ~HitTestRequest::kListBased);
2103 // If we did a rect-based hit test it must be resolved to the best single node
2104 // by now to ensure consumers don't accidentally use one of the other
2106 DCHECK(!location.IsRectBasedTest());
2108 return GestureEventWithHitTestResults(adjusted_event, location,
2112 void EventHandler::ApplyTouchAdjustment(WebGestureEvent* gesture_event,
2113 HitTestLocation& location,
2114 HitTestResult& hit_test_result) {
2115 TouchAdjustmentCandidateType touch_adjustment_candiate_type =
2116 TouchAdjustmentCandidateType::kClickable;
2117 switch (gesture_event->GetType()) {
2118 case WebInputEvent::Type::kGestureTap:
2119 case WebInputEvent::Type::kGestureTapUnconfirmed:
2120 case WebInputEvent::Type::kGestureTapDown:
2121 case WebInputEvent::Type::kGestureShowPress:
2123 case WebInputEvent::Type::kGestureShortPress:
2124 case WebInputEvent::Type::kGestureLongPress:
2125 case WebInputEvent::Type::kGestureLongTap:
2126 case WebInputEvent::Type::kGestureTwoFingerTap:
2127 touch_adjustment_candiate_type =
2128 TouchAdjustmentCandidateType::kContextMenu;
2134 Node* adjusted_node = nullptr;
2135 gfx::Point adjusted_point;
2136 if (BestNodeForHitTestResult(touch_adjustment_candiate_type, location,
2137 hit_test_result, adjusted_point,
2139 // Update the hit-test result to be a point-based result instead of a
2140 // rect-based result.
2141 PhysicalOffset point(frame_->View()->ConvertFromRootFrame(adjusted_point));
2142 DCHECK(location.ContainsPoint(gfx::PointF(point)));
2143 DCHECK(location.IsRectBasedTest());
2144 location = hit_test_result.ResolveRectBasedTest(adjusted_node, point);
2145 gesture_event->ApplyTouchAdjustment(
2146 gfx::PointF(adjusted_point.x(), adjusted_point.y()));
2150 WebInputEventResult EventHandler::SendContextMenuEvent(
2151 const WebMouseEvent& event,
2152 Element* override_target_element) {
2153 LocalFrameView* v = frame_->View();
2155 return WebInputEventResult::kNotHandled;
2157 // Clear mouse press state to avoid initiating a drag while context menu is
2159 mouse_event_manager_->ReleaseMousePress();
2160 if (last_scrollbar_under_mouse_)
2161 last_scrollbar_under_mouse_->MouseUp(event);
2163 PhysicalOffset position_in_contents(v->ConvertFromRootFrame(
2164 gfx::ToFlooredPoint(event.PositionInRootFrame())));
2165 HitTestRequest request(HitTestRequest::kActive);
2166 MouseEventWithHitTestResults mev =
2167 frame_->GetDocument()->PerformMouseEventHitTest(
2168 request, position_in_contents, event);
2169 // Since |Document::performMouseEventHitTest()| modifies layout tree for
2170 // setting hover element, we need to update layout tree for requirement of
2171 // |SelectionController::sendContextMenuEvent()|.
2172 frame_->GetDocument()->UpdateStyleAndLayout(
2173 DocumentUpdateReason::kContextMenu);
2175 Element* target_element =
2176 override_target_element ? override_target_element : mev.InnerElement();
2177 return mouse_event_manager_->DispatchMouseEvent(
2178 EffectiveMouseEventTargetElement(target_element),
2179 event_type_names::kContextmenu, event, nullptr, nullptr, false, event.id,
2180 PointerEventFactory::PointerTypeNameForWebPointPointerType(
2181 event.pointer_type));
2184 static bool ShouldShowContextMenuAtSelection(const FrameSelection& selection) {
2185 // TODO(editing-dev): The use of UpdateStyleAndLayout
2186 // needs to be audited. See http://crbug.com/590369 for more details.
2187 selection.GetDocument().UpdateStyleAndLayout(
2188 DocumentUpdateReason::kContextMenu);
2190 const VisibleSelection& visible_selection =
2191 selection.ComputeVisibleSelectionInDOMTree();
2192 if (!visible_selection.IsRange() && !visible_selection.RootEditableElement())
2194 return selection.SelectionHasFocus();
2197 WebInputEventResult EventHandler::ShowNonLocatedContextMenu(
2198 Element* override_target_element,
2199 WebMenuSourceType source_type) {
2200 LocalFrameView* view = frame_->View();
2202 return WebInputEventResult::kNotHandled;
2204 Document* doc = frame_->GetDocument();
2206 return WebInputEventResult::kNotHandled;
2208 static const int kContextMenuMargin = 1;
2210 gfx::Point location_in_root_frame;
2212 Element* focused_element =
2213 override_target_element ? override_target_element : doc->FocusedElement();
2214 FrameSelection& selection = frame_->Selection();
2215 VisualViewport& visual_viewport = frame_->GetPage()->GetVisualViewport();
2217 if (!override_target_element && ShouldShowContextMenuAtSelection(selection)) {
2218 DCHECK(!doc->NeedsLayoutTreeUpdate());
2220 // Enclose the selection rect fully between the handles. If the handles are
2221 // on the same line, the selection rect is empty.
2222 const SelectionInDOMTree& visible_selection =
2223 selection.ComputeVisibleSelectionInDOMTree().AsSelection();
2224 const PositionWithAffinity start_position(
2225 visible_selection.ComputeStartPosition(), visible_selection.Affinity());
2226 const gfx::Point start_point =
2227 GetMiddleSelectionCaretOfPosition(start_position);
2228 const PositionWithAffinity end_position(
2229 visible_selection.ComputeEndPosition(), visible_selection.Affinity());
2230 const gfx::Point end_point =
2231 GetMiddleSelectionCaretOfPosition(end_position);
2233 int left = std::min(start_point.x(), end_point.x());
2234 int top = std::min(start_point.y(), end_point.y());
2235 int right = std::max(start_point.x(), end_point.x());
2236 int bottom = std::max(start_point.y(), end_point.y());
2238 // If selection is a caret and is inside an anchor element, then set that
2239 // as the "focused" element so we can show "open link" option in context
2241 if (visible_selection.IsCaret()) {
2242 Element* anchor_element =
2243 EnclosingAnchorElement(visible_selection.ComputeStartPosition());
2245 focused_element = anchor_element;
2247 // Intersect the selection rect and the visible bounds of focused_element.
2248 if (focused_element) {
2249 gfx::Rect clipped_rect = view->ConvertFromRootFrame(
2250 GetFocusedElementRectForNonLocatedContextMenu(focused_element));
2251 left = std::max(clipped_rect.x(), left);
2252 top = std::max(clipped_rect.y(), top);
2253 right = std::min(clipped_rect.right(), right);
2254 bottom = std::min(clipped_rect.bottom(), bottom);
2256 gfx::Rect selection_rect = gfx::Rect(left, top, right - left, bottom - top);
2258 if (ContainsEvenAtEdge(selection_rect, start_point)) {
2259 location_in_root_frame = view->ConvertToRootFrame(start_point);
2260 } else if (ContainsEvenAtEdge(selection_rect, end_point)) {
2261 location_in_root_frame = view->ConvertToRootFrame(end_point);
2263 location_in_root_frame =
2264 view->ConvertToRootFrame(selection_rect.CenterPoint());
2266 } else if (focused_element) {
2267 gfx::Rect clipped_rect =
2268 GetFocusedElementRectForNonLocatedContextMenu(focused_element);
2269 location_in_root_frame = clipped_rect.CenterPoint();
2271 // TODO(crbug.com/1274078): Should this use ScrollPosition()?
2272 location_in_root_frame =
2273 gfx::Point(visual_viewport.GetScrollOffset().x() + kContextMenuMargin,
2274 visual_viewport.GetScrollOffset().y() + kContextMenuMargin);
2277 frame_->View()->SetCursor(PointerCursor());
2278 gfx::Point global_position =
2279 view->GetChromeClient()
2280 ->LocalRootToScreenDIPs(
2281 gfx::Rect(location_in_root_frame, gfx::Size()), frame_->View())
2284 // Use the focused node as the target for hover and active.
2285 HitTestRequest request(HitTestRequest::kActive);
2286 HitTestLocation location(location_in_root_frame);
2287 HitTestResult result(request, location);
2288 result.SetInnerNode(focused_element ? static_cast<Node*>(focused_element)
2290 doc->UpdateHoverActiveState(request.Active(), !request.Move(),
2291 result.InnerElement());
2293 // The contextmenu event is a mouse event even when invoked using the
2294 // keyboard. This is required for web compatibility.
2295 WebInputEvent::Type event_type = WebInputEvent::Type::kMouseDown;
2296 if (frame_->GetSettings() &&
2297 frame_->GetSettings()->GetShowContextMenuOnMouseUp())
2298 event_type = WebInputEvent::Type::kMouseUp;
2300 WebMouseEvent mouse_event(
2302 gfx::PointF(location_in_root_frame.x(), location_in_root_frame.y()),
2303 gfx::PointF(global_position.x(), global_position.y()),
2304 WebPointerProperties::Button::kNoButton, /* clickCount */ 0,
2305 WebInputEvent::kNoModifiers, base::TimeTicks::Now(), source_type);
2306 mouse_event.id = PointerEventFactory::kMouseId;
2308 // TODO(dtapuska): Transition the mouseEvent to be created really in viewport
2309 // coordinates instead of root frame coordinates.
2310 mouse_event.SetFrameScale(1);
2312 return SendContextMenuEvent(mouse_event, focused_element);
2315 gfx::Rect EventHandler::GetFocusedElementRectForNonLocatedContextMenu(
2316 Element* focused_element) {
2317 gfx::Rect visible_rect = focused_element->VisibleBoundsInLocalRoot();
2319 VisualViewport& visual_viewport = frame_->GetPage()->GetVisualViewport();
2321 // TODO(bokan): This method may not work as expected when the local root
2322 // isn't the main frame since the result won't be transformed and clipped by
2323 // the visual viewport (which is accessible only from the outermost main
2325 if (frame_->LocalFrameRoot().IsOutermostMainFrame()) {
2326 visible_rect = visual_viewport.RootFrameToViewport(visible_rect);
2327 visible_rect.Intersect(gfx::Rect(visual_viewport.Size()));
2330 gfx::Rect clipped_rect = visible_rect;
2331 // The bounding rect of multiline elements may include points that are
2332 // not within the element. Intersect the clipped rect with the first
2333 // outline rect to ensure that the selection rect only includes visible
2334 // points within the focused element.
2335 Vector<gfx::Rect> outline_rects = focused_element->OutlineRectsInWidget();
2336 if (outline_rects.size() > 1)
2337 clipped_rect.Intersect(outline_rects[0]);
2339 return visual_viewport.ViewportToRootFrame(clipped_rect);
2342 void EventHandler::ScheduleHoverStateUpdate() {
2343 // TODO(https://crbug.com/668758): Use a normal BeginFrame update for this.
2344 if (!hover_timer_.IsActive() &&
2345 !mouse_event_manager_->IsMousePositionUnknown())
2346 hover_timer_.StartOneShot(base::TimeDelta(), FROM_HERE);
2349 void EventHandler::ScheduleCursorUpdate() {
2350 // We only want one timer for the page, rather than each frame having it's own
2351 // timer competing which eachother (since there's only one mouse cursor).
2352 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
2354 // TODO(https://crbug.com/668758): Use a normal BeginFrame update for this.
2355 if (!cursor_update_timer_.IsActive())
2356 cursor_update_timer_.StartOneShot(kCursorUpdateInterval, FROM_HERE);
2359 bool EventHandler::CursorUpdatePending() {
2360 return cursor_update_timer_.IsActive();
2363 bool EventHandler::IsHandlingKeyEvent() const {
2364 return keyboard_event_manager_->is_handling_key_event();
2367 void EventHandler::SetResizingFrameSet(HTMLFrameSetElement* frame_set) {
2368 CaptureMouseEventsToWidget(true);
2369 frame_set_being_resized_ = frame_set;
2372 void EventHandler::ResizeScrollableAreaDestroyed() {
2373 scroll_manager_->ClearResizeScrollableArea(true);
2376 void EventHandler::HoverTimerFired(TimerBase*) {
2377 TRACE_EVENT0("input", "EventHandler::hoverTimerFired");
2380 DCHECK(frame_->GetDocument());
2382 if (auto* layout_object = frame_->ContentLayoutObject()) {
2383 if (LocalFrameView* view = frame_->View()) {
2384 HitTestRequest request(HitTestRequest::kMove);
2385 HitTestLocation location(view->ViewportToFrame(
2386 mouse_event_manager_->LastKnownMousePositionInViewport()));
2387 HitTestResult result(request, location);
2388 layout_object->HitTest(location, result);
2389 frame_->GetDocument()->UpdateHoverActiveState(
2390 request.Active(), !request.Move(), result.InnerElement());
2395 void EventHandler::ActiveIntervalTimerFired(TimerBase*) {
2396 TRACE_EVENT0("input", "EventHandler::activeIntervalTimerFired");
2398 if (frame_ && frame_->GetDocument() && last_deferred_tap_element_) {
2399 // FIXME: Enable condition when http://crbug.com/226842 lands
2400 // m_lastDeferredTapElement.get() == m_frame->document()->activeElement()
2401 HitTestRequest request(HitTestRequest::kTouchEvent |
2402 HitTestRequest::kRelease);
2403 frame_->GetDocument()->UpdateHoverActiveState(
2404 request.Active(), !request.Move(), last_deferred_tap_element_.Get());
2406 last_deferred_tap_element_ = nullptr;
2409 void EventHandler::NotifyElementActivated() {
2410 // Since another element has been set to active, stop current timer and clear
2412 active_interval_timer_.Stop();
2413 last_deferred_tap_element_ = nullptr;
2416 bool EventHandler::HandleAccessKey(const WebKeyboardEvent& evt) {
2417 return keyboard_event_manager_->HandleAccessKey(evt);
2420 WebInputEventResult EventHandler::KeyEvent(
2421 const WebKeyboardEvent& initial_key_event) {
2422 return keyboard_event_manager_->KeyEvent(initial_key_event);
2425 void EventHandler::DefaultKeyboardEventHandler(KeyboardEvent* event) {
2426 keyboard_event_manager_->DefaultKeyboardEventHandler(
2427 event, mouse_event_manager_->MousePressNode());
2430 void EventHandler::DragSourceEndedAt(
2431 const WebMouseEvent& event,
2432 ui::mojom::blink::DragOperation operation) {
2433 // Asides from routing the event to the correct frame, the hit test is also an
2434 // opportunity for Layer to update the :hover and :active pseudoclasses.
2435 HitTestRequest request(HitTestRequest::kRelease);
2436 MouseEventWithHitTestResults mev =
2437 event_handling_util::PerformMouseEventHitTest(frame_, request, event);
2439 if (auto* target_frame = LocalFrameFromTargetNode(mev.InnerNode())) {
2440 target_frame->GetEventHandler().DragSourceEndedAt(event, operation);
2444 mouse_event_manager_->DragSourceEndedAt(event, operation);
2446 if (frame_->GetSettings() &&
2447 frame_->GetSettings()->GetTouchDragDropEnabled() &&
2448 frame_->GetSettings()->GetTouchDragEndContextMenu()) {
2449 gesture_manager_->SendContextMenuEventTouchDragEnd(event);
2453 void EventHandler::UpdateDragStateAfterEditDragIfNeeded(
2454 Element* root_editable_element) {
2455 // If inserting the dragged contents removed the drag source, we still want to
2456 // fire dragend at the root editble element.
2457 if (mouse_event_manager_->GetDragState().drag_src_ &&
2458 !mouse_event_manager_->GetDragState().drag_src_->isConnected())
2459 mouse_event_manager_->GetDragState().drag_src_ = root_editable_element;
2462 bool EventHandler::HandleTextInputEvent(const String& text,
2463 Event* underlying_event,
2464 TextEventInputType input_type) {
2465 // Platforms should differentiate real commands like selectAll from text input
2466 // in disguise (like insertNewline), and avoid dispatching text input events
2467 // from keydown default handlers.
2468 auto* keyboard_event = DynamicTo<KeyboardEvent>(underlying_event);
2469 DCHECK(!keyboard_event ||
2470 keyboard_event->type() == event_type_names::kKeypress);
2475 EventTarget* target;
2476 if (underlying_event)
2477 target = underlying_event->target();
2479 target = EventTargetNodeForDocument(frame_->GetDocument());
2483 TextEvent* event = TextEvent::Create(frame_->DomWindow(), text, input_type);
2484 event->SetUnderlyingEvent(underlying_event);
2486 target->DispatchEvent(*event);
2487 return event->DefaultHandled() || event->defaultPrevented();
2490 void EventHandler::DefaultTextInputEventHandler(TextEvent* event) {
2491 if (frame_->GetEditor().HandleTextEvent(event))
2492 event->SetDefaultHandled();
2495 void EventHandler::CapsLockStateMayHaveChanged() {
2496 keyboard_event_manager_->CapsLockStateMayHaveChanged();
2499 bool EventHandler::PassMousePressEventToScrollbar(
2500 MouseEventWithHitTestResults& mev) {
2501 // Do not pass the mouse press to scrollbar if scrollbar pressed. If the
2502 // user's left button is down, then the cursor moves outside the scrollbar
2503 // and presses the middle button , we should not clear
2504 // last_scrollbar_under_mouse_.
2505 if (last_scrollbar_under_mouse_ &&
2506 last_scrollbar_under_mouse_->PressedPart() != ScrollbarPart::kNoPart) {
2510 Scrollbar* scrollbar = mev.GetScrollbar();
2511 UpdateLastScrollbarUnderMouse(scrollbar, true);
2513 if (!scrollbar || !scrollbar->Enabled())
2515 scrollbar->MouseDown(mev.Event());
2516 if (scrollbar->PressedPart() == ScrollbarPart::kThumbPart)
2517 CaptureMouseEventsToWidget(true);
2521 // If scrollbar (under mouse) is different from last, send a mouse exited. Set
2522 // last to scrollbar if setLast is true; else set last to 0.
2523 void EventHandler::UpdateLastScrollbarUnderMouse(Scrollbar* scrollbar,
2525 if (last_scrollbar_under_mouse_ != scrollbar) {
2526 // Send mouse exited to the old scrollbar.
2527 if (last_scrollbar_under_mouse_)
2528 last_scrollbar_under_mouse_->MouseExited();
2530 // Send mouse entered if we're setting a new scrollbar.
2531 if (scrollbar && set_last)
2532 scrollbar->MouseEntered();
2534 last_scrollbar_under_mouse_ = set_last ? scrollbar : nullptr;
2538 WebInputEventResult EventHandler::PassMousePressEventToSubframe(
2539 MouseEventWithHitTestResults& mev,
2540 LocalFrame* subframe) {
2541 GetSelectionController().PassMousePressEventToSubframe(mev);
2542 WebInputEventResult result =
2543 subframe->GetEventHandler().HandleMousePressEvent(mev.Event());
2544 if (result != WebInputEventResult::kNotHandled)
2546 return WebInputEventResult::kHandledSystem;
2549 WebInputEventResult EventHandler::PassMouseMoveEventToSubframe(
2550 MouseEventWithHitTestResults& mev,
2551 const Vector<WebMouseEvent>& coalesced_events,
2552 const Vector<WebMouseEvent>& predicted_events,
2553 LocalFrame* subframe,
2554 HitTestResult* hovered_node,
2555 HitTestLocation* hit_test_location) {
2556 if (mouse_event_manager_->MouseDownMayStartDrag())
2557 return WebInputEventResult::kNotHandled;
2558 WebInputEventResult result =
2559 subframe->GetEventHandler().HandleMouseMoveOrLeaveEvent(
2560 mev.Event(), coalesced_events, predicted_events, hovered_node,
2562 if (result != WebInputEventResult::kNotHandled)
2564 return WebInputEventResult::kHandledSystem;
2567 WebInputEventResult EventHandler::PassMouseReleaseEventToSubframe(
2568 MouseEventWithHitTestResults& mev,
2569 LocalFrame* subframe) {
2570 return subframe->GetEventHandler().HandleMouseReleaseEvent(mev.Event());
2573 void EventHandler::CaptureMouseEventsToWidget(bool capture) {
2574 if (!frame_->IsLocalRoot()) {
2575 frame_->LocalFrameRoot().GetEventHandler().CaptureMouseEventsToWidget(
2580 if (capture == is_widget_capturing_mouse_events_)
2583 frame_->LocalFrameRoot().Client()->SetMouseCapture(capture);
2584 is_widget_capturing_mouse_events_ = capture;
2587 MouseEventWithHitTestResults EventHandler::GetMouseEventTarget(
2588 const HitTestRequest& request,
2589 const WebMouseEvent& event) {
2590 PhysicalOffset document_point =
2591 event_handling_util::ContentPointFromRootFrame(
2592 frame_, event.PositionInRootFrame());
2594 // TODO(eirage): This does not handle chorded buttons yet.
2595 if (event.GetType() != WebInputEvent::Type::kMouseDown) {
2596 HitTestResult result(request, HitTestLocation(document_point));
2598 Element* capture_target;
2599 if (event_handling_util::SubframeForTargetNode(
2600 capturing_subframe_element_)) {
2601 capture_target = capturing_subframe_element_;
2602 result.SetIsOverEmbeddedContentView(true);
2604 capture_target = pointer_event_manager_->GetMouseCaptureTarget();
2607 if (capture_target) {
2608 LayoutObject* layout_object = capture_target->GetLayoutObject();
2609 PhysicalOffset local_point =
2610 layout_object ? layout_object->AbsoluteToLocalPoint(document_point)
2612 result.SetNodeAndPosition(capture_target, local_point);
2614 result.SetScrollbar(last_scrollbar_under_mouse_);
2615 result.SetURLElement(capture_target->EnclosingLinkEventParentOrSelf());
2617 if (!request.ReadOnly()) {
2618 frame_->GetDocument()->UpdateHoverActiveState(
2619 request.Active(), !request.Move(), result.InnerElement());
2622 return MouseEventWithHitTestResults(
2623 event, HitTestLocation(document_point), result);
2626 return frame_->GetDocument()->PerformMouseEventHitTest(request,
2627 document_point, event);
2630 void EventHandler::ReleaseMouseCaptureFromLocalRoot() {
2631 CaptureMouseEventsToWidget(false);
2633 frame_->LocalFrameRoot()
2635 .ReleaseMouseCaptureFromCurrentFrame();
2638 void EventHandler::ReleaseMouseCaptureFromCurrentFrame() {
2639 if (LocalFrame* subframe = event_handling_util::SubframeForTargetNode(
2640 capturing_subframe_element_))
2641 subframe->GetEventHandler().ReleaseMouseCaptureFromCurrentFrame();
2642 pointer_event_manager_->ReleaseMousePointerCapture();
2643 capturing_subframe_element_ = nullptr;
2646 #if BUILDFLAG(IS_TIZEN_TV)
2647 bool EventHandler::CanRunArrowScrolling(const HitTestResult& result) {
2648 if (result.IsContentEditable() || result.GetScrollbar() ||
2649 result.MediaHasAudio() || result.MediaIsVideo())
2652 if (Node* node = result.InnerNode()) {
2653 if (node->HasMediaControlAncestor() || node->IsMediaControls() ||
2654 node->IsMediaControlElement() || node->WillRespondToMouseClickEvents())
2662 #if BUILDFLAG(IS_EFL)
2663 void EventHandler::ContextMenuEventForWordOrLinkSelection(
2664 const WebGestureEvent& gesture_event) {
2665 LocalFrameView* frame_view = frame_->View();
2669 // Hit-test the provided gesture event, applying touch-adjustment and
2670 // adjusting the location to the most likely nearby node.
2671 WebMouseEvent web_mouse_event(WebInputEvent::Type::kMouseMove, gesture_event,
2672 WebPointerProperties::Button::kNoButton, 0,
2673 WebInputEvent::kNoModifiers,
2674 gesture_event.TimeStamp(), 0);
2676 gfx::Point viewport_position = frame_view->ViewportToFrame(
2677 gfx::ToFlooredPoint(web_mouse_event.PositionInRootFrame()));
2678 HitTestRequest request(HitTestRequest::kActive);
2679 MouseEventWithHitTestResults mouse_event =
2680 frame_->GetDocument()->PerformMouseEventHitTest(
2681 request, PhysicalOffset(viewport_position), web_mouse_event);
2683 if (!frame_->Selection().Contains(PhysicalOffset(viewport_position)) &&
2684 !mouse_event.GetScrollbar() &&
2685 (frame_->Selection()
2686 .ComputeVisibleSelectionInDOMTreeDeprecated()
2687 .IsContentEditable() ||
2688 (mouse_event.InnerNode() && mouse_event.InnerNode()->IsTextNode()) ||
2689 (mouse_event.GetHitTestResult().IsLiveLink()))) {
2690 GetSelectionController().SetMouseDownMayStartSelect(
2691 true); // context menu events are always allowed to perform a selection
2692 GetSelectionController().SelectClosestWordOrLinkFromMouseEvent(
2693 (MouseEvent*)(&mouse_event.Event()), mouse_event.GetHitTestResult());
2698 } // namespace blink