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"
48 #include "FrameLoader.h"
49 #include "FrameSelection.h"
50 #include "FrameTree.h"
51 #include "FrameView.h"
52 #include "HTMLFrameElement.h"
53 #include "HTMLFrameSetElement.h"
54 #include "HTMLHtmlElement.h"
55 #include "HTMLIFrameElement.h"
56 #include "HTMLInputElement.h"
57 #include "HTMLNames.h"
58 #include "HitTestRequest.h"
59 #include "HitTestResult.h"
61 #include "InspectorInstrumentation.h"
62 #include "KeyboardEvent.h"
64 #include "MainFrame.h"
65 #include "MouseEvent.h"
66 #include "MouseEventWithHitTestResults.h"
68 #include "PageOverlayController.h"
69 #include "PlatformEvent.h"
70 #include "PlatformKeyboardEvent.h"
71 #include "PlatformWheelEvent.h"
72 #include "PluginDocument.h"
74 #include "RenderFrameSet.h"
75 #include "RenderLayer.h"
76 #include "RenderListBox.h"
77 #include "RenderNamedFlowThread.h"
78 #include "RenderTextControlSingleLine.h"
79 #include "RenderView.h"
80 #include "RenderWidget.h"
81 #include "RuntimeApplicationChecks.h"
82 #include "SVGDocument.h"
84 #include "ScrollLatchingState.h"
85 #include "Scrollbar.h"
87 #include "ShadowRoot.h"
88 #include "SpatialNavigation.h"
89 #include "StyleCachedImage.h"
90 #include "TextEvent.h"
91 #include "TextIterator.h"
92 #include "UserGestureIndicator.h"
93 #include "UserTypingGestureIndicator.h"
94 #include "ValidationMessageClient.h"
95 #include "VisibleUnits.h"
96 #include "WheelEvent.h"
97 #include "WheelEventDeltaFilter.h"
98 #include "WindowsKeyboardCodes.h"
99 #include <wtf/Assertions.h>
100 #include <wtf/CurrentTime.h>
101 #include <wtf/NeverDestroyed.h>
102 #include <wtf/StdLibExtras.h>
104 #if ENABLE(IOS_TOUCH_EVENTS)
105 #include "PlatformTouchEventIOS.h"
108 #if ENABLE(TOUCH_EVENTS)
109 #include "TouchEvent.h"
110 #include "TouchList.h"
113 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
114 #include "PlatformTouchEvent.h"
117 #if ENABLE(MAC_GESTURE_EVENTS)
118 #include "PlatformGestureEventMac.h"
121 #if ENABLE(POINTER_LOCK)
122 #include "PointerLockController.h"
127 using namespace HTMLNames;
129 #if ENABLE(DRAG_SUPPORT)
130 // The link drag hysteresis is much larger than the others because there
131 // needs to be enough space to cancel the link press without starting a link drag,
132 // and because dragging links is rare.
133 const int LinkDragHysteresis = 40;
134 const int ImageDragHysteresis = 5;
135 const int TextDragHysteresis = 3;
136 const int GeneralDragHysteresis = 3;
138 const double EventHandler::TextDragDelay = 0.15;
140 #endif // ENABLE(DRAG_SUPPORT)
142 #if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS)
143 const float GestureUnknown = 0;
146 #if ENABLE(IOS_TOUCH_EVENTS)
147 // FIXME: Share this constant with EventHandler and SliderThumbElement.
148 const unsigned InvalidTouchIdentifier = 0;
151 // Match key code of composition keydown event on windows.
152 // IE sends VK_PROCESSKEY which has value 229;
153 const int CompositionEventKeyCode = 229;
155 using namespace SVGNames;
157 #if !ENABLE(IOS_TOUCH_EVENTS)
158 // The amount of time to wait before sending a fake mouse event, triggered
159 // during a scroll. The short interval is used if the content responds to the mouse events
160 // in fakeMouseMoveDurationThreshold or less, otherwise the long interval is used.
161 const double fakeMouseMoveDurationThreshold = 0.01;
162 const Seconds fakeMouseMoveShortInterval = { 100_ms };
163 const Seconds fakeMouseMoveLongInterval = { 250_ms };
166 #if ENABLE(CURSOR_SUPPORT)
167 // The amount of time to wait for a cursor update on style and layout changes
168 // Set to 50Hz, no need to be faster than common screen refresh rate
169 static const Seconds cursorUpdateInterval { 20_ms };
171 const int maximumCursorSize = 128;
174 #if ENABLE(MOUSE_CURSOR_SCALE)
175 // It's pretty unlikely that a scale of less than one would ever be used. But all we really
176 // need to ensure here is that the scale isn't so small that integer overflow can occur when
177 // dividing cursor sizes (limited above) by the scale.
178 const double minimumCursorScale = 0.001;
181 class MaximumDurationTracker {
183 explicit MaximumDurationTracker(double *maxDuration)
184 : m_maxDuration(maxDuration)
185 , m_start(monotonicallyIncreasingTime())
189 ~MaximumDurationTracker()
191 *m_maxDuration = std::max(*m_maxDuration, monotonicallyIncreasingTime() - m_start);
195 double* m_maxDuration;
199 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
200 class SyntheticTouchPoint : public PlatformTouchPoint {
203 // The default values are based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
204 explicit SyntheticTouchPoint(const PlatformMouseEvent& event)
206 const static int idDefaultValue = 0;
207 const static int radiusYDefaultValue = 1;
208 const static int radiusXDefaultValue = 1;
209 const static float rotationAngleDefaultValue = 0.0f;
210 const static float forceDefaultValue = 1.0f;
212 m_id = idDefaultValue; // There is only one active TouchPoint.
213 m_screenPos = event.globalPosition();
214 m_pos = event.position();
215 m_radiusY = radiusYDefaultValue;
216 m_radiusX = radiusXDefaultValue;
217 m_rotationAngle = rotationAngleDefaultValue;
218 m_force = forceDefaultValue;
220 PlatformEvent::Type type = event.type();
221 ASSERT(type == PlatformEvent::MouseMoved || type == PlatformEvent::MousePressed || type == PlatformEvent::MouseReleased);
224 case PlatformEvent::MouseMoved:
225 m_state = TouchMoved;
227 case PlatformEvent::MousePressed:
228 m_state = TouchPressed;
230 case PlatformEvent::MouseReleased:
231 m_state = TouchReleased;
234 ASSERT_NOT_REACHED();
240 class SyntheticSingleTouchEvent : public PlatformTouchEvent {
242 explicit SyntheticSingleTouchEvent(const PlatformMouseEvent& event)
244 switch (event.type()) {
245 case PlatformEvent::MouseMoved:
248 case PlatformEvent::MousePressed:
251 case PlatformEvent::MouseReleased:
255 ASSERT_NOT_REACHED();
259 m_timestamp = event.timestamp();
260 m_modifiers = event.modifiers();
261 m_touchPoints.append(SyntheticTouchPoint(event));
264 #endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
266 static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned deltaMode)
269 case WheelEvent::DOM_DELTA_PAGE:
271 case WheelEvent::DOM_DELTA_LINE:
273 case WheelEvent::DOM_DELTA_PIXEL:
274 return ScrollByPixel;
276 return ScrollByPixel;
280 static inline bool didScrollInScrollableArea(ScrollableArea* scrollableArea, WheelEvent& wheelEvent)
282 ScrollGranularity scrollGranularity = wheelGranularityToScrollGranularity(wheelEvent.deltaMode());
283 bool didHandleWheelEvent = false;
284 if (float absoluteDelta = std::abs(wheelEvent.deltaX()))
285 didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaX() > 0 ? ScrollRight : ScrollLeft, scrollGranularity, absoluteDelta);
287 if (float absoluteDelta = std::abs(wheelEvent.deltaY()))
288 didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaY() > 0 ? ScrollDown : ScrollUp, scrollGranularity, absoluteDelta);
290 return didHandleWheelEvent;
293 static inline bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, WheelEvent& wheelEvent, Element** stopElement, const FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity)
295 bool shouldHandleEvent = wheelEvent.deltaX() || wheelEvent.deltaY();
297 shouldHandleEvent |= wheelEvent.phase() == PlatformWheelEventPhaseEnded;
298 #if ENABLE(CSS_SCROLL_SNAP)
299 shouldHandleEvent |= wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded;
302 if (!startNode->renderer() || !shouldHandleEvent)
305 RenderBox& initialEnclosingBox = startNode->renderer()->enclosingBox();
306 if (initialEnclosingBox.isListBox())
307 return didScrollInScrollableArea(static_cast<RenderListBox*>(&initialEnclosingBox), wheelEvent);
309 RenderBox* currentEnclosingBox = &initialEnclosingBox;
310 while (currentEnclosingBox) {
311 if (RenderLayer* boxLayer = currentEnclosingBox->layer()) {
312 const PlatformWheelEvent* platformEvent = wheelEvent.wheelEvent();
313 bool scrollingWasHandled;
314 if (platformEvent != nullptr) {
315 auto copiedEvent = platformEvent->copyWithDeltasAndVelocity(filteredPlatformDelta.width(), filteredPlatformDelta.height(), filteredVelocity);
316 scrollingWasHandled = boxLayer->handleWheelEvent(copiedEvent);
318 scrollingWasHandled = didScrollInScrollableArea(boxLayer, wheelEvent);
320 if (scrollingWasHandled) {
322 *stopElement = currentEnclosingBox->element();
327 if (stopElement && *stopElement && *stopElement == currentEnclosingBox->element())
330 currentEnclosingBox = currentEnclosingBox->containingBlock();
331 if (currentEnclosingBox && currentEnclosingBox->isRenderNamedFlowThread())
332 currentEnclosingBox = RenderNamedFlowThread::fragmentFromRenderBoxAsRenderBlock(currentEnclosingBox, roundedIntPoint(wheelEvent.absoluteLocation()), initialEnclosingBox);
333 if (!currentEnclosingBox || currentEnclosingBox->isRenderView())
339 #if (ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS))
340 static inline bool shouldGesturesTriggerActive()
342 // If the platform we're on supports GestureTapDown and GestureTapCancel then we'll
343 // rely on them to set the active state. Unfortunately there's no generic way to
344 // know in advance what event types are supported.
351 inline bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&)
356 #if ENABLE(DRAG_SUPPORT)
357 inline bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&)
365 EventHandler::EventHandler(Frame& frame)
367 , m_hoverTimer(*this, &EventHandler::hoverTimerFired)
368 #if ENABLE(CURSOR_SUPPORT)
369 , m_cursorUpdateTimer(*this, &EventHandler::cursorUpdateTimerFired)
372 , m_pendingMomentumWheelEventsTimer(*this, &EventHandler::clearLatchedState)
374 , m_autoscrollController(std::make_unique<AutoscrollController>())
375 #if !ENABLE(IOS_TOUCH_EVENTS)
376 , m_fakeMouseMoveEventTimer(*this, &EventHandler::fakeMouseMoveEventTimerFired)
378 #if ENABLE(CURSOR_VISIBILITY)
379 , m_autoHideCursorTimer(*this, &EventHandler::autoHideCursorTimerFired)
384 EventHandler::~EventHandler()
386 #if !ENABLE(IOS_TOUCH_EVENTS)
387 ASSERT(!m_fakeMouseMoveEventTimer.isActive());
389 #if ENABLE(CURSOR_VISIBILITY)
390 ASSERT(!m_autoHideCursorTimer.isActive());
394 #if ENABLE(DRAG_SUPPORT)
396 DragState& EventHandler::dragState()
398 static NeverDestroyed<DragState> state;
404 void EventHandler::clear()
407 #if ENABLE(CURSOR_SUPPORT)
408 m_cursorUpdateTimer.stop();
410 #if !ENABLE(IOS_TOUCH_EVENTS)
411 m_fakeMouseMoveEventTimer.stop();
413 #if ENABLE(CURSOR_VISIBILITY)
414 cancelAutoHideCursorTimer();
416 m_resizeLayer = nullptr;
417 m_elementUnderMouse = nullptr;
418 m_lastElementUnderMouse = nullptr;
419 m_lastMouseMoveEventSubframe = nullptr;
420 m_lastScrollbarUnderMouse = nullptr;
422 m_clickNode = nullptr;
423 #if ENABLE(IOS_GESTURE_EVENTS)
424 m_gestureInitialDiameter = GestureUnknown;
425 m_gestureInitialRotation = GestureUnknown;
427 #if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS)
428 m_gestureLastDiameter = GestureUnknown;
429 m_gestureLastRotation = GestureUnknown;
430 m_gestureTargets.clear();
432 #if ENABLE(IOS_TOUCH_EVENTS)
434 m_firstTouchID = InvalidTouchIdentifier;
435 m_touchEventTargetSubframe = nullptr;
437 m_frameSetBeingResized = nullptr;
438 #if ENABLE(DRAG_SUPPORT)
439 m_dragTarget = nullptr;
440 m_shouldOnlyFireDragOverEvent = false;
442 m_mousePositionIsUnknown = true;
443 m_lastKnownMousePosition = IntPoint();
444 m_lastKnownMouseGlobalPosition = IntPoint();
445 m_mousePressNode = nullptr;
446 m_mousePressed = false;
447 m_capturesDragging = false;
448 m_capturingMouseEventsElement = nullptr;
450 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
451 m_originatingTouchPointTargets.clear();
452 m_originatingTouchPointDocument = nullptr;
453 m_originatingTouchPointTargetKey = 0;
455 m_maxMouseMovedDuration = 0;
456 m_didStartDrag = false;
459 void EventHandler::nodeWillBeRemoved(Node& nodeToBeRemoved)
461 if (nodeToBeRemoved.contains(m_clickNode.get()))
462 m_clickNode = nullptr;
465 static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection)
467 if (selection.selection() != newSelection && selection.shouldChangeSelection(newSelection))
468 selection.setSelection(newSelection);
471 static inline bool dispatchSelectStart(Node* node)
473 if (!node || !node->renderer())
476 return node->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true));
479 static Node* nodeToSelectOnMouseDownForNode(Node& targetNode)
481 #if ENABLE(USERSELECT_ALL)
482 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(&targetNode))
483 return rootUserSelectAll;
486 if (targetNode.shouldSelectOnMouseDown())
492 static VisibleSelection expandSelectionToRespectSelectOnMouseDown(Node& targetNode, const VisibleSelection& selection)
494 Node* nodeToSelect = nodeToSelectOnMouseDownForNode(targetNode);
498 VisibleSelection newSelection(selection);
499 newSelection.setBase(positionBeforeNode(nodeToSelect).upstream(CanCrossEditingBoundary));
500 newSelection.setExtent(positionAfterNode(nodeToSelect).downstream(CanCrossEditingBoundary));
505 bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity)
507 if (Position::nodeIsUserSelectNone(targetNode))
510 if (!dispatchSelectStart(targetNode))
513 if (selection.isRange())
514 m_selectionInitiationState = ExtendedSelection;
516 granularity = CharacterGranularity;
517 m_selectionInitiationState = PlacedCaret;
520 m_frame.selection().setSelectionByMouseIfDifferent(selection, granularity);
525 void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
527 Node* targetNode = result.targetNode();
528 VisibleSelection newSelection;
530 if (targetNode && targetNode->renderer()) {
531 VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr));
532 if (pos.isNotNull()) {
533 newSelection = VisibleSelection(pos);
534 newSelection.expandUsingGranularity(WordGranularity);
537 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
538 newSelection.appendTrailingWhitespace();
540 updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity);
544 static AppendTrailingWhitespace shouldAppendTrailingWhitespace(const MouseEventWithHitTestResults& result, const Frame& frame)
546 return (result.event().clickCount() == 2 && frame.editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace;
549 void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result)
551 if (m_mouseDownMayStartSelect)
552 selectClosestWordFromHitTestResult(result.hitTestResult(), shouldAppendTrailingWhitespace(result, m_frame));
556 VisibleSelection EventHandler::selectClosestWordFromHitTestResultBasedOnLookup(const HitTestResult&)
558 return VisibleSelection();
562 void EventHandler::selectClosestContextualWordFromMouseEvent(const MouseEventWithHitTestResults& mouseEvent)
564 Node* targetNode = mouseEvent.targetNode();
565 const HitTestResult& result = mouseEvent.hitTestResult();
566 VisibleSelection newSelection;
567 bool appendTrailingWhitespace = shouldAppendTrailingWhitespace(mouseEvent, m_frame);
569 if (targetNode && targetNode->renderer()) {
570 newSelection = selectClosestWordFromHitTestResultBasedOnLookup(result);
571 if (newSelection.isNone()) {
572 VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr));
573 if (pos.isNotNull()) {
574 newSelection = VisibleSelection(pos);
575 newSelection.expandUsingGranularity(WordGranularity);
579 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
580 newSelection.appendTrailingWhitespace();
582 updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity);
586 void EventHandler::selectClosestContextualWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result)
588 Element* urlElement = result.hitTestResult().URLElement();
589 if (!urlElement || !isDraggableLink(*urlElement)) {
590 if (Node* targetNode = result.targetNode()) {
591 if (isEditableNode(*targetNode))
592 return selectClosestWordFromMouseEvent(result);
595 return selectClosestContextualWordFromMouseEvent(result);
598 Node* targetNode = result.targetNode();
600 if (targetNode && targetNode->renderer() && m_mouseDownMayStartSelect) {
601 VisibleSelection newSelection;
602 VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr));
603 if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(*urlElement))
604 newSelection = VisibleSelection::selectionFromContentsOfNode(urlElement);
606 updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity);
610 bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestResults& event)
612 if (event.event().button() != LeftButton)
615 if (m_frame.selection().isRange())
616 // A double-click when range is already selected
617 // should not change the selection. So, do not call
618 // selectClosestWordFromMouseEvent, but do set
619 // m_beganSelectingText to prevent handleMouseReleaseEvent
620 // from setting caret selection.
621 m_selectionInitiationState = ExtendedSelection;
623 selectClosestWordFromMouseEvent(event);
628 bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestResults& event)
630 if (event.event().button() != LeftButton)
633 Node* targetNode = event.targetNode();
634 if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect))
637 VisibleSelection newSelection;
638 VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr));
639 if (pos.isNotNull()) {
640 newSelection = VisibleSelection(pos);
641 newSelection.expandUsingGranularity(ParagraphGranularity);
644 return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), ParagraphGranularity);
647 static int textDistance(const Position& start, const Position& end)
649 RefPtr<Range> range = Range::create(start.anchorNode()->document(), start, end);
650 return TextIterator::rangeLength(range.get(), true);
653 bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event)
655 Ref<Frame> protectedFrame(m_frame);
657 m_frame.document()->updateLayoutIgnorePendingStylesheets();
658 Node* targetNode = event.targetNode();
659 if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect))
662 // Extend the selection if the Shift key is down, unless the click is in a link.
663 bool extendSelection = event.event().shiftKey() && !event.isOverLink();
665 // Don't restart the selection when the mouse is pressed on an
666 // existing selection so we can allow for text dragging.
667 if (FrameView* view = m_frame.view()) {
668 LayoutPoint vPoint = view->windowToContents(event.event().position());
669 if (!extendSelection && m_frame.selection().contains(vPoint)) {
670 m_mouseDownWasSingleClickInSelection = true;
675 VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr));
676 if (visiblePos.isNull())
677 visiblePos = VisiblePosition(firstPositionInOrBeforeNode(targetNode), DOWNSTREAM);
678 Position pos = visiblePos.deepEquivalent();
680 VisibleSelection newSelection = m_frame.selection().selection();
681 TextGranularity granularity = CharacterGranularity;
683 if (extendSelection && newSelection.isCaretOrRange()) {
684 VisibleSelection selectionInUserSelectAll = expandSelectionToRespectSelectOnMouseDown(*targetNode, VisibleSelection(pos));
685 if (selectionInUserSelectAll.isRange()) {
686 if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0)
687 pos = selectionInUserSelectAll.start();
688 else if (comparePositions(newSelection.end(), selectionInUserSelectAll.end()) < 0)
689 pos = selectionInUserSelectAll.end();
692 if (!m_frame.editor().behavior().shouldConsiderSelectionAsDirectional() && pos.isNotNull()) {
693 // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection
694 // was created right-to-left
695 Position start = newSelection.start();
696 Position end = newSelection.end();
697 int distanceToStart = textDistance(start, pos);
698 int distanceToEnd = textDistance(pos, end);
699 if (distanceToStart <= distanceToEnd)
700 newSelection = VisibleSelection(end, pos);
702 newSelection = VisibleSelection(start, pos);
704 newSelection.setExtent(pos);
706 if (m_frame.selection().granularity() != CharacterGranularity) {
707 granularity = m_frame.selection().granularity();
708 newSelection.expandUsingGranularity(m_frame.selection().granularity());
711 newSelection = expandSelectionToRespectSelectOnMouseDown(*targetNode, visiblePos);
713 bool handled = updateSelectionForMouseDownDispatchingSelectStart(targetNode, newSelection, granularity);
715 if (event.event().button() == MiddleButton) {
716 // Ignore handled, since we want to paste to where the caret was placed anyway.
717 handled = handlePasteGlobalSelection(event.event()) || handled;
722 static inline bool canMouseDownStartSelect(Node* node)
724 if (!node || !node->renderer())
727 return node->canStartSelection() || Position::nodeIsUserSelectAll(node);
730 bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event)
732 Ref<Frame> protectedFrame(m_frame);
734 #if ENABLE(DRAG_SUPPORT)
736 dragState().source = nullptr;
739 #if !ENABLE(IOS_TOUCH_EVENTS)
740 cancelFakeMouseMoveEvent();
743 m_frame.document()->updateLayoutIgnorePendingStylesheets();
745 if (ScrollView* scrollView = m_frame.view()) {
746 if (scrollView->isPointInScrollbarCorner(event.event().position()))
750 bool singleClick = event.event().clickCount() <= 1;
752 // If we got the event back, that must mean it wasn't prevented,
753 // so it's allowed to start a drag or selection if it wasn't in a scrollbar.
754 m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode()) && !event.scrollbar();
756 #if ENABLE(DRAG_SUPPORT)
757 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
758 // FIXME: eventMayStartDrag() does not check for shift key press, link or image event targets.
759 // Bug: https://bugs.webkit.org/show_bug.cgi?id=155390
761 // Single mouse down on links or images can always trigger drag-n-drop.
762 bool isMouseDownOnLinkOrImage = event.isOverLink() || event.hitTestResult().image();
763 m_mouseDownMayStartDrag = singleClick && (!event.event().shiftKey() || isMouseDownOnLinkOrImage);
766 m_mouseDownWasSingleClickInSelection = false;
768 m_mouseDown = event.event();
770 if (m_immediateActionStage != ImmediateActionStage::PerformedHitTest)
771 m_immediateActionStage = ImmediateActionStage::None;
773 if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event))
776 if (is<SVGDocument>(*m_frame.document()) && downcast<SVGDocument>(*m_frame.document()).zoomAndPanEnabled()) {
777 if (event.event().shiftKey() && singleClick) {
779 downcast<SVGDocument>(*m_frame.document()).startPan(m_frame.view()->windowToContents(event.event().position()));
784 // We don't do this at the start of mouse down handling,
785 // because we don't want to do it until we know we didn't hit a widget.
789 m_mousePressNode = event.targetNode();
790 m_frame.document()->setFocusNavigationStartingNode(event.targetNode());
792 #if ENABLE(DRAG_SUPPORT)
793 m_dragStartPosition = event.event().position();
796 m_mousePressed = true;
797 m_selectionInitiationState = HaveNotStartedSelection;
799 bool swallowEvent = false;
800 if (event.event().clickCount() == 2)
801 swallowEvent = handleMousePressEventDoubleClick(event);
802 else if (event.event().clickCount() >= 3)
803 swallowEvent = handleMousePressEventTripleClick(event);
805 swallowEvent = handleMousePressEventSingleClick(event);
807 m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect
808 || (m_mousePressNode && m_mousePressNode->renderBox() && m_mousePressNode->renderBox()->canBeProgramaticallyScrolled());
813 #if ENABLE(DRAG_SUPPORT)
814 bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis)
819 Ref<Frame> protectedFrame(m_frame);
821 if (handleDrag(event, checkDragHysteresis))
824 Node* targetNode = event.targetNode();
825 if (event.event().button() != LeftButton || !targetNode)
828 RenderObject* renderer = targetNode->renderer();
830 Element* parent = targetNode->parentOrShadowHostElement();
834 renderer = parent->renderer();
835 if (!renderer || !renderer->isListBox())
839 #if PLATFORM(COCOA) // FIXME: Why does this assertion fire on other platforms?
840 ASSERT(m_mouseDownMayStartSelect || m_mouseDownMayStartAutoscroll);
843 m_mouseDownMayStartDrag = false;
845 if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) {
846 m_autoscrollController->startAutoscrollForSelection(renderer);
847 m_mouseDownMayStartAutoscroll = false;
850 if (m_selectionInitiationState != ExtendedSelection) {
851 HitTestResult result(m_mouseDownPos);
852 m_frame.document()->renderView()->hitTest(HitTestRequest(), result);
854 updateSelectionForMouseDrag(result);
856 updateSelectionForMouseDrag(event.hitTestResult());
860 bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const
862 // This is a pre-flight check of whether the event might lead to a drag being started. Be careful
863 // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag
864 // in handleMousePressEvent
865 RenderView* renderView = m_frame.contentRenderer();
869 if (event.button() != LeftButton || event.clickCount() != 1)
872 FrameView* view = m_frame.view();
876 Page* page = m_frame.page();
880 Ref<Frame> protectedFrame(m_frame);
882 updateDragSourceActionsAllowed();
883 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
884 HitTestResult result(view->windowToContents(event.position()));
885 renderView->hitTest(request, result);
887 Element* targetElement = result.targetElement();
888 return targetElement && page->dragController().draggableElement(&m_frame, targetElement, result.roundedPointInInnerNodeFrame(), state);
891 void EventHandler::updateSelectionForMouseDrag()
893 FrameView* view = m_frame.view();
896 RenderView* renderView = m_frame.contentRenderer();
900 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent);
901 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
902 renderView->hitTest(request, result);
903 updateSelectionForMouseDrag(result);
906 static VisiblePosition selectionExtentRespectingEditingBoundary(const VisibleSelection& selection, const LayoutPoint& localPoint, Node* targetNode)
908 FloatPoint selectionEndPoint = localPoint;
909 Element* editableElement = selection.rootEditableElement();
911 if (!targetNode->renderer())
912 return VisiblePosition();
914 if (editableElement && !editableElement->contains(targetNode)) {
915 if (!editableElement->renderer())
916 return VisiblePosition();
918 FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint));
919 selectionEndPoint = editableElement->renderer()->absoluteToLocal(absolutePoint);
920 targetNode = editableElement;
923 return targetNode->renderer()->positionForPoint(LayoutPoint(selectionEndPoint), nullptr);
926 void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult)
928 if (!m_mouseDownMayStartSelect)
931 Node* target = hitTestResult.targetNode();
935 VisiblePosition targetPosition = selectionExtentRespectingEditingBoundary(m_frame.selection().selection(), hitTestResult.localPoint(), target);
937 // Don't modify the selection if we're not on a node.
938 if (targetPosition.isNull())
941 // Restart the selection if this is the first mouse move. This work is usually
942 // done in handleMousePressEvent, but not if the mouse press was on an existing selection.
943 VisibleSelection newSelection = m_frame.selection().selection();
945 // Special case to limit selection to the containing block for SVG text.
946 // FIXME: Isn't there a better non-SVG-specific way to do this?
947 if (Node* selectionBaseNode = newSelection.base().deprecatedNode())
948 if (RenderObject* selectionBaseRenderer = selectionBaseNode->renderer())
949 if (selectionBaseRenderer->isSVGText())
950 if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock())
953 if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelectStart(target))
956 if (m_selectionInitiationState != ExtendedSelection) {
957 // Always extend selection here because it's caused by a mouse drag
958 m_selectionInitiationState = ExtendedSelection;
959 newSelection = VisibleSelection(targetPosition);
962 #if ENABLE(USERSELECT_ALL)
963 Node* rootUserSelectAllForMousePressNode = Position::rootUserSelectAllForNode(m_mousePressNode.get());
964 if (rootUserSelectAllForMousePressNode && rootUserSelectAllForMousePressNode == Position::rootUserSelectAllForNode(target)) {
965 newSelection.setBase(positionBeforeNode(rootUserSelectAllForMousePressNode).upstream(CanCrossEditingBoundary));
966 newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
968 // Reset base for user select all when base is inside user-select-all area and extent < base.
969 if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0)
970 newSelection.setBase(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
972 Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNode(target);
973 if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0)
974 newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTarget).upstream(CanCrossEditingBoundary));
975 else if (rootUserSelectAllForTarget && m_mousePressNode->renderer())
976 newSelection.setExtent(positionAfterNode(rootUserSelectAllForTarget).downstream(CanCrossEditingBoundary));
978 newSelection.setExtent(targetPosition);
981 newSelection.setExtent(targetPosition);
984 if (m_frame.selection().granularity() != CharacterGranularity)
985 newSelection.expandUsingGranularity(m_frame.selection().granularity());
987 m_frame.selection().setSelectionByMouseIfDifferent(newSelection, m_frame.selection().granularity(),
988 FrameSelection::AdjustEndpointsAtBidiBoundary);
990 #endif // ENABLE(DRAG_SUPPORT)
992 void EventHandler::lostMouseCapture()
994 m_frame.selection().setCaretBlinkingSuspended(false);
997 bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event)
999 if (eventLoopHandleMouseUp(event))
1002 // If this was the first click in the window, we don't even want to clear the selection.
1003 // This case occurs when the user clicks on a draggable element, since we have to process
1004 // the mouse down and drag events to see if we might start a drag. For other first clicks
1005 // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
1006 // ignored upstream of this layer.
1007 return eventActivatedView(event.event());
1010 bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
1012 if (autoscrollInProgress())
1013 stopAutoscrollTimer();
1015 Ref<Frame> protectedFrame(m_frame);
1017 if (handleMouseUp(event))
1020 // Used to prevent mouseMoveEvent from initiating a drag before
1021 // the mouse is pressed again.
1022 m_mousePressed = false;
1023 m_capturesDragging = false;
1024 #if ENABLE(DRAG_SUPPORT)
1025 m_mouseDownMayStartDrag = false;
1027 m_mouseDownMayStartSelect = false;
1028 m_mouseDownMayStartAutoscroll = false;
1029 m_mouseDownWasInSubframe = false;
1031 bool handled = false;
1033 // Clear the selection if the mouse didn't move after the last mouse
1034 // press and it's not a context menu click. We do this so when clicking
1035 // on the selection, the selection goes away. However, if we are
1036 // editing, place the caret.
1037 if (m_mouseDownWasSingleClickInSelection && m_selectionInitiationState != ExtendedSelection
1038 #if ENABLE(DRAG_SUPPORT)
1039 && m_dragStartPosition == event.event().position()
1041 && m_frame.selection().isRange()
1042 && event.event().button() != RightButton) {
1043 VisibleSelection newSelection;
1044 Node* node = event.targetNode();
1045 bool caretBrowsing = m_frame.settings().caretBrowsingEnabled();
1046 if (node && node->renderer() && (caretBrowsing || node->hasEditableStyle())) {
1047 VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint(), nullptr);
1048 newSelection = VisibleSelection(pos);
1051 setSelectionIfNeeded(m_frame.selection(), newSelection);
1056 if (event.event().button() == MiddleButton) {
1057 // Ignore handled, since we want to paste to where the caret was placed anyway.
1058 handled = handlePasteGlobalSelection(event.event()) || handled;
1064 #if ENABLE(PAN_SCROLLING)
1066 void EventHandler::didPanScrollStart()
1068 m_autoscrollController->didPanScrollStart();
1071 void EventHandler::didPanScrollStop()
1073 m_autoscrollController->didPanScrollStop();
1076 void EventHandler::startPanScrolling(RenderElement& renderer)
1079 if (!is<RenderBox>(renderer))
1081 m_autoscrollController->startPanScrolling(&downcast<RenderBox>(renderer), lastKnownMousePosition());
1086 #endif // ENABLE(PAN_SCROLLING)
1088 RenderBox* EventHandler::autoscrollRenderer() const
1090 return m_autoscrollController->autoscrollRenderer();
1093 void EventHandler::updateAutoscrollRenderer()
1095 m_autoscrollController->updateAutoscrollRenderer();
1098 bool EventHandler::autoscrollInProgress() const
1100 return m_autoscrollController->autoscrollInProgress();
1103 bool EventHandler::panScrollInProgress() const
1105 return m_autoscrollController->panScrollInProgress();
1108 #if ENABLE(DRAG_SUPPORT)
1109 DragSourceAction EventHandler::updateDragSourceActionsAllowed() const
1111 Page* page = m_frame.page();
1113 return DragSourceActionNone;
1115 FrameView* view = m_frame.view();
1117 return DragSourceActionNone;
1119 return page->dragController().delegateDragSourceAction(view->contentsToRootView(m_mouseDownPos));
1121 #endif // ENABLE(DRAG_SUPPORT)
1123 HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) const
1125 ASSERT((hitType & HitTestRequest::CollectMultipleElements) || padding.isEmpty());
1127 Ref<Frame> protectedFrame(m_frame);
1129 // We always send hitTestResultAtPoint to the main frame if we have one,
1130 // otherwise we might hit areas that are obscured by higher frames.
1131 if (!m_frame.isMainFrame()) {
1132 Frame& mainFrame = m_frame.mainFrame();
1133 FrameView* frameView = m_frame.view();
1134 FrameView* mainView = mainFrame.view();
1135 if (frameView && mainView) {
1136 IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point)));
1137 return mainFrame.eventHandler().hitTestResultAtPoint(mainFramePoint, hitType, padding);
1141 unsigned nonNegativePaddingWidth = std::max<LayoutUnit>(0, padding.width()).toUnsigned();
1142 unsigned nonNegativePaddingHeight = std::max<LayoutUnit>(0, padding.height()).toUnsigned();
1144 // We should always start hit testing a clean tree.
1145 if (auto* frameView = m_frame.view())
1146 frameView->updateLayoutAndStyleIfNeededRecursive();
1148 HitTestResult result(point, nonNegativePaddingHeight, nonNegativePaddingWidth, nonNegativePaddingHeight, nonNegativePaddingWidth);
1149 RenderView* renderView = m_frame.contentRenderer();
1153 // hitTestResultAtPoint is specifically used to hitTest into all frames, thus it always allows child frame content.
1154 HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent);
1155 renderView->hitTest(request, result);
1156 if (!request.readOnly())
1157 m_frame.document()->updateHoverActiveState(request, result.targetElement());
1159 if (request.disallowsUserAgentShadowContent())
1160 result.setToNonUserAgentShadowAncestor();
1165 void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed)
1167 m_autoscrollController->stopAutoscrollTimer(rendererIsBeingDestroyed);
1170 bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
1172 Node* node = startingNode;
1175 node = m_frame.document()->focusedElement();
1178 node = m_mousePressNode.get();
1181 auto r = node->renderer();
1182 if (r && !r->isListBox() && r->enclosingBox().scroll(direction, granularity)) {
1183 setFrameWasScrolledByUser();
1191 bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode)
1193 Node* node = startingNode;
1196 node = m_frame.document()->focusedElement();
1199 node = m_mousePressNode.get();
1202 auto r = node->renderer();
1203 if (r && !r->isListBox() && r->enclosingBox().logicalScroll(direction, granularity)) {
1204 setFrameWasScrolledByUser();
1212 bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
1214 Ref<Frame> protectedFrame(m_frame);
1216 // The layout needs to be up to date to determine if we can scroll. We may be
1217 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
1218 m_frame.document()->updateLayoutIgnorePendingStylesheets();
1219 if (scrollOverflow(direction, granularity, startingNode))
1221 Frame* frame = &m_frame;
1222 FrameView* view = frame->view();
1223 if (view && view->scroll(direction, granularity))
1225 frame = frame->tree().parent();
1228 return frame->eventHandler().scrollRecursively(direction, granularity, m_frame.ownerElement());
1231 bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode)
1233 Ref<Frame> protectedFrame(m_frame);
1235 // The layout needs to be up to date to determine if we can scroll. We may be
1236 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
1237 m_frame.document()->updateLayoutIgnorePendingStylesheets();
1238 if (logicalScrollOverflow(direction, granularity, startingNode))
1240 Frame* frame = &m_frame;
1241 FrameView* view = frame->view();
1243 bool scrolled = false;
1245 // Mac also resets the scroll position in the inline direction.
1246 if (granularity == ScrollByDocument && view && view->logicalScroll(ScrollInlineDirectionBackward, ScrollByDocument))
1249 if (view && view->logicalScroll(direction, granularity))
1255 frame = frame->tree().parent();
1259 return frame->eventHandler().logicalScrollRecursively(direction, granularity, m_frame.ownerElement());
1262 IntPoint EventHandler::lastKnownMousePosition() const
1264 return m_lastKnownMousePosition;
1267 Frame* EventHandler::subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult)
1269 if (!hitTestResult.isOverWidget())
1271 return subframeForTargetNode(hitTestResult.targetNode());
1274 Frame* EventHandler::subframeForTargetNode(Node* node)
1279 auto renderer = node->renderer();
1280 if (!is<RenderWidget>(renderer))
1283 Widget* widget = downcast<RenderWidget>(*renderer).widget();
1284 if (!is<FrameView>(widget))
1287 return &downcast<FrameView>(*widget).frame();
1290 #if ENABLE(CURSOR_SUPPORT)
1291 static bool isSubmitImage(Node* node)
1293 return is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*node).isImageButton();
1296 // Returns true if the node's editable block is not current focused for editing
1297 static bool nodeIsNotBeingEdited(const Node& node, const Frame& frame)
1299 return frame.selection().selection().rootEditableElement() != node.rootEditableElement();
1302 bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey)
1307 bool editable = node->hasEditableStyle();
1308 bool editableLinkEnabled = false;
1310 // If the link is editable, then we need to check the settings to see whether or not the link should be followed
1312 switch (m_frame.settings().editableLinkBehavior()) {
1314 case EditableLinkDefaultBehavior:
1315 case EditableLinkAlwaysLive:
1316 editableLinkEnabled = true;
1319 case EditableLinkNeverLive:
1320 editableLinkEnabled = false;
1323 case EditableLinkLiveWhenNotFocused:
1324 editableLinkEnabled = nodeIsNotBeingEdited(*node, m_frame) || shiftKey;
1327 case EditableLinkOnlyLiveWithShiftKey:
1328 editableLinkEnabled = shiftKey;
1333 return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled));
1336 void EventHandler::cursorUpdateTimerFired()
1338 ASSERT(m_frame.document());
1342 void EventHandler::updateCursor()
1344 if (m_mousePositionIsUnknown)
1347 FrameView* view = m_frame.view();
1351 RenderView* renderView = view->renderView();
1355 if (!view->shouldSetCursor())
1362 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
1364 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::AllowFrameScrollbars);
1365 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
1366 renderView->hitTest(request, result);
1368 updateCursor(*view, result, shiftKey);
1371 void EventHandler::updateCursor(FrameView& view, const HitTestResult& result, bool shiftKey)
1373 if (auto optionalCursor = selectCursor(result, shiftKey)) {
1374 m_currentMouseCursor = WTFMove(optionalCursor.value());
1375 view.setCursor(m_currentMouseCursor);
1379 std::optional<Cursor> EventHandler::selectCursor(const HitTestResult& result, bool shiftKey)
1381 if (m_resizeLayer && m_resizeLayer->inResizeMode())
1382 return std::nullopt;
1384 if (!m_frame.page())
1385 return std::nullopt;
1387 #if ENABLE(PAN_SCROLLING)
1388 if (m_frame.mainFrame().eventHandler().panScrollInProgress())
1389 return std::nullopt;
1392 Ref<Frame> protectedFrame(m_frame);
1394 // Use always pointer cursor for scrollbars.
1395 if (result.scrollbar()) {
1396 #if ENABLE(CURSOR_VISIBILITY)
1397 cancelAutoHideCursorTimer();
1399 return pointerCursor();
1402 Node* node = result.targetNode();
1404 return std::nullopt;
1406 auto renderer = node->renderer();
1407 auto* style = renderer ? &renderer->style() : nullptr;
1408 bool horizontalText = !style || style->isHorizontalWritingMode();
1409 const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCursor();
1411 #if ENABLE(CURSOR_VISIBILITY)
1412 if (style && style->cursorVisibility() == CursorVisibilityAutoHide)
1413 startAutoHideCursorTimer();
1415 cancelAutoHideCursorTimer();
1419 Cursor overrideCursor;
1420 switch (renderer->getCursor(roundedIntPoint(result.localPoint()), overrideCursor)) {
1421 case SetCursorBasedOnStyle:
1424 return overrideCursor;
1425 case DoNotSetCursor:
1426 return std::nullopt;
1430 if (style && style->cursors()) {
1431 const CursorList* cursors = style->cursors();
1432 for (unsigned i = 0; i < cursors->size(); ++i) {
1433 StyleImage* styleImage = (*cursors)[i].image();
1436 CachedImage* cachedImage = styleImage->cachedImage();
1439 float scale = styleImage->imageScaleFactor();
1440 // Get hotspot and convert from logical pixels to physical pixels.
1441 IntPoint hotSpot = (*cursors)[i].hotSpot();
1442 FloatSize size = cachedImage->imageForRenderer(renderer)->size();
1443 if (cachedImage->errorOccurred())
1445 // Limit the size of cursors (in UI pixels) so that they cannot be
1446 // used to cover UI elements in chrome.
1447 size.scale(1 / scale);
1448 if (size.width() > maximumCursorSize || size.height() > maximumCursorSize)
1451 Image* image = cachedImage->imageForRenderer(renderer);
1452 #if ENABLE(MOUSE_CURSOR_SCALE)
1453 // Ensure no overflow possible in calculations above.
1454 if (scale < minimumCursorScale)
1456 return Cursor(image, hotSpot, scale);
1459 return Cursor(image, hotSpot);
1460 #endif // ENABLE(MOUSE_CURSOR_SCALE)
1464 // During selection, use an I-beam regardless of the content beneath the cursor.
1465 // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection.
1467 && m_mouseDownMayStartSelect
1468 #if ENABLE(DRAG_SUPPORT)
1469 && !m_mouseDownMayStartDrag
1471 && m_frame.selection().isCaretOrRange()
1472 && !m_capturingMouseEventsElement)
1475 switch (style ? style->cursor() : CursorAuto) {
1477 bool editable = node->hasEditableStyle();
1479 if (useHandCursor(node, result.isOverLink(), shiftKey))
1480 return handCursor();
1482 bool inResizer = false;
1484 if (RenderLayer* layer = renderer->enclosingLayer()) {
1485 if (FrameView* view = m_frame.view())
1486 inResizer = layer->isPointInResizeControl(view->windowToContents(roundedIntPoint(result.localPoint())));
1490 if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar())
1492 return pointerCursor();
1495 return pointerCursor();
1497 return noneCursor();
1498 case CursorContextMenu:
1499 return contextMenuCursor();
1501 return helpCursor();
1503 return handCursor();
1504 case CursorProgress:
1505 return progressCursor();
1507 return waitCursor();
1509 return cellCursor();
1510 case CursorCrosshair:
1511 return crossCursor();
1513 return iBeamCursor();
1514 case CursorVerticalText:
1515 return verticalTextCursor();
1517 return aliasCursor();
1519 return copyCursor();
1521 return moveCursor();
1523 return noDropCursor();
1524 case CursorNotAllowed:
1525 return notAllowedCursor();
1527 return grabCursor();
1528 case CursorGrabbing:
1529 return grabbingCursor();
1531 return eastResizeCursor();
1533 return northResizeCursor();
1534 case CursorNeResize:
1535 return northEastResizeCursor();
1536 case CursorNwResize:
1537 return northWestResizeCursor();
1539 return southResizeCursor();
1540 case CursorSeResize:
1541 return southEastResizeCursor();
1542 case CursorSwResize:
1543 return southWestResizeCursor();
1545 return westResizeCursor();
1546 case CursorEwResize:
1547 return eastWestResizeCursor();
1548 case CursorNsResize:
1549 return northSouthResizeCursor();
1550 case CursorNeswResize:
1551 return northEastSouthWestResizeCursor();
1552 case CursorNwseResize:
1553 return northWestSouthEastResizeCursor();
1554 case CursorColResize:
1555 return columnResizeCursor();
1556 case CursorRowResize:
1557 return rowResizeCursor();
1558 case CursorAllScroll:
1559 return moveCursor();
1561 return zoomInCursor();
1563 return zoomOutCursor();
1565 return pointerCursor();
1567 #endif // ENABLE(CURSOR_SUPPORT)
1569 #if ENABLE(CURSOR_VISIBILITY)
1570 void EventHandler::startAutoHideCursorTimer()
1572 Page* page = m_frame.page();
1576 m_autoHideCursorTimer.startOneShot(page->settings().timeWithoutMouseMovementBeforeHidingControls());
1578 #if !ENABLE(IOS_TOUCH_EVENTS)
1579 // The fake mouse move event screws up the auto-hide feature (by resetting the auto-hide timer)
1580 // so cancel any pending fake mouse moves.
1581 if (m_fakeMouseMoveEventTimer.isActive())
1582 m_fakeMouseMoveEventTimer.stop();
1586 void EventHandler::cancelAutoHideCursorTimer()
1588 if (m_autoHideCursorTimer.isActive())
1589 m_autoHideCursorTimer.stop();
1592 void EventHandler::autoHideCursorTimerFired()
1594 m_currentMouseCursor = noneCursor();
1595 FrameView* view = m_frame.view();
1596 if (view && view->isActive())
1597 view->setCursor(m_currentMouseCursor);
1601 static LayoutPoint documentPointForWindowPoint(Frame& frame, const IntPoint& windowPoint)
1603 FrameView* view = frame.view();
1604 // FIXME: Is it really OK to use the wrong coordinates here when view is 0?
1605 // Historically the code would just crash; this is clearly no worse than that.
1606 return view ? view->windowToContents(windowPoint) : windowPoint;
1609 static Scrollbar* scrollbarForMouseEvent(const MouseEventWithHitTestResults& mouseEvent, FrameView* view)
1612 if (auto* scrollbar = view->scrollbarAtPoint(mouseEvent.event().position()))
1615 return mouseEvent.scrollbar();
1619 bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& platformMouseEvent)
1621 Ref<Frame> protectedFrame(m_frame);
1622 RefPtr<FrameView> protector(m_frame.view());
1624 if (InspectorInstrumentation::handleMousePress(m_frame)) {
1629 #if ENABLE(POINTER_LOCK)
1630 if (m_frame.page()->pointerLockController().isLocked()) {
1631 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousedownEvent);
1636 if (m_frame.mainFrame().pageOverlayController().handleMouseEvent(platformMouseEvent))
1639 #if ENABLE(TOUCH_EVENTS)
1640 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
1641 if (defaultPrevented)
1645 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
1647 // FIXME (bug 68185): this call should be made at another abstraction layer
1648 m_frame.loader().resetMultipleFormSubmissionProtection();
1650 #if !ENABLE(IOS_TOUCH_EVENTS)
1651 cancelFakeMouseMoveEvent();
1653 m_mousePressed = true;
1654 m_capturesDragging = true;
1655 setLastKnownMousePosition(platformMouseEvent);
1656 m_mouseDownTimestamp = platformMouseEvent.timestamp();
1657 #if ENABLE(DRAG_SUPPORT)
1658 m_mouseDownMayStartDrag = false;
1660 m_mouseDownMayStartSelect = false;
1661 m_mouseDownMayStartAutoscroll = false;
1662 if (FrameView* view = m_frame.view())
1663 m_mouseDownPos = view->windowToContents(platformMouseEvent.position());
1668 m_mouseDownWasInSubframe = false;
1670 HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
1671 // Save the document point we generate in case the window coordinate is invalidated by what happens
1672 // when we dispatch the event.
1673 LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, platformMouseEvent.position());
1674 MouseEventWithHitTestResults mouseEvent = m_frame.document()->prepareMouseEvent(request, documentPoint, platformMouseEvent);
1676 if (!mouseEvent.targetNode()) {
1681 m_mousePressNode = mouseEvent.targetNode();
1682 m_frame.document()->setFocusNavigationStartingNode(mouseEvent.targetNode());
1684 Scrollbar* scrollbar = scrollbarForMouseEvent(mouseEvent, m_frame.view());
1685 updateLastScrollbarUnderMouse(scrollbar, SetOrClearLastScrollbar::Set);
1686 bool passedToScrollbar = scrollbar && passMousePressEventToScrollbar(mouseEvent, scrollbar);
1688 if (!passedToScrollbar) {
1689 RefPtr<Frame> subframe = subframeForHitTestResult(mouseEvent);
1690 if (subframe && passMousePressEventToSubframe(mouseEvent, subframe.get())) {
1691 // Start capturing future events for this frame. We only do this if we didn't clear
1692 // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop.
1693 m_capturesDragging = subframe->eventHandler().capturesDragging();
1694 if (m_mousePressed && m_capturesDragging) {
1695 m_capturingMouseEventsElement = subframe->ownerElement();
1696 m_eventHandlerWillResetCapturingMouseEventsElement = true;
1703 #if ENABLE(PAN_SCROLLING)
1704 // We store whether pan scrolling is in progress before calling stopAutoscrollTimer()
1705 // because it will set m_autoscrollType to NoAutoscroll on return.
1706 bool isPanScrollInProgress = m_frame.mainFrame().eventHandler().panScrollInProgress();
1707 stopAutoscrollTimer();
1708 if (isPanScrollInProgress) {
1709 // We invalidate the click when exiting pan scrolling so that we don't inadvertently navigate
1710 // away from the current page (e.g. the click was on a hyperlink). See <rdar://problem/6095023>.
1716 m_clickCount = platformMouseEvent.clickCount();
1717 m_clickNode = mouseEvent.targetNode();
1724 if (FrameView* view = m_frame.view()) {
1725 RenderLayer* layer = m_clickNode->renderer() ? m_clickNode->renderer()->enclosingLayer() : 0;
1726 IntPoint p = view->windowToContents(platformMouseEvent.position());
1727 if (layer && layer->isPointInResizeControl(p)) {
1728 layer->setInResizeMode(true);
1729 m_resizeLayer = layer;
1730 m_offsetFromResizeCorner = layer->offsetFromResizeCorner(p);
1736 m_frame.selection().setCaretBlinkingSuspended(true);
1738 bool swallowEvent = !dispatchMouseEvent(eventNames().mousedownEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true);
1739 m_capturesDragging = !swallowEvent || mouseEvent.scrollbar();
1741 // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults
1742 // in case the scrollbar widget was destroyed when the mouse event was handled.
1743 if (mouseEvent.scrollbar()) {
1744 const bool wasLastScrollBar = mouseEvent.scrollbar() == m_lastScrollbarUnderMouse;
1745 mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent);
1746 if (wasLastScrollBar && mouseEvent.scrollbar() != m_lastScrollbarUnderMouse)
1747 m_lastScrollbarUnderMouse = nullptr;
1750 if (!swallowEvent) {
1751 // Refetch the event target node if it currently is the shadow node inside an <input> element.
1752 // If a mouse event handler changes the input element type to one that has a widget associated,
1753 // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the
1754 // event target node can't still be the shadow node.
1755 if (is<ShadowRoot>(*mouseEvent.targetNode()) && is<HTMLInputElement>(*downcast<ShadowRoot>(*mouseEvent.targetNode()).host()))
1756 mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent);
1759 if (!swallowEvent) {
1760 if (passedToScrollbar)
1761 swallowEvent = true;
1763 swallowEvent = handleMousePressEvent(mouseEvent);
1765 return swallowEvent;
1768 // This method only exists for platforms that don't know how to deliver
1769 bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& platformMouseEvent)
1771 Ref<Frame> protectedFrame(m_frame);
1772 RefPtr<FrameView> protector(m_frame.view());
1774 m_frame.selection().setCaretBlinkingSuspended(false);
1776 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
1778 #if ENABLE(POINTER_LOCK)
1779 if (m_frame.page()->pointerLockController().isLocked()) {
1780 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent);
1785 // We get this instead of a second mouse-up
1786 m_mousePressed = false;
1787 setLastKnownMousePosition(platformMouseEvent);
1789 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
1790 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
1791 Frame* subframe = subframeForHitTestResult(mouseEvent);
1792 if (m_eventHandlerWillResetCapturingMouseEventsElement)
1793 m_capturingMouseEventsElement = nullptr;
1794 if (subframe && passMousePressEventToSubframe(mouseEvent, subframe))
1797 m_clickCount = platformMouseEvent.clickCount();
1798 bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false);
1800 bool swallowClickEvent = platformMouseEvent.button() != RightButton && mouseEvent.targetNode() == m_clickNode && !dispatchMouseEvent(eventNames().clickEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true);
1802 if (m_lastScrollbarUnderMouse)
1803 swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent);
1805 bool swallowMouseReleaseEvent = !swallowMouseUpEvent && handleMouseReleaseEvent(mouseEvent);
1809 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
1812 static ScrollableArea* enclosingScrollableArea(Node* node)
1814 for (auto ancestor = node; ancestor; ancestor = ancestor->parentOrShadowHostNode()) {
1815 if (is<HTMLIFrameElement>(*ancestor) || is<HTMLHtmlElement>(*ancestor) || is<HTMLDocument>(*ancestor))
1818 auto renderer = ancestor->renderer();
1822 if (is<RenderListBox>(*renderer))
1823 return downcast<RenderListBox>(renderer);
1825 return renderer->enclosingLayer();
1831 bool EventHandler::mouseMoved(const PlatformMouseEvent& event)
1833 Ref<Frame> protectedFrame(m_frame);
1834 RefPtr<FrameView> protector(m_frame.view());
1835 MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration);
1837 if (m_frame.mainFrame().pageOverlayController().handleMouseEvent(event))
1840 HitTestResult hoveredNode = HitTestResult(LayoutPoint());
1841 bool result = handleMouseMoveEvent(event, &hoveredNode);
1843 Page* page = m_frame.page();
1847 if (auto scrolledArea = enclosingScrollableArea(hoveredNode.innerNode())) {
1848 if (FrameView* frameView = m_frame.view()) {
1849 if (frameView->containsScrollableArea(scrolledArea))
1850 scrolledArea->mouseMovedInContentArea();
1854 if (FrameView* frameView = m_frame.view())
1855 frameView->mouseMovedInContentArea();
1857 hoveredNode.setToNonUserAgentShadowAncestor();
1858 page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags());
1859 page->chrome().setToolTip(hoveredNode);
1863 bool EventHandler::passMouseMovedEventToScrollbars(const PlatformMouseEvent& event)
1865 HitTestResult hoveredNode;
1866 return handleMouseMoveEvent(event, &hoveredNode, true);
1869 bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& platformMouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars)
1871 #if ENABLE(TOUCH_EVENTS)
1872 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
1873 if (defaultPrevented)
1877 Ref<Frame> protectedFrame(m_frame);
1878 RefPtr<FrameView> protector(m_frame.view());
1880 #if ENABLE(POINTER_LOCK)
1881 if (m_frame.page()->pointerLockController().isLocked()) {
1882 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousemoveEvent);
1887 setLastKnownMousePosition(platformMouseEvent);
1889 if (m_hoverTimer.isActive())
1890 m_hoverTimer.stop();
1892 #if ENABLE(CURSOR_SUPPORT)
1893 m_cursorUpdateTimer.stop();
1896 #if !ENABLE(IOS_TOUCH_EVENTS)
1897 cancelFakeMouseMoveEvent();
1901 downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition));
1905 if (m_frameSetBeingResized)
1906 return !dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, platformMouseEvent, false);
1908 // On iOS, our scrollbars are managed by UIKit.
1910 // Send events right to a scrollbar if the mouse is pressed.
1911 if (m_lastScrollbarUnderMouse && m_mousePressed)
1912 return m_lastScrollbarUnderMouse->mouseMoved(platformMouseEvent);
1915 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::AllowFrameScrollbars;
1917 hitType |= HitTestRequest::Active;
1918 else if (onlyUpdateScrollbars) {
1919 // Mouse events should be treated as "read-only" if we're updating only scrollbars. This
1920 // means that :hover and :active freeze in the state they were in, rather than updating
1921 // for nodes the mouse moves while the window is not key (which will be the case if
1922 // onlyUpdateScrollbars is true).
1923 hitType |= HitTestRequest::ReadOnly;
1926 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
1927 // Treat any mouse move events as readonly if the user is currently touching the screen.
1929 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
1931 HitTestRequest request(hitType);
1932 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
1934 *hoveredNode = mouseEvent.hitTestResult();
1936 if (m_resizeLayer && m_resizeLayer->inResizeMode())
1937 m_resizeLayer->resize(platformMouseEvent, m_offsetFromResizeCorner);
1939 Scrollbar* scrollbar = mouseEvent.scrollbar();
1940 updateLastScrollbarUnderMouse(scrollbar, m_mousePressed ? SetOrClearLastScrollbar::Clear : SetOrClearLastScrollbar::Set);
1942 // On iOS, our scrollbars are managed by UIKit.
1944 if (!m_mousePressed && scrollbar)
1945 scrollbar->mouseMoved(platformMouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering.
1947 if (onlyUpdateScrollbars) {
1948 updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true);
1953 bool swallowEvent = false;
1954 RefPtr<Frame> newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent);
1956 // 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.
1957 if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(&m_frame) && m_lastMouseMoveEventSubframe != newSubframe)
1958 passMouseMoveEventToSubframe(mouseEvent, m_lastMouseMoveEventSubframe.get());
1961 // Update over/out state before passing the event to the subframe.
1962 updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true);
1964 // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target
1965 // node to be detached from its FrameView, in which case the event should not be passed.
1966 if (newSubframe->view())
1967 swallowEvent |= passMouseMoveEventToSubframe(mouseEvent, newSubframe.get(), hoveredNode);
1970 if (!newSubframe || mouseEvent.scrollbar()) {
1971 #if ENABLE(CURSOR_SUPPORT)
1972 if (auto* view = m_frame.view())
1973 updateCursor(*view, mouseEvent.hitTestResult(), platformMouseEvent.shiftKey());
1977 m_lastMouseMoveEventSubframe = newSubframe;
1982 swallowEvent = !dispatchMouseEvent(eventNames().mousemoveEvent, mouseEvent.targetNode(), false, 0, platformMouseEvent, true);
1983 #if ENABLE(DRAG_SUPPORT)
1985 swallowEvent = handleMouseDraggedEvent(mouseEvent);
1988 return swallowEvent;
1991 void EventHandler::invalidateClick()
1994 m_clickNode = nullptr;
1997 static Node* targetNodeForClickEvent(Node* mousePressNode, Node* mouseReleaseNode)
1999 if (!mousePressNode || !mouseReleaseNode)
2002 if (mousePressNode == mouseReleaseNode)
2003 return mouseReleaseNode;
2005 // If mousePressNode and mouseReleaseNode differ, we should fire the event at their common ancestor if there is one.
2006 if (&mousePressNode->document() == &mouseReleaseNode->document()) {
2007 if (auto* commonAncestor = Range::commonAncestorContainer(mousePressNode, mouseReleaseNode))
2008 return commonAncestor;
2011 Element* mouseReleaseShadowHost = mouseReleaseNode->shadowHost();
2012 if (mouseReleaseShadowHost && mouseReleaseShadowHost == mousePressNode->shadowHost()) {
2013 // We want to dispatch the click to the shadow tree host element to give listeners the illusion that the
2014 // shadom tree is a single element. For example, we want to give the illusion that <input type="range">
2015 // is a single element even though it is a composition of multiple shadom tree elements.
2016 return mouseReleaseShadowHost;
2021 bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& platformMouseEvent)
2023 Ref<Frame> protectedFrame(m_frame);
2024 RefPtr<FrameView> protector(m_frame.view());
2026 m_frame.selection().setCaretBlinkingSuspended(false);
2028 #if ENABLE(POINTER_LOCK)
2029 if (m_frame.page()->pointerLockController().isLocked()) {
2030 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent);
2035 if (m_frame.mainFrame().pageOverlayController().handleMouseEvent(platformMouseEvent))
2038 #if ENABLE(TOUCH_EVENTS)
2039 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
2040 if (defaultPrevented)
2044 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
2046 #if ENABLE(PAN_SCROLLING)
2047 m_autoscrollController->handleMouseReleaseEvent(platformMouseEvent);
2050 m_mousePressed = false;
2051 setLastKnownMousePosition(platformMouseEvent);
2055 downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition));
2059 if (m_frameSetBeingResized)
2060 return !dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, platformMouseEvent, false);
2062 // If an immediate action began or was completed using this series of mouse events, then we should send mouseup to
2063 // the DOM and return now so that we don't perform our own default behaviors.
2064 if (m_immediateActionStage == ImmediateActionStage::ActionCompleted || m_immediateActionStage == ImmediateActionStage::ActionUpdated || m_immediateActionStage == ImmediateActionStage::ActionCancelledAfterUpdate) {
2065 m_immediateActionStage = ImmediateActionStage::None;
2066 return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), true, m_clickCount, platformMouseEvent, false);
2068 m_immediateActionStage = ImmediateActionStage::None;
2070 if (m_lastScrollbarUnderMouse) {
2072 m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent);
2073 bool cancelable = true;
2074 bool setUnder = false;
2075 return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), cancelable, m_clickCount, platformMouseEvent, setUnder);
2078 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
2079 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
2080 Frame* subframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent);
2081 if (m_eventHandlerWillResetCapturingMouseEventsElement)
2082 m_capturingMouseEventsElement = nullptr;
2083 if (subframe && passMouseReleaseEventToSubframe(mouseEvent, subframe))
2086 bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false);
2088 bool contextMenuEvent = platformMouseEvent.button() == RightButton;
2090 Node* nodeToClick = targetNodeForClickEvent(m_clickNode.get(), mouseEvent.targetNode());
2091 bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && nodeToClick && !dispatchMouseEvent(eventNames().clickEvent, nodeToClick, true, m_clickCount, platformMouseEvent, true);
2093 if (m_resizeLayer) {
2094 m_resizeLayer->setInResizeMode(false);
2095 m_resizeLayer = nullptr;
2098 bool swallowMouseReleaseEvent = false;
2099 if (!swallowMouseUpEvent)
2100 swallowMouseReleaseEvent = handleMouseReleaseEvent(mouseEvent);
2104 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
2107 #if ENABLE(MOUSE_FORCE_EVENTS)
2108 bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& event)
2110 Ref<Frame> protectedFrame(m_frame);
2111 RefPtr<FrameView> protector(m_frame.view());
2113 #if ENABLE(POINTER_LOCK)
2114 if (m_frame.page()->pointerLockController().isLocked()) {
2115 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcechangedEvent);
2116 if (event.type() == PlatformEvent::MouseForceDown)
2117 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcedownEvent);
2118 if (event.type() == PlatformEvent::MouseForceUp)
2119 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforceupEvent);
2124 setLastKnownMousePosition(event);
2126 HitTestRequest::HitTestRequestType hitType = HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::Active;
2128 HitTestRequest request(hitType);
2129 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event);
2131 bool swallowedEvent = !dispatchMouseEvent(eventNames().webkitmouseforcechangedEvent, mouseEvent.targetNode(), false, 0, event, false);
2132 if (event.type() == PlatformEvent::MouseForceDown)
2133 swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforcedownEvent, mouseEvent.targetNode(), false, 0, event, false);
2134 if (event.type() == PlatformEvent::MouseForceUp)
2135 swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforceupEvent, mouseEvent.targetNode(), false, 0, event, false);
2137 return swallowedEvent;
2140 bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& )
2144 #endif // #if ENABLE(MOUSE_FORCE_EVENTS)
2146 bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& platformMouseEvent)
2148 // If the event was a middle click, attempt to copy global selection in after
2149 // the newly set caret position.
2151 // This code is called from either the mouse up or mouse down handling. There
2152 // is some debate about when the global selection is pasted:
2153 // xterm: pastes on up.
2154 // GTK: pastes on down.
2155 // Qt: pastes on up.
2156 // Firefox: pastes on up.
2157 // Chromium: pastes on up.
2159 // There is something of a webcompat angle to this well, as highlighted by
2160 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
2161 // down then the text is pasted just before the onclick handler runs and
2162 // clears the text box. So it's important this happens after the event
2163 // handlers have been fired.
2165 if (platformMouseEvent.type() != PlatformEvent::MousePressed)
2168 if (platformMouseEvent.type() != PlatformEvent::MouseReleased)
2172 if (!m_frame.page())
2174 Frame& focusFrame = m_frame.page()->focusController().focusedOrMainFrame();
2175 // Do not paste here if the focus was moved somewhere else.
2176 if (&m_frame == &focusFrame && m_frame.editor().client()->supportsGlobalSelection())
2177 return m_frame.editor().command(ASCIILiteral("PasteGlobalSelection")).execute();
2182 #if ENABLE(DRAG_SUPPORT)
2184 bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer* dataTransfer)
2186 Ref<Frame> protectedFrame(m_frame);
2187 FrameView* view = m_frame.view();
2189 // FIXME: We might want to dispatch a dragleave even if the view is gone.
2193 view->disableLayerFlushThrottlingTemporarilyForInteraction();
2194 Ref<MouseEvent> me = MouseEvent::create(eventType,
2195 true, true, event.timestamp(), m_frame.document()->defaultView(),
2196 0, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(),
2197 #if ENABLE(POINTER_LOCK)
2198 event.movementDelta().x(), event.movementDelta().y(),
2200 event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(),
2201 0, 0, event.force(), NoTap, dataTransfer);
2203 dragTarget.dispatchEvent(me);
2204 return me->defaultPrevented();
2207 static bool targetIsFrame(Node* target, Frame*& frame)
2209 if (!is<HTMLFrameElementBase>(target))
2212 frame = downcast<HTMLFrameElementBase>(*target).contentFrame();
2216 static DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation)
2218 if (dragOperation == "copy")
2219 return DragOperationCopy;
2220 if (dragOperation == "move")
2221 return DragOperationMove;
2222 if (dragOperation == "link")
2223 return DragOperationLink;
2224 return DragOperationNone;
2227 static String convertDragOperationToDropZoneOperation(DragOperation operation)
2229 switch (operation) {
2230 case DragOperationCopy:
2231 return ASCIILiteral("copy");
2232 case DragOperationMove:
2233 return ASCIILiteral("move");
2234 case DragOperationLink:
2235 return ASCIILiteral("link");
2237 return ASCIILiteral("copy");
2241 static bool hasDropZoneType(DataTransfer& dataTransfer, const String& keyword)
2243 if (keyword.startsWith("file:"))
2244 return dataTransfer.hasFileOfType(keyword.substring(5));
2246 if (keyword.startsWith("string:"))
2247 return dataTransfer.hasStringOfType(keyword.substring(7));
2252 static bool findDropZone(Node* target, DataTransfer* dataTransfer)
2255 Element* element = is<Element>(*target) ? downcast<Element>(target) : target->parentElement();
2256 for (; element; element = element->parentElement()) {
2257 SpaceSplitString keywords(element->attributeWithoutSynchronization(webkitdropzoneAttr), true);
2258 bool matched = false;
2259 DragOperation dragOperation = DragOperationNone;
2260 for (unsigned i = 0, size = keywords.size(); i < size; ++i) {
2261 DragOperation op = convertDropZoneOperationToDragOperation(keywords[i]);
2262 if (op != DragOperationNone) {
2263 if (dragOperation == DragOperationNone)
2266 matched = matched || hasDropZoneType(*dataTransfer, keywords[i].string());
2267 if (matched && dragOperation != DragOperationNone)
2271 dataTransfer->setDropEffect(convertDragOperationToDropZoneOperation(dragOperation));
2278 bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer)
2280 Ref<Frame> protectedFrame(m_frame);
2282 bool accept = false;
2284 if (!m_frame.view())
2287 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
2288 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event);
2290 RefPtr<Element> newTarget;
2291 if (Node* targetNode = mouseEvent.targetNode()) {
2292 // Drag events should never go to non-element nodes (following IE, and proper mouseover/out dispatch)
2293 if (!is<Element>(*targetNode))
2294 newTarget = targetNode->parentOrShadowHostElement();
2296 newTarget = downcast<Element>(targetNode);
2299 m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp());
2301 if (m_dragTarget != newTarget) {
2302 // FIXME: this ordering was explicitly chosen to match WinIE. However,
2303 // it is sometimes incorrect when dragging within subframes, as seen with
2304 // LayoutTests/fast/events/drag-in-frames.html.
2306 // 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>.
2308 if (targetIsFrame(newTarget.get(), targetFrame)) {
2310 accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer);
2311 } else if (newTarget) {
2312 // 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.
2313 if (dragState().source && dragState().shouldDispatchEvents) {
2314 // for now we don't care if event handler cancels default behavior, since there is none
2315 dispatchDragSrcEvent(eventNames().dragEvent, event);
2317 accept = dispatchDragEvent(eventNames().dragenterEvent, *newTarget, event, &dataTransfer);
2319 accept = findDropZone(newTarget.get(), &dataTransfer);
2322 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2324 accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer);
2325 } else if (m_dragTarget)
2326 dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, &dataTransfer);
2329 // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that
2330 // two dragover events fired. So, we mark that we should only fire a dragover event on the next call to this function.
2331 m_shouldOnlyFireDragOverEvent = true;
2335 if (targetIsFrame(newTarget.get(), targetFrame)) {
2337 accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer);
2338 } else if (newTarget) {
2339 // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier.
2340 if (!m_shouldOnlyFireDragOverEvent && dragState().source && dragState().shouldDispatchEvents) {
2341 // for now we don't care if event handler cancels default behavior, since there is none
2342 dispatchDragSrcEvent(eventNames().dragEvent, event);
2344 accept = dispatchDragEvent(eventNames().dragoverEvent, *newTarget, event, &dataTransfer);
2346 accept = findDropZone(newTarget.get(), &dataTransfer);
2347 m_shouldOnlyFireDragOverEvent = false;
2350 m_dragTarget = WTFMove(newTarget);
2354 void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer)
2356 Ref<Frame> protectedFrame(m_frame);
2359 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2361 targetFrame->eventHandler().cancelDragAndDrop(event, dataTransfer);
2362 } else if (m_dragTarget) {
2363 if (dragState().source && dragState().shouldDispatchEvents)
2364 dispatchDragSrcEvent(eventNames().dragEvent, event);
2365 dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, &dataTransfer);
2370 bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer)
2372 Ref<Frame> protectedFrame(m_frame);
2375 bool preventedDefault = false;
2376 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2378 preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, dataTransfer);
2379 } else if (m_dragTarget)
2380 preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, &dataTransfer);
2382 return preventedDefault;
2385 void EventHandler::clearDragState()
2387 stopAutoscrollTimer();
2388 m_dragTarget = nullptr;
2389 m_capturingMouseEventsElement = nullptr;
2390 m_shouldOnlyFireDragOverEvent = false;
2392 m_sendingEventToSubview = false;
2396 #endif // ENABLE(DRAG_SUPPORT)
2398 void EventHandler::setCapturingMouseEventsElement(Element* element)
2400 m_capturingMouseEventsElement = element;
2401 m_eventHandlerWillResetCapturingMouseEventsElement = false;
2404 MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mouseEvent)
2406 Ref<Frame> protectedFrame(m_frame);
2407 ASSERT(m_frame.document());
2408 return m_frame.document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mouseEvent.position()), mouseEvent);
2411 static RenderElement* nearestCommonHoverAncestor(RenderElement* obj1, RenderElement* obj2)
2416 for (RenderElement* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor()) {
2417 for (RenderElement* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor()) {
2418 if (currObj1 == currObj2)
2426 static bool hierarchyHasCapturingEventListeners(Element* element, const AtomicString& eventName)
2428 for (ContainerNode* curr = element; curr; curr = curr->parentOrShadowHostNode()) {
2429 if (curr->hasCapturingEventListeners(eventName))
2435 void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& platformMouseEvent, bool fireMouseOverOut)
2437 Ref<Frame> protectedFrame(m_frame);
2438 Element* targetElement = nullptr;
2440 // If we're capturing, we always go right to that element.
2441 if (m_capturingMouseEventsElement)
2442 targetElement = m_capturingMouseEventsElement.get();
2443 else if (targetNode) {
2444 // If the target node is a non-element, dispatch on the parent. <rdar://problem/4196646>
2445 while (targetNode && !is<Element>(*targetNode))
2446 targetNode = targetNode->parentInComposedTree();
2447 targetElement = downcast<Element>(targetNode);
2450 m_elementUnderMouse = targetElement;
2452 // Fire mouseout/mouseover if the mouse has shifted to a different node.
2453 if (fireMouseOverOut) {
2454 auto scrollableAreaForLastNode = enclosingScrollableArea(m_lastElementUnderMouse.get());
2455 auto scrollableAreaForNodeUnderMouse = enclosingScrollableArea(m_elementUnderMouse.get());
2456 Page* page = m_frame.page();
2458 if (m_lastElementUnderMouse && (!m_elementUnderMouse || &m_elementUnderMouse->document() != m_frame.document())) {
2459 // The mouse has moved between frames.
2460 if (Frame* frame = m_lastElementUnderMouse->document().frame()) {
2461 if (FrameView* frameView = frame->view())
2462 frameView->mouseExitedContentArea();
2464 } else if (page && (scrollableAreaForLastNode && (!scrollableAreaForNodeUnderMouse || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) {
2465 // The mouse has moved between layers.
2466 if (Frame* frame = m_lastElementUnderMouse->document().frame()) {
2467 if (FrameView* frameView = frame->view()) {
2468 if (frameView->containsScrollableArea(scrollableAreaForLastNode))
2469 scrollableAreaForLastNode->mouseExitedContentArea();
2474 if (m_elementUnderMouse && (!m_lastElementUnderMouse || &m_lastElementUnderMouse->document() != m_frame.document())) {
2475 // The mouse has moved between frames.
2476 if (Frame* frame = m_elementUnderMouse->document().frame()) {
2477 if (FrameView* frameView = frame->view())
2478 frameView->mouseEnteredContentArea();
2480 } else if (page && (scrollableAreaForNodeUnderMouse && (!scrollableAreaForLastNode || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) {
2481 // The mouse has moved between layers.
2482 if (Frame* frame = m_elementUnderMouse->document().frame()) {
2483 if (FrameView* frameView = frame->view()) {
2484 if (frameView->containsScrollableArea(scrollableAreaForNodeUnderMouse))
2485 scrollableAreaForNodeUnderMouse->mouseEnteredContentArea();
2490 if (m_lastElementUnderMouse && &m_lastElementUnderMouse->document() != m_frame.document()) {
2491 m_lastElementUnderMouse = nullptr;
2492 m_lastScrollbarUnderMouse = nullptr;
2495 if (m_lastElementUnderMouse != m_elementUnderMouse) {
2496 // mouseenter and mouseleave events are only dispatched if there is a capturing eventhandler on an ancestor
2497 // or a normal eventhandler on the element itself (they don't bubble).
2498 // This optimization is necessary since these events can cause O(n^2) capturing event-handler checks.
2499 bool hasCapturingMouseEnterListener = hierarchyHasCapturingEventListeners(m_elementUnderMouse.get(), eventNames().mouseenterEvent);
2500 bool hasCapturingMouseLeaveListener = hierarchyHasCapturingEventListeners(m_lastElementUnderMouse.get(), eventNames().mouseleaveEvent);
2502 RenderElement* oldHoverRenderer = m_lastElementUnderMouse ? m_lastElementUnderMouse->renderer() : nullptr;
2503 RenderElement* newHoverRenderer = m_elementUnderMouse ? m_elementUnderMouse->renderer() : nullptr;
2504 RenderElement* ancestor = nearestCommonHoverAncestor(oldHoverRenderer, newHoverRenderer);
2506 Vector<Ref<Element>, 32> leftElementsChain;
2507 if (oldHoverRenderer) {
2508 for (RenderElement* curr = oldHoverRenderer; curr && curr != ancestor; curr = curr->hoverAncestor()) {
2509 if (Element* element = curr->element())
2510 leftElementsChain.append(*element);
2513 // If the old hovered element is not null but it's renderer is, it was probably detached.
2514 // In this case, the old hovered element (and its ancestors) must be updated, to ensure it's normal style is re-applied.
2515 for (Element* element = m_lastElementUnderMouse.get(); element; element = element->parentElement())
2516 leftElementsChain.append(*element);
2519 Vector<Ref<Element>, 32> enteredElementsChain;
2520 const Element* ancestorElement = ancestor ? ancestor->element() : nullptr;
2521 for (RenderElement* curr = newHoverRenderer; curr; curr = curr->hoverAncestor()) {
2522 if (Element *element = curr->element()) {
2523 if (element == ancestorElement)
2525 enteredElementsChain.append(*element);
2529 // Send mouseout event to the old node.
2530 if (m_lastElementUnderMouse)
2531 m_lastElementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoutEvent, 0, m_elementUnderMouse.get());
2533 // Send mouseleave to the node hierarchy no longer under the mouse.
2534 for (auto& chain : leftElementsChain) {
2535 if (hasCapturingMouseLeaveListener || chain->hasEventListeners(eventNames().mouseleaveEvent))
2536 chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseleaveEvent, 0, m_elementUnderMouse.get());
2539 // Send mouseover event to the new node.
2540 if (m_elementUnderMouse)
2541 m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoverEvent, 0, m_lastElementUnderMouse.get());
2543 // Send mouseleave event to the nodes hierarchy under the mouse.
2544 for (auto& chain : enteredElementsChain) {
2545 if (hasCapturingMouseEnterListener || chain->hasEventListeners(eventNames().mouseenterEvent))
2546 chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseenterEvent, 0, m_lastElementUnderMouse.get());
2549 m_lastElementUnderMouse = m_elementUnderMouse;
2553 bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& platformMouseEvent, bool setUnder)
2555 Ref<Frame> protectedFrame(m_frame);
2557 if (auto* view = m_frame.view())
2558 view->disableLayerFlushThrottlingTemporarilyForInteraction();
2560 updateMouseEventTargetNode(targetNode, platformMouseEvent, setUnder);
2562 if (m_elementUnderMouse && !m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventType, clickCount))
2565 if (eventType != eventNames().mousedownEvent)
2568 // If clicking on a frame scrollbar, do not make any change to which element is focused.
2569 auto* view = m_frame.view();
2570 if (view && view->scrollbarAtPoint(platformMouseEvent.position()))
2573 // The layout needs to be up to date to determine if an element is focusable.
2574 m_frame.document()->updateLayoutIgnorePendingStylesheets();
2576 // Remove focus from the currently focused element when a link or button is clicked.
2577 // This is expected by some sites that rely on change event handlers running
2578 // from form fields before the button click is processed, behavior that was inherited
2579 // from the user interface of Windows, where pushing a button moves focus to the button.
2581 // Walk up the DOM tree to search for an element to focus.
2583 for (element = m_elementUnderMouse.get(); element; element = element->parentOrShadowHostElement()) {
2584 if (element->isMouseFocusable())
2588 // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus an
2589 // element on mouse down if it's selected and inside a focused element. It will be
2590 // focused if the user does a mouseup over it, however, because the mouseup
2591 // will set a selection inside it, which will also set the focused element.
2592 if (element && m_frame.selection().isRange()) {
2593 if (auto range = m_frame.selection().toNormalizedRange()) {
2594 auto result = range->compareNode(*element);
2595 if (!result.hasException() && result.releaseReturnValue() == Range::NODE_INSIDE && element->isDescendantOf(m_frame.document()->focusedElement()))
2600 // Only change the focus when clicking scrollbars if it can be transferred to a mouse focusable node.
2601 if (!element && isInsideScrollbar(platformMouseEvent.position()))
2604 // If focus shift is blocked, we eat the event.
2605 auto* page = m_frame.page();
2606 if (page && !page->focusController().setFocusedElement(element, m_frame))
2612 bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const
2614 if (RenderView* renderView = m_frame.contentRenderer()) {
2615 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
2616 HitTestResult result(windowPoint);
2617 renderView->hitTest(request, result);
2618 return result.scrollbar();
2624 #if !PLATFORM(GTK) && !PLATFORM(WPE)
2626 bool EventHandler::shouldTurnVerticalTicksIntoHorizontal(const HitTestResult&, const PlatformWheelEvent&) const
2635 void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&, const HitTestResult&, RefPtr<Element>&, RefPtr<ContainerNode>&, WeakPtr<ScrollableArea>&, bool&)
2639 void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& event)
2641 m_frame.mainFrame().wheelEventDeltaFilter()->updateFromDelta(FloatSize(event.deltaX(), event.deltaY()));
2644 bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& event, ContainerNode*, const WeakPtr<ScrollableArea>&)
2646 Ref<Frame> protectedFrame(m_frame);
2648 // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
2649 FrameView* view = m_frame.view();
2651 bool didHandleEvent = view ? view->wheelEvent(event) : false;
2652 m_isHandlingWheelEvent = false;
2653 return didHandleEvent;
2656 bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode*)
2661 void EventHandler::platformNotifyIfEndGesture(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&)
2665 IntPoint EventHandler::effectiveMousePositionForSelectionAutoscroll() const
2667 return m_lastKnownMousePosition;
2670 void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&)
2672 clearLatchedState();
2676 Widget* EventHandler::widgetForEventTarget(Element* eventTarget)
2681 auto* target = eventTarget->renderer();
2682 if (!is<RenderWidget>(target))
2685 return downcast<RenderWidget>(*target).widget();
2688 static WeakPtr<Widget> widgetForElement(const Element& element)
2690 auto target = element.renderer();
2691 if (!is<RenderWidget>(target) || !downcast<RenderWidget>(*target).widget())
2694 return downcast<RenderWidget>(*target).widget()->createWeakPtr();
2697 bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr<Widget>& widget, const WeakPtr<ScrollableArea>& scrollableArea, ContainerNode* scrollableContainer)
2699 m_isHandlingWheelEvent = false;
2701 // We do another check on the widget because the event handler can run JS which results in the frame getting destroyed.
2706 scrollableArea->setScrolledProgrammatically(false);
2708 platformNotifyIfEndGesture(event, scrollableArea);
2710 if (!widget->platformWidget())
2713 return platformCompletePlatformWidgetWheelEvent(event, *widget.get(), scrollableContainer);
2716 bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event)
2718 RenderView* renderView = m_frame.contentRenderer();
2722 Ref<Frame> protectedFrame(m_frame);
2723 RefPtr<FrameView> protector(m_frame.view());
2725 FrameView* view = m_frame.view();
2729 #if ENABLE(POINTER_LOCK)
2730 if (m_frame.page()->pointerLockController().isLocked()) {
2731 m_frame.page()->pointerLockController().dispatchLockedWheelEvent(event);
2736 m_isHandlingWheelEvent = true;
2737 setFrameWasScrolledByUser();
2739 HitTestRequest request;
2740 HitTestResult result(view->windowToContents(event.position()));
2741 renderView->hitTest(request, result);
2743 RefPtr<Element> element = result.targetElement();
2744 RefPtr<ContainerNode> scrollableContainer;
2745 WeakPtr<ScrollableArea> scrollableArea;
2746 bool isOverWidget = result.isOverWidget();
2747 platformPrepareForWheelEvents(event, result, element, scrollableContainer, scrollableArea, isOverWidget);
2750 if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone)
2751 m_frame.mainFrame().resetLatchingState();
2754 // FIXME: It should not be necessary to do this mutation here.
2755 // Instead, the handlers should know convert vertical scrolls appropriately.
2756 PlatformWheelEvent adjustedEvent = event;
2757 if (shouldTurnVerticalTicksIntoHorizontal(result, event))
2758 adjustedEvent = event.copyTurningVerticalTicksIntoHorizontalTicks();
2760 platformRecordWheelEvent(adjustedEvent);
2764 if (WeakPtr<Widget> widget = widgetForElement(*element)) {
2765 if (widgetDidHandleWheelEvent(event, *widget.get()))
2766 return completeWidgetWheelEvent(adjustedEvent, widget, scrollableArea, scrollableContainer.get());
2770 if (!element->dispatchWheelEvent(adjustedEvent)) {
2771 m_isHandlingWheelEvent = false;
2772 if (scrollableArea && scrollableArea->isScrolledProgrammatically()) {
2773 // Web developer is controlling scrolling, so don't attempt to latch.
2774 clearLatchedState();
2775 scrollableArea->setScrolledProgrammatically(false);
2778 platformNotifyIfEndGesture(adjustedEvent, scrollableArea);
2784 scrollableArea->setScrolledProgrammatically(false);
2786 bool handledEvent = platformCompleteWheelEvent(adjustedEvent, scrollableContainer.get(), scrollableArea);
2787 platformNotifyIfEndGesture(adjustedEvent, scrollableArea);
2788 return handledEvent;
2791 void EventHandler::clearLatchedState()
2794 m_frame.mainFrame().resetLatchingState();
2796 if (auto filter = m_frame.mainFrame().wheelEventDeltaFilter())
2797 filter->endFilteringDeltas();
2800 void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent& wheelEvent)
2805 Ref<Frame> protectedFrame(m_frame);
2807 FloatSize filteredPlatformDelta(wheelEvent.deltaX(), wheelEvent.deltaY());
2808 FloatSize filteredVelocity;
2809 if (const PlatformWheelEvent* platformWheelEvent = wheelEvent.wheelEvent()) {
2810 filteredPlatformDelta.setWidth(platformWheelEvent->deltaX());
2811 filteredPlatformDelta.setHeight(platformWheelEvent->deltaY());
2815 ScrollLatchingState* latchedState = m_frame.mainFrame().latchingState();
2816 Element* stopElement = latchedState ? latchedState->previousWheelScrolledElement() : nullptr;
2818 if (m_frame.mainFrame().wheelEventDeltaFilter()->isFilteringDeltas()) {
2819 filteredPlatformDelta = m_frame.mainFrame().wheelEventDeltaFilter()->filteredDelta();
2820 filteredVelocity = m_frame.mainFrame().wheelEventDeltaFilter()->filteredVelocity();
2823 Element* stopElement = nullptr;
2827 if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &stopElement, filteredPlatformDelta, filteredVelocity))
2828 wheelEvent.setDefaultHandled();
2831 if (latchedState && !latchedState->wheelEventElement())
2832 latchedState->setPreviousWheelScrolledElement(stopElement);
2836 #if ENABLE(CONTEXT_MENUS)
2837 bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event)
2839 Ref<Frame> protectedFrame(m_frame);
2841 Document* doc = m_frame.document();
2842 FrameView* view = m_frame.view();
2846 // Clear mouse press state to avoid initiating a drag while context menu is up.
2847 m_mousePressed = false;
2849 LayoutPoint viewportPos = view->windowToContents(event.position());
2850 HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
2851 MouseEventWithHitTestResults mouseEvent = doc->prepareMouseEvent(request, viewportPos, event);
2853 // Do not show context menus when clicking on scrollbars.
2854 if (mouseEvent.scrollbar() || view->scrollbarAtPoint(event.position()))
2857 if (m_frame.editor().behavior().shouldSelectOnContextualMenuClick()
2858 && !m_frame.selection().contains(viewportPos)
2859 // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse.
2860 // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items
2861 // available for text selections. But only if we're above text.
2862 && (m_frame.selection().selection().isContentEditable() || (mouseEvent.targetNode() && mouseEvent.targetNode()->isTextNode()))) {
2863 m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2864 selectClosestContextualWordOrLinkFromMouseEvent(mouseEvent);
2867 swallowEvent = !dispatchMouseEvent(eventNames().contextmenuEvent, mouseEvent.targetNode(), true, 0, event, false);
2869 return swallowEvent;
2872 bool EventHandler::sendContextMenuEventForKey()
2874 Ref<Frame> protectedFrame(m_frame);
2876 FrameView* view = m_frame.view();
2880 Document* doc = m_frame.document();
2884 // Clear mouse press state to avoid initiating a drag while context menu is up.
2885 m_mousePressed = false;
2887 static const int kContextMenuMargin = 1;
2890 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
2892 int rightAligned = 0;
2896 Element* focusedElement = doc->focusedElement();
2897 const VisibleSelection& selection = m_frame.selection().selection();
2898 Position start = selection.start();
2900 if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) {
2901 RefPtr<Range> selectionRange = selection.toNormalizedRange();
2902 IntRect firstRect = m_frame.editor().firstRectForRange(selectionRange.get());
2904 int x = rightAligned ? firstRect.maxX() : firstRect.x();
2905 // In a multiline edit, firstRect.maxY() would endup on the next line, so -1.
2906 int y = firstRect.maxY() ? firstRect.maxY() - 1 : 0;
2907 location = IntPoint(x, y);
2908 } else if (focusedElement) {
2909 RenderBoxModelObject* box = focusedElement->renderBoxModelObject();
2913 IntRect boundingBoxRect = box->absoluteBoundingBoxRect(true);
2914 location = IntPoint(boundingBoxRect.x(), boundingBoxRect.maxY() - 1);
2916 location = IntPoint(
2917 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
2918 kContextMenuMargin);
2921 m_frame.view()->setCursor(pointerCursor());
2923 IntPoint position = view->contentsToRootView(location);
2924 IntPoint globalPosition = view->hostWindow()->rootViewToScreen(IntRect(position, IntSize())).location();
2926 Node* targetNode = doc->focusedElement();
2930 // Use the focused node as the target for hover and active.
2931 HitTestResult result(position);
2932 result.setInnerNode(targetNode);
2933 doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent, result.targetElement());
2935 // The contextmenu event is a mouse event even when invoked using the keyboard.
2936 // This is required for web compatibility.
2939 PlatformEvent::Type eventType = PlatformEvent::MouseReleased;
2941 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
2944 PlatformMouseEvent platformMouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime(), ForceAtClick, NoTap);
2946 return sendContextMenuEvent(platformMouseEvent);
2948 #endif // ENABLE(CONTEXT_MENUS)
2950 void EventHandler::scheduleHoverStateUpdate()
2952 if (!m_hoverTimer.isActive())
2953 m_hoverTimer.startOneShot(0_s);
2956 #if ENABLE(CURSOR_SUPPORT)
2957 void EventHandler::scheduleCursorUpdate()
2959 if (!m_cursorUpdateTimer.isActive())
2960 m_cursorUpdateTimer.startOneShot(cursorUpdateInterval);
2964 void EventHandler::dispatchFakeMouseMoveEventSoon()
2966 #if !ENABLE(IOS_TOUCH_EVENTS)
2970 if (m_mousePositionIsUnknown)
2973 if (Page* page = m_frame.page()) {
2974 if (!page->chrome().client().shouldDispatchFakeMouseMoveEvents())
2978 // If the content has ever taken longer than fakeMouseMoveShortInterval we
2979 // reschedule the timer and use a longer time. This will cause the content
2980 // to receive these moves only after the user is done scrolling, reducing
2981 // pauses during the scroll.
2982 if (m_fakeMouseMoveEventTimer.isActive())
2983 m_fakeMouseMoveEventTimer.stop();
2984 m_fakeMouseMoveEventTimer.startOneShot(m_maxMouseMovedDuration > fakeMouseMoveDurationThreshold ? fakeMouseMoveLongInterval : fakeMouseMoveShortInterval);
2988 void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad)
2990 #if ENABLE(IOS_TOUCH_EVENTS)
2993 FrameView* view = m_frame.view();
2997 if (!quad.containsPoint(view->windowToContents(m_lastKnownMousePosition)))
3000 dispatchFakeMouseMoveEventSoon();
3004 #if !ENABLE(IOS_TOUCH_EVENTS)
3005 void EventHandler::cancelFakeMouseMoveEvent()
3007 m_fakeMouseMoveEventTimer.stop();
3010 void EventHandler::fakeMouseMoveEventTimerFired()
3012 ASSERT(!m_mousePressed);
3014 FrameView* view = m_frame.view();
3018 if (!m_frame.page() || !m_frame.page()->isVisible() || !m_frame.page()->focusController().isActive())
3025 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
3026 PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, currentTime(), 0, NoTap);
3027 mouseMoved(fakeMouseMoveEvent);
3029 #endif // !ENABLE(IOS_TOUCH_EVENTS)
3031 void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet)
3033 m_frameSetBeingResized = frameSet;
3036 void EventHandler::resizeLayerDestroyed()
3038 ASSERT(m_resizeLayer);
3039 m_resizeLayer = nullptr;
3042 void EventHandler::hoverTimerFired()
3044 m_hoverTimer.stop();
3046 ASSERT(m_frame.document());
3048 Ref<Frame> protectedFrame(m_frame);
3050 if (RenderView* renderView = m_frame.contentRenderer()) {
3051 if (FrameView* view = m_frame.view()) {
3052 HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent);
3053 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
3054 renderView->hitTest(request, result);
3055 m_frame.document()->updateHoverActiveState(request, result.targetElement());
3060 bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& event)
3062 // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do.
3063 // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and
3064 // lower case variants are present in a document, the correct element is matched based on Shift key state.
3065 // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively.
3066 ASSERT(!accessKeyModifiers().contains(PlatformEvent::Modifier::ShiftKey));
3068 if ((event.modifiers() - PlatformEvent::Modifier::ShiftKey) != accessKeyModifiers())
3070 Element* element = m_frame.document()->getElementByAccessKey(event.unmodifiedText());
3073 element->accessKeyAction(false);
3078 bool EventHandler::needsKeyboardEventDisambiguationQuirks() const
3084 #if ENABLE(FULLSCREEN_API)
3085 bool EventHandler::isKeyEventAllowedInFullScreen(const PlatformKeyboardEvent& keyEvent) const
3087 Document* document = m_frame.document();
3088 if (document->webkitFullScreenKeyboardInputAllowed())
3091 if (keyEvent.type() == PlatformKeyboardEvent::Char) {
3092 if (keyEvent.text().length() != 1)
3094 UChar character = keyEvent.text()[0];
3095 return character == ' ';
3098 int keyCode = keyEvent.windowsVirtualKeyCode();
3099 return (keyCode >= VK_BACK && keyCode <= VK_CAPITAL)
3100 || (keyCode >= VK_SPACE && keyCode <= VK_DELETE)
3101 || (keyCode >= VK_OEM_1 && keyCode <= VK_OEM_PLUS)
3102 || (keyCode >= VK_MULTIPLY && keyCode <= VK_OEM_8);
3106 bool EventHandler::keyEvent(const PlatformKeyboardEvent& keyEvent)
3108 Document* topDocument = m_frame.document() ? &m_frame.document()->topDocument() : nullptr;
3109 bool savedUserDidInteractWithPage = topDocument ? topDocument->userDidInteractWithPage() : false;
3110 bool wasHandled = internalKeyEvent(keyEvent);
3112 // If the key event was not handled, do not treat it as user interaction with the page.
3113 if (topDocument && !wasHandled)
3114 topDocument->setUserDidInteractWithPage(savedUserDidInteractWithPage);
3119 bool EventHandler::internalKeyEvent(const PlatformKeyboardEvent& initialKeyEvent)
3121 Ref<Frame> protectedFrame(m_frame);
3122 RefPtr<FrameView> protector(m_frame.view());
3124 LOG(Editing, "EventHandler %p keyEvent (text %s keyIdentifier %s)", this, initialKeyEvent.text().utf8().data(), initialKeyEvent.keyIdentifier().utf8().data());
3126 #if ENABLE(POINTER_LOCK)
3127 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE && m_frame.page()->pointerLockController().element()) {
3128 m_frame.page()->pointerLockController().requestPointerUnlockAndForceCursorVisible();
3132 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) {
3133 if (auto* page = m_frame.page()) {
3134 if (auto* validationMessageClient = page->validationMessageClient())
3135 validationMessageClient->hideAnyValidationMessage();
3139 #if ENABLE(FULLSCREEN_API)
3140 if (m_frame.document()->webkitIsFullScreen()) {
3141 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) {
3142 m_frame.document()->webkitCancelFullScreen();
3146 if (!isKeyEventAllowedInFullScreen(initialKeyEvent))
3151 if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL) {
3152 if (auto* element = m_frame.document()->focusedElement()) {
3153 if (is<HTMLInputElement>(*element))
3154 downcast<HTMLInputElement>(*element).capsLockStateMayHaveChanged();
3158 #if ENABLE(PAN_SCROLLING)
3159 if (m_frame.mainFrame().eventHandler().panScrollInProgress()) {
3160 // If a key is pressed while the panScroll is in progress then we want to stop
3161 if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent.type() == PlatformEvent::RawKeyDown)
3162 stopAutoscrollTimer();
3164 // If we were in panscroll mode, we swallow the key event
3169 // Check for cases where we are too early for events -- possible unmatched key up
3170 // from pressing return in the location bar.
3171 RefPtr<Element> element = eventTargetElementForDocument(m_frame.document());
3175 UserGestureType gestureType = UserGestureType::Other;
3176 if (initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE)
3177 gestureType = UserGestureType::EscapeKey;
3179 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document(), gestureType);
3180 UserTypingGestureIndicator typingGestureIndicator(m_frame);
3182 if (FrameView* view = m_frame.view())
3183 view->disableLayerFlushThrottlingTemporarilyForInteraction();
3185 // FIXME (bug 68185): this call should be made at another abstraction layer
3186 m_frame.loader().resetMultipleFormSubmissionProtection();
3188 // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match.
3189 // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict
3190 // with access keys. Then we dispatch keydown, but suppress its default handling.
3191 // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatching a keypress event for WM_SYSCHAR messages.
3192 // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events.
3193 bool matchedAnAccessKey = false;
3194 if (initialKeyEvent.type() == PlatformEvent::KeyDown)
3195 matchedAnAccessKey = handleAccessKey(initialKeyEvent);
3197 // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch.
3198 if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char)
3199 return !element->dispatchKeyEvent(initialKeyEvent);
3201 bool backwardCompatibilityMode = needsKeyboardEventDisambiguationQuirks();
3203 PlatformKeyboardEvent keyDownEvent = initialKeyEvent;
3204 if (keyDownEvent.type() != PlatformEvent::RawKeyDown)
3205 keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown, backwardCompatibilityMode);
3206 Ref<KeyboardEvent> keydown = KeyboardEvent::create(keyDownEvent, m_frame.document()->defaultView());
3207 if (matchedAnAccessKey)
3208 keydown->setDefaultPrevented(true);
3209 keydown->setTarget(element);
3211 if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) {
3212 element->dispatchEvent(keydown);
3213 // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame.
3214 bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame();
3215 return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3218 // Run input method in advance of DOM event handling. This may result in the IM
3219 // modifying the page prior the keydown event, but this behaviour is necessary
3220 // in order to match IE:
3221 // 1. preventing default handling of keydown and keypress events has no effect on IM input;
3222 // 2. if an input method handles the event, its keyCode is set to 229 in keydown event.
3223 m_frame.editor().handleInputMethodKeydown(keydown.get());
3225 bool handledByInputMethod = keydown->defaultHandled();
3227 if (handledByInputMethod) {
3228 keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode);
3229 keydown = KeyboardEvent::create(keyDownEvent, m_frame.document()->defaultView());
3230 keydown->setTarget(element);
3231 keydown->setDefaultHandled();
3234 if (accessibilityPreventsEventPropogation(keydown))
3235 keydown->stopPropagation();
3237 element->dispatchEvent(keydown);
3238 // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame.
3239 bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame();
3240 bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3241 if (handledByInputMethod || (keydownResult && !backwardCompatibilityMode))
3242 return keydownResult;
3244 // Focus may have changed during keydown handling, so refetch element.
3245 // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original element.
3246 if (!keydownResult) {
3247 element = eventTargetElementForDocument(m_frame.document());
3252 PlatformKeyboardEvent keyPressEvent = initialKeyEvent;
3253 keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char, backwardCompatibilityMode);
3254 if (keyPressEvent.text().isEmpty())
3255 return keydownResult;
3256 Ref<KeyboardEvent> keypress = KeyboardEvent::create(keyPressEvent, m_frame.document()->defaultView());
3257 keypress->setTarget(element);
3259 keypress->setDefaultPrevented(true);
3261 keypress->keypressCommands() = keydown->keypressCommands();
3263 element->dispatchEvent(keypress);
3265 return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled();
3268 static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier)
3270 static NeverDestroyed<AtomicString> Down("Down", AtomicString::ConstructFromLiteral);
3271 static NeverDestroyed<AtomicString> Up("Up", AtomicString::ConstructFromLiteral);
3272 static NeverDestroyed<AtomicString> Left("Left", AtomicString::ConstructFromLiteral);
3273 static NeverDestroyed<AtomicString> Right("Right", AtomicString::ConstructFromLiteral);
3275 FocusDirection retVal = FocusDirectionNone;
3277 if (keyIdentifier == Down)
3278 retVal = FocusDirectionDown;
3279 else if (keyIdentifier == Up)
3280 retVal = FocusDirectionUp;
3281 else if (keyIdentifier == Left)
3282 retVal = FocusDirectionLeft;
3283 else if (keyIdentifier == Right)
3284 retVal = FocusDirectionRight;
3289 static void setInitialKeyboardSelection(Frame& frame, SelectionDirection direction)
3291 Document* document = frame.document();
3295 FrameSelection& selection = frame.selection();
3297 if (!selection.isNone())
3300 Element* focusedElement = document->focusedElement();
3301 VisiblePosition visiblePosition;
3303 switch (direction) {
3304 case DirectionBackward:
3307 visiblePosition = VisiblePosition(positionBeforeNode(focusedElement));
3309 visiblePosition = endOfDocument(document);
3311 case DirectionForward:
3312 case DirectionRight:
3314 visiblePosition = VisiblePosition(positionAfterNode(focusedElement));
3316 visiblePosition = startOfDocument(document);
3320 AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
3321 selection.setSelection(visiblePosition, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent);
3324 static void handleKeyboardSelectionMovement(Frame& frame, KeyboardEvent& event)
3326 FrameSelection& selection = frame.selection();
3328 bool isCommanded = event.getModifierState("Meta");
3329 bool isOptioned = event.getModifierState("Alt");
3330 bool isSelection = !selection.isNone();
3332 FrameSelection::EAlteration alternation = event.getModifierState("Shift") ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove;
3333 SelectionDirection direction = DirectionForward;
3334 TextGranularity granularity = CharacterGranularity;
3336 switch (focusDirectionForKey(event.keyIdentifier())) {
3337 case FocusDirectionNone:
3339 case FocusDirectionForward:
3340 case FocusDirectionBackward:
3341 ASSERT_NOT_REACHED();
3343 case FocusDirectionUp:
3344 direction = DirectionBackward;
3345 granularity = isCommanded ? DocumentBoundary : LineGranularity;
3347 case FocusDirectionDown:
3348 direction = DirectionForward;
3349 granularity = isCommanded ? DocumentBoundary : LineGranularity;
3351 case FocusDirectionLeft:
3352 direction = DirectionLeft;
3353 granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity;
3355 case FocusDirectionRight:
3356 direction = DirectionRight;
3357 granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity;
3362 selection.modify(alternation, direction, granularity, UserTriggered);
3364 setInitialKeyboardSelection(frame, direction);
3366 event.setDefaultHandled();
3369 void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent& event)
3371 if (event.type() == eventNames().keydownEvent) {
3372 if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
3373 handleKeyboardSelectionMovement(m_frame, event);
3377 bool EventHandler::accessibilityPreventsEventPropogation(KeyboardEvent& event)
3380 if (!AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
3383 if (!m_frame.settings().preventKeyboardDOMEventDispatch())
3386 // Check for key events that are relevant to accessibility: tab and arrows keys that change focus
3387 if (event.keyIdentifier() == "U+0009")
3389 FocusDirection direction = focusDirectionForKey(event.keyIdentifier());
3390 if (direction != FocusDirectionNone)
3393 UNUSED_PARAM(event);
3398 void EventHandler::defaultKeyboardEventHandler(KeyboardEvent& event)
3400 Ref<Frame> protectedFrame(m_frame);
3402 if (event.type() == eventNames().keydownEvent) {
3403 m_frame.editor().handleKeyboardEvent(event);
3404 if (event.defaultHandled())
3406 if (event.keyIdentifier() == "U+0009")
3407 defaultTabEventHandler(event);
3408 else if (event.keyIdentifier() == "U+0008")
3409 defaultBackspaceEventHandler(event);
3411 FocusDirection direction = focusDirectionForKey(event.keyIdentifier());
3412 if (direction != FocusDirectionNone)
3413 defaultArrowEventHandler(direction, event);
3416 handleKeyboardSelectionMovementForAccessibility(event);
3418 if (event.type() == eventNames().keypressEvent) {
3419 m_frame.editor().handleKeyboardEvent(event);
3420 if (event.defaultHandled())
3422 if (event.charCode() == ' ')
3423 defaultSpaceEventHandler(event);
3427 #if ENABLE(DRAG_SUPPORT)
3428 bool EventHandler::dragHysteresisExceeded(const IntPoint& floatDragViewportLocation) const
3430 FloatPoint dragViewportLocation(floatDragViewportLocation.x(), floatDragViewportLocation.y());
3431 return dragHysteresisExceeded(dragViewportLocation);
3434 bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation) const
3436 int threshold = GeneralDragHysteresis;
3437 switch (dragState().type) {
3438 case DragSourceActionSelection:
3439 threshold = TextDragHysteresis;
3441 case DragSourceActionImage:
3442 #if ENABLE(ATTACHMENT_ELEMENT)
3443 case DragSourceActionAttachment:
3445 threshold = ImageDragHysteresis;
3447 case DragSourceActionLink:
3448 threshold = LinkDragHysteresis;
3450 case DragSourceActionDHTML:
3452 case DragSourceActionNone:
3453 case DragSourceActionAny:
3454 ASSERT_NOT_REACHED();
3457 return mouseMovementExceedsThreshold(dragViewportLocation, threshold);
3460 void EventHandler::invalidateDataTransfer()
3462 if (!dragState().dataTransfer)
3464 dragState().dataTransfer->makeInvalidForSecurity();
3465 dragState().dataTransfer = nullptr;
3468 static void repaintContentsOfRange(RefPtr<Range> range)
3473 auto* container = range->commonAncestorContainer();
3477 // This ensures that all nodes enclosed in this Range are repainted.
3478 if (auto rendererToRepaint = container->renderer()) {
3479 if (auto* containingRenderer = rendererToRepaint->container())
3480 rendererToRepaint = containingRenderer;
3481 rendererToRepaint->repaint();
3485 void EventHandler::dragCancelled()
3487 #if ENABLE(DATA_INTERACTION)
3488 if (auto range = dragState().draggedContentRange) {
3489 range->ownerDocument().markers().removeMarkers(DocumentMarker::DraggedContent);
3490 repaintContentsOfRange(range);
3492 dragState().draggedContentRange = nullptr;
3496 void EventHandler::didStartDrag()
3498 #if ENABLE(DATA_INTERACTION)
3499 auto dragSource = dragState().source;
3503 auto* renderer = dragSource->renderer();
3507 if (dragState().type & DragSourceActionSelection)
3508 dragState().draggedContentRange = m_frame.selection().selection().toNormalizedRange();