1/*
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)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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.
14 *
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.
26 */
27
28#include "config.h"
29#include "EventHandler.h"
30
31#include "AutoscrollController.h"
32#include "BackForwardController.h"
33#include "CachedImage.h"
34#include "Chrome.h"
35#include "ChromeClient.h"
36#include "ComposedTreeAncestorIterator.h"
37#include "CursorList.h"
38#include "DocumentMarkerController.h"
39#include "DragController.h"
40#include "DragState.h"
41#include "Editing.h"
42#include "Editor.h"
43#include "EditorClient.h"
44#include "EventNames.h"
45#include "FileList.h"
46#include "FloatPoint.h"
47#include "FloatRect.h"
48#include "FocusController.h"
49#include "Frame.h"
50#include "FrameLoader.h"
51#include "FrameSelection.h"
52#include "FrameTree.h"
53#include "FrameView.h"
54#include "FullscreenManager.h"
55#include "HTMLDocument.h"
56#include "HTMLFrameElement.h"
57#include "HTMLFrameSetElement.h"
58#include "HTMLHtmlElement.h"
59#include "HTMLIFrameElement.h"
60#include "HTMLInputElement.h"
61#include "HTMLNames.h"
62#include "HitTestRequest.h"
63#include "HitTestResult.h"
64#include "Image.h"
65#include "InspectorInstrumentation.h"
66#include "KeyboardEvent.h"
67#include "Logging.h"
68#include "MouseEvent.h"
69#include "MouseEventWithHitTestResults.h"
70#include "NotImplemented.h"
71#include "Page.h"
72#include "PageOverlayController.h"
73#include "Pasteboard.h"
74#include "PlatformEvent.h"
75#include "PlatformKeyboardEvent.h"
76#include "PlatformWheelEvent.h"
77#include "PluginDocument.h"
78#include "Range.h"
79#include "RenderFrameSet.h"
80#include "RenderLayer.h"
81#include "RenderListBox.h"
82#include "RenderTextControlSingleLine.h"
83#include "RenderView.h"
84#include "RenderWidget.h"
85#include "ResourceLoadObserver.h"
86#include "RuntimeApplicationChecks.h"
87#include "SVGDocument.h"
88#include "SVGNames.h"
89#include "ScrollLatchingState.h"
90#include "Scrollbar.h"
91#include "Settings.h"
92#include "ShadowRoot.h"
93#include "SpatialNavigation.h"
94#include "StaticPasteboard.h"
95#include "StyleCachedImage.h"
96#include "TextEvent.h"
97#include "TextIterator.h"
98#include "UserGestureIndicator.h"
99#include "UserTypingGestureIndicator.h"
100#include "ValidationMessageClient.h"
101#include "VisibleUnits.h"
102#include "WheelEvent.h"
103#include "WheelEventDeltaFilter.h"
104#include "WindowsKeyboardCodes.h"
105#include <wtf/Assertions.h>
106#include <wtf/NeverDestroyed.h>
107#include <wtf/StdLibExtras.h>
108
109#if ENABLE(IOS_TOUCH_EVENTS)
110#include "PlatformTouchEventIOS.h"
111#endif
112
113#if ENABLE(TOUCH_EVENTS)
114#include "TouchEvent.h"
115#include "TouchList.h"
116#endif
117
118#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
119#include "PlatformTouchEvent.h"
120#endif
121
122#if ENABLE(MAC_GESTURE_EVENTS)
123#include "PlatformGestureEventMac.h"
124#endif
125
126#if ENABLE(POINTER_LOCK)
127#include "PointerLockController.h"
128#endif
129
130namespace WebCore {
131
132using namespace HTMLNames;
133
134#if ENABLE(DRAG_SUPPORT)
135// The link drag hysteresis is much larger than the others because there
136// needs to be enough space to cancel the link press without starting a link drag,
137// and because dragging links is rare.
138const int LinkDragHysteresis = 40;
139const int ImageDragHysteresis = 5;
140const int TextDragHysteresis = 3;
141const int ColorDragHystersis = 3;
142const int GeneralDragHysteresis = 3;
143#if PLATFORM(MAC)
144const Seconds EventHandler::TextDragDelay { 150_ms };
145#else
146const Seconds EventHandler::TextDragDelay { 0_s };
147#endif
148#endif // ENABLE(DRAG_SUPPORT)
149
150#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS)
151const float GestureUnknown = 0;
152#endif
153
154#if ENABLE(IOS_TOUCH_EVENTS)
155// FIXME: Share this constant with EventHandler and SliderThumbElement.
156const unsigned InvalidTouchIdentifier = 0;
157#endif
158
159// Match key code of composition keydown event on windows.
160// IE sends VK_PROCESSKEY which has value 229;
161const int CompositionEventKeyCode = 229;
162
163using namespace SVGNames;
164
165#if !ENABLE(IOS_TOUCH_EVENTS)
166// The amount of time to wait before sending a fake mouse event, triggered
167// during a scroll. The short interval is used if the content responds to the mouse events
168// in fakeMouseMoveDurationThreshold or less, otherwise the long interval is used.
169const double fakeMouseMoveDurationThreshold = 0.01;
170const Seconds fakeMouseMoveShortInterval = { 100_ms };
171const Seconds fakeMouseMoveLongInterval = { 250_ms };
172#endif
173
174#if ENABLE(CURSOR_SUPPORT)
175// The amount of time to wait for a cursor update on style and layout changes
176// Set to 50Hz, no need to be faster than common screen refresh rate
177static const Seconds cursorUpdateInterval { 20_ms };
178
179const int maximumCursorSize = 128;
180#endif
181
182#if ENABLE(MOUSE_CURSOR_SCALE)
183// It's pretty unlikely that a scale of less than one would ever be used. But all we really
184// need to ensure here is that the scale isn't so small that integer overflow can occur when
185// dividing cursor sizes (limited above) by the scale.
186const double minimumCursorScale = 0.001;
187#endif
188
189class MaximumDurationTracker {
190public:
191 explicit MaximumDurationTracker(double *maxDuration)
192 : m_maxDuration(maxDuration)
193 , m_start(MonotonicTime::now())
194 {
195 }
196
197 ~MaximumDurationTracker()
198 {
199 *m_maxDuration = std::max(*m_maxDuration, (MonotonicTime::now() - m_start).seconds());
200 }
201
202private:
203 double* m_maxDuration;
204 MonotonicTime m_start;
205};
206
207#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
208class SyntheticTouchPoint : public PlatformTouchPoint {
209public:
210
211 // The default values are based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
212 explicit SyntheticTouchPoint(const PlatformMouseEvent& event)
213 {
214 const static int idDefaultValue = 0;
215 const static int radiusYDefaultValue = 1;
216 const static int radiusXDefaultValue = 1;
217 const static float rotationAngleDefaultValue = 0.0f;
218 const static float forceDefaultValue = 1.0f;
219
220 m_id = idDefaultValue; // There is only one active TouchPoint.
221 m_screenPos = event.globalPosition();
222 m_pos = event.position();
223 m_radiusY = radiusYDefaultValue;
224 m_radiusX = radiusXDefaultValue;
225 m_rotationAngle = rotationAngleDefaultValue;
226 m_force = forceDefaultValue;
227
228 PlatformEvent::Type type = event.type();
229 ASSERT(type == PlatformEvent::MouseMoved || type == PlatformEvent::MousePressed || type == PlatformEvent::MouseReleased);
230
231 switch (type) {
232 case PlatformEvent::MouseMoved:
233 m_state = TouchMoved;
234 break;
235 case PlatformEvent::MousePressed:
236 m_state = TouchPressed;
237 break;
238 case PlatformEvent::MouseReleased:
239 m_state = TouchReleased;
240 break;
241 default:
242 ASSERT_NOT_REACHED();
243 break;
244 }
245 }
246};
247
248class SyntheticSingleTouchEvent : public PlatformTouchEvent {
249public:
250 explicit SyntheticSingleTouchEvent(const PlatformMouseEvent& event)
251 {
252 switch (event.type()) {
253 case PlatformEvent::MouseMoved:
254 m_type = TouchMove;
255 break;
256 case PlatformEvent::MousePressed:
257 m_type = TouchStart;
258 break;
259 case PlatformEvent::MouseReleased:
260 m_type = TouchEnd;
261 break;
262 default:
263 ASSERT_NOT_REACHED();
264 m_type = NoType;
265 break;
266 }
267 m_timestamp = event.timestamp();
268 m_modifiers = event.modifiers();
269 m_touchPoints.append(SyntheticTouchPoint(event));
270 }
271};
272#endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
273
274static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned deltaMode)
275{
276 switch (deltaMode) {
277 case WheelEvent::DOM_DELTA_PAGE:
278 return ScrollByPage;
279 case WheelEvent::DOM_DELTA_LINE:
280 return ScrollByLine;
281 case WheelEvent::DOM_DELTA_PIXEL:
282 return ScrollByPixel;
283 default:
284 return ScrollByPixel;
285 }
286}
287
288static inline bool didScrollInScrollableArea(ScrollableArea* scrollableArea, WheelEvent& wheelEvent)
289{
290 ScrollGranularity scrollGranularity = wheelGranularityToScrollGranularity(wheelEvent.deltaMode());
291 bool didHandleWheelEvent = false;
292 if (float absoluteDelta = std::abs(wheelEvent.deltaX()))
293 didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaX() > 0 ? ScrollRight : ScrollLeft, scrollGranularity, absoluteDelta);
294
295 if (float absoluteDelta = std::abs(wheelEvent.deltaY()))
296 didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaY() > 0 ? ScrollDown : ScrollUp, scrollGranularity, absoluteDelta);
297
298 return didHandleWheelEvent;
299}
300
301static inline bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, WheelEvent& wheelEvent, Element** stopElement, const FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity)
302{
303 bool shouldHandleEvent = wheelEvent.deltaX() || wheelEvent.deltaY();
304#if PLATFORM(MAC)
305 shouldHandleEvent |= wheelEvent.phase() == PlatformWheelEventPhaseEnded;
306#if ENABLE(CSS_SCROLL_SNAP)
307 shouldHandleEvent |= wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded;
308#endif
309#endif
310 if (!startNode->renderer() || !shouldHandleEvent)
311 return false;
312
313 RenderBox& initialEnclosingBox = startNode->renderer()->enclosingBox();
314 if (initialEnclosingBox.isListBox())
315 return didScrollInScrollableArea(static_cast<RenderListBox*>(&initialEnclosingBox), wheelEvent);
316
317 RenderBox* currentEnclosingBox = &initialEnclosingBox;
318 while (currentEnclosingBox) {
319 if (RenderLayer* boxLayer = currentEnclosingBox->layer()) {
320 auto platformEvent = wheelEvent.underlyingPlatformEvent();
321 bool scrollingWasHandled;
322 if (platformEvent) {
323 auto copiedEvent = platformEvent->copyWithDeltasAndVelocity(filteredPlatformDelta.width(), filteredPlatformDelta.height(), filteredVelocity);
324 scrollingWasHandled = boxLayer->handleWheelEvent(copiedEvent);
325 } else
326 scrollingWasHandled = didScrollInScrollableArea(boxLayer, wheelEvent);
327
328 if (scrollingWasHandled) {
329 if (stopElement)
330 *stopElement = currentEnclosingBox->element();
331 return true;
332 }
333 }
334
335 if (stopElement && *stopElement && *stopElement == currentEnclosingBox->element())
336 return true;
337
338 currentEnclosingBox = currentEnclosingBox->containingBlock();
339 if (!currentEnclosingBox || currentEnclosingBox->isRenderView())
340 return false;
341 }
342 return false;
343}
344
345#if (ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS_FAMILY))
346static inline bool shouldGesturesTriggerActive()
347{
348 // If the platform we're on supports GestureTapDown and GestureTapCancel then we'll
349 // rely on them to set the active state. Unfortunately there's no generic way to
350 // know in advance what event types are supported.
351 return false;
352}
353#endif
354
355#if !PLATFORM(COCOA)
356
357inline bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&)
358{
359 return false;
360}
361
362#if ENABLE(DRAG_SUPPORT)
363inline bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&)
364{
365 return false;
366}
367#endif
368
369#endif
370
371EventHandler::EventHandler(Frame& frame)
372 : m_frame(frame)
373 , m_hoverTimer(*this, &EventHandler::hoverTimerFired)
374#if ENABLE(CURSOR_SUPPORT)
375 , m_cursorUpdateTimer(*this, &EventHandler::cursorUpdateTimerFired)
376#endif
377#if PLATFORM(MAC)
378 , m_pendingMomentumWheelEventsTimer(*this, &EventHandler::clearLatchedState)
379#endif
380 , m_autoscrollController(std::make_unique<AutoscrollController>())
381#if !ENABLE(IOS_TOUCH_EVENTS)
382 , m_fakeMouseMoveEventTimer(*this, &EventHandler::fakeMouseMoveEventTimerFired)
383#endif
384#if ENABLE(CURSOR_VISIBILITY)
385 , m_autoHideCursorTimer(*this, &EventHandler::autoHideCursorTimerFired)
386#endif
387{
388}
389
390EventHandler::~EventHandler()
391{
392#if !ENABLE(IOS_TOUCH_EVENTS)
393 ASSERT(!m_fakeMouseMoveEventTimer.isActive());
394#endif
395#if ENABLE(CURSOR_VISIBILITY)
396 ASSERT(!m_autoHideCursorTimer.isActive());
397#endif
398}
399
400#if ENABLE(DRAG_SUPPORT)
401
402DragState& EventHandler::dragState()
403{
404 static NeverDestroyed<DragState> state;
405 return state;
406}
407
408#endif
409
410void EventHandler::clear()
411{
412 m_hoverTimer.stop();
413#if ENABLE(CURSOR_SUPPORT)
414 m_cursorUpdateTimer.stop();
415#endif
416#if !ENABLE(IOS_TOUCH_EVENTS)
417 m_fakeMouseMoveEventTimer.stop();
418#endif
419#if ENABLE(CURSOR_VISIBILITY)
420 cancelAutoHideCursorTimer();
421#endif
422 m_resizeLayer = nullptr;
423 m_elementUnderMouse = nullptr;
424 m_lastElementUnderMouse = nullptr;
425 m_lastMouseMoveEventSubframe = nullptr;
426 m_lastScrollbarUnderMouse = nullptr;
427 m_clickCount = 0;
428 m_clickNode = nullptr;
429#if ENABLE(IOS_GESTURE_EVENTS)
430 m_gestureInitialDiameter = GestureUnknown;
431 m_gestureInitialRotation = GestureUnknown;
432#endif
433#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS)
434 m_gestureLastDiameter = GestureUnknown;
435 m_gestureLastRotation = GestureUnknown;
436 m_gestureTargets.clear();
437#endif
438#if ENABLE(IOS_TOUCH_EVENTS)
439 m_touches.clear();
440 m_firstTouchID = InvalidTouchIdentifier;
441 m_touchEventTargetSubframe = nullptr;
442#endif
443 m_frameSetBeingResized = nullptr;
444#if ENABLE(DRAG_SUPPORT)
445 m_dragTarget = nullptr;
446 m_shouldOnlyFireDragOverEvent = false;
447#endif
448 m_mousePositionIsUnknown = true;
449 m_lastKnownMousePosition = IntPoint();
450 m_lastKnownMouseGlobalPosition = IntPoint();
451 m_mousePressNode = nullptr;
452 m_mousePressed = false;
453 m_capturesDragging = false;
454 m_capturingMouseEventsElement = nullptr;
455 clearLatchedState();
456#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
457 m_originatingTouchPointTargets.clear();
458 m_originatingTouchPointDocument = nullptr;
459 m_originatingTouchPointTargetKey = 0;
460#endif
461 m_maxMouseMovedDuration = 0;
462 m_didStartDrag = false;
463}
464
465void EventHandler::nodeWillBeRemoved(Node& nodeToBeRemoved)
466{
467 if (nodeToBeRemoved.contains(m_clickNode.get()))
468 m_clickNode = nullptr;
469}
470
471static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection)
472{
473 if (selection.selection() != newSelection && selection.shouldChangeSelection(newSelection))
474 selection.setSelection(newSelection);
475}
476
477static inline bool dispatchSelectStart(Node* node)
478{
479 if (!node || !node->renderer())
480 return true;
481
482 auto event = Event::create(eventNames().selectstartEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes);
483 node->dispatchEvent(event);
484 return !event->defaultPrevented();
485}
486
487static Node* nodeToSelectOnMouseDownForNode(Node& targetNode)
488{
489#if ENABLE(USERSELECT_ALL)
490 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(&targetNode))
491 return rootUserSelectAll;
492#endif
493
494 if (targetNode.shouldSelectOnMouseDown())
495 return &targetNode;
496
497 return nullptr;
498}
499
500static VisibleSelection expandSelectionToRespectSelectOnMouseDown(Node& targetNode, const VisibleSelection& selection)
501{
502 Node* nodeToSelect = nodeToSelectOnMouseDownForNode(targetNode);
503 if (!nodeToSelect)
504 return selection;
505
506 VisibleSelection newSelection(selection);
507 newSelection.setBase(positionBeforeNode(nodeToSelect).upstream(CanCrossEditingBoundary));
508 newSelection.setExtent(positionAfterNode(nodeToSelect).downstream(CanCrossEditingBoundary));
509
510 return newSelection;
511}
512
513bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity)
514{
515 if (Position::nodeIsUserSelectNone(targetNode))
516 return false;
517
518 if (!dispatchSelectStart(targetNode)) {
519 m_mouseDownMayStartSelect = false;
520 return false;
521 }
522
523 if (selection.isRange())
524 m_selectionInitiationState = ExtendedSelection;
525 else {
526 granularity = CharacterGranularity;
527 m_selectionInitiationState = PlacedCaret;
528 }
529
530 m_frame.selection().setSelectionByMouseIfDifferent(selection, granularity);
531
532 return true;
533}
534
535void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
536{
537 Node* targetNode = result.targetNode();
538 VisibleSelection newSelection;
539
540 if (targetNode && targetNode->renderer()) {
541 VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr));
542 if (pos.isNotNull()) {
543 newSelection = VisibleSelection(pos);
544 newSelection.expandUsingGranularity(WordGranularity);
545 }
546
547 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
548 newSelection.appendTrailingWhitespace();
549
550 updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity);
551 }
552}
553
554static AppendTrailingWhitespace shouldAppendTrailingWhitespace(const MouseEventWithHitTestResults& result, const Frame& frame)
555{
556 return (result.event().clickCount() == 2 && frame.editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace;
557}
558
559void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result)
560{
561 if (m_mouseDownMayStartSelect)
562 selectClosestWordFromHitTestResult(result.hitTestResult(), shouldAppendTrailingWhitespace(result, m_frame));
563}
564
565#if !PLATFORM(MAC)
566VisibleSelection EventHandler::selectClosestWordFromHitTestResultBasedOnLookup(const HitTestResult&)
567{
568 return VisibleSelection();
569}
570#endif
571
572void EventHandler::selectClosestContextualWordFromMouseEvent(const MouseEventWithHitTestResults& mouseEvent)
573{
574 Node* targetNode = mouseEvent.targetNode();
575 const HitTestResult& result = mouseEvent.hitTestResult();
576 VisibleSelection newSelection;
577 bool appendTrailingWhitespace = shouldAppendTrailingWhitespace(mouseEvent, m_frame);
578
579 if (targetNode && targetNode->renderer()) {
580 newSelection = selectClosestWordFromHitTestResultBasedOnLookup(result);
581 if (newSelection.isNone()) {
582 VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr));
583 if (pos.isNotNull()) {
584 newSelection = VisibleSelection(pos);
585 newSelection.expandUsingGranularity(WordGranularity);
586 }
587 }
588
589 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
590 newSelection.appendTrailingWhitespace();
591
592 updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity);
593 }
594}
595
596void EventHandler::selectClosestContextualWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result)
597{
598 Element* urlElement = result.hitTestResult().URLElement();
599 if (!urlElement || !isDraggableLink(*urlElement)) {
600 if (Node* targetNode = result.targetNode()) {
601 if (isEditableNode(*targetNode))
602 return selectClosestWordFromMouseEvent(result);
603 }
604
605 return selectClosestContextualWordFromMouseEvent(result);
606 }
607
608 Node* targetNode = result.targetNode();
609
610 if (targetNode && targetNode->renderer() && m_mouseDownMayStartSelect) {
611 VisibleSelection newSelection;
612 VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr));
613 if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(*urlElement))
614 newSelection = VisibleSelection::selectionFromContentsOfNode(urlElement);
615
616 updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity);
617 }
618}
619
620bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestResults& event)
621{
622 if (event.event().button() != LeftButton)
623 return false;
624
625 if (m_frame.selection().isRange())
626 // A double-click when range is already selected
627 // should not change the selection. So, do not call
628 // selectClosestWordFromMouseEvent, but do set
629 // m_beganSelectingText to prevent handleMouseReleaseEvent
630 // from setting caret selection.
631 m_selectionInitiationState = ExtendedSelection;
632 else
633 selectClosestWordFromMouseEvent(event);
634
635 return true;
636}
637
638bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestResults& event)
639{
640 if (event.event().button() != LeftButton)
641 return false;
642
643 Node* targetNode = event.targetNode();
644 if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect))
645 return false;
646
647 VisibleSelection newSelection;
648 VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr));
649 if (pos.isNotNull()) {
650 newSelection = VisibleSelection(pos);
651 newSelection.expandUsingGranularity(ParagraphGranularity);
652 }
653
654 return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), ParagraphGranularity);
655}
656
657static int textDistance(const Position& start, const Position& end)
658{
659 auto range = Range::create(start.anchorNode()->document(), start, end);
660 return TextIterator::rangeLength(range.ptr(), true);
661}
662
663bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event)
664{
665 Ref<Frame> protectedFrame(m_frame);
666
667 m_frame.document()->updateLayoutIgnorePendingStylesheets();
668 Node* targetNode = event.targetNode();
669 if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect))
670 return false;
671
672 // Extend the selection if the Shift key is down, unless the click is in a link.
673 bool extendSelection = event.event().shiftKey() && !event.isOverLink();
674
675 // Don't restart the selection when the mouse is pressed on an
676 // existing selection so we can allow for text dragging.
677 if (FrameView* view = m_frame.view()) {
678 LayoutPoint vPoint = view->windowToContents(event.event().position());
679 if (!extendSelection && m_frame.selection().contains(vPoint)) {
680 m_mouseDownWasSingleClickInSelection = true;
681 return false;
682 }
683 }
684
685 VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr));
686 if (visiblePos.isNull())
687 visiblePos = VisiblePosition(firstPositionInOrBeforeNode(targetNode), DOWNSTREAM);
688 Position pos = visiblePos.deepEquivalent();
689
690 VisibleSelection newSelection = m_frame.selection().selection();
691 TextGranularity granularity = CharacterGranularity;
692
693#if PLATFORM(IOS_FAMILY)
694 // The text selection assistant will handle selection in the case where we are already editing the node
695 if (newSelection.rootEditableElement() == targetNode->rootEditableElement())
696 return true;
697#endif
698
699 if (extendSelection && newSelection.isCaretOrRange()) {
700 VisibleSelection selectionInUserSelectAll = expandSelectionToRespectSelectOnMouseDown(*targetNode, VisibleSelection(pos));
701 if (selectionInUserSelectAll.isRange()) {
702 if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0)
703 pos = selectionInUserSelectAll.start();
704 else if (comparePositions(newSelection.end(), selectionInUserSelectAll.end()) < 0)
705 pos = selectionInUserSelectAll.end();
706 }
707
708 if (!m_frame.editor().behavior().shouldConsiderSelectionAsDirectional() && pos.isNotNull()) {
709 // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection
710 // was created right-to-left
711 Position start = newSelection.start();
712 Position end = newSelection.end();
713 int distanceToStart = textDistance(start, pos);
714 int distanceToEnd = textDistance(pos, end);
715 if (distanceToStart <= distanceToEnd)
716 newSelection = VisibleSelection(end, pos);
717 else
718 newSelection = VisibleSelection(start, pos);
719 } else
720 newSelection.setExtent(pos);
721
722 if (m_frame.selection().granularity() != CharacterGranularity) {
723 granularity = m_frame.selection().granularity();
724 newSelection.expandUsingGranularity(m_frame.selection().granularity());
725 }
726 } else
727 newSelection = expandSelectionToRespectSelectOnMouseDown(*targetNode, visiblePos);
728
729 bool handled = updateSelectionForMouseDownDispatchingSelectStart(targetNode, newSelection, granularity);
730
731 if (event.event().button() == MiddleButton) {
732 // Ignore handled, since we want to paste to where the caret was placed anyway.
733 handled = handlePasteGlobalSelection(event.event()) || handled;
734 }
735 return handled;
736}
737
738static inline bool canMouseDownStartSelect(Node* node)
739{
740 if (!node || !node->renderer())
741 return true;
742
743 return node->canStartSelection() || Position::nodeIsUserSelectAll(node);
744}
745
746bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event)
747{
748 Ref<Frame> protectedFrame(m_frame);
749
750#if ENABLE(DRAG_SUPPORT)
751 // Reset drag state.
752 dragState().source = nullptr;
753#endif
754
755#if !ENABLE(IOS_TOUCH_EVENTS)
756 cancelFakeMouseMoveEvent();
757#endif
758
759 m_frame.document()->updateLayoutIgnorePendingStylesheets();
760
761 if (ScrollView* scrollView = m_frame.view()) {
762 if (scrollView->isPointInScrollbarCorner(event.event().position()))
763 return false;
764 }
765
766 bool singleClick = event.event().clickCount() <= 1;
767
768 // If we got the event back, that must mean it wasn't prevented,
769 // so it's allowed to start a drag or selection if it wasn't in a scrollbar.
770 m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode()) && !event.scrollbar();
771
772#if ENABLE(DRAG_SUPPORT)
773 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
774 // FIXME: eventMayStartDrag() does not check for shift key press, link or image event targets.
775 // Bug: https://bugs.webkit.org/show_bug.cgi?id=155390
776
777 // Single mouse down on links or images can always trigger drag-n-drop.
778 bool isMouseDownOnLinkOrImage = event.isOverLink() || event.hitTestResult().image();
779 m_mouseDownMayStartDrag = singleClick && (!event.event().shiftKey() || isMouseDownOnLinkOrImage) && shouldAllowMouseDownToStartDrag();
780#endif
781
782 m_mouseDownWasSingleClickInSelection = false;
783
784 m_mouseDown = event.event();
785
786 if (m_immediateActionStage != ImmediateActionStage::PerformedHitTest)
787 m_immediateActionStage = ImmediateActionStage::None;
788
789 if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event))
790 return true;
791
792 if (is<SVGDocument>(*m_frame.document()) && downcast<SVGDocument>(*m_frame.document()).zoomAndPanEnabled()) {
793 if (event.event().shiftKey() && singleClick) {
794 m_svgPan = true;
795 downcast<SVGDocument>(*m_frame.document()).startPan(m_frame.view()->windowToContents(event.event().position()));
796 return true;
797 }
798 }
799
800 // We don't do this at the start of mouse down handling,
801 // because we don't want to do it until we know we didn't hit a widget.
802 if (singleClick)
803 focusDocumentView();
804
805 m_mousePressNode = event.targetNode();
806 m_frame.document()->setFocusNavigationStartingNode(event.targetNode());
807
808#if ENABLE(DRAG_SUPPORT)
809 m_dragStartPosition = event.event().position();
810#endif
811
812 m_mousePressed = true;
813 m_selectionInitiationState = HaveNotStartedSelection;
814
815 bool swallowEvent = false;
816 if (event.event().clickCount() == 2)
817 swallowEvent = handleMousePressEventDoubleClick(event);
818 else if (event.event().clickCount() >= 3)
819 swallowEvent = handleMousePressEventTripleClick(event);
820 else
821 swallowEvent = handleMousePressEventSingleClick(event);
822
823 m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect
824 || (m_mousePressNode && m_mousePressNode->renderBox() && m_mousePressNode->renderBox()->canBeProgramaticallyScrolled());
825
826 return swallowEvent;
827}
828
829VisiblePosition EventHandler::selectionExtentRespectingEditingBoundary(const VisibleSelection& selection, const LayoutPoint& localPoint, Node* targetNode)
830{
831 FloatPoint selectionEndPoint = localPoint;
832 Element* editableElement = selection.rootEditableElement();
833
834 if (!targetNode || !targetNode->renderer())
835 return VisiblePosition();
836
837 if (editableElement && !editableElement->contains(targetNode)) {
838 if (!editableElement->renderer())
839 return VisiblePosition();
840
841 FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint));
842 selectionEndPoint = editableElement->renderer()->absoluteToLocal(absolutePoint);
843 targetNode = editableElement;
844 }
845
846 return targetNode->renderer()->positionForPoint(LayoutPoint(selectionEndPoint), nullptr);
847}
848
849#if ENABLE(DRAG_SUPPORT)
850
851#if !PLATFORM(IOS_FAMILY)
852
853bool EventHandler::supportsSelectionUpdatesOnMouseDrag() const
854{
855 return true;
856}
857
858bool EventHandler::shouldAllowMouseDownToStartDrag() const
859{
860 return true;
861}
862
863#endif
864
865bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis)
866{
867 if (!m_mousePressed)
868 return false;
869
870 Ref<Frame> protectedFrame(m_frame);
871
872 if (handleDrag(event, checkDragHysteresis))
873 return true;
874
875 Node* targetNode = event.targetNode();
876 if (event.event().button() != LeftButton || !targetNode)
877 return false;
878
879 RenderObject* renderer = targetNode->renderer();
880 if (!renderer) {
881 Element* parent = targetNode->parentOrShadowHostElement();
882 if (!parent)
883 return false;
884
885 renderer = parent->renderer();
886 if (!renderer || !renderer->isListBox())
887 return false;
888 }
889
890#if PLATFORM(COCOA) // FIXME: Why does this assertion fire on other platforms?
891 ASSERT(m_mouseDownMayStartSelect || m_mouseDownMayStartAutoscroll);
892#endif
893
894 m_mouseDownMayStartDrag = false;
895
896 if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) {
897 m_autoscrollController->startAutoscrollForSelection(renderer);
898 m_mouseDownMayStartAutoscroll = false;
899 }
900
901 if (m_selectionInitiationState != ExtendedSelection) {
902 HitTestResult result(m_mouseDownPos);
903 m_frame.document()->hitTest(HitTestRequest(), result);
904
905 updateSelectionForMouseDrag(result);
906 }
907 updateSelectionForMouseDrag(event.hitTestResult());
908 return true;
909}
910
911bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const
912{
913 // This is a pre-flight check of whether the event might lead to a drag being started. Be careful
914 // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag
915 // in handleMousePressEvent
916 auto* document = m_frame.document();
917 if (!document)
918 return false;
919
920 if (event.button() != LeftButton || event.clickCount() != 1)
921 return false;
922
923 FrameView* view = m_frame.view();
924 if (!view)
925 return false;
926
927 Page* page = m_frame.page();
928 if (!page)
929 return false;
930
931 Ref<Frame> protectedFrame(m_frame);
932
933 updateDragSourceActionsAllowed();
934 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
935 HitTestResult result(view->windowToContents(event.position()));
936 document->hitTest(request, result);
937 DragState state;
938 Element* targetElement = result.targetElement();
939 return targetElement && page->dragController().draggableElement(&m_frame, targetElement, result.roundedPointInInnerNodeFrame(), state);
940}
941
942void EventHandler::updateSelectionForMouseDrag()
943{
944 if (!supportsSelectionUpdatesOnMouseDrag())
945 return;
946
947 FrameView* view = m_frame.view();
948 if (!view)
949 return;
950 auto* document = m_frame.document();
951 if (!document)
952 return;
953
954 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent);
955 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
956 document->hitTest(request, result);
957 updateSelectionForMouseDrag(result);
958}
959
960void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult)
961{
962 if (!supportsSelectionUpdatesOnMouseDrag())
963 return;
964
965 if (!m_mouseDownMayStartSelect)
966 return;
967
968 Node* target = hitTestResult.targetNode();
969 if (!target)
970 return;
971
972 VisiblePosition targetPosition = selectionExtentRespectingEditingBoundary(m_frame.selection().selection(), hitTestResult.localPoint(), target);
973
974 // Don't modify the selection if we're not on a node.
975 if (targetPosition.isNull())
976 return;
977
978 // Restart the selection if this is the first mouse move. This work is usually
979 // done in handleMousePressEvent, but not if the mouse press was on an existing selection.
980 VisibleSelection newSelection = m_frame.selection().selection();
981
982 // Special case to limit selection to the containing block for SVG text.
983 // FIXME: Isn't there a better non-SVG-specific way to do this?
984 if (Node* selectionBaseNode = newSelection.base().deprecatedNode()) {
985 if (RenderObject* selectionBaseRenderer = selectionBaseNode->renderer()) {
986 if (selectionBaseRenderer->isSVGText()) {
987 if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock())
988 return;
989 }
990 }
991 }
992
993
994 if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelectStart(target)) {
995 m_mouseDownMayStartSelect = false;
996 return;
997 }
998
999 if (m_selectionInitiationState != ExtendedSelection) {
1000 // Always extend selection here because it's caused by a mouse drag
1001 m_selectionInitiationState = ExtendedSelection;
1002 newSelection = VisibleSelection(targetPosition);
1003 }
1004
1005#if ENABLE(USERSELECT_ALL)
1006 Node* rootUserSelectAllForMousePressNode = Position::rootUserSelectAllForNode(m_mousePressNode.get());
1007 if (rootUserSelectAllForMousePressNode && rootUserSelectAllForMousePressNode == Position::rootUserSelectAllForNode(target)) {
1008 newSelection.setBase(positionBeforeNode(rootUserSelectAllForMousePressNode).upstream(CanCrossEditingBoundary));
1009 newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
1010 } else {
1011 // Reset base for user select all when base is inside user-select-all area and extent < base.
1012 if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0)
1013 newSelection.setBase(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
1014
1015 Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNode(target);
1016 if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0)
1017 newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTarget).upstream(CanCrossEditingBoundary));
1018 else if (rootUserSelectAllForTarget && m_mousePressNode->renderer())
1019 newSelection.setExtent(positionAfterNode(rootUserSelectAllForTarget).downstream(CanCrossEditingBoundary));
1020 else
1021 newSelection.setExtent(targetPosition);
1022 }
1023#else
1024 newSelection.setExtent(targetPosition);
1025#endif
1026
1027 if (m_frame.selection().granularity() != CharacterGranularity)
1028 newSelection.expandUsingGranularity(m_frame.selection().granularity());
1029
1030 m_frame.selection().setSelectionByMouseIfDifferent(newSelection, m_frame.selection().granularity(),
1031 FrameSelection::AdjustEndpointsAtBidiBoundary);
1032}
1033#endif // ENABLE(DRAG_SUPPORT)
1034
1035void EventHandler::lostMouseCapture()
1036{
1037 m_frame.selection().setCaretBlinkingSuspended(false);
1038}
1039
1040bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event)
1041{
1042 if (eventLoopHandleMouseUp(event))
1043 return true;
1044
1045 // If this was the first click in the window, we don't even want to clear the selection.
1046 // This case occurs when the user clicks on a draggable element, since we have to process
1047 // the mouse down and drag events to see if we might start a drag. For other first clicks
1048 // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
1049 // ignored upstream of this layer.
1050 return eventActivatedView(event.event());
1051}
1052
1053bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
1054{
1055 if (autoscrollInProgress())
1056 stopAutoscrollTimer();
1057
1058 Ref<Frame> protectedFrame(m_frame);
1059
1060 if (handleMouseUp(event))
1061 return true;
1062
1063 // Used to prevent mouseMoveEvent from initiating a drag before
1064 // the mouse is pressed again.
1065 m_mousePressed = false;
1066 m_capturesDragging = false;
1067#if ENABLE(DRAG_SUPPORT)
1068 m_mouseDownMayStartDrag = false;
1069#endif
1070 m_mouseDownMayStartSelect = false;
1071 m_mouseDownMayStartAutoscroll = false;
1072 m_mouseDownWasInSubframe = false;
1073
1074 bool handled = false;
1075
1076 // Clear the selection if the mouse didn't move after the last mouse
1077 // press and it's not a context menu click. We do this so when clicking
1078 // on the selection, the selection goes away. However, if we are
1079 // editing, place the caret.
1080 if (m_mouseDownWasSingleClickInSelection && m_selectionInitiationState != ExtendedSelection
1081#if ENABLE(DRAG_SUPPORT)
1082 && m_dragStartPosition == event.event().position()
1083#endif
1084 && m_frame.selection().isRange()
1085 && event.event().button() != RightButton) {
1086 VisibleSelection newSelection;
1087 Node* node = event.targetNode();
1088 bool caretBrowsing = m_frame.settings().caretBrowsingEnabled();
1089 bool allowSelectionChanges = true;
1090 if (node && node->renderer() && (caretBrowsing || node->hasEditableStyle())) {
1091 VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint(), nullptr);
1092 newSelection = VisibleSelection(pos);
1093#if PLATFORM(IOS_FAMILY)
1094 // On iOS, selection changes are triggered using platform-specific text interaction gestures rather than
1095 // default behavior on click or mouseup. As such, the only time we should allow click events to change the
1096 // selection on iOS is when we focus a different editable element, in which case the text interaction
1097 // gestures will fail.
1098 allowSelectionChanges = m_frame.selection().selection().rootEditableElement() != newSelection.rootEditableElement();
1099#endif
1100 }
1101
1102 if (allowSelectionChanges)
1103 setSelectionIfNeeded(m_frame.selection(), newSelection);
1104
1105 handled = true;
1106 }
1107
1108 if (event.event().button() == MiddleButton) {
1109 // Ignore handled, since we want to paste to where the caret was placed anyway.
1110 handled = handlePasteGlobalSelection(event.event()) || handled;
1111 }
1112
1113 return handled;
1114}
1115
1116#if ENABLE(PAN_SCROLLING)
1117
1118void EventHandler::didPanScrollStart()
1119{
1120 m_autoscrollController->didPanScrollStart();
1121}
1122
1123void EventHandler::didPanScrollStop()
1124{
1125 m_autoscrollController->didPanScrollStop();
1126}
1127
1128void EventHandler::startPanScrolling(RenderElement& renderer)
1129{
1130#if !PLATFORM(IOS_FAMILY)
1131 if (!is<RenderBox>(renderer))
1132 return;
1133 m_autoscrollController->startPanScrolling(&downcast<RenderBox>(renderer), lastKnownMousePosition());
1134 invalidateClick();
1135#endif
1136}
1137
1138#endif // ENABLE(PAN_SCROLLING)
1139
1140RenderBox* EventHandler::autoscrollRenderer() const
1141{
1142 return m_autoscrollController->autoscrollRenderer();
1143}
1144
1145void EventHandler::updateAutoscrollRenderer()
1146{
1147 m_autoscrollController->updateAutoscrollRenderer();
1148}
1149
1150bool EventHandler::autoscrollInProgress() const
1151{
1152 return m_autoscrollController->autoscrollInProgress();
1153}
1154
1155bool EventHandler::panScrollInProgress() const
1156{
1157 return m_autoscrollController->panScrollInProgress();
1158}
1159
1160#if ENABLE(DRAG_SUPPORT)
1161DragSourceAction EventHandler::updateDragSourceActionsAllowed() const
1162{
1163 Page* page = m_frame.page();
1164 if (!page)
1165 return DragSourceActionNone;
1166
1167 FrameView* view = m_frame.view();
1168 if (!view)
1169 return DragSourceActionNone;
1170
1171 return page->dragController().delegateDragSourceAction(view->contentsToRootView(m_mouseDownPos));
1172}
1173#endif // ENABLE(DRAG_SUPPORT)
1174
1175HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) const
1176{
1177 ASSERT((hitType & HitTestRequest::CollectMultipleElements) || padding.isEmpty());
1178
1179 Ref<Frame> protectedFrame(m_frame);
1180
1181 // We always send hitTestResultAtPoint to the main frame if we have one,
1182 // otherwise we might hit areas that are obscured by higher frames.
1183 if (!m_frame.isMainFrame()) {
1184 Frame& mainFrame = m_frame.mainFrame();
1185 FrameView* frameView = m_frame.view();
1186 FrameView* mainView = mainFrame.view();
1187 if (frameView && mainView) {
1188 IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point)));
1189 return mainFrame.eventHandler().hitTestResultAtPoint(mainFramePoint, hitType, padding);
1190 }
1191 }
1192
1193 unsigned nonNegativePaddingWidth = std::max<LayoutUnit>(0, padding.width()).toUnsigned();
1194 unsigned nonNegativePaddingHeight = std::max<LayoutUnit>(0, padding.height()).toUnsigned();
1195
1196 // We should always start hit testing a clean tree.
1197 if (auto* frameView = m_frame.view())
1198 frameView->updateLayoutAndStyleIfNeededRecursive();
1199
1200 HitTestResult result(point, nonNegativePaddingHeight, nonNegativePaddingWidth, nonNegativePaddingHeight, nonNegativePaddingWidth);
1201 auto* document = m_frame.document();
1202 if (!document)
1203 return result;
1204
1205 // hitTestResultAtPoint is specifically used to hitTest into all frames, thus it always allows child frame content.
1206 HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent);
1207 document->hitTest(request, result);
1208 if (!request.readOnly())
1209 m_frame.document()->updateHoverActiveState(request, result.targetElement());
1210
1211 if (request.disallowsUserAgentShadowContent())
1212 result.setToNonUserAgentShadowAncestor();
1213
1214 return result;
1215}
1216
1217void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed)
1218{
1219 m_autoscrollController->stopAutoscrollTimer(rendererIsBeingDestroyed);
1220}
1221
1222bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
1223{
1224 Node* node = startingNode;
1225
1226 if (!node)
1227 node = m_frame.document()->focusedElement();
1228
1229 if (!node)
1230 node = m_mousePressNode.get();
1231
1232 if (node) {
1233 auto r = node->renderer();
1234 if (r && !r->isListBox() && r->enclosingBox().scroll(direction, granularity)) {
1235 setFrameWasScrolledByUser();
1236 return true;
1237 }
1238 }
1239
1240 return false;
1241}
1242
1243bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode)
1244{
1245 Node* node = startingNode;
1246
1247 if (!node)
1248 node = m_frame.document()->focusedElement();
1249
1250 if (!node)
1251 node = m_mousePressNode.get();
1252
1253 if (node) {
1254 auto r = node->renderer();
1255 if (r && !r->isListBox() && r->enclosingBox().logicalScroll(direction, granularity)) {
1256 setFrameWasScrolledByUser();
1257 return true;
1258 }
1259 }
1260
1261 return false;
1262}
1263
1264bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
1265{
1266 Ref<Frame> protectedFrame(m_frame);
1267
1268 // The layout needs to be up to date to determine if we can scroll. We may be
1269 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
1270 m_frame.document()->updateLayoutIgnorePendingStylesheets();
1271 if (scrollOverflow(direction, granularity, startingNode))
1272 return true;
1273 Frame* frame = &m_frame;
1274 FrameView* view = frame->view();
1275 if (view && view->scroll(direction, granularity))
1276 return true;
1277 frame = frame->tree().parent();
1278 if (!frame)
1279 return false;
1280 return frame->eventHandler().scrollRecursively(direction, granularity, m_frame.ownerElement());
1281}
1282
1283bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode)
1284{
1285 Ref<Frame> protectedFrame(m_frame);
1286
1287 // The layout needs to be up to date to determine if we can scroll. We may be
1288 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
1289 m_frame.document()->updateLayoutIgnorePendingStylesheets();
1290 if (logicalScrollOverflow(direction, granularity, startingNode))
1291 return true;
1292 Frame* frame = &m_frame;
1293 FrameView* view = frame->view();
1294
1295 bool scrolled = false;
1296#if PLATFORM(COCOA)
1297 // Mac also resets the scroll position in the inline direction.
1298 if (granularity == ScrollByDocument && view && view->logicalScroll(ScrollInlineDirectionBackward, ScrollByDocument))
1299 scrolled = true;
1300#endif
1301 if (view && view->logicalScroll(direction, granularity))
1302 scrolled = true;
1303
1304 if (scrolled)
1305 return true;
1306
1307 frame = frame->tree().parent();
1308 if (!frame)
1309 return false;
1310
1311 return frame->eventHandler().logicalScrollRecursively(direction, granularity, m_frame.ownerElement());
1312}
1313
1314IntPoint EventHandler::lastKnownMousePosition() const
1315{
1316 return m_lastKnownMousePosition;
1317}
1318
1319Frame* EventHandler::subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult)
1320{
1321 if (!hitTestResult.isOverWidget())
1322 return 0;
1323 return subframeForTargetNode(hitTestResult.targetNode());
1324}
1325
1326Frame* EventHandler::subframeForTargetNode(Node* node)
1327{
1328 if (!node)
1329 return nullptr;
1330
1331 auto renderer = node->renderer();
1332 if (!is<RenderWidget>(renderer))
1333 return nullptr;
1334
1335 Widget* widget = downcast<RenderWidget>(*renderer).widget();
1336 if (!is<FrameView>(widget))
1337 return nullptr;
1338
1339 return &downcast<FrameView>(*widget).frame();
1340}
1341
1342#if ENABLE(CURSOR_SUPPORT)
1343static bool isSubmitImage(Node* node)
1344{
1345 return is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*node).isImageButton();
1346}
1347
1348// Returns true if the node's editable block is not current focused for editing
1349static bool nodeIsNotBeingEdited(const Node& node, const Frame& frame)
1350{
1351 return frame.selection().selection().rootEditableElement() != node.rootEditableElement();
1352}
1353
1354bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey)
1355{
1356 if (!node)
1357 return false;
1358
1359 bool editable = node->hasEditableStyle();
1360 bool editableLinkEnabled = false;
1361
1362 // If the link is editable, then we need to check the settings to see whether or not the link should be followed
1363 if (editable) {
1364 switch (m_frame.settings().editableLinkBehavior()) {
1365 default:
1366 case EditableLinkDefaultBehavior:
1367 case EditableLinkAlwaysLive:
1368 editableLinkEnabled = true;
1369 break;
1370
1371 case EditableLinkNeverLive:
1372 editableLinkEnabled = false;
1373 break;
1374
1375 case EditableLinkLiveWhenNotFocused:
1376 editableLinkEnabled = nodeIsNotBeingEdited(*node, m_frame) || shiftKey;
1377 break;
1378
1379 case EditableLinkOnlyLiveWithShiftKey:
1380 editableLinkEnabled = shiftKey;
1381 break;
1382 }
1383 }
1384
1385 return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled));
1386}
1387
1388void EventHandler::cursorUpdateTimerFired()
1389{
1390 ASSERT(m_frame.document());
1391 updateCursor();
1392}
1393
1394void EventHandler::updateCursor()
1395{
1396 if (m_mousePositionIsUnknown)
1397 return;
1398
1399 FrameView* view = m_frame.view();
1400 if (!view)
1401 return;
1402
1403 auto* document = m_frame.document();
1404 if (!document)
1405 return;
1406
1407 if (!view->shouldSetCursor())
1408 return;
1409
1410 bool shiftKey;
1411 bool ctrlKey;
1412 bool altKey;
1413 bool metaKey;
1414 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
1415
1416 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::AllowFrameScrollbars);
1417 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
1418 document->hitTest(request, result);
1419
1420 updateCursor(*view, result, shiftKey);
1421}
1422
1423void EventHandler::updateCursor(FrameView& view, const HitTestResult& result, bool shiftKey)
1424{
1425 if (auto optionalCursor = selectCursor(result, shiftKey)) {
1426 m_currentMouseCursor = WTFMove(optionalCursor.value());
1427 view.setCursor(m_currentMouseCursor);
1428 }
1429}
1430
1431Optional<Cursor> EventHandler::selectCursor(const HitTestResult& result, bool shiftKey)
1432{
1433 if (m_resizeLayer && m_resizeLayer->inResizeMode())
1434 return WTF::nullopt;
1435
1436 if (!m_frame.page())
1437 return WTF::nullopt;
1438
1439#if ENABLE(PAN_SCROLLING)
1440 if (m_frame.mainFrame().eventHandler().panScrollInProgress())
1441 return WTF::nullopt;
1442#endif
1443
1444 Ref<Frame> protectedFrame(m_frame);
1445
1446 // Use always pointer cursor for scrollbars.
1447 if (result.scrollbar()) {
1448#if ENABLE(CURSOR_VISIBILITY)
1449 cancelAutoHideCursorTimer();
1450#endif
1451 return pointerCursor();
1452 }
1453
1454 Node* node = result.targetNode();
1455 if (!node)
1456 return WTF::nullopt;
1457
1458 auto renderer = node->renderer();
1459 auto* style = renderer ? &renderer->style() : nullptr;
1460 bool horizontalText = !style || style->isHorizontalWritingMode();
1461 const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCursor();
1462
1463#if ENABLE(CURSOR_VISIBILITY)
1464 if (style && style->cursorVisibility() == CursorVisibility::AutoHide)
1465 startAutoHideCursorTimer();
1466 else
1467 cancelAutoHideCursorTimer();
1468#endif
1469
1470 if (renderer) {
1471 Cursor overrideCursor;
1472 switch (renderer->getCursor(roundedIntPoint(result.localPoint()), overrideCursor)) {
1473 case SetCursorBasedOnStyle:
1474 break;
1475 case SetCursor:
1476 return overrideCursor;
1477 case DoNotSetCursor:
1478 return WTF::nullopt;
1479 }
1480 }
1481
1482 if (style && style->cursors()) {
1483 const CursorList* cursors = style->cursors();
1484 for (unsigned i = 0; i < cursors->size(); ++i) {
1485 StyleImage* styleImage = (*cursors)[i].image();
1486 if (!styleImage)
1487 continue;
1488 CachedImage* cachedImage = styleImage->cachedImage();
1489 if (!cachedImage)
1490 continue;
1491 float scale = styleImage->imageScaleFactor();
1492 // Get hotspot and convert from logical pixels to physical pixels.
1493 IntPoint hotSpot = (*cursors)[i].hotSpot();
1494 FloatSize size = cachedImage->imageForRenderer(renderer)->size();
1495 if (cachedImage->errorOccurred())
1496 continue;
1497 // Limit the size of cursors (in UI pixels) so that they cannot be
1498 // used to cover UI elements in chrome.
1499 size.scale(1 / scale);
1500 if (size.width() > maximumCursorSize || size.height() > maximumCursorSize)
1501 continue;
1502
1503 Image* image = cachedImage->imageForRenderer(renderer);
1504#if ENABLE(MOUSE_CURSOR_SCALE)
1505 // Ensure no overflow possible in calculations above.
1506 if (scale < minimumCursorScale)
1507 continue;
1508 return Cursor(image, hotSpot, scale);
1509#else
1510 ASSERT(scale == 1);
1511 return Cursor(image, hotSpot);
1512#endif // ENABLE(MOUSE_CURSOR_SCALE)
1513 }
1514 }
1515
1516 // During selection, use an I-beam regardless of the content beneath the cursor.
1517 // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection.
1518 if (m_mousePressed
1519 && m_mouseDownMayStartSelect
1520#if ENABLE(DRAG_SUPPORT)
1521 && !m_mouseDownMayStartDrag
1522#endif
1523 && m_frame.selection().isCaretOrRange()
1524 && !m_capturingMouseEventsElement)
1525 return iBeam;
1526
1527 switch (style ? style->cursor() : CursorType::Auto) {
1528 case CursorType::Auto: {
1529 bool editable = node->hasEditableStyle();
1530
1531 if (useHandCursor(node, result.isOverLink(), shiftKey))
1532 return handCursor();
1533
1534 bool inResizer = false;
1535 if (renderer) {
1536 if (RenderLayer* layer = renderer->enclosingLayer()) {
1537 if (FrameView* view = m_frame.view())
1538 inResizer = layer->isPointInResizeControl(view->windowToContents(roundedIntPoint(result.localPoint())));
1539 }
1540 }
1541
1542 if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar())
1543 return iBeam;
1544 return pointerCursor();
1545 }
1546 case CursorType::Default:
1547 return pointerCursor();
1548 case CursorType::None:
1549 return noneCursor();
1550 case CursorType::ContextMenu:
1551 return contextMenuCursor();
1552 case CursorType::Help:
1553 return helpCursor();
1554 case CursorType::Pointer:
1555 return handCursor();
1556 case CursorType::Progress:
1557 return progressCursor();
1558 case CursorType::Wait:
1559 return waitCursor();
1560 case CursorType::Cell:
1561 return cellCursor();
1562 case CursorType::Crosshair:
1563 return crossCursor();
1564 case CursorType::Text:
1565 return iBeamCursor();
1566 case CursorType::VerticalText:
1567 return verticalTextCursor();
1568 case CursorType::Alias:
1569 return aliasCursor();
1570 case CursorType::Copy:
1571 return copyCursor();
1572 case CursorType::Move:
1573 return moveCursor();
1574 case CursorType::NoDrop:
1575 return noDropCursor();
1576 case CursorType::NotAllowed:
1577 return notAllowedCursor();
1578 case CursorType::Grab:
1579 return grabCursor();
1580 case CursorType::Grabbing:
1581 return grabbingCursor();
1582 case CursorType::EResize:
1583 return eastResizeCursor();
1584 case CursorType::NResize:
1585 return northResizeCursor();
1586 case CursorType::NEResize:
1587 return northEastResizeCursor();
1588 case CursorType::NWResize:
1589 return northWestResizeCursor();
1590 case CursorType::SResize:
1591 return southResizeCursor();
1592 case CursorType::SEResize:
1593 return southEastResizeCursor();
1594 case CursorType::SWResize:
1595 return southWestResizeCursor();
1596 case CursorType::WResize:
1597 return westResizeCursor();
1598 case CursorType::EWResize:
1599 return eastWestResizeCursor();
1600 case CursorType::NSResize:
1601 return northSouthResizeCursor();
1602 case CursorType::NESWResize:
1603 return northEastSouthWestResizeCursor();
1604 case CursorType::NWSEResize:
1605 return northWestSouthEastResizeCursor();
1606 case CursorType::ColumnResize:
1607 return columnResizeCursor();
1608 case CursorType::RowResize:
1609 return rowResizeCursor();
1610 case CursorType::AllScroll:
1611 return moveCursor();
1612 case CursorType::ZoomIn:
1613 return zoomInCursor();
1614 case CursorType::ZoomOut:
1615 return zoomOutCursor();
1616 }
1617 return pointerCursor();
1618}
1619#endif // ENABLE(CURSOR_SUPPORT)
1620
1621#if ENABLE(CURSOR_VISIBILITY)
1622void EventHandler::startAutoHideCursorTimer()
1623{
1624 Page* page = m_frame.page();
1625 if (!page)
1626 return;
1627
1628 m_autoHideCursorTimer.startOneShot(page->settings().timeWithoutMouseMovementBeforeHidingControls());
1629
1630#if !ENABLE(IOS_TOUCH_EVENTS)
1631 // The fake mouse move event screws up the auto-hide feature (by resetting the auto-hide timer)
1632 // so cancel any pending fake mouse moves.
1633 if (m_fakeMouseMoveEventTimer.isActive())
1634 m_fakeMouseMoveEventTimer.stop();
1635#endif
1636}
1637
1638void EventHandler::cancelAutoHideCursorTimer()
1639{
1640 if (m_autoHideCursorTimer.isActive())
1641 m_autoHideCursorTimer.stop();
1642}
1643
1644void EventHandler::autoHideCursorTimerFired()
1645{
1646 FrameView* view = m_frame.view();
1647 if (!view || !view->isActive())
1648 return;
1649
1650 if (auto page = m_frame.page())
1651 page->chrome().setCursorHiddenUntilMouseMoves(true);
1652}
1653#endif
1654
1655static LayoutPoint documentPointForWindowPoint(Frame& frame, const IntPoint& windowPoint)
1656{
1657 FrameView* view = frame.view();
1658 // FIXME: Is it really OK to use the wrong coordinates here when view is 0?
1659 // Historically the code would just crash; this is clearly no worse than that.
1660 return view ? view->windowToContents(windowPoint) : windowPoint;
1661}
1662
1663static Scrollbar* scrollbarForMouseEvent(const MouseEventWithHitTestResults& mouseEvent, FrameView* view)
1664{
1665 if (view) {
1666 if (auto* scrollbar = view->scrollbarAtPoint(mouseEvent.event().position()))
1667 return scrollbar;
1668 }
1669 return mouseEvent.scrollbar();
1670
1671}
1672
1673bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& platformMouseEvent)
1674{
1675 Ref<Frame> protectedFrame(m_frame);
1676 RefPtr<FrameView> protector(m_frame.view());
1677
1678 if (InspectorInstrumentation::handleMousePress(m_frame)) {
1679 invalidateClick();
1680 return true;
1681 }
1682
1683#if ENABLE(POINTER_LOCK)
1684 if (m_frame.page()->pointerLockController().isLocked()) {
1685 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousedownEvent);
1686 return true;
1687 }
1688#endif
1689
1690 if (m_frame.page()->pageOverlayController().handleMouseEvent(platformMouseEvent))
1691 return true;
1692
1693#if ENABLE(TOUCH_EVENTS)
1694 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
1695 if (defaultPrevented)
1696 return true;
1697#endif
1698
1699 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
1700
1701 // FIXME (bug 68185): this call should be made at another abstraction layer
1702 m_frame.loader().resetMultipleFormSubmissionProtection();
1703
1704#if !ENABLE(IOS_TOUCH_EVENTS)
1705 cancelFakeMouseMoveEvent();
1706#endif
1707 m_mousePressed = true;
1708 m_capturesDragging = true;
1709 setLastKnownMousePosition(platformMouseEvent);
1710 m_mouseDownTimestamp = platformMouseEvent.timestamp();
1711#if ENABLE(DRAG_SUPPORT)
1712 m_mouseDownMayStartDrag = false;
1713#endif
1714 m_mouseDownMayStartSelect = false;
1715 m_mouseDownMayStartAutoscroll = false;
1716 if (FrameView* view = m_frame.view())
1717 m_mouseDownPos = view->windowToContents(platformMouseEvent.position());
1718 else {
1719 invalidateClick();
1720 return false;
1721 }
1722 m_mouseDownWasInSubframe = false;
1723
1724 HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
1725 // Save the document point we generate in case the window coordinate is invalidated by what happens
1726 // when we dispatch the event.
1727 LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, platformMouseEvent.position());
1728 MouseEventWithHitTestResults mouseEvent = m_frame.document()->prepareMouseEvent(request, documentPoint, platformMouseEvent);
1729
1730 if (!mouseEvent.targetNode()) {
1731 invalidateClick();
1732 return false;
1733 }
1734
1735 m_mousePressNode = mouseEvent.targetNode();
1736 m_frame.document()->setFocusNavigationStartingNode(mouseEvent.targetNode());
1737
1738 Scrollbar* scrollbar = scrollbarForMouseEvent(mouseEvent, m_frame.view());
1739 updateLastScrollbarUnderMouse(scrollbar, SetOrClearLastScrollbar::Set);
1740 bool passedToScrollbar = scrollbar && passMousePressEventToScrollbar(mouseEvent, scrollbar);
1741
1742 if (!passedToScrollbar) {
1743 RefPtr<Frame> subframe = subframeForHitTestResult(mouseEvent);
1744 if (subframe && passMousePressEventToSubframe(mouseEvent, subframe.get())) {
1745 // Start capturing future events for this frame. We only do this if we didn't clear
1746 // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop.
1747 m_capturesDragging = subframe->eventHandler().capturesDragging();
1748 if (m_mousePressed && m_capturesDragging) {
1749 m_capturingMouseEventsElement = subframe->ownerElement();
1750 m_eventHandlerWillResetCapturingMouseEventsElement = true;
1751 }
1752 invalidateClick();
1753 return true;
1754 }
1755 }
1756
1757#if ENABLE(PAN_SCROLLING)
1758 // We store whether pan scrolling is in progress before calling stopAutoscrollTimer()
1759 // because it will set m_autoscrollType to NoAutoscroll on return.
1760 bool isPanScrollInProgress = m_frame.mainFrame().eventHandler().panScrollInProgress();
1761 stopAutoscrollTimer();
1762 if (isPanScrollInProgress) {
1763 // We invalidate the click when exiting pan scrolling so that we don't inadvertently navigate
1764 // away from the current page (e.g. the click was on a hyperlink). See <rdar://problem/6095023>.
1765 invalidateClick();
1766 return true;
1767 }
1768#endif
1769
1770 m_clickCount = platformMouseEvent.clickCount();
1771 m_clickNode = mouseEvent.targetNode();
1772
1773 if (!m_clickNode) {
1774 invalidateClick();
1775 return false;
1776 }
1777
1778 if (FrameView* view = m_frame.view()) {
1779 RenderLayer* layer = m_clickNode->renderer() ? m_clickNode->renderer()->enclosingLayer() : 0;
1780 IntPoint p = view->windowToContents(platformMouseEvent.position());
1781 if (layer && layer->isPointInResizeControl(p)) {
1782 layer->setInResizeMode(true);
1783 m_resizeLayer = layer;
1784 m_offsetFromResizeCorner = layer->offsetFromResizeCorner(p);
1785 invalidateClick();
1786 return true;
1787 }
1788 }
1789
1790 m_frame.selection().setCaretBlinkingSuspended(true);
1791
1792 bool swallowEvent = !dispatchMouseEvent(eventNames().mousedownEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true);
1793 m_capturesDragging = !swallowEvent || mouseEvent.scrollbar();
1794
1795 // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults
1796 // in case the scrollbar widget was destroyed when the mouse event was handled.
1797 if (mouseEvent.scrollbar()) {
1798 const bool wasLastScrollBar = mouseEvent.scrollbar() == m_lastScrollbarUnderMouse;
1799 mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent);
1800 if (wasLastScrollBar && mouseEvent.scrollbar() != m_lastScrollbarUnderMouse)
1801 m_lastScrollbarUnderMouse = nullptr;
1802 }
1803
1804 if (!swallowEvent) {
1805 // Refetch the event target node if it currently is the shadow node inside an <input> element.
1806 // If a mouse event handler changes the input element type to one that has a widget associated,
1807 // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the
1808 // event target node can't still be the shadow node.
1809 if (is<ShadowRoot>(*mouseEvent.targetNode()) && is<HTMLInputElement>(*downcast<ShadowRoot>(*mouseEvent.targetNode()).host()))
1810 mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent);
1811 }
1812
1813 if (!swallowEvent) {
1814 if (passedToScrollbar)
1815 swallowEvent = true;
1816 else
1817 swallowEvent = handleMousePressEvent(mouseEvent);
1818 }
1819 return swallowEvent;
1820}
1821
1822// This method only exists for platforms that don't know how to deliver
1823bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& platformMouseEvent)
1824{
1825 Ref<Frame> protectedFrame(m_frame);
1826 RefPtr<FrameView> protector(m_frame.view());
1827
1828 m_frame.selection().setCaretBlinkingSuspended(false);
1829
1830 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
1831
1832#if ENABLE(POINTER_LOCK)
1833 if (m_frame.page()->pointerLockController().isLocked()) {
1834 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent);
1835 return true;
1836 }
1837#endif
1838
1839 // We get this instead of a second mouse-up
1840 m_mousePressed = false;
1841 setLastKnownMousePosition(platformMouseEvent);
1842
1843 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
1844 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
1845 Frame* subframe = subframeForHitTestResult(mouseEvent);
1846 if (m_eventHandlerWillResetCapturingMouseEventsElement)
1847 m_capturingMouseEventsElement = nullptr;
1848 if (subframe && passMousePressEventToSubframe(mouseEvent, subframe))
1849 return true;
1850
1851 m_clickCount = platformMouseEvent.clickCount();
1852 bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false);
1853
1854 bool swallowClickEvent = platformMouseEvent.button() != RightButton && mouseEvent.targetNode() == m_clickNode && !dispatchMouseEvent(eventNames().clickEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true);
1855
1856 if (m_lastScrollbarUnderMouse)
1857 swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent);
1858
1859 bool swallowMouseReleaseEvent = !swallowMouseUpEvent && handleMouseReleaseEvent(mouseEvent);
1860
1861 invalidateClick();
1862
1863 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
1864}
1865
1866static ScrollableArea* enclosingScrollableArea(Node* node)
1867{
1868 for (auto ancestor = node; ancestor; ancestor = ancestor->parentOrShadowHostNode()) {
1869 if (is<HTMLIFrameElement>(*ancestor) || is<HTMLHtmlElement>(*ancestor) || is<HTMLDocument>(*ancestor))
1870 return nullptr;
1871
1872 auto renderer = ancestor->renderer();
1873 if (!renderer)
1874 continue;
1875
1876 if (is<RenderListBox>(*renderer))
1877 return downcast<RenderListBox>(renderer);
1878
1879 return renderer->enclosingLayer();
1880 }
1881
1882 return nullptr;
1883}
1884
1885bool EventHandler::mouseMoved(const PlatformMouseEvent& event)
1886{
1887 Ref<Frame> protectedFrame(m_frame);
1888 RefPtr<FrameView> protector(m_frame.view());
1889 MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration);
1890
1891 if (m_frame.page() && m_frame.page()->pageOverlayController().handleMouseEvent(event))
1892 return true;
1893
1894 HitTestResult hoveredNode = HitTestResult(LayoutPoint());
1895 bool result = handleMouseMoveEvent(event, &hoveredNode);
1896
1897 Page* page = m_frame.page();
1898 if (!page)
1899 return result;
1900
1901 if (auto scrolledArea = enclosingScrollableArea(hoveredNode.innerNode())) {
1902 if (FrameView* frameView = m_frame.view()) {
1903 if (frameView->containsScrollableArea(scrolledArea))
1904 scrolledArea->mouseMovedInContentArea();
1905 }
1906 }
1907
1908 if (FrameView* frameView = m_frame.view())
1909 frameView->mouseMovedInContentArea();
1910
1911 hoveredNode.setToNonUserAgentShadowAncestor();
1912 page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags());
1913 page->chrome().setToolTip(hoveredNode);
1914 return result;
1915}
1916
1917bool EventHandler::passMouseMovedEventToScrollbars(const PlatformMouseEvent& event)
1918{
1919 HitTestResult hoveredNode;
1920 return handleMouseMoveEvent(event, &hoveredNode, true);
1921}
1922
1923bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& platformMouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars)
1924{
1925#if ENABLE(TOUCH_EVENTS)
1926 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
1927 if (defaultPrevented)
1928 return true;
1929#endif
1930
1931 Ref<Frame> protectedFrame(m_frame);
1932 RefPtr<FrameView> protector(m_frame.view());
1933
1934#if ENABLE(POINTER_LOCK)
1935 if (m_frame.page()->pointerLockController().isLocked()) {
1936 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousemoveEvent);
1937 return true;
1938 }
1939#endif
1940
1941 setLastKnownMousePosition(platformMouseEvent);
1942
1943 if (m_hoverTimer.isActive())
1944 m_hoverTimer.stop();
1945
1946#if ENABLE(CURSOR_SUPPORT)
1947 m_cursorUpdateTimer.stop();
1948#endif
1949
1950#if !ENABLE(IOS_TOUCH_EVENTS)
1951 cancelFakeMouseMoveEvent();
1952#endif
1953
1954 if (m_svgPan) {
1955 downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition));
1956 return true;
1957 }
1958
1959 if (m_frameSetBeingResized)
1960 return !dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, platformMouseEvent, false);
1961
1962 // On iOS, our scrollbars are managed by UIKit.
1963#if !PLATFORM(IOS_FAMILY)
1964 // Send events right to a scrollbar if the mouse is pressed.
1965 if (m_lastScrollbarUnderMouse && m_mousePressed)
1966 return m_lastScrollbarUnderMouse->mouseMoved(platformMouseEvent);
1967#endif
1968
1969 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::AllowFrameScrollbars;
1970 if (m_mousePressed)
1971 hitType |= HitTestRequest::Active;
1972 else if (onlyUpdateScrollbars) {
1973 // Mouse events should be treated as "read-only" if we're updating only scrollbars. This
1974 // means that :hover and :active freeze in the state they were in, rather than updating
1975 // for nodes the mouse moves while the window is not key (which will be the case if
1976 // onlyUpdateScrollbars is true).
1977 hitType |= HitTestRequest::ReadOnly;
1978 }
1979
1980#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
1981 // Treat any mouse move events as readonly if the user is currently touching the screen.
1982 if (m_touchPressed)
1983 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
1984#endif
1985 HitTestRequest request(hitType);
1986 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
1987 if (hoveredNode)
1988 *hoveredNode = mouseEvent.hitTestResult();
1989
1990 if (m_resizeLayer && m_resizeLayer->inResizeMode())
1991 m_resizeLayer->resize(platformMouseEvent, m_offsetFromResizeCorner);
1992 else {
1993 Scrollbar* scrollbar = mouseEvent.scrollbar();
1994 updateLastScrollbarUnderMouse(scrollbar, m_mousePressed ? SetOrClearLastScrollbar::Clear : SetOrClearLastScrollbar::Set);
1995
1996 // On iOS, our scrollbars are managed by UIKit.
1997#if !PLATFORM(IOS_FAMILY)
1998 if (!m_mousePressed && scrollbar)
1999 scrollbar->mouseMoved(platformMouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering.
2000#endif
2001 if (onlyUpdateScrollbars) {
2002 if (shouldSendMouseEventsToInactiveWindows())
2003 updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, FireMouseOverOut::Yes);
2004
2005 return true;
2006 }
2007 }
2008
2009 bool swallowEvent = false;
2010 RefPtr<Frame> newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent);
2011
2012 // 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.
2013 if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(&m_frame) && m_lastMouseMoveEventSubframe != newSubframe)
2014 passMouseMoveEventToSubframe(mouseEvent, m_lastMouseMoveEventSubframe.get());
2015
2016 if (newSubframe) {
2017 // Update over/out state before passing the event to the subframe.
2018 updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, FireMouseOverOut::Yes);
2019
2020 // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target
2021 // node to be detached from its FrameView, in which case the event should not be passed.
2022 if (newSubframe->view())
2023 swallowEvent |= passMouseMoveEventToSubframe(mouseEvent, newSubframe.get(), hoveredNode);
2024 }
2025
2026 if (!newSubframe || mouseEvent.scrollbar()) {
2027#if ENABLE(CURSOR_SUPPORT)
2028 if (auto* view = m_frame.view())
2029 updateCursor(*view, mouseEvent.hitTestResult(), platformMouseEvent.shiftKey());
2030#endif
2031 }
2032
2033 m_lastMouseMoveEventSubframe = newSubframe;
2034
2035 if (swallowEvent)
2036 return true;
2037
2038 swallowEvent = !dispatchMouseEvent(eventNames().mousemoveEvent, mouseEvent.targetNode(), false, 0, platformMouseEvent, true);
2039#if ENABLE(DRAG_SUPPORT)
2040 if (!swallowEvent)
2041 swallowEvent = handleMouseDraggedEvent(mouseEvent);
2042#endif
2043
2044 return swallowEvent;
2045}
2046
2047bool EventHandler::shouldSendMouseEventsToInactiveWindows() const
2048{
2049#if PLATFORM(GTK)
2050 return true;
2051#endif
2052 return false;
2053}
2054
2055void EventHandler::invalidateClick()
2056{
2057 m_clickCount = 0;
2058 m_clickNode = nullptr;
2059}
2060
2061static Node* targetNodeForClickEvent(Node* mousePressNode, Node* mouseReleaseNode)
2062{
2063 if (!mousePressNode || !mouseReleaseNode)
2064 return nullptr;
2065
2066 if (mousePressNode == mouseReleaseNode)
2067 return mouseReleaseNode;
2068
2069 // If mousePressNode and mouseReleaseNode differ, we should fire the event at their common ancestor if there is one.
2070 if (&mousePressNode->document() == &mouseReleaseNode->document()) {
2071 if (auto* commonAncestor = Range::commonAncestorContainer(mousePressNode, mouseReleaseNode))
2072 return commonAncestor;
2073 }
2074
2075 Element* mouseReleaseShadowHost = mouseReleaseNode->shadowHost();
2076 if (mouseReleaseShadowHost && mouseReleaseShadowHost == mousePressNode->shadowHost()) {
2077 // We want to dispatch the click to the shadow tree host element to give listeners the illusion that the
2078 // shadom tree is a single element. For example, we want to give the illusion that <input type="range">
2079 // is a single element even though it is a composition of multiple shadom tree elements.
2080 return mouseReleaseShadowHost;
2081 }
2082 return nullptr;
2083}
2084
2085bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& platformMouseEvent)
2086{
2087 Ref<Frame> protectedFrame(m_frame);
2088 RefPtr<FrameView> protector(m_frame.view());
2089
2090 m_frame.selection().setCaretBlinkingSuspended(false);
2091
2092#if ENABLE(POINTER_LOCK)
2093 if (m_frame.page()->pointerLockController().isLocked()) {
2094 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent);
2095 return true;
2096 }
2097#endif
2098
2099 if (m_frame.page()->pageOverlayController().handleMouseEvent(platformMouseEvent))
2100 return true;
2101
2102#if ENABLE(TOUCH_EVENTS)
2103 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent);
2104 if (defaultPrevented)
2105 return true;
2106#endif
2107
2108 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
2109
2110#if ENABLE(PAN_SCROLLING)
2111 m_autoscrollController->handleMouseReleaseEvent(platformMouseEvent);
2112#endif
2113
2114 m_mousePressed = false;
2115 setLastKnownMousePosition(platformMouseEvent);
2116
2117 if (m_svgPan) {
2118 m_svgPan = false;
2119 downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition));
2120 return true;
2121 }
2122
2123 if (m_frameSetBeingResized)
2124 return !dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, platformMouseEvent, false);
2125
2126 // If an immediate action began or was completed using this series of mouse events, then we should send mouseup to
2127 // the DOM and return now so that we don't perform our own default behaviors.
2128 if (m_immediateActionStage == ImmediateActionStage::ActionCompleted || m_immediateActionStage == ImmediateActionStage::ActionUpdated || m_immediateActionStage == ImmediateActionStage::ActionCancelledAfterUpdate) {
2129 m_immediateActionStage = ImmediateActionStage::None;
2130 return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), true, m_clickCount, platformMouseEvent, false);
2131 }
2132 m_immediateActionStage = ImmediateActionStage::None;
2133
2134 if (m_lastScrollbarUnderMouse) {
2135 invalidateClick();
2136 m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent);
2137 bool cancelable = true;
2138 bool setUnder = false;
2139 return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), cancelable, m_clickCount, platformMouseEvent, setUnder);
2140 }
2141
2142 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
2143 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
2144 Frame* subframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent);
2145 if (m_eventHandlerWillResetCapturingMouseEventsElement)
2146 m_capturingMouseEventsElement = nullptr;
2147 if (subframe && passMouseReleaseEventToSubframe(mouseEvent, subframe))
2148 return true;
2149
2150 bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false);
2151
2152 bool contextMenuEvent = platformMouseEvent.button() == RightButton;
2153
2154 Node* nodeToClick = targetNodeForClickEvent(m_clickNode.get(), mouseEvent.targetNode());
2155 bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && nodeToClick && !dispatchMouseEvent(eventNames().clickEvent, nodeToClick, true, m_clickCount, platformMouseEvent, true);
2156
2157 if (m_resizeLayer) {
2158 m_resizeLayer->setInResizeMode(false);
2159 m_resizeLayer = nullptr;
2160 }
2161
2162 bool swallowMouseReleaseEvent = false;
2163 if (!swallowMouseUpEvent)
2164 swallowMouseReleaseEvent = handleMouseReleaseEvent(mouseEvent);
2165
2166 invalidateClick();
2167
2168 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
2169}
2170
2171#if ENABLE(MOUSE_FORCE_EVENTS)
2172bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& event)
2173{
2174 Ref<Frame> protectedFrame(m_frame);
2175 RefPtr<FrameView> protector(m_frame.view());
2176
2177#if ENABLE(POINTER_LOCK)
2178 if (m_frame.page()->pointerLockController().isLocked()) {
2179 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcechangedEvent);
2180 if (event.type() == PlatformEvent::MouseForceDown)
2181 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcedownEvent);
2182 if (event.type() == PlatformEvent::MouseForceUp)
2183 m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforceupEvent);
2184 return true;
2185 }
2186#endif
2187
2188 setLastKnownMousePosition(event);
2189
2190 HitTestRequest::HitTestRequestType hitType = HitTestRequest::DisallowUserAgentShadowContent;
2191
2192 if (event.force())
2193 hitType |= HitTestRequest::Active;
2194
2195 HitTestRequest request(hitType);
2196 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event);
2197
2198 bool swallowedEvent = !dispatchMouseEvent(eventNames().webkitmouseforcechangedEvent, mouseEvent.targetNode(), false, 0, event, false);
2199 if (event.type() == PlatformEvent::MouseForceDown)
2200 swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforcedownEvent, mouseEvent.targetNode(), false, 0, event, false);
2201 if (event.type() == PlatformEvent::MouseForceUp)
2202 swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforceupEvent, mouseEvent.targetNode(), false, 0, event, false);
2203
2204 return swallowedEvent;
2205}
2206#else
2207bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& )
2208{
2209 return false;
2210}
2211#endif // #if ENABLE(MOUSE_FORCE_EVENTS)
2212
2213bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& platformMouseEvent)
2214{
2215 // If the event was a middle click, attempt to copy global selection in after
2216 // the newly set caret position.
2217 //
2218 // This code is called from either the mouse up or mouse down handling. There
2219 // is some debate about when the global selection is pasted:
2220 // xterm: pastes on up.
2221 // GTK: pastes on down.
2222 // Qt: pastes on up.
2223 // Firefox: pastes on up.
2224 // Chromium: pastes on up.
2225 //
2226 // There is something of a webcompat angle to this well, as highlighted by
2227 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
2228 // down then the text is pasted just before the onclick handler runs and
2229 // clears the text box. So it's important this happens after the event
2230 // handlers have been fired.
2231#if PLATFORM(GTK)
2232 if (platformMouseEvent.type() != PlatformEvent::MousePressed)
2233 return false;
2234#else
2235 if (platformMouseEvent.type() != PlatformEvent::MouseReleased)
2236 return false;
2237#endif
2238
2239 if (!m_frame.page())
2240 return false;
2241 Frame& focusFrame = m_frame.page()->focusController().focusedOrMainFrame();
2242 // Do not paste here if the focus was moved somewhere else.
2243 if (&m_frame == &focusFrame && m_frame.editor().client()->supportsGlobalSelection())
2244 return m_frame.editor().command("PasteGlobalSelection"_s).execute();
2245
2246 return false;
2247}
2248
2249#if ENABLE(DRAG_SUPPORT)
2250
2251bool EventHandler::dispatchDragEvent(const AtomString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer& dataTransfer)
2252{
2253 Ref<Frame> protectedFrame(m_frame);
2254 FrameView* view = m_frame.view();
2255
2256 // FIXME: We might want to dispatch a dragleave even if the view is gone.
2257 if (!view)
2258 return false;
2259
2260 view->disableLayerFlushThrottlingTemporarilyForInteraction();
2261 // FIXME: Use MouseEvent::create which takes PlatformMouseEvent.
2262 Ref<MouseEvent> me = MouseEvent::create(eventType, Event::CanBubble::Yes, Event::IsCancelable::Yes, Event::IsComposed::Yes,
2263 event.timestamp().approximateMonotonicTime(), &m_frame.windowProxy(), 0,
2264 event.globalPosition(), event.position(),
2265#if ENABLE(POINTER_LOCK)
2266 event.movementDelta(),
2267#else
2268 { },
2269#endif
2270 event.modifiers(), 0, 0, nullptr, event.force(), NoTap, &dataTransfer);
2271
2272 dragTarget.dispatchEvent(me);
2273 return me->defaultPrevented();
2274}
2275
2276static bool targetIsFrame(Node* target, Frame*& frame)
2277{
2278 if (!is<HTMLFrameElementBase>(target))
2279 return false;
2280
2281 frame = downcast<HTMLFrameElementBase>(*target).contentFrame();
2282 return true;
2283}
2284
2285static DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation)
2286{
2287 if (dragOperation == "copy")
2288 return DragOperationCopy;
2289 if (dragOperation == "move")
2290 return DragOperationMove;
2291 if (dragOperation == "link")
2292 return DragOperationLink;
2293 return DragOperationNone;
2294}
2295
2296static String convertDragOperationToDropZoneOperation(DragOperation operation)
2297{
2298 switch (operation) {
2299 case DragOperationCopy:
2300 return "copy"_s;
2301 case DragOperationMove:
2302 return "move"_s;
2303 case DragOperationLink:
2304 return "link"_s;
2305 default:
2306 return "copy"_s;
2307 }
2308}
2309
2310static bool hasDropZoneType(DataTransfer& dataTransfer, const String& keyword)
2311{
2312 if (keyword.startsWith("file:"))
2313 return dataTransfer.hasFileOfType(keyword.substring(5));
2314
2315 if (keyword.startsWith("string:"))
2316 return dataTransfer.hasStringOfType(keyword.substring(7));
2317
2318 return false;
2319}
2320
2321static bool findDropZone(Node& target, DataTransfer& dataTransfer)
2322{
2323 RefPtr<Element> element = is<Element>(target) ? &downcast<Element>(target) : target.parentElement();
2324 for (; element; element = element->parentElement()) {
2325 SpaceSplitString keywords(element->attributeWithoutSynchronization(webkitdropzoneAttr), true);
2326 bool matched = false;
2327 DragOperation dragOperation = DragOperationNone;
2328 for (unsigned i = 0, size = keywords.size(); i < size; ++i) {
2329 DragOperation op = convertDropZoneOperationToDragOperation(keywords[i]);
2330 if (op != DragOperationNone) {
2331 if (dragOperation == DragOperationNone)
2332 dragOperation = op;
2333 } else
2334 matched = matched || hasDropZoneType(dataTransfer, keywords[i].string());
2335 if (matched && dragOperation != DragOperationNone)
2336 break;
2337 }
2338 if (matched) {
2339 dataTransfer.setDropEffect(convertDragOperationToDropZoneOperation(dragOperation));
2340 return true;
2341 }
2342 }
2343 return false;
2344}
2345
2346EventHandler::DragTargetResponse EventHandler::dispatchDragEnterOrDragOverEvent(const AtomString& eventType, Element& target, const PlatformMouseEvent& event,
2347 std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
2348{
2349 auto dataTransfer = DataTransfer::createForUpdatingDropTarget(target.document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
2350 bool accept = dispatchDragEvent(eventType, target, event, dataTransfer.get());
2351 if (!accept)
2352 accept = findDropZone(target, dataTransfer);
2353 dataTransfer->makeInvalidForSecurity();
2354 if (accept && !dataTransfer->dropEffectIsUninitialized())
2355 return { true, dataTransfer->destinationOperation() };
2356 return { accept, WTF::nullopt };
2357}
2358
2359EventHandler::DragTargetResponse EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, const std::function<std::unique_ptr<Pasteboard>()>& makePasteboard, DragOperation sourceOperation, bool draggingFiles)
2360{
2361 Ref<Frame> protectedFrame(m_frame);
2362 if (!m_frame.view())
2363 return { };
2364
2365 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
2366 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event);
2367
2368 RefPtr<Element> newTarget;
2369 if (Node* targetNode = mouseEvent.targetNode()) {
2370 // Drag events should never go to non-element nodes (following IE, and proper mouseover/out dispatch)
2371 if (!is<Element>(*targetNode))
2372 newTarget = targetNode->parentOrShadowHostElement();
2373 else
2374 newTarget = downcast<Element>(targetNode);
2375 }
2376
2377 m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp());
2378
2379 DragTargetResponse response;
2380 if (m_dragTarget != newTarget) {
2381 // FIXME: this ordering was explicitly chosen to match WinIE. However,
2382 // it is sometimes incorrect when dragging within subframes, as seen with
2383 // LayoutTests/fast/events/drag-in-frames.html.
2384 //
2385 // 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>.
2386 Frame* targetFrame;
2387 if (targetIsFrame(newTarget.get(), targetFrame)) {
2388 if (targetFrame)
2389 response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
2390 } else if (newTarget) {
2391 // 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.
2392 if (dragState().source && dragState().shouldDispatchEvents)
2393 dispatchDragSrcEvent(eventNames().dragEvent, event);
2394 response = dispatchDragEnterOrDragOverEvent(eventNames().dragenterEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles);
2395 }
2396
2397 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2398 // FIXME: Recursing again here doesn't make sense if the newTarget and m_dragTarget were in the same frame.
2399 if (targetFrame)
2400 response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
2401 } else if (m_dragTarget) {
2402 auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), makePasteboard(), sourceOperation, draggingFiles);
2403 dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get());
2404 dataTransfer->makeInvalidForSecurity();
2405 }
2406
2407 if (newTarget) {
2408 // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that
2409 // two dragover events fired. So, we mark that we should only fire a dragover event on the next call to this function.
2410 m_shouldOnlyFireDragOverEvent = true;
2411 }
2412 } else {
2413 Frame* targetFrame;
2414 if (targetIsFrame(newTarget.get(), targetFrame)) {
2415 if (targetFrame)
2416 response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles);
2417 } else if (newTarget) {
2418 // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier.
2419 if (!m_shouldOnlyFireDragOverEvent && dragState().source && dragState().shouldDispatchEvents)
2420 dispatchDragSrcEvent(eventNames().dragEvent, event);
2421 response = dispatchDragEnterOrDragOverEvent(eventNames().dragoverEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles);
2422 m_shouldOnlyFireDragOverEvent = false;
2423 }
2424 }
2425 m_dragTarget = WTFMove(newTarget);
2426 return response;
2427}
2428
2429void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
2430{
2431 Ref<Frame> protectedFrame(m_frame);
2432
2433 Frame* targetFrame;
2434 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2435 if (targetFrame)
2436 targetFrame->eventHandler().cancelDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles);
2437 } else if (m_dragTarget) {
2438 if (dragState().source && dragState().shouldDispatchEvents)
2439 dispatchDragSrcEvent(eventNames().dragEvent, event);
2440
2441 auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
2442 dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get());
2443 dataTransfer->makeInvalidForSecurity();
2444 }
2445 clearDragState();
2446}
2447
2448bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
2449{
2450 Ref<Frame> protectedFrame(m_frame);
2451
2452 Frame* targetFrame;
2453 bool preventedDefault = false;
2454 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
2455 if (targetFrame)
2456 preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles);
2457 } else if (m_dragTarget) {
2458 auto dataTransfer = DataTransfer::createForDrop(m_dragTarget->document(), WTFMove(pasteboard), sourceOperation, draggingFiles);
2459 preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, dataTransfer);
2460 dataTransfer->makeInvalidForSecurity();
2461 }
2462 clearDragState();
2463 return preventedDefault;
2464}
2465
2466void EventHandler::clearDragState()
2467{
2468 stopAutoscrollTimer();
2469 m_dragTarget = nullptr;
2470 m_capturingMouseEventsElement = nullptr;
2471 m_shouldOnlyFireDragOverEvent = false;
2472#if PLATFORM(COCOA)
2473 m_sendingEventToSubview = false;
2474#endif
2475}
2476
2477#endif // ENABLE(DRAG_SUPPORT)
2478
2479void EventHandler::setCapturingMouseEventsElement(Element* element)
2480{
2481 m_capturingMouseEventsElement = element;
2482 m_eventHandlerWillResetCapturingMouseEventsElement = false;
2483}
2484
2485MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mouseEvent)
2486{
2487 Ref<Frame> protectedFrame(m_frame);
2488 ASSERT(m_frame.document());
2489 return m_frame.document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mouseEvent.position()), mouseEvent);
2490}
2491
2492static bool hierarchyHasCapturingEventListeners(Element* element, const AtomString& pointerEventName, const AtomString& compatibilityMouseEventName)
2493{
2494 for (ContainerNode* curr = element; curr; curr = curr->parentInComposedTree()) {
2495 if (curr->hasCapturingEventListeners(pointerEventName) || curr->hasCapturingEventListeners(compatibilityMouseEventName))
2496 return true;
2497 }
2498 return false;
2499}
2500
2501void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& platformMouseEvent, FireMouseOverOut fireMouseOverOut)
2502{
2503 Ref<Frame> protectedFrame(m_frame);
2504 Element* targetElement = nullptr;
2505
2506 // If we're capturing, we always go right to that element.
2507 if (m_capturingMouseEventsElement)
2508 targetElement = m_capturingMouseEventsElement.get();
2509 else if (targetNode) {
2510 // If the target node is a non-element, dispatch on the parent. <rdar://problem/4196646>
2511 while (targetNode && !is<Element>(*targetNode))
2512 targetNode = targetNode->parentInComposedTree();
2513 targetElement = downcast<Element>(targetNode);
2514 }
2515
2516 m_elementUnderMouse = targetElement;
2517
2518 // Fire mouseout/mouseover if the mouse has shifted to a different node.
2519 if (fireMouseOverOut == FireMouseOverOut::Yes) {
2520 auto scrollableAreaForLastNode = enclosingScrollableArea(m_lastElementUnderMouse.get());
2521 auto scrollableAreaForNodeUnderMouse = enclosingScrollableArea(m_elementUnderMouse.get());
2522 Page* page = m_frame.page();
2523
2524 if (m_lastElementUnderMouse && (!m_elementUnderMouse || &m_elementUnderMouse->document() != m_frame.document())) {
2525 // The mouse has moved between frames.
2526 if (Frame* frame = m_lastElementUnderMouse->document().frame()) {
2527 if (FrameView* frameView = frame->view())
2528 frameView->mouseExitedContentArea();
2529 }
2530 } else if (page && (scrollableAreaForLastNode && (!scrollableAreaForNodeUnderMouse || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) {
2531 // The mouse has moved between layers.
2532 if (Frame* frame = m_lastElementUnderMouse->document().frame()) {
2533 if (FrameView* frameView = frame->view()) {
2534 if (frameView->containsScrollableArea(scrollableAreaForLastNode))
2535 scrollableAreaForLastNode->mouseExitedContentArea();
2536 }
2537 }
2538 }
2539
2540 if (m_elementUnderMouse && (!m_lastElementUnderMouse || &m_lastElementUnderMouse->document() != m_frame.document())) {
2541 // The mouse has moved between frames.
2542 if (Frame* frame = m_elementUnderMouse->document().frame()) {
2543 if (FrameView* frameView = frame->view())
2544 frameView->mouseEnteredContentArea();
2545 }
2546 } else if (page && (scrollableAreaForNodeUnderMouse && (!scrollableAreaForLastNode || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) {
2547 // The mouse has moved between layers.
2548 if (Frame* frame = m_elementUnderMouse->document().frame()) {
2549 if (FrameView* frameView = frame->view()) {
2550 if (frameView->containsScrollableArea(scrollableAreaForNodeUnderMouse))
2551 scrollableAreaForNodeUnderMouse->mouseEnteredContentArea();
2552 }
2553 }
2554 }
2555
2556 if (m_lastElementUnderMouse && &m_lastElementUnderMouse->document() != m_frame.document()) {
2557 m_lastElementUnderMouse = nullptr;
2558 m_lastScrollbarUnderMouse = nullptr;
2559 }
2560
2561 if (m_lastElementUnderMouse != m_elementUnderMouse) {
2562 // mouseenter and mouseleave events are only dispatched if there is a capturing eventhandler on an ancestor
2563 // or a normal eventhandler on the element itself (they don't bubble).
2564 // This optimization is necessary since these events can cause O(n^2) capturing event-handler checks.
2565 bool hasCapturingMouseEnterListener = hierarchyHasCapturingEventListeners(m_elementUnderMouse.get(), eventNames().pointerenterEvent, eventNames().mouseenterEvent);
2566 bool hasCapturingMouseLeaveListener = hierarchyHasCapturingEventListeners(m_lastElementUnderMouse.get(), eventNames().pointerleaveEvent, eventNames().mouseleaveEvent);
2567
2568 Vector<Ref<Element>, 32> leftElementsChain;
2569 for (Element* element = m_lastElementUnderMouse.get(); element; element = element->parentElementInComposedTree())
2570 leftElementsChain.append(*element);
2571 Vector<Ref<Element>, 32> enteredElementsChain;
2572 for (Element* element = m_elementUnderMouse.get(); element; element = element->parentElementInComposedTree())
2573 enteredElementsChain.append(*element);
2574
2575 if (!leftElementsChain.isEmpty() && !enteredElementsChain.isEmpty() && leftElementsChain.last().ptr() == enteredElementsChain.last().ptr()) {
2576 size_t minHeight = std::min(leftElementsChain.size(), enteredElementsChain.size());
2577 size_t i;
2578 for (i = 0; i < minHeight; ++i) {
2579 if (leftElementsChain[leftElementsChain.size() - i - 1].ptr() != enteredElementsChain[enteredElementsChain.size() - i - 1].ptr())
2580 break;
2581 }
2582 leftElementsChain.shrink(leftElementsChain.size() - i);
2583 enteredElementsChain.shrink(enteredElementsChain.size() - i);
2584 }
2585
2586 if (m_lastElementUnderMouse)
2587 m_lastElementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoutEvent, 0, m_elementUnderMouse.get());
2588
2589 for (auto& chain : leftElementsChain) {
2590 if (hasCapturingMouseLeaveListener || chain->hasEventListeners(eventNames().pointerleaveEvent) || chain->hasEventListeners(eventNames().mouseleaveEvent))
2591 chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseleaveEvent, 0, m_elementUnderMouse.get());
2592 }
2593
2594 if (m_elementUnderMouse)
2595 m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoverEvent, 0, m_lastElementUnderMouse.get());
2596
2597 for (auto& chain : WTF::makeReversedRange(enteredElementsChain)) {
2598 if (hasCapturingMouseEnterListener || chain->hasEventListeners(eventNames().pointerenterEvent) || chain->hasEventListeners(eventNames().mouseenterEvent))
2599 chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseenterEvent, 0, m_lastElementUnderMouse.get());
2600 }
2601 }
2602 m_lastElementUnderMouse = m_elementUnderMouse;
2603 }
2604}
2605
2606bool EventHandler::dispatchMouseEvent(const AtomString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& platformMouseEvent, bool setUnder)
2607{
2608 Ref<Frame> protectedFrame(m_frame);
2609
2610 if (auto* view = m_frame.view())
2611 view->disableLayerFlushThrottlingTemporarilyForInteraction();
2612
2613 updateMouseEventTargetNode(targetNode, platformMouseEvent, setUnder ? FireMouseOverOut::Yes : FireMouseOverOut::No);
2614
2615 if (m_elementUnderMouse && !m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventType, clickCount))
2616 return false;
2617
2618 if (eventType != eventNames().mousedownEvent)
2619 return true;
2620
2621 // If clicking on a frame scrollbar, do not make any change to which element is focused.
2622 auto* view = m_frame.view();
2623 if (view && view->scrollbarAtPoint(platformMouseEvent.position()))
2624 return true;
2625
2626 // The layout needs to be up to date to determine if an element is focusable.
2627 m_frame.document()->updateLayoutIgnorePendingStylesheets();
2628
2629 // Remove focus from the currently focused element when a link or button is clicked.
2630 // This is expected by some sites that rely on change event handlers running
2631 // from form fields before the button click is processed, behavior that was inherited
2632 // from the user interface of Windows, where pushing a button moves focus to the button.
2633
2634 // Walk up the DOM tree to search for an element to focus.
2635 RefPtr<Element> element;
2636 for (element = m_elementUnderMouse.get(); element; element = element->parentElementInComposedTree()) {
2637 if (element->isMouseFocusable())
2638 break;
2639 }
2640
2641 // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus an
2642 // element on mouse down if it's selected and inside a focused element. It will be
2643 // focused if the user does a mouseup over it, however, because the mouseup
2644 // will set a selection inside it, which will also set the focused element.
2645 if (element && m_frame.selection().isRange()) {
2646 if (auto range = m_frame.selection().toNormalizedRange()) {
2647 auto result = range->compareNode(*element);
2648 if (!result.hasException() && result.releaseReturnValue() == Range::NODE_INSIDE && element->isDescendantOf(m_frame.document()->focusedElement()))
2649 return true;
2650 }
2651 }
2652
2653 // Only change the focus when clicking scrollbars if it can be transferred to a mouse focusable node.
2654 if (!element && isInsideScrollbar(platformMouseEvent.position()))
2655 return false;
2656
2657 // If focus shift is blocked, we eat the event.
2658 auto* page = m_frame.page();
2659 if (page && !page->focusController().setFocusedElement(element.get(), m_frame))
2660 return false;
2661
2662 return true;
2663}
2664
2665bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const
2666{
2667 if (auto* document = m_frame.document()) {
2668 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
2669 HitTestResult result(windowPoint);
2670 document->hitTest(request, result);
2671 return result.scrollbar();
2672 }
2673
2674 return false;
2675}
2676
2677#if !USE(GLIB)
2678
2679bool EventHandler::shouldSwapScrollDirection(const HitTestResult&, const PlatformWheelEvent&) const
2680{
2681 return false;
2682}
2683
2684#endif
2685
2686#if !PLATFORM(MAC)
2687
2688void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&, const HitTestResult&, RefPtr<Element>&, RefPtr<ContainerNode>&, WeakPtr<ScrollableArea>&, bool&)
2689{
2690}
2691
2692void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& event)
2693{
2694 if (auto* page = m_frame.page())
2695 page->wheelEventDeltaFilter()->updateFromDelta(FloatSize(event.deltaX(), event.deltaY()));
2696}
2697
2698bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& event, ContainerNode*, const WeakPtr<ScrollableArea>&)
2699{
2700 Ref<Frame> protectedFrame(m_frame);
2701
2702 // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
2703 FrameView* view = m_frame.view();
2704
2705 bool didHandleEvent = view ? view->wheelEvent(event) : false;
2706 m_isHandlingWheelEvent = false;
2707 return didHandleEvent;
2708}
2709
2710bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode*)
2711{
2712 return true;
2713}
2714
2715void EventHandler::platformNotifyIfEndGesture(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&)
2716{
2717}
2718
2719void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&)
2720{
2721 clearLatchedState();
2722}
2723
2724#if !PLATFORM(IOS_FAMILY)
2725
2726IntPoint EventHandler::targetPositionInWindowForSelectionAutoscroll() const
2727{
2728 return m_lastKnownMousePosition;
2729}
2730
2731#endif // !PLATFORM(IOS_FAMILY)
2732
2733#endif // !PLATFORM(MAC)
2734
2735#if !PLATFORM(IOS_FAMILY)
2736
2737bool EventHandler::shouldUpdateAutoscroll()
2738{
2739 return mousePressed();
2740}
2741
2742#endif // !PLATFORM(IOS_FAMILY)
2743
2744Widget* EventHandler::widgetForEventTarget(Element* eventTarget)
2745{
2746 if (!eventTarget)
2747 return nullptr;
2748
2749 auto* target = eventTarget->renderer();
2750 if (!is<RenderWidget>(target))
2751 return nullptr;
2752
2753 return downcast<RenderWidget>(*target).widget();
2754}
2755
2756static WeakPtr<Widget> widgetForElement(const Element& element)
2757{
2758 auto target = element.renderer();
2759 if (!is<RenderWidget>(target) || !downcast<RenderWidget>(*target).widget())
2760 return { };
2761
2762 return makeWeakPtr(*downcast<RenderWidget>(*target).widget());
2763}
2764
2765bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr<Widget>& widget, const WeakPtr<ScrollableArea>& scrollableArea, ContainerNode* scrollableContainer)
2766{
2767 m_isHandlingWheelEvent = false;
2768
2769 // We do another check on the widget because the event handler can run JS which results in the frame getting destroyed.
2770 if (!widget)
2771 return false;
2772
2773 if (scrollableArea)
2774 scrollableArea->setScrollShouldClearLatchedState(false);
2775
2776 platformNotifyIfEndGesture(event, scrollableArea);
2777
2778 if (!widget->platformWidget())
2779 return true;
2780
2781 return platformCompletePlatformWidgetWheelEvent(event, *widget.get(), scrollableContainer);
2782}
2783
2784bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event)
2785{
2786 auto* document = m_frame.document();
2787 if (!document)
2788 return false;
2789
2790 Ref<Frame> protectedFrame(m_frame);
2791 RefPtr<FrameView> protector(m_frame.view());
2792
2793 FrameView* view = m_frame.view();
2794 if (!view)
2795 return false;
2796
2797#if ENABLE(POINTER_LOCK)
2798 if (m_frame.page()->pointerLockController().isLocked()) {
2799 m_frame.page()->pointerLockController().dispatchLockedWheelEvent(event);
2800 return true;
2801 }
2802#endif
2803
2804 m_isHandlingWheelEvent = true;
2805 setFrameWasScrolledByUser();
2806
2807 HitTestRequest request;
2808 HitTestResult result(view->windowToContents(event.position()));
2809 document->hitTest(request, result);
2810
2811 RefPtr<Element> element = result.targetElement();
2812 RefPtr<ContainerNode> scrollableContainer;
2813 WeakPtr<ScrollableArea> scrollableArea;
2814 bool isOverWidget = result.isOverWidget();
2815 platformPrepareForWheelEvents(event, result, element, scrollableContainer, scrollableArea, isOverWidget);
2816
2817#if PLATFORM(MAC)
2818 if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone && m_frame.page())
2819 m_frame.page()->resetLatchingState();
2820#endif
2821
2822 // FIXME: It should not be necessary to do this mutation here.
2823 // Instead, the handlers should know convert vertical scrolls appropriately.
2824 PlatformWheelEvent adjustedEvent = shouldSwapScrollDirection(result, event) ? event.copySwappingDirection() : event;
2825 platformRecordWheelEvent(adjustedEvent);
2826
2827 if (element) {
2828 if (isOverWidget) {
2829 if (WeakPtr<Widget> widget = widgetForElement(*element)) {
2830 if (widgetDidHandleWheelEvent(event, *widget.get()))
2831 return completeWidgetWheelEvent(adjustedEvent, widget, scrollableArea, scrollableContainer.get());
2832 }
2833 }
2834
2835 if (!element->dispatchWheelEvent(adjustedEvent)) {
2836 m_isHandlingWheelEvent = false;
2837 if (scrollableArea && scrollableArea->scrollShouldClearLatchedState()) {
2838 // Web developer is controlling scrolling, so don't attempt to latch.
2839 clearLatchedState();
2840 scrollableArea->setScrollShouldClearLatchedState(false);
2841 }
2842
2843 platformNotifyIfEndGesture(adjustedEvent, scrollableArea);
2844 return true;
2845 }
2846 }
2847
2848 if (scrollableArea)
2849 scrollableArea->setScrollShouldClearLatchedState(false);
2850
2851 bool handledEvent = platformCompleteWheelEvent(adjustedEvent, scrollableContainer.get(), scrollableArea);
2852 platformNotifyIfEndGesture(adjustedEvent, scrollableArea);
2853 return handledEvent;
2854}
2855
2856void EventHandler::clearLatchedState()
2857{
2858 auto* page = m_frame.page();
2859 if (!page)
2860 return;
2861
2862#if PLATFORM(MAC)
2863 page->resetLatchingState();
2864#endif
2865 if (auto filter = page->wheelEventDeltaFilter())
2866 filter->endFilteringDeltas();
2867}
2868
2869void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent& wheelEvent)
2870{
2871 if (!startNode)
2872 return;
2873
2874 auto protectedFrame = makeRef(m_frame);
2875
2876 FloatSize filteredPlatformDelta(wheelEvent.deltaX(), wheelEvent.deltaY());
2877 FloatSize filteredVelocity;
2878 if (auto platformWheelEvent = wheelEvent.underlyingPlatformEvent()) {
2879 filteredPlatformDelta.setWidth(platformWheelEvent->deltaX());
2880 filteredPlatformDelta.setHeight(platformWheelEvent->deltaY());
2881 }
2882
2883#if PLATFORM(MAC)
2884 ScrollLatchingState* latchedState = m_frame.page() ? m_frame.page()->latchingState() : nullptr;
2885 Element* stopElement = latchedState ? latchedState->previousWheelScrolledElement() : nullptr;
2886
2887 if (m_frame.page() && m_frame.page()->wheelEventDeltaFilter()->isFilteringDeltas()) {
2888 filteredPlatformDelta = m_frame.page()->wheelEventDeltaFilter()->filteredDelta();
2889 filteredVelocity = m_frame.page()->wheelEventDeltaFilter()->filteredVelocity();
2890 }
2891#else
2892 Element* stopElement = nullptr;
2893#endif
2894
2895 if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &stopElement, filteredPlatformDelta, filteredVelocity))
2896 wheelEvent.setDefaultHandled();
2897
2898#if PLATFORM(MAC)
2899 if (latchedState && !latchedState->wheelEventElement())
2900 latchedState->setPreviousWheelScrolledElement(stopElement);
2901#endif
2902}
2903
2904#if ENABLE(CONTEXT_MENUS)
2905bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event)
2906{
2907 Ref<Frame> protectedFrame(m_frame);
2908
2909 Document* doc = m_frame.document();
2910 FrameView* view = m_frame.view();
2911 if (!view)
2912 return false;
2913
2914 // Caret blinking is normally un-suspended in handleMouseReleaseEvent, but we
2915 // won't receive that event once the context menu is up.
2916 m_frame.selection().setCaretBlinkingSuspended(false);
2917 // Clear mouse press state to avoid initiating a drag while context menu is up.
2918 m_mousePressed = false;
2919 bool swallowEvent;
2920 LayoutPoint viewportPos = view->windowToContents(event.position());
2921 HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
2922 MouseEventWithHitTestResults mouseEvent = doc->prepareMouseEvent(request, viewportPos, event);
2923
2924 // Do not show context menus when clicking on scrollbars.
2925 if (mouseEvent.scrollbar() || view->scrollbarAtPoint(event.position()))
2926 return false;
2927
2928 if (m_frame.editor().behavior().shouldSelectOnContextualMenuClick()
2929 && !m_frame.selection().contains(viewportPos)
2930 // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse.
2931 // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items
2932 // available for text selections. But only if we're above text.
2933 && (m_frame.selection().selection().isContentEditable() || (mouseEvent.targetNode() && mouseEvent.targetNode()->isTextNode()))) {
2934 m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2935 selectClosestContextualWordOrLinkFromMouseEvent(mouseEvent);
2936 }
2937
2938 swallowEvent = !dispatchMouseEvent(eventNames().contextmenuEvent, mouseEvent.targetNode(), true, 0, event, false);
2939
2940 return swallowEvent;
2941}
2942
2943bool EventHandler::sendContextMenuEventForKey()
2944{
2945 Ref<Frame> protectedFrame(m_frame);
2946
2947 FrameView* view = m_frame.view();
2948 if (!view)
2949 return false;
2950
2951 Document* doc = m_frame.document();
2952 if (!doc)
2953 return false;
2954
2955 // Clear mouse press state to avoid initiating a drag while context menu is up.
2956 m_mousePressed = false;
2957
2958 static const int kContextMenuMargin = 1;
2959
2960#if OS(WINDOWS)
2961 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
2962#else
2963 int rightAligned = 0;
2964#endif
2965 IntPoint location;
2966
2967 Element* focusedElement = doc->focusedElement();
2968 const VisibleSelection& selection = m_frame.selection().selection();
2969 Position start = selection.start();
2970
2971 if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) {
2972 RefPtr<Range> selectionRange = selection.toNormalizedRange();
2973 IntRect firstRect = m_frame.editor().firstRectForRange(selectionRange.get());
2974
2975 int x = rightAligned ? firstRect.maxX() : firstRect.x();
2976 // In a multiline edit, firstRect.maxY() would endup on the next line, so -1.
2977 int y = firstRect.maxY() ? firstRect.maxY() - 1 : 0;
2978 location = IntPoint(x, y);
2979 } else if (focusedElement) {
2980 RenderBoxModelObject* box = focusedElement->renderBoxModelObject();
2981 if (!box)
2982 return false;
2983
2984 IntRect boundingBoxRect = box->absoluteBoundingBoxRect(true);
2985 location = IntPoint(boundingBoxRect.x(), boundingBoxRect.maxY() - 1);
2986 } else {
2987 location = IntPoint(
2988 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
2989 kContextMenuMargin);
2990 }
2991
2992 m_frame.view()->setCursor(pointerCursor());
2993
2994 IntPoint position = view->contentsToRootView(location);
2995 IntPoint globalPosition = view->hostWindow()->rootViewToScreen(IntRect(position, IntSize())).location();
2996
2997 Node* targetNode = doc->focusedElement();
2998 if (!targetNode)
2999 targetNode = doc;
3000
3001 // Use the focused node as the target for hover and active.
3002 HitTestResult result(position);
3003 result.setInnerNode(targetNode);
3004 doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent, result.targetElement());
3005
3006 // The contextmenu event is a mouse event even when invoked using the keyboard.
3007 // This is required for web compatibility.
3008
3009#if OS(WINDOWS)
3010 PlatformEvent::Type eventType = PlatformEvent::MouseReleased;
3011#else
3012 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
3013#endif
3014
3015 PlatformMouseEvent platformMouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WallTime::now(), ForceAtClick, NoTap);
3016
3017 return sendContextMenuEvent(platformMouseEvent);
3018}
3019#endif // ENABLE(CONTEXT_MENUS)
3020
3021void EventHandler::scheduleHoverStateUpdate()
3022{
3023 if (!m_hoverTimer.isActive())
3024 m_hoverTimer.startOneShot(0_s);
3025}
3026
3027#if ENABLE(CURSOR_SUPPORT)
3028void EventHandler::scheduleCursorUpdate()
3029{
3030 if (!m_cursorUpdateTimer.isActive())
3031 m_cursorUpdateTimer.startOneShot(cursorUpdateInterval);
3032}
3033#endif
3034
3035void EventHandler::dispatchFakeMouseMoveEventSoon()
3036{
3037#if !ENABLE(IOS_TOUCH_EVENTS)
3038 if (m_mousePressed)
3039 return;
3040
3041 if (m_mousePositionIsUnknown)
3042 return;
3043
3044 if (Page* page = m_frame.page()) {
3045 if (!page->chrome().client().shouldDispatchFakeMouseMoveEvents())
3046 return;
3047 }
3048
3049 // If the content has ever taken longer than fakeMouseMoveShortInterval we
3050 // reschedule the timer and use a longer time. This will cause the content
3051 // to receive these moves only after the user is done scrolling, reducing
3052 // pauses during the scroll.
3053 if (m_fakeMouseMoveEventTimer.isActive())
3054 m_fakeMouseMoveEventTimer.stop();
3055 m_fakeMouseMoveEventTimer.startOneShot(m_maxMouseMovedDuration > fakeMouseMoveDurationThreshold ? fakeMouseMoveLongInterval : fakeMouseMoveShortInterval);
3056#endif
3057}
3058
3059void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad)
3060{
3061#if ENABLE(IOS_TOUCH_EVENTS)
3062 UNUSED_PARAM(quad);
3063#else
3064 FrameView* view = m_frame.view();
3065 if (!view)
3066 return;
3067
3068 if (!quad.containsPoint(view->windowToContents(m_lastKnownMousePosition)))
3069 return;
3070
3071 dispatchFakeMouseMoveEventSoon();
3072#endif
3073}
3074
3075#if !ENABLE(IOS_TOUCH_EVENTS)
3076void EventHandler::cancelFakeMouseMoveEvent()
3077{
3078 m_fakeMouseMoveEventTimer.stop();
3079}
3080
3081void EventHandler::fakeMouseMoveEventTimerFired()
3082{
3083 ASSERT(!m_mousePressed);
3084
3085 FrameView* view = m_frame.view();
3086 if (!view)
3087 return;
3088
3089 if (!m_frame.page() || !m_frame.page()->isVisible() || !m_frame.page()->focusController().isActive())
3090 return;
3091
3092 bool shiftKey;
3093 bool ctrlKey;
3094 bool altKey;
3095 bool metaKey;
3096 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
3097 PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), 0, NoTap);
3098 mouseMoved(fakeMouseMoveEvent);
3099}
3100#endif // !ENABLE(IOS_TOUCH_EVENTS)
3101
3102void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet)
3103{
3104 m_frameSetBeingResized = frameSet;
3105}
3106
3107void EventHandler::resizeLayerDestroyed()
3108{
3109 ASSERT(m_resizeLayer);
3110 m_resizeLayer = nullptr;
3111}
3112
3113void EventHandler::hoverTimerFired()
3114{
3115 m_hoverTimer.stop();
3116
3117 ASSERT(m_frame.document());
3118
3119 Ref<Frame> protectedFrame(m_frame);
3120
3121 if (auto* document = m_frame.document()) {
3122 if (FrameView* view = m_frame.view()) {
3123 HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent);
3124 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
3125 document->hitTest(request, result);
3126 document->updateHoverActiveState(request, result.targetElement());
3127 }
3128 }
3129}
3130
3131bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& event)
3132{
3133 // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do.
3134 // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and
3135 // lower case variants are present in a document, the correct element is matched based on Shift key state.
3136 // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively.
3137 ASSERT(!accessKeyModifiers().contains(PlatformEvent::Modifier::ShiftKey));
3138
3139 if ((event.modifiers() - PlatformEvent::Modifier::ShiftKey) != accessKeyModifiers())
3140 return false;
3141 auto* element = m_frame.document()->elementForAccessKey(event.unmodifiedText());
3142 if (!element)
3143 return false;
3144 element->accessKeyAction(false);
3145 return true;
3146}
3147
3148#if !PLATFORM(MAC)
3149bool EventHandler::needsKeyboardEventDisambiguationQuirks() const
3150{
3151 return false;
3152}
3153#endif
3154
3155#if ENABLE(FULLSCREEN_API)
3156bool EventHandler::isKeyEventAllowedInFullScreen(const PlatformKeyboardEvent& keyEvent) const
3157{
3158 Document* document = m_frame.document();
3159 if (document->fullscreenManager().isFullscreenKeyboardInputAllowed())
3160 return true;
3161
3162 if (keyEvent.type() == PlatformKeyboardEvent::Char) {
3163 if (keyEvent.text().length() != 1)
3164 return false;
3165 UChar character = keyEvent.text()[0];
3166 return character == ' ';
3167 }
3168
3169 int keyCode = keyEvent.windowsVirtualKeyCode();
3170 return (keyCode >= VK_BACK && keyCode <= VK_CAPITAL)
3171 || (keyCode >= VK_SPACE && keyCode <= VK_DELETE)
3172 || (keyCode >= VK_OEM_1 && keyCode <= VK_OEM_PLUS)
3173 || (keyCode >= VK_MULTIPLY && keyCode <= VK_OEM_8);
3174}
3175#endif
3176
3177bool EventHandler::keyEvent(const PlatformKeyboardEvent& keyEvent)
3178{
3179 Document* topDocument = m_frame.document() ? &m_frame.document()->topDocument() : nullptr;
3180 MonotonicTime savedLastHandledUserGestureTimestamp;
3181 bool savedUserDidInteractWithPage = topDocument ? topDocument->userDidInteractWithPage() : false;
3182
3183 if (m_frame.document())
3184 savedLastHandledUserGestureTimestamp = m_frame.document()->lastHandledUserGestureTimestamp();
3185
3186 bool wasHandled = internalKeyEvent(keyEvent);
3187
3188 // If the key event was not handled, do not treat it as user interaction with the page.
3189 if (topDocument) {
3190 if (!wasHandled)
3191 topDocument->setUserDidInteractWithPage(savedUserDidInteractWithPage);
3192 else
3193 ResourceLoadObserver::shared().logUserInteractionWithReducedTimeResolution(*topDocument);
3194 }
3195
3196 if (!wasHandled && m_frame.document())
3197 m_frame.document()->updateLastHandledUserGestureTimestamp(savedLastHandledUserGestureTimestamp);
3198
3199 return wasHandled;
3200}
3201
3202void EventHandler::capsLockStateMayHaveChanged() const
3203{
3204 auto* focusedElement = m_frame.document()->focusedElement();
3205 if (!is<HTMLInputElement>(focusedElement))
3206 return;
3207 downcast<HTMLInputElement>(*focusedElement).capsLockStateMayHaveChanged();
3208}
3209
3210bool EventHandler::internalKeyEvent(const PlatformKeyboardEvent& initialKeyEvent)
3211{
3212 Ref<Frame> protectedFrame(m_frame);
3213 RefPtr<FrameView> protector(m_frame.view());
3214
3215 LOG(Editing, "EventHandler %p keyEvent (text %s keyIdentifier %s)", this, initialKeyEvent.text().utf8().data(), initialKeyEvent.keyIdentifier().utf8().data());
3216
3217#if ENABLE(POINTER_LOCK)
3218 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE && m_frame.page()->pointerLockController().element()) {
3219 m_frame.page()->pointerLockController().requestPointerUnlockAndForceCursorVisible();
3220 }
3221#endif
3222
3223 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) {
3224 if (auto* page = m_frame.page()) {
3225 if (auto* validationMessageClient = page->validationMessageClient())
3226 validationMessageClient->hideAnyValidationMessage();
3227 }
3228 }
3229
3230#if ENABLE(FULLSCREEN_API)
3231 if (m_frame.document()->fullscreenManager().isFullscreen()) {
3232 if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) {
3233 m_frame.document()->fullscreenManager().cancelFullscreen();
3234 return true;
3235 }
3236
3237 if (!isKeyEventAllowedInFullScreen(initialKeyEvent))
3238 return false;
3239 }
3240#endif
3241
3242 if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL)
3243 capsLockStateMayHaveChanged();
3244
3245#if ENABLE(PAN_SCROLLING)
3246 if (m_frame.mainFrame().eventHandler().panScrollInProgress()) {
3247 // If a key is pressed while the panScroll is in progress then we want to stop
3248 if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent.type() == PlatformEvent::RawKeyDown)
3249 stopAutoscrollTimer();
3250
3251 // If we were in panscroll mode, we swallow the key event
3252 return true;
3253 }
3254#endif
3255
3256 // Check for cases where we are too early for events -- possible unmatched key up
3257 // from pressing return in the location bar.
3258 RefPtr<Element> element = eventTargetElementForDocument(m_frame.document());
3259 if (!element)
3260 return false;
3261
3262 UserGestureType gestureType = UserGestureType::Other;
3263 if (initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE)
3264 gestureType = UserGestureType::EscapeKey;
3265
3266 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document(), gestureType, UserGestureIndicator::ProcessInteractionStyle::Delayed);
3267 UserTypingGestureIndicator typingGestureIndicator(m_frame);
3268
3269 if (FrameView* view = m_frame.view())
3270 view->disableLayerFlushThrottlingTemporarilyForInteraction();
3271
3272 // FIXME (bug 68185): this call should be made at another abstraction layer
3273 m_frame.loader().resetMultipleFormSubmissionProtection();
3274
3275 // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match.
3276 // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict
3277 // with access keys. Then we dispatch keydown, but suppress its default handling.
3278 // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatching a keypress event for WM_SYSCHAR messages.
3279 // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events.
3280 bool matchedAnAccessKey = false;
3281 if (initialKeyEvent.type() == PlatformEvent::KeyDown)
3282 matchedAnAccessKey = handleAccessKey(initialKeyEvent);
3283
3284 // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch.
3285 if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char)
3286 return !element->dispatchKeyEvent(initialKeyEvent);
3287
3288 bool backwardCompatibilityMode = needsKeyboardEventDisambiguationQuirks();
3289
3290 PlatformKeyboardEvent keyDownEvent = initialKeyEvent;
3291 if (keyDownEvent.type() != PlatformEvent::RawKeyDown)
3292 keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown, backwardCompatibilityMode);
3293 auto keydown = KeyboardEvent::create(keyDownEvent, &m_frame.windowProxy());
3294 if (matchedAnAccessKey)
3295 keydown->preventDefault();
3296 keydown->setTarget(element);
3297
3298 if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) {
3299 element->dispatchEvent(keydown);
3300 // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame.
3301 bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame();
3302 return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3303 }
3304
3305 // Run input method in advance of DOM event handling. This may result in the IM
3306 // modifying the page prior the keydown event, but this behaviour is necessary
3307 // in order to match IE:
3308 // 1. preventing default handling of keydown and keypress events has no effect on IM input;
3309 // 2. if an input method handles the event, its keyCode is set to 229 in keydown event.
3310 m_frame.editor().handleInputMethodKeydown(keydown.get());
3311
3312 bool handledByInputMethod = keydown->defaultHandled();
3313
3314 if (handledByInputMethod) {
3315 keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode);
3316 keydown = KeyboardEvent::create(keyDownEvent, &m_frame.windowProxy());
3317 keydown->setTarget(element);
3318 keydown->setIsDefaultEventHandlerIgnored();
3319 }
3320
3321 if (accessibilityPreventsEventPropagation(keydown))
3322 keydown->stopPropagation();
3323
3324 element->dispatchEvent(keydown);
3325 if (handledByInputMethod)
3326 return true;
3327
3328 // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame.
3329 bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame();
3330 bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3331 if (keydownResult && !backwardCompatibilityMode)
3332 return keydownResult;
3333
3334 // Focus may have changed during keydown handling, so refetch element.
3335 // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original element.
3336 if (!keydownResult) {
3337 element = eventTargetElementForDocument(m_frame.document());
3338 if (!element)
3339 return false;
3340 }
3341
3342 PlatformKeyboardEvent keyPressEvent = initialKeyEvent;
3343 keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char, backwardCompatibilityMode);
3344 if (keyPressEvent.text().isEmpty())
3345 return keydownResult;
3346 auto keypress = KeyboardEvent::create(keyPressEvent, &m_frame.windowProxy());
3347 keypress->setTarget(element);
3348 if (keydownResult)
3349 keypress->preventDefault();
3350#if PLATFORM(COCOA)
3351 keypress->keypressCommands() = keydown->keypressCommands();
3352#endif
3353 element->dispatchEvent(keypress);
3354
3355 return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled();
3356}
3357
3358static FocusDirection focusDirectionForKey(const AtomString& keyIdentifier)
3359{
3360 static NeverDestroyed<AtomString> Down("Down", AtomString::ConstructFromLiteral);
3361 static NeverDestroyed<AtomString> Up("Up", AtomString::ConstructFromLiteral);
3362 static NeverDestroyed<AtomString> Left("Left", AtomString::ConstructFromLiteral);
3363 static NeverDestroyed<AtomString> Right("Right", AtomString::ConstructFromLiteral);
3364
3365 FocusDirection retVal = FocusDirectionNone;
3366
3367 if (keyIdentifier == Down)
3368 retVal = FocusDirectionDown;
3369 else if (keyIdentifier == Up)
3370 retVal = FocusDirectionUp;
3371 else if (keyIdentifier == Left)
3372 retVal = FocusDirectionLeft;
3373 else if (keyIdentifier == Right)
3374 retVal = FocusDirectionRight;
3375
3376 return retVal;
3377}
3378
3379static void setInitialKeyboardSelection(Frame& frame, SelectionDirection direction)
3380{
3381 Document* document = frame.document();
3382 if (!document)
3383 return;
3384
3385 FrameSelection& selection = frame.selection();
3386
3387 if (!selection.isNone())
3388 return;
3389
3390 Element* focusedElement = document->focusedElement();
3391 VisiblePosition visiblePosition;
3392
3393 switch (direction) {
3394 case DirectionBackward:
3395 case DirectionLeft:
3396 if (focusedElement)
3397 visiblePosition = VisiblePosition(positionBeforeNode(focusedElement));
3398 else
3399 visiblePosition = endOfDocument(document);
3400 break;
3401 case DirectionForward:
3402 case DirectionRight:
3403 if (focusedElement)
3404 visiblePosition = VisiblePosition(positionAfterNode(focusedElement));
3405 else
3406 visiblePosition = startOfDocument(document);
3407 break;
3408 }
3409
3410 AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
3411 selection.setSelection(visiblePosition, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent);
3412}
3413
3414static void handleKeyboardSelectionMovement(Frame& frame, KeyboardEvent& event)
3415{
3416 FrameSelection& selection = frame.selection();
3417
3418 bool isCommanded = event.getModifierState("Meta");
3419 bool isOptioned = event.getModifierState("Alt");
3420 bool isSelection = !selection.isNone();
3421
3422 FrameSelection::EAlteration alternation = event.getModifierState("Shift") ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove;
3423 SelectionDirection direction = DirectionForward;
3424 TextGranularity granularity = CharacterGranularity;
3425
3426 switch (focusDirectionForKey(event.keyIdentifier())) {
3427 case FocusDirectionNone:
3428 return;
3429 case FocusDirectionForward:
3430 case FocusDirectionBackward:
3431 ASSERT_NOT_REACHED();
3432 return;
3433 case FocusDirectionUp:
3434 direction = DirectionBackward;
3435 granularity = isCommanded ? DocumentBoundary : LineGranularity;
3436 break;
3437 case FocusDirectionDown:
3438 direction = DirectionForward;
3439 granularity = isCommanded ? DocumentBoundary : LineGranularity;
3440 break;
3441 case FocusDirectionLeft:
3442 direction = DirectionLeft;
3443 granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity;
3444 break;
3445 case FocusDirectionRight:
3446 direction = DirectionRight;
3447 granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity;
3448 break;
3449 }
3450
3451 if (isSelection)
3452 selection.modify(alternation, direction, granularity, UserTriggered);
3453 else
3454 setInitialKeyboardSelection(frame, direction);
3455
3456 event.setDefaultHandled();
3457}
3458
3459void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent& event)
3460{
3461 if (event.type() == eventNames().keydownEvent) {
3462 if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
3463 handleKeyboardSelectionMovement(m_frame, event);
3464 }
3465}
3466
3467bool EventHandler::accessibilityPreventsEventPropagation(KeyboardEvent& event)
3468{
3469#if PLATFORM(COCOA)
3470 if (!AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
3471 return false;
3472
3473 if (!m_frame.settings().preventKeyboardDOMEventDispatch())
3474 return false;
3475
3476 // Check for key events that are relevant to accessibility: tab and arrows keys that change focus
3477 if (event.keyIdentifier() == "U+0009")
3478 return true;
3479 FocusDirection direction = focusDirectionForKey(event.keyIdentifier());
3480 if (direction != FocusDirectionNone)
3481 return true;
3482#else
3483 UNUSED_PARAM(event);
3484#endif
3485 return false;
3486}
3487
3488void EventHandler::defaultKeyboardEventHandler(KeyboardEvent& event)
3489{
3490 Ref<Frame> protectedFrame(m_frame);
3491
3492 if (event.type() == eventNames().keydownEvent) {
3493 m_frame.editor().handleKeyboardEvent(event);
3494 if (event.defaultHandled())
3495 return;
3496 if (event.keyIdentifier() == "U+0009")
3497 defaultTabEventHandler(event);
3498 else if (event.keyIdentifier() == "U+0008")
3499 defaultBackspaceEventHandler(event);
3500 else {
3501 FocusDirection direction = focusDirectionForKey(event.keyIdentifier());
3502 if (direction != FocusDirectionNone)
3503 defaultArrowEventHandler(direction, event);
3504 }
3505
3506 handleKeyboardSelectionMovementForAccessibility(event);
3507 }
3508 if (event.type() == eventNames().keypressEvent) {
3509 m_frame.editor().handleKeyboardEvent(event);
3510 if (event.defaultHandled())
3511 return;
3512 if (event.charCode() == ' ')
3513 defaultSpaceEventHandler(event);
3514 }
3515}
3516
3517#if ENABLE(DRAG_SUPPORT)
3518bool EventHandler::dragHysteresisExceeded(const IntPoint& floatDragViewportLocation) const
3519{
3520 FloatPoint dragViewportLocation(floatDragViewportLocation.x(), floatDragViewportLocation.y());
3521 return dragHysteresisExceeded(dragViewportLocation);
3522}
3523
3524bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation) const
3525{
3526 int threshold = GeneralDragHysteresis;
3527 switch (dragState().type) {
3528 case DragSourceActionSelection:
3529 threshold = TextDragHysteresis;
3530 break;
3531 case DragSourceActionImage:
3532#if ENABLE(ATTACHMENT_ELEMENT)
3533 case DragSourceActionAttachment:
3534#endif
3535 threshold = ImageDragHysteresis;
3536 break;
3537 case DragSourceActionLink:
3538 threshold = LinkDragHysteresis;
3539 break;
3540#if ENABLE(INPUT_TYPE_COLOR)
3541 case DragSourceActionColor:
3542 threshold = ColorDragHystersis;
3543 break;
3544#endif
3545 case DragSourceActionDHTML:
3546 break;
3547 case DragSourceActionNone:
3548 case DragSourceActionAny:
3549 ASSERT_NOT_REACHED();
3550 }
3551
3552 return mouseMovementExceedsThreshold(dragViewportLocation, threshold);
3553}
3554
3555void EventHandler::invalidateDataTransfer()
3556{
3557 if (!dragState().dataTransfer)
3558 return;
3559 dragState().dataTransfer->makeInvalidForSecurity();
3560 dragState().dataTransfer = nullptr;
3561}
3562
3563static void removeDraggedContentDocumentMarkersFromAllFramesInPage(Page& page)
3564{
3565 for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
3566 if (auto* document = frame->document())
3567 document->markers().removeMarkers(DocumentMarker::DraggedContent);
3568 }
3569
3570 if (auto* mainFrameRenderer = page.mainFrame().contentRenderer())
3571 mainFrameRenderer->repaintRootContents();
3572}
3573
3574void EventHandler::dragCancelled()
3575{
3576#if ENABLE(DATA_INTERACTION)
3577 if (auto* page = m_frame.page())
3578 removeDraggedContentDocumentMarkersFromAllFramesInPage(*page);
3579#endif
3580}
3581
3582void EventHandler::didStartDrag()
3583{
3584#if ENABLE(DATA_INTERACTION)
3585 auto dragSource = dragState().source;
3586 if (!dragSource)
3587 return;
3588
3589 auto* renderer = dragSource->renderer();
3590 if (!renderer)
3591 return;
3592
3593 RefPtr<Range> draggedContentRange;
3594 if (dragState().type & DragSourceActionSelection)
3595 draggedContentRange = m_frame.selection().selection().toNormalizedRange();
3596 else {
3597 Position startPosition(dragSource.get(), Position::PositionIsBeforeAnchor);
3598 Position endPosition(dragSource.get(), Position::PositionIsAfterAnchor);
3599 draggedContentRange = Range::create(dragSource->document(), startPosition, endPosition);
3600 }
3601
3602 if (draggedContentRange) {
3603 draggedContentRange->ownerDocument().markers().addDraggedContentMarker(*draggedContentRange);
3604 if (auto* renderer = m_frame.contentRenderer())
3605 renderer->repaintRootContents();
3606 }
3607#endif
3608}
3609
3610void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation, MayExtendDragSession mayExtendDragSession)
3611{
3612 // Send a hit test request so that RenderLayer gets a chance to update the :hover and :active pseudoclasses.
3613 HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
3614 prepareMouseEvent(request, event);
3615
3616 if (dragState().source && dragState().shouldDispatchEvents) {
3617 dragState().dataTransfer->setDestinationOperation(operation);
3618 dispatchDragSrcEvent(eventNames().dragendEvent, event);
3619 }
3620 invalidateDataTransfer();
3621
3622 if (mayExtendDragSession == MayExtendDragSession::No) {
3623 if (auto* page = m_frame.page())
3624 removeDraggedContentDocumentMarkersFromAllFramesInPage(*page);
3625 }
3626
3627 dragState().source = nullptr;
3628 // In case the drag was ended due to an escape key press we need to ensure
3629 // that consecutive mousemove events don't reinitiate the drag and drop.
3630 m_mouseDownMayStartDrag = false;
3631}
3632
3633void EventHandler::updateDragStateAfterEditDragIfNeeded(Element& rootEditableElement)
3634{
3635 // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editable element.
3636 if (dragState().source && !dragState().source->isConnected())
3637 dragState().source = &rootEditableElement;
3638}
3639
3640void EventHandler::dispatchDragSrcEvent(const AtomString& eventType, const PlatformMouseEvent& event)
3641{
3642 ASSERT(dragState().dataTransfer);
3643 dispatchDragEvent(eventType, *dragState().source, event, *dragState().dataTransfer);
3644}
3645
3646bool EventHandler::dispatchDragStartEventOnSourceElement(DataTransfer& dataTransfer)
3647{
3648 return !dispatchDragEvent(eventNames().dragstartEvent, *dragState().source, m_mouseDown, dataTransfer) && !m_frame.selection().selection().isInPasswordField();
3649}
3650
3651static bool ExactlyOneBitSet(DragSourceAction n)
3652{
3653 return n && !(n & (n - 1));
3654}
3655
3656RefPtr<Element> EventHandler::draggedElement() const
3657{
3658 return dragState().source;
3659}
3660
3661bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis)
3662{
3663 if (event.event().button() != LeftButton || event.event().type() != PlatformEvent::MouseMoved) {
3664 // If we allowed the other side of the bridge to handle a drag
3665 // last time, then m_mousePressed might still be set. So we
3666 // clear it now to make sure the next move after a drag
3667 // doesn't look like a drag.
3668 m_mousePressed = false;
3669 return false;
3670 }
3671
3672 Ref<Frame> protectedFrame(m_frame);
3673
3674 if (eventLoopHandleMouseDragged(event))
3675 return true;
3676
3677 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
3678
3679 if (m_mouseDownMayStartDrag && !dragState().source) {
3680 dragState().shouldDispatchEvents = (updateDragSourceActionsAllowed() & DragSourceActionDHTML);
3681
3682 // try to find an element that wants to be dragged
3683 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent);
3684 HitTestResult result(m_mouseDownPos);
3685 m_frame.document()->hitTest(request, result);
3686 if (m_frame.page())
3687 dragState().source = m_frame.page()->dragController().draggableElement(&m_frame, result.targetElement(), m_mouseDownPos, dragState());
3688
3689 if (!dragState().source)
3690 m_mouseDownMayStartDrag = false; // no element is draggable
3691 else
3692 m_dragMayStartSelectionInstead = (dragState().type & DragSourceActionSelection);
3693 }
3694
3695 // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
3696 // or else we bail on the dragging stuff and allow selection to occur
3697 if (m_mouseDownMayStartDrag && m_dragMayStartSelectionInstead && (dragState().type & DragSourceActionSelection) && event.event().timestamp() - m_mouseDownTimestamp < TextDragDelay) {
3698 ASSERT(event.event().type() == PlatformEvent::MouseMoved);
3699 if ((dragState().type & DragSourceActionImage)) {
3700 // ... unless the mouse is over an image, then we start dragging just the image
3701 dragState().type = DragSourceActionImage;
3702 } else if (!(dragState().type & (DragSourceActionDHTML | DragSourceActionLink))) {
3703 // ... but only bail if we're not over an unselectable element.
3704 m_mouseDownMayStartDrag = false;
3705 dragState().source = nullptr;
3706 // ... but if this was the first click in the window, we don't even want to start selection
3707 if (eventActivatedView(event.event()))
3708 m_mouseDownMayStartSelect = false;
3709 } else {
3710 // Prevent the following case from occuring:
3711 // 1. User starts a drag immediately after mouse down over an unselectable element.
3712 // 2. We enter this block and decided that since we're over an unselectable element, don't cancel the drag.
3713 // 3. The drag gets resolved as a potential selection drag below /but/ we haven't exceeded the drag hysteresis yet.
3714 // 4. We enter this block again, and since it's now marked as a selection drag, we cancel the drag.
3715 m_dragMayStartSelectionInstead = false;
3716 }
3717 }
3718
3719 if (!m_mouseDownMayStartDrag)
3720 return !mouseDownMayStartSelect() && !m_mouseDownMayStartAutoscroll;
3721 ASSERT(dragState().source);
3722
3723 if (!ExactlyOneBitSet(dragState().type)) {
3724 ASSERT(dragState().type & DragSourceActionSelection);
3725 ASSERT(ExactlyOneBitSet(static_cast<DragSourceAction>(dragState().type & ~DragSourceActionSelection)));
3726
3727 dragState().type = DragSourceActionSelection;
3728 }
3729
3730 // We are starting a text/image/url drag, so the cursor should be an arrow
3731 if (FrameView* view = m_frame.view()) {
3732 // FIXME <rdar://7577595>: Custom cursors aren't supported during drag and drop (default to pointer).
3733 view->setCursor(pointerCursor());
3734 }
3735
3736 if (checkDragHysteresis == ShouldCheckDragHysteresis && !dragHysteresisExceeded(event.event().position()))
3737 return true;
3738
3739 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
3740 invalidateClick();
3741
3742 DragOperation srcOp = DragOperationNone;
3743
3744 // This does work only if we missed a dragEnd. Do it anyway, just to make sure the old dataTransfer gets numbed.
3745 invalidateDataTransfer();
3746
3747 dragState().dataTransfer = DataTransfer::createForDrag();
3748 HasNonDefaultPasteboardData hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::No;
3749
3750 if (dragState().shouldDispatchEvents) {
3751 ASSERT(dragState().source);
3752 auto dragStartDataTransfer = DataTransfer::createForDragStartEvent(dragState().source->document());
3753 m_mouseDownMayStartDrag = dispatchDragStartEventOnSourceElement(dragStartDataTransfer);
3754 hasNonDefaultPasteboardData = dragStartDataTransfer->pasteboard().hasData() ? HasNonDefaultPasteboardData::Yes : HasNonDefaultPasteboardData::No;
3755 dragState().dataTransfer->moveDragState(WTFMove(dragStartDataTransfer));
3756
3757 if (dragState().source && dragState().type == DragSourceActionDHTML && !dragState().dataTransfer->hasDragImage()) {
3758 dragState().source->document().updateStyleIfNeeded();
3759 if (auto* renderer = dragState().source->renderer()) {
3760 auto absolutePosition = renderer->localToAbsolute();
3761 auto delta = m_mouseDownPos - roundedIntPoint(absolutePosition);
3762 dragState().dataTransfer->setDragImage(dragState().source.get(), delta.width(), delta.height());
3763 } else {
3764 dispatchDragSrcEvent(eventNames().dragendEvent, event.event());
3765 m_mouseDownMayStartDrag = false;
3766 invalidateDataTransfer();
3767 dragState().source = nullptr;
3768 return true;
3769 }
3770 }
3771
3772 dragState().dataTransfer->makeInvalidForSecurity();
3773
3774 if (m_mouseDownMayStartDrag) {
3775 // Gather values from DHTML element, if it set any.
3776 srcOp = dragState().dataTransfer->sourceOperation();
3777
3778 // Yuck, a draggedImage:moveTo: message can be fired as a result of kicking off the
3779 // drag with dragImage! Because of that dumb reentrancy, we may think we've not
3780 // started the drag when that happens. So we have to assume it's started before we kick it off.
3781 dragState().dataTransfer->setDragHasStarted();
3782 }
3783 }
3784
3785 if (m_mouseDownMayStartDrag) {
3786 Page* page = m_frame.page();
3787 m_didStartDrag = page && page->dragController().startDrag(m_frame, dragState(), srcOp, event.event(), m_mouseDownPos, hasNonDefaultPasteboardData);
3788 // In WebKit2 we could re-enter this code and start another drag.
3789 // On OS X this causes problems with the ownership of the pasteboard and the promised types.
3790 if (m_didStartDrag) {
3791 m_mouseDownMayStartDrag = false;
3792 return true;
3793 }
3794 if (dragState().source && dragState().shouldDispatchEvents) {
3795 // Drag was canned at the last minute. We owe dragSource a dragend event.
3796 dispatchDragSrcEvent(eventNames().dragendEvent, event.event());
3797 m_mouseDownMayStartDrag = false;
3798 }
3799 }
3800
3801 if (!m_mouseDownMayStartDrag) {
3802 // Something failed to start the drag, clean up.
3803 invalidateDataTransfer();
3804 dragState().source = nullptr;
3805 }
3806
3807 // No more default handling (like selection), whether we're past the hysteresis bounds or not
3808 return true;
3809}
3810#endif // ENABLE(DRAG_SUPPORT)
3811
3812bool EventHandler::mouseMovementExceedsThreshold(const FloatPoint& viewportLocation, int pointsThreshold) const
3813{
3814 FrameView* view = m_frame.view();
3815 if (!view)
3816 return false;
3817 IntPoint location = view->windowToContents(flooredIntPoint(viewportLocation));
3818 IntSize delta = location - m_mouseDownPos;
3819
3820 return abs(delta.width()) >= pointsThreshold || abs(delta.height()) >= pointsThreshold;
3821}
3822
3823bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEvent, TextEventInputType inputType)
3824{
3825 LOG(Editing, "EventHandler %p handleTextInputEvent (text %s)", this, text.utf8().data());
3826
3827 // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline),
3828 // and avoid dispatching text input events from keydown default handlers.
3829 ASSERT(!is<KeyboardEvent>(underlyingEvent) || downcast<KeyboardEvent>(*underlyingEvent).type() == eventNames().keypressEvent);
3830
3831 Ref<Frame> protectedFrame(m_frame);
3832
3833 EventTarget* target;
3834 if (underlyingEvent)
3835 target = underlyingEvent->target();
3836 else
3837 target = eventTargetElementForDocument(m_frame.document());
3838 if (!target)
3839 return false;
3840
3841 if (FrameView* view = m_frame.view())
3842 view->disableLayerFlushThrottlingTemporarilyForInteraction();
3843
3844 auto event = TextEvent::create(&m_frame.windowProxy(), text, inputType);
3845 event->setUnderlyingEvent(underlyingEvent);
3846
3847 target->dispatchEvent(event);
3848 return event->defaultHandled();
3849}
3850
3851bool EventHandler::isKeyboardOptionTab(KeyboardEvent& event)
3852{
3853 return (event.type() == eventNames().keydownEvent || event.type() == eventNames().keypressEvent)
3854 && event.altKey()
3855 && event.keyIdentifier() == "U+0009";
3856}
3857
3858bool EventHandler::eventInvertsTabsToLinksClientCallResult(KeyboardEvent& event)
3859{
3860#if PLATFORM(COCOA)
3861 return isKeyboardOptionTab(event);
3862#else
3863 UNUSED_PARAM(event);
3864 return false;
3865#endif
3866}
3867
3868bool EventHandler::tabsToLinks(KeyboardEvent* event) const
3869{
3870 // FIXME: This function needs a better name. It can be called for keypresses other than Tab when spatial navigation is enabled.
3871
3872 Page* page = m_frame.page();
3873 if (!page)
3874 return false;
3875
3876 bool tabsToLinksClientCallResult = page->chrome().client().keyboardUIMode() & KeyboardAccessTabsToLinks;
3877 return (event && eventInvertsTabsToLinksClientCallResult(*event)) ? !tabsToLinksClientCallResult : tabsToLinksClientCallResult;
3878}
3879
3880void EventHandler::defaultTextInputEventHandler(TextEvent& event)
3881{
3882 if (m_frame.editor().handleTextEvent(event))
3883 event.setDefaultHandled();
3884}
3885
3886
3887void EventHandler::defaultSpaceEventHandler(KeyboardEvent& event)
3888{
3889 Ref<Frame> protectedFrame(m_frame);
3890
3891 ASSERT(event.type() == eventNames().keypressEvent);
3892
3893 if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey())
3894 return;
3895
3896 ScrollLogicalDirection direction = event.shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward;
3897 if (logicalScrollOverflow(direction, ScrollByPage)) {
3898 event.setDefaultHandled();
3899 return;
3900 }
3901
3902 FrameView* view = m_frame.view();
3903 if (!view)
3904 return;
3905
3906 if (view->logicalScroll(direction, ScrollByPage))
3907 event.setDefaultHandled();
3908}
3909
3910void EventHandler::defaultBackspaceEventHandler(KeyboardEvent& event)
3911{
3912 ASSERT(event.type() == eventNames().keydownEvent);
3913
3914 if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey())
3915 return;
3916
3917 if (!m_frame.editor().behavior().shouldNavigateBackOnBackspace())
3918 return;
3919
3920 Page* page = m_frame.page();
3921 if (!page)
3922 return;
3923
3924 if (!m_frame.settings().backspaceKeyNavigationEnabled())
3925 return;
3926
3927 bool handledEvent = false;
3928
3929 if (event.shiftKey())
3930 handledEvent = page->backForward().goForward();
3931 else
3932 handledEvent = page->backForward().goBack();
3933
3934 if (handledEvent)
3935 event.setDefaultHandled();
3936}
3937
3938
3939void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent& event)
3940{
3941 ASSERT(event.type() == eventNames().keydownEvent);
3942
3943 if (event.ctrlKey() || event.metaKey() || event.altGraphKey() || event.shiftKey())
3944 return;
3945
3946 Page* page = m_frame.page();
3947 if (!page)
3948 return;
3949
3950 if (!isSpatialNavigationEnabled(&m_frame))
3951 return;
3952
3953 // Arrows and other possible directional navigation keys can be used in design
3954 // mode editing.
3955 if (m_frame.document()->inDesignMode())
3956 return;
3957
3958 if (page->focusController().advanceFocus(focusDirection, &event))
3959 event.setDefaultHandled();
3960}
3961
3962void EventHandler::defaultTabEventHandler(KeyboardEvent& event)
3963{
3964 Ref<Frame> protectedFrame(m_frame);
3965
3966 ASSERT(event.type() == eventNames().keydownEvent);
3967
3968 // We should only advance focus on tabs if no special modifier keys are held down.
3969 if (event.ctrlKey() || event.metaKey() || event.altGraphKey())
3970 return;
3971
3972 Page* page = m_frame.page();
3973 if (!page)
3974 return;
3975 if (!page->tabKeyCyclesThroughElements())
3976 return;
3977
3978 FocusDirection focusDirection = event.shiftKey() ? FocusDirectionBackward : FocusDirectionForward;
3979
3980 // Tabs can be used in design mode editing.
3981 if (m_frame.document()->inDesignMode())
3982 return;
3983
3984 if (page->focusController().advanceFocus(focusDirection, &event))
3985 event.setDefaultHandled();
3986}
3987
3988void EventHandler::sendScrollEvent()
3989{
3990 Ref<Frame> protectedFrame(m_frame);
3991 setFrameWasScrolledByUser();
3992 if (m_frame.view() && m_frame.document())
3993 m_frame.document()->eventQueue().enqueueOrDispatchScrollEvent(*m_frame.document());
3994}
3995
3996void EventHandler::setFrameWasScrolledByUser()
3997{
3998 FrameView* v = m_frame.view();
3999 if (v)
4000 v->setWasScrolledByUser(true);
4001}
4002
4003bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mouseEvent, Scrollbar* scrollbar)
4004{
4005 if (!scrollbar || !scrollbar->enabled())
4006 return false;
4007 setFrameWasScrolledByUser();
4008 return scrollbar->mouseDown(mouseEvent.event());
4009}
4010
4011// If scrollbar (under mouse) is different from last, send a mouse exited.
4012void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, SetOrClearLastScrollbar setOrClear)
4013{
4014 if (m_lastScrollbarUnderMouse != scrollbar) {
4015 // Send mouse exited to the old scrollbar.
4016 if (m_lastScrollbarUnderMouse)
4017 m_lastScrollbarUnderMouse->mouseExited();
4018
4019 // Send mouse entered if we're setting a new scrollbar.
4020 if (scrollbar && setOrClear == SetOrClearLastScrollbar::Set) {
4021 scrollbar->mouseEntered();
4022 m_lastScrollbarUnderMouse = makeWeakPtr(*scrollbar);
4023 } else
4024 m_lastScrollbarUnderMouse = nullptr;
4025 }
4026}
4027
4028#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
4029static const AtomString& eventNameForTouchPointState(PlatformTouchPoint::State state)
4030{
4031 switch (state) {
4032 case PlatformTouchPoint::TouchReleased:
4033 return eventNames().touchendEvent;
4034 case PlatformTouchPoint::TouchCancelled:
4035 return eventNames().touchcancelEvent;
4036 case PlatformTouchPoint::TouchPressed:
4037 return eventNames().touchstartEvent;
4038 case PlatformTouchPoint::TouchMoved:
4039 return eventNames().touchmoveEvent;
4040 case PlatformTouchPoint::TouchStationary:
4041 // TouchStationary state is not converted to touch events, so fall through to assert.
4042 default:
4043 ASSERT_NOT_REACHED();
4044 return emptyAtom();
4045 }
4046}
4047
4048static HitTestResult hitTestResultInFrame(Frame* frame, const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType)
4049{
4050 HitTestResult result(point);
4051
4052 if (!frame || !frame->contentRenderer())
4053 return result;
4054
4055 if (frame->view()) {
4056 IntRect rect = frame->view()->visibleContentRect();
4057 if (!rect.contains(roundedIntPoint(point)))
4058 return result;
4059 }
4060 frame->document()->hitTest(HitTestRequest(hitType), result);
4061 return result;
4062}
4063
4064bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
4065{
4066 Ref<Frame> protectedFrame(m_frame);
4067
4068 // First build up the lists to use for the 'touches', 'targetTouches' and 'changedTouches' attributes
4069 // in the JS event. See http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/
4070 // for an overview of how these lists fit together.
4071
4072 // Holds the complete set of touches on the screen and will be used as the 'touches' list in the JS event.
4073 RefPtr<TouchList> touches = TouchList::create();
4074
4075 // A different view on the 'touches' list above, filtered and grouped by event target. Used for the
4076 // 'targetTouches' list in the JS event.
4077 typedef HashMap<EventTarget*, RefPtr<TouchList>> TargetTouchesMap;
4078 TargetTouchesMap touchesByTarget;
4079
4080 // Array of touches per state, used to assemble the 'changedTouches' list in the JS event.
4081 typedef HashSet<RefPtr<EventTarget>> EventTargetSet;
4082 struct {
4083 // The touches corresponding to the particular change state this struct instance represents.
4084 RefPtr<TouchList> m_touches;
4085 // Set of targets involved in m_touches.
4086 EventTargetSet m_targets;
4087 } changedTouches[PlatformTouchPoint::TouchStateEnd];
4088
4089 const Vector<PlatformTouchPoint>& points = event.touchPoints();
4090
4091 UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document());
4092
4093 bool freshTouchEvents = true;
4094 bool allTouchReleased = true;
4095 for (auto& point : points) {
4096 if (point.state() != PlatformTouchPoint::TouchPressed)
4097 freshTouchEvents = false;
4098 if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled)
4099 allTouchReleased = false;
4100 }
4101
4102 for (auto& point : points) {
4103 PlatformTouchPoint::State pointState = point.state();
4104 LayoutPoint pagePoint = documentPointForWindowPoint(m_frame, point.pos());
4105
4106 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent;
4107 // The HitTestRequest types used for mouse events map quite adequately
4108 // to touch events. Note that in addition to meaning that the hit test
4109 // should affect the active state of the current node if necessary,
4110 // HitTestRequest::Active signifies that the hit test is taking place
4111 // with the mouse (or finger in this case) being pressed.
4112 switch (pointState) {
4113 case PlatformTouchPoint::TouchPressed:
4114 hitType |= HitTestRequest::Active;
4115 break;
4116 case PlatformTouchPoint::TouchMoved:
4117 hitType |= HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::ReadOnly;
4118 break;
4119 case PlatformTouchPoint::TouchReleased:
4120 case PlatformTouchPoint::TouchCancelled:
4121 hitType |= HitTestRequest::Release;
4122 break;
4123 case PlatformTouchPoint::TouchStationary:
4124 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
4125 break;
4126 default:
4127 ASSERT_NOT_REACHED();
4128 break;
4129 }
4130
4131 if (shouldGesturesTriggerActive())
4132 hitType |= HitTestRequest::ReadOnly;
4133
4134 // Increment the platform touch id by 1 to avoid storing a key of 0 in the hashmap.
4135 unsigned touchPointTargetKey = point.id() + 1;
4136 RefPtr<EventTarget> touchTarget;
4137 if (pointState == PlatformTouchPoint::TouchPressed) {
4138 HitTestResult result;
4139 if (freshTouchEvents) {
4140 result = hitTestResultAtPoint(pagePoint, hitType);
4141 m_originatingTouchPointTargetKey = touchPointTargetKey;
4142 } else if (m_originatingTouchPointDocument.get() && m_originatingTouchPointDocument->frame()) {
4143 LayoutPoint pagePointInOriginatingDocument = documentPointForWindowPoint(*m_originatingTouchPointDocument->frame(), point.pos());
4144 result = hitTestResultInFrame(m_originatingTouchPointDocument->frame(), pagePointInOriginatingDocument, hitType);
4145 if (!result.innerNode())
4146 continue;
4147 } else
4148 continue;
4149
4150 Element* element = result.targetElement();
4151 ASSERT(element);
4152
4153 if (element && InspectorInstrumentation::handleTouchEvent(m_frame, *element))
4154 return true;
4155
4156 Document& doc = element->document();
4157 // Record the originating touch document even if it does not have a touch listener.
4158 if (freshTouchEvents) {
4159 m_originatingTouchPointDocument = &doc;
4160 freshTouchEvents = false;
4161 }
4162 if (!doc.hasTouchEventHandlers())
4163 continue;
4164 m_originatingTouchPointTargets.set(touchPointTargetKey, element);
4165 touchTarget = element;
4166 } else if (pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled) {
4167 // No need to perform a hit-test since we only need to unset :hover and :active states.
4168 if (!shouldGesturesTriggerActive() && allTouchReleased)
4169 m_frame.document()->updateHoverActiveState(hitType, 0);
4170 if (touchPointTargetKey == m_originatingTouchPointTargetKey)
4171 m_originatingTouchPointTargetKey = 0;
4172
4173 // The target should be the original target for this touch, so get it from the hashmap. As it's a release or cancel
4174 // we also remove it from the map.
4175 touchTarget = m_originatingTouchPointTargets.take(touchPointTargetKey);
4176 } else
4177 // No hittest is performed on move or stationary, since the target is not allowed to change anyway.
4178 touchTarget = m_originatingTouchPointTargets.get(touchPointTargetKey);
4179
4180 if (!is<Node>(touchTarget))
4181 continue;
4182 auto& document = downcast<Node>(*touchTarget).document();
4183 if (!document.hasTouchEventHandlers())
4184 continue;
4185 auto* targetFrame = document.frame();
4186 if (!targetFrame)
4187 continue;
4188
4189 if (&m_frame != targetFrame) {
4190 // pagePoint should always be relative to the target elements containing frame.
4191 pagePoint = documentPointForWindowPoint(*targetFrame, point.pos());
4192 }
4193
4194 float scaleFactor = targetFrame->pageZoomFactor() * targetFrame->frameScaleFactor();
4195
4196 int adjustedPageX = lroundf(pagePoint.x() / scaleFactor);
4197 int adjustedPageY = lroundf(pagePoint.y() / scaleFactor);
4198
4199 auto touch = Touch::create(targetFrame, touchTarget.get(), point.id(),
4200 point.screenPos().x(), point.screenPos().y(), adjustedPageX, adjustedPageY,
4201 point.radiusX(), point.radiusY(), point.rotationAngle(), point.force());
4202
4203 // Ensure this target's touch list exists, even if it ends up empty, so it can always be passed to TouchEvent::Create below.
4204 TargetTouchesMap::iterator targetTouchesIterator = touchesByTarget.find(touchTarget.get());
4205 if (targetTouchesIterator == touchesByTarget.end())
4206 targetTouchesIterator = touchesByTarget.set(touchTarget.get(), TouchList::create()).iterator;
4207
4208 // touches and targetTouches should only contain information about touches still on the screen, so if this point is
4209 // released or cancelled it will only appear in the changedTouches list.
4210 if (pointState != PlatformTouchPoint::TouchReleased && pointState != PlatformTouchPoint::TouchCancelled) {
4211 touches->append(touch.copyRef());
4212 targetTouchesIterator->value->append(touch.copyRef());
4213 }
4214
4215 // Now build up the correct list for changedTouches.
4216 // Note that any touches that are in the TouchStationary state (e.g. if
4217 // the user had several points touched but did not move them all) should
4218 // never be in the changedTouches list so we do not handle them explicitly here.
4219 // See https://bugs.webkit.org/show_bug.cgi?id=37609 for further discussion
4220 // about the TouchStationary state.
4221 if (pointState != PlatformTouchPoint::TouchStationary) {
4222 ASSERT(pointState < PlatformTouchPoint::TouchStateEnd);
4223 if (!changedTouches[pointState].m_touches)
4224 changedTouches[pointState].m_touches = TouchList::create();
4225 changedTouches[pointState].m_touches->append(WTFMove(touch));
4226 changedTouches[pointState].m_targets.add(touchTarget);
4227 }
4228 }
4229 m_touchPressed = touches->length() > 0;
4230 if (allTouchReleased)
4231 m_originatingTouchPointDocument = nullptr;
4232
4233 // Now iterate the changedTouches list and m_targets within it, sending events to the targets as required.
4234 bool swallowedEvent = false;
4235 RefPtr<TouchList> emptyList = TouchList::create();
4236 for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state) {
4237 if (!changedTouches[state].m_touches)
4238 continue;
4239
4240 // When sending a touch cancel event, use empty touches and targetTouches lists.
4241 bool isTouchCancelEvent = (state == PlatformTouchPoint::TouchCancelled);
4242 RefPtr<TouchList>& effectiveTouches(isTouchCancelEvent ? emptyList : touches);
4243 const AtomString& stateName(eventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state)));
4244
4245 for (auto& target : changedTouches[state].m_targets) {
4246 ASSERT(is<Node>(target));
4247
4248 RefPtr<TouchList> targetTouches(isTouchCancelEvent ? emptyList : touchesByTarget.get(target.get()));
4249 ASSERT(targetTouches);
4250
4251 Ref<TouchEvent> touchEvent = TouchEvent::create(effectiveTouches.get(), targetTouches.get(), changedTouches[state].m_touches.get(),
4252 stateName, downcast<Node>(*target).document().windowProxy(), { }, event.modifiers());
4253 target->dispatchEvent(touchEvent);
4254 swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled();
4255 }
4256 }
4257
4258 return swallowedEvent;
4259}
4260#endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
4261
4262#if ENABLE(TOUCH_EVENTS)
4263bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& platformMouseEvent)
4264{
4265#if ENABLE(IOS_TOUCH_EVENTS)
4266 UNUSED_PARAM(platformMouseEvent);
4267 return false;
4268#else
4269 if (!m_frame.settings().isTouchEventEmulationEnabled())
4270 return false;
4271
4272 PlatformEvent::Type eventType = platformMouseEvent.type();
4273 if (eventType != PlatformEvent::MouseMoved && eventType != PlatformEvent::MousePressed && eventType != PlatformEvent::MouseReleased)
4274 return false;
4275
4276 HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
4277 MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
4278 if (mouseEvent.scrollbar() || subframeForHitTestResult(mouseEvent))
4279 return false;
4280
4281 // The order is important. This check should follow the subframe test: http://webkit.org/b/111292.
4282 if (eventType == PlatformEvent::MouseMoved && !m_touchPressed)
4283 return true;
4284
4285 SyntheticSingleTouchEvent touchEvent(platformMouseEvent);
4286 return handleTouchEvent(touchEvent);
4287#endif
4288}
4289#endif // ENABLE(TOUCH_EVENTS)
4290
4291void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event)
4292{
4293 m_mousePositionIsUnknown = false;
4294 m_lastKnownMousePosition = event.position();
4295 m_lastKnownMouseGlobalPosition = event.globalPosition();
4296}
4297
4298void EventHandler::setImmediateActionStage(ImmediateActionStage stage)
4299{
4300 m_immediateActionStage = stage;
4301}
4302
4303#if !PLATFORM(COCOA)
4304OptionSet<PlatformEvent::Modifier> EventHandler::accessKeyModifiers()
4305{
4306 return PlatformEvent::Modifier::AltKey;
4307}
4308
4309bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
4310{
4311 subframe->eventHandler().handleMousePressEvent(mev.event());
4312 return true;
4313}
4314
4315bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
4316{
4317 subframe->eventHandler().handleMouseReleaseEvent(mev.event());
4318 return true;
4319}
4320
4321bool EventHandler::widgetDidHandleWheelEvent(const PlatformWheelEvent& event, Widget& widget)
4322{
4323 if (!is<FrameView>(widget))
4324 return false;
4325
4326 return downcast<FrameView>(widget).frame().eventHandler().handleWheelEvent(event);
4327}
4328
4329bool EventHandler::tabsToAllFormControls(KeyboardEvent*) const
4330{
4331 // We always allow tabs to all controls
4332 return true;
4333}
4334
4335bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget)
4336{
4337 return passMouseDownEventToWidget(renderWidget->widget());
4338}
4339
4340bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
4341{
4342 // Figure out which view to send the event to.
4343 RenderObject* target = event.targetNode() ? event.targetNode()->renderer() : nullptr;
4344 if (!is<RenderWidget>(target))
4345 return false;
4346 return passMouseDownEventToWidget(downcast<RenderWidget>(*target).widget());
4347}
4348
4349bool EventHandler::passMouseDownEventToWidget(Widget*)
4350{
4351 notImplemented();
4352 return false;
4353}
4354
4355void EventHandler::focusDocumentView()
4356{
4357 if (Page* page = m_frame.page())
4358 page->focusController().setFocusedFrame(&m_frame);
4359}
4360#endif // !PLATFORM(COCOA)
4361
4362#if !PLATFORM(COCOA) && !PLATFORM(WIN)
4363bool EventHandler::eventActivatedView(const PlatformMouseEvent&) const
4364{
4365 notImplemented();
4366 return false;
4367}
4368
4369bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
4370{
4371 subframe->eventHandler().handleMouseMoveEvent(mev.event(), hoveredNode);
4372 return true;
4373}
4374#endif // !PLATFORM(COCOA) && !PLATFORM(WIN)
4375
4376} // namespace WebCore
4377