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;
805 mouse_press_event_swallowed_ = false;
808 HitTestRequest request(HitTestRequest::kActive);
809 // Save the document point we generate in case the window coordinate is
810 // invalidated by what happens when we dispatch the event.
811 PhysicalOffset document_point = frame_->View()->ConvertFromRootFrame(
812 PhysicalOffset(gfx::ToFlooredPoint(mouse_event.PositionInRootFrame())));
813 MouseEventWithHitTestResults mev = GetMouseEventTarget(request, mouse_event);
814 if (!mev.InnerNode()) {
815 // An anonymous box can be scrollable.
816 if (PassMousePressEventToScrollbar(mev))
817 return WebInputEventResult::kHandledSystem;
819 mouse_event_manager_->InvalidateClick();
820 return WebInputEventResult::kNotHandled;
823 mouse_event_manager_->SetMousePressNode(mev.InnerNode());
824 frame_->GetDocument()->SetSequentialFocusNavigationStartingPoint(
827 LocalFrame* subframe = event_handling_util::GetTargetSubframe(mev);
829 WebInputEventResult result = PassMousePressEventToSubframe(mev, subframe);
830 // Start capturing future events for this frame. We only do this if we
831 // didn't clear the m_mousePressed flag, which may happen if an AppKit
832 // EmbeddedContentView entered a modal event loop. The capturing should be
833 // done only when the result indicates it has been handled. See
835 mouse_event_manager_->SetCapturesDragging(
836 subframe->GetEventHandler().mouse_event_manager_->CapturesDragging());
837 if (mouse_event_manager_->MousePressed() &&
838 mouse_event_manager_->CapturesDragging()) {
839 capturing_mouse_events_element_ = mev.InnerElement();
840 capturing_subframe_element_ = mev.InnerElement();
843 mouse_event_manager_->InvalidateClick();
847 if (discarded_events_.mouse_down_target != kInvalidDOMNodeId &&
848 discarded_events_.mouse_down_target == mev.InnerNode()->GetDomNodeId() &&
849 mouse_event.TimeStamp() - discarded_events_.mouse_down_time <
850 event_handling_util::kDiscardedEventMistakeInterval) {
851 mev.InnerNode()->GetDocument().CountUse(
852 WebFeature::kInputEventToRecentlyMovedIframeMistakenlyDiscarded);
854 if (event_handling_util::ShouldDiscardEventTargetingFrame(mev.Event(),
856 discarded_events_.mouse_down_target = mev.InnerNode()->GetDomNodeId();
857 discarded_events_.mouse_down_time = mouse_event.TimeStamp();
858 return WebInputEventResult::kHandledSuppressed;
860 discarded_events_.mouse_down_target = kInvalidDOMNodeId;
861 discarded_events_.mouse_down_time = base::TimeTicks();
864 LocalFrame::NotifyUserActivation(
865 frame_, mojom::blink::UserActivationNotificationType::kInteraction,
866 RuntimeEnabledFeatures::BrowserVerifiedUserActivationMouseEnabled());
868 if (RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled()) {
869 // We store whether middle click autoscroll is in progress before calling
870 // stopAutoscroll() because it will set m_autoscrollType to NoAutoscroll on
872 bool is_middle_click_autoscroll_in_progress =
873 scroll_manager_->MiddleClickAutoscrollInProgress();
874 scroll_manager_->StopMiddleClickAutoscroll();
875 if (is_middle_click_autoscroll_in_progress) {
876 // We invalidate the click when exiting middle click auto scroll so that
877 // we don't inadvertently navigate away from the current page (e.g. the
878 // click was on a hyperlink). See <rdar://problem/6095023>.
879 mouse_event_manager_->InvalidateClick();
880 return WebInputEventResult::kHandledSuppressed;
884 mouse_event_manager_->SetClickCount(mouse_event.click_count);
885 mouse_event_manager_->SetClickElement(mev.InnerElement());
887 if (!mouse_event.FromTouch())
888 frame_->Selection().SetCaretBlinkingSuspended(true);
890 WebInputEventResult event_result = DispatchMousePointerEvent(
891 WebInputEvent::Type::kPointerDown, mev.InnerElement(), mev.Event(),
892 Vector<WebMouseEvent>(), Vector<WebMouseEvent>());
894 // Disabled form controls still need to resize the scrollable area.
895 if ((event_result == WebInputEventResult::kNotHandled ||
896 event_result == WebInputEventResult::kHandledSuppressed) &&
898 LocalFrameView* view = frame_->View();
900 mev.InnerNode()->GetLayoutObject()
901 ? mev.InnerNode()->GetLayoutObject()->EnclosingLayer()
903 gfx::Point p = view->ConvertFromRootFrame(
904 gfx::ToFlooredPoint(mouse_event.PositionInRootFrame()));
905 if (layer && layer->GetScrollableArea() &&
906 layer->GetScrollableArea()->IsAbsolutePointInResizeControl(
907 p, kResizerForPointer)) {
908 scroll_manager_->SetResizeScrollableArea(layer, p);
909 return WebInputEventResult::kHandledSystem;
913 // m_selectionInitiationState is initialized after dispatching mousedown
914 // event in order not to keep the selection by DOM APIs because we can't
915 // give the user the chance to handle the selection by user action like
916 // dragging if we keep the selection in case of mousedown. FireFox also has
917 // the same behavior and it's more compatible with other browsers.
918 GetSelectionController().InitializeSelectionState();
919 HitTestResult hit_test_result = event_handling_util::HitTestResultInFrame(
920 frame_, HitTestLocation(document_point), HitTestRequest::kReadOnly);
921 InputDeviceCapabilities* source_capabilities =
922 frame_->DomWindow()->GetInputDeviceCapabilities()->FiresTouchEvents(
923 mouse_event.FromTouch());
925 if (event_result == WebInputEventResult::kNotHandled) {
926 event_result = mouse_event_manager_->HandleMouseFocus(hit_test_result,
927 source_capabilities);
930 if (event_result == WebInputEventResult::kNotHandled || mev.GetScrollbar()) {
931 mouse_event_manager_->SetCapturesDragging(true);
932 // Outermost main frames don't implicitly capture mouse input on MouseDown,
933 // all subframes do (regardless of whether local or remote or fenced).
934 if (frame_->IsAttached() && !frame_->IsOutermostMainFrame())
935 CaptureMouseEventsToWidget(true);
937 mouse_event_manager_->SetCapturesDragging(false);
939 #if BUILDFLAG(IS_TIZEN_TV)
940 if (frame_->GetSettings() && frame_->GetSettings()->GetUseArrowScroll() &&
941 mouse_event.button == WebPointerProperties::Button::kLeft) {
942 handled_mouse_left_button_press_event_ =
943 (SelectCursor(mev.GetHitTestLocation(), mev.GetHitTestResult())
945 .type() == ui::mojom::blink::CursorType::kPointer)
946 ? event_result != WebInputEventResult::kNotHandled
950 if (mev.GetScrollbar())
951 pressed_scrollbar_ = true;
954 if (PassMousePressEventToScrollbar(mev))
955 event_result = WebInputEventResult::kHandledSystem;
957 if (event_result == WebInputEventResult::kNotHandled) {
958 if (ShouldRefetchEventTarget(mev)) {
959 HitTestRequest read_only_request(HitTestRequest::kReadOnly |
960 HitTestRequest::kActive);
961 mev = frame_->GetDocument()->PerformMouseEventHitTest(
962 read_only_request, document_point, mouse_event);
964 event_result = mouse_event_manager_->HandleMousePressEvent(mev);
967 if (mev.GetHitTestResult().InnerNode() &&
968 mouse_event.button == WebPointerProperties::Button::kLeft) {
969 DCHECK_EQ(WebInputEvent::Type::kMouseDown, mouse_event.GetType());
970 HitTestResult result = mev.GetHitTestResult();
971 result.SetToShadowHostIfInUAShadowRoot();
972 frame_->GetChromeClient().OnMouseDown(*result.InnerNode());
975 #if BUILDFLAG(IS_TIZEN_TV)
976 mouse_press_event_swallowed_ =
977 (event_result == WebInputEventResult::kHandledApplication);
983 WebInputEventResult EventHandler::HandleMouseMoveEvent(
984 const WebMouseEvent& event,
985 const Vector<WebMouseEvent>& coalesced_events,
986 const Vector<WebMouseEvent>& predicted_events) {
987 TRACE_EVENT0("blink", "EventHandler::handleMouseMoveEvent");
988 DCHECK(event.GetType() == WebInputEvent::Type::kMouseMove);
989 HitTestResult hovered_node_result;
990 HitTestLocation location;
991 WebInputEventResult result =
992 HandleMouseMoveOrLeaveEvent(event, coalesced_events, predicted_events,
993 &hovered_node_result, &location);
995 Page* page = frame_->GetPage();
999 if (PaintLayer* layer =
1000 event_handling_util::LayerForNode(hovered_node_result.InnerNode())) {
1001 if (ScrollableArea* layer_scrollable_area =
1002 event_handling_util::AssociatedScrollableArea(layer))
1003 layer_scrollable_area->MouseMovedInContentArea();
1006 // Should not convert the hit shadow element to its shadow host, so that
1007 // tooltips in the shadow tree appear correctly.
1008 if (!HasTitleAndNotSVGUseElement(hovered_node_result)) {
1009 hovered_node_result.SetToShadowHostIfInUAShadowRoot();
1011 page->GetChromeClient().MouseDidMoveOverElement(*frame_, location,
1012 hovered_node_result);
1017 void EventHandler::HandleMouseLeaveEvent(const WebMouseEvent& event) {
1018 TRACE_EVENT0("blink", "EventHandler::handleMouseLeaveEvent");
1019 DCHECK(event.GetType() == WebInputEvent::Type::kMouseLeave);
1021 Page* page = frame_->GetPage();
1023 page->GetChromeClient().ClearToolTip(*frame_);
1024 HandleMouseMoveOrLeaveEvent(event, Vector<WebMouseEvent>(),
1025 Vector<WebMouseEvent>());
1026 pointer_event_manager_->RemoveLastMousePosition();
1029 WebInputEventResult EventHandler::HandleMouseMoveOrLeaveEvent(
1030 const WebMouseEvent& mouse_event,
1031 const Vector<WebMouseEvent>& coalesced_events,
1032 const Vector<WebMouseEvent>& predicted_events,
1033 HitTestResult* hovered_node_result,
1034 HitTestLocation* hit_test_location) {
1036 DCHECK(frame_->View());
1037 DCHECK(mouse_event.GetType() == WebInputEvent::Type::kMouseMove ||
1038 mouse_event.GetType() == WebInputEvent::Type::kMouseLeave);
1039 mouse_event_manager_->SetLastKnownMousePosition(mouse_event);
1041 hover_timer_.Stop();
1042 cursor_update_timer_.Stop();
1044 mouse_event_manager_->HandleSvgPanIfNeeded(false);
1046 if (mouse_event.GetType() == WebInputEvent::Type::kMouseMove) {
1047 AnchorElementInteractionTracker* tracker =
1048 frame_->GetDocument()->GetAnchorElementInteractionTracker();
1050 tracker->OnMouseMoveEvent(mouse_event);
1054 // Mouse states need to be reset when mouse move with no button down.
1055 // This is for popup/context_menu opened at mouse_down event and
1056 // mouse_release is not handled in page.
1058 if (mouse_event.button == WebPointerProperties::Button::kNoButton &&
1059 !(mouse_event.GetModifiers() &
1060 WebInputEvent::Modifiers::kRelativeMotionEvent)) {
1061 mouse_event_manager_->ClearDragHeuristicState();
1062 capturing_mouse_events_element_ = nullptr;
1063 ReleaseMouseCaptureFromLocalRoot();
1065 // If the scrollbar still thinks it's being dragged, tell it to stop.
1066 // Can happen on Win if we lose focus (e.g. from Alt-Tab) mid-drag.
1067 if (last_scrollbar_under_mouse_ &&
1068 last_scrollbar_under_mouse_->PressedPart() != ScrollbarPart::kNoPart)
1069 last_scrollbar_under_mouse_->MouseUp(mouse_event);
1072 if (RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled()) {
1073 if (Page* page = frame_->GetPage()) {
1074 page->GetAutoscrollController().HandleMouseMoveForMiddleClickAutoscroll(
1075 frame_, mouse_event_manager_->LastKnownMouseScreenPosition(),
1076 mouse_event.button == WebPointerProperties::Button::kMiddle);
1080 if (frame_set_being_resized_) {
1081 return DispatchMousePointerEvent(
1082 WebInputEvent::Type::kPointerMove, frame_set_being_resized_.Get(),
1083 mouse_event, coalesced_events, predicted_events);
1086 // Send events right to a scrollbar if the mouse is pressed.
1087 if (last_scrollbar_under_mouse_ && mouse_event_manager_->MousePressed()) {
1088 last_scrollbar_under_mouse_->MouseMoved(mouse_event);
1089 return WebInputEventResult::kHandledSystem;
1092 HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kMove;
1093 if (mouse_event_manager_->MousePressed()) {
1094 hit_type |= HitTestRequest::kActive;
1097 // Treat any mouse move events as readonly if the user is currently touching
1099 if (pointer_event_manager_->IsAnyTouchActive() &&
1100 mouse_event.GetType() == WebInputEvent::Type::kMouseMove) {
1101 hit_type |= HitTestRequest::kActive | HitTestRequest::kReadOnly;
1103 HitTestRequest request(hit_type);
1104 HitTestLocation out_location((PhysicalOffset()));
1105 MouseEventWithHitTestResults mev = MouseEventWithHitTestResults(
1106 mouse_event, out_location, HitTestResult(request, out_location));
1108 // We don't want to do a hit-test in MouseLeave scenarios because there
1109 // might actually be some other frame above this one at the specified
1110 // coordinate. So we avoid the hit test but still clear the hover/active
1112 if (mouse_event.GetType() == WebInputEvent::Type::kMouseLeave) {
1113 frame_->GetDocument()->UpdateHoverActiveState(request.Active(),
1114 /*update_active_chain=*/false,
1117 mev = GetMouseEventTarget(request, mouse_event);
1120 if (hovered_node_result)
1121 *hovered_node_result = mev.GetHitTestResult();
1123 if (hit_test_location)
1124 *hit_test_location = mev.GetHitTestLocation();
1126 Scrollbar* scrollbar = nullptr;
1128 if (scroll_manager_->InResizeMode()) {
1129 scroll_manager_->Resize(mev.Event());
1131 scrollbar = mev.GetScrollbar();
1133 UpdateLastScrollbarUnderMouse(scrollbar,
1134 !mouse_event_manager_->MousePressed());
1137 WebInputEventResult event_result = WebInputEventResult::kNotHandled;
1139 mev.InnerElement() && IsA<HTMLPortalElement>(*mev.InnerElement());
1140 bool is_remote_frame = false;
1141 LocalFrame* current_subframe = event_handling_util::GetTargetSubframe(
1142 mev, capturing_mouse_events_element_, &is_remote_frame);
1144 // We want mouseouts to happen first, from the inside out. First send a
1145 // move event to the last subframe so that it will fire mouseouts.
1146 // TODO(lanwei): figure out here if we should call HandleMouseLeaveEvent on a
1147 // mouse move event.
1148 if (last_mouse_move_event_subframe_ &&
1149 last_mouse_move_event_subframe_->Tree().IsDescendantOf(frame_) &&
1150 last_mouse_move_event_subframe_ != current_subframe) {
1151 WebMouseEvent event = mev.Event();
1152 event.SetType(WebInputEvent::Type::kMouseLeave);
1153 last_mouse_move_event_subframe_->GetEventHandler().HandleMouseLeaveEvent(
1155 last_mouse_move_event_subframe_->GetEventHandler()
1156 .mouse_event_manager_->SetLastMousePositionAsUnknown();
1159 if (current_subframe) {
1160 // Update over/out state before passing the event to the subframe.
1161 pointer_event_manager_->SendMouseAndPointerBoundaryEvents(
1162 EffectiveMouseEventTargetElement(mev.InnerElement()), mev.Event());
1164 // Event dispatch in sendMouseAndPointerBoundaryEvents may have caused the
1165 // subframe of the target node to be detached from its LocalFrameView, in
1166 // which case the event should not be passed.
1167 if (current_subframe->View()) {
1169 PassMouseMoveEventToSubframe(mev, coalesced_events, predicted_events,
1170 current_subframe, hovered_node_result);
1173 if (scrollbar && !mouse_event_manager_->MousePressed()) {
1174 // Handle hover effects on platforms that support visual feedback on
1175 // scrollbar hovering.
1176 scrollbar->MouseMoved(mev.Event());
1179 // Set Effective pan action before Pointer cursor is updated.
1180 const WebPointerEvent web_pointer_event(WebInputEvent::Type::kPointerMove,
1181 mev.Event().FlattenTransform());
1182 pointer_event_manager_->SendEffectivePanActionAtPointer(web_pointer_event,
1185 LocalFrameView* view = frame_->View();
1186 if ((!is_remote_frame || is_portal) && view) {
1187 absl::optional<ui::Cursor> optional_cursor =
1188 SelectCursor(mev.GetHitTestLocation(), mev.GetHitTestResult());
1189 if (optional_cursor.has_value()) {
1190 view->SetCursor(optional_cursor.value());
1195 last_mouse_move_event_subframe_ = current_subframe;
1197 if (event_result != WebInputEventResult::kNotHandled)
1198 return event_result;
1200 event_result = DispatchMousePointerEvent(WebInputEvent::Type::kPointerMove,
1201 mev.InnerElement(), mev.Event(),
1202 coalesced_events, predicted_events);
1203 // TODO(crbug.com/346473): Since there is no default action for the mousemove
1204 // event we should consider doing drag&drop even when js cancels the
1205 // mouse move event.
1206 // https://w3c.github.io/uievents/#event-type-mousemove
1207 if (event_result != WebInputEventResult::kNotHandled)
1208 return event_result;
1210 return mouse_event_manager_->HandleMouseDraggedEvent(mev);
1213 WebInputEventResult EventHandler::HandleMouseReleaseEvent(
1214 const WebMouseEvent& mouse_event) {
1215 TRACE_EVENT0("blink", "EventHandler::handleMouseReleaseEvent");
1217 // For 4th/5th button in the mouse since Chrome does not yet send
1218 // button value to Blink but in some cases it does send the event.
1219 // This check is needed to suppress such an event (crbug.com/574959)
1220 if (mouse_event.button == WebPointerProperties::Button::kNoButton)
1221 return WebInputEventResult::kHandledSuppressed;
1223 if (!mouse_event.FromTouch())
1224 frame_->Selection().SetCaretBlinkingSuspended(false);
1226 if (RuntimeEnabledFeatures::MiddleClickAutoscrollEnabled()) {
1227 if (Page* page = frame_->GetPage()) {
1228 page->GetAutoscrollController()
1229 .HandleMouseReleaseForMiddleClickAutoscroll(
1231 mouse_event.button == WebPointerProperties::Button::kMiddle);
1235 mouse_event_manager_->ReleaseMousePress();
1236 mouse_event_manager_->SetLastKnownMousePosition(mouse_event);
1237 mouse_event_manager_->HandleSvgPanIfNeeded(true);
1239 #if BUILDFLAG(IS_TIZEN_TV)
1240 pressed_scrollbar_ = false;
1243 if (frame_set_being_resized_) {
1244 WebInputEventResult result =
1245 mouse_event_manager_->SetMousePositionAndDispatchMouseEvent(
1246 EffectiveMouseEventTargetElement(frame_set_being_resized_.Get()),
1247 event_type_names::kMouseup, mouse_event);
1248 // crbug.com/1053385 release mouse capture only if there are no more mouse
1249 // buttons depressed
1250 if (MouseEvent::WebInputEventModifiersToButtons(
1251 mouse_event.GetModifiers()) == 0)
1252 ReleaseMouseCaptureFromLocalRoot();
1256 if (last_scrollbar_under_mouse_) {
1257 mouse_event_manager_->InvalidateClick();
1258 last_scrollbar_under_mouse_->MouseUp(mouse_event);
1259 // crbug.com/1053385 release mouse capture only if there are no more mouse
1260 // buttons depressed
1261 if (MouseEvent::WebInputEventModifiersToButtons(
1262 mouse_event.GetModifiers()) == 0) {
1263 ReleaseMouseCaptureFromLocalRoot();
1265 return DispatchMousePointerEvent(
1266 WebInputEvent::Type::kPointerUp,
1267 mouse_event_manager_->GetElementUnderMouse(), mouse_event,
1268 Vector<WebMouseEvent>(), Vector<WebMouseEvent>());
1271 // Mouse events simulated from touch should not hit-test again.
1272 DCHECK(!mouse_event.FromTouch());
1273 HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kRelease;
1274 HitTestRequest request(hit_type);
1275 MouseEventWithHitTestResults mev = GetMouseEventTarget(request, mouse_event);
1276 LocalFrame* subframe = event_handling_util::GetTargetSubframe(
1277 mev, capturing_mouse_events_element_.Get());
1278 capturing_mouse_events_element_ = nullptr;
1280 return PassMouseReleaseEventToSubframe(mev, subframe);
1282 WebInputEventResult event_result = WebInputEventResult::kNotHandled;
1284 if (event_handling_util::ShouldDiscardEventTargetingFrame(mev.Event(),
1286 event_result = WebInputEventResult::kHandledSuppressed;
1288 event_result = DispatchMousePointerEvent(
1289 WebInputEvent::Type::kPointerUp, mev.InnerElement(), mev.Event(),
1290 Vector<WebMouseEvent>(), Vector<WebMouseEvent>(),
1291 (GetSelectionController().HasExtendedSelection() &&
1292 IsSelectionOverLink(mev)));
1294 scroll_manager_->ClearResizeScrollableArea(false);
1295 #if BUILDFLAG(IS_TIZEN_TV)
1296 if (frame_->GetSettings() && frame_->GetSettings()->GetUseArrowScroll() &&
1297 mouse_event.button == WebPointerProperties::Button::kLeft) {
1298 bool handled_mouse_left_button_release_event =
1299 (SelectCursor(mev.GetHitTestLocation(), mev.GetHitTestResult())
1301 .type() == ui::mojom::blink::CursorType::kPointer)
1302 ? (event_result != WebInputEventResult::kNotHandled)
1304 if (!handled_mouse_left_button_press_event_ &&
1305 !handled_mouse_left_button_release_event &&
1306 CanRunArrowScrolling(mev.GetHitTestResult())) {
1308 frame_->Client()->RunArrowScroll();
1314 if (event_result == WebInputEventResult::kNotHandled)
1315 event_result = mouse_event_manager_->HandleMouseReleaseEvent(mev);
1317 mouse_event_manager_->HandleMouseReleaseEventUpdateStates();
1319 // crbug.com/1053385 release mouse capture only if there are no more mouse
1320 // buttons depressed
1321 if (MouseEvent::WebInputEventModifiersToButtons(mouse_event.GetModifiers()) ==
1323 ReleaseMouseCaptureFromLocalRoot();
1325 return event_result;
1328 static LocalFrame* LocalFrameFromTargetNode(Node* target) {
1329 auto* html_frame_base_element = DynamicTo<HTMLFrameElementBase>(target);
1330 if (!html_frame_base_element)
1333 // Cross-process drag and drop is not yet supported.
1334 return DynamicTo<LocalFrame>(html_frame_base_element->ContentFrame());
1337 WebInputEventResult EventHandler::UpdateDragAndDrop(
1338 const WebMouseEvent& event,
1339 DataTransfer* data_transfer) {
1340 WebInputEventResult event_result = WebInputEventResult::kNotHandled;
1342 if (!frame_->View())
1343 return event_result;
1345 HitTestRequest request(HitTestRequest::kReadOnly);
1346 MouseEventWithHitTestResults mev =
1347 event_handling_util::PerformMouseEventHitTest(frame_, request, event);
1349 // Drag events should never go to text nodes (following IE, and proper
1350 // mouseover/out dispatch)
1351 Node* new_target = mev.InnerNode();
1352 if (new_target && new_target->IsTextNode())
1353 new_target = FlatTreeTraversal::Parent(*new_target);
1355 if (AutoscrollController* controller =
1356 scroll_manager_->GetAutoscrollController()) {
1357 controller->UpdateDragAndDrop(new_target, event.PositionInRootFrame(),
1361 if (drag_target_ != new_target) {
1362 // FIXME: this ordering was explicitly chosen to match WinIE. However,
1363 // it is sometimes incorrect when dragging within subframes, as seen with
1364 // web_tests/fast/events/drag-in-frames.html.
1366 // Moreover, this ordering conforms to section 7.9.4 of the HTML 5 spec.
1367 // <http://dev.w3.org/html5/spec/Overview.html#drag-and-drop-processing-model>.
1368 if (auto* target_frame = LocalFrameFromTargetNode(new_target)) {
1369 event_result = target_frame->GetEventHandler().UpdateDragAndDrop(
1370 event, data_transfer);
1371 } else if (new_target) {
1372 // As per section 7.9.4 of the HTML 5 spec., we must always fire a drag
1373 // event before firing a dragenter, dragleave, or dragover event.
1374 if (mouse_event_manager_->GetDragState().drag_src_) {
1375 // For now we don't care if event handler cancels default behavior,
1376 // since there is none.
1377 mouse_event_manager_->DispatchDragSrcEvent(event_type_names::kDrag,
1380 event_result = mouse_event_manager_->DispatchDragEvent(
1381 event_type_names::kDragenter, new_target, drag_target_, event,
1385 if (auto* target_frame = LocalFrameFromTargetNode(drag_target_.Get())) {
1386 event_result = target_frame->GetEventHandler().UpdateDragAndDrop(
1387 event, data_transfer);
1388 } else if (drag_target_) {
1389 mouse_event_manager_->DispatchDragEvent(event_type_names::kDragleave,
1390 drag_target_.Get(), new_target,
1391 event, data_transfer);
1395 // We do not explicitly call m_mouseEventManager->dispatchDragEvent here
1396 // because it could ultimately result in the appearance that two dragover
1397 // events fired. So, we mark that we should only fire a dragover event on
1398 // the next call to this function.
1399 should_only_fire_drag_over_event_ = true;
1402 if (auto* target_frame = LocalFrameFromTargetNode(new_target)) {
1403 event_result = target_frame->GetEventHandler().UpdateDragAndDrop(
1404 event, data_transfer);
1405 } else if (new_target) {
1406 // Note, when dealing with sub-frames, we may need to fire only a dragover
1407 // event as a drag event may have been fired earlier.
1408 if (!should_only_fire_drag_over_event_ &&
1409 mouse_event_manager_->GetDragState().drag_src_) {
1410 // For now we don't care if event handler cancels default behavior,
1411 // since there is none.
1412 mouse_event_manager_->DispatchDragSrcEvent(event_type_names::kDrag,
1415 event_result = mouse_event_manager_->DispatchDragEvent(
1416 event_type_names::kDragover, new_target, nullptr, event,
1418 should_only_fire_drag_over_event_ = false;
1421 drag_target_ = new_target;
1423 return event_result;
1426 void EventHandler::CancelDragAndDrop(const WebMouseEvent& event,
1427 DataTransfer* data_transfer) {
1428 if (auto* target_frame = LocalFrameFromTargetNode(drag_target_.Get())) {
1429 target_frame->GetEventHandler().CancelDragAndDrop(event, data_transfer);
1430 } else if (drag_target_.Get()) {
1431 if (mouse_event_manager_->GetDragState().drag_src_) {
1432 mouse_event_manager_->DispatchDragSrcEvent(event_type_names::kDrag,
1435 mouse_event_manager_->DispatchDragEvent(event_type_names::kDragleave,
1436 drag_target_.Get(), nullptr, event,
1442 WebInputEventResult EventHandler::PerformDragAndDrop(
1443 const WebMouseEvent& event,
1444 DataTransfer* data_transfer) {
1445 WebInputEventResult result = WebInputEventResult::kNotHandled;
1446 if (auto* target_frame = LocalFrameFromTargetNode(drag_target_.Get())) {
1447 result = target_frame->GetEventHandler().PerformDragAndDrop(event,
1449 } else if (drag_target_.Get()) {
1450 result = mouse_event_manager_->DispatchDragEvent(
1451 event_type_names::kDrop, drag_target_.Get(), nullptr, event,
1458 #if BUILDFLAG(IS_TIZEN)
1459 void EventHandler::EnterDragState() {
1460 gesture_manager_->EnterDragState();
1464 void EventHandler::ClearDragState() {
1465 scroll_manager_->StopAutoscroll();
1466 drag_target_ = nullptr;
1467 capturing_mouse_events_element_ = nullptr;
1468 ReleaseMouseCaptureFromLocalRoot();
1469 should_only_fire_drag_over_event_ = false;
1472 #if BUILDFLAG(IS_TIZEN_TV)
1473 bool EventHandler::ScrollNewPosition(const ScrollOffset& scroll_offset,
1474 LocalFrameView* frame_view) {
1475 ScrollableArea* const scrollable_area = frame_view->LayoutViewport();
1476 if (!scrollable_area)
1479 gfx::PointF ori_point = scrollable_area->ScrollPosition();
1480 scrollable_area->ScrollBy(scroll_offset, mojom::blink::ScrollType::kUser);
1481 gfx::PointF new_point = scrollable_area->ScrollPosition();
1482 return new_point != ori_point;
1485 bool EventHandler::ScrollWithMultiplier(const ScrollOffset& scroll_offset,
1487 LocalFrame* frame = frame_;
1489 mojom::blink::ScrollDirection vertical_direction =
1490 (scroll_offset.y() < 0)
1491 ? mojom::blink::ScrollDirection::kScrollUpIgnoringWritingMode
1492 : mojom::blink::ScrollDirection::kScrollDownIgnoringWritingMode;
1493 mojom::blink::ScrollDirection horizontal_direction =
1494 (scroll_offset.x() < 0)
1495 ? mojom::blink::ScrollDirection::kScrollLeftIgnoringWritingMode
1496 : mojom::blink::ScrollDirection::kScrollRightIgnoringWritingMode;
1497 if (scroll_manager_->LogicalScroll(
1498 vertical_direction, ui::ScrollGranularity::kScrollByPixel, start_node,
1499 nullptr, std::abs(scroll_offset.y())))
1502 if (scroll_manager_->LogicalScroll(
1503 horizontal_direction, ui::ScrollGranularity::kScrollByPixel, start_node,
1504 nullptr, std::abs(scroll_offset.x())))
1507 LocalFrameView* frame_view = frame->View();
1508 if (frame_view && ScrollNewPosition(scroll_offset, frame_view))
1511 frame = DynamicTo<LocalFrame>(frame->Tree().Parent());
1513 start_node = frame->DeprecatedLocalOwner();
1519 void EventHandler::RecomputeMouseHoverStateIfNeeded() {
1520 mouse_event_manager_->RecomputeMouseHoverStateIfNeeded();
1523 void EventHandler::MarkHoverStateDirty() {
1524 mouse_event_manager_->MarkHoverStateDirty();
1527 Element* EventHandler::EffectiveMouseEventTargetElement(
1528 Element* target_element) {
1529 Element* new_element_under_mouse = target_element;
1530 if (pointer_event_manager_->GetMouseCaptureTarget())
1531 new_element_under_mouse = pointer_event_manager_->GetMouseCaptureTarget();
1532 return new_element_under_mouse;
1535 Element* EventHandler::GetElementUnderMouse() {
1536 return mouse_event_manager_->GetElementUnderMouse();
1539 Element* EventHandler::CurrentTouchDownElement() {
1540 return pointer_event_manager_->CurrentTouchDownElement();
1543 void EventHandler::SetDelayedNavigationTaskHandle(TaskHandle task_handle) {
1544 delayed_navigation_task_handle_ = std::move(task_handle);
1547 TaskHandle& EventHandler::GetDelayedNavigationTaskHandle() {
1548 return delayed_navigation_task_handle_;
1551 bool EventHandler::IsPointerIdActiveOnFrame(PointerId pointer_id,
1552 LocalFrame* frame) const {
1553 DCHECK(frame_ == &frame_->LocalFrameRoot() || frame_ == frame);
1554 return pointer_event_manager_->IsPointerIdActiveOnFrame(pointer_id, frame);
1557 bool EventHandler::RootFrameTrackedActivePointerInCurrentFrame(
1558 PointerId pointer_id) const {
1559 return frame_ != &frame_->LocalFrameRoot() &&
1560 frame_->LocalFrameRoot().GetEventHandler().IsPointerIdActiveOnFrame(
1561 pointer_id, frame_);
1564 bool EventHandler::IsPointerEventActive(PointerId pointer_id) {
1565 return pointer_event_manager_->IsActive(pointer_id) ||
1566 RootFrameTrackedActivePointerInCurrentFrame(pointer_id);
1569 LocalFrame* EventHandler::DetermineActivePointerTrackerFrame(
1570 PointerId pointer_id) const {
1571 // If pointer_id is active on current |frame_|, pointer states are in
1572 // current frame's PEM; otherwise, check if it's a touch-like pointer that
1573 // have its active states in the local frame root's PEM.
1574 if (IsPointerIdActiveOnFrame(pointer_id, frame_))
1575 return frame_.Get();
1576 if (RootFrameTrackedActivePointerInCurrentFrame(pointer_id))
1577 return &frame_->LocalFrameRoot();
1581 void EventHandler::SetPointerCapture(PointerId pointer_id,
1583 bool explicit_capture) {
1584 // TODO(crbug.com/591387): This functionality should be per page not per
1586 LocalFrame* tracking_frame = DetermineActivePointerTrackerFrame(pointer_id);
1589 tracking_frame && tracking_frame->GetEventHandler()
1590 .pointer_event_manager_->SetPointerCapture(
1591 pointer_id, target, explicit_capture);
1593 if (captured && pointer_id == PointerEventFactory::kMouseId) {
1594 CaptureMouseEventsToWidget(true);
1598 void EventHandler::ReleasePointerCapture(PointerId pointer_id,
1600 LocalFrame* tracking_frame = DetermineActivePointerTrackerFrame(pointer_id);
1604 tracking_frame->GetEventHandler()
1605 .pointer_event_manager_->ReleasePointerCapture(pointer_id, target);
1607 if (released && pointer_id == PointerEventFactory::kMouseId) {
1608 CaptureMouseEventsToWidget(false);
1612 void EventHandler::ReleaseMousePointerCapture() {
1613 ReleaseMouseCaptureFromLocalRoot();
1616 bool EventHandler::HasPointerCapture(PointerId pointer_id,
1617 const Element* target) const {
1618 if (LocalFrame* tracking_frame =
1619 DetermineActivePointerTrackerFrame(pointer_id)) {
1620 return tracking_frame->GetEventHandler()
1621 .pointer_event_manager_->HasPointerCapture(pointer_id, target);
1626 void EventHandler::ElementRemoved(Element* target) {
1627 pointer_event_manager_->ElementRemoved(target);
1629 mouse_wheel_event_manager_->ElementRemoved(target);
1632 void EventHandler::ResetMousePositionForPointerUnlock() {
1633 pointer_event_manager_->RemoveLastMousePosition();
1636 bool EventHandler::LongTapShouldInvokeContextMenu() {
1637 return gesture_manager_->GestureContextMenuDeferred();
1640 WebInputEventResult EventHandler::DispatchMousePointerEvent(
1641 const WebInputEvent::Type event_type,
1642 Element* target_element,
1643 const WebMouseEvent& mouse_event,
1644 const Vector<WebMouseEvent>& coalesced_events,
1645 const Vector<WebMouseEvent>& predicted_events,
1646 bool skip_click_dispatch) {
1647 const auto& event_result = pointer_event_manager_->SendMousePointerEvent(
1648 EffectiveMouseEventTargetElement(target_element), event_type, mouse_event,
1649 coalesced_events, predicted_events, skip_click_dispatch);
1650 return event_result;
1653 WebInputEventResult EventHandler::HandleWheelEvent(
1654 const WebMouseWheelEvent& event) {
1655 return mouse_wheel_event_manager_->HandleWheelEvent(event);
1658 // TODO(crbug.com/665924): This function bypasses all Handle*Event path.
1659 // It should be using that flow instead of creating/sending events directly.
1660 WebInputEventResult EventHandler::HandleTargetedMouseEvent(
1662 const WebMouseEvent& event,
1663 const AtomicString& mouse_event_type,
1664 const Vector<WebMouseEvent>& coalesced_events,
1665 const Vector<WebMouseEvent>& predicted_events) {
1666 mouse_event_manager_->SetClickCount(event.click_count);
1667 return pointer_event_manager_->DirectDispatchMousePointerEvent(
1668 target, event, mouse_event_type, coalesced_events, predicted_events);
1671 WebInputEventResult EventHandler::HandleGestureEvent(
1672 const WebGestureEvent& gesture_event) {
1673 // Propagation to inner frames is handled below this function.
1674 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
1675 DCHECK_NE(0, gesture_event.FrameScale());
1677 // Gesture scroll events are handled on the compositor thread.
1678 DCHECK(!gesture_event.IsScrollEvent());
1680 // Hit test across all frames and do touch adjustment as necessary for the
1682 GestureEventWithHitTestResults targeted_event =
1683 TargetGestureEvent(gesture_event);
1685 return HandleGestureEvent(targeted_event);
1688 WebInputEventResult EventHandler::HandleGestureEvent(
1689 const GestureEventWithHitTestResults& targeted_event) {
1690 TRACE_EVENT0("input", "EventHandler::handleGestureEvent");
1691 if (!frame_->GetPage())
1692 return WebInputEventResult::kNotHandled;
1694 // Propagation to inner frames is handled below this function.
1695 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
1697 // Non-scrolling related gesture events do a single cross-frame hit-test and
1698 // jump directly to the inner most frame. This matches handleMousePressEvent
1700 DCHECK(!targeted_event.Event().IsScrollEvent());
1702 if (targeted_event.Event().GetType() ==
1703 WebInputEvent::Type::kGestureShowPress)
1704 last_show_press_timestamp_ = base::TimeTicks::Now();
1706 // Update mouseout/leave/over/enter events before jumping directly to the
1707 // inner most frame.
1708 if (targeted_event.Event().GetType() == WebInputEvent::Type::kGestureTap)
1709 UpdateGestureTargetNodeForMouseEvent(targeted_event);
1711 // Route to the correct frame.
1712 if (LocalFrame* inner_frame =
1713 targeted_event.GetHitTestResult().InnerNodeFrame())
1714 return inner_frame->GetEventHandler().HandleGestureEventInFrame(
1717 // No hit test result, handle in root instance. Perhaps we should just return
1719 return gesture_manager_->HandleGestureEventInFrame(targeted_event);
1722 WebInputEventResult EventHandler::HandleGestureEventInFrame(
1723 const GestureEventWithHitTestResults& targeted_event) {
1725 targeted_event.Event().GetType() == WebInputEvent::Type::kGestureTap;
1726 if (is_tap && discarded_events_.tap_target != kInvalidDOMNodeId &&
1727 discarded_events_.tap_target ==
1728 targeted_event.InnerNode()->GetDomNodeId() &&
1729 targeted_event.Event().TimeStamp() - discarded_events_.tap_time <
1730 event_handling_util::kDiscardedEventMistakeInterval) {
1731 targeted_event.InnerNode()->GetDocument().CountUse(
1732 WebFeature::kInputEventToRecentlyMovedIframeMistakenlyDiscarded);
1734 if (event_handling_util::ShouldDiscardEventTargetingFrame(
1735 targeted_event.Event(), *frame_)) {
1737 discarded_events_.tap_target = targeted_event.InnerNode()->GetDomNodeId();
1738 discarded_events_.tap_time = targeted_event.Event().TimeStamp();
1740 return WebInputEventResult::kHandledSuppressed;
1743 discarded_events_.tap_target = kInvalidDOMNodeId;
1744 discarded_events_.tap_time = base::TimeTicks();
1746 return gesture_manager_->HandleGestureEventInFrame(targeted_event);
1749 WebInputEventResult EventHandler::HandleGestureScrollEvent(
1750 const WebGestureEvent& gesture_event) {
1751 TRACE_EVENT0("input", "EventHandler::handleGestureScrollEvent");
1752 if (!frame_->GetPage())
1753 return WebInputEventResult::kNotHandled;
1755 return scroll_manager_->HandleGestureScrollEvent(gesture_event);
1758 void EventHandler::SetMouseDownMayStartAutoscroll() {
1759 mouse_event_manager_->SetMouseDownMayStartAutoscroll();
1762 bool EventHandler::IsScrollbarHandlingGestures() const {
1763 return scroll_manager_->IsScrollbarHandlingGestures();
1766 bool EventHandler::ShouldApplyTouchAdjustment(
1767 const WebGestureEvent& event) const {
1768 if (event.primary_pointer_type == WebPointerProperties::PointerType::kPen)
1771 return !event.TapAreaInRootFrame().IsEmpty();
1774 void EventHandler::CacheTouchAdjustmentResult(uint32_t id,
1775 gfx::PointF adjusted_point) {
1776 touch_adjustment_result_.unique_event_id = id;
1777 touch_adjustment_result_.adjusted_point = adjusted_point;
1780 bool EventHandler::GestureCorrespondsToAdjustedTouch(
1781 const WebGestureEvent& event) {
1782 // Gesture events start with a GestureTapDown. If GestureTapDown's unique id
1783 // matches stored adjusted touchstart event id, then we can use the stored
1784 // result for following gesture event.
1785 if (event.GetType() == WebInputEvent::Type::kGestureTapDown) {
1786 should_use_touch_event_adjusted_point_ =
1787 (event.unique_touch_event_id != 0 &&
1788 event.unique_touch_event_id ==
1789 touch_adjustment_result_.unique_event_id);
1792 // Check if the adjusted point is in the gesture event tap rect.
1793 // If not, should not use this touch point in following events.
1794 if (should_use_touch_event_adjusted_point_) {
1795 gfx::SizeF size = event.TapAreaInRootFrame();
1796 gfx::RectF tap_rect(
1797 event.PositionInRootFrame() -
1798 gfx::Vector2dF(size.width() * 0.5, size.height() * 0.5),
1800 should_use_touch_event_adjusted_point_ =
1801 tap_rect.InclusiveContains(touch_adjustment_result_.adjusted_point);
1804 return should_use_touch_event_adjusted_point_;
1807 bool EventHandler::BestNodeForHitTestResult(
1808 TouchAdjustmentCandidateType candidate_type,
1809 const HitTestLocation& location,
1810 const HitTestResult& result,
1811 gfx::Point& adjusted_point,
1812 Node*& adjusted_node) {
1813 TRACE_EVENT0("input", "EventHandler::BestNodeForHitTestResult");
1814 CHECK(location.IsRectBasedTest());
1816 // If the touch is over a scrollbar or a resizer, we don't adjust the touch
1817 // point. This is because touch adjustment only takes into account DOM nodes
1818 // so a touch over a scrollbar or a resizer would be adjusted towards a nearby
1819 // DOM node, making the scrollbar/resizer unusable.
1821 // Context-menu hittests are excluded from this consideration because a
1822 // right-click/long-press doesn't drag the scrollbar therefore prefers DOM
1823 // nodes with relevant contextmenu properties.
1824 if (candidate_type != TouchAdjustmentCandidateType::kContextMenu &&
1825 (result.GetScrollbar() || result.IsOverResizer())) {
1829 gfx::Point touch_hotspot =
1830 frame_->View()->ConvertToRootFrame(location.RoundedPoint());
1831 gfx::Rect touch_rect =
1832 frame_->View()->ConvertToRootFrame(location.ToEnclosingRect());
1834 if (touch_rect.IsEmpty()) {
1838 HeapVector<Member<Node>, 11> nodes(result.ListBasedTestResult());
1840 return FindBestTouchAdjustmentCandidate(candidate_type, adjusted_node,
1841 adjusted_point, touch_hotspot,
1845 // Update the hover and active state across all frames. This logic is
1846 // different than the mouse case because mice send MouseLeave events to frames
1847 // as they're exited. With gestures or manual applications, a single event
1848 // conceptually both 'leaves' whatever frame currently had hover and enters a
1849 // new frame so we need to update state in the old frame chain as well.
1850 void EventHandler::UpdateCrossFrameHoverActiveState(bool is_active,
1851 Element* inner_element) {
1852 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
1854 HeapVector<Member<LocalFrame>> new_hover_frame_chain;
1855 LocalFrame* new_hover_frame_in_document =
1856 inner_element ? inner_element->GetDocument().GetFrame() : nullptr;
1857 // Insert the ancestors of the frame having the new hovered element to the
1858 // frame chain. The frame chain doesn't include the main frame to avoid the
1859 // redundant work that cleans the hover state because the hover state for the
1860 // main frame is updated by calling Document::UpdateHoverActiveState.
1861 while (new_hover_frame_in_document && new_hover_frame_in_document != frame_) {
1862 new_hover_frame_chain.push_back(new_hover_frame_in_document);
1863 Frame* parent_frame = new_hover_frame_in_document->Tree().Parent();
1864 new_hover_frame_in_document = DynamicTo<LocalFrame>(parent_frame);
1867 Element* old_hover_element_in_cur_doc = frame_->GetDocument()->HoverElement();
1868 Element* new_innermost_hover_element = inner_element;
1870 if (new_innermost_hover_element != old_hover_element_in_cur_doc) {
1871 wtf_size_t index_frame_chain = new_hover_frame_chain.size();
1873 // Clear the hover state on any frames which are no longer in the frame
1874 // chain of the hovered element.
1875 while (old_hover_element_in_cur_doc &&
1876 old_hover_element_in_cur_doc->IsFrameOwnerElement()) {
1877 LocalFrame* new_hover_frame = nullptr;
1878 // If we can't get the frame from the new hover frame chain,
1879 // the newHoverFrame will be null and the old hover state will be cleared.
1880 if (index_frame_chain > 0)
1881 new_hover_frame = new_hover_frame_chain[--index_frame_chain];
1883 auto* owner = To<HTMLFrameOwnerElement>(old_hover_element_in_cur_doc);
1884 LocalFrame* old_hover_frame =
1885 DynamicTo<LocalFrame>(owner->ContentFrame());
1886 if (!old_hover_frame)
1889 Document* doc = old_hover_frame->GetDocument();
1893 old_hover_element_in_cur_doc = doc->HoverElement();
1894 // If the old hovered frame is different from the new hovered frame.
1895 // we should clear the old hovered element from the old hovered frame.
1896 if (new_hover_frame != old_hover_frame) {
1897 doc->UpdateHoverActiveState(is_active,
1898 /*update_active_chain=*/true, nullptr);
1903 // Recursively set the new active/hover states on every frame in the chain of
1905 frame_->GetDocument()->UpdateHoverActiveState(is_active,
1906 /*update_active_chain=*/true,
1910 // Update the mouseover/mouseenter/mouseout/mouseleave events across all frames
1911 // for this gesture, before passing the targeted gesture event directly to a hit
1913 void EventHandler::UpdateGestureTargetNodeForMouseEvent(
1914 const GestureEventWithHitTestResults& targeted_event) {
1915 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
1917 // Behaviour of this function is as follows:
1918 // - Create the chain of all entered frames.
1919 // - Compare the last frame chain under the gesture to newly entered frame
1920 // chain from the main frame one by one.
1921 // - If the last frame doesn't match with the entered frame, then create the
1922 // chain of exited frames from the last frame chain.
1923 // - Dispatch mouseout/mouseleave events of the exited frames from the inside
1925 // - Dispatch mouseover/mouseenter events of the entered frames into the
1928 // Insert the ancestors of the frame having the new target node to the entered
1930 HeapVector<Member<LocalFrame>, 2> entered_frame_chain;
1931 LocalFrame* entered_frame_in_document =
1932 targeted_event.GetHitTestResult().InnerNodeFrame();
1933 while (entered_frame_in_document) {
1934 entered_frame_chain.push_back(entered_frame_in_document);
1935 Frame* parent_frame = entered_frame_in_document->Tree().Parent();
1936 entered_frame_in_document = DynamicTo<LocalFrame>(parent_frame);
1939 wtf_size_t index_entered_frame_chain = entered_frame_chain.size();
1940 LocalFrame* exited_frame_in_document = frame_;
1941 HeapVector<Member<LocalFrame>, 2> exited_frame_chain;
1942 // Insert the frame from the disagreement between last frames and entered
1944 while (exited_frame_in_document) {
1945 Node* last_node_under_tap =
1946 exited_frame_in_document->GetEventHandler()
1947 .mouse_event_manager_->GetElementUnderMouse();
1948 if (!last_node_under_tap)
1951 LocalFrame* next_exited_frame_in_document = nullptr;
1952 if (auto* owner = DynamicTo<HTMLFrameOwnerElement>(last_node_under_tap)) {
1953 next_exited_frame_in_document =
1954 DynamicTo<LocalFrame>(owner->ContentFrame());
1957 if (exited_frame_chain.size() > 0) {
1958 exited_frame_chain.push_back(exited_frame_in_document);
1960 LocalFrame* last_entered_frame_in_document =
1961 index_entered_frame_chain
1962 ? entered_frame_chain[index_entered_frame_chain - 1]
1964 if (exited_frame_in_document != last_entered_frame_in_document)
1965 exited_frame_chain.push_back(exited_frame_in_document);
1966 else if (next_exited_frame_in_document && index_entered_frame_chain)
1967 --index_entered_frame_chain;
1969 exited_frame_in_document = next_exited_frame_in_document;
1972 const WebGestureEvent& gesture_event = targeted_event.Event();
1973 unsigned modifiers = gesture_event.GetModifiers();
1974 WebMouseEvent fake_mouse_move(
1975 WebInputEvent::Type::kMouseMove, gesture_event,
1976 WebPointerProperties::Button::kNoButton,
1978 modifiers | WebInputEvent::Modifiers::kIsCompatibilityEventForTouch,
1979 gesture_event.TimeStamp());
1981 // Update the mouseout/mouseleave event
1982 wtf_size_t index_exited_frame_chain = exited_frame_chain.size();
1983 while (index_exited_frame_chain) {
1984 LocalFrame* leave_frame = exited_frame_chain[--index_exited_frame_chain];
1985 leave_frame->GetEventHandler().mouse_event_manager_->SetElementUnderMouse(
1986 EffectiveMouseEventTargetElement(nullptr), fake_mouse_move);
1989 // update the mouseover/mouseenter event
1990 while (index_entered_frame_chain) {
1991 Frame* parent_frame =
1992 entered_frame_chain[--index_entered_frame_chain]->Tree().Parent();
1993 if (auto* parent_local_frame = DynamicTo<LocalFrame>(parent_frame)) {
1994 parent_local_frame->GetEventHandler()
1995 .mouse_event_manager_->SetElementUnderMouse(
1996 EffectiveMouseEventTargetElement(To<HTMLFrameOwnerElement>(
1997 entered_frame_chain[index_entered_frame_chain]->Owner())),
2003 GestureEventWithHitTestResults EventHandler::TargetGestureEvent(
2004 const WebGestureEvent& gesture_event,
2006 TRACE_EVENT0("input", "EventHandler::targetGestureEvent");
2008 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
2009 // Scrolling events get hit tested per frame (like wheel events do).
2010 DCHECK(!gesture_event.IsScrollEvent());
2012 HitTestRequest::HitTestRequestType hit_type =
2013 gesture_manager_->GetHitTypeForGestureType(gesture_event.GetType());
2014 base::TimeDelta active_interval;
2015 bool should_keep_active_for_min_interval = false;
2017 hit_type |= HitTestRequest::kReadOnly;
2019 #if BUILDFLAG(IS_EFL)
2020 !RuntimeEnabledFeatures::TizenCompatibilityModeEnabled() &&
2022 gesture_event.GetType() == WebInputEvent::Type::kGestureTap &&
2023 last_show_press_timestamp_) {
2024 // If the Tap is received very shortly after ShowPress, we want to
2025 // delay clearing of the active state so that it's visible to the user
2026 // for at least a couple of frames.
2028 base::TimeTicks::Now() - last_show_press_timestamp_.value();
2029 should_keep_active_for_min_interval =
2030 active_interval < kMinimumActiveInterval;
2031 if (should_keep_active_for_min_interval)
2032 hit_type |= HitTestRequest::kReadOnly;
2035 GestureEventWithHitTestResults event_with_hit_test_results =
2036 HitTestResultForGestureEvent(gesture_event, hit_type);
2037 // Now apply hover/active state to the final target.
2038 HitTestRequest request(hit_type | HitTestRequest::kAllowChildFrameContent);
2039 if (!request.ReadOnly()) {
2040 UpdateCrossFrameHoverActiveState(
2042 event_with_hit_test_results.GetHitTestResult().InnerElement());
2045 if (should_keep_active_for_min_interval) {
2046 last_deferred_tap_element_ =
2047 event_with_hit_test_results.GetHitTestResult().InnerElement();
2048 // TODO(https://crbug.com/668758): Use a normal BeginFrame update for this.
2049 active_interval_timer_.StartOneShot(
2050 kMinimumActiveInterval - active_interval, FROM_HERE);
2053 return event_with_hit_test_results;
2056 GestureEventWithHitTestResults EventHandler::HitTestResultForGestureEvent(
2057 const WebGestureEvent& gesture_event,
2058 HitTestRequest::HitTestRequestType hit_type) {
2059 // Perform the rect-based hit-test (or point-based if adjustment is
2060 // disabled). Note that we don't yet apply hover/active state here because
2061 // we need to resolve touch adjustment first so that we apply hover/active
2062 // it to the final adjusted node.
2063 hit_type |= HitTestRequest::kReadOnly;
2064 WebGestureEvent adjusted_event = gesture_event;
2065 PhysicalSize hit_rect_size;
2066 if (ShouldApplyTouchAdjustment(gesture_event)) {
2067 // If gesture_event unique id matches the stored touch event result, do
2068 // point-base hit test. Otherwise add padding and do rect-based hit test.
2069 if (GestureCorrespondsToAdjustedTouch(gesture_event)) {
2070 adjusted_event.ApplyTouchAdjustment(
2071 touch_adjustment_result_.adjusted_point);
2073 gfx::SizeF tap_area = adjusted_event.TapAreaInRootFrame();
2074 hit_rect_size = GetHitTestRectForAdjustment(
2075 *frame_, PhysicalSize(LayoutUnit(tap_area.width()),
2076 LayoutUnit(tap_area.height())));
2077 if (!hit_rect_size.IsEmpty())
2078 hit_type |= HitTestRequest::kListBased;
2082 HitTestLocation location;
2083 LocalFrame& root_frame = frame_->LocalFrameRoot();
2084 HitTestResult hit_test_result;
2085 if (hit_rect_size.IsEmpty()) {
2086 location = HitTestLocation(adjusted_event.PositionInRootFrame());
2087 hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
2088 location, hit_type);
2090 PhysicalOffset top_left =
2091 PhysicalOffset::FromPointFRound(adjusted_event.PositionInRootFrame());
2092 top_left -= PhysicalOffset(LayoutUnit(hit_rect_size.width * 0.5f),
2093 LayoutUnit(hit_rect_size.height * 0.5f));
2094 location = HitTestLocation(PhysicalRect(top_left, hit_rect_size));
2095 hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
2096 location, hit_type);
2098 // Adjust the location of the gesture to the most likely nearby node, as
2099 // appropriate for the type of event.
2100 ApplyTouchAdjustment(&adjusted_event, location, hit_test_result);
2101 // Do a new hit-test at the (adjusted) gesture coordinates. This is
2102 // necessary because rect-based hit testing and touch adjustment sometimes
2103 // return a different node than what a point-based hit test would return for
2105 // FIXME: Fix touch adjustment to avoid the need for a redundant hit test.
2106 // http://crbug.com/398914
2107 LocalFrame* hit_frame = hit_test_result.InnerNodeFrame();
2110 location = HitTestLocation(adjusted_event.PositionInRootFrame());
2111 hit_test_result = root_frame.GetEventHandler().HitTestResultAtLocation(
2113 (hit_type | HitTestRequest::kReadOnly) & ~HitTestRequest::kListBased);
2116 // If we did a rect-based hit test it must be resolved to the best single node
2117 // by now to ensure consumers don't accidentally use one of the other
2119 DCHECK(!location.IsRectBasedTest());
2121 return GestureEventWithHitTestResults(adjusted_event, location,
2125 void EventHandler::ApplyTouchAdjustment(WebGestureEvent* gesture_event,
2126 HitTestLocation& location,
2127 HitTestResult& hit_test_result) {
2128 TouchAdjustmentCandidateType touch_adjustment_candiate_type =
2129 TouchAdjustmentCandidateType::kClickable;
2130 switch (gesture_event->GetType()) {
2131 case WebInputEvent::Type::kGestureTap:
2132 case WebInputEvent::Type::kGestureTapUnconfirmed:
2133 case WebInputEvent::Type::kGestureTapDown:
2134 case WebInputEvent::Type::kGestureShowPress:
2136 case WebInputEvent::Type::kGestureShortPress:
2137 case WebInputEvent::Type::kGestureLongPress:
2138 case WebInputEvent::Type::kGestureLongTap:
2139 case WebInputEvent::Type::kGestureTwoFingerTap:
2140 touch_adjustment_candiate_type =
2141 TouchAdjustmentCandidateType::kContextMenu;
2147 Node* adjusted_node = nullptr;
2148 gfx::Point adjusted_point;
2149 if (BestNodeForHitTestResult(touch_adjustment_candiate_type, location,
2150 hit_test_result, adjusted_point,
2152 // Update the hit-test result to be a point-based result instead of a
2153 // rect-based result.
2154 PhysicalOffset point(frame_->View()->ConvertFromRootFrame(adjusted_point));
2155 DCHECK(location.ContainsPoint(gfx::PointF(point)));
2156 DCHECK(location.IsRectBasedTest());
2157 location = hit_test_result.ResolveRectBasedTest(adjusted_node, point);
2158 gesture_event->ApplyTouchAdjustment(
2159 gfx::PointF(adjusted_point.x(), adjusted_point.y()));
2163 WebInputEventResult EventHandler::SendContextMenuEvent(
2164 const WebMouseEvent& event,
2165 Element* override_target_element) {
2166 LocalFrameView* v = frame_->View();
2168 return WebInputEventResult::kNotHandled;
2170 // Clear mouse press state to avoid initiating a drag while context menu is
2172 mouse_event_manager_->ReleaseMousePress();
2173 if (last_scrollbar_under_mouse_)
2174 last_scrollbar_under_mouse_->MouseUp(event);
2176 PhysicalOffset position_in_contents(v->ConvertFromRootFrame(
2177 gfx::ToFlooredPoint(event.PositionInRootFrame())));
2178 HitTestRequest request(HitTestRequest::kActive);
2179 MouseEventWithHitTestResults mev =
2180 frame_->GetDocument()->PerformMouseEventHitTest(
2181 request, position_in_contents, event);
2182 // Since |Document::performMouseEventHitTest()| modifies layout tree for
2183 // setting hover element, we need to update layout tree for requirement of
2184 // |SelectionController::sendContextMenuEvent()|.
2185 frame_->GetDocument()->UpdateStyleAndLayout(
2186 DocumentUpdateReason::kContextMenu);
2188 Element* target_element =
2189 override_target_element ? override_target_element : mev.InnerElement();
2190 return mouse_event_manager_->DispatchMouseEvent(
2191 EffectiveMouseEventTargetElement(target_element),
2192 event_type_names::kContextmenu, event, nullptr, nullptr, false, event.id,
2193 PointerEventFactory::PointerTypeNameForWebPointPointerType(
2194 event.pointer_type));
2197 static bool ShouldShowContextMenuAtSelection(const FrameSelection& selection) {
2198 // TODO(editing-dev): The use of UpdateStyleAndLayout
2199 // needs to be audited. See http://crbug.com/590369 for more details.
2200 selection.GetDocument().UpdateStyleAndLayout(
2201 DocumentUpdateReason::kContextMenu);
2203 const VisibleSelection& visible_selection =
2204 selection.ComputeVisibleSelectionInDOMTree();
2205 if (!visible_selection.IsRange() && !visible_selection.RootEditableElement())
2207 return selection.SelectionHasFocus();
2210 WebInputEventResult EventHandler::ShowNonLocatedContextMenu(
2211 Element* override_target_element,
2212 WebMenuSourceType source_type) {
2213 LocalFrameView* view = frame_->View();
2215 return WebInputEventResult::kNotHandled;
2217 Document* doc = frame_->GetDocument();
2219 return WebInputEventResult::kNotHandled;
2221 static const int kContextMenuMargin = 1;
2223 gfx::Point location_in_root_frame;
2225 Element* focused_element =
2226 override_target_element ? override_target_element : doc->FocusedElement();
2227 FrameSelection& selection = frame_->Selection();
2228 VisualViewport& visual_viewport = frame_->GetPage()->GetVisualViewport();
2230 if (!override_target_element && ShouldShowContextMenuAtSelection(selection)) {
2231 DCHECK(!doc->NeedsLayoutTreeUpdate());
2233 // Enclose the selection rect fully between the handles. If the handles are
2234 // on the same line, the selection rect is empty.
2235 const SelectionInDOMTree& visible_selection =
2236 selection.ComputeVisibleSelectionInDOMTree().AsSelection();
2237 const PositionWithAffinity start_position(
2238 visible_selection.ComputeStartPosition(), visible_selection.Affinity());
2239 const gfx::Point start_point =
2240 GetMiddleSelectionCaretOfPosition(start_position);
2241 const PositionWithAffinity end_position(
2242 visible_selection.ComputeEndPosition(), visible_selection.Affinity());
2243 const gfx::Point end_point =
2244 GetMiddleSelectionCaretOfPosition(end_position);
2246 int left = std::min(start_point.x(), end_point.x());
2247 int top = std::min(start_point.y(), end_point.y());
2248 int right = std::max(start_point.x(), end_point.x());
2249 int bottom = std::max(start_point.y(), end_point.y());
2251 // If selection is a caret and is inside an anchor element, then set that
2252 // as the "focused" element so we can show "open link" option in context
2254 if (visible_selection.IsCaret()) {
2255 Element* anchor_element =
2256 EnclosingAnchorElement(visible_selection.ComputeStartPosition());
2258 focused_element = anchor_element;
2260 // Intersect the selection rect and the visible bounds of focused_element.
2261 if (focused_element) {
2262 gfx::Rect clipped_rect = view->ConvertFromRootFrame(
2263 GetFocusedElementRectForNonLocatedContextMenu(focused_element));
2264 left = std::max(clipped_rect.x(), left);
2265 top = std::max(clipped_rect.y(), top);
2266 right = std::min(clipped_rect.right(), right);
2267 bottom = std::min(clipped_rect.bottom(), bottom);
2269 gfx::Rect selection_rect = gfx::Rect(left, top, right - left, bottom - top);
2271 if (ContainsEvenAtEdge(selection_rect, start_point)) {
2272 location_in_root_frame = view->ConvertToRootFrame(start_point);
2273 } else if (ContainsEvenAtEdge(selection_rect, end_point)) {
2274 location_in_root_frame = view->ConvertToRootFrame(end_point);
2276 location_in_root_frame =
2277 view->ConvertToRootFrame(selection_rect.CenterPoint());
2279 } else if (focused_element) {
2280 gfx::Rect clipped_rect =
2281 GetFocusedElementRectForNonLocatedContextMenu(focused_element);
2282 location_in_root_frame = clipped_rect.CenterPoint();
2284 // TODO(crbug.com/1274078): Should this use ScrollPosition()?
2285 location_in_root_frame =
2286 gfx::Point(visual_viewport.GetScrollOffset().x() + kContextMenuMargin,
2287 visual_viewport.GetScrollOffset().y() + kContextMenuMargin);
2290 frame_->View()->SetCursor(PointerCursor());
2291 gfx::Point global_position =
2292 view->GetChromeClient()
2293 ->LocalRootToScreenDIPs(
2294 gfx::Rect(location_in_root_frame, gfx::Size()), frame_->View())
2297 // Use the focused node as the target for hover and active.
2298 HitTestRequest request(HitTestRequest::kActive);
2299 HitTestLocation location(location_in_root_frame);
2300 HitTestResult result(request, location);
2301 result.SetInnerNode(focused_element ? static_cast<Node*>(focused_element)
2303 doc->UpdateHoverActiveState(request.Active(), !request.Move(),
2304 result.InnerElement());
2306 // The contextmenu event is a mouse event even when invoked using the
2307 // keyboard. This is required for web compatibility.
2308 WebInputEvent::Type event_type = WebInputEvent::Type::kMouseDown;
2309 if (frame_->GetSettings() &&
2310 frame_->GetSettings()->GetShowContextMenuOnMouseUp())
2311 event_type = WebInputEvent::Type::kMouseUp;
2313 WebMouseEvent mouse_event(
2315 gfx::PointF(location_in_root_frame.x(), location_in_root_frame.y()),
2316 gfx::PointF(global_position.x(), global_position.y()),
2317 WebPointerProperties::Button::kNoButton, /* clickCount */ 0,
2318 WebInputEvent::kNoModifiers, base::TimeTicks::Now(), source_type);
2319 mouse_event.id = PointerEventFactory::kMouseId;
2321 // TODO(dtapuska): Transition the mouseEvent to be created really in viewport
2322 // coordinates instead of root frame coordinates.
2323 mouse_event.SetFrameScale(1);
2325 return SendContextMenuEvent(mouse_event, focused_element);
2328 gfx::Rect EventHandler::GetFocusedElementRectForNonLocatedContextMenu(
2329 Element* focused_element) {
2330 gfx::Rect visible_rect = focused_element->VisibleBoundsInLocalRoot();
2332 VisualViewport& visual_viewport = frame_->GetPage()->GetVisualViewport();
2334 // TODO(bokan): This method may not work as expected when the local root
2335 // isn't the main frame since the result won't be transformed and clipped by
2336 // the visual viewport (which is accessible only from the outermost main
2338 if (frame_->LocalFrameRoot().IsOutermostMainFrame()) {
2339 visible_rect = visual_viewport.RootFrameToViewport(visible_rect);
2340 visible_rect.Intersect(gfx::Rect(visual_viewport.Size()));
2343 gfx::Rect clipped_rect = visible_rect;
2344 // The bounding rect of multiline elements may include points that are
2345 // not within the element. Intersect the clipped rect with the first
2346 // outline rect to ensure that the selection rect only includes visible
2347 // points within the focused element.
2348 Vector<gfx::Rect> outline_rects = focused_element->OutlineRectsInWidget();
2349 if (outline_rects.size() > 1)
2350 clipped_rect.Intersect(outline_rects[0]);
2352 return visual_viewport.ViewportToRootFrame(clipped_rect);
2355 void EventHandler::ScheduleHoverStateUpdate() {
2356 // TODO(https://crbug.com/668758): Use a normal BeginFrame update for this.
2357 if (!hover_timer_.IsActive() &&
2358 !mouse_event_manager_->IsMousePositionUnknown())
2359 hover_timer_.StartOneShot(base::TimeDelta(), FROM_HERE);
2362 void EventHandler::ScheduleCursorUpdate() {
2363 // We only want one timer for the page, rather than each frame having it's own
2364 // timer competing which eachother (since there's only one mouse cursor).
2365 DCHECK_EQ(frame_, &frame_->LocalFrameRoot());
2367 // TODO(https://crbug.com/668758): Use a normal BeginFrame update for this.
2368 if (!cursor_update_timer_.IsActive())
2369 cursor_update_timer_.StartOneShot(kCursorUpdateInterval, FROM_HERE);
2372 bool EventHandler::CursorUpdatePending() {
2373 return cursor_update_timer_.IsActive();
2376 bool EventHandler::IsHandlingKeyEvent() const {
2377 return keyboard_event_manager_->is_handling_key_event();
2380 void EventHandler::SetResizingFrameSet(HTMLFrameSetElement* frame_set) {
2381 CaptureMouseEventsToWidget(true);
2382 frame_set_being_resized_ = frame_set;
2385 void EventHandler::ResizeScrollableAreaDestroyed() {
2386 scroll_manager_->ClearResizeScrollableArea(true);
2389 void EventHandler::HoverTimerFired(TimerBase*) {
2390 TRACE_EVENT0("input", "EventHandler::hoverTimerFired");
2393 DCHECK(frame_->GetDocument());
2395 if (auto* layout_object = frame_->ContentLayoutObject()) {
2396 if (LocalFrameView* view = frame_->View()) {
2397 HitTestRequest request(HitTestRequest::kMove);
2398 HitTestLocation location(view->ViewportToFrame(
2399 mouse_event_manager_->LastKnownMousePositionInViewport()));
2400 HitTestResult result(request, location);
2401 layout_object->HitTest(location, result);
2402 frame_->GetDocument()->UpdateHoverActiveState(
2403 request.Active(), !request.Move(), result.InnerElement());
2408 void EventHandler::ActiveIntervalTimerFired(TimerBase*) {
2409 TRACE_EVENT0("input", "EventHandler::activeIntervalTimerFired");
2411 if (frame_ && frame_->GetDocument() && last_deferred_tap_element_) {
2412 // FIXME: Enable condition when http://crbug.com/226842 lands
2413 // m_lastDeferredTapElement.get() == m_frame->document()->activeElement()
2414 HitTestRequest request(HitTestRequest::kTouchEvent |
2415 HitTestRequest::kRelease);
2416 frame_->GetDocument()->UpdateHoverActiveState(
2417 request.Active(), !request.Move(), last_deferred_tap_element_.Get());
2419 last_deferred_tap_element_ = nullptr;
2422 void EventHandler::NotifyElementActivated() {
2423 // Since another element has been set to active, stop current timer and clear
2425 active_interval_timer_.Stop();
2426 last_deferred_tap_element_ = nullptr;
2429 bool EventHandler::HandleAccessKey(const WebKeyboardEvent& evt) {
2430 return keyboard_event_manager_->HandleAccessKey(evt);
2433 WebInputEventResult EventHandler::KeyEvent(
2434 const WebKeyboardEvent& initial_key_event) {
2435 return keyboard_event_manager_->KeyEvent(initial_key_event);
2438 void EventHandler::DefaultKeyboardEventHandler(KeyboardEvent* event) {
2439 keyboard_event_manager_->DefaultKeyboardEventHandler(
2440 event, mouse_event_manager_->MousePressNode());
2443 void EventHandler::DragSourceEndedAt(
2444 const WebMouseEvent& event,
2445 ui::mojom::blink::DragOperation operation) {
2446 // Asides from routing the event to the correct frame, the hit test is also an
2447 // opportunity for Layer to update the :hover and :active pseudoclasses.
2448 HitTestRequest request(HitTestRequest::kRelease);
2449 MouseEventWithHitTestResults mev =
2450 event_handling_util::PerformMouseEventHitTest(frame_, request, event);
2452 if (auto* target_frame = LocalFrameFromTargetNode(mev.InnerNode())) {
2453 target_frame->GetEventHandler().DragSourceEndedAt(event, operation);
2457 mouse_event_manager_->DragSourceEndedAt(event, operation);
2459 if (frame_->GetSettings() &&
2460 frame_->GetSettings()->GetTouchDragDropEnabled() &&
2461 frame_->GetSettings()->GetTouchDragEndContextMenu()) {
2462 gesture_manager_->SendContextMenuEventTouchDragEnd(event);
2466 void EventHandler::UpdateDragStateAfterEditDragIfNeeded(
2467 Element* root_editable_element) {
2468 // If inserting the dragged contents removed the drag source, we still want to
2469 // fire dragend at the root editble element.
2470 if (mouse_event_manager_->GetDragState().drag_src_ &&
2471 !mouse_event_manager_->GetDragState().drag_src_->isConnected())
2472 mouse_event_manager_->GetDragState().drag_src_ = root_editable_element;
2475 bool EventHandler::HandleTextInputEvent(const String& text,
2476 Event* underlying_event,
2477 TextEventInputType input_type) {
2478 // Platforms should differentiate real commands like selectAll from text input
2479 // in disguise (like insertNewline), and avoid dispatching text input events
2480 // from keydown default handlers.
2481 auto* keyboard_event = DynamicTo<KeyboardEvent>(underlying_event);
2482 DCHECK(!keyboard_event ||
2483 keyboard_event->type() == event_type_names::kKeypress);
2488 EventTarget* target;
2489 if (underlying_event)
2490 target = underlying_event->target();
2492 target = EventTargetNodeForDocument(frame_->GetDocument());
2496 TextEvent* event = TextEvent::Create(frame_->DomWindow(), text, input_type);
2497 event->SetUnderlyingEvent(underlying_event);
2499 target->DispatchEvent(*event);
2500 return event->DefaultHandled() || event->defaultPrevented();
2503 void EventHandler::DefaultTextInputEventHandler(TextEvent* event) {
2504 if (frame_->GetEditor().HandleTextEvent(event))
2505 event->SetDefaultHandled();
2508 void EventHandler::CapsLockStateMayHaveChanged() {
2509 keyboard_event_manager_->CapsLockStateMayHaveChanged();
2512 bool EventHandler::PassMousePressEventToScrollbar(
2513 MouseEventWithHitTestResults& mev) {
2514 // Do not pass the mouse press to scrollbar if scrollbar pressed. If the
2515 // user's left button is down, then the cursor moves outside the scrollbar
2516 // and presses the middle button , we should not clear
2517 // last_scrollbar_under_mouse_.
2518 if (last_scrollbar_under_mouse_ &&
2519 last_scrollbar_under_mouse_->PressedPart() != ScrollbarPart::kNoPart) {
2523 Scrollbar* scrollbar = mev.GetScrollbar();
2524 UpdateLastScrollbarUnderMouse(scrollbar, true);
2526 if (!scrollbar || !scrollbar->Enabled())
2528 scrollbar->MouseDown(mev.Event());
2529 if (scrollbar->PressedPart() == ScrollbarPart::kThumbPart)
2530 CaptureMouseEventsToWidget(true);
2534 // If scrollbar (under mouse) is different from last, send a mouse exited. Set
2535 // last to scrollbar if setLast is true; else set last to 0.
2536 void EventHandler::UpdateLastScrollbarUnderMouse(Scrollbar* scrollbar,
2538 if (last_scrollbar_under_mouse_ != scrollbar) {
2539 // Send mouse exited to the old scrollbar.
2540 if (last_scrollbar_under_mouse_)
2541 last_scrollbar_under_mouse_->MouseExited();
2543 // Send mouse entered if we're setting a new scrollbar.
2544 if (scrollbar && set_last)
2545 scrollbar->MouseEntered();
2547 last_scrollbar_under_mouse_ = set_last ? scrollbar : nullptr;
2551 WebInputEventResult EventHandler::PassMousePressEventToSubframe(
2552 MouseEventWithHitTestResults& mev,
2553 LocalFrame* subframe) {
2554 GetSelectionController().PassMousePressEventToSubframe(mev);
2555 WebInputEventResult result =
2556 subframe->GetEventHandler().HandleMousePressEvent(mev.Event());
2557 if (result != WebInputEventResult::kNotHandled)
2559 return WebInputEventResult::kHandledSystem;
2562 WebInputEventResult EventHandler::PassMouseMoveEventToSubframe(
2563 MouseEventWithHitTestResults& mev,
2564 const Vector<WebMouseEvent>& coalesced_events,
2565 const Vector<WebMouseEvent>& predicted_events,
2566 LocalFrame* subframe,
2567 HitTestResult* hovered_node,
2568 HitTestLocation* hit_test_location) {
2569 if (mouse_event_manager_->MouseDownMayStartDrag())
2570 return WebInputEventResult::kNotHandled;
2571 WebInputEventResult result =
2572 subframe->GetEventHandler().HandleMouseMoveOrLeaveEvent(
2573 mev.Event(), coalesced_events, predicted_events, hovered_node,
2575 if (result != WebInputEventResult::kNotHandled)
2577 return WebInputEventResult::kHandledSystem;
2580 WebInputEventResult EventHandler::PassMouseReleaseEventToSubframe(
2581 MouseEventWithHitTestResults& mev,
2582 LocalFrame* subframe) {
2583 return subframe->GetEventHandler().HandleMouseReleaseEvent(mev.Event());
2586 void EventHandler::CaptureMouseEventsToWidget(bool capture) {
2587 if (!frame_->IsLocalRoot()) {
2588 frame_->LocalFrameRoot().GetEventHandler().CaptureMouseEventsToWidget(
2593 if (capture == is_widget_capturing_mouse_events_)
2596 frame_->LocalFrameRoot().Client()->SetMouseCapture(capture);
2597 is_widget_capturing_mouse_events_ = capture;
2600 MouseEventWithHitTestResults EventHandler::GetMouseEventTarget(
2601 const HitTestRequest& request,
2602 const WebMouseEvent& event) {
2603 PhysicalOffset document_point =
2604 event_handling_util::ContentPointFromRootFrame(
2605 frame_, event.PositionInRootFrame());
2607 // TODO(eirage): This does not handle chorded buttons yet.
2608 if (event.GetType() != WebInputEvent::Type::kMouseDown) {
2609 HitTestResult result(request, HitTestLocation(document_point));
2611 Element* capture_target;
2612 if (event_handling_util::SubframeForTargetNode(
2613 capturing_subframe_element_)) {
2614 capture_target = capturing_subframe_element_;
2615 result.SetIsOverEmbeddedContentView(true);
2617 capture_target = pointer_event_manager_->GetMouseCaptureTarget();
2620 if (capture_target) {
2621 LayoutObject* layout_object = capture_target->GetLayoutObject();
2622 PhysicalOffset local_point =
2623 layout_object ? layout_object->AbsoluteToLocalPoint(document_point)
2625 result.SetNodeAndPosition(capture_target, local_point);
2627 result.SetScrollbar(last_scrollbar_under_mouse_);
2628 result.SetURLElement(capture_target->EnclosingLinkEventParentOrSelf());
2630 if (!request.ReadOnly()) {
2631 frame_->GetDocument()->UpdateHoverActiveState(
2632 request.Active(), !request.Move(), result.InnerElement());
2635 return MouseEventWithHitTestResults(
2636 event, HitTestLocation(document_point), result);
2639 return frame_->GetDocument()->PerformMouseEventHitTest(request,
2640 document_point, event);
2643 void EventHandler::ReleaseMouseCaptureFromLocalRoot() {
2644 CaptureMouseEventsToWidget(false);
2646 frame_->LocalFrameRoot()
2648 .ReleaseMouseCaptureFromCurrentFrame();
2651 void EventHandler::ReleaseMouseCaptureFromCurrentFrame() {
2652 if (LocalFrame* subframe = event_handling_util::SubframeForTargetNode(
2653 capturing_subframe_element_))
2654 subframe->GetEventHandler().ReleaseMouseCaptureFromCurrentFrame();
2655 pointer_event_manager_->ReleaseMousePointerCapture();
2656 capturing_subframe_element_ = nullptr;
2659 #if BUILDFLAG(IS_TIZEN_TV)
2660 bool EventHandler::CanRunArrowScrolling(const HitTestResult& result) {
2661 if (result.IsContentEditable() || result.GetScrollbar() ||
2662 result.MediaHasAudio() || result.MediaIsVideo())
2665 if (Node* node = result.InnerNode()) {
2666 if (node->HasMediaControlAncestor() || node->IsMediaControls() ||
2667 node->IsMediaControlElement() || node->WillRespondToMouseClickEvents())
2675 #if BUILDFLAG(IS_EFL)
2676 void EventHandler::ContextMenuEventForWordOrLinkSelection(
2677 const WebGestureEvent& gesture_event) {
2678 LocalFrameView* frame_view = frame_->View();
2682 // Hit-test the provided gesture event, applying touch-adjustment and
2683 // adjusting the location to the most likely nearby node.
2684 WebMouseEvent web_mouse_event(WebInputEvent::Type::kMouseMove, gesture_event,
2685 WebPointerProperties::Button::kNoButton, 0,
2686 WebInputEvent::kNoModifiers,
2687 gesture_event.TimeStamp(), 0);
2689 gfx::Point viewport_position = frame_view->ViewportToFrame(
2690 gfx::ToFlooredPoint(web_mouse_event.PositionInRootFrame()));
2691 HitTestRequest request(HitTestRequest::kActive);
2692 MouseEventWithHitTestResults mouse_event =
2693 frame_->GetDocument()->PerformMouseEventHitTest(
2694 request, PhysicalOffset(viewport_position), web_mouse_event);
2696 if (!frame_->Selection().Contains(PhysicalOffset(viewport_position)) &&
2697 !mouse_event.GetScrollbar() &&
2698 (frame_->Selection()
2699 .ComputeVisibleSelectionInDOMTreeDeprecated()
2700 .IsContentEditable() ||
2701 (mouse_event.InnerNode() && mouse_event.InnerNode()->IsTextNode()) ||
2702 (mouse_event.GetHitTestResult().IsLiveLink()))) {
2703 GetSelectionController().SetMouseDownMayStartSelect(
2704 true); // context menu events are always allowed to perform a selection
2705 GetSelectionController().SelectClosestWordOrLinkFromMouseEvent(
2706 (MouseEvent*)(&mouse_event.Event()), mouse_event.GetHitTestResult());
2711 } // namespace blink