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 if (node && node->renderer() && (caretBrowsing || node->hasEditableStyle())) {
1054 VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint(), nullptr);
1055 newSelection = VisibleSelection(pos);
1058 setSelectionIfNeeded(m_frame.selection(), newSelection);
1063 if (event.event().button() == MiddleButton) {
1064 // Ignore handled, since we want to paste to where the caret was placed anyway.
1065 handled = handlePasteGlobalSelection(event.event()) || handled;
1071 #if ENABLE(PAN_SCROLLING)
1073 void EventHandler::didPanScrollStart()
1075 m_autoscrollController->didPanScrollStart();
1078 void EventHandler::didPanScrollStop()
1080 m_autoscrollController->didPanScrollStop();
1083 void EventHandler::startPanScrolling(RenderElement& renderer)
1086 if (!is<RenderBox>(renderer))
1088 m_autoscrollController->startPanScrolling(&downcast<RenderBox>(renderer), lastKnownMousePosition());
1093 #endif // ENABLE(PAN_SCROLLING)
1095 RenderBox* EventHandler::autoscrollRenderer() const
1097 return m_autoscrollController->autoscrollRenderer();
1100 void EventHandler::updateAutoscrollRenderer()
1102 m_autoscrollController->updateAutoscrollRenderer();
1105 bool EventHandler::autoscrollInProgress() const
1107 return m_autoscrollController->autoscrollInProgress();
1110 bool EventHandler::panScrollInProgress() const
1112 return m_autoscrollController->panScrollInProgress();
1115 #if ENABLE(DRAG_SUPPORT)
1116 DragSourceAction EventHandler::updateDragSourceActionsAllowed() const
1118 Page* page = m_frame.page();
1120 return DragSourceActionNone;
1122 FrameView* view = m_frame.view();
1124 return DragSourceActionNone;
1126 return page->dragController().delegateDragSourceAction(view->contentsToRootView(m_mouseDownPos));
1128 #endif // ENABLE(DRAG_SUPPORT)
1130 HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) const
1132 ASSERT((hitType & HitTestRequest::CollectMultipleElements) || padding.isEmpty());
1134 Ref<Frame> protectedFrame(m_frame);
1136 // We always send hitTestResultAtPoint to the main frame if we have one,
1137 // otherwise we might hit areas that are obscured by higher frames.
1138 if (!m_frame.isMainFrame()) {
1139 Frame& mainFrame = m_frame.mainFrame();
1140 FrameView* frameView = m_frame.view();
1141 FrameView* mainView = mainFrame.view();
1142 if (frameView && mainView) {
1143 IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point)));
1144 return mainFrame.eventHandler().hitTestResultAtPoint(mainFramePoint, hitType, padding);
1148 unsigned nonNegativePaddingWidth = std::max<LayoutUnit>(0, padding.width()).toUnsigned();
1149 unsigned nonNegativePaddingHeight = std::max<LayoutUnit>(0, padding.height()).toUnsigned();
1151 // We should always start hit testing a clean tree.
1152 if (auto* frameView = m_frame.view())
1153 frameView->updateLayoutAndStyleIfNeededRecursive();
1155 HitTestResult result(point, nonNegativePaddingHeight, nonNegativePaddingWidth, nonNegativePaddingHeight, nonNegativePaddingWidth);
1156 RenderView* renderView = m_frame.contentRenderer();
1160 // hitTestResultAtPoint is specifically used to hitTest into all frames, thus it always allows child frame content.
1161 HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent);
1162 renderView->hitTest(request, result);
1163 if (!request.readOnly())
1164 m_frame.document()->updateHoverActiveState(request, result.targetElement());
1166 if (request.disallowsUserAgentShadowContent())
1167 result.setToNonUserAgentShadowAncestor();
1172 void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed)
1174 m_autoscrollController->stopAutoscrollTimer(rendererIsBeingDestroyed);
1177 bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
1179 Node* node = startingNode;
1182 node = m_frame.document()->focusedElement();
1185 node = m_mousePressNode.get();
1188 auto r = node->renderer();
1189 if (r && !r->isListBox() && r->enclosingBox().scroll(direction, granularity)) {
1190 setFrameWasScrolledByUser();
1198 bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode)
1200 Node* node = startingNode;
1203 node = m_frame.document()->focusedElement();
1206 node = m_mousePressNode.get();
1209 auto r = node->renderer();
1210 if (r && !r->isListBox() && r->enclosingBox().logicalScroll(direction, granularity)) {
1211 setFrameWasScrolledByUser();
1219 bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
1221 Ref<Frame> protectedFrame(m_frame);
1223 // The layout needs to be up to date to determine if we can scroll. We may be
1224 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
1225 m_frame.document()->updateLayoutIgnorePendingStylesheets();
1226 if (scrollOverflow(direction, granularity, startingNode))
1228 Frame* frame = &m_frame;
1229 FrameView* view = frame->view();
1230 if (view && view->scroll(direction, granularity))
1232 frame = frame->tree().parent();
1235 return frame->eventHandler().scrollRecursively(direction, granularity, m_frame.ownerElement());
1238 bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode)
1240 Ref<Frame> protectedFrame(m_frame);
1242 // The layout needs to be up to date to determine if we can scroll. We may be
1243 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
1244 m_frame.document()->updateLayoutIgnorePendingStylesheets();
1245 if (logicalScrollOverflow(direction, granularity, startingNode))
1247 Frame* frame = &m_frame;
1248 FrameView* view = frame->view();
1250 bool scrolled = false;
1252 // Mac also resets the scroll position in the inline direction.
1253 if (granularity == ScrollByDocument && view && view->logicalScroll(ScrollInlineDirectionBackward, ScrollByDocument))
1256 if (view && view->logicalScroll(direction, granularity))
1262 frame = frame->tree().parent();
1266 return frame->eventHandler().logicalScrollRecursively(direction, granularity, m_frame.ownerElement());
1269 IntPoint EventHandler::lastKnownMousePosition() const
1271 return m_lastKnownMousePosition;
1274 Frame* EventHandler::subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult)
1276 if (!hitTestResult.isOverWidget())
1278 return subframeForTargetNode(hitTestResult.targetNode());
1281 Frame* EventHandler::subframeForTargetNode(Node* node)
1286 auto renderer = node->renderer();
1287 if (!is<RenderWidget>(renderer))
1290 Widget* widget = downcast<RenderWidget>(*renderer).widget();
1291 if (!is<FrameView>(widget))
1294 return &downcast<FrameView>(*widget).frame();
1297 #if ENABLE(CURSOR_SUPPORT)
1298 static bool isSubmitImage(Node* node)
1300 return is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*node).isImageButton();
1303 // Returns true if the node's editable block is not current focused for editing
1304 static bool nodeIsNotBeingEdited(const Node& node, const Frame& frame)
1306 return frame.selection().selection().rootEditableElement() != node.rootEditableElement();
1309 bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey)
1314 bool editable = node->hasEditableStyle();
1315 bool editableLinkEnabled = false;
1317 // If the link is editable, then we need to check the settings to see whether or not the link should be followed
1319 switch (m_frame.settings().editableLinkBehavior()) {
1321 case EditableLinkDefaultBehavior:
1322 case EditableLinkAlwaysLive:
1323 editableLinkEnabled = true;
1326 case EditableLinkNeverLive:
1327 editableLinkEnabled = false;
1330 case EditableLinkLiveWhenNotFocused:
1331 editableLinkEnabled = nodeIsNotBeingEdited(*node, m_frame) || shiftKey;
1334 case EditableLinkOnlyLiveWithShiftKey:
1335 editableLinkEnabled = shiftKey;
1340 return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled));
1343 void EventHandler::cursorUpdateTimerFired()
1345 ASSERT(m_frame.document());
1349 void EventHandler::updateCursor()
1351 if (m_mousePositionIsUnknown)
1354 FrameView* view = m_frame.view();
1358 RenderView* renderView = view->renderView();
1362 if (!view->shouldSetCursor())
1369 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
1371 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::AllowFrameScrollbars);
1372 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
1373 renderView->hitTest(request, result);
1375 updateCursor(*view, result, shiftKey);
1378 void EventHandler::updateCursor(FrameView& view, const HitTestResult& result, bool shiftKey)
1380 if (auto optionalCursor = selectCursor(result, shiftKey)) {
1381 m_currentMouseCursor = WTFMove(optionalCursor.value());
1382 view.setCursor(m_currentMouseCursor);
1386 std::optional<Cursor> EventHandler::selectCursor(const HitTestResult& result, bool shiftKey)
1388 if (m_resizeLayer && m_resizeLayer->inResizeMode())
1389 return std::nullopt;
1391 if (!m_frame.page())
1392 return std::nullopt;
1394 #if ENABLE(PAN_SCROLLING)
1395 if (m_frame.mainFrame().eventHandler().panScrollInProgress())
1396 return std::nullopt;
1399 Ref<Frame> protectedFrame(m_frame);
1401 // Use always pointer cursor for scrollbars.
1402 if (result.scrollbar()) {
1403 #if ENABLE(CURSOR_VISIBILITY)
1404 cancelAutoHideCursorTimer();
1406 return pointerCursor();
1409 Node* node = result.targetNode();
1411 return std::nullopt;
1413 auto renderer = node->renderer();
1414 auto* style = renderer ? &renderer->style() : nullptr;
1415 bool horizontalText = !style || style->isHorizontalWritingMode();
1416 const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCursor();
1418 #if ENABLE(CURSOR_VISIBILITY)
1419 if (style && style->cursorVisibility() == CursorVisibility::AutoHide)
1420 startAutoHideCursorTimer();
1422 cancelAutoHideCursorTimer();
1426 Cursor overrideCursor;
1427 switch (renderer->getCursor(roundedIntPoint(result.localPoint()), overrideCursor)) {
1428 case SetCursorBasedOnStyle:
1431 return overrideCursor;
1432 case DoNotSetCursor:
1433 return std::nullopt;
1437 if (style && style->cursors()) {
1438 const CursorList* cursors = style->cursors();
1439 for (unsigned i = 0; i < cursors->size(); ++i) {
1440 StyleImage* styleImage = (*cursors)[i].image();
1443 CachedImage* cachedImage = styleImage->cachedImage();
1446 float scale = styleImage->imageScaleFactor();
1447 // Get hotspot and convert from logical pixels to physical pixels.
1448 IntPoint hotSpot = (*cursors)[i].hotSpot();
1449 FloatSize size = cachedImage->imageForRenderer(renderer)->size();
1450 if (cachedImage->errorOccurred())
1452 // Limit the size of cursors (in UI pixels) so that they cannot be
1453 // used to cover UI elements in chrome.
1454 size.scale(1 / scale);
1455 if (size.width() > maximumCursorSize || size.height() > maximumCursorSize)
1458 Image* image = cachedImage->imageForRenderer(renderer);
1459 #if ENABLE(MOUSE_CURSOR_SCALE)
1460 // Ensure no overflow possible in calculations above.
1461 if (scale < minimumCursorScale)
1463 return Cursor(image, hotSpot, scale);
1466 return Cursor(image, hotSpot);
1467 #endif // ENABLE(MOUSE_CURSOR_SCALE)
1471 // During selection, use an I-beam regardless of the content beneath the cursor.
1472 // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection.
1474 && m_mouseDownMayStartSelect
1475 #if ENABLE(DRAG_SUPPORT)
1476 && !m_mouseDownMayStartDrag
1478 && m_frame.selection().isCaretOrRange()
1479 && !m_capturingMouseEventsElement)
1482 switch (style ? style->cursor() : CursorType::Auto) {
1483 case CursorType::Auto: {
1484 bool editable = node->hasEditableStyle();
1486 if (useHandCursor(node, result.isOverLink(), shiftKey))
1487 return handCursor();
1489 bool inResizer = false;
1491 if (RenderLayer* layer = renderer->enclosingLayer()) {
1492 if (FrameView* view = m_frame.view())
1493 inResizer = layer->isPointInResizeControl(view->windowToContents(roundedIntPoint(result.localPoint())));
1497 if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar())
1499 return pointerCursor();
1501 case CursorType::Default:
1502 return pointerCursor();
1503 case CursorType::None:
1504 return noneCursor();
1505 case CursorType::ContextMenu:
1506 return contextMenuCursor();
1507 case CursorType::Help:
1508 return helpCursor();
1509 case CursorType::Pointer:
1510 return handCursor();
1511 case CursorType::Progress:
1512 return progressCursor();
1513 case CursorType::Wait:
1514 return waitCursor();
1515 case CursorType::Cell:
1516 return cellCursor();
1517 case CursorType::Crosshair:
1518 return crossCursor();
1519 case CursorType::Text:
1520 return iBeamCursor();
1521 case CursorType::VerticalText:
1522 return verticalTextCursor();
1523 case CursorType::Alias:
1524 return aliasCursor();
1525 case CursorType::Copy:
1526 return copyCursor();
1527 case CursorType::Move:
1528 return moveCursor();
1529 case CursorType::NoDrop:
1530 return noDropCursor();
1531 case CursorType::NotAllowed:
1532 return notAllowedCursor();
1533 case CursorType::Grab:
1534 return grabCursor();
1535 case CursorType::Grabbing:
1536 return grabbingCursor();
1537 case CursorType::EResize:
1538 return eastResizeCursor();
1539 case CursorType::NResize:
1540 return northResizeCursor();
1541 case CursorType::NEResize:
1542 return northEastResizeCursor();
1543 case CursorType::NWResize:
1544 return northWestResizeCursor();
1545 case CursorType::SResize:
1546 return southResizeCursor();
1547 case CursorType::SEResize:
1548 return southEastResizeCursor();
1549 case CursorType::SWResize:
1550 return southWestResizeCursor();
1551 case CursorType::WResize:
1552 return westResizeCursor();
1553 case CursorType::EWResize:
1554 return eastWestResizeCursor();
1555 case CursorType::NSResize:
1556 return northSouthResizeCursor();
1557 case CursorType::NESWResize:
1558 return northEastSouthWestResizeCursor();
1559 case CursorType::NWSEResize:
1560 return northWestSouthEastResizeCursor();
1561 case CursorType::ColumnResize:
1562 return columnResizeCursor();
1563 case CursorType::RowResize:
1564 return rowResizeCursor();
1565 case CursorType::AllScroll:
1566 return moveCursor();
1567 case CursorType::ZoomIn:
1568 return zoomInCursor();
1569 case CursorType::ZoomOut:
1570 return zoomOutCursor();
1572 return pointerCursor();
1574 #endif // ENABLE(CURSOR_SUPPORT)
1576 #if ENABLE(CURSOR_VISIBILITY)
1577 void EventHandler::startAutoHideCursorTimer()
1579 Page* page = m_frame.page();
1583 m_autoHideCursorTimer.startOneShot(page->settings().timeWithoutMouseMovementBeforeHidingControls());
1585 #if !ENABLE(IOS_TOUCH_EVENTS)
1586 // The fake mouse move event screws up the auto-hide feature (by resetting the auto-hide timer)
1587 // so cancel any pending fake mouse moves.
1588 if (m_fakeMouseMoveEventTimer.isActive())
1589 m_fakeMouseMoveEventTimer.stop();
1593 void EventHandler::cancelAutoHideCursorTimer()
1595 if (m_autoHideCursorTimer.isActive())
1596 m_autoHideCursorTimer.stop();
1599 void EventHandler::autoHideCursorTimerFired()
1601 FrameView* view = m_frame.view();
1602 if (!view || !view->isActive())
1605 if (auto page = m_frame.page())
1606 page->chrome().setCursorHiddenUntilMouseMoves(true);
1610 static LayoutPoint documentPointForWindowPoint(Frame& frame, const IntPoint& windowPoint)
1612 FrameView* view = frame.view();
1613 // FIXME: Is it really OK to use the wrong coordinates here when view is 0?
1614 // Historically the code would just crash; this is clearly no worse than that.
1615 return view ? view->windowToContents(windowPoint) : windowPoint;
1618 static Scrollbar* scrollbarForMouseEvent(const MouseEventWithHitTestResults& mouseEvent, FrameView* view)
1621 if (auto* scrollbar = view->scrollbarAtPoint(mouseEvent.event().position()))
1624 return mouseEvent.scrollbar();
1628 bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& platformMouseEvent)
1630 Ref<Frame> protectedFrame(m_frame);
1631 RefPtr<FrameView> protector(m_frame.view());
1633 if (InspectorInstrumentation::handleMousePress(m_frame)) {
1638 #if ENABLE(POINTER_LOCK)
1639 if (m_frame.page()->pointerLockController().isLocked()) {
1640 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousedownEvent);
1645 if (m_frame.page()->pageOverlayController().handleMouseEvent(platformMouseEvent))
1648 #if ENABLE(TOUCH_EVENTS)
1649 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
1650 if (defaultPrevented)
1654 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
1656 // FIXME (bug 68185): this call should be made at another abstraction layer
1657 m_frame.loader().resetMultipleFormSubmissionProtection();
1659 #if !ENABLE(IOS_TOUCH_EVENTS)
1660 cancelFakeMouseMoveEvent();
1662 m_mousePressed = true;
1663 m_capturesDragging = true;
1664 setLastKnownMousePosition(platformMouseEvent);
1665 m_mouseDownTimestamp = platformMouseEvent.timestamp();
1666 #if ENABLE(DRAG_SUPPORT)
1667 m_mouseDownMayStartDrag = false;
1669 m_mouseDownMayStartSelect = false;
1670 m_mouseDownMayStartAutoscroll = false;
1671 if (FrameView* view = m_frame.view())
1672 m_mouseDownPos = view->windowToContents(platformMouseEvent.position());
1677 m_mouseDownWasInSubframe = false;
1679 HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
1680 // Save the document point we generate in case the window coordinate is invalidated by what happens
1681 // when we dispatch the event.
1682 LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, platformMouseEvent.position());
1683 MouseEventWithHitTestResults mouseEvent = m_frame.document()->prepareMouseEvent(request, documentPoint, platformMouseEvent);
1685 if (!mouseEvent.targetNode()) {
1690 m_mousePressNode = mouseEvent.targetNode();
1691 m_frame.document()->setFocusNavigationStartingNode(mouseEvent.targetNode());
1693 Scrollbar* scrollbar = scrollbarForMouseEvent(mouseEvent, m_frame.view());
1694 updateLastScrollbarUnderMouse(scrollbar, SetOrClearLastScrollbar::Set);
1695 bool passedToScrollbar = scrollbar && passMousePressEventToScrollbar(mouseEvent, scrollbar);
1697 if (!passedToScrollbar) {
1698 RefPtr<Frame> subframe = subframeForHitTestResult(mouseEvent);
1699 if (subframe && passMousePressEventToSubframe(mouseEvent, subframe.get())) {
1700 // Start capturing future events for this frame. We only do this if we didn't clear
1701 // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop.
1702 m_capturesDragging = subframe->eventHandler().capturesDragging();
1703 if (m_mousePressed && m_capturesDragging) {
1704 m_capturingMouseEventsElement = subframe->ownerElement();
1705 m_eventHandlerWillResetCapturingMouseEventsElement = true;
1712 #if ENABLE(PAN_SCROLLING)
1713 // We store whether pan scrolling is in progress before calling stopAutoscrollTimer()
1714 // because it will set m_autoscrollType to NoAutoscroll on return.
1715 bool isPanScrollInProgress = m_frame.mainFrame().eventHandler().panScrollInProgress();
1716 stopAutoscrollTimer();
1717 if (isPanScrollInProgress) {
1718 // We invalidate the click when exiting pan scrolling so that we don't inadvertently navigate
1719 // away from the current page (e.g. the click was on a hyperlink). See <rdar://problem/6095023>.
1725 m_clickCount = platformMouseEvent.clickCount();
1726 m_clickNode = mouseEvent.targetNode();
1733 if (FrameView* view = m_frame.view()) {
1734 RenderLayer* layer = m_clickNode->renderer() ? m_clickNode->renderer()->enclosingLayer() : 0;
1735 IntPoint p = view->windowToContents(platformMouseEvent.position());
1736 if (layer && layer->isPointInResizeControl(p)) {
1737 layer->setInResizeMode(true);
1738 m_resizeLayer = layer;
1739 m_offsetFromResizeCorner = layer->offsetFromResizeCorner(p);
1745 m_frame.selection().setCaretBlinkingSuspended(true);
1747 bool swallowEvent = !dispatchMouseEvent(eventNames().mousedownEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true);
1748 m_capturesDragging = !swallowEvent || mouseEvent.scrollbar();
1750 // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults
1751 // in case the scrollbar widget was destroyed when the mouse event was handled.
1752 if (mouseEvent.scrollbar()) {
1753 const bool wasLastScrollBar = mouseEvent.scrollbar() == m_lastScrollbarUnderMouse;
1754 mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent);
1755 if (wasLastScrollBar && mouseEvent.scrollbar() != m_lastScrollbarUnderMouse)
1756 m_lastScrollbarUnderMouse = nullptr;
1759 if (!swallowEvent) {
1760 // Refetch the event target node if it currently is the shadow node inside an <input> element.
1761 // If a mouse event handler changes the input element type to one that has a widget associated,
1762 // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the
1763 // event target node can't still be the shadow node.
1764 if (is<ShadowRoot>(*mouseEvent.targetNode()) && is<HTMLInputElement>(*downcast<ShadowRoot>(*mouseEvent.targetNode()).host()))
1765 mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent);
1768 if (!swallowEvent) {
1769 if (passedToScrollbar)
1770 swallowEvent = true;
1772 swallowEvent = handleMousePressEvent(mouseEvent);
1774 return swallowEvent;
1777 // This method only exists for platforms that don't know how to deliver
1778 bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& platformMouseEvent)
1780 Ref<Frame> protectedFrame(m_frame);
1781 RefPtr<FrameView> protector(m_frame.view());
1783 m_frame.selection().setCaretBlinkingSuspended(false);
1785 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
1787 #if ENABLE(POINTER_LOCK)
1788 if (m_frame.page()->pointerLockController().isLocked()) {
1789 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent);
1794 // We get this instead of a second mouse-up
1795 m_mousePressed = false;
1796 setLastKnownMousePosition(platformMouseEvent);
1798 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
1799 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
1800 Frame* subframe = subframeForHitTestResult(mouseEvent);
1801 if (m_eventHandlerWillResetCapturingMouseEventsElement)
1802 m_capturingMouseEventsElement = nullptr;
1803 if (subframe && passMousePressEventToSubframe(mouseEvent, subframe))
1806 m_clickCount = platformMouseEvent.clickCount();
1807 bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false);
1809 bool swallowClickEvent = platformMouseEvent.button() != RightButton && mouseEvent.targetNode() == m_clickNode && !dispatchMouseEvent(eventNames().clickEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true);
1811 if (m_lastScrollbarUnderMouse)
1812 swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent);
1814 bool swallowMouseReleaseEvent = !swallowMouseUpEvent && handleMouseReleaseEvent(mouseEvent);
1818 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
1821 static ScrollableArea* enclosingScrollableArea(Node* node)
1823 for (auto ancestor = node; ancestor; ancestor = ancestor->parentOrShadowHostNode()) {
1824 if (is<HTMLIFrameElement>(*ancestor) || is<HTMLHtmlElement>(*ancestor) || is<HTMLDocument>(*ancestor))
1827 auto renderer = ancestor->renderer();
1831 if (is<RenderListBox>(*renderer))
1832 return downcast<RenderListBox>(renderer);
1834 return renderer->enclosingLayer();
1840 bool EventHandler::mouseMoved(const PlatformMouseEvent& event)
1842 Ref<Frame> protectedFrame(m_frame);
1843 RefPtr<FrameView> protector(m_frame.view());
1844 MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration);
1846 if (m_frame.page() && m_frame.page()->pageOverlayController().handleMouseEvent(event))
1849 HitTestResult hoveredNode = HitTestResult(LayoutPoint());
1850 bool result = handleMouseMoveEvent(event, &hoveredNode);
1852 Page* page = m_frame.page();
1856 if (auto scrolledArea = enclosingScrollableArea(hoveredNode.innerNode())) {
1857 if (FrameView* frameView = m_frame.view()) {
1858 if (frameView->containsScrollableArea(scrolledArea))
1859 scrolledArea->mouseMovedInContentArea();
1863 if (FrameView* frameView = m_frame.view())
1864 frameView->mouseMovedInContentArea();
1866 hoveredNode.setToNonUserAgentShadowAncestor();
1867 page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags());
1868 page->chrome().setToolTip(hoveredNode);
1872 bool EventHandler::passMouseMovedEventToScrollbars(const PlatformMouseEvent& event)
1874 HitTestResult hoveredNode;
1875 return handleMouseMoveEvent(event, &hoveredNode, true);
1878 bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& platformMouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars)
1880 #if ENABLE(TOUCH_EVENTS)
1881 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
1882 if (defaultPrevented)
1886 Ref<Frame> protectedFrame(m_frame);
1887 RefPtr<FrameView> protector(m_frame.view());
1889 #if ENABLE(POINTER_LOCK)
1890 if (m_frame.page()->pointerLockController().isLocked()) {
1891 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousemoveEvent);
1896 setLastKnownMousePosition(platformMouseEvent);
1898 if (m_hoverTimer.isActive())
1899 m_hoverTimer.stop();
1901 #if ENABLE(CURSOR_SUPPORT)
1902 m_cursorUpdateTimer.stop();
1905 #if !ENABLE(IOS_TOUCH_EVENTS)
1906 cancelFakeMouseMoveEvent();
1910 downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition));
1914 if (m_frameSetBeingResized)
1915 return !dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, platformMouseEvent, false);
1917 // On iOS, our scrollbars are managed by UIKit.
1919 // Send events right to a scrollbar if the mouse is pressed.
1920 if (m_lastScrollbarUnderMouse && m_mousePressed)
1921 return m_lastScrollbarUnderMouse->mouseMoved(platformMouseEvent);
1924 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::AllowFrameScrollbars;
1926 hitType |= HitTestRequest::Active;
1927 else if (onlyUpdateScrollbars) {
1928 // Mouse events should be treated as "read-only" if we're updating only scrollbars. This
1929 // means that :hover and :active freeze in the state they were in, rather than updating
1930 // for nodes the mouse moves while the window is not key (which will be the case if
1931 // onlyUpdateScrollbars is true).
1932 hitType |= HitTestRequest::ReadOnly;
1935 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
1936 // Treat any mouse move events as readonly if the user is currently touching the screen.
1938 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
1940 HitTestRequest request(hitType);
1941 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
1943 *hoveredNode = mouseEvent.hitTestResult();
1945 if (m_resizeLayer && m_resizeLayer->inResizeMode())
1946 m_resizeLayer->resize(platformMouseEvent, m_offsetFromResizeCorner);
1948 Scrollbar* scrollbar = mouseEvent.scrollbar();
1949 updateLastScrollbarUnderMouse(scrollbar, m_mousePressed ? SetOrClearLastScrollbar::Clear : SetOrClearLastScrollbar::Set);
1951 // On iOS, our scrollbars are managed by UIKit.
1953 if (!m_mousePressed && scrollbar)
1954 scrollbar->mouseMoved(platformMouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering.
1956 if (onlyUpdateScrollbars) {
1957 updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true);
1962 bool swallowEvent = false;
1963 RefPtr<Frame> newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent);
1965 // 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.
1966 if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(&m_frame) && m_lastMouseMoveEventSubframe != newSubframe)
1967 passMouseMoveEventToSubframe(mouseEvent, m_lastMouseMoveEventSubframe.get());
1970 // Update over/out state before passing the event to the subframe.
1971 updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true);
1973 // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target
1974 // node to be detached from its FrameView, in which case the event should not be passed.
1975 if (newSubframe->view())
1976 swallowEvent |= passMouseMoveEventToSubframe(mouseEvent, newSubframe.get(), hoveredNode);
1979 if (!newSubframe || mouseEvent.scrollbar()) {
1980 #if ENABLE(CURSOR_SUPPORT)
1981 if (auto* view = m_frame.view())
1982 updateCursor(*view, mouseEvent.hitTestResult(), platformMouseEvent.shiftKey());
1986 m_lastMouseMoveEventSubframe = newSubframe;
1991 swallowEvent = !dispatchMouseEvent(eventNames().mousemoveEvent, mouseEvent.targetNode(), false, 0, platformMouseEvent, true);
1992 #if ENABLE(DRAG_SUPPORT)
1994 swallowEvent = handleMouseDraggedEvent(mouseEvent);
1997 return swallowEvent;
2000 void EventHandler::invalidateClick()
2003 m_clickNode = nullptr;
2006 static Node* targetNodeForClickEvent(Node* mousePressNode, Node* mouseReleaseNode)
2008 if (!mousePressNode || !mouseReleaseNode)
2011 if (mousePressNode == mouseReleaseNode)
2012 return mouseReleaseNode;
2014 // If mousePressNode and mouseReleaseNode differ, we should fire the event at their common ancestor if there is one.
2015 if (&mousePressNode->document() == &mouseReleaseNode->document()) {
2016 if (auto* commonAncestor = Range::commonAncestorContainer(mousePressNode, mouseReleaseNode))
2017 return commonAncestor;
2020 Element* mouseReleaseShadowHost = mouseReleaseNode->shadowHost();
2021 if (mouseReleaseShadowHost && mouseReleaseShadowHost == mousePressNode->shadowHost()) {
2022 // We want to dispatch the click to the shadow tree host element to give listeners the illusion that the
2023 // shadom tree is a single element. For example, we want to give the illusion that <input type="range">
2024 // is a single element even though it is a composition of multiple shadom tree elements.
2025 return mouseReleaseShadowHost;
2030 bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& platformMouseEvent)
2032 Ref<Frame> protectedFrame(m_frame);
2033 RefPtr<FrameView> protector(m_frame.view());
2035 m_frame.selection().setCaretBlinkingSuspended(false);
2037 #if ENABLE(POINTER_LOCK)
2038 if (m_frame.page()->pointerLockController().isLocked()) {
2039 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent);
2044 if (m_frame.page()->pageOverlayController().handleMouseEvent(platformMouseEvent))
2047 #if ENABLE(TOUCH_EVENTS)
2048 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
2049 if (defaultPrevented)
2053 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
2055 #if ENABLE(PAN_SCROLLING)
2056 m_autoscrollController->handleMouseReleaseEvent(platformMouseEvent);
2059 m_mousePressed = false;
2060 setLastKnownMousePosition(platformMouseEvent);
2064 downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition));
2068 if (m_frameSetBeingResized)
2069 return !dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, platformMouseEvent, false);
2071 // If an immediate action began or was completed using this series of mouse events, then we should send mouseup to
2072 // the DOM and return now so that we don't perform our own default behaviors.
2073 if (m_immediateActionStage == ImmediateActionStage::ActionCompleted || m_immediateActionStage == ImmediateActionStage::ActionUpdated || m_immediateActionStage == ImmediateActionStage::ActionCancelledAfterUpdate) {
2074 m_immediateActionStage = ImmediateActionStage::None;
2075 return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), true, m_clickCount, platformMouseEvent, false);
2077 m_immediateActionStage = ImmediateActionStage::None;
2079 if (m_lastScrollbarUnderMouse) {
2081 m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent);
2082 bool cancelable = true;
2083 bool setUnder = false;
2084 return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), cancelable, m_clickCount, platformMouseEvent, setUnder);
2087 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
2088 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
2089 Frame* subframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent);
2090 if (m_eventHandlerWillResetCapturingMouseEventsElement)
2091 m_capturingMouseEventsElement = nullptr;
2092 if (subframe && passMouseReleaseEventToSubframe(mouseEvent, subframe))
2095 bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false);
2097 bool contextMenuEvent = platformMouseEvent.button() == RightButton;
2099 Node* nodeToClick = targetNodeForClickEvent(m_clickNode.get(), mouseEvent.targetNode());
2100 bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && nodeToClick && !dispatchMouseEvent(eventNames().clickEvent, nodeToClick, true, m_clickCount, platformMouseEvent, true);
2102 if (m_resizeLayer) {
2103 m_resizeLayer->setInResizeMode(false);
2104 m_resizeLayer = nullptr;
2107 bool swallowMouseReleaseEvent = false;
2108 if (!swallowMouseUpEvent)
2109 swallowMouseReleaseEvent = handleMouseReleaseEvent(mouseEvent);
2113 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
2116 #if ENABLE(MOUSE_FORCE_EVENTS)
2117 bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& event)
2119 Ref<Frame> protectedFrame(m_frame);
2120 RefPtr<FrameView> protector(m_frame.view());
2122 #if ENABLE(POINTER_LOCK)
2123 if (m_frame.page()->pointerLockController().isLocked()) {
2124 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcechangedEvent);
2125 if (event.type() == PlatformEvent::MouseForceDown)
2126 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcedownEvent);
2127 if (event.type() == PlatformEvent::MouseForceUp)
2128 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforceupEvent);
2133 setLastKnownMousePosition(event);
2135 HitTestRequest::HitTestRequestType hitType = HitTestRequest::DisallowUserAgentShadowContent;
2138 hitType |= HitTestRequest::Active;
2140 HitTestRequest request(hitType);
2141 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event);
2143 bool swallowedEvent = !dispatchMouseEvent(eventNames().webkitmouseforcechangedEvent, mouseEvent.targetNode(), false, 0, event, false);
2144 if (event.type() == PlatformEvent::MouseForceDown)
2145 swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforcedownEvent, mouseEvent.targetNode(), false, 0, event, false);
2146 if (event.type() == PlatformEvent::MouseForceUp)
2147 swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforceupEvent, mouseEvent.targetNode(), false, 0, event, false);
2149 return swallowedEvent;
2152 bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& )
2156 #endif // #if ENABLE(MOUSE_FORCE_EVENTS)
2158 bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& platformMouseEvent)
2160 // If the event was a middle click, attempt to copy global selection in after
2161 // the newly set caret position.
2163 // This code is called from either the mouse up or mouse down handling. There
2164 // is some debate about when the global selection is pasted:
2165 // xterm: pastes on up.
2166 // GTK: pastes on down.
2167 // Qt: pastes on up.
2168 // Firefox: pastes on up.
2169 // Chromium: pastes on up.
2171 // There is something of a webcompat angle to this well, as highlighted by
2172 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
2173 // down then the text is pasted just before the onclick handler runs and
2174 // clears the text box. So it's important this happens after the event
2175 // handlers have been fired.
2177 if (platformMouseEvent.type() != PlatformEvent::MousePressed)
2180 if (platformMouseEvent.type() != PlatformEvent::MouseReleased)
2184 if (!m_frame.page())
2186 Frame& focusFrame = m_frame.page()->focusController().focusedOrMainFrame();
2187 // Do not paste here if the focus was moved somewhere else.
2188 if (&m_frame == &focusFrame && m_frame.editor().client()->supportsGlobalSelection())
2189 return m_frame.editor().command("PasteGlobalSelection"_s).execute();
2194 #if ENABLE(DRAG_SUPPORT)
2196 bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer& dataTransfer)
2198 Ref<Frame> protectedFrame(m_frame);
2199 FrameView* view = m_frame.view();
2201 // FIXME: We might want to dispatch a dragleave even if the view is gone.
2205 view->disableLayerFlushThrottlingTemporarilyForInteraction();
2206 Ref<MouseEvent> me = MouseEvent::create(eventType,
2207 true, true, event.timestamp().approximateMonotonicTime(), &m_frame.windowProxy(),
2208 0, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(),
2209 #if ENABLE(POINTER_LOCK)
2210 event.movementDelta().x(), event.movementDelta().y(),
2212 event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(),
2213 0, 0, nullptr, event.force(), NoTap, &dataTransfer);
2215 dragTarget.dispatchEvent(me);
2216 return me->defaultPrevented();
2219 static bool targetIsFrame(Node* target, Frame*& frame)
2221 if (!is<HTMLFrameElementBase>(target))
2224 frame = downcast<HTMLFrameElementBase>(*target).contentFrame();
2228 static DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation)
2230 if (dragOperation == "copy")
2231 return DragOperationCopy;
2232 if (dragOperation == "move")
2233 return DragOperationMove;
2234 if (dragOperation == "link")
2235 return DragOperationLink;
2236 return DragOperationNone;
2239 static String convertDragOperationToDropZoneOperation(DragOperation operation)
2241 switch (operation) {
2242 case DragOperationCopy:
2244 case DragOperationMove:
2246 case DragOperationLink:
2253 static bool hasDropZoneType(DataTransfer& dataTransfer, const String& keyword)
2255 if (keyword.startsWith("file:"))
2256 return dataTransfer.hasFileOfType(keyword.substring(5));
2258 if (keyword.startsWith("string:"))
2259 return dataTransfer.hasStringOfType(keyword.substring(7));
2264 static bool findDropZone(Node& target, DataTransfer& dataTransfer)
2266 RefPtr<Element> element = is<Element>(target) ? &downcast<Element>(target) : target.parentElement();
2267 for (; element; element = element->parentElement()) {
2268 SpaceSplitString keywords(element->attributeWithoutSynchronization(webkitdropzoneAttr), true);
2269 bool matched = false;
2270 DragOperation dragOperation = DragOperationNone;
2271 for (unsigned i = 0, size = keywords.size(); i < size; ++i) {
2272 DragOperation op = convertDropZoneOperationToDragOperation(keywords[i]);
2273 if (op != DragOperationNone) {
2274 if (dragOperation == DragOperationNone)
2277 matched = matched || hasDropZoneType(dataTransfer, keywords[i].string());
2278 if (matched && dragOperation != DragOperationNone)
2282 dataTransfer.setDropEffect(convertDragOperationToDropZoneOperation(dragOperation));
2289 EventHandler::DragTargetResponse EventHandler::dispatchDragEnterOrDragOverEvent(const AtomicString& eventType, Element& target, const PlatformMouseEvent& event,
2290 std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
2292 auto dataTransfer = DataTransfer::createForUpdatingDropTarget(target.document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
2293 bool accept = dispatchDragEvent(eventType, target, event, dataTransfer.get());
2295 accept = findDropZone(target, dataTransfer);
2296 dataTransfer->makeInvalidForSecurity();
2297 if (accept && !dataTransfer->dropEffectIsUninitialized())
2298 return { true, dataTransfer->destinationOperation() };
2299 return { accept, std::nullopt };
2302 EventHandler::DragTargetResponse EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, const std::function<std::unique_ptr<Pasteboard>()>& makePasteboard, DragOperation sourceOperation, bool draggingFiles)
2304 Ref<Frame> protectedFrame(m_frame);
2305 if (!m_frame.view())
2308 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
2309 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event);
2311 RefPtr<Element> newTarget;
2312 if (Node* targetNode = mouseEvent.targetNode()) {
2313 // Drag events should never go to non-element nodes (following IE, and proper mouseover/out dispatch)
2314 if (!is<Element>(*targetNode))
2315 newTarget = targetNode->parentOrShadowHostElement();
2317 newTarget = downcast<Element>(targetNode);
2320 m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp());
2322 DragTargetResponse response;
2323 if (m_dragTarget != newTarget) {
2324 // FIXME: this ordering was explicitly chosen to match WinIE. However,
2325 // it is sometimes incorrect when dragging within subframes, as seen with
2326 // LayoutTests/fast/events/drag-in-frames.html.
2328 // 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>.
2330 if (targetIsFrame(newTarget.get(), targetFrame)) {
2332 response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
2333 } else if (newTarget) {
2334 // 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.
2335 if (dragState().source && dragState().shouldDispatchEvents)
2336 dispatchDragSrcEvent(eventNames().dragEvent, event);
2337 response = dispatchDragEnterOrDragOverEvent(eventNames().dragenterEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles);
2340 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2341 // FIXME: Recursing again here doesn't make sense if the newTarget and m_dragTarget were in the same frame.
2343 response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
2344 } else if (m_dragTarget) {
2345 auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), makePasteboard(), sourceOperation, draggingFiles);
2346 dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get());
2347 dataTransfer->makeInvalidForSecurity();
2351 // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that
2352 // two dragover events fired. So, we mark that we should only fire a dragover event on the next call to this function.
2353 m_shouldOnlyFireDragOverEvent = true;
2357 if (targetIsFrame(newTarget.get(), targetFrame)) {
2359 response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
2360 } else if (newTarget) {
2361 // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier.
2362 if (!m_shouldOnlyFireDragOverEvent && dragState().source && dragState().shouldDispatchEvents)
2363 dispatchDragSrcEvent(eventNames().dragEvent, event);
2364 response = dispatchDragEnterOrDragOverEvent(eventNames().dragoverEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles);
2365 m_shouldOnlyFireDragOverEvent = false;
2368 m_dragTarget = WTFMove(newTarget);
2372 void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
2374 Ref<Frame> protectedFrame(m_frame);
2377 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2379 targetFrame->eventHandler().cancelDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles);
2380 } else if (m_dragTarget) {
2381 if (dragState().source && dragState().shouldDispatchEvents)
2382 dispatchDragSrcEvent(eventNames().dragEvent, event);
2384 auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
2385 dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get());
2386 dataTransfer->makeInvalidForSecurity();
2391 bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
2393 Ref<Frame> protectedFrame(m_frame);
2396 bool preventedDefault = false;
2397 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2399 preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles);
2400 } else if (m_dragTarget) {
2401 auto dataTransfer = DataTransfer::createForDrop(m_dragTarget->document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
2402 preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, dataTransfer);
2403 dataTransfer->makeInvalidForSecurity();
2406 return preventedDefault;
2409 void EventHandler::clearDragState()
2411 stopAutoscrollTimer();
2412 m_dragTarget = nullptr;
2413 m_capturingMouseEventsElement = nullptr;
2414 m_shouldOnlyFireDragOverEvent = false;
2416 m_sendingEventToSubview = false;
2420 #endif // ENABLE(DRAG_SUPPORT)
2422 void EventHandler::setCapturingMouseEventsElement(Element* element)
2424 m_capturingMouseEventsElement = element;
2425 m_eventHandlerWillResetCapturingMouseEventsElement = false;
2428 MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mouseEvent)
2430 Ref<Frame> protectedFrame(m_frame);
2431 ASSERT(m_frame.document());
2432 return m_frame.document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mouseEvent.position()), mouseEvent);
2435 static RenderElement* nearestCommonHoverAncestor(RenderElement* obj1, RenderElement* obj2)
2440 for (RenderElement* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor()) {
2441 for (RenderElement* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor()) {
2442 if (currObj1 == currObj2)
2450 static bool hierarchyHasCapturingEventListeners(Element* element, const AtomicString& eventName)
2452 for (ContainerNode* curr = element; curr; curr = curr->parentOrShadowHostNode()) {
2453 if (curr->hasCapturingEventListeners(eventName))
2459 void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& platformMouseEvent, bool fireMouseOverOut)
2461 Ref<Frame> protectedFrame(m_frame);
2462 Element* targetElement = nullptr;
2464 // If we're capturing, we always go right to that element.
2465 if (m_capturingMouseEventsElement)
2466 targetElement = m_capturingMouseEventsElement.get();
2467 else if (targetNode) {
2468 // If the target node is a non-element, dispatch on the parent. <rdar://problem/4196646>
2469 while (targetNode && !is<Element>(*targetNode))
2470 targetNode = targetNode->parentInComposedTree();
2471 targetElement = downcast<Element>(targetNode);
2474 m_elementUnderMouse = targetElement;
2476 // Fire mouseout/mouseover if the mouse has shifted to a different node.
2477 if (fireMouseOverOut) {
2478 auto scrollableAreaForLastNode = enclosingScrollableArea(m_lastElementUnderMouse.get());
2479 auto scrollableAreaForNodeUnderMouse = enclosingScrollableArea(m_elementUnderMouse.get());
2480 Page* page = m_frame.page();
2482 if (m_lastElementUnderMouse && (!m_elementUnderMouse || &m_elementUnderMouse->document() != m_frame.document())) {
2483 // The mouse has moved between frames.
2484 if (Frame* frame = m_lastElementUnderMouse->document().frame()) {
2485 if (FrameView* frameView = frame->view())
2486 frameView->mouseExitedContentArea();
2488 } else if (page && (scrollableAreaForLastNode && (!scrollableAreaForNodeUnderMouse || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) {
2489 // The mouse has moved between layers.
2490 if (Frame* frame = m_lastElementUnderMouse->document().frame()) {
2491 if (FrameView* frameView = frame->view()) {
2492 if (frameView->containsScrollableArea(scrollableAreaForLastNode))
2493 scrollableAreaForLastNode->mouseExitedContentArea();
2498 if (m_elementUnderMouse && (!m_lastElementUnderMouse || &m_lastElementUnderMouse->document() != m_frame.document())) {
2499 // The mouse has moved between frames.
2500 if (Frame* frame = m_elementUnderMouse->document().frame()) {
2501 if (FrameView* frameView = frame->view())
2502 frameView->mouseEnteredContentArea();
2504 } else if (page && (scrollableAreaForNodeUnderMouse && (!scrollableAreaForLastNode || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) {
2505 // The mouse has moved between layers.
2506 if (Frame* frame = m_elementUnderMouse->document().frame()) {
2507 if (FrameView* frameView = frame->view()) {
2508 if (frameView->containsScrollableArea(scrollableAreaForNodeUnderMouse))
2509 scrollableAreaForNodeUnderMouse->mouseEnteredContentArea();
2514 if (m_lastElementUnderMouse && &m_lastElementUnderMouse->document() != m_frame.document()) {
2515 m_lastElementUnderMouse = nullptr;
2516 m_lastScrollbarUnderMouse = nullptr;
2519 if (m_lastElementUnderMouse != m_elementUnderMouse) {
2520 // mouseenter and mouseleave events are only dispatched if there is a capturing eventhandler on an ancestor
2521 // or a normal eventhandler on the element itself (they don't bubble).
2522 // This optimization is necessary since these events can cause O(n^2) capturing event-handler checks.
2523 bool hasCapturingMouseEnterListener = hierarchyHasCapturingEventListeners(m_elementUnderMouse.get(), eventNames().mouseenterEvent);
2524 bool hasCapturingMouseLeaveListener = hierarchyHasCapturingEventListeners(m_lastElementUnderMouse.get(), eventNames().mouseleaveEvent);
2526 RenderElement* oldHoverRenderer = m_lastElementUnderMouse ? m_lastElementUnderMouse->renderer() : nullptr;
2527 RenderElement* newHoverRenderer = m_elementUnderMouse ? m_elementUnderMouse->renderer() : nullptr;
2528 RenderElement* ancestor = nearestCommonHoverAncestor(oldHoverRenderer, newHoverRenderer);
2530 Vector<Ref<Element>, 32> leftElementsChain;
2531 if (oldHoverRenderer) {
2532 for (RenderElement* curr = oldHoverRenderer; curr && curr != ancestor; curr = curr->hoverAncestor()) {
2533 if (Element* element = curr->element())
2534 leftElementsChain.append(*element);
2537 // If the old hovered element is not null but it's renderer is, it was probably detached.
2538 // In this case, the old hovered element (and its ancestors) must be updated, to ensure it's normal style is re-applied.
2539 for (Element* element = m_lastElementUnderMouse.get(); element; element = element->parentElement())
2540 leftElementsChain.append(*element);
2543 Vector<Ref<Element>, 32> enteredElementsChain;
2544 const Element* ancestorElement = ancestor ? ancestor->element() : nullptr;
2545 for (RenderElement* curr = newHoverRenderer; curr; curr = curr->hoverAncestor()) {
2546 if (Element *element = curr->element()) {
2547 if (element == ancestorElement)
2549 enteredElementsChain.append(*element);
2553 // Send mouseout event to the old node.
2554 if (m_lastElementUnderMouse)
2555 m_lastElementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoutEvent, 0, m_elementUnderMouse.get());
2557 // Send mouseleave to the node hierarchy no longer under the mouse.
2558 for (auto& chain : leftElementsChain) {
2559 if (hasCapturingMouseLeaveListener || chain->hasEventListeners(eventNames().mouseleaveEvent))
2560 chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseleaveEvent, 0, m_elementUnderMouse.get());
2563 // Send mouseover event to the new node.
2564 if (m_elementUnderMouse)
2565 m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoverEvent, 0, m_lastElementUnderMouse.get());
2567 // Send mouseleave event to the nodes hierarchy under the mouse.
2568 for (auto& chain : enteredElementsChain) {
2569 if (hasCapturingMouseEnterListener || chain->hasEventListeners(eventNames().mouseenterEvent))
2570 chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseenterEvent, 0, m_lastElementUnderMouse.get());
2573 m_lastElementUnderMouse = m_elementUnderMouse;
2577 bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& platformMouseEvent, bool setUnder)
2579 Ref<Frame> protectedFrame(m_frame);
2581 if (auto* view = m_frame.view())
2582 view->disableLayerFlushThrottlingTemporarilyForInteraction();
2584 updateMouseEventTargetNode(targetNode, platformMouseEvent, setUnder);
2586 if (m_elementUnderMouse && !m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventType, clickCount))
2589 if (eventType != eventNames().mousedownEvent)
2592 // If clicking on a frame scrollbar, do not make any change to which element is focused.
2593 auto* view = m_frame.view();
2594 if (view && view->scrollbarAtPoint(platformMouseEvent.position()))
2597 // The layout needs to be up to date to determine if an element is focusable.
2598 m_frame.document()->updateLayoutIgnorePendingStylesheets();
2600 // Remove focus from the currently focused element when a link or button is clicked.
2601 // This is expected by some sites that rely on change event handlers running
2602 // from form fields before the button click is processed, behavior that was inherited
2603 // from the user interface of Windows, where pushing a button moves focus to the button.
2605 // Walk up the DOM tree to search for an element to focus.
2607 for (element = m_elementUnderMouse.get(); element; element = element->parentOrShadowHostElement()) {
2608 if (element->isMouseFocusable())
2612 // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus an
2613 // element on mouse down if it's selected and inside a focused element. It will be
2614 // focused if the user does a mouseup over it, however, because the mouseup
2615 // will set a selection inside it, which will also set the focused element.
2616 if (element && m_frame.selection().isRange()) {
2617 if (auto range = m_frame.selection().toNormalizedRange()) {
2618 auto result = range->compareNode(*element);
2619 if (!result.hasException() && result.releaseReturnValue() == Range::NODE_INSIDE && element->isDescendantOf(m_frame.document()->focusedElement()))
2624 // Only change the focus when clicking scrollbars if it can be transferred to a mouse focusable node.
2625 if (!element && isInsideScrollbar(platformMouseEvent.position()))
2628 // If focus shift is blocked, we eat the event.
2629 auto* page = m_frame.page();
2630 if (page && !page->focusController().setFocusedElement(element, m_frame))
2636 bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const
2638 if (RenderView* renderView = m_frame.contentRenderer()) {
2639 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
2640 HitTestResult result(windowPoint);
2641 renderView->hitTest(request, result);
2642 return result.scrollbar();
2648 #if !PLATFORM(GTK) && !PLATFORM(WPE)
2650 bool EventHandler::shouldSwapScrollDirection(const HitTestResult&, const PlatformWheelEvent&) const
2659 void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&, const HitTestResult&, RefPtr<Element>&, RefPtr<ContainerNode>&, WeakPtr<ScrollableArea>&, bool&)
2663 void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& event)
2665 if (auto* page = m_frame.page())
2666 page->wheelEventDeltaFilter()->updateFromDelta(FloatSize(event.deltaX(), event.deltaY()));
2669 bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& event, ContainerNode*, const WeakPtr<ScrollableArea>&)
2671 Ref<Frame> protectedFrame(m_frame);
2673 // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
2674 FrameView* view = m_frame.view();
2676 bool didHandleEvent = view ? view->wheelEvent(event) : false;
2677 m_isHandlingWheelEvent = false;
2678 return didHandleEvent;
2681 bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode*)
2686 void EventHandler::platformNotifyIfEndGesture(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&)
2690 void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&)
2692 clearLatchedState();
2697 IntPoint EventHandler::targetPositionInWindowForSelectionAutoscroll() const
2699 return m_lastKnownMousePosition;
2702 #endif // !PLATFORM(IOS)
2704 #endif // !PLATFORM(MAC)
2708 bool EventHandler::shouldUpdateAutoscroll()
2710 return mousePressed();
2713 #endif // !PLATFORM(IOS)
2715 Widget* EventHandler::widgetForEventTarget(Element* eventTarget)
2720 auto* target = eventTarget->renderer();
2721 if (!is<RenderWidget>(target))
2724 return downcast<RenderWidget>(*target).widget();
2727 static WeakPtr<Widget> widgetForElement(const Element& element)
2729 auto target = element.renderer();
2730 if (!is<RenderWidget>(target) || !downcast<RenderWidget>(*target).widget())
2733 return makeWeakPtr(*downcast<RenderWidget>(*target).widget());
2736 bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr<Widget>& widget, const WeakPtr<ScrollableArea>& scrollableArea, ContainerNode* scrollableContainer)
2738 m_isHandlingWheelEvent = false;
2740 // We do another check on the widget because the event handler can run JS which results in the frame getting destroyed.
2745 scrollableArea->setScrolledProgrammatically(false);
2747 platformNotifyIfEndGesture(event, scrollableArea);
2749 if (!widget->platformWidget())
2752 return platformCompletePlatformWidgetWheelEvent(event, *widget.get(), scrollableContainer);
2755 bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event)
2757 RenderView* renderView = m_frame.contentRenderer();
2761 Ref<Frame> protectedFrame(m_frame);
2762 RefPtr<FrameView> protector(m_frame.view());
2764 FrameView* view = m_frame.view();
2768 #if ENABLE(POINTER_LOCK)
2769 if (m_frame.page()->pointerLockController().isLocked()) {
2770 m_frame.page()->pointerLockController().dispatchLockedWheelEvent(event);
2775 m_isHandlingWheelEvent = true;
2776 setFrameWasScrolledByUser();
2778 HitTestRequest request;
2779 HitTestResult result(view->windowToContents(event.position()));
2780 renderView->hitTest(request, result);
2782 RefPtr<Element> element = result.targetElement();
2783 RefPtr<ContainerNode> scrollableContainer;
2784 WeakPtr<ScrollableArea> scrollableArea;
2785 bool isOverWidget = result.isOverWidget();
2786 platformPrepareForWheelEvents(event, result, element, scrollableContainer, scrollableArea, isOverWidget);
2789 if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone && m_frame.page())
2790 m_frame.page()->resetLatchingState();
2793 // FIXME: It should not be necessary to do this mutation here.
2794 // Instead, the handlers should know convert vertical scrolls appropriately.
2795 PlatformWheelEvent adjustedEvent = shouldSwapScrollDirection(result, event) ? event.copySwappingDirection() : event;
2796 platformRecordWheelEvent(adjustedEvent);
2800 if (WeakPtr<Widget> widget = widgetForElement(*element)) {
2801 if (widgetDidHandleWheelEvent(event, *widget.get()))
2802 return completeWidgetWheelEvent(adjustedEvent, widget, scrollableArea, scrollableContainer.get());
2806 if (!element->dispatchWheelEvent(adjustedEvent)) {
2807 m_isHandlingWheelEvent = false;
2808 if (scrollableArea && scrollableArea->isScrolledProgrammatically()) {
2809 // Web developer is controlling scrolling, so don't attempt to latch.
2810 clearLatchedState();
2811 scrollableArea->setScrolledProgrammatically(false);
2814 platformNotifyIfEndGesture(adjustedEvent, scrollableArea);
2820 scrollableArea->setScrolledProgrammatically(false);
2822 bool handledEvent = platformCompleteWheelEvent(adjustedEvent, scrollableContainer.get(), scrollableArea);
2823 platformNotifyIfEndGesture(adjustedEvent, scrollableArea);
2824 return handledEvent;
2827 void EventHandler::clearLatchedState()
2829 auto* page = m_frame.page();
2834 page->resetLatchingState();
2836 if (auto filter = page->wheelEventDeltaFilter())
2837 filter->endFilteringDeltas();
2840 void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent& wheelEvent)
2845 auto protectedFrame = makeRef(m_frame);
2847 FloatSize filteredPlatformDelta(wheelEvent.deltaX(), wheelEvent.deltaY());
2848 FloatSize filteredVelocity;
2849 if (auto platformWheelEvent = wheelEvent.underlyingPlatformEvent()) {
2850 filteredPlatformDelta.setWidth(platformWheelEvent->deltaX());
2851 filteredPlatformDelta.setHeight(platformWheelEvent->deltaY());
2855 ScrollLatchingState* latchedState = m_frame.page() ? m_frame.page()->latchingState() : nullptr;
2856 Element* stopElement = latchedState ? latchedState->previousWheelScrolledElement() : nullptr;
2858 if (m_frame.page() && m_frame.page()->wheelEventDeltaFilter()->isFilteringDeltas()) {
2859 filteredPlatformDelta = m_frame.page()->wheelEventDeltaFilter()->filteredDelta();
2860 filteredVelocity = m_frame.page()->wheelEventDeltaFilter()->filteredVelocity();
2863 Element* stopElement = nullptr;
2866 if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &stopElement, filteredPlatformDelta, filteredVelocity))
2867 wheelEvent.setDefaultHandled();
2870 if (latchedState && !latchedState->wheelEventElement())
2871 latchedState->setPreviousWheelScrolledElement(stopElement);
2875 #if ENABLE(CONTEXT_MENUS)
2876 bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event)
2878 Ref<Frame> protectedFrame(m_frame);
2880 Document* doc = m_frame.document();
2881 FrameView* view = m_frame.view();
2885 // Clear mouse press state to avoid initiating a drag while context menu is up.
2886 m_mousePressed = false;
2888 LayoutPoint viewportPos = view->windowToContents(event.position());
2889 HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
2890 MouseEventWithHitTestResults mouseEvent = doc->prepareMouseEvent(request, viewportPos, event);
2892 // Do not show context menus when clicking on scrollbars.
2893 if (mouseEvent.scrollbar() || view->scrollbarAtPoint(event.position()))
2896 if (m_frame.editor().behavior().shouldSelectOnContextualMenuClick()
2897 && !m_frame.selection().contains(viewportPos)
2898 // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse.
2899 // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items
2900 // available for text selections. But only if we're above text.
2901 && (m_frame.selection().selection().isContentEditable() || (mouseEvent.targetNode() && mouseEvent.targetNode()->isTextNode()))) {
2902 m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2903 selectClosestContextualWordOrLinkFromMouseEvent(mouseEvent);
2906 swallowEvent = !dispatchMouseEvent(eventNames().contextmenuEvent, mouseEvent.targetNode(), true, 0, event, false);
2908 return swallowEvent;
2911 bool EventHandler::sendContextMenuEventForKey()
2913 Ref<Frame> protectedFrame(m_frame);
2915 FrameView* view = m_frame.view();
2919 Document* doc = m_frame.document();
2923 // Clear mouse press state to avoid initiating a drag while context menu is up.
2924 m_mousePressed = false;
2926 static const int kContextMenuMargin = 1;
2929 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
2931 int rightAligned = 0;
2935 Element* focusedElement = doc->focusedElement();
2936 const VisibleSelection& selection = m_frame.selection().selection();
2937 Position start = selection.start();
2939 if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) {
2940 RefPtr<Range> selectionRange = selection.toNormalizedRange();
2941 IntRect firstRect = m_frame.editor().firstRectForRange(selectionRange.get());
2943 int x = rightAligned ? firstRect.maxX() : firstRect.x();
2944 // In a multiline edit, firstRect.maxY() would endup on the next line, so -1.
2945 int y = firstRect.maxY() ? firstRect.maxY() - 1 : 0;
2946 location = IntPoint(x, y);
2947 } else if (focusedElement) {
2948 RenderBoxModelObject* box = focusedElement->renderBoxModelObject();
2952 IntRect boundingBoxRect = box->absoluteBoundingBoxRect(true);
2953 location = IntPoint(boundingBoxRect.x(), boundingBoxRect.maxY() - 1);
2955 location = IntPoint(
2956 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
2957 kContextMenuMargin);
2960 m_frame.view()->setCursor(pointerCursor());
2962 IntPoint position = view->contentsToRootView(location);
2963 IntPoint globalPosition = view->hostWindow()->rootViewToScreen(IntRect(position, IntSize())).location();
2965 Node* targetNode = doc->focusedElement();
2969 // Use the focused node as the target for hover and active.
2970 HitTestResult result(position);
2971 result.setInnerNode(targetNode);
2972 doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent, result.targetElement());
2974 // The contextmenu event is a mouse event even when invoked using the keyboard.
2975 // This is required for web compatibility.
2978 PlatformEvent::Type eventType = PlatformEvent::MouseReleased;
2980 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
2983 PlatformMouseEvent platformMouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WallTime::now(), ForceAtClick, NoTap);
2985 return sendContextMenuEvent(platformMouseEvent);
2987 #endif // ENABLE(CONTEXT_MENUS)
2989 void EventHandler::scheduleHoverStateUpdate()
2991 if (!m_hoverTimer.isActive())
2992 m_hoverTimer.startOneShot(0_s);
2995 #if ENABLE(CURSOR_SUPPORT)
2996 void EventHandler::scheduleCursorUpdate()
2998 if (!m_cursorUpdateTimer.isActive())
2999 m_cursorUpdateTimer.startOneShot(cursorUpdateInterval);
3003 void EventHandler::dispatchFakeMouseMoveEventSoon()
3005 #if !ENABLE(IOS_TOUCH_EVENTS)
3009 if (m_mousePositionIsUnknown)
3012 if (Page* page = m_frame.page()) {
3013 if (!page->chrome().client().shouldDispatchFakeMouseMoveEvents())
3017 // If the content has ever taken longer than fakeMouseMoveShortInterval we
3018 // reschedule the timer and use a longer time. This will cause the content
3019 // to receive these moves only after the user is done scrolling, reducing
3020 // pauses during the scroll.
3021 if (m_fakeMouseMoveEventTimer.isActive())
3022 m_fakeMouseMoveEventTimer.stop();
3023 m_fakeMouseMoveEventTimer.startOneShot(m_maxMouseMovedDuration > fakeMouseMoveDurationThreshold ? fakeMouseMoveLongInterval : fakeMouseMoveShortInterval);
3027 void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad)
3029 #if ENABLE(IOS_TOUCH_EVENTS)
3032 FrameView* view = m_frame.view();
3036 if (!quad.containsPoint(view->windowToContents(m_lastKnownMousePosition)))
3039 dispatchFakeMouseMoveEventSoon();
3043 #if !ENABLE(IOS_TOUCH_EVENTS)
3044 void EventHandler::cancelFakeMouseMoveEvent()
3046 m_fakeMouseMoveEventTimer.stop();
3049 void EventHandler::fakeMouseMoveEventTimerFired()
3051 ASSERT(!m_mousePressed);
3053 FrameView* view = m_frame.view();
3057 if (!m_frame.page() || !m_frame.page()->isVisible() || !m_frame.page()->focusController().isActive())
3064 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
3065 PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), 0, NoTap);
3066 mouseMoved(fakeMouseMoveEvent);
3068 #endif // !ENABLE(IOS_TOUCH_EVENTS)
3070 void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet)
3072 m_frameSetBeingResized = frameSet;
3075 void EventHandler::resizeLayerDestroyed()
3077 ASSERT(m_resizeLayer);
3078 m_resizeLayer = nullptr;
3081 void EventHandler::hoverTimerFired()
3083 m_hoverTimer.stop();
3085 ASSERT(m_frame.document());
3087 Ref<Frame> protectedFrame(m_frame);
3089 if (RenderView* renderView = m_frame.contentRenderer()) {
3090 if (FrameView* view = m_frame.view()) {
3091 HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent);
3092 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
3093 renderView->hitTest(request, result);
3094 m_frame.document()->updateHoverActiveState(request, result.targetElement());
3099 bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& event)
3101 // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do.
3102 // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and
3103 // lower case variants are present in a document, the correct element is matched based on Shift key state.
3104 // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively.
3105 ASSERT(!accessKeyModifiers().contains(PlatformEvent::Modifier::ShiftKey));
3107 if ((event.modifiers() - PlatformEvent::Modifier::ShiftKey) != accessKeyModifiers())
3109 Element* element = m_frame.document()->getElementByAccessKey(event.unmodifiedText());
3112 element->accessKeyAction(false);
3117 bool EventHandler::needsKeyboardEventDisambiguationQuirks() const
3123 #if ENABLE(FULLSCREEN_API)
3124 bool EventHandler::isKeyEventAllowedInFullScreen(const PlatformKeyboardEvent& keyEvent) const
3126 Document* document = m_frame.document();
3127 if (document->webkitFullScreenKeyboardInputAllowed())
3130 if (keyEvent.type() == PlatformKeyboardEvent::Char) {
3131 if (keyEvent.text().length() != 1)
3133 UChar character = keyEvent.text()[0];
3134 return character == ' ';
3137 int keyCode = keyEvent.windowsVirtualKeyCode();
3138 return (keyCode >= VK_BACK && keyCode <= VK_CAPITAL)
3139 || (keyCode >= VK_SPACE && keyCode <= VK_DELETE)
3140 || (keyCode >= VK_OEM_1 && keyCode <= VK_OEM_PLUS)
3141 || (keyCode >= VK_MULTIPLY && keyCode <= VK_OEM_8);
3145 bool EventHandler::keyEvent(const PlatformKeyboardEvent& keyEvent)
3147 Document* topDocument = m_frame.document() ? &m_frame.document()->topDocument() : nullptr;
3148 MonotonicTime savedLastHandledUserGestureTimestamp;
3149 bool savedUserDidInteractWithPage = topDocument ? topDocument->userDidInteractWithPage() : false;
3151 if (m_frame.document())
3152 savedLastHandledUserGestureTimestamp = m_frame.document()->lastHandledUserGestureTimestamp();
3154 bool wasHandled = internalKeyEvent(keyEvent);
3156 // If the key event was not handled, do not treat it as user interaction with the page.
3159 topDocument->setUserDidInteractWithPage(savedUserDidInteractWithPage);
3161 ResourceLoadObserver::shared().logUserInteractionWithReducedTimeResolution(*topDocument);
3164 if (!wasHandled && m_frame.document())
3165 m_frame.document()->updateLastHandledUserGestureTimestamp(savedLastHandledUserGestureTimestamp);
3170 bool EventHandler::internalKeyEvent(const PlatformKeyboardEvent& initialKeyEvent)
3172 Ref<Frame> protectedFrame(m_frame);
3173 RefPtr<FrameView> protector(m_frame.view());
3175 LOG(Editing, "EventHandler %p keyEvent (text %s keyIdentifier %s)", this, initialKeyEvent.text().utf8().data(), initialKeyEvent.keyIdentifier().utf8().data());
3177 #if ENABLE(POINTER_LOCK)
3178 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE && m_frame.page()->pointerLockController().element()) {
3179 m_frame.page()->pointerLockController().requestPointerUnlockAndForceCursorVisible();
3183 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) {
3184 if (auto* page = m_frame.page()) {
3185 if (auto* validationMessageClient = page->validationMessageClient())
3186 validationMessageClient->hideAnyValidationMessage();
3190 #if ENABLE(FULLSCREEN_API)
3191 if (m_frame.document()->webkitIsFullScreen()) {
3192 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) {
3193 m_frame.document()->webkitCancelFullScreen();
3197 if (!isKeyEventAllowedInFullScreen(initialKeyEvent))
3202 if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL) {
3203 if (auto* element = m_frame.document()->focusedElement()) {
3204 if (is<HTMLInputElement>(*element))
3205 downcast<HTMLInputElement>(*element).capsLockStateMayHaveChanged();
3209 #if ENABLE(PAN_SCROLLING)
3210 if (m_frame.mainFrame().eventHandler().panScrollInProgress()) {
3211 // If a key is pressed while the panScroll is in progress then we want to stop
3212 if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent.type() == PlatformEvent::RawKeyDown)
3213 stopAutoscrollTimer();
3215 // If we were in panscroll mode, we swallow the key event
3220 // Check for cases where we are too early for events -- possible unmatched key up
3221 // from pressing return in the location bar.
3222 RefPtr<Element> element = eventTargetElementForDocument(m_frame.document());
3226 UserGestureType gestureType = UserGestureType::Other;
3227 if (initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE)
3228 gestureType = UserGestureType::EscapeKey;
3230 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document(), gestureType, UserGestureIndicator::ProcessInteractionStyle::Delayed);
3231 UserTypingGestureIndicator typingGestureIndicator(m_frame);
3233 if (FrameView* view = m_frame.view())
3234 view->disableLayerFlushThrottlingTemporarilyForInteraction();
3236 // FIXME (bug 68185): this call should be made at another abstraction layer
3237 m_frame.loader().resetMultipleFormSubmissionProtection();
3239 // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match.
3240 // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict
3241 // with access keys. Then we dispatch keydown, but suppress its default handling.
3242 // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatching a keypress event for WM_SYSCHAR messages.
3243 // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events.
3244 bool matchedAnAccessKey = false;
3245 if (initialKeyEvent.type() == PlatformEvent::KeyDown)
3246 matchedAnAccessKey = handleAccessKey(initialKeyEvent);
3248 // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch.
3249 if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char)
3250 return !element->dispatchKeyEvent(initialKeyEvent);
3252 bool backwardCompatibilityMode = needsKeyboardEventDisambiguationQuirks();
3254 PlatformKeyboardEvent keyDownEvent = initialKeyEvent;
3255 if (keyDownEvent.type() != PlatformEvent::RawKeyDown)
3256 keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown, backwardCompatibilityMode);
3257 auto keydown = KeyboardEvent::create(keyDownEvent, &m_frame.windowProxy());
3258 if (matchedAnAccessKey)
3259 keydown->preventDefault();
3260 keydown->setTarget(element);
3262 if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) {
3263 element->dispatchEvent(keydown);
3264 // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame.
3265 bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame();
3266 return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3269 // Run input method in advance of DOM event handling. This may result in the IM
3270 // modifying the page prior the keydown event, but this behaviour is necessary
3271 // in order to match IE:
3272 // 1. preventing default handling of keydown and keypress events has no effect on IM input;
3273 // 2. if an input method handles the event, its keyCode is set to 229 in keydown event.
3274 m_frame.editor().handleInputMethodKeydown(keydown.get());
3276 bool handledByInputMethod = keydown->defaultHandled();
3278 if (handledByInputMethod) {
3279 keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode);
3280 keydown = KeyboardEvent::create(keyDownEvent, &m_frame.windowProxy());
3281 keydown->setTarget(element);
3282 keydown->setDefaultHandled();
3285 if (accessibilityPreventsEventPropagation(keydown))
3286 keydown->stopPropagation();
3288 element->dispatchEvent(keydown);
3289 if (handledByInputMethod)
3292 // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame.
3293 bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame();
3294 bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3295 if (keydownResult && !backwardCompatibilityMode)
3296 return keydownResult;
3298 // Focus may have changed during keydown handling, so refetch element.
3299 // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original element.
3300 if (!keydownResult) {
3301 element = eventTargetElementForDocument(m_frame.document());
3306 PlatformKeyboardEvent keyPressEvent = initialKeyEvent;
3307 keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char, backwardCompatibilityMode);
3308 if (keyPressEvent.text().isEmpty())
3309 return keydownResult;
3310 auto keypress = KeyboardEvent::create(keyPressEvent, &m_frame.windowProxy());
3311 keypress->setTarget(element);
3313 keypress->preventDefault();
3315 keypress->keypressCommands() = keydown->keypressCommands();
3317 element->dispatchEvent(keypress);
3319 return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled();
3322 static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier)
3324 static NeverDestroyed<AtomicString> Down("Down", AtomicString::ConstructFromLiteral);
3325 static NeverDestroyed<AtomicString> Up("Up", AtomicString::ConstructFromLiteral);
3326 static NeverDestroyed<AtomicString> Left("Left", AtomicString::ConstructFromLiteral);
3327 static NeverDestroyed<AtomicString> Right("Right", AtomicString::ConstructFromLiteral);
3329 FocusDirection retVal = FocusDirectionNone;
3331 if (keyIdentifier == Down)
3332 retVal = FocusDirectionDown;
3333 else if (keyIdentifier == Up)
3334 retVal = FocusDirectionUp;
3335 else if (keyIdentifier == Left)
3336 retVal = FocusDirectionLeft;
3337 else if (keyIdentifier == Right)
3338 retVal = FocusDirectionRight;
3343 static void setInitialKeyboardSelection(Frame& frame, SelectionDirection direction)
3345 Document* document = frame.document();
3349 FrameSelection& selection = frame.selection();
3351 if (!selection.isNone())
3354 Element* focusedElement = document->focusedElement();
3355 VisiblePosition visiblePosition;
3357 switch (direction) {
3358 case DirectionBackward:
3361 visiblePosition = VisiblePosition(positionBeforeNode(focusedElement));
3363 visiblePosition = endOfDocument(document);
3365 case DirectionForward:
3366 case DirectionRight:
3368 visiblePosition = VisiblePosition(positionAfterNode(focusedElement));
3370 visiblePosition = startOfDocument(document);
3374 AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
3375 selection.setSelection(visiblePosition, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent);
3378 static void handleKeyboardSelectionMovement(Frame& frame, KeyboardEvent& event)
3380 FrameSelection& selection = frame.selection();
3382 bool isCommanded = event.getModifierState("Meta");
3383 bool isOptioned = event.getModifierState("Alt");
3384 bool isSelection = !selection.isNone();
3386 FrameSelection::EAlteration alternation = event.getModifierState("Shift") ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove;
3387 SelectionDirection direction = DirectionForward;
3388 TextGranularity granularity = CharacterGranularity;
3390 switch (focusDirectionForKey(event.keyIdentifier())) {
3391 case FocusDirectionNone:
3393 case FocusDirectionForward:
3394 case FocusDirectionBackward:
3395 ASSERT_NOT_REACHED();
3397 case FocusDirectionUp:
3398 direction = DirectionBackward;
3399 granularity = isCommanded ? DocumentBoundary : LineGranularity;
3401 case FocusDirectionDown:
3402 direction = DirectionForward;
3403 granularity = isCommanded ? DocumentBoundary : LineGranularity;
3405 case FocusDirectionLeft:
3406 direction = DirectionLeft;
3407 granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity;
3409 case FocusDirectionRight:
3410 direction = DirectionRight;
3411 granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity;
3416 selection.modify(alternation, direction, granularity, UserTriggered);
3418 setInitialKeyboardSelection(frame, direction);
3420 event.setDefaultHandled();
3423 void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent& event)
3425 if (event.type() == eventNames().keydownEvent) {
3426 if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
3427 handleKeyboardSelectionMovement(m_frame, event);
3431 bool EventHandler::accessibilityPreventsEventPropagation(KeyboardEvent& event)
3434 if (!AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
3437 if (!m_frame.settings().preventKeyboardDOMEventDispatch())
3440 // Check for key events that are relevant to accessibility: tab and arrows keys that change focus
3441 if (event.keyIdentifier() == "U+0009")
3443 FocusDirection direction = focusDirectionForKey(event.keyIdentifier());
3444 if (direction != FocusDirectionNone)
3447 UNUSED_PARAM(event);
3452 void EventHandler::defaultKeyboardEventHandler(KeyboardEvent& event)
3454 Ref<Frame> protectedFrame(m_frame);
3456 if (event.type() == eventNames().keydownEvent) {
3457 m_frame.editor().handleKeyboardEvent(event);
3458 if (event.defaultHandled())
3460 if (event.keyIdentifier() == "U+0009")
3461 defaultTabEventHandler(event);
3462 else if (event.keyIdentifier() == "U+0008")
3463 defaultBackspaceEventHandler(event);
3465 FocusDirection direction = focusDirectionForKey(event.keyIdentifier());
3466 if (direction != FocusDirectionNone)
3467 defaultArrowEventHandler(direction, event);
3470 handleKeyboardSelectionMovementForAccessibility(event);
3472 if (event.type() == eventNames().keypressEvent) {
3473 m_frame.editor().handleKeyboardEvent(event);
3474 if (event.defaultHandled())
3476 if (event.charCode() == ' ')
3477 defaultSpaceEventHandler(event);
3481 #if ENABLE(DRAG_SUPPORT)
3482 bool EventHandler::dragHysteresisExceeded(const IntPoint& floatDragViewportLocation) const
3484 FloatPoint dragViewportLocation(floatDragViewportLocation.x(), floatDragViewportLocation.y());
3485 return dragHysteresisExceeded(dragViewportLocation);
3488 bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation) const
3490 int threshold = GeneralDragHysteresis;
3491 switch (dragState().type) {
3492 case DragSourceActionSelection:
3493 threshold = TextDragHysteresis;
3495 case DragSourceActionImage:
3496 #if ENABLE(ATTACHMENT_ELEMENT)
3497 case DragSourceActionAttachment:
3499 threshold = ImageDragHysteresis;
3501 case DragSourceActionLink:
3502 threshold = LinkDragHysteresis;
3504 case DragSourceActionDHTML:
3506 case DragSourceActionNone:
3507 case DragSourceActionAny:
3508 ASSERT_NOT_REACHED();
3511 return mouseMovementExceedsThreshold(dragViewportLocation, threshold);
3514 void EventHandler::invalidateDataTransfer()
3516 if (!dragState().dataTransfer)
3518 dragState().dataTransfer->makeInvalidForSecurity();
3519 dragState().dataTransfer = nullptr;
3522 static void removeDraggedContentDocumentMarkersFromAllFramesInPage(Page& page)
3524 for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
3525 if (auto* document = frame->document())
3526 document->markers().removeMarkers(DocumentMarker::DraggedContent);
3529 if (auto* mainFrameRenderer = page.mainFrame().contentRenderer())
3530 mainFrameRenderer->repaintRootContents();
3533 void EventHandler::dragCancelled()
3535 #if ENABLE(DATA_INTERACTION)
3536 if (auto* page = m_frame.page())
3537 removeDraggedContentDocumentMarkersFromAllFramesInPage(*page);
3541 void EventHandler::didStartDrag()
3543 #if ENABLE(DATA_INTERACTION)
3544 auto dragSource = dragState().source;
3548 auto* renderer = dragSource->renderer();
3552 RefPtr<Range> draggedContentRange;
3553 if (dragState().type & DragSourceActionSelection)
3554 draggedContentRange = m_frame.selection().selection().toNormalizedRange();
3556 Position startPosition(dragSource.get(), Position::PositionIsBeforeAnchor);
3557 Position endPosition(dragSource.get(), Position::PositionIsAfterAnchor);
3558 draggedContentRange = Range::create(dragSource->document(), startPosition, endPosition);
3561 if (draggedContentRange) {
3562 draggedContentRange->ownerDocument().markers().addDraggedContentMarker(draggedContentRange.get());
3563 if (auto* renderer = m_frame.contentRenderer())
3564 renderer->repaintRootContents();
3569 void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation, MayExtendDragSession mayExtendDragSession)
3571 // Send a hit test request so that RenderLayer gets a chance to update the :hover and :active pseudoclasses.
3572 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
3573 prepareMouseEvent(request, event);
3575 if (dragState().source && dragState().shouldDispatchEvents) {
3576 dragState().dataTransfer->setDestinationOperation(operation);
3577 dispatchDragSrcEvent(eventNames().dragendEvent, event);
3579 invalidateDataTransfer();
3581 if (mayExtendDragSession == MayExtendDragSession::No) {
3582 if (auto* page = m_frame.page())
3583 removeDraggedContentDocumentMarkersFromAllFramesInPage(*page);
3586 dragState().source = nullptr;
3587 // In case the drag was ended due to an escape key press we need to ensure
3588 // that consecutive mousemove events don't reinitiate the drag and drop.
3589 m_mouseDownMayStartDrag = false;
3592 void EventHandler::updateDragStateAfterEditDragIfNeeded(Element& rootEditableElement)
3594 // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editable element.
3595 if (dragState().source && !dragState().source->isConnected())
3596 dragState().source = &rootEditableElement;
3599 void EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent& event)
3601 ASSERT(dragState().dataTransfer);
3602 dispatchDragEvent(eventType, *dragState().source, event, *dragState().dataTransfer);
3605 bool EventHandler::dispatchDragStartEventOnSourceElement(DataTransfer& dataTransfer)
3607 return !dispatchDragEvent(eventNames().dragstartEvent, *dragState().source, m_mouseDown, dataTransfer) && !m_frame.selection().selection().isInPasswordField();
3610 static bool ExactlyOneBitSet(DragSourceAction n)
3612 return n && !(n & (n - 1));
3615 RefPtr<Element> EventHandler::draggedElement() const
3617 return dragState().source;
3620 bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis)
3622 if (event.event().button() != LeftButton || event.event().type() != PlatformEvent::MouseMoved) {
3623 // If we allowed the other side of the bridge to handle a drag
3624 // last time, then m_mousePressed might still be set. So we
3625 // clear it now to make sure the next move after a drag
3626 // doesn't look like a drag.
3627 m_mousePressed = false;
3631 Ref<Frame> protectedFrame(m_frame);
3633 if (eventLoopHandleMouseDragged(event))
3636 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
3638 if (m_mouseDownMayStartDrag && !dragState().source) {
3639 dragState().shouldDispatchEvents = (updateDragSourceActionsAllowed() & DragSourceActionDHTML);
3641 // try to find an element that wants to be dragged
3642 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
3643 HitTestResult result(m_mouseDownPos);
3644 m_frame.contentRenderer()->hitTest(request, result);
3646 dragState().source = m_frame.page()->dragController().draggableElement(&m_frame, result.targetElement(), m_mouseDownPos, dragState());
3648 if (!dragState().source)
3649 m_mouseDownMayStartDrag = false; // no element is draggable
3651 m_dragMayStartSelectionInstead = (dragState().type & DragSourceActionSelection);
3654 // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
3655 // or else we bail on the dragging stuff and allow selection to occur
3656 if (m_mouseDownMayStartDrag && m_dragMayStartSelectionInstead && (dragState().type & DragSourceActionSelection) && event.event().timestamp() - m_mouseDownTimestamp < TextDragDelay) {
3657 ASSERT(event.event().type() == PlatformEvent::MouseMoved);
3658 if ((dragState().type & DragSourceActionImage)) {
3659 // ... unless the mouse is over an image, then we start dragging just the image
3660 dragState().type = DragSourceActionImage;
3661 } else if (!(dragState().type & (DragSourceActionDHTML | DragSourceActionLink))) {
3662 // ... but only bail if we're not over an unselectable element.
3663 m_mouseDownMayStartDrag = false;
3664 dragState().source = nullptr;
3665 // ... but if this was the first click in the window, we don't even want to start selection
3666 if (eventActivatedView(event.event()))
3667 m_mouseDownMayStartSelect = false;
3669 // Prevent the following case from occuring:
3670 // 1. User starts a drag immediately after mouse down over an unselectable element.
3671 // 2. We enter this block and decided that since we're over an unselectable element, don't cancel the drag.
3672 // 3. The drag gets resolved as a potential selection drag below /but/ we haven't exceeded the drag hysteresis yet.
3673 // 4. We enter this block again, and since it's now marked as a selection drag, we cancel the drag.
3674 m_dragMayStartSelectionInstead = false;
3678 if (!m_mouseDownMayStartDrag)
3679 return !mouseDownMayStartSelect() && !m_mouseDownMayStartAutoscroll;
3680 ASSERT(dragState().source);
3682 if (!ExactlyOneBitSet(dragState().type)) {
3683 ASSERT((dragState().type & DragSourceActionSelection));
3684 #if ENABLE(ATTACHMENT_ELEMENT)
3685 ASSERT((dragState().type & ~DragSourceActionSelection) == DragSourceActionDHTML
3686 || (dragState().type & ~DragSourceActionSelection) == DragSourceActionImage
3687 || (dragState().type & ~DragSourceActionSelection) == DragSourceActionAttachment
3688 || (dragState().type & ~DragSourceActionSelection) == DragSourceActionLink);
3690 ASSERT((dragState().type & ~DragSourceActionSelection) == DragSourceActionDHTML
3691 || (dragState().type & ~DragSourceActionSelection) == DragSourceActionImage
3692 || (dragState().type & ~DragSourceActionSelection) == DragSourceActionLink);
3694 dragState().type = DragSourceActionSelection;
3697 // We are starting a text/image/url drag, so the cursor should be an arrow
3698 if (FrameView* view = m_frame.view()) {
3699 // FIXME <rdar://7577595>: Custom cursors aren't supported during drag and drop (default to pointer).
3700 view->setCursor(pointerCursor());
3703 if (checkDragHysteresis == ShouldCheckDragHysteresis && !dragHysteresisExceeded(event.event().position()))
3706 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
3709 DragOperation srcOp = DragOperationNone;
3711 // This does work only if we missed a dragEnd. Do it anyway, just to make sure the old dataTransfer gets numbed.
3712 invalidateDataTransfer();
3714 dragState().dataTransfer = DataTransfer::createForDrag();
3715 HasNonDefaultPasteboardData hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::No;
3717 if (dragState().shouldDispatchEvents) {
3718 ASSERT(dragState().source);
3719 auto dragStartDataTransfer = DataTransfer::createForDragStartEvent(dragState().source->document());
3720 m_mouseDownMayStartDrag = dispatchDragStartEventOnSourceElement(dragStartDataTransfer);
3721 hasNonDefaultPasteboardData = dragStartDataTransfer->pasteboard().hasData() ? HasNonDefaultPasteboardData::Yes : HasNonDefaultPasteboardData::No;
3722 dragState().dataTransfer->moveDragState(WTFMove(dragStartDataTransfer));
3724 if (dragState().source && dragState().type == DragSourceActionDHTML && !dragState().dataTransfer->hasDragImage()) {
3725 dragState().source->document().updateStyleIfNeeded();
3726 if (auto* renderer = dragState().source->renderer()) {
3727 auto absolutePosition = renderer->localToAbsolute();
3728 auto delta = m_mouseDownPos - roundedIntPoint(absolutePosition);
3729 dragState().dataTransfer->setDragImage(dragState().source.get(), delta.width(), delta.height());
3731 dispatchDragSrcEvent(eventNames().dragendEvent, event.event());
3732 m_mouseDownMayStartDrag = false;
3733 invalidateDataTransfer();
3734 dragState().source = nullptr;
3739 dragState().dataTransfer->makeInvalidForSecurity();
3741 if (m_mouseDownMayStartDrag) {
3742 // Gather values from DHTML element, if it set any.
3743 srcOp = dragState().dataTransfer->sourceOperation();
3745 // Yuck, a draggedImage:moveTo: message can be fired as a result of kicking off the
3746 // drag with dragImage! Because of that dumb reentrancy, we may think we've not
3747 // started the drag when that happens. So we have to assume it's started before we kick it off.
3748 dragState().dataTransfer->setDragHasStarted();
3752 if (m_mouseDownMayStartDrag) {
3753 Page* page = m_frame.page();
3754 m_didStartDrag = page && page->dragController().startDrag(m_frame, dragState(), srcOp, event.event(), m_mouseDownPos, hasNonDefaultPasteboardData);
3755 // In WebKit2 we could re-enter this code and start another drag.
3756 // On OS X this causes problems with the ownership of the pasteboard and the promised types.
3757 if (m_didStartDrag) {
3758 m_mouseDownMayStartDrag = false;
3761 if (dragState().source && dragState().shouldDispatchEvents) {
3762 // Drag was canned at the last minute. We owe dragSource a dragend event.
3763 dispatchDragSrcEvent(eventNames().dragendEvent, event.event());
3764 m_mouseDownMayStartDrag = false;
3768 if (!m_mouseDownMayStartDrag) {
3769 // Something failed to start the drag, clean up.
3770 invalidateDataTransfer();
3771 dragState().source = nullptr;
3774 // No more default handling (like selection), whether we're past the hysteresis bounds or not
3777 #endif // ENABLE(DRAG_SUPPORT)
3779 bool EventHandler::mouseMovementExceedsThreshold(const FloatPoint& viewportLocation, int pointsThreshold) const
3781 FrameView* view = m_frame.view();
3784 IntPoint location = view->windowToContents(flooredIntPoint(viewportLocation));
3785 IntSize delta = location - m_mouseDownPos;
3787 return abs(delta.width()) >= pointsThreshold || abs(delta.height()) >= pointsThreshold;
3790 bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEvent, TextEventInputType inputType)
3792 LOG(Editing, "EventHandler %p handleTextInputEvent (text %s)", this, text.utf8().data());
3794 // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline),
3795 // and avoid dispatching text input events from keydown default handlers.
3796 ASSERT(!is<KeyboardEvent>(underlyingEvent) || downcast<KeyboardEvent>(*underlyingEvent).type() == eventNames().keypressEvent);
3798 Ref<Frame> protectedFrame(m_frame);
3800 EventTarget* target;
3801 if (underlyingEvent)
3802 target = underlyingEvent->target();
3804 target = eventTargetElementForDocument(m_frame.document());
3808 if (FrameView* view = m_frame.view())
3809 view->disableLayerFlushThrottlingTemporarilyForInteraction();
3811 auto event = TextEvent::create(&m_frame.windowProxy(), text, inputType);
3812 event->setUnderlyingEvent(underlyingEvent);
3814 target->dispatchEvent(event);
3815 return event->defaultHandled();
3818 bool EventHandler::isKeyboardOptionTab(KeyboardEvent& event)
3820 return (event.type() == eventNames().keydownEvent || event.type() == eventNames().keypressEvent)
3822 && event.keyIdentifier() == "U+0009";
3825 bool EventHandler::eventInvertsTabsToLinksClientCallResult(KeyboardEvent& event)
3828 return isKeyboardOptionTab(event);
3830 UNUSED_PARAM(event);
3835 bool EventHandler::tabsToLinks(KeyboardEvent* event) const
3837 // FIXME: This function needs a better name. It can be called for keypresses other than Tab when spatial navigation is enabled.
3839 Page* page = m_frame.page();
3843 bool tabsToLinksClientCallResult = page->chrome().client().keyboardUIMode() & KeyboardAccessTabsToLinks;
3844 return (event && eventInvertsTabsToLinksClientCallResult(*event)) ? !tabsToLinksClientCallResult : tabsToLinksClientCallResult;
3847 void EventHandler::defaultTextInputEventHandler(TextEvent& event)
3849 if (m_frame.editor().handleTextEvent(event))
3850 event.setDefaultHandled();
3854 void EventHandler::defaultSpaceEventHandler(KeyboardEvent& event)
3856 Ref<Frame> protectedFrame(m_frame);
3858 ASSERT(event.type() == eventNames().keypressEvent);
3860 if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey())
3863 ScrollLogicalDirection direction = event.shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward;
3864 if (logicalScrollOverflow(direction, ScrollByPage)) {
3865 event.setDefaultHandled();
3869 FrameView* view = m_frame.view();
3873 if (view->logicalScroll(direction, ScrollByPage))
3874 event.setDefaultHandled();
3877 void EventHandler::defaultBackspaceEventHandler(KeyboardEvent& event)
3879 ASSERT(event.type() == eventNames().keydownEvent);
3881 if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey())
3884 if (!m_frame.editor().behavior().shouldNavigateBackOnBackspace())
3887 Page* page = m_frame.page();
3891 if (!m_frame.settings().backspaceKeyNavigationEnabled())
3894 bool handledEvent = false;
3896 if (event.shiftKey())
3897 handledEvent = page->backForward().goForward();
3899 handledEvent = page->backForward().goBack();
3902 event.setDefaultHandled();
3906 void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent& event)
3908 ASSERT(event.type() == eventNames().keydownEvent);
3910 if (event.ctrlKey() || event.metaKey() || event.altGraphKey() || event.shiftKey())
3913 Page* page = m_frame.page();
3917 if (!isSpatialNavigationEnabled(&m_frame))
3920 // Arrows and other possible directional navigation keys can be used in design
3922 if (m_frame.document()->inDesignMode())
3925 if (page->focusController().advanceFocus(focusDirection, &event))
3926 event.setDefaultHandled();
3929 void EventHandler::defaultTabEventHandler(KeyboardEvent& event)
3931 Ref<Frame> protectedFrame(m_frame);
3933 ASSERT(event.type() == eventNames().keydownEvent);
3935 // We should only advance focus on tabs if no special modifier keys are held down.
3936 if (event.ctrlKey() || event.metaKey() || event.altGraphKey())
3939 Page* page = m_frame.page();
3942 if (!page->tabKeyCyclesThroughElements())
3945 FocusDirection focusDirection = event.shiftKey() ? FocusDirectionBackward : FocusDirectionForward;
3947 // Tabs can be used in design mode editing.
3948 if (m_frame.document()->inDesignMode())
3951 if (page->focusController().advanceFocus(focusDirection, &event))
3952 event.setDefaultHandled();
3955 void EventHandler::sendScrollEvent()
3957 Ref<Frame> protectedFrame(m_frame);
3958 setFrameWasScrolledByUser();
3959 if (m_frame.view() && m_frame.document())
3960 m_frame.document()->eventQueue().enqueueOrDispatchScrollEvent(*m_frame.document());
3963 void EventHandler::setFrameWasScrolledByUser()
3965 FrameView* v = m_frame.view();
3967 v->setWasScrolledByUser(true);
3970 bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mouseEvent, Scrollbar* scrollbar)
3972 if (!scrollbar || !scrollbar->enabled())
3974 setFrameWasScrolledByUser();
3975 return scrollbar->mouseDown(mouseEvent.event());
3978 // If scrollbar (under mouse) is different from last, send a mouse exited.
3979 void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, SetOrClearLastScrollbar setOrClear)
3981 if (m_lastScrollbarUnderMouse != scrollbar) {
3982 // Send mouse exited to the old scrollbar.
3983 if (m_lastScrollbarUnderMouse)
3984 m_lastScrollbarUnderMouse->mouseExited();
3986 // Send mouse entered if we're setting a new scrollbar.
3987 if (scrollbar && setOrClear == SetOrClearLastScrollbar::Set) {
3988 scrollbar->mouseEntered();
3989 m_lastScrollbarUnderMouse = makeWeakPtr(*scrollbar);
3991 m_lastScrollbarUnderMouse = nullptr;
3995 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
3996 static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State state)
3999 case PlatformTouchPoint::TouchReleased:
4000 return eventNames().touchendEvent;
4001 case PlatformTouchPoint::TouchCancelled:
4002 return eventNames().touchcancelEvent;
4003 case PlatformTouchPoint::TouchPressed:
4004 return eventNames().touchstartEvent;
4005 case PlatformTouchPoint::TouchMoved:
4006 return eventNames().touchmoveEvent;
4007 case PlatformTouchPoint::TouchStationary:
4008 // TouchStationary state is not converted to touch events, so fall through to assert.
4010 ASSERT_NOT_REACHED();
4015 static HitTestResult hitTestResultInFrame(Frame* frame, const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType)
4017 HitTestResult result(point);
4019 if (!frame || !frame->contentRenderer())
4022 if (frame->view()) {
4023 IntRect rect = frame->view()->visibleContentRect();
4024 if (!rect.contains(roundedIntPoint(point)))
4027 frame->contentRenderer()->hitTest(HitTestRequest(hitType), result);
4031 bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
4033 Ref<Frame> protectedFrame(m_frame);
4035 // First build up the lists to use for the 'touches', 'targetTouches' and 'changedTouches' attributes
4036 // in the JS event. See http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/
4037 // for an overview of how these lists fit together.
4039 // Holds the complete set of touches on the screen and will be used as the 'touches' list in the JS event.
4040 RefPtr<TouchList> touches = TouchList::create();
4042 // A different view on the 'touches' list above, filtered and grouped by event target. Used for the
4043 // 'targetTouches' list in the JS event.
4044 typedef HashMap<EventTarget*, RefPtr<TouchList>> TargetTouchesMap;
4045 TargetTouchesMap touchesByTarget;
4047 // Array of touches per state, used to assemble the 'changedTouches' list in the JS event.
4048 typedef HashSet<RefPtr<EventTarget>> EventTargetSet;
4050 // The touches corresponding to the particular change state this struct instance represents.
4051 RefPtr<TouchList> m_touches;
4052 // Set of targets involved in m_touches.
4053 EventTargetSet m_targets;
4054 } changedTouches[PlatformTouchPoint::TouchStateEnd];
4056 const Vector<PlatformTouchPoint>& points = event.touchPoints();
4058 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
4060 bool freshTouchEvents = true;
4061 bool allTouchReleased = true;
4062 for (auto& point : points) {
4063 if (point.state() != PlatformTouchPoint::TouchPressed)
4064 freshTouchEvents = false;
4065 if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled)
4066 allTouchReleased = false;
4069 for (auto& point : points) {
4070 PlatformTouchPoint::State pointState = point.state();
4071 LayoutPoint pagePoint = documentPointForWindowPoint(m_frame, point.pos());
4073 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent;
4074 // The HitTestRequest types used for mouse events map quite adequately
4075 // to touch events. Note that in addition to meaning that the hit test
4076 // should affect the active state of the current node if necessary,
4077 // HitTestRequest::Active signifies that the hit test is taking place
4078 // with the mouse (or finger in this case) being pressed.
4079 switch (pointState) {
4080 case PlatformTouchPoint::TouchPressed:
4081 hitType |= HitTestRequest::Active;
4083 case PlatformTouchPoint::TouchMoved:
4084 hitType |= HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::ReadOnly;
4086 case PlatformTouchPoint::TouchReleased:
4087 case PlatformTouchPoint::TouchCancelled:
4088 hitType |= HitTestRequest::Release;
4090 case PlatformTouchPoint::TouchStationary:
4091 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
4094 ASSERT_NOT_REACHED();
4098 if (shouldGesturesTriggerActive())