2 * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
3 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
4 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "EventHandler.h"
31 #include "AutoscrollController.h"
32 #include "BackForwardController.h"
33 #include "CachedImage.h"
35 #include "ChromeClient.h"
36 #include "CursorList.h"
37 #include "DocumentMarkerController.h"
38 #include "DragController.h"
39 #include "DragState.h"
42 #include "EditorClient.h"
43 #include "EventNames.h"
45 #include "FloatPoint.h"
46 #include "FloatRect.h"
47 #include "FocusController.h"
49 #include "FrameLoader.h"
50 #include "FrameSelection.h"
51 #include "FrameTree.h"
52 #include "FrameView.h"
53 #include "HTMLFrameElement.h"
54 #include "HTMLFrameSetElement.h"
55 #include "HTMLHtmlElement.h"
56 #include "HTMLIFrameElement.h"
57 #include "HTMLInputElement.h"
58 #include "HTMLNames.h"
59 #include "HitTestRequest.h"
60 #include "HitTestResult.h"
62 #include "InspectorInstrumentation.h"
63 #include "KeyboardEvent.h"
65 #include "MouseEvent.h"
66 #include "MouseEventWithHitTestResults.h"
68 #include "PageOverlayController.h"
69 #include "Pasteboard.h"
70 #include "PlatformEvent.h"
71 #include "PlatformKeyboardEvent.h"
72 #include "PlatformWheelEvent.h"
73 #include "PluginDocument.h"
75 #include "RenderFrameSet.h"
76 #include "RenderLayer.h"
77 #include "RenderListBox.h"
78 #include "RenderTextControlSingleLine.h"
79 #include "RenderView.h"
80 #include "RenderWidget.h"
81 #include "ResourceLoadObserver.h"
82 #include "RuntimeApplicationChecks.h"
83 #include "SVGDocument.h"
85 #include "ScrollLatchingState.h"
86 #include "Scrollbar.h"
88 #include "ShadowRoot.h"
89 #include "SpatialNavigation.h"
90 #include "StaticPasteboard.h"
91 #include "StyleCachedImage.h"
92 #include "TextEvent.h"
93 #include "TextIterator.h"
94 #include "UserGestureIndicator.h"
95 #include "UserTypingGestureIndicator.h"
96 #include "ValidationMessageClient.h"
97 #include "VisibleUnits.h"
98 #include "WheelEvent.h"
99 #include "WheelEventDeltaFilter.h"
100 #include "WindowsKeyboardCodes.h"
101 #include <wtf/Assertions.h>
102 #include <wtf/NeverDestroyed.h>
103 #include <wtf/StdLibExtras.h>
105 #if ENABLE(IOS_TOUCH_EVENTS)
106 #include "PlatformTouchEventIOS.h"
109 #if ENABLE(TOUCH_EVENTS)
110 #include "TouchEvent.h"
111 #include "TouchList.h"
114 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
115 #include "PlatformTouchEvent.h"
118 #if ENABLE(MAC_GESTURE_EVENTS)
119 #include "PlatformGestureEventMac.h"
122 #if ENABLE(POINTER_LOCK)
123 #include "PointerLockController.h"
128 using namespace HTMLNames;
130 #if ENABLE(DRAG_SUPPORT)
131 // The link drag hysteresis is much larger than the others because there
132 // needs to be enough space to cancel the link press without starting a link drag,
133 // and because dragging links is rare.
134 const int LinkDragHysteresis = 40;
135 const int ImageDragHysteresis = 5;
136 const int TextDragHysteresis = 3;
137 const int GeneralDragHysteresis = 3;
139 const Seconds EventHandler::TextDragDelay { 150_ms };
141 #endif // ENABLE(DRAG_SUPPORT)
143 #if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS)
144 const float GestureUnknown = 0;
147 #if ENABLE(IOS_TOUCH_EVENTS)
148 // FIXME: Share this constant with EventHandler and SliderThumbElement.
149 const unsigned InvalidTouchIdentifier = 0;
152 // Match key code of composition keydown event on windows.
153 // IE sends VK_PROCESSKEY which has value 229;
154 const int CompositionEventKeyCode = 229;
156 using namespace SVGNames;
158 #if !ENABLE(IOS_TOUCH_EVENTS)
159 // The amount of time to wait before sending a fake mouse event, triggered
160 // during a scroll. The short interval is used if the content responds to the mouse events
161 // in fakeMouseMoveDurationThreshold or less, otherwise the long interval is used.
162 const double fakeMouseMoveDurationThreshold = 0.01;
163 const Seconds fakeMouseMoveShortInterval = { 100_ms };
164 const Seconds fakeMouseMoveLongInterval = { 250_ms };
167 #if ENABLE(CURSOR_SUPPORT)
168 // The amount of time to wait for a cursor update on style and layout changes
169 // Set to 50Hz, no need to be faster than common screen refresh rate
170 static const Seconds cursorUpdateInterval { 20_ms };
172 const int maximumCursorSize = 128;
175 #if ENABLE(MOUSE_CURSOR_SCALE)
176 // It's pretty unlikely that a scale of less than one would ever be used. But all we really
177 // need to ensure here is that the scale isn't so small that integer overflow can occur when
178 // dividing cursor sizes (limited above) by the scale.
179 const double minimumCursorScale = 0.001;
182 class MaximumDurationTracker {
184 explicit MaximumDurationTracker(double *maxDuration)
185 : m_maxDuration(maxDuration)
186 , m_start(MonotonicTime::now())
190 ~MaximumDurationTracker()
192 *m_maxDuration = std::max(*m_maxDuration, (MonotonicTime::now() - m_start).seconds());
196 double* m_maxDuration;
197 MonotonicTime m_start;
200 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
201 class SyntheticTouchPoint : public PlatformTouchPoint {
204 // The default values are based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
205 explicit SyntheticTouchPoint(const PlatformMouseEvent& event)
207 const static int idDefaultValue = 0;
208 const static int radiusYDefaultValue = 1;
209 const static int radiusXDefaultValue = 1;
210 const static float rotationAngleDefaultValue = 0.0f;
211 const static float forceDefaultValue = 1.0f;
213 m_id = idDefaultValue; // There is only one active TouchPoint.
214 m_screenPos = event.globalPosition();
215 m_pos = event.position();
216 m_radiusY = radiusYDefaultValue;
217 m_radiusX = radiusXDefaultValue;
218 m_rotationAngle = rotationAngleDefaultValue;
219 m_force = forceDefaultValue;
221 PlatformEvent::Type type = event.type();
222 ASSERT(type == PlatformEvent::MouseMoved || type == PlatformEvent::MousePressed || type == PlatformEvent::MouseReleased);
225 case PlatformEvent::MouseMoved:
226 m_state = TouchMoved;
228 case PlatformEvent::MousePressed:
229 m_state = TouchPressed;
231 case PlatformEvent::MouseReleased:
232 m_state = TouchReleased;
235 ASSERT_NOT_REACHED();
241 class SyntheticSingleTouchEvent : public PlatformTouchEvent {
243 explicit SyntheticSingleTouchEvent(const PlatformMouseEvent& event)
245 switch (event.type()) {
246 case PlatformEvent::MouseMoved:
249 case PlatformEvent::MousePressed:
252 case PlatformEvent::MouseReleased:
256 ASSERT_NOT_REACHED();
260 m_timestamp = event.timestamp();
261 m_modifiers = event.modifiers();
262 m_touchPoints.append(SyntheticTouchPoint(event));
265 #endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
267 static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned deltaMode)
270 case WheelEvent::DOM_DELTA_PAGE:
272 case WheelEvent::DOM_DELTA_LINE:
274 case WheelEvent::DOM_DELTA_PIXEL:
275 return ScrollByPixel;
277 return ScrollByPixel;
281 static inline bool didScrollInScrollableArea(ScrollableArea* scrollableArea, WheelEvent& wheelEvent)
283 ScrollGranularity scrollGranularity = wheelGranularityToScrollGranularity(wheelEvent.deltaMode());
284 bool didHandleWheelEvent = false;
285 if (float absoluteDelta = std::abs(wheelEvent.deltaX()))
286 didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaX() > 0 ? ScrollRight : ScrollLeft, scrollGranularity, absoluteDelta);
288 if (float absoluteDelta = std::abs(wheelEvent.deltaY()))
289 didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaY() > 0 ? ScrollDown : ScrollUp, scrollGranularity, absoluteDelta);
291 return didHandleWheelEvent;
294 static inline bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, WheelEvent& wheelEvent, Element** stopElement, const FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity)
296 bool shouldHandleEvent = wheelEvent.deltaX() || wheelEvent.deltaY();
298 shouldHandleEvent |= wheelEvent.phase() == PlatformWheelEventPhaseEnded;
299 #if ENABLE(CSS_SCROLL_SNAP)
300 shouldHandleEvent |= wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded;
303 if (!startNode->renderer() || !shouldHandleEvent)
306 RenderBox& initialEnclosingBox = startNode->renderer()->enclosingBox();
307 if (initialEnclosingBox.isListBox())
308 return didScrollInScrollableArea(static_cast<RenderListBox*>(&initialEnclosingBox), wheelEvent);
310 RenderBox* currentEnclosingBox = &initialEnclosingBox;
311 while (currentEnclosingBox) {
312 if (RenderLayer* boxLayer = currentEnclosingBox->layer()) {
313 auto platformEvent = wheelEvent.underlyingPlatformEvent();
314 bool scrollingWasHandled;
316 auto copiedEvent = platformEvent->copyWithDeltasAndVelocity(filteredPlatformDelta.width(), filteredPlatformDelta.height(), filteredVelocity);
317 scrollingWasHandled = boxLayer->handleWheelEvent(copiedEvent);
319 scrollingWasHandled = didScrollInScrollableArea(boxLayer, wheelEvent);
321 if (scrollingWasHandled) {
323 *stopElement = currentEnclosingBox->element();
328 if (stopElement && *stopElement && *stopElement == currentEnclosingBox->element())
331 currentEnclosingBox = currentEnclosingBox->containingBlock();
332 if (!currentEnclosingBox || currentEnclosingBox->isRenderView())
338 #if (ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS))
339 static inline bool shouldGesturesTriggerActive()
341 // If the platform we're on supports GestureTapDown and GestureTapCancel then we'll
342 // rely on them to set the active state. Unfortunately there's no generic way to
343 // know in advance what event types are supported.
350 inline bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&)
355 #if ENABLE(DRAG_SUPPORT)
356 inline bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&)
364 EventHandler::EventHandler(Frame& frame)
366 , m_hoverTimer(*this, &EventHandler::hoverTimerFired)
367 #if ENABLE(CURSOR_SUPPORT)
368 , m_cursorUpdateTimer(*this, &EventHandler::cursorUpdateTimerFired)
371 , m_pendingMomentumWheelEventsTimer(*this, &EventHandler::clearLatchedState)
373 , m_autoscrollController(std::make_unique<AutoscrollController>())
374 #if !ENABLE(IOS_TOUCH_EVENTS)
375 , m_fakeMouseMoveEventTimer(*this, &EventHandler::fakeMouseMoveEventTimerFired)
377 #if ENABLE(CURSOR_VISIBILITY)
378 , m_autoHideCursorTimer(*this, &EventHandler::autoHideCursorTimerFired)
383 EventHandler::~EventHandler()
385 #if !ENABLE(IOS_TOUCH_EVENTS)
386 ASSERT(!m_fakeMouseMoveEventTimer.isActive());
388 #if ENABLE(CURSOR_VISIBILITY)
389 ASSERT(!m_autoHideCursorTimer.isActive());
393 #if ENABLE(DRAG_SUPPORT)
395 DragState& EventHandler::dragState()
397 static NeverDestroyed<DragState> state;
403 void EventHandler::clear()
406 #if ENABLE(CURSOR_SUPPORT)
407 m_cursorUpdateTimer.stop();
409 #if !ENABLE(IOS_TOUCH_EVENTS)
410 m_fakeMouseMoveEventTimer.stop();
412 #if ENABLE(CURSOR_VISIBILITY)
413 cancelAutoHideCursorTimer();
415 m_resizeLayer = nullptr;
416 m_elementUnderMouse = nullptr;
417 m_lastElementUnderMouse = nullptr;
418 m_lastMouseMoveEventSubframe = nullptr;
419 m_lastScrollbarUnderMouse = nullptr;
421 m_clickNode = nullptr;
422 #if ENABLE(IOS_GESTURE_EVENTS)
423 m_gestureInitialDiameter = GestureUnknown;
424 m_gestureInitialRotation = GestureUnknown;
426 #if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS)
427 m_gestureLastDiameter = GestureUnknown;
428 m_gestureLastRotation = GestureUnknown;
429 m_gestureTargets.clear();
431 #if ENABLE(IOS_TOUCH_EVENTS)
433 m_firstTouchID = InvalidTouchIdentifier;
434 m_touchEventTargetSubframe = nullptr;
436 m_frameSetBeingResized = nullptr;
437 #if ENABLE(DRAG_SUPPORT)
438 m_dragTarget = nullptr;
439 m_shouldOnlyFireDragOverEvent = false;
441 m_mousePositionIsUnknown = true;
442 m_lastKnownMousePosition = IntPoint();
443 m_lastKnownMouseGlobalPosition = IntPoint();
444 m_mousePressNode = nullptr;
445 m_mousePressed = false;
446 m_capturesDragging = false;
447 m_capturingMouseEventsElement = nullptr;
449 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
450 m_originatingTouchPointTargets.clear();
451 m_originatingTouchPointDocument = nullptr;
452 m_originatingTouchPointTargetKey = 0;
454 m_maxMouseMovedDuration = 0;
455 m_didStartDrag = false;
458 void EventHandler::nodeWillBeRemoved(Node& nodeToBeRemoved)
460 if (nodeToBeRemoved.contains(m_clickNode.get()))
461 m_clickNode = nullptr;
464 static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection)
466 if (selection.selection() != newSelection && selection.shouldChangeSelection(newSelection))
467 selection.setSelection(newSelection);
470 static inline bool dispatchSelectStart(Node* node)
472 if (!node || !node->renderer())
475 auto event = Event::create(eventNames().selectstartEvent, true, true);
476 node->dispatchEvent(event);
477 return !event->defaultPrevented();
480 static Node* nodeToSelectOnMouseDownForNode(Node& targetNode)
482 #if ENABLE(USERSELECT_ALL)
483 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(&targetNode))
484 return rootUserSelectAll;
487 if (targetNode.shouldSelectOnMouseDown())
493 static VisibleSelection expandSelectionToRespectSelectOnMouseDown(Node& targetNode, const VisibleSelection& selection)
495 Node* nodeToSelect = nodeToSelectOnMouseDownForNode(targetNode);
499 VisibleSelection newSelection(selection);
500 newSelection.setBase(positionBeforeNode(nodeToSelect).upstream(CanCrossEditingBoundary));
501 newSelection.setExtent(positionAfterNode(nodeToSelect).downstream(CanCrossEditingBoundary));
506 bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity)
508 if (Position::nodeIsUserSelectNone(targetNode))
511 if (!dispatchSelectStart(targetNode))
514 if (selection.isRange())
515 m_selectionInitiationState = ExtendedSelection;
517 granularity = CharacterGranularity;
518 m_selectionInitiationState = PlacedCaret;
521 m_frame.selection().setSelectionByMouseIfDifferent(selection, granularity);
526 void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
528 Node* targetNode = result.targetNode();
529 VisibleSelection newSelection;
531 if (targetNode && targetNode->renderer()) {
532 VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr));
533 if (pos.isNotNull()) {
534 newSelection = VisibleSelection(pos);
535 newSelection.expandUsingGranularity(WordGranularity);
538 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
539 newSelection.appendTrailingWhitespace();
541 updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity);
545 static AppendTrailingWhitespace shouldAppendTrailingWhitespace(const MouseEventWithHitTestResults& result, const Frame& frame)
547 return (result.event().clickCount() == 2 && frame.editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace;
550 void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result)
552 if (m_mouseDownMayStartSelect)
553 selectClosestWordFromHitTestResult(result.hitTestResult(), shouldAppendTrailingWhitespace(result, m_frame));
557 VisibleSelection EventHandler::selectClosestWordFromHitTestResultBasedOnLookup(const HitTestResult&)
559 return VisibleSelection();
563 void EventHandler::selectClosestContextualWordFromMouseEvent(const MouseEventWithHitTestResults& mouseEvent)
565 Node* targetNode = mouseEvent.targetNode();
566 const HitTestResult& result = mouseEvent.hitTestResult();
567 VisibleSelection newSelection;
568 bool appendTrailingWhitespace = shouldAppendTrailingWhitespace(mouseEvent, m_frame);
570 if (targetNode && targetNode->renderer()) {
571 newSelection = selectClosestWordFromHitTestResultBasedOnLookup(result);
572 if (newSelection.isNone()) {
573 VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr));
574 if (pos.isNotNull()) {
575 newSelection = VisibleSelection(pos);
576 newSelection.expandUsingGranularity(WordGranularity);
580 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
581 newSelection.appendTrailingWhitespace();
583 updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity);
587 void EventHandler::selectClosestContextualWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result)
589 Element* urlElement = result.hitTestResult().URLElement();
590 if (!urlElement || !isDraggableLink(*urlElement)) {
591 if (Node* targetNode = result.targetNode()) {
592 if (isEditableNode(*targetNode))
593 return selectClosestWordFromMouseEvent(result);
596 return selectClosestContextualWordFromMouseEvent(result);
599 Node* targetNode = result.targetNode();
601 if (targetNode && targetNode->renderer() && m_mouseDownMayStartSelect) {
602 VisibleSelection newSelection;
603 VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr));
604 if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(*urlElement))
605 newSelection = VisibleSelection::selectionFromContentsOfNode(urlElement);
607 updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity);
611 bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestResults& event)
613 if (event.event().button() != LeftButton)
616 if (m_frame.selection().isRange())
617 // A double-click when range is already selected
618 // should not change the selection. So, do not call
619 // selectClosestWordFromMouseEvent, but do set
620 // m_beganSelectingText to prevent handleMouseReleaseEvent
621 // from setting caret selection.
622 m_selectionInitiationState = ExtendedSelection;
624 selectClosestWordFromMouseEvent(event);
629 bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestResults& event)
631 if (event.event().button() != LeftButton)
634 Node* targetNode = event.targetNode();
635 if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect))
638 VisibleSelection newSelection;
639 VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr));
640 if (pos.isNotNull()) {
641 newSelection = VisibleSelection(pos);
642 newSelection.expandUsingGranularity(ParagraphGranularity);
645 return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), ParagraphGranularity);
648 static int textDistance(const Position& start, const Position& end)
650 RefPtr<Range> range = Range::create(start.anchorNode()->document(), start, end);
651 return TextIterator::rangeLength(range.get(), true);
654 bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event)
656 Ref<Frame> protectedFrame(m_frame);
658 m_frame.document()->updateLayoutIgnorePendingStylesheets();
659 Node* targetNode = event.targetNode();
660 if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect))
663 // Extend the selection if the Shift key is down, unless the click is in a link.
664 bool extendSelection = event.event().shiftKey() && !event.isOverLink();
666 // Don't restart the selection when the mouse is pressed on an
667 // existing selection so we can allow for text dragging.
668 if (FrameView* view = m_frame.view()) {
669 LayoutPoint vPoint = view->windowToContents(event.event().position());
670 if (!extendSelection && m_frame.selection().contains(vPoint)) {
671 m_mouseDownWasSingleClickInSelection = true;
676 VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr));
677 if (visiblePos.isNull())
678 visiblePos = VisiblePosition(firstPositionInOrBeforeNode(targetNode), DOWNSTREAM);
679 Position pos = visiblePos.deepEquivalent();
681 VisibleSelection newSelection = m_frame.selection().selection();
682 TextGranularity granularity = CharacterGranularity;
685 // The text selection assistant will handle selection in the case where we are already editing the node
686 if (newSelection.rootEditableElement() == targetNode->rootEditableElement())
690 if (extendSelection && newSelection.isCaretOrRange()) {
691 VisibleSelection selectionInUserSelectAll = expandSelectionToRespectSelectOnMouseDown(*targetNode, VisibleSelection(pos));
692 if (selectionInUserSelectAll.isRange()) {
693 if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0)
694 pos = selectionInUserSelectAll.start();
695 else if (comparePositions(newSelection.end(), selectionInUserSelectAll.end()) < 0)
696 pos = selectionInUserSelectAll.end();
699 if (!m_frame.editor().behavior().shouldConsiderSelectionAsDirectional() && pos.isNotNull()) {
700 // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection
701 // was created right-to-left
702 Position start = newSelection.start();
703 Position end = newSelection.end();
704 int distanceToStart = textDistance(start, pos);
705 int distanceToEnd = textDistance(pos, end);
706 if (distanceToStart <= distanceToEnd)
707 newSelection = VisibleSelection(end, pos);
709 newSelection = VisibleSelection(start, pos);
711 newSelection.setExtent(pos);
713 if (m_frame.selection().granularity() != CharacterGranularity) {
714 granularity = m_frame.selection().granularity();
715 newSelection.expandUsingGranularity(m_frame.selection().granularity());
718 newSelection = expandSelectionToRespectSelectOnMouseDown(*targetNode, visiblePos);
720 bool handled = updateSelectionForMouseDownDispatchingSelectStart(targetNode, newSelection, granularity);
722 if (event.event().button() == MiddleButton) {
723 // Ignore handled, since we want to paste to where the caret was placed anyway.
724 handled = handlePasteGlobalSelection(event.event()) || handled;
729 static inline bool canMouseDownStartSelect(Node* node)
731 if (!node || !node->renderer())
734 return node->canStartSelection() || Position::nodeIsUserSelectAll(node);
737 bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event)
739 Ref<Frame> protectedFrame(m_frame);
741 #if ENABLE(DRAG_SUPPORT)
743 dragState().source = nullptr;
746 #if !ENABLE(IOS_TOUCH_EVENTS)
747 cancelFakeMouseMoveEvent();
750 m_frame.document()->updateLayoutIgnorePendingStylesheets();
752 if (ScrollView* scrollView = m_frame.view()) {
753 if (scrollView->isPointInScrollbarCorner(event.event().position()))
757 bool singleClick = event.event().clickCount() <= 1;
759 // If we got the event back, that must mean it wasn't prevented,
760 // so it's allowed to start a drag or selection if it wasn't in a scrollbar.
761 m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode()) && !event.scrollbar();
763 #if ENABLE(DRAG_SUPPORT)
764 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
765 // FIXME: eventMayStartDrag() does not check for shift key press, link or image event targets.
766 // Bug: https://bugs.webkit.org/show_bug.cgi?id=155390
768 // Single mouse down on links or images can always trigger drag-n-drop.
769 bool isMouseDownOnLinkOrImage = event.isOverLink() || event.hitTestResult().image();
770 m_mouseDownMayStartDrag = singleClick && (!event.event().shiftKey() || isMouseDownOnLinkOrImage);
773 m_mouseDownWasSingleClickInSelection = false;
775 m_mouseDown = event.event();
777 if (m_immediateActionStage != ImmediateActionStage::PerformedHitTest)
778 m_immediateActionStage = ImmediateActionStage::None;
780 if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event))
783 if (is<SVGDocument>(*m_frame.document()) && downcast<SVGDocument>(*m_frame.document()).zoomAndPanEnabled()) {
784 if (event.event().shiftKey() && singleClick) {
786 downcast<SVGDocument>(*m_frame.document()).startPan(m_frame.view()->windowToContents(event.event().position()));
791 // We don't do this at the start of mouse down handling,
792 // because we don't want to do it until we know we didn't hit a widget.
796 m_mousePressNode = event.targetNode();
797 m_frame.document()->setFocusNavigationStartingNode(event.targetNode());
799 #if ENABLE(DRAG_SUPPORT)
800 m_dragStartPosition = event.event().position();
803 m_mousePressed = true;
804 m_selectionInitiationState = HaveNotStartedSelection;
806 bool swallowEvent = false;
807 if (event.event().clickCount() == 2)
808 swallowEvent = handleMousePressEventDoubleClick(event);
809 else if (event.event().clickCount() >= 3)
810 swallowEvent = handleMousePressEventTripleClick(event);
812 swallowEvent = handleMousePressEventSingleClick(event);
814 m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect
815 || (m_mousePressNode && m_mousePressNode->renderBox() && m_mousePressNode->renderBox()->canBeProgramaticallyScrolled());
820 VisiblePosition EventHandler::selectionExtentRespectingEditingBoundary(const VisibleSelection& selection, const LayoutPoint& localPoint, Node* targetNode)
822 FloatPoint selectionEndPoint = localPoint;
823 Element* editableElement = selection.rootEditableElement();
825 if (!targetNode || !targetNode->renderer())
826 return VisiblePosition();
828 if (editableElement && !editableElement->contains(targetNode)) {
829 if (!editableElement->renderer())
830 return VisiblePosition();
832 FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint));
833 selectionEndPoint = editableElement->renderer()->absoluteToLocal(absolutePoint);
834 targetNode = editableElement;
837 return targetNode->renderer()->positionForPoint(LayoutPoint(selectionEndPoint), nullptr);
840 #if ENABLE(DRAG_SUPPORT)
841 bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis)
846 Ref<Frame> protectedFrame(m_frame);
848 if (handleDrag(event, checkDragHysteresis))
851 Node* targetNode = event.targetNode();
852 if (event.event().button() != LeftButton || !targetNode)
855 RenderObject* renderer = targetNode->renderer();
857 Element* parent = targetNode->parentOrShadowHostElement();
861 renderer = parent->renderer();
862 if (!renderer || !renderer->isListBox())
866 #if PLATFORM(COCOA) // FIXME: Why does this assertion fire on other platforms?
867 ASSERT(m_mouseDownMayStartSelect || m_mouseDownMayStartAutoscroll);
870 m_mouseDownMayStartDrag = false;
872 if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) {
873 m_autoscrollController->startAutoscrollForSelection(renderer);
874 m_mouseDownMayStartAutoscroll = false;
877 if (m_selectionInitiationState != ExtendedSelection) {
878 HitTestResult result(m_mouseDownPos);
879 m_frame.document()->renderView()->hitTest(HitTestRequest(), result);
881 updateSelectionForMouseDrag(result);
883 updateSelectionForMouseDrag(event.hitTestResult());
887 bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const
889 // This is a pre-flight check of whether the event might lead to a drag being started. Be careful
890 // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag
891 // in handleMousePressEvent
892 RenderView* renderView = m_frame.contentRenderer();
896 if (event.button() != LeftButton || event.clickCount() != 1)
899 FrameView* view = m_frame.view();
903 Page* page = m_frame.page();
907 Ref<Frame> protectedFrame(m_frame);
909 updateDragSourceActionsAllowed();
910 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
911 HitTestResult result(view->windowToContents(event.position()));
912 renderView->hitTest(request, result);
914 Element* targetElement = result.targetElement();
915 return targetElement && page->dragController().draggableElement(&m_frame, targetElement, result.roundedPointInInnerNodeFrame(), state);
918 void EventHandler::updateSelectionForMouseDrag()
920 FrameView* view = m_frame.view();
923 RenderView* renderView = m_frame.contentRenderer();
927 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent);
928 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
929 renderView->hitTest(request, result);
930 updateSelectionForMouseDrag(result);
933 void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult)
935 if (!m_mouseDownMayStartSelect)
938 Node* target = hitTestResult.targetNode();
942 VisiblePosition targetPosition = selectionExtentRespectingEditingBoundary(m_frame.selection().selection(), hitTestResult.localPoint(), target);
944 // Don't modify the selection if we're not on a node.
945 if (targetPosition.isNull())
948 // Restart the selection if this is the first mouse move. This work is usually
949 // done in handleMousePressEvent, but not if the mouse press was on an existing selection.
950 VisibleSelection newSelection = m_frame.selection().selection();
952 // Special case to limit selection to the containing block for SVG text.
953 // FIXME: Isn't there a better non-SVG-specific way to do this?
954 if (Node* selectionBaseNode = newSelection.base().deprecatedNode())
955 if (RenderObject* selectionBaseRenderer = selectionBaseNode->renderer())
956 if (selectionBaseRenderer->isSVGText())
957 if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock())
960 if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelectStart(target))
963 if (m_selectionInitiationState != ExtendedSelection) {
964 // Always extend selection here because it's caused by a mouse drag
965 m_selectionInitiationState = ExtendedSelection;
966 newSelection = VisibleSelection(targetPosition);
969 #if ENABLE(USERSELECT_ALL)
970 Node* rootUserSelectAllForMousePressNode = Position::rootUserSelectAllForNode(m_mousePressNode.get());
971 if (rootUserSelectAllForMousePressNode && rootUserSelectAllForMousePressNode == Position::rootUserSelectAllForNode(target)) {
972 newSelection.setBase(positionBeforeNode(rootUserSelectAllForMousePressNode).upstream(CanCrossEditingBoundary));
973 newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
975 // Reset base for user select all when base is inside user-select-all area and extent < base.
976 if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0)
977 newSelection.setBase(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
979 Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNode(target);
980 if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0)
981 newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTarget).upstream(CanCrossEditingBoundary));
982 else if (rootUserSelectAllForTarget && m_mousePressNode->renderer())
983 newSelection.setExtent(positionAfterNode(rootUserSelectAllForTarget).downstream(CanCrossEditingBoundary));
985 newSelection.setExtent(targetPosition);
988 newSelection.setExtent(targetPosition);
991 if (m_frame.selection().granularity() != CharacterGranularity)
992 newSelection.expandUsingGranularity(m_frame.selection().granularity());
994 m_frame.selection().setSelectionByMouseIfDifferent(newSelection, m_frame.selection().granularity(),
995 FrameSelection::AdjustEndpointsAtBidiBoundary);
997 #endif // ENABLE(DRAG_SUPPORT)
999 void EventHandler::lostMouseCapture()
1001 m_frame.selection().setCaretBlinkingSuspended(false);
1004 bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event)
1006 if (eventLoopHandleMouseUp(event))
1009 // If this was the first click in the window, we don't even want to clear the selection.
1010 // This case occurs when the user clicks on a draggable element, since we have to process
1011 // the mouse down and drag events to see if we might start a drag. For other first clicks
1012 // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
1013 // ignored upstream of this layer.
1014 return eventActivatedView(event.event());
1017 bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
1019 if (autoscrollInProgress())
1020 stopAutoscrollTimer();
1022 Ref<Frame> protectedFrame(m_frame);
1024 if (handleMouseUp(event))
1027 // Used to prevent mouseMoveEvent from initiating a drag before
1028 // the mouse is pressed again.
1029 m_mousePressed = false;
1030 m_capturesDragging = false;
1031 #if ENABLE(DRAG_SUPPORT)
1032 m_mouseDownMayStartDrag = false;
1034 m_mouseDownMayStartSelect = false;
1035 m_mouseDownMayStartAutoscroll = false;
1036 m_mouseDownWasInSubframe = false;
1038 bool handled = false;
1040 // Clear the selection if the mouse didn't move after the last mouse
1041 // press and it's not a context menu click. We do this so when clicking
1042 // on the selection, the selection goes away. However, if we are
1043 // editing, place the caret.
1044 if (m_mouseDownWasSingleClickInSelection && m_selectionInitiationState != ExtendedSelection
1045 #if ENABLE(DRAG_SUPPORT)
1046 && m_dragStartPosition == event.event().position()
1048 && m_frame.selection().isRange()
1049 && event.event().button() != RightButton) {
1050 VisibleSelection newSelection;
1051 Node* node = event.targetNode();
1052 bool caretBrowsing = m_frame.settings().caretBrowsingEnabled();
1053 bool allowSelectionChanges = true;
1054 if (node && node->renderer() && (caretBrowsing || node->hasEditableStyle())) {
1055 VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint(), nullptr);
1056 newSelection = VisibleSelection(pos);
1058 // On iOS, selection changes are triggered using platform-specific text interaction gestures rather than
1059 // default behavior on click or mouseup. As such, the only time we should allow click events to change the
1060 // selection on iOS is when we focus a different editable element, in which case the text interaction
1061 // gestures will fail.
1062 allowSelectionChanges = m_frame.selection().selection().rootEditableElement() != newSelection.rootEditableElement();
1066 if (allowSelectionChanges)
1067 setSelectionIfNeeded(m_frame.selection(), newSelection);
1072 if (event.event().button() == MiddleButton) {
1073 // Ignore handled, since we want to paste to where the caret was placed anyway.
1074 handled = handlePasteGlobalSelection(event.event()) || handled;
1080 #if ENABLE(PAN_SCROLLING)
1082 void EventHandler::didPanScrollStart()
1084 m_autoscrollController->didPanScrollStart();
1087 void EventHandler::didPanScrollStop()
1089 m_autoscrollController->didPanScrollStop();
1092 void EventHandler::startPanScrolling(RenderElement& renderer)
1095 if (!is<RenderBox>(renderer))
1097 m_autoscrollController->startPanScrolling(&downcast<RenderBox>(renderer), lastKnownMousePosition());
1102 #endif // ENABLE(PAN_SCROLLING)
1104 RenderBox* EventHandler::autoscrollRenderer() const
1106 return m_autoscrollController->autoscrollRenderer();
1109 void EventHandler::updateAutoscrollRenderer()
1111 m_autoscrollController->updateAutoscrollRenderer();
1114 bool EventHandler::autoscrollInProgress() const
1116 return m_autoscrollController->autoscrollInProgress();
1119 bool EventHandler::panScrollInProgress() const
1121 return m_autoscrollController->panScrollInProgress();
1124 #if ENABLE(DRAG_SUPPORT)
1125 DragSourceAction EventHandler::updateDragSourceActionsAllowed() const
1127 Page* page = m_frame.page();
1129 return DragSourceActionNone;
1131 FrameView* view = m_frame.view();
1133 return DragSourceActionNone;
1135 return page->dragController().delegateDragSourceAction(view->contentsToRootView(m_mouseDownPos));
1137 #endif // ENABLE(DRAG_SUPPORT)
1139 HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) const
1141 ASSERT((hitType & HitTestRequest::CollectMultipleElements) || padding.isEmpty());
1143 Ref<Frame> protectedFrame(m_frame);
1145 // We always send hitTestResultAtPoint to the main frame if we have one,
1146 // otherwise we might hit areas that are obscured by higher frames.
1147 if (!m_frame.isMainFrame()) {
1148 Frame& mainFrame = m_frame.mainFrame();
1149 FrameView* frameView = m_frame.view();
1150 FrameView* mainView = mainFrame.view();
1151 if (frameView && mainView) {
1152 IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point)));
1153 return mainFrame.eventHandler().hitTestResultAtPoint(mainFramePoint, hitType, padding);
1157 unsigned nonNegativePaddingWidth = std::max<LayoutUnit>(0, padding.width()).toUnsigned();
1158 unsigned nonNegativePaddingHeight = std::max<LayoutUnit>(0, padding.height()).toUnsigned();
1160 // We should always start hit testing a clean tree.
1161 if (auto* frameView = m_frame.view())
1162 frameView->updateLayoutAndStyleIfNeededRecursive();
1164 HitTestResult result(point, nonNegativePaddingHeight, nonNegativePaddingWidth, nonNegativePaddingHeight, nonNegativePaddingWidth);
1165 RenderView* renderView = m_frame.contentRenderer();
1169 // hitTestResultAtPoint is specifically used to hitTest into all frames, thus it always allows child frame content.
1170 HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent);
1171 renderView->hitTest(request, result);
1172 if (!request.readOnly())
1173 m_frame.document()->updateHoverActiveState(request, result.targetElement());
1175 if (request.disallowsUserAgentShadowContent())
1176 result.setToNonUserAgentShadowAncestor();
1181 void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed)
1183 m_autoscrollController->stopAutoscrollTimer(rendererIsBeingDestroyed);
1186 bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
1188 Node* node = startingNode;
1191 node = m_frame.document()->focusedElement();
1194 node = m_mousePressNode.get();
1197 auto r = node->renderer();
1198 if (r && !r->isListBox() && r->enclosingBox().scroll(direction, granularity)) {
1199 setFrameWasScrolledByUser();
1207 bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode)
1209 Node* node = startingNode;
1212 node = m_frame.document()->focusedElement();
1215 node = m_mousePressNode.get();
1218 auto r = node->renderer();
1219 if (r && !r->isListBox() && r->enclosingBox().logicalScroll(direction, granularity)) {
1220 setFrameWasScrolledByUser();
1228 bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
1230 Ref<Frame> protectedFrame(m_frame);
1232 // The layout needs to be up to date to determine if we can scroll. We may be
1233 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
1234 m_frame.document()->updateLayoutIgnorePendingStylesheets();
1235 if (scrollOverflow(direction, granularity, startingNode))
1237 Frame* frame = &m_frame;
1238 FrameView* view = frame->view();
1239 if (view && view->scroll(direction, granularity))
1241 frame = frame->tree().parent();
1244 return frame->eventHandler().scrollRecursively(direction, granularity, m_frame.ownerElement());
1247 bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode)
1249 Ref<Frame> protectedFrame(m_frame);
1251 // The layout needs to be up to date to determine if we can scroll. We may be
1252 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
1253 m_frame.document()->updateLayoutIgnorePendingStylesheets();
1254 if (logicalScrollOverflow(direction, granularity, startingNode))
1256 Frame* frame = &m_frame;
1257 FrameView* view = frame->view();
1259 bool scrolled = false;
1261 // Mac also resets the scroll position in the inline direction.
1262 if (granularity == ScrollByDocument && view && view->logicalScroll(ScrollInlineDirectionBackward, ScrollByDocument))
1265 if (view && view->logicalScroll(direction, granularity))
1271 frame = frame->tree().parent();
1275 return frame->eventHandler().logicalScrollRecursively(direction, granularity, m_frame.ownerElement());
1278 IntPoint EventHandler::lastKnownMousePosition() const
1280 return m_lastKnownMousePosition;
1283 Frame* EventHandler::subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult)
1285 if (!hitTestResult.isOverWidget())
1287 return subframeForTargetNode(hitTestResult.targetNode());
1290 Frame* EventHandler::subframeForTargetNode(Node* node)
1295 auto renderer = node->renderer();
1296 if (!is<RenderWidget>(renderer))
1299 Widget* widget = downcast<RenderWidget>(*renderer).widget();
1300 if (!is<FrameView>(widget))
1303 return &downcast<FrameView>(*widget).frame();
1306 #if ENABLE(CURSOR_SUPPORT)
1307 static bool isSubmitImage(Node* node)
1309 return is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*node).isImageButton();
1312 // Returns true if the node's editable block is not current focused for editing
1313 static bool nodeIsNotBeingEdited(const Node& node, const Frame& frame)
1315 return frame.selection().selection().rootEditableElement() != node.rootEditableElement();
1318 bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey)
1323 bool editable = node->hasEditableStyle();
1324 bool editableLinkEnabled = false;
1326 // If the link is editable, then we need to check the settings to see whether or not the link should be followed
1328 switch (m_frame.settings().editableLinkBehavior()) {
1330 case EditableLinkDefaultBehavior:
1331 case EditableLinkAlwaysLive:
1332 editableLinkEnabled = true;
1335 case EditableLinkNeverLive:
1336 editableLinkEnabled = false;
1339 case EditableLinkLiveWhenNotFocused:
1340 editableLinkEnabled = nodeIsNotBeingEdited(*node, m_frame) || shiftKey;
1343 case EditableLinkOnlyLiveWithShiftKey:
1344 editableLinkEnabled = shiftKey;
1349 return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled));
1352 void EventHandler::cursorUpdateTimerFired()
1354 ASSERT(m_frame.document());
1358 void EventHandler::updateCursor()
1360 if (m_mousePositionIsUnknown)
1363 FrameView* view = m_frame.view();
1367 RenderView* renderView = view->renderView();
1371 if (!view->shouldSetCursor())
1378 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
1380 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::AllowFrameScrollbars);
1381 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
1382 renderView->hitTest(request, result);
1384 updateCursor(*view, result, shiftKey);
1387 void EventHandler::updateCursor(FrameView& view, const HitTestResult& result, bool shiftKey)
1389 if (auto optionalCursor = selectCursor(result, shiftKey)) {
1390 m_currentMouseCursor = WTFMove(optionalCursor.value());
1391 view.setCursor(m_currentMouseCursor);
1395 std::optional<Cursor> EventHandler::selectCursor(const HitTestResult& result, bool shiftKey)
1397 if (m_resizeLayer && m_resizeLayer->inResizeMode())
1398 return std::nullopt;
1400 if (!m_frame.page())
1401 return std::nullopt;
1403 #if ENABLE(PAN_SCROLLING)
1404 if (m_frame.mainFrame().eventHandler().panScrollInProgress())
1405 return std::nullopt;
1408 Ref<Frame> protectedFrame(m_frame);
1410 // Use always pointer cursor for scrollbars.
1411 if (result.scrollbar()) {
1412 #if ENABLE(CURSOR_VISIBILITY)
1413 cancelAutoHideCursorTimer();
1415 return pointerCursor();
1418 Node* node = result.targetNode();
1420 return std::nullopt;
1422 auto renderer = node->renderer();
1423 auto* style = renderer ? &renderer->style() : nullptr;
1424 bool horizontalText = !style || style->isHorizontalWritingMode();
1425 const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCursor();
1427 #if ENABLE(CURSOR_VISIBILITY)
1428 if (style && style->cursorVisibility() == CursorVisibility::AutoHide)
1429 startAutoHideCursorTimer();
1431 cancelAutoHideCursorTimer();
1435 Cursor overrideCursor;
1436 switch (renderer->getCursor(roundedIntPoint(result.localPoint()), overrideCursor)) {
1437 case SetCursorBasedOnStyle:
1440 return overrideCursor;
1441 case DoNotSetCursor:
1442 return std::nullopt;
1446 if (style && style->cursors()) {
1447 const CursorList* cursors = style->cursors();
1448 for (unsigned i = 0; i < cursors->size(); ++i) {
1449 StyleImage* styleImage = (*cursors)[i].image();
1452 CachedImage* cachedImage = styleImage->cachedImage();
1455 float scale = styleImage->imageScaleFactor();
1456 // Get hotspot and convert from logical pixels to physical pixels.
1457 IntPoint hotSpot = (*cursors)[i].hotSpot();
1458 FloatSize size = cachedImage->imageForRenderer(renderer)->size();
1459 if (cachedImage->errorOccurred())
1461 // Limit the size of cursors (in UI pixels) so that they cannot be
1462 // used to cover UI elements in chrome.
1463 size.scale(1 / scale);
1464 if (size.width() > maximumCursorSize || size.height() > maximumCursorSize)
1467 Image* image = cachedImage->imageForRenderer(renderer);
1468 #if ENABLE(MOUSE_CURSOR_SCALE)
1469 // Ensure no overflow possible in calculations above.
1470 if (scale < minimumCursorScale)
1472 return Cursor(image, hotSpot, scale);
1475 return Cursor(image, hotSpot);
1476 #endif // ENABLE(MOUSE_CURSOR_SCALE)
1480 // During selection, use an I-beam regardless of the content beneath the cursor.
1481 // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection.
1483 && m_mouseDownMayStartSelect
1484 #if ENABLE(DRAG_SUPPORT)
1485 && !m_mouseDownMayStartDrag
1487 && m_frame.selection().isCaretOrRange()
1488 && !m_capturingMouseEventsElement)
1491 switch (style ? style->cursor() : CursorType::Auto) {
1492 case CursorType::Auto: {
1493 bool editable = node->hasEditableStyle();
1495 if (useHandCursor(node, result.isOverLink(), shiftKey))
1496 return handCursor();
1498 bool inResizer = false;
1500 if (RenderLayer* layer = renderer->enclosingLayer()) {
1501 if (FrameView* view = m_frame.view())
1502 inResizer = layer->isPointInResizeControl(view->windowToContents(roundedIntPoint(result.localPoint())));
1506 if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar())
1508 return pointerCursor();
1510 case CursorType::Default:
1511 return pointerCursor();
1512 case CursorType::None:
1513 return noneCursor();
1514 case CursorType::ContextMenu:
1515 return contextMenuCursor();
1516 case CursorType::Help:
1517 return helpCursor();
1518 case CursorType::Pointer:
1519 return handCursor();
1520 case CursorType::Progress:
1521 return progressCursor();
1522 case CursorType::Wait:
1523 return waitCursor();
1524 case CursorType::Cell:
1525 return cellCursor();
1526 case CursorType::Crosshair:
1527 return crossCursor();
1528 case CursorType::Text:
1529 return iBeamCursor();
1530 case CursorType::VerticalText:
1531 return verticalTextCursor();
1532 case CursorType::Alias:
1533 return aliasCursor();
1534 case CursorType::Copy:
1535 return copyCursor();
1536 case CursorType::Move:
1537 return moveCursor();
1538 case CursorType::NoDrop:
1539 return noDropCursor();
1540 case CursorType::NotAllowed:
1541 return notAllowedCursor();
1542 case CursorType::Grab:
1543 return grabCursor();
1544 case CursorType::Grabbing:
1545 return grabbingCursor();
1546 case CursorType::EResize:
1547 return eastResizeCursor();
1548 case CursorType::NResize:
1549 return northResizeCursor();
1550 case CursorType::NEResize:
1551 return northEastResizeCursor();
1552 case CursorType::NWResize:
1553 return northWestResizeCursor();
1554 case CursorType::SResize:
1555 return southResizeCursor();
1556 case CursorType::SEResize:
1557 return southEastResizeCursor();
1558 case CursorType::SWResize:
1559 return southWestResizeCursor();
1560 case CursorType::WResize:
1561 return westResizeCursor();
1562 case CursorType::EWResize:
1563 return eastWestResizeCursor();
1564 case CursorType::NSResize:
1565 return northSouthResizeCursor();
1566 case CursorType::NESWResize:
1567 return northEastSouthWestResizeCursor();
1568 case CursorType::NWSEResize:
1569 return northWestSouthEastResizeCursor();
1570 case CursorType::ColumnResize:
1571 return columnResizeCursor();
1572 case CursorType::RowResize:
1573 return rowResizeCursor();
1574 case CursorType::AllScroll:
1575 return moveCursor();
1576 case CursorType::ZoomIn:
1577 return zoomInCursor();
1578 case CursorType::ZoomOut:
1579 return zoomOutCursor();
1581 return pointerCursor();
1583 #endif // ENABLE(CURSOR_SUPPORT)
1585 #if ENABLE(CURSOR_VISIBILITY)
1586 void EventHandler::startAutoHideCursorTimer()
1588 Page* page = m_frame.page();
1592 m_autoHideCursorTimer.startOneShot(page->settings().timeWithoutMouseMovementBeforeHidingControls());
1594 #if !ENABLE(IOS_TOUCH_EVENTS)
1595 // The fake mouse move event screws up the auto-hide feature (by resetting the auto-hide timer)
1596 // so cancel any pending fake mouse moves.
1597 if (m_fakeMouseMoveEventTimer.isActive())
1598 m_fakeMouseMoveEventTimer.stop();
1602 void EventHandler::cancelAutoHideCursorTimer()
1604 if (m_autoHideCursorTimer.isActive())
1605 m_autoHideCursorTimer.stop();
1608 void EventHandler::autoHideCursorTimerFired()
1610 FrameView* view = m_frame.view();
1611 if (!view || !view->isActive())
1614 if (auto page = m_frame.page())
1615 page->chrome().setCursorHiddenUntilMouseMoves(true);
1619 static LayoutPoint documentPointForWindowPoint(Frame& frame, const IntPoint& windowPoint)
1621 FrameView* view = frame.view();
1622 // FIXME: Is it really OK to use the wrong coordinates here when view is 0?
1623 // Historically the code would just crash; this is clearly no worse than that.
1624 return view ? view->windowToContents(windowPoint) : windowPoint;
1627 static Scrollbar* scrollbarForMouseEvent(const MouseEventWithHitTestResults& mouseEvent, FrameView* view)
1630 if (auto* scrollbar = view->scrollbarAtPoint(mouseEvent.event().position()))
1633 return mouseEvent.scrollbar();
1637 bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& platformMouseEvent)
1639 Ref<Frame> protectedFrame(m_frame);
1640 RefPtr<FrameView> protector(m_frame.view());
1642 if (InspectorInstrumentation::handleMousePress(m_frame)) {
1647 #if ENABLE(POINTER_LOCK)
1648 if (m_frame.page()->pointerLockController().isLocked()) {
1649 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousedownEvent);
1654 if (m_frame.page()->pageOverlayController().handleMouseEvent(platformMouseEvent))
1657 #if ENABLE(TOUCH_EVENTS)
1658 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
1659 if (defaultPrevented)
1663 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
1665 // FIXME (bug 68185): this call should be made at another abstraction layer
1666 m_frame.loader().resetMultipleFormSubmissionProtection();
1668 #if !ENABLE(IOS_TOUCH_EVENTS)
1669 cancelFakeMouseMoveEvent();
1671 m_mousePressed = true;
1672 m_capturesDragging = true;
1673 setLastKnownMousePosition(platformMouseEvent);
1674 m_mouseDownTimestamp = platformMouseEvent.timestamp();
1675 #if ENABLE(DRAG_SUPPORT)
1676 m_mouseDownMayStartDrag = false;
1678 m_mouseDownMayStartSelect = false;
1679 m_mouseDownMayStartAutoscroll = false;
1680 if (FrameView* view = m_frame.view())
1681 m_mouseDownPos = view->windowToContents(platformMouseEvent.position());
1686 m_mouseDownWasInSubframe = false;
1688 HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
1689 // Save the document point we generate in case the window coordinate is invalidated by what happens
1690 // when we dispatch the event.
1691 LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, platformMouseEvent.position());
1692 MouseEventWithHitTestResults mouseEvent = m_frame.document()->prepareMouseEvent(request, documentPoint, platformMouseEvent);
1694 if (!mouseEvent.targetNode()) {
1699 m_mousePressNode = mouseEvent.targetNode();
1700 m_frame.document()->setFocusNavigationStartingNode(mouseEvent.targetNode());
1702 Scrollbar* scrollbar = scrollbarForMouseEvent(mouseEvent, m_frame.view());
1703 updateLastScrollbarUnderMouse(scrollbar, SetOrClearLastScrollbar::Set);
1704 bool passedToScrollbar = scrollbar && passMousePressEventToScrollbar(mouseEvent, scrollbar);
1706 if (!passedToScrollbar) {
1707 RefPtr<Frame> subframe = subframeForHitTestResult(mouseEvent);
1708 if (subframe && passMousePressEventToSubframe(mouseEvent, subframe.get())) {
1709 // Start capturing future events for this frame. We only do this if we didn't clear
1710 // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop.
1711 m_capturesDragging = subframe->eventHandler().capturesDragging();
1712 if (m_mousePressed && m_capturesDragging) {
1713 m_capturingMouseEventsElement = subframe->ownerElement();
1714 m_eventHandlerWillResetCapturingMouseEventsElement = true;
1721 #if ENABLE(PAN_SCROLLING)
1722 // We store whether pan scrolling is in progress before calling stopAutoscrollTimer()
1723 // because it will set m_autoscrollType to NoAutoscroll on return.
1724 bool isPanScrollInProgress = m_frame.mainFrame().eventHandler().panScrollInProgress();
1725 stopAutoscrollTimer();
1726 if (isPanScrollInProgress) {
1727 // We invalidate the click when exiting pan scrolling so that we don't inadvertently navigate
1728 // away from the current page (e.g. the click was on a hyperlink). See <rdar://problem/6095023>.
1734 m_clickCount = platformMouseEvent.clickCount();
1735 m_clickNode = mouseEvent.targetNode();
1742 if (FrameView* view = m_frame.view()) {
1743 RenderLayer* layer = m_clickNode->renderer() ? m_clickNode->renderer()->enclosingLayer() : 0;
1744 IntPoint p = view->windowToContents(platformMouseEvent.position());
1745 if (layer && layer->isPointInResizeControl(p)) {
1746 layer->setInResizeMode(true);
1747 m_resizeLayer = layer;
1748 m_offsetFromResizeCorner = layer->offsetFromResizeCorner(p);
1754 m_frame.selection().setCaretBlinkingSuspended(true);
1756 bool swallowEvent = !dispatchMouseEvent(eventNames().mousedownEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true);
1757 m_capturesDragging = !swallowEvent || mouseEvent.scrollbar();
1759 // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults
1760 // in case the scrollbar widget was destroyed when the mouse event was handled.
1761 if (mouseEvent.scrollbar()) {
1762 const bool wasLastScrollBar = mouseEvent.scrollbar() == m_lastScrollbarUnderMouse;
1763 mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent);
1764 if (wasLastScrollBar && mouseEvent.scrollbar() != m_lastScrollbarUnderMouse)
1765 m_lastScrollbarUnderMouse = nullptr;
1768 if (!swallowEvent) {
1769 // Refetch the event target node if it currently is the shadow node inside an <input> element.
1770 // If a mouse event handler changes the input element type to one that has a widget associated,
1771 // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the
1772 // event target node can't still be the shadow node.
1773 if (is<ShadowRoot>(*mouseEvent.targetNode()) && is<HTMLInputElement>(*downcast<ShadowRoot>(*mouseEvent.targetNode()).host()))
1774 mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent);
1777 if (!swallowEvent) {
1778 if (passedToScrollbar)
1779 swallowEvent = true;
1781 swallowEvent = handleMousePressEvent(mouseEvent);
1783 return swallowEvent;
1786 // This method only exists for platforms that don't know how to deliver
1787 bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& platformMouseEvent)
1789 Ref<Frame> protectedFrame(m_frame);
1790 RefPtr<FrameView> protector(m_frame.view());
1792 m_frame.selection().setCaretBlinkingSuspended(false);
1794 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
1796 #if ENABLE(POINTER_LOCK)
1797 if (m_frame.page()->pointerLockController().isLocked()) {
1798 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent);
1803 // We get this instead of a second mouse-up
1804 m_mousePressed = false;
1805 setLastKnownMousePosition(platformMouseEvent);
1807 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
1808 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
1809 Frame* subframe = subframeForHitTestResult(mouseEvent);
1810 if (m_eventHandlerWillResetCapturingMouseEventsElement)
1811 m_capturingMouseEventsElement = nullptr;
1812 if (subframe && passMousePressEventToSubframe(mouseEvent, subframe))
1815 m_clickCount = platformMouseEvent.clickCount();
1816 bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false);
1818 bool swallowClickEvent = platformMouseEvent.button() != RightButton && mouseEvent.targetNode() == m_clickNode && !dispatchMouseEvent(eventNames().clickEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true);
1820 if (m_lastScrollbarUnderMouse)
1821 swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent);
1823 bool swallowMouseReleaseEvent = !swallowMouseUpEvent && handleMouseReleaseEvent(mouseEvent);
1827 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
1830 static ScrollableArea* enclosingScrollableArea(Node* node)
1832 for (auto ancestor = node; ancestor; ancestor = ancestor->parentOrShadowHostNode()) {
1833 if (is<HTMLIFrameElement>(*ancestor) || is<HTMLHtmlElement>(*ancestor) || is<HTMLDocument>(*ancestor))
1836 auto renderer = ancestor->renderer();
1840 if (is<RenderListBox>(*renderer))
1841 return downcast<RenderListBox>(renderer);
1843 return renderer->enclosingLayer();
1849 bool EventHandler::mouseMoved(const PlatformMouseEvent& event)
1851 Ref<Frame> protectedFrame(m_frame);
1852 RefPtr<FrameView> protector(m_frame.view());
1853 MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration);
1855 if (m_frame.page() && m_frame.page()->pageOverlayController().handleMouseEvent(event))
1858 HitTestResult hoveredNode = HitTestResult(LayoutPoint());
1859 bool result = handleMouseMoveEvent(event, &hoveredNode);
1861 Page* page = m_frame.page();
1865 if (auto scrolledArea = enclosingScrollableArea(hoveredNode.innerNode())) {
1866 if (FrameView* frameView = m_frame.view()) {
1867 if (frameView->containsScrollableArea(scrolledArea))
1868 scrolledArea->mouseMovedInContentArea();
1872 if (FrameView* frameView = m_frame.view())
1873 frameView->mouseMovedInContentArea();
1875 hoveredNode.setToNonUserAgentShadowAncestor();
1876 page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags());
1877 page->chrome().setToolTip(hoveredNode);
1881 bool EventHandler::passMouseMovedEventToScrollbars(const PlatformMouseEvent& event)
1883 HitTestResult hoveredNode;
1884 return handleMouseMoveEvent(event, &hoveredNode, true);
1887 bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& platformMouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars)
1889 #if ENABLE(TOUCH_EVENTS)
1890 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
1891 if (defaultPrevented)
1895 Ref<Frame> protectedFrame(m_frame);
1896 RefPtr<FrameView> protector(m_frame.view());
1898 #if ENABLE(POINTER_LOCK)
1899 if (m_frame.page()->pointerLockController().isLocked()) {
1900 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousemoveEvent);
1905 setLastKnownMousePosition(platformMouseEvent);
1907 if (m_hoverTimer.isActive())
1908 m_hoverTimer.stop();
1910 #if ENABLE(CURSOR_SUPPORT)
1911 m_cursorUpdateTimer.stop();
1914 #if !ENABLE(IOS_TOUCH_EVENTS)
1915 cancelFakeMouseMoveEvent();
1919 downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition));
1923 if (m_frameSetBeingResized)
1924 return !dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, platformMouseEvent, false);
1926 // On iOS, our scrollbars are managed by UIKit.
1928 // Send events right to a scrollbar if the mouse is pressed.
1929 if (m_lastScrollbarUnderMouse && m_mousePressed)
1930 return m_lastScrollbarUnderMouse->mouseMoved(platformMouseEvent);
1933 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::AllowFrameScrollbars;
1935 hitType |= HitTestRequest::Active;
1936 else if (onlyUpdateScrollbars) {
1937 // Mouse events should be treated as "read-only" if we're updating only scrollbars. This
1938 // means that :hover and :active freeze in the state they were in, rather than updating
1939 // for nodes the mouse moves while the window is not key (which will be the case if
1940 // onlyUpdateScrollbars is true).
1941 hitType |= HitTestRequest::ReadOnly;
1944 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
1945 // Treat any mouse move events as readonly if the user is currently touching the screen.
1947 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
1949 HitTestRequest request(hitType);
1950 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
1952 *hoveredNode = mouseEvent.hitTestResult();
1954 if (m_resizeLayer && m_resizeLayer->inResizeMode())
1955 m_resizeLayer->resize(platformMouseEvent, m_offsetFromResizeCorner);
1957 Scrollbar* scrollbar = mouseEvent.scrollbar();
1958 updateLastScrollbarUnderMouse(scrollbar, m_mousePressed ? SetOrClearLastScrollbar::Clear : SetOrClearLastScrollbar::Set);
1960 // On iOS, our scrollbars are managed by UIKit.
1962 if (!m_mousePressed && scrollbar)
1963 scrollbar->mouseMoved(platformMouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering.
1965 if (onlyUpdateScrollbars) {
1966 updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true);
1971 bool swallowEvent = false;
1972 RefPtr<Frame> newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent);
1974 // We want mouseouts to happen first, from the inside out. First send a move event to the last subframe so that it will fire mouseouts.
1975 if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(&m_frame) && m_lastMouseMoveEventSubframe != newSubframe)
1976 passMouseMoveEventToSubframe(mouseEvent, m_lastMouseMoveEventSubframe.get());
1979 // Update over/out state before passing the event to the subframe.
1980 updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true);
1982 // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target
1983 // node to be detached from its FrameView, in which case the event should not be passed.
1984 if (newSubframe->view())
1985 swallowEvent |= passMouseMoveEventToSubframe(mouseEvent, newSubframe.get(), hoveredNode);
1988 if (!newSubframe || mouseEvent.scrollbar()) {
1989 #if ENABLE(CURSOR_SUPPORT)
1990 if (auto* view = m_frame.view())
1991 updateCursor(*view, mouseEvent.hitTestResult(), platformMouseEvent.shiftKey());
1995 m_lastMouseMoveEventSubframe = newSubframe;
2000 swallowEvent = !dispatchMouseEvent(eventNames().mousemoveEvent, mouseEvent.targetNode(), false, 0, platformMouseEvent, true);
2001 #if ENABLE(DRAG_SUPPORT)
2003 swallowEvent = handleMouseDraggedEvent(mouseEvent);
2006 return swallowEvent;
2009 void EventHandler::invalidateClick()
2012 m_clickNode = nullptr;
2015 static Node* targetNodeForClickEvent(Node* mousePressNode, Node* mouseReleaseNode)
2017 if (!mousePressNode || !mouseReleaseNode)
2020 if (mousePressNode == mouseReleaseNode)
2021 return mouseReleaseNode;
2023 // If mousePressNode and mouseReleaseNode differ, we should fire the event at their common ancestor if there is one.
2024 if (&mousePressNode->document() == &mouseReleaseNode->document()) {
2025 if (auto* commonAncestor = Range::commonAncestorContainer(mousePressNode, mouseReleaseNode))
2026 return commonAncestor;
2029 Element* mouseReleaseShadowHost = mouseReleaseNode->shadowHost();
2030 if (mouseReleaseShadowHost && mouseReleaseShadowHost == mousePressNode->shadowHost()) {
2031 // We want to dispatch the click to the shadow tree host element to give listeners the illusion that the
2032 // shadom tree is a single element. For example, we want to give the illusion that <input type="range">
2033 // is a single element even though it is a composition of multiple shadom tree elements.
2034 return mouseReleaseShadowHost;
2039 bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& platformMouseEvent)
2041 Ref<Frame> protectedFrame(m_frame);
2042 RefPtr<FrameView> protector(m_frame.view());
2044 m_frame.selection().setCaretBlinkingSuspended(false);
2046 #if ENABLE(POINTER_LOCK)
2047 if (m_frame.page()->pointerLockController().isLocked()) {
2048 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent);
2053 if (m_frame.page()->pageOverlayController().handleMouseEvent(platformMouseEvent))
2056 #if ENABLE(TOUCH_EVENTS)
2057 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
2058 if (defaultPrevented)
2062 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
2064 #if ENABLE(PAN_SCROLLING)
2065 m_autoscrollController->handleMouseReleaseEvent(platformMouseEvent);
2068 m_mousePressed = false;
2069 setLastKnownMousePosition(platformMouseEvent);
2073 downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition));
2077 if (m_frameSetBeingResized)
2078 return !dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, platformMouseEvent, false);
2080 // If an immediate action began or was completed using this series of mouse events, then we should send mouseup to
2081 // the DOM and return now so that we don't perform our own default behaviors.
2082 if (m_immediateActionStage == ImmediateActionStage::ActionCompleted || m_immediateActionStage == ImmediateActionStage::ActionUpdated || m_immediateActionStage == ImmediateActionStage::ActionCancelledAfterUpdate) {
2083 m_immediateActionStage = ImmediateActionStage::None;
2084 return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), true, m_clickCount, platformMouseEvent, false);
2086 m_immediateActionStage = ImmediateActionStage::None;
2088 if (m_lastScrollbarUnderMouse) {
2090 m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent);
2091 bool cancelable = true;
2092 bool setUnder = false;
2093 return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), cancelable, m_clickCount, platformMouseEvent, setUnder);
2096 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
2097 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
2098 Frame* subframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent);
2099 if (m_eventHandlerWillResetCapturingMouseEventsElement)
2100 m_capturingMouseEventsElement = nullptr;
2101 if (subframe && passMouseReleaseEventToSubframe(mouseEvent, subframe))
2104 bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false);
2106 bool contextMenuEvent = platformMouseEvent.button() == RightButton;
2108 Node* nodeToClick = targetNodeForClickEvent(m_clickNode.get(), mouseEvent.targetNode());
2109 bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && nodeToClick && !dispatchMouseEvent(eventNames().clickEvent, nodeToClick, true, m_clickCount, platformMouseEvent, true);
2111 if (m_resizeLayer) {
2112 m_resizeLayer->setInResizeMode(false);
2113 m_resizeLayer = nullptr;
2116 bool swallowMouseReleaseEvent = false;
2117 if (!swallowMouseUpEvent)
2118 swallowMouseReleaseEvent = handleMouseReleaseEvent(mouseEvent);
2122 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
2125 #if ENABLE(MOUSE_FORCE_EVENTS)
2126 bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& event)
2128 Ref<Frame> protectedFrame(m_frame);
2129 RefPtr<FrameView> protector(m_frame.view());
2131 #if ENABLE(POINTER_LOCK)
2132 if (m_frame.page()->pointerLockController().isLocked()) {
2133 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcechangedEvent);
2134 if (event.type() == PlatformEvent::MouseForceDown)
2135 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcedownEvent);
2136 if (event.type() == PlatformEvent::MouseForceUp)
2137 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforceupEvent);
2142 setLastKnownMousePosition(event);
2144 HitTestRequest::HitTestRequestType hitType = HitTestRequest::DisallowUserAgentShadowContent;
2147 hitType |= HitTestRequest::Active;
2149 HitTestRequest request(hitType);
2150 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event);
2152 bool swallowedEvent = !dispatchMouseEvent(eventNames().webkitmouseforcechangedEvent, mouseEvent.targetNode(), false, 0, event, false);
2153 if (event.type() == PlatformEvent::MouseForceDown)
2154 swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforcedownEvent, mouseEvent.targetNode(), false, 0, event, false);
2155 if (event.type() == PlatformEvent::MouseForceUp)
2156 swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforceupEvent, mouseEvent.targetNode(), false, 0, event, false);
2158 return swallowedEvent;
2161 bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& )
2165 #endif // #if ENABLE(MOUSE_FORCE_EVENTS)
2167 bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& platformMouseEvent)
2169 // If the event was a middle click, attempt to copy global selection in after
2170 // the newly set caret position.
2172 // This code is called from either the mouse up or mouse down handling. There
2173 // is some debate about when the global selection is pasted:
2174 // xterm: pastes on up.
2175 // GTK: pastes on down.
2176 // Qt: pastes on up.
2177 // Firefox: pastes on up.
2178 // Chromium: pastes on up.
2180 // There is something of a webcompat angle to this well, as highlighted by
2181 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
2182 // down then the text is pasted just before the onclick handler runs and
2183 // clears the text box. So it's important this happens after the event
2184 // handlers have been fired.
2186 if (platformMouseEvent.type() != PlatformEvent::MousePressed)
2189 if (platformMouseEvent.type() != PlatformEvent::MouseReleased)
2193 if (!m_frame.page())
2195 Frame& focusFrame = m_frame.page()->focusController().focusedOrMainFrame();
2196 // Do not paste here if the focus was moved somewhere else.
2197 if (&m_frame == &focusFrame && m_frame.editor().client()->supportsGlobalSelection())
2198 return m_frame.editor().command("PasteGlobalSelection"_s).execute();
2203 #if ENABLE(DRAG_SUPPORT)
2205 bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer& dataTransfer)
2207 Ref<Frame> protectedFrame(m_frame);
2208 FrameView* view = m_frame.view();
2210 // FIXME: We might want to dispatch a dragleave even if the view is gone.
2214 view->disableLayerFlushThrottlingTemporarilyForInteraction();
2215 Ref<MouseEvent> me = MouseEvent::create(eventType,
2216 true, true, event.timestamp().approximateMonotonicTime(), &m_frame.windowProxy(),
2217 0, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(),
2218 #if ENABLE(POINTER_LOCK)
2219 event.movementDelta().x(), event.movementDelta().y(),
2221 event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(),
2222 0, 0, nullptr, event.force(), NoTap, &dataTransfer);
2224 dragTarget.dispatchEvent(me);
2225 return me->defaultPrevented();
2228 static bool targetIsFrame(Node* target, Frame*& frame)
2230 if (!is<HTMLFrameElementBase>(target))
2233 frame = downcast<HTMLFrameElementBase>(*target).contentFrame();
2237 static DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation)
2239 if (dragOperation == "copy")
2240 return DragOperationCopy;
2241 if (dragOperation == "move")
2242 return DragOperationMove;
2243 if (dragOperation == "link")
2244 return DragOperationLink;
2245 return DragOperationNone;
2248 static String convertDragOperationToDropZoneOperation(DragOperation operation)
2250 switch (operation) {
2251 case DragOperationCopy:
2253 case DragOperationMove:
2255 case DragOperationLink:
2262 static bool hasDropZoneType(DataTransfer& dataTransfer, const String& keyword)
2264 if (keyword.startsWith("file:"))
2265 return dataTransfer.hasFileOfType(keyword.substring(5));
2267 if (keyword.startsWith("string:"))
2268 return dataTransfer.hasStringOfType(keyword.substring(7));
2273 static bool findDropZone(Node& target, DataTransfer& dataTransfer)
2275 RefPtr<Element> element = is<Element>(target) ? &downcast<Element>(target) : target.parentElement();
2276 for (; element; element = element->parentElement()) {
2277 SpaceSplitString keywords(element->attributeWithoutSynchronization(webkitdropzoneAttr), true);
2278 bool matched = false;
2279 DragOperation dragOperation = DragOperationNone;
2280 for (unsigned i = 0, size = keywords.size(); i < size; ++i) {
2281 DragOperation op = convertDropZoneOperationToDragOperation(keywords[i]);
2282 if (op != DragOperationNone) {
2283 if (dragOperation == DragOperationNone)
2286 matched = matched || hasDropZoneType(dataTransfer, keywords[i].string());
2287 if (matched && dragOperation != DragOperationNone)
2291 dataTransfer.setDropEffect(convertDragOperationToDropZoneOperation(dragOperation));
2298 EventHandler::DragTargetResponse EventHandler::dispatchDragEnterOrDragOverEvent(const AtomicString& eventType, Element& target, const PlatformMouseEvent& event,
2299 std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
2301 auto dataTransfer = DataTransfer::createForUpdatingDropTarget(target.document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
2302 bool accept = dispatchDragEvent(eventType, target, event, dataTransfer.get());
2304 accept = findDropZone(target, dataTransfer);
2305 dataTransfer->makeInvalidForSecurity();
2306 if (accept && !dataTransfer->dropEffectIsUninitialized())
2307 return { true, dataTransfer->destinationOperation() };
2308 return { accept, std::nullopt };
2311 EventHandler::DragTargetResponse EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, const std::function<std::unique_ptr<Pasteboard>()>& makePasteboard, DragOperation sourceOperation, bool draggingFiles)
2313 Ref<Frame> protectedFrame(m_frame);
2314 if (!m_frame.view())
2317 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
2318 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event);
2320 RefPtr<Element> newTarget;
2321 if (Node* targetNode = mouseEvent.targetNode()) {
2322 // Drag events should never go to non-element nodes (following IE, and proper mouseover/out dispatch)
2323 if (!is<Element>(*targetNode))
2324 newTarget = targetNode->parentOrShadowHostElement();
2326 newTarget = downcast<Element>(targetNode);
2329 m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp());
2331 DragTargetResponse response;
2332 if (m_dragTarget != newTarget) {
2333 // FIXME: this ordering was explicitly chosen to match WinIE. However,
2334 // it is sometimes incorrect when dragging within subframes, as seen with
2335 // LayoutTests/fast/events/drag-in-frames.html.
2337 // Moreover, this ordering conforms to section 7.9.4 of the HTML 5 spec. <http://dev.w3.org/html5/spec/Overview.html#drag-and-drop-processing-model>.
2339 if (targetIsFrame(newTarget.get(), targetFrame)) {
2341 response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
2342 } else if (newTarget) {
2343 // As per section 7.9.4 of the HTML 5 spec., we must always fire a drag event before firing a dragenter, dragleave, or dragover event.
2344 if (dragState().source && dragState().shouldDispatchEvents)
2345 dispatchDragSrcEvent(eventNames().dragEvent, event);
2346 response = dispatchDragEnterOrDragOverEvent(eventNames().dragenterEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles);
2349 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2350 // FIXME: Recursing again here doesn't make sense if the newTarget and m_dragTarget were in the same frame.
2352 response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
2353 } else if (m_dragTarget) {
2354 auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), makePasteboard(), sourceOperation, draggingFiles);
2355 dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get());
2356 dataTransfer->makeInvalidForSecurity();
2360 // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that
2361 // two dragover events fired. So, we mark that we should only fire a dragover event on the next call to this function.
2362 m_shouldOnlyFireDragOverEvent = true;
2366 if (targetIsFrame(newTarget.get(), targetFrame)) {
2368 response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
2369 } else if (newTarget) {
2370 // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier.
2371 if (!m_shouldOnlyFireDragOverEvent && dragState().source && dragState().shouldDispatchEvents)
2372 dispatchDragSrcEvent(eventNames().dragEvent, event);
2373 response = dispatchDragEnterOrDragOverEvent(eventNames().dragoverEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles);
2374 m_shouldOnlyFireDragOverEvent = false;
2377 m_dragTarget = WTFMove(newTarget);
2381 void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
2383 Ref<Frame> protectedFrame(m_frame);
2386 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2388 targetFrame->eventHandler().cancelDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles);
2389 } else if (m_dragTarget) {
2390 if (dragState().source && dragState().shouldDispatchEvents)
2391 dispatchDragSrcEvent(eventNames().dragEvent, event);
2393 auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
2394 dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get());
2395 dataTransfer->makeInvalidForSecurity();
2400 bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
2402 Ref<Frame> protectedFrame(m_frame);
2405 bool preventedDefault = false;
2406 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2408 preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles);
2409 } else if (m_dragTarget) {
2410 auto dataTransfer = DataTransfer::createForDrop(m_dragTarget->document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
2411 preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, dataTransfer);
2412 dataTransfer->makeInvalidForSecurity();
2415 return preventedDefault;
2418 void EventHandler::clearDragState()
2420 stopAutoscrollTimer();
2421 m_dragTarget = nullptr;
2422 m_capturingMouseEventsElement = nullptr;
2423 m_shouldOnlyFireDragOverEvent = false;
2425 m_sendingEventToSubview = false;
2429 #endif // ENABLE(DRAG_SUPPORT)
2431 void EventHandler::setCapturingMouseEventsElement(Element* element)
2433 m_capturingMouseEventsElement = element;
2434 m_eventHandlerWillResetCapturingMouseEventsElement = false;
2437 MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mouseEvent)
2439 Ref<Frame> protectedFrame(m_frame);
2440 ASSERT(m_frame.document());
2441 return m_frame.document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mouseEvent.position()), mouseEvent);
2444 static RenderElement* nearestCommonHoverAncestor(RenderElement* obj1, RenderElement* obj2)
2449 for (RenderElement* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor()) {
2450 for (RenderElement* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor()) {
2451 if (currObj1 == currObj2)
2459 static bool hierarchyHasCapturingEventListeners(Element* element, const AtomicString& eventName)
2461 for (ContainerNode* curr = element; curr; curr = curr->parentOrShadowHostNode()) {
2462 if (curr->hasCapturingEventListeners(eventName))
2468 void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& platformMouseEvent, bool fireMouseOverOut)
2470 Ref<Frame> protectedFrame(m_frame);
2471 Element* targetElement = nullptr;
2473 // If we're capturing, we always go right to that element.
2474 if (m_capturingMouseEventsElement)
2475 targetElement = m_capturingMouseEventsElement.get();
2476 else if (targetNode) {
2477 // If the target node is a non-element, dispatch on the parent. <rdar://problem/4196646>
2478 while (targetNode && !is<Element>(*targetNode))
2479 targetNode = targetNode->parentInComposedTree();
2480 targetElement = downcast<Element>(targetNode);
2483 m_elementUnderMouse = targetElement;
2485 // Fire mouseout/mouseover if the mouse has shifted to a different node.
2486 if (fireMouseOverOut) {
2487 auto scrollableAreaForLastNode = enclosingScrollableArea(m_lastElementUnderMouse.get());
2488 auto scrollableAreaForNodeUnderMouse = enclosingScrollableArea(m_elementUnderMouse.get());
2489 Page* page = m_frame.page();
2491 if (m_lastElementUnderMouse && (!m_elementUnderMouse || &m_elementUnderMouse->document() != m_frame.document())) {
2492 // The mouse has moved between frames.
2493 if (Frame* frame = m_lastElementUnderMouse->document().frame()) {
2494 if (FrameView* frameView = frame->view())
2495 frameView->mouseExitedContentArea();
2497 } else if (page && (scrollableAreaForLastNode && (!scrollableAreaForNodeUnderMouse || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) {
2498 // The mouse has moved between layers.
2499 if (Frame* frame = m_lastElementUnderMouse->document().frame()) {
2500 if (FrameView* frameView = frame->view()) {
2501 if (frameView->containsScrollableArea(scrollableAreaForLastNode))
2502 scrollableAreaForLastNode->mouseExitedContentArea();
2507 if (m_elementUnderMouse && (!m_lastElementUnderMouse || &m_lastElementUnderMouse->document() != m_frame.document())) {
2508 // The mouse has moved between frames.
2509 if (Frame* frame = m_elementUnderMouse->document().frame()) {
2510 if (FrameView* frameView = frame->view())
2511 frameView->mouseEnteredContentArea();
2513 } else if (page && (scrollableAreaForNodeUnderMouse && (!scrollableAreaForLastNode || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) {
2514 // The mouse has moved between layers.
2515 if (Frame* frame = m_elementUnderMouse->document().frame()) {
2516 if (FrameView* frameView = frame->view()) {
2517 if (frameView->containsScrollableArea(scrollableAreaForNodeUnderMouse))
2518 scrollableAreaForNodeUnderMouse->mouseEnteredContentArea();
2523 if (m_lastElementUnderMouse && &m_lastElementUnderMouse->document() != m_frame.document()) {
2524 m_lastElementUnderMouse = nullptr;
2525 m_lastScrollbarUnderMouse = nullptr;
2528 if (m_lastElementUnderMouse != m_elementUnderMouse) {
2529 // mouseenter and mouseleave events are only dispatched if there is a capturing eventhandler on an ancestor
2530 // or a normal eventhandler on the element itself (they don't bubble).
2531 // This optimization is necessary since these events can cause O(n^2) capturing event-handler checks.
2532 bool hasCapturingMouseEnterListener = hierarchyHasCapturingEventListeners(m_elementUnderMouse.get(), eventNames().mouseenterEvent);
2533 bool hasCapturingMouseLeaveListener = hierarchyHasCapturingEventListeners(m_lastElementUnderMouse.get(), eventNames().mouseleaveEvent);
2535 RenderElement* oldHoverRenderer = m_lastElementUnderMouse ? m_lastElementUnderMouse->renderer() : nullptr;
2536 RenderElement* newHoverRenderer = m_elementUnderMouse ? m_elementUnderMouse->renderer() : nullptr;
2537 RenderElement* ancestor = nearestCommonHoverAncestor(oldHoverRenderer, newHoverRenderer);
2539 Vector<Ref<Element>, 32> leftElementsChain;
2540 if (oldHoverRenderer) {
2541 for (RenderElement* curr = oldHoverRenderer; curr && curr != ancestor; curr = curr->hoverAncestor()) {
2542 if (Element* element = curr->element())
2543 leftElementsChain.append(*element);
2546 // If the old hovered element is not null but it's renderer is, it was probably detached.
2547 // In this case, the old hovered element (and its ancestors) must be updated, to ensure it's normal style is re-applied.
2548 for (Element* element = m_lastElementUnderMouse.get(); element; element = element->parentElement())
2549 leftElementsChain.append(*element);
2552 Vector<Ref<Element>, 32> enteredElementsChain;
2553 const Element* ancestorElement = ancestor ? ancestor->element() : nullptr;
2554 for (RenderElement* curr = newHoverRenderer; curr; curr = curr->hoverAncestor()) {
2555 if (Element *element = curr->element()) {
2556 if (element == ancestorElement)
2558 enteredElementsChain.append(*element);
2562 // Send mouseout event to the old node.
2563 if (m_lastElementUnderMouse)
2564 m_lastElementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoutEvent, 0, m_elementUnderMouse.get());
2566 // Send mouseleave to the node hierarchy no longer under the mouse.
2567 for (auto& chain : leftElementsChain) {
2568 if (hasCapturingMouseLeaveListener || chain->hasEventListeners(eventNames().mouseleaveEvent))
2569 chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseleaveEvent, 0, m_elementUnderMouse.get());
2572 // Send mouseover event to the new node.
2573 if (m_elementUnderMouse)
2574 m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoverEvent, 0, m_lastElementUnderMouse.get());
2576 // Send mouseleave event to the nodes hierarchy under the mouse.
2577 for (auto& chain : enteredElementsChain) {
2578 if (hasCapturingMouseEnterListener || chain->hasEventListeners(eventNames().mouseenterEvent))
2579 chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseenterEvent, 0, m_lastElementUnderMouse.get());
2582 m_lastElementUnderMouse = m_elementUnderMouse;
2586 bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& platformMouseEvent, bool setUnder)
2588 Ref<Frame> protectedFrame(m_frame);
2590 if (auto* view = m_frame.view())
2591 view->disableLayerFlushThrottlingTemporarilyForInteraction();
2593 updateMouseEventTargetNode(targetNode, platformMouseEvent, setUnder);
2595 if (m_elementUnderMouse && !m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventType, clickCount))
2598 if (eventType != eventNames().mousedownEvent)
2601 // If clicking on a frame scrollbar, do not make any change to which element is focused.
2602 auto* view = m_frame.view();
2603 if (view && view->scrollbarAtPoint(platformMouseEvent.position()))
2606 // The layout needs to be up to date to determine if an element is focusable.
2607 m_frame.document()->updateLayoutIgnorePendingStylesheets();
2609 // Remove focus from the currently focused element when a link or button is clicked.
2610 // This is expected by some sites that rely on change event handlers running
2611 // from form fields before the button click is processed, behavior that was inherited
2612 // from the user interface of Windows, where pushing a button moves focus to the button.
2614 // Walk up the DOM tree to search for an element to focus.
2616 for (element = m_elementUnderMouse.get(); element; element = element->parentOrShadowHostElement()) {
2617 if (element->isMouseFocusable())
2621 // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus an
2622 // element on mouse down if it's selected and inside a focused element. It will be
2623 // focused if the user does a mouseup over it, however, because the mouseup
2624 // will set a selection inside it, which will also set the focused element.
2625 if (element && m_frame.selection().isRange()) {
2626 if (auto range = m_frame.selection().toNormalizedRange()) {
2627 auto result = range->compareNode(*element);
2628 if (!result.hasException() && result.releaseReturnValue() == Range::NODE_INSIDE && element->isDescendantOf(m_frame.document()->focusedElement()))
2633 // Only change the focus when clicking scrollbars if it can be transferred to a mouse focusable node.
2634 if (!element && isInsideScrollbar(platformMouseEvent.position()))
2637 // If focus shift is blocked, we eat the event.
2638 auto* page = m_frame.page();
2639 if (page && !page->focusController().setFocusedElement(element, m_frame))
2645 bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const
2647 if (RenderView* renderView = m_frame.contentRenderer()) {
2648 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
2649 HitTestResult result(windowPoint);
2650 renderView->hitTest(request, result);
2651 return result.scrollbar();
2657 #if !PLATFORM(GTK) && !PLATFORM(WPE)
2659 bool EventHandler::shouldSwapScrollDirection(const HitTestResult&, const PlatformWheelEvent&) const
2668 void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&, const HitTestResult&, RefPtr<Element>&, RefPtr<ContainerNode>&, WeakPtr<ScrollableArea>&, bool&)
2672 void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& event)
2674 if (auto* page = m_frame.page())
2675 page->wheelEventDeltaFilter()->updateFromDelta(FloatSize(event.deltaX(), event.deltaY()));
2678 bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& event, ContainerNode*, const WeakPtr<ScrollableArea>&)
2680 Ref<Frame> protectedFrame(m_frame);
2682 // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
2683 FrameView* view = m_frame.view();
2685 bool didHandleEvent = view ? view->wheelEvent(event) : false;
2686 m_isHandlingWheelEvent = false;
2687 return didHandleEvent;
2690 bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode*)
2695 void EventHandler::platformNotifyIfEndGesture(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&)
2699 void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&)
2701 clearLatchedState();
2706 IntPoint EventHandler::targetPositionInWindowForSelectionAutoscroll() const
2708 return m_lastKnownMousePosition;
2711 #endif // !PLATFORM(IOS)
2713 #endif // !PLATFORM(MAC)
2717 bool EventHandler::shouldUpdateAutoscroll()
2719 return mousePressed();
2722 #endif // !PLATFORM(IOS)
2724 Widget* EventHandler::widgetForEventTarget(Element* eventTarget)
2729 auto* target = eventTarget->renderer();
2730 if (!is<RenderWidget>(target))
2733 return downcast<RenderWidget>(*target).widget();
2736 static WeakPtr<Widget> widgetForElement(const Element& element)
2738 auto target = element.renderer();
2739 if (!is<RenderWidget>(target) || !downcast<RenderWidget>(*target).widget())
2742 return makeWeakPtr(*downcast<RenderWidget>(*target).widget());
2745 bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr<Widget>& widget, const WeakPtr<ScrollableArea>& scrollableArea, ContainerNode* scrollableContainer)
2747 m_isHandlingWheelEvent = false;
2749 // We do another check on the widget because the event handler can run JS which results in the frame getting destroyed.
2754 scrollableArea->setScrolledProgrammatically(false);
2756 platformNotifyIfEndGesture(event, scrollableArea);
2758 if (!widget->platformWidget())
2761 return platformCompletePlatformWidgetWheelEvent(event, *widget.get(), scrollableContainer);
2764 bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event)
2766 RenderView* renderView = m_frame.contentRenderer();
2770 Ref<Frame> protectedFrame(m_frame);
2771 RefPtr<FrameView> protector(m_frame.view());
2773 FrameView* view = m_frame.view();
2777 #if ENABLE(POINTER_LOCK)
2778 if (m_frame.page()->pointerLockController().isLocked()) {
2779 m_frame.page()->pointerLockController().dispatchLockedWheelEvent(event);
2784 m_isHandlingWheelEvent = true;
2785 setFrameWasScrolledByUser();
2787 HitTestRequest request;
2788 HitTestResult result(view->windowToContents(event.position()));
2789 renderView->hitTest(request, result);
2791 RefPtr<Element> element = result.targetElement();
2792 RefPtr<ContainerNode> scrollableContainer;
2793 WeakPtr<ScrollableArea> scrollableArea;
2794 bool isOverWidget = result.isOverWidget();
2795 platformPrepareForWheelEvents(event, result, element, scrollableContainer, scrollableArea, isOverWidget);
2798 if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone && m_frame.page())
2799 m_frame.page()->resetLatchingState();
2802 // FIXME: It should not be necessary to do this mutation here.
2803 // Instead, the handlers should know convert vertical scrolls appropriately.
2804 PlatformWheelEvent adjustedEvent = shouldSwapScrollDirection(result, event) ? event.copySwappingDirection() : event;
2805 platformRecordWheelEvent(adjustedEvent);
2809 if (WeakPtr<Widget> widget = widgetForElement(*element)) {
2810 if (widgetDidHandleWheelEvent(event, *widget.get()))
2811 return completeWidgetWheelEvent(adjustedEvent, widget, scrollableArea, scrollableContainer.get());
2815 if (!element->dispatchWheelEvent(adjustedEvent)) {
2816 m_isHandlingWheelEvent = false;
2817 if (scrollableArea && scrollableArea->isScrolledProgrammatically()) {
2818 // Web developer is controlling scrolling, so don't attempt to latch.
2819 clearLatchedState();
2820 scrollableArea->setScrolledProgrammatically(false);
2823 platformNotifyIfEndGesture(adjustedEvent, scrollableArea);
2829 scrollableArea->setScrolledProgrammatically(false);
2831 bool handledEvent = platformCompleteWheelEvent(adjustedEvent, scrollableContainer.get(), scrollableArea);
2832 platformNotifyIfEndGesture(adjustedEvent, scrollableArea);
2833 return handledEvent;
2836 void EventHandler::clearLatchedState()
2838 auto* page = m_frame.page();
2843 page->resetLatchingState();
2845 if (auto filter = page->wheelEventDeltaFilter())
2846 filter->endFilteringDeltas();
2849 void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent& wheelEvent)
2854 auto protectedFrame = makeRef(m_frame);
2856 FloatSize filteredPlatformDelta(wheelEvent.deltaX(), wheelEvent.deltaY());
2857 FloatSize filteredVelocity;
2858 if (auto platformWheelEvent = wheelEvent.underlyingPlatformEvent()) {
2859 filteredPlatformDelta.setWidth(platformWheelEvent->deltaX());
2860 filteredPlatformDelta.setHeight(platformWheelEvent->deltaY());
2864 ScrollLatchingState* latchedState = m_frame.page() ? m_frame.page()->latchingState() : nullptr;
2865 Element* stopElement = latchedState ? latchedState->previousWheelScrolledElement() : nullptr;
2867 if (m_frame.page() && m_frame.page()->wheelEventDeltaFilter()->isFilteringDeltas()) {
2868 filteredPlatformDelta = m_frame.page()->wheelEventDeltaFilter()->filteredDelta();
2869 filteredVelocity = m_frame.page()->wheelEventDeltaFilter()->filteredVelocity();
2872 Element* stopElement = nullptr;
2875 if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &stopElement, filteredPlatformDelta, filteredVelocity))
2876 wheelEvent.setDefaultHandled();
2879 if (latchedState && !latchedState->wheelEventElement())
2880 latchedState->setPreviousWheelScrolledElement(stopElement);
2884 #if ENABLE(CONTEXT_MENUS)
2885 bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event)
2887 Ref<Frame> protectedFrame(m_frame);
2889 Document* doc = m_frame.document();
2890 FrameView* view = m_frame.view();
2894 // Clear mouse press state to avoid initiating a drag while context menu is up.
2895 m_mousePressed = false;
2897 LayoutPoint viewportPos = view->windowToContents(event.position());
2898 HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
2899 MouseEventWithHitTestResults mouseEvent = doc->prepareMouseEvent(request, viewportPos, event);
2901 // Do not show context menus when clicking on scrollbars.
2902 if (mouseEvent.scrollbar() || view->scrollbarAtPoint(event.position()))
2905 if (m_frame.editor().behavior().shouldSelectOnContextualMenuClick()
2906 && !m_frame.selection().contains(viewportPos)
2907 // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse.
2908 // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items
2909 // available for text selections. But only if we're above text.
2910 && (m_frame.selection().selection().isContentEditable() || (mouseEvent.targetNode() && mouseEvent.targetNode()->isTextNode()))) {
2911 m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2912 selectClosestContextualWordOrLinkFromMouseEvent(mouseEvent);
2915 swallowEvent = !dispatchMouseEvent(eventNames().contextmenuEvent, mouseEvent.targetNode(), true, 0, event, false);
2917 return swallowEvent;
2920 bool EventHandler::sendContextMenuEventForKey()
2922 Ref<Frame> protectedFrame(m_frame);
2924 FrameView* view = m_frame.view();
2928 Document* doc = m_frame.document();
2932 // Clear mouse press state to avoid initiating a drag while context menu is up.
2933 m_mousePressed = false;
2935 static const int kContextMenuMargin = 1;
2938 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
2940 int rightAligned = 0;
2944 Element* focusedElement = doc->focusedElement();
2945 const VisibleSelection& selection = m_frame.selection().selection();
2946 Position start = selection.start();
2948 if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) {
2949 RefPtr<Range> selectionRange = selection.toNormalizedRange();
2950 IntRect firstRect = m_frame.editor().firstRectForRange(selectionRange.get());
2952 int x = rightAligned ? firstRect.maxX() : firstRect.x();
2953 // In a multiline edit, firstRect.maxY() would endup on the next line, so -1.
2954 int y = firstRect.maxY() ? firstRect.maxY() - 1 : 0;
2955 location = IntPoint(x, y);
2956 } else if (focusedElement) {
2957 RenderBoxModelObject* box = focusedElement->renderBoxModelObject();
2961 IntRect boundingBoxRect = box->absoluteBoundingBoxRect(true);
2962 location = IntPoint(boundingBoxRect.x(), boundingBoxRect.maxY() - 1);
2964 location = IntPoint(
2965 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
2966 kContextMenuMargin);
2969 m_frame.view()->setCursor(pointerCursor());
2971 IntPoint position = view->contentsToRootView(location);
2972 IntPoint globalPosition = view->hostWindow()->rootViewToScreen(IntRect(position, IntSize())).location();
2974 Node* targetNode = doc->focusedElement();
2978 // Use the focused node as the target for hover and active.
2979 HitTestResult result(position);
2980 result.setInnerNode(targetNode);
2981 doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent, result.targetElement());
2983 // The contextmenu event is a mouse event even when invoked using the keyboard.
2984 // This is required for web compatibility.
2987 PlatformEvent::Type eventType = PlatformEvent::MouseReleased;
2989 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
2992 PlatformMouseEvent platformMouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WallTime::now(), ForceAtClick, NoTap);
2994 return sendContextMenuEvent(platformMouseEvent);
2996 #endif // ENABLE(CONTEXT_MENUS)
2998 void EventHandler::scheduleHoverStateUpdate()
3000 if (!m_hoverTimer.isActive())
3001 m_hoverTimer.startOneShot(0_s);
3004 #if ENABLE(CURSOR_SUPPORT)
3005 void EventHandler::scheduleCursorUpdate()
3007 if (!m_cursorUpdateTimer.isActive())
3008 m_cursorUpdateTimer.startOneShot(cursorUpdateInterval);
3012 void EventHandler::dispatchFakeMouseMoveEventSoon()
3014 #if !ENABLE(IOS_TOUCH_EVENTS)
3018 if (m_mousePositionIsUnknown)
3021 if (Page* page = m_frame.page()) {
3022 if (!page->chrome().client().shouldDispatchFakeMouseMoveEvents())
3026 // If the content has ever taken longer than fakeMouseMoveShortInterval we
3027 // reschedule the timer and use a longer time. This will cause the content
3028 // to receive these moves only after the user is done scrolling, reducing
3029 // pauses during the scroll.
3030 if (m_fakeMouseMoveEventTimer.isActive())
3031 m_fakeMouseMoveEventTimer.stop();
3032 m_fakeMouseMoveEventTimer.startOneShot(m_maxMouseMovedDuration > fakeMouseMoveDurationThreshold ? fakeMouseMoveLongInterval : fakeMouseMoveShortInterval);
3036 void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad)
3038 #if ENABLE(IOS_TOUCH_EVENTS)
3041 FrameView* view = m_frame.view();
3045 if (!quad.containsPoint(view->windowToContents(m_lastKnownMousePosition)))
3048 dispatchFakeMouseMoveEventSoon();
3052 #if !ENABLE(IOS_TOUCH_EVENTS)
3053 void EventHandler::cancelFakeMouseMoveEvent()
3055 m_fakeMouseMoveEventTimer.stop();
3058 void EventHandler::fakeMouseMoveEventTimerFired()
3060 ASSERT(!m_mousePressed);
3062 FrameView* view = m_frame.view();
3066 if (!m_frame.page() || !m_frame.page()->isVisible() || !m_frame.page()->focusController().isActive())
3073 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
3074 PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), 0, NoTap);
3075 mouseMoved(fakeMouseMoveEvent);
3077 #endif // !ENABLE(IOS_TOUCH_EVENTS)
3079 void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet)
3081 m_frameSetBeingResized = frameSet;
3084 void EventHandler::resizeLayerDestroyed()
3086 ASSERT(m_resizeLayer);
3087 m_resizeLayer = nullptr;
3090 void EventHandler::hoverTimerFired()
3092 m_hoverTimer.stop();
3094 ASSERT(m_frame.document());
3096 Ref<Frame> protectedFrame(m_frame);
3098 if (RenderView* renderView = m_frame.contentRenderer()) {
3099 if (FrameView* view = m_frame.view()) {
3100 HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent);
3101 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
3102 renderView->hitTest(request, result);
3103 m_frame.document()->updateHoverActiveState(request, result.targetElement());
3108 bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& event)
3110 // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do.
3111 // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and
3112 // lower case variants are present in a document, the correct element is matched based on Shift key state.
3113 // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively.
3114 ASSERT(!accessKeyModifiers().contains(PlatformEvent::Modifier::ShiftKey));
3116 if ((event.modifiers() - PlatformEvent::Modifier::ShiftKey) != accessKeyModifiers())
3118 Element* element = m_frame.document()->getElementByAccessKey(event.unmodifiedText());
3121 element->accessKeyAction(false);
3126 bool EventHandler::needsKeyboardEventDisambiguationQuirks() const
3132 #if ENABLE(FULLSCREEN_API)
3133 bool EventHandler::isKeyEventAllowedInFullScreen(const PlatformKeyboardEvent& keyEvent) const
3135 Document* document = m_frame.document();
3136 if (document->webkitFullScreenKeyboardInputAllowed())
3139 if (keyEvent.type() == PlatformKeyboardEvent::Char) {
3140 if (keyEvent.text().length() != 1)
3142 UChar character = keyEvent.text()[0];
3143 return character == ' ';
3146 int keyCode = keyEvent.windowsVirtualKeyCode();
3147 return (keyCode >= VK_BACK && keyCode <= VK_CAPITAL)
3148 || (keyCode >= VK_SPACE && keyCode <= VK_DELETE)
3149 || (keyCode >= VK_OEM_1 && keyCode <= VK_OEM_PLUS)
3150 || (keyCode >= VK_MULTIPLY && keyCode <= VK_OEM_8);
3154 bool EventHandler::keyEvent(const PlatformKeyboardEvent& keyEvent)
3156 Document* topDocument = m_frame.document() ? &m_frame.document()->topDocument() : nullptr;
3157 MonotonicTime savedLastHandledUserGestureTimestamp;
3158 bool savedUserDidInteractWithPage = topDocument ? topDocument->userDidInteractWithPage() : false;
3160 if (m_frame.document())
3161 savedLastHandledUserGestureTimestamp = m_frame.document()->lastHandledUserGestureTimestamp();
3163 bool wasHandled = internalKeyEvent(keyEvent);
3165 // If the key event was not handled, do not treat it as user interaction with the page.
3168 topDocument->setUserDidInteractWithPage(savedUserDidInteractWithPage);
3170 ResourceLoadObserver::shared().logUserInteractionWithReducedTimeResolution(*topDocument);
3173 if (!wasHandled && m_frame.document())
3174 m_frame.document()->updateLastHandledUserGestureTimestamp(savedLastHandledUserGestureTimestamp);
3179 bool EventHandler::internalKeyEvent(const PlatformKeyboardEvent& initialKeyEvent)
3181 Ref<Frame> protectedFrame(m_frame);
3182 RefPtr<FrameView> protector(m_frame.view());
3184 LOG(Editing, "EventHandler %p keyEvent (text %s keyIdentifier %s)", this, initialKeyEvent.text().utf8().data(), initialKeyEvent.keyIdentifier().utf8().data());
3186 #if ENABLE(POINTER_LOCK)
3187 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE && m_frame.page()->pointerLockController().element()) {
3188 m_frame.page()->pointerLockController().requestPointerUnlockAndForceCursorVisible();
3192 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) {
3193 if (auto* page = m_frame.page()) {
3194 if (auto* validationMessageClient = page->validationMessageClient())
3195 validationMessageClient->hideAnyValidationMessage();
3199 #if ENABLE(FULLSCREEN_API)
3200 if (m_frame.document()->webkitIsFullScreen()) {
3201 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) {
3202 m_frame.document()->webkitCancelFullScreen();
3206 if (!isKeyEventAllowedInFullScreen(initialKeyEvent))
3211 if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL) {
3212 if (auto* element = m_frame.document()->focusedElement()) {
3213 if (is<HTMLInputElement>(*element))
3214 downcast<HTMLInputElement>(*element).capsLockStateMayHaveChanged();
3218 #if ENABLE(PAN_SCROLLING)
3219 if (m_frame.mainFrame().eventHandler().panScrollInProgress()) {
3220 // If a key is pressed while the panScroll is in progress then we want to stop
3221 if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent.type() == PlatformEvent::RawKeyDown)
3222 stopAutoscrollTimer();
3224 // If we were in panscroll mode, we swallow the key event
3229 // Check for cases where we are too early for events -- possible unmatched key up
3230 // from pressing return in the location bar.
3231 RefPtr<Element> element = eventTargetElementForDocument(m_frame.document());
3235 UserGestureType gestureType = UserGestureType::Other;
3236 if (initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE)
3237 gestureType = UserGestureType::EscapeKey;
3239 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document(), gestureType, UserGestureIndicator::ProcessInteractionStyle::Delayed);
3240 UserTypingGestureIndicator typingGestureIndicator(m_frame);
3242 if (FrameView* view = m_frame.view())
3243 view->disableLayerFlushThrottlingTemporarilyForInteraction();
3245 // FIXME (bug 68185): this call should be made at another abstraction layer
3246 m_frame.loader().resetMultipleFormSubmissionProtection();
3248 // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match.
3249 // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict
3250 // with access keys. Then we dispatch keydown, but suppress its default handling.
3251 // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatching a keypress event for WM_SYSCHAR messages.
3252 // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events.
3253 bool matchedAnAccessKey = false;
3254 if (initialKeyEvent.type() == PlatformEvent::KeyDown)
3255 matchedAnAccessKey = handleAccessKey(initialKeyEvent);
3257 // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch.
3258 if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char)
3259 return !element->dispatchKeyEvent(initialKeyEvent);
3261 bool backwardCompatibilityMode = needsKeyboardEventDisambiguationQuirks();
3263 PlatformKeyboardEvent keyDownEvent = initialKeyEvent;
3264 if (keyDownEvent.type() != PlatformEvent::RawKeyDown)
3265 keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown, backwardCompatibilityMode);
3266 auto keydown = KeyboardEvent::create(keyDownEvent, &m_frame.windowProxy());
3267 if (matchedAnAccessKey)
3268 keydown->preventDefault();
3269 keydown->setTarget(element);
3271 if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) {
3272 element->dispatchEvent(keydown);
3273 // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame.
3274 bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame();
3275 return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3278 // Run input method in advance of DOM event handling. This may result in the IM
3279 // modifying the page prior the keydown event, but this behaviour is necessary
3280 // in order to match IE:
3281 // 1. preventing default handling of keydown and keypress events has no effect on IM input;
3282 // 2. if an input method handles the event, its keyCode is set to 229 in keydown event.
3283 m_frame.editor().handleInputMethodKeydown(keydown.get());
3285 bool handledByInputMethod = keydown->defaultHandled();
3287 if (handledByInputMethod) {
3288 keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode);
3289 keydown = KeyboardEvent::create(keyDownEvent, &m_frame.windowProxy());
3290 keydown->setTarget(element);
3291 keydown->setDefaultHandled();
3294 if (accessibilityPreventsEventPropagation(keydown))
3295 keydown->stopPropagation();
3297 element->dispatchEvent(keydown);
3298 if (handledByInputMethod)
3301 // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame.
3302 bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame();
3303 bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3304 if (keydownResult && !backwardCompatibilityMode)
3305 return keydownResult;
3307 // Focus may have changed during keydown handling, so refetch element.
3308 // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original element.
3309 if (!keydownResult) {
3310 element = eventTargetElementForDocument(m_frame.document());
3315 PlatformKeyboardEvent keyPressEvent = initialKeyEvent;
3316 keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char, backwardCompatibilityMode);
3317 if (keyPressEvent.text().isEmpty())
3318 return keydownResult;
3319 auto keypress = KeyboardEvent::create(keyPressEvent, &m_frame.windowProxy());
3320 keypress->setTarget(element);
3322 keypress->preventDefault();
3324 keypress->keypressCommands() = keydown->keypressCommands();
3326 element->dispatchEvent(keypress);
3328 return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled();
3331 static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier)
3333 static NeverDestroyed<AtomicString> Down("Down", AtomicString::ConstructFromLiteral);
3334 static NeverDestroyed<AtomicString> Up("Up", AtomicString::ConstructFromLiteral);
3335 static NeverDestroyed<AtomicString> Left("Left", AtomicString::ConstructFromLiteral);
3336 static NeverDestroyed<AtomicString> Right("Right", AtomicString::ConstructFromLiteral);
3338 FocusDirection retVal = FocusDirectionNone;
3340 if (keyIdentifier == Down)
3341 retVal = FocusDirectionDown;
3342 else if (keyIdentifier == Up)
3343 retVal = FocusDirectionUp;
3344 else if (keyIdentifier == Left)
3345 retVal = FocusDirectionLeft;
3346 else if (keyIdentifier == Right)
3347 retVal = FocusDirectionRight;
3352 static void setInitialKeyboardSelection(Frame& frame, SelectionDirection direction)
3354 Document* document = frame.document();
3358 FrameSelection& selection = frame.selection();
3360 if (!selection.isNone())
3363 Element* focusedElement = document->focusedElement();
3364 VisiblePosition visiblePosition;
3366 switch (direction) {
3367 case DirectionBackward:
3370 visiblePosition = VisiblePosition(positionBeforeNode(focusedElement));
3372 visiblePosition = endOfDocument(document);
3374 case DirectionForward:
3375 case DirectionRight:
3377 visiblePosition = VisiblePosition(positionAfterNode(focusedElement));
3379 visiblePosition = startOfDocument(document);
3383 AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
3384 selection.setSelection(visiblePosition, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent);
3387 static void handleKeyboardSelectionMovement(Frame& frame, KeyboardEvent& event)
3389 FrameSelection& selection = frame.selection();
3391 bool isCommanded = event.getModifierState("Meta");
3392 bool isOptioned = event.getModifierState("Alt");
3393 bool isSelection = !selection.isNone();
3395 FrameSelection::EAlteration alternation = event.getModifierState("Shift") ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove;
3396 SelectionDirection direction = DirectionForward;
3397 TextGranularity granularity = CharacterGranularity;
3399 switch (focusDirectionForKey(event.keyIdentifier())) {
3400 case FocusDirectionNone:
3402 case FocusDirectionForward:
3403 case FocusDirectionBackward:
3404 ASSERT_NOT_REACHED();
3406 case FocusDirectionUp:
3407 direction = DirectionBackward;
3408 granularity = isCommanded ? DocumentBoundary : LineGranularity;
3410 case FocusDirectionDown:
3411 direction = DirectionForward;
3412 granularity = isCommanded ? DocumentBoundary : LineGranularity;
3414 case FocusDirectionLeft:
3415 direction = DirectionLeft;
3416 granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity;
3418 case FocusDirectionRight:
3419 direction = DirectionRight;
3420 granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity;
3425 selection.modify(alternation, direction, granularity, UserTriggered);
3427 setInitialKeyboardSelection(frame, direction);
3429 event.setDefaultHandled();
3432 void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent& event)
3434 if (event.type() == eventNames().keydownEvent) {
3435 if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
3436 handleKeyboardSelectionMovement(m_frame, event);
3440 bool EventHandler::accessibilityPreventsEventPropagation(KeyboardEvent& event)
3443 if (!AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
3446 if (!m_frame.settings().preventKeyboardDOMEventDispatch())
3449 // Check for key events that are relevant to accessibility: tab and arrows keys that change focus
3450 if (event.keyIdentifier() == "U+0009")
3452 FocusDirection direction = focusDirectionForKey(event.keyIdentifier());
3453 if (direction != FocusDirectionNone)
3456 UNUSED_PARAM(event);
3461 void EventHandler::defaultKeyboardEventHandler(KeyboardEvent& event)
3463 Ref<Frame> protectedFrame(m_frame);
3465 if (event.type() == eventNames().keydownEvent) {
3466 m_frame.editor().handleKeyboardEvent(event);
3467 if (event.defaultHandled())
3469 if (event.keyIdentifier() == "U+0009")
3470 defaultTabEventHandler(event);
3471 else if (event.keyIdentifier() == "U+0008")
3472 defaultBackspaceEventHandler(event);
3474 FocusDirection direction = focusDirectionForKey(event.keyIdentifier());
3475 if (direction != FocusDirectionNone)
3476 defaultArrowEventHandler(direction, event);
3479 handleKeyboardSelectionMovementForAccessibility(event);
3481 if (event.type() == eventNames().keypressEvent) {
3482 m_frame.editor().handleKeyboardEvent(event);
3483 if (event.defaultHandled())
3485 if (event.charCode() == ' ')
3486 defaultSpaceEventHandler(event);
3490 #if ENABLE(DRAG_SUPPORT)
3491 bool EventHandler::dragHysteresisExceeded(const IntPoint& floatDragViewportLocation) const
3493 FloatPoint dragViewportLocation(floatDragViewportLocation.x(), floatDragViewportLocation.y());
3494 return dragHysteresisExceeded(dragViewportLocation);
3497 bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation) const