diff options
Diffstat (limited to 'Source/WebCore/page/EventHandler.cpp')
-rw-r--r-- | Source/WebCore/page/EventHandler.cpp | 2224 |
1 files changed, 1277 insertions, 947 deletions
diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp index bf0f7631c..9d48975cb 100644 --- a/Source/WebCore/page/EventHandler.cpp +++ b/Source/WebCore/page/EventHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2015 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) * @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,22 +28,17 @@ #include "config.h" #include "EventHandler.h" -#include "AXObjectCache.h" #include "AutoscrollController.h" #include "BackForwardController.h" #include "CachedImage.h" #include "Chrome.h" #include "ChromeClient.h" -#include "Cursor.h" #include "CursorList.h" -#include "Document.h" -#include "DocumentEventQueue.h" #include "DragController.h" #include "DragState.h" #include "Editor.h" #include "EditorClient.h" #include "EventNames.h" -#include "ExceptionCodePlaceholder.h" #include "FileList.h" #include "FloatPoint.h" #include "FloatRect.h" @@ -52,9 +47,10 @@ #include "FrameSelection.h" #include "FrameTree.h" #include "FrameView.h" -#include "htmlediting.h" -#include "HTMLFrameElementBase.h" +#include "HTMLFrameElement.h" #include "HTMLFrameSetElement.h" +#include "HTMLHtmlElement.h" +#include "HTMLIFrameElement.h" #include "HTMLInputElement.h" #include "HTMLNames.h" #include "HitTestRequest.h" @@ -62,21 +58,27 @@ #include "Image.h" #include "InspectorInstrumentation.h" #include "KeyboardEvent.h" +#include "Logging.h" #include "MainFrame.h" #include "MouseEvent.h" #include "MouseEventWithHitTestResults.h" #include "Page.h" +#include "PageOverlayController.h" #include "PlatformEvent.h" #include "PlatformKeyboardEvent.h" #include "PlatformWheelEvent.h" #include "PluginDocument.h" #include "RenderFrameSet.h" #include "RenderLayer.h" +#include "RenderListBox.h" +#include "RenderNamedFlowThread.h" #include "RenderTextControlSingleLine.h" #include "RenderView.h" #include "RenderWidget.h" #include "RuntimeApplicationChecks.h" -#include "ScrollAnimator.h" +#include "SVGDocument.h" +#include "SVGNames.h" +#include "ScrollLatchingState.h" #include "Scrollbar.h" #include "Settings.h" #include "ShadowRoot.h" @@ -86,33 +88,36 @@ #include "TextIterator.h" #include "UserGestureIndicator.h" #include "UserTypingGestureIndicator.h" +#include "ValidationMessageClient.h" +#include "VisibleUnits.h" #include "WheelEvent.h" +#include "WheelEventDeltaFilter.h" #include "WindowsKeyboardCodes.h" +#include "htmlediting.h" #include <wtf/Assertions.h> #include <wtf/CurrentTime.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> -#include <wtf/TemporaryChange.h> -#include <wtf/WeakPtr.h> -#if ENABLE(SVG) -#include "SVGDocument.h" -#include "SVGElementInstance.h" -#include "SVGNames.h" -#include "SVGUseElement.h" -#endif - -#if ENABLE(TOUCH_EVENTS) #if ENABLE(IOS_TOUCH_EVENTS) #include "PlatformTouchEventIOS.h" -#else -#include "PlatformTouchEvent.h" #endif + +#if ENABLE(TOUCH_EVENTS) #include "TouchEvent.h" #include "TouchList.h" #endif -#if ENABLE(CSS_IMAGE_SET) -#include "StyleCachedImageSet.h" +#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) +#include "PlatformTouchEvent.h" +#endif + +#if ENABLE(MAC_GESTURE_EVENTS) +#include "PlatformGestureEventMac.h" +#endif + +#if ENABLE(POINTER_LOCK) +#include "PointerLockController.h" #endif namespace WebCore { @@ -127,9 +132,12 @@ const int LinkDragHysteresis = 40; const int ImageDragHysteresis = 5; const int TextDragHysteresis = 3; const int GeneralDragHysteresis = 3; +#if PLATFORM(COCOA) +const double EventHandler::TextDragDelay = 0.15; +#endif #endif // ENABLE(DRAG_SUPPORT) -#if ENABLE(IOS_GESTURE_EVENTS) +#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) const float GestureUnknown = 0; #endif @@ -142,16 +150,16 @@ const unsigned InvalidTouchIdentifier = 0; // IE sends VK_PROCESSKEY which has value 229; const int CompositionEventKeyCode = 229; -#if ENABLE(SVG) using namespace SVGNames; -#endif +#if !ENABLE(IOS_TOUCH_EVENTS) // The amount of time to wait before sending a fake mouse event, triggered // during a scroll. The short interval is used if the content responds to the mouse events // in fakeMouseMoveDurationThreshold or less, otherwise the long interval is used. const double fakeMouseMoveDurationThreshold = 0.01; const double fakeMouseMoveShortInterval = 0.1; const double fakeMouseMoveLongInterval = 0.25; +#endif #if ENABLE(CURSOR_SUPPORT) // The amount of time to wait for a cursor update on style and layout changes @@ -168,21 +176,6 @@ const int maximumCursorSize = 128; const double minimumCursorScale = 0.001; #endif -enum NoCursorChangeType { NoCursorChange }; - -class OptionalCursor { -public: - OptionalCursor(NoCursorChangeType) : m_isCursorChange(false) { } - OptionalCursor(const Cursor& cursor) : m_isCursorChange(true), m_cursor(cursor) { } - - bool isCursorChange() const { return m_isCursorChange; } - const Cursor& cursor() const { ASSERT(m_isCursorChange); return m_cursor; } - -private: - bool m_isCursorChange; - Cursor m_cursor; -}; - class MaximumDurationTracker { public: explicit MaximumDurationTracker(double *maxDuration) @@ -282,16 +275,63 @@ static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned del } } -static inline bool scrollNode(float delta, ScrollGranularity granularity, ScrollDirection positiveDirection, ScrollDirection negativeDirection, Node* node, Element** stopElement, const IntPoint& wheelEventAbsolutePoint) +static inline bool didScrollInScrollableArea(ScrollableArea* scrollableArea, WheelEvent& wheelEvent) { - if (!delta) - return false; - if (!node->renderer()) + ScrollGranularity scrollGranularity = wheelGranularityToScrollGranularity(wheelEvent.deltaMode()); + bool didHandleWheelEvent = false; + if (float absoluteDelta = std::abs(wheelEvent.deltaX())) + didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaX() > 0 ? ScrollRight : ScrollLeft, scrollGranularity, absoluteDelta); + + if (float absoluteDelta = std::abs(wheelEvent.deltaY())) + didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaY() > 0 ? ScrollDown : ScrollUp, scrollGranularity, absoluteDelta); + + return didHandleWheelEvent; +} + +static inline bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, WheelEvent& wheelEvent, Element** stopElement, const FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity) +{ + bool shouldHandleEvent = wheelEvent.deltaX() || wheelEvent.deltaY(); +#if PLATFORM(MAC) + shouldHandleEvent |= wheelEvent.phase() == PlatformWheelEventPhaseEnded; +#if ENABLE(CSS_SCROLL_SNAP) + shouldHandleEvent |= wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded; +#endif +#endif + if (!startNode->renderer() || !shouldHandleEvent) return false; - RenderBox* enclosingBox = node->renderer()->enclosingBox(); - float absDelta = delta > 0 ? delta : -delta; - return enclosingBox->scroll(delta < 0 ? negativeDirection : positiveDirection, granularity, absDelta, stopElement, enclosingBox, wheelEventAbsolutePoint); + RenderBox& initialEnclosingBox = startNode->renderer()->enclosingBox(); + if (initialEnclosingBox.isListBox()) + return didScrollInScrollableArea(static_cast<RenderListBox*>(&initialEnclosingBox), wheelEvent); + + RenderBox* currentEnclosingBox = &initialEnclosingBox; + while (currentEnclosingBox) { + if (RenderLayer* boxLayer = currentEnclosingBox->layer()) { + const PlatformWheelEvent* platformEvent = wheelEvent.wheelEvent(); + bool scrollingWasHandled; + if (platformEvent != nullptr) { + auto copiedEvent = platformEvent->copyWithDeltasAndVelocity(filteredPlatformDelta.width(), filteredPlatformDelta.height(), filteredVelocity); + scrollingWasHandled = boxLayer->handleWheelEvent(copiedEvent); + } else + scrollingWasHandled = didScrollInScrollableArea(boxLayer, wheelEvent); + + if (scrollingWasHandled) { + if (stopElement) + *stopElement = currentEnclosingBox->element(); + return true; + } + } + + if (stopElement && *stopElement && *stopElement == currentEnclosingBox->element()) + return true; + + currentEnclosingBox = currentEnclosingBox->containingBlock(); + if (currentEnclosingBox && currentEnclosingBox->isRenderNamedFlowThread()) + currentEnclosingBox = RenderNamedFlowThread::fragmentFromRenderBoxAsRenderBlock(currentEnclosingBox, roundedIntPoint(wheelEvent.absoluteLocation()), initialEnclosingBox); + if (!currentEnclosingBox || currentEnclosingBox->isRenderView()) + return false; + } + return false; } #if (ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS)) @@ -304,7 +344,7 @@ static inline bool shouldGesturesTriggerActive() } #endif -#if !PLATFORM(MAC) +#if !PLATFORM(COCOA) inline bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&) { @@ -322,79 +362,42 @@ inline bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTes EventHandler::EventHandler(Frame& frame) : m_frame(frame) - , m_mousePressed(false) - , m_capturesDragging(false) - , m_mouseDownMayStartSelect(false) -#if ENABLE(DRAG_SUPPORT) - , m_mouseDownMayStartDrag(false) - , m_dragMayStartSelectionInstead(false) -#endif - , m_mouseDownWasSingleClickInSelection(false) - , m_selectionInitiationState(HaveNotStartedSelection) - , m_hoverTimer(this, &EventHandler::hoverTimerFired) + , m_hoverTimer(*this, &EventHandler::hoverTimerFired) #if ENABLE(CURSOR_SUPPORT) - , m_cursorUpdateTimer(this, &EventHandler::cursorUpdateTimerFired) -#endif - , m_autoscrollController(adoptPtr(new AutoscrollController)) - , m_mouseDownMayStartAutoscroll(false) - , m_mouseDownWasInSubframe(false) - , m_fakeMouseMoveEventTimer(this, &EventHandler::fakeMouseMoveEventTimerFired) -#if ENABLE(SVG) - , m_svgPan(false) -#endif - , m_resizeLayer(0) - , m_eventHandlerWillResetCapturingMouseEventsElement(nullptr) - , m_clickCount(0) -#if ENABLE(IOS_GESTURE_EVENTS) - , m_gestureInitialDiameter(GestureUnknown) - , m_gestureLastDiameter(GestureUnknown) - , m_gestureInitialRotation(GestureUnknown) - , m_gestureLastRotation(GestureUnknown) -#endif -#if ENABLE(IOS_TOUCH_EVENTS) - , m_firstTouchID(InvalidTouchIdentifier) + , m_cursorUpdateTimer(*this, &EventHandler::cursorUpdateTimerFired) #endif - , m_mousePositionIsUnknown(true) - , m_mouseDownTimestamp(0) - , m_inTrackingScrollGesturePhase(false) - , m_widgetIsLatched(false) #if PLATFORM(MAC) - , m_mouseDownView(nil) - , m_sendingEventToSubview(false) -#if !PLATFORM(IOS) - , m_activationEventNumber(-1) -#endif // !PLATFORM(IOS) + , m_pendingMomentumWheelEventsTimer(*this, &EventHandler::clearLatchedState) +#endif + , m_autoscrollController(std::make_unique<AutoscrollController>()) +#if !ENABLE(IOS_TOUCH_EVENTS) + , m_fakeMouseMoveEventTimer(*this, &EventHandler::fakeMouseMoveEventTimerFired) #endif -#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) - , m_originatingTouchPointTargetKey(0) - , m_touchPressed(false) -#endif - , m_maxMouseMovedDuration(0) - , m_baseEventType(PlatformEvent::NoType) - , m_didStartDrag(false) - , m_didLongPressInvokeContextMenu(false) - , m_isHandlingWheelEvent(false) #if ENABLE(CURSOR_VISIBILITY) - , m_autoHideCursorTimer(this, &EventHandler::autoHideCursorTimerFired) + , m_autoHideCursorTimer(*this, &EventHandler::autoHideCursorTimerFired) #endif { } EventHandler::~EventHandler() { +#if !ENABLE(IOS_TOUCH_EVENTS) ASSERT(!m_fakeMouseMoveEventTimer.isActive()); +#endif #if ENABLE(CURSOR_VISIBILITY) ASSERT(!m_autoHideCursorTimer.isActive()); #endif } #if ENABLE(DRAG_SUPPORT) + DragState& EventHandler::dragState() { - DEFINE_STATIC_LOCAL(DragState, state, ()); + static NeverDestroyed<DragState> state; return state; } -#endif // ENABLE(DRAG_SUPPORT) + +#endif void EventHandler::clear() { @@ -402,62 +405,59 @@ void EventHandler::clear() #if ENABLE(CURSOR_SUPPORT) m_cursorUpdateTimer.stop(); #endif +#if !ENABLE(IOS_TOUCH_EVENTS) m_fakeMouseMoveEventTimer.stop(); +#endif #if ENABLE(CURSOR_VISIBILITY) cancelAutoHideCursorTimer(); #endif - m_resizeLayer = 0; + m_resizeLayer = nullptr; m_elementUnderMouse = nullptr; m_lastElementUnderMouse = nullptr; -#if ENABLE(SVG) - m_instanceUnderMouse = 0; - m_lastInstanceUnderMouse = 0; -#endif - m_lastMouseMoveEventSubframe = 0; + m_lastMouseMoveEventSubframe = nullptr; m_lastScrollbarUnderMouse = nullptr; m_clickCount = 0; - m_clickNode = 0; + m_clickNode = nullptr; #if ENABLE(IOS_GESTURE_EVENTS) m_gestureInitialDiameter = GestureUnknown; - m_gestureLastDiameter = GestureUnknown; m_gestureInitialRotation = GestureUnknown; +#endif +#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) + m_gestureLastDiameter = GestureUnknown; m_gestureLastRotation = GestureUnknown; m_gestureTargets.clear(); #endif #if ENABLE(IOS_TOUCH_EVENTS) m_touches.clear(); m_firstTouchID = InvalidTouchIdentifier; - m_touchEventTargetSubframe = 0; + m_touchEventTargetSubframe = nullptr; #endif - m_frameSetBeingResized = 0; + m_frameSetBeingResized = nullptr; #if ENABLE(DRAG_SUPPORT) - m_dragTarget = 0; + m_dragTarget = nullptr; m_shouldOnlyFireDragOverEvent = false; #endif m_mousePositionIsUnknown = true; m_lastKnownMousePosition = IntPoint(); m_lastKnownMouseGlobalPosition = IntPoint(); - m_mousePressNode = 0; + m_mousePressNode = nullptr; m_mousePressed = false; m_capturesDragging = false; m_capturingMouseEventsElement = nullptr; - m_latchedWheelEventElement = nullptr; - m_previousWheelScrolledElement = nullptr; + clearLatchedState(); #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) m_originatingTouchPointTargets.clear(); - m_originatingTouchPointDocument.clear(); + m_originatingTouchPointDocument = nullptr; m_originatingTouchPointTargetKey = 0; #endif m_maxMouseMovedDuration = 0; - m_baseEventType = PlatformEvent::NoType; m_didStartDrag = false; - m_didLongPressInvokeContextMenu = false; } -void EventHandler::nodeWillBeRemoved(Node* nodeToBeRemoved) +void EventHandler::nodeWillBeRemoved(Node& nodeToBeRemoved) { - if (nodeToBeRemoved->contains(m_clickNode.get())) - m_clickNode = 0; + if (nodeToBeRemoved.contains(m_clickNode.get())) + m_clickNode = nullptr; } static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection) @@ -474,22 +474,30 @@ static inline bool dispatchSelectStart(Node* node) return node->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)); } -static VisibleSelection expandSelectionToRespectUserSelectAll(Node* targetNode, const VisibleSelection& selection) +static Node* nodeToSelectOnMouseDownForNode(Node& targetNode) { #if ENABLE(USERSELECT_ALL) - Node* rootUserSelectAll = Position::rootUserSelectAllForNode(targetNode); - if (!rootUserSelectAll) + if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(&targetNode)) + return rootUserSelectAll; +#endif + + if (targetNode.shouldSelectOnMouseDown()) + return &targetNode; + + return nullptr; +} + +static VisibleSelection expandSelectionToRespectSelectOnMouseDown(Node& targetNode, const VisibleSelection& selection) +{ + Node* nodeToSelect = nodeToSelectOnMouseDownForNode(targetNode); + if (!nodeToSelect) return selection; VisibleSelection newSelection(selection); - newSelection.setBase(positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary)); - newSelection.setExtent(positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary)); + newSelection.setBase(positionBeforeNode(nodeToSelect).upstream(CanCrossEditingBoundary)); + newSelection.setExtent(positionAfterNode(nodeToSelect).downstream(CanCrossEditingBoundary)); return newSelection; -#else - UNUSED_PARAM(targetNode); - return selection; -#endif } bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity) @@ -507,7 +515,7 @@ bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targe m_selectionInitiationState = PlacedCaret; } - m_frame.selection().setNonDirectionalSelectionIfNeeded(selection, granularity); + m_frame.selection().setSelectionByMouseIfDifferent(selection, granularity); return true; } @@ -518,7 +526,7 @@ void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& resul VisibleSelection newSelection; if (targetNode && targetNode->renderer()) { - VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint())); + VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); if (pos.isNotNull()) { newSelection = VisibleSelection(pos); newSelection.expandUsingGranularity(WordGranularity); @@ -527,33 +535,73 @@ void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& resul if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange()) newSelection.appendTrailingWhitespace(); - updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), WordGranularity); + updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); } } +static AppendTrailingWhitespace shouldAppendTrailingWhitespace(const MouseEventWithHitTestResults& result, const Frame& frame) +{ + return (result.event().clickCount() == 2 && frame.editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace; +} + void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result) { - if (m_mouseDownMayStartSelect) { - selectClosestWordFromHitTestResult(result.hitTestResult(), - (result.event().clickCount() == 2 && m_frame.editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace); - } + if (m_mouseDownMayStartSelect) + selectClosestWordFromHitTestResult(result.hitTestResult(), shouldAppendTrailingWhitespace(result, m_frame)); } -void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result) +#if !PLATFORM(MAC) +VisibleSelection EventHandler::selectClosestWordFromHitTestResultBasedOnLookup(const HitTestResult&) +{ + return VisibleSelection(); +} +#endif + +void EventHandler::selectClosestContextualWordFromMouseEvent(const MouseEventWithHitTestResults& mouseEvent) +{ + Node* targetNode = mouseEvent.targetNode(); + const HitTestResult& result = mouseEvent.hitTestResult(); + VisibleSelection newSelection; + bool appendTrailingWhitespace = shouldAppendTrailingWhitespace(mouseEvent, m_frame); + + if (targetNode && targetNode->renderer()) { + newSelection = selectClosestWordFromHitTestResultBasedOnLookup(result); + if (newSelection.isNone()) { + VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); + if (pos.isNotNull()) { + newSelection = VisibleSelection(pos); + newSelection.expandUsingGranularity(WordGranularity); + } + } + + if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange()) + newSelection.appendTrailingWhitespace(); + + updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); + } +} + +void EventHandler::selectClosestContextualWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result) { - if (!result.hitTestResult().isLiveLink()) - return selectClosestWordFromMouseEvent(result); + Element* urlElement = result.hitTestResult().URLElement(); + if (!urlElement || !isDraggableLink(*urlElement)) { + if (Node* targetNode = result.targetNode()) { + if (isEditableNode(*targetNode)) + return selectClosestWordFromMouseEvent(result); + } + + return selectClosestContextualWordFromMouseEvent(result); + } Node* targetNode = result.targetNode(); if (targetNode && targetNode->renderer() && m_mouseDownMayStartSelect) { VisibleSelection newSelection; - Element* URLElement = result.hitTestResult().URLElement(); - VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint())); - if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(URLElement)) - newSelection = VisibleSelection::selectionFromContentsOfNode(URLElement); + VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); + if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(*urlElement)) + newSelection = VisibleSelection::selectionFromContentsOfNode(urlElement); - updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), WordGranularity); + updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); } } @@ -585,13 +633,13 @@ bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestR return false; VisibleSelection newSelection; - VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint())); + VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr)); if (pos.isNotNull()) { newSelection = VisibleSelection(pos); newSelection.expandUsingGranularity(ParagraphGranularity); } - return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), ParagraphGranularity); + return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), ParagraphGranularity); } static int textDistance(const Position& start, const Position& end) @@ -602,6 +650,8 @@ static int textDistance(const Position& start, const Position& end) bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event) { + Ref<Frame> protectedFrame(m_frame); + m_frame.document()->updateLayoutIgnorePendingStylesheets(); Node* targetNode = event.targetNode(); if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect)) @@ -620,7 +670,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR } } - VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint())); + VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr)); if (visiblePos.isNull()) visiblePos = VisiblePosition(firstPositionInOrBeforeNode(targetNode), DOWNSTREAM); Position pos = visiblePos.deepEquivalent(); @@ -629,7 +679,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR TextGranularity granularity = CharacterGranularity; if (extendSelection && newSelection.isCaretOrRange()) { - VisibleSelection selectionInUserSelectAll = expandSelectionToRespectUserSelectAll(targetNode, VisibleSelection(pos)); + VisibleSelection selectionInUserSelectAll = expandSelectionToRespectSelectOnMouseDown(*targetNode, VisibleSelection(pos)); if (selectionInUserSelectAll.isRange()) { if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0) pos = selectionInUserSelectAll.start(); @@ -656,7 +706,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR newSelection.expandUsingGranularity(m_frame.selection().granularity()); } } else - newSelection = expandSelectionToRespectUserSelectAll(targetNode, visiblePos); + newSelection = expandSelectionToRespectSelectOnMouseDown(*targetNode, visiblePos); bool handled = updateSelectionForMouseDownDispatchingSelectStart(targetNode, newSelection, granularity); @@ -677,12 +727,16 @@ static inline bool canMouseDownStartSelect(Node* node) bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event) { + Ref<Frame> protectedFrame(m_frame); + #if ENABLE(DRAG_SUPPORT) // Reset drag state. - dragState().source = 0; + dragState().source = nullptr; #endif +#if !ENABLE(IOS_TOUCH_EVENTS) cancelFakeMouseMoveEvent(); +#endif m_frame.document()->updateLayoutIgnorePendingStylesheets(); @@ -699,26 +753,31 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve #if ENABLE(DRAG_SUPPORT) // Careful that the drag starting logic stays in sync with eventMayStartDrag() - m_mouseDownMayStartDrag = singleClick; + // FIXME: eventMayStartDrag() does not check for shift key press, link or image event targets. + // Bug: https://bugs.webkit.org/show_bug.cgi?id=155390 + + // Single mouse down on links or images can always trigger drag-n-drop. + bool isMouseDownOnLinkOrImage = event.isOverLink() || event.hitTestResult().image(); + m_mouseDownMayStartDrag = singleClick && (!event.event().shiftKey() || isMouseDownOnLinkOrImage); #endif m_mouseDownWasSingleClickInSelection = false; m_mouseDown = event.event(); + if (m_immediateActionStage != ImmediateActionStage::PerformedHitTest) + m_immediateActionStage = ImmediateActionStage::None; + if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event)) return true; -#if ENABLE(SVG) - if (m_frame.document()->isSVGDocument() - && toSVGDocument(m_frame.document())->zoomAndPanEnabled()) { + if (is<SVGDocument>(*m_frame.document()) && downcast<SVGDocument>(*m_frame.document()).zoomAndPanEnabled()) { if (event.event().shiftKey() && singleClick) { m_svgPan = true; - toSVGDocument(m_frame.document())->startPan(m_frame.view()->windowToContents(event.event().position())); + downcast<SVGDocument>(*m_frame.document()).startPan(m_frame.view()->windowToContents(event.event().position())); return true; } } -#endif // We don't do this at the start of mouse down handling, // because we don't want to do it until we know we didn't hit a widget. @@ -726,14 +785,16 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve focusDocumentView(); m_mousePressNode = event.targetNode(); + m_frame.document()->setFocusNavigationStartingNode(event.targetNode()); + #if ENABLE(DRAG_SUPPORT) - m_dragStartPos = event.event().position(); + m_dragStartPosition = event.event().position(); #endif - bool swallowEvent = false; m_mousePressed = true; m_selectionInitiationState = HaveNotStartedSelection; + bool swallowEvent = false; if (event.event().clickCount() == 2) swallowEvent = handleMousePressEventDoubleClick(event); else if (event.event().clickCount() >= 3) @@ -748,12 +809,14 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve } #if ENABLE(DRAG_SUPPORT) -bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event) +bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis) { if (!m_mousePressed) return false; - if (handleDrag(event, ShouldCheckDragHysteresis)) + Ref<Frame> protectedFrame(m_frame); + + if (handleDrag(event, checkDragHysteresis)) return true; Node* targetNode = event.targetNode(); @@ -771,7 +834,7 @@ bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& e return false; } -#if PLATFORM(MAC) // FIXME: Why does this assertion fire on other platforms? +#if PLATFORM(COCOA) // FIXME: Why does this assertion fire on other platforms? ASSERT(m_mouseDownMayStartSelect || m_mouseDownMayStartAutoscroll); #endif @@ -783,9 +846,8 @@ bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& e } if (m_selectionInitiationState != ExtendedSelection) { - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); HitTestResult result(m_mouseDownPos); - m_frame.document()->renderView()->hitTest(request, result); + m_frame.document()->renderView()->hitTest(HitTestRequest(), result); updateSelectionForMouseDrag(result); } @@ -798,13 +860,13 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const // This is a pre-flight check of whether the event might lead to a drag being started. Be careful // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag // in handleMousePressEvent - - if (!m_frame.contentRenderer() || !m_frame.contentRenderer()->hasLayer()) + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) return false; if (event.button() != LeftButton || event.clickCount() != 1) return false; - + FrameView* view = m_frame.view(); if (!view) return false; @@ -813,12 +875,15 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const if (!page) return false; + Ref<Frame> protectedFrame(m_frame); + updateDragSourceActionsAllowed(); - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(view->windowToContents(event.position())); - m_frame.contentRenderer()->hitTest(request, result); + renderView->hitTest(request, result); DragState state; - return result.innerElement() && page->dragController().draggableElement(&m_frame, result.innerElement(), result.roundedPointInInnerNodeFrame(), state); + Element* targetElement = result.targetElement(); + return targetElement && page->dragController().draggableElement(&m_frame, targetElement, result.roundedPointInInnerNodeFrame(), state); } void EventHandler::updateSelectionForMouseDrag() @@ -826,19 +891,19 @@ void EventHandler::updateSelectionForMouseDrag() FrameView* view = m_frame.view(); if (!view) return; - RenderView* renderer = m_frame.contentRenderer(); - if (!renderer) + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) return; - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); - renderer->hitTest(request, result); + renderView->hitTest(request, result); updateSelectionForMouseDrag(result); } static VisiblePosition selectionExtentRespectingEditingBoundary(const VisibleSelection& selection, const LayoutPoint& localPoint, Node* targetNode) { - LayoutPoint selectionEndPoint = localPoint; + FloatPoint selectionEndPoint = localPoint; Element* editableElement = selection.rootEditableElement(); if (!targetNode->renderer()) @@ -849,11 +914,11 @@ static VisiblePosition selectionExtentRespectingEditingBoundary(const VisibleSel return VisiblePosition(); FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint)); - selectionEndPoint = roundedLayoutPoint(editableElement->renderer()->absoluteToLocal(absolutePoint)); + selectionEndPoint = editableElement->renderer()->absoluteToLocal(absolutePoint); targetNode = editableElement; } - return targetNode->renderer()->positionForPoint(selectionEndPoint); + return targetNode->renderer()->positionForPoint(LayoutPoint(selectionEndPoint), nullptr); } void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult) @@ -875,7 +940,6 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul // done in handleMousePressEvent, but not if the mouse press was on an existing selection. VisibleSelection newSelection = m_frame.selection().selection(); -#if ENABLE(SVG) // Special case to limit selection to the containing block for SVG text. // FIXME: Isn't there a better non-SVG-specific way to do this? if (Node* selectionBaseNode = newSelection.base().deprecatedNode()) @@ -883,7 +947,6 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul if (selectionBaseRenderer->isSVGText()) if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock()) return; -#endif if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelectStart(target)) return; @@ -901,11 +964,11 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary)); } else { // Reset base for user select all when base is inside user-select-all area and extent < base. - if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint()), m_mousePressNode->renderer()->positionForPoint(m_dragStartPos)) < 0) + if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0) newSelection.setBase(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary)); Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNode(target); - if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint()), m_mousePressNode->renderer()->positionForPoint(m_dragStartPos)) < 0) + if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0) newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTarget).upstream(CanCrossEditingBoundary)); else if (rootUserSelectAllForTarget && m_mousePressNode->renderer()) newSelection.setExtent(positionAfterNode(rootUserSelectAllForTarget).downstream(CanCrossEditingBoundary)); @@ -919,7 +982,7 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul if (m_frame.selection().granularity() != CharacterGranularity) newSelection.expandUsingGranularity(m_frame.selection().granularity()); - m_frame.selection().setNonDirectionalSelectionIfNeeded(newSelection, m_frame.selection().granularity(), + m_frame.selection().setSelectionByMouseIfDifferent(newSelection, m_frame.selection().granularity(), FrameSelection::AdjustEndpointsAtBidiBoundary); } #endif // ENABLE(DRAG_SUPPORT) @@ -947,6 +1010,8 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e if (autoscrollInProgress()) stopAutoscrollTimer(); + Ref<Frame> protectedFrame(m_frame); + if (handleMouseUp(event)) return true; @@ -969,7 +1034,7 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e // editing, place the caret. if (m_mouseDownWasSingleClickInSelection && m_selectionInitiationState != ExtendedSelection #if ENABLE(DRAG_SUPPORT) - && m_dragStartPos == event.event().position() + && m_dragStartPosition == event.event().position() #endif && m_frame.selection().isRange() && event.event().button() != RightButton) { @@ -977,7 +1042,7 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e Node* node = event.targetNode(); bool caretBrowsing = m_frame.settings().caretBrowsingEnabled(); if (node && node->renderer() && (caretBrowsing || node->hasEditableStyle())) { - VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint()); + VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint(), nullptr); newSelection = VisibleSelection(pos); } @@ -986,10 +1051,6 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e handled = true; } - m_frame.selection().updateSelectionCachesIfSelectionIsInsideTextFormControl(UserTriggered); - - m_frame.selection().selectFrameElementInParentIfFullySelected(); - if (event.event().button() == MiddleButton) { // Ignore handled, since we want to paste to where the caret was placed anyway. handled = handlePasteGlobalSelection(event.event()) || handled; @@ -1010,12 +1071,12 @@ void EventHandler::didPanScrollStop() m_autoscrollController->didPanScrollStop(); } -void EventHandler::startPanScrolling(RenderElement* renderer) +void EventHandler::startPanScrolling(RenderElement& renderer) { #if !PLATFORM(IOS) - if (!renderer->isBox()) + if (!is<RenderBox>(renderer)) return; - m_autoscrollController->startPanScrolling(toRenderBox(renderer), lastKnownMousePosition()); + m_autoscrollController->startPanScrolling(&downcast<RenderBox>(renderer), lastKnownMousePosition()); invalidateClick(); #endif } @@ -1059,6 +1120,8 @@ DragSourceAction EventHandler::updateDragSourceActionsAllowed() const HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) { + Ref<Frame> protectedFrame(m_frame); + // We always send hitTestResultAtPoint to the main frame if we have one, // otherwise we might hit areas that are obscured by higher frames. if (!m_frame.isMainFrame()) { @@ -1071,19 +1134,26 @@ HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTe } } - HitTestResult result(point, padding.height(), padding.width(), padding.height(), padding.width()); + unsigned nonNegativePaddingWidth = std::max<LayoutUnit>(0, padding.width()).toUnsigned(); + unsigned nonNegativePaddingHeight = std::max<LayoutUnit>(0, padding.height()).toUnsigned(); + + // We should always start hit testing a clean tree. + if (auto* frameView = m_frame.view()) + frameView->updateLayoutAndStyleIfNeededRecursive(); - if (!m_frame.contentRenderer()) + HitTestResult result(point, nonNegativePaddingHeight, nonNegativePaddingWidth, nonNegativePaddingHeight, nonNegativePaddingWidth); + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) return result; // hitTestResultAtPoint is specifically used to hitTest into all frames, thus it always allows child frame content. HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent); - m_frame.contentRenderer()->hitTest(request, result); + renderView->hitTest(request, result); if (!request.readOnly()) - m_frame.document()->updateHoverActiveState(request, result.innerElement()); + m_frame.document()->updateHoverActiveState(request, result.targetElement()); - if (request.disallowsShadowContent()) - result.setToNonShadowAncestor(); + if (request.disallowsUserAgentShadowContent()) + result.setToNonUserAgentShadowAncestor(); return result; } @@ -1093,16 +1163,6 @@ void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed) m_autoscrollController->stopAutoscrollTimer(rendererIsBeingDestroyed); } -Node* EventHandler::mousePressNode() const -{ - return m_mousePressNode.get(); -} - -void EventHandler::setMousePressNode(PassRefPtr<Node> node) -{ - m_mousePressNode = node; -} - bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode) { Node* node = startingNode; @@ -1115,7 +1175,7 @@ bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity g if (node) { auto r = node->renderer(); - if (r && !r->isListBox() && r->enclosingBox()->scroll(direction, granularity)) { + if (r && !r->isListBox() && r->enclosingBox().scroll(direction, granularity)) { setFrameWasScrolledByUser(); return true; } @@ -1136,7 +1196,7 @@ bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, Scrol if (node) { auto r = node->renderer(); - if (r && !r->isListBox() && r->enclosingBox()->logicalScroll(direction, granularity)) { + if (r && !r->isListBox() && r->enclosingBox().logicalScroll(direction, granularity)) { setFrameWasScrolledByUser(); return true; } @@ -1147,6 +1207,8 @@ bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, Scrol bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode) { + Ref<Frame> protectedFrame(m_frame); + // The layout needs to be up to date to determine if we can scroll. We may be // here because of an onLoad event, in which case the final layout hasn't been performed yet. m_frame.document()->updateLayoutIgnorePendingStylesheets(); @@ -1164,6 +1226,8 @@ bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularit bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode) { + Ref<Frame> protectedFrame(m_frame); + // The layout needs to be up to date to determine if we can scroll. We may be // here because of an onLoad event, in which case the final layout hasn't been performed yet. m_frame.document()->updateLayoutIgnorePendingStylesheets(); @@ -1173,7 +1237,7 @@ bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, Sc FrameView* view = frame->view(); bool scrolled = false; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // Mac also resets the scroll position in the inline direction. if (granularity == ScrollByDocument && view && view->logicalScroll(ScrollInlineDirectionBackward, ScrollByDocument)) scrolled = true; @@ -1206,29 +1270,29 @@ Frame* EventHandler::subframeForHitTestResult(const MouseEventWithHitTestResults Frame* EventHandler::subframeForTargetNode(Node* node) { if (!node) - return 0; + return nullptr; auto renderer = node->renderer(); - if (!renderer || !renderer->isWidget()) - return 0; + if (!is<RenderWidget>(renderer)) + return nullptr; - Widget* widget = toRenderWidget(renderer)->widget(); - if (!widget || !widget->isFrameView()) - return 0; + Widget* widget = downcast<RenderWidget>(*renderer).widget(); + if (!is<FrameView>(widget)) + return nullptr; - return &toFrameView(widget)->frame(); + return &downcast<FrameView>(*widget).frame(); } #if ENABLE(CURSOR_SUPPORT) static bool isSubmitImage(Node* node) { - return node && isHTMLInputElement(node) && toHTMLInputElement(node)->isImageButton(); + return is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*node).isImageButton(); } // Returns true if the node's editable block is not current focused for editing static bool nodeIsNotBeingEdited(const Node& node, const Frame& frame) { - return frame.selection().rootEditableElement() != node.rootEditableElement(); + return frame.selection().selection().rootEditableElement() != node.rootEditableElement(); } bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey) @@ -1265,7 +1329,7 @@ bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey) return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled)); } -void EventHandler::cursorUpdateTimerFired(Timer<EventHandler>&) +void EventHandler::cursorUpdateTimerFired() { ASSERT(m_frame.document()); updateCursor(); @@ -1293,46 +1357,57 @@ void EventHandler::updateCursor() bool metaKey; PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey); - m_frame.document()->updateLayout(); - - HitTestRequest request(HitTestRequest::ReadOnly); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::AllowFrameScrollbars); HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); renderView->hitTest(request, result); - OptionalCursor optionalCursor = selectCursor(result, shiftKey); - if (optionalCursor.isCursorChange()) { - m_currentMouseCursor = optionalCursor.cursor(); - view->setCursor(m_currentMouseCursor); + updateCursor(*view, result, shiftKey); +} + +void EventHandler::updateCursor(FrameView& view, const HitTestResult& result, bool shiftKey) +{ + if (auto optionalCursor = selectCursor(result, shiftKey)) { + m_currentMouseCursor = WTFMove(optionalCursor.value()); + view.setCursor(m_currentMouseCursor); } } -OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shiftKey) +std::optional<Cursor> EventHandler::selectCursor(const HitTestResult& result, bool shiftKey) { if (m_resizeLayer && m_resizeLayer->inResizeMode()) - return NoCursorChange; + return std::nullopt; if (!m_frame.page()) - return NoCursorChange; + return std::nullopt; #if ENABLE(PAN_SCROLLING) if (m_frame.mainFrame().eventHandler().panScrollInProgress()) - return NoCursorChange; + return std::nullopt; +#endif + + Ref<Frame> protectedFrame(m_frame); + + // Use always pointer cursor for scrollbars. + if (result.scrollbar()) { +#if ENABLE(CURSOR_VISIBILITY) + cancelAutoHideCursorTimer(); #endif + return pointerCursor(); + } Node* node = result.targetNode(); if (!node) - return NoCursorChange; + return std::nullopt; auto renderer = node->renderer(); - RenderStyle* style = renderer ? &renderer->style() : nullptr; + auto* style = renderer ? &renderer->style() : nullptr; bool horizontalText = !style || style->isHorizontalWritingMode(); const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCursor(); #if ENABLE(CURSOR_VISIBILITY) - if (style && style->cursorVisibility() == CursorVisibilityAutoHide) { - FeatureObserver::observe(m_frame.document(), FeatureObserver::CursorVisibility); + if (style && style->cursorVisibility() == CursorVisibilityAutoHide) startAutoHideCursorTimer(); - } else + else cancelAutoHideCursorTimer(); #endif @@ -1344,7 +1419,7 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif case SetCursor: return overrideCursor; case DoNotSetCursor: - return NoCursorChange; + return std::nullopt; } } @@ -1360,8 +1435,7 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif float scale = styleImage->imageScaleFactor(); // Get hotspot and convert from logical pixels to physical pixels. IntPoint hotSpot = (*cursors)[i].hotSpot(); - hotSpot.scale(scale, scale); - IntSize size = cachedImage->imageForRenderer(renderer)->size(); + FloatSize size = cachedImage->imageForRenderer(renderer)->size(); if (cachedImage->errorOccurred()) continue; // Limit the size of cursors (in UI pixels) so that they cannot be @@ -1383,8 +1457,19 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif } } - switch (style ? style->cursor() : CURSOR_AUTO) { - case CURSOR_AUTO: { + // During selection, use an I-beam regardless of the content beneath the cursor. + // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection. + if (m_mousePressed + && m_mouseDownMayStartSelect +#if ENABLE(DRAG_SUPPORT) + && !m_mouseDownMayStartDrag +#endif + && m_frame.selection().isCaretOrRange() + && !m_capturingMouseEventsElement) + return iBeam; + + switch (style ? style->cursor() : CursorAuto) { + case CursorAuto: { bool editable = node->hasEditableStyle(); if (useHandCursor(node, result.isOverLink(), shiftKey)) @@ -1398,90 +1483,79 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif } } - // During selection, use an I-beam regardless of the content beneath the cursor when cursor style is not explicitly specified. - // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection. - if (m_mousePressed && m_mouseDownMayStartSelect -#if ENABLE(DRAG_SUPPORT) - && !m_mouseDownMayStartDrag -#endif - && m_frame.selection().isCaretOrRange() - && !m_capturingMouseEventsElement) { - return iBeam; - } - if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar()) return iBeam; return pointerCursor(); } - case CURSOR_CROSS: + case CursorCross: return crossCursor(); - case CURSOR_POINTER: + case CursorPointer: return handCursor(); - case CURSOR_MOVE: + case CursorMove: return moveCursor(); - case CURSOR_ALL_SCROLL: + case CursorAllScroll: return moveCursor(); - case CURSOR_E_RESIZE: + case CursorEResize: return eastResizeCursor(); - case CURSOR_W_RESIZE: + case CursorWResize: return westResizeCursor(); - case CURSOR_N_RESIZE: + case CursorNResize: return northResizeCursor(); - case CURSOR_S_RESIZE: + case CursorSResize: return southResizeCursor(); - case CURSOR_NE_RESIZE: + case CursorNeResize: return northEastResizeCursor(); - case CURSOR_SW_RESIZE: + case CursorSwResize: return southWestResizeCursor(); - case CURSOR_NW_RESIZE: + case CursorNwResize: return northWestResizeCursor(); - case CURSOR_SE_RESIZE: + case CursorSeResize: return southEastResizeCursor(); - case CURSOR_NS_RESIZE: + case CursorNsResize: return northSouthResizeCursor(); - case CURSOR_EW_RESIZE: + case CursorEwResize: return eastWestResizeCursor(); - case CURSOR_NESW_RESIZE: + case CursorNeswResize: return northEastSouthWestResizeCursor(); - case CURSOR_NWSE_RESIZE: + case CursorNwseResize: return northWestSouthEastResizeCursor(); - case CURSOR_COL_RESIZE: + case CursorColResize: return columnResizeCursor(); - case CURSOR_ROW_RESIZE: + case CursorRowResize: return rowResizeCursor(); - case CURSOR_TEXT: + case CursorText: return iBeamCursor(); - case CURSOR_WAIT: + case CursorWait: return waitCursor(); - case CURSOR_HELP: + case CursorHelp: return helpCursor(); - case CURSOR_VERTICAL_TEXT: + case CursorVerticalText: return verticalTextCursor(); - case CURSOR_CELL: + case CursorCell: return cellCursor(); - case CURSOR_CONTEXT_MENU: + case CursorContextMenu: return contextMenuCursor(); - case CURSOR_PROGRESS: + case CursorProgress: return progressCursor(); - case CURSOR_NO_DROP: + case CursorNoDrop: return noDropCursor(); - case CURSOR_ALIAS: + case CursorAlias: return aliasCursor(); - case CURSOR_COPY: + case CursorCopy: return copyCursor(); - case CURSOR_NONE: + case CursorNone: return noneCursor(); - case CURSOR_NOT_ALLOWED: + case CursorNotAllowed: return notAllowedCursor(); - case CURSOR_DEFAULT: + case CursorDefault: return pointerCursor(); - case CURSOR_WEBKIT_ZOOM_IN: + case CursorZoomIn: return zoomInCursor(); - case CURSOR_WEBKIT_ZOOM_OUT: + case CursorZoomOut: return zoomOutCursor(); - case CURSOR_WEBKIT_GRAB: + case CursorWebkitGrab: return grabCursor(); - case CURSOR_WEBKIT_GRABBING: + case CursorWebkitGrabbing: return grabbingCursor(); } return pointerCursor(); @@ -1497,10 +1571,12 @@ void EventHandler::startAutoHideCursorTimer() m_autoHideCursorTimer.startOneShot(page->settings().timeWithoutMouseMovementBeforeHidingControls()); +#if !ENABLE(IOS_TOUCH_EVENTS) // The fake mouse move event screws up the auto-hide feature (by resetting the auto-hide timer) // so cancel any pending fake mouse moves. if (m_fakeMouseMoveEventTimer.isActive()) m_fakeMouseMoveEventTimer.stop(); +#endif } void EventHandler::cancelAutoHideCursorTimer() @@ -1509,9 +1585,8 @@ void EventHandler::cancelAutoHideCursorTimer() m_autoHideCursorTimer.stop(); } -void EventHandler::autoHideCursorTimerFired(Timer<EventHandler>& timer) +void EventHandler::autoHideCursorTimerFired() { - ASSERT_UNUSED(timer, &timer == &m_autoHideCursorTimer); m_currentMouseCursor = noneCursor(); FrameView* view = m_frame.view(); if (view && view->isActive()) @@ -1527,68 +1602,98 @@ static LayoutPoint documentPointForWindowPoint(Frame& frame, const IntPoint& win return view ? view->windowToContents(windowPoint) : windowPoint; } -bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) +static Scrollbar* scrollbarForMouseEvent(const MouseEventWithHitTestResults& mouseEvent, FrameView* view) +{ + if (view) { + if (auto* scrollbar = view->scrollbarAtPoint(mouseEvent.event().position())) + return scrollbar; + } + return mouseEvent.scrollbar(); + +} + +bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& platformMouseEvent) { + Ref<Frame> protectedFrame(m_frame); RefPtr<FrameView> protector(m_frame.view()); - if (InspectorInstrumentation::handleMousePress(m_frame.page())) { + if (InspectorInstrumentation::handleMousePress(m_frame)) { invalidateClick(); return true; } +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousedownEvent); + return true; + } +#endif + + if (m_frame.mainFrame().pageOverlayController().handleMouseEvent(platformMouseEvent)) + return true; + #if ENABLE(TOUCH_EVENTS) - bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent); + bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); if (defaultPrevented) return true; #endif - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); // FIXME (bug 68185): this call should be made at another abstraction layer m_frame.loader().resetMultipleFormSubmissionProtection(); - + +#if !ENABLE(IOS_TOUCH_EVENTS) cancelFakeMouseMoveEvent(); +#endif m_mousePressed = true; m_capturesDragging = true; - setLastKnownMousePosition(mouseEvent); - m_mouseDownTimestamp = mouseEvent.timestamp(); + setLastKnownMousePosition(platformMouseEvent); + m_mouseDownTimestamp = platformMouseEvent.timestamp(); #if ENABLE(DRAG_SUPPORT) m_mouseDownMayStartDrag = false; #endif m_mouseDownMayStartSelect = false; m_mouseDownMayStartAutoscroll = false; if (FrameView* view = m_frame.view()) - m_mouseDownPos = view->windowToContents(mouseEvent.position()); + m_mouseDownPos = view->windowToContents(platformMouseEvent.position()); else { invalidateClick(); return false; } m_mouseDownWasInSubframe = false; - HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent); // Save the document point we generate in case the window coordinate is invalidated by what happens // when we dispatch the event. - LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, mouseEvent.position()); - MouseEventWithHitTestResults mev = m_frame.document()->prepareMouseEvent(request, documentPoint, mouseEvent); + LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, platformMouseEvent.position()); + MouseEventWithHitTestResults mouseEvent = m_frame.document()->prepareMouseEvent(request, documentPoint, platformMouseEvent); - if (!mev.targetNode()) { + if (!mouseEvent.targetNode()) { invalidateClick(); return false; } - m_mousePressNode = mev.targetNode(); - - RefPtr<Frame> subframe = subframeForHitTestResult(mev); - if (subframe && passMousePressEventToSubframe(mev, subframe.get())) { - // Start capturing future events for this frame. We only do this if we didn't clear - // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop. - m_capturesDragging = subframe->eventHandler().capturesDragging(); - if (m_mousePressed && m_capturesDragging) { - m_capturingMouseEventsElement = subframe->ownerElement(); - m_eventHandlerWillResetCapturingMouseEventsElement = true; + m_mousePressNode = mouseEvent.targetNode(); + m_frame.document()->setFocusNavigationStartingNode(mouseEvent.targetNode()); + + Scrollbar* scrollbar = scrollbarForMouseEvent(mouseEvent, m_frame.view()); + updateLastScrollbarUnderMouse(scrollbar, SetOrClearLastScrollbar::Set); + bool passedToScrollbar = scrollbar && passMousePressEventToScrollbar(mouseEvent, scrollbar); + + if (!passedToScrollbar) { + RefPtr<Frame> subframe = subframeForHitTestResult(mouseEvent); + if (subframe && passMousePressEventToSubframe(mouseEvent, subframe.get())) { + // Start capturing future events for this frame. We only do this if we didn't clear + // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop. + m_capturesDragging = subframe->eventHandler().capturesDragging(); + if (m_mousePressed && m_capturesDragging) { + m_capturingMouseEventsElement = subframe->ownerElement(); + m_eventHandlerWillResetCapturingMouseEventsElement = true; + } + invalidateClick(); + return true; } - invalidateClick(); - return true; } #if ENABLE(PAN_SCROLLING) @@ -1604,8 +1709,8 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) } #endif - m_clickCount = mouseEvent.clickCount(); - m_clickNode = mev.targetNode(); + m_clickCount = platformMouseEvent.clickCount(); + m_clickNode = mouseEvent.targetNode(); if (!m_clickNode) { invalidateClick(); @@ -1614,7 +1719,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) if (FrameView* view = m_frame.view()) { RenderLayer* layer = m_clickNode->renderer() ? m_clickNode->renderer()->enclosingLayer() : 0; - IntPoint p = view->windowToContents(mouseEvent.position()); + IntPoint p = view->windowToContents(platformMouseEvent.position()); if (layer && layer->isPointInResizeControl(p)) { layer->setInResizeMode(true); m_resizeLayer = layer; @@ -1626,110 +1731,108 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) m_frame.selection().setCaretBlinkingSuspended(true); - bool swallowEvent = !dispatchMouseEvent(eventNames().mousedownEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); - m_capturesDragging = !swallowEvent || mev.scrollbar(); + bool swallowEvent = !dispatchMouseEvent(eventNames().mousedownEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true); + m_capturesDragging = !swallowEvent || mouseEvent.scrollbar(); // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults // in case the scrollbar widget was destroyed when the mouse event was handled. - if (mev.scrollbar()) { - const bool wasLastScrollBar = mev.scrollbar() == m_lastScrollbarUnderMouse.get(); - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - mev = m_frame.document()->prepareMouseEvent(request, documentPoint, mouseEvent); - if (wasLastScrollBar && mev.scrollbar() != m_lastScrollbarUnderMouse.get()) + if (mouseEvent.scrollbar()) { + const bool wasLastScrollBar = mouseEvent.scrollbar() == m_lastScrollbarUnderMouse; + mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent); + if (wasLastScrollBar && mouseEvent.scrollbar() != m_lastScrollbarUnderMouse) m_lastScrollbarUnderMouse = nullptr; } - if (swallowEvent) { - // scrollbars should get events anyway, even disabled controls might be scrollable - Scrollbar* scrollbar = mev.scrollbar(); - - updateLastScrollbarUnderMouse(scrollbar, true); - - if (scrollbar) - passMousePressEventToScrollbar(mev, scrollbar); - } else { + if (!swallowEvent) { // Refetch the event target node if it currently is the shadow node inside an <input> element. // If a mouse event handler changes the input element type to one that has a widget associated, // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the // event target node can't still be the shadow node. - if (mev.targetNode()->isShadowRoot() && isHTMLInputElement(toShadowRoot(mev.targetNode())->hostElement())) { - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - mev = m_frame.document()->prepareMouseEvent(request, documentPoint, mouseEvent); - } - - FrameView* view = m_frame.view(); - Scrollbar* scrollbar = view ? view->scrollbarAtPoint(mouseEvent.position()) : 0; - if (!scrollbar) - scrollbar = mev.scrollbar(); - - updateLastScrollbarUnderMouse(scrollbar, true); + if (is<ShadowRoot>(*mouseEvent.targetNode()) && is<HTMLInputElement>(*downcast<ShadowRoot>(*mouseEvent.targetNode()).host())) + mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent); + } - if (scrollbar && passMousePressEventToScrollbar(mev, scrollbar)) + if (!swallowEvent) { + if (passedToScrollbar) swallowEvent = true; else - swallowEvent = handleMousePressEvent(mev); + swallowEvent = handleMousePressEvent(mouseEvent); } - return swallowEvent; } // This method only exists for platforms that don't know how to deliver -bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& mouseEvent) +bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& platformMouseEvent) { + Ref<Frame> protectedFrame(m_frame); RefPtr<FrameView> protector(m_frame.view()); m_frame.selection().setCaretBlinkingSuspended(false); - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); + +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent); + return true; + } +#endif // We get this instead of a second mouse-up m_mousePressed = false; - setLastKnownMousePosition(mouseEvent); + setLastKnownMousePosition(platformMouseEvent); - HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent); - Frame* subframe = subframeForHitTestResult(mev); + HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); + Frame* subframe = subframeForHitTestResult(mouseEvent); if (m_eventHandlerWillResetCapturingMouseEventsElement) m_capturingMouseEventsElement = nullptr; - if (subframe && passMousePressEventToSubframe(mev, subframe)) + if (subframe && passMousePressEventToSubframe(mouseEvent, subframe)) return true; - m_clickCount = mouseEvent.clickCount(); - bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); + m_clickCount = platformMouseEvent.clickCount(); + bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false); - bool swallowClickEvent = mouseEvent.button() != RightButton && mev.targetNode() == m_clickNode && !dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + bool swallowClickEvent = platformMouseEvent.button() != RightButton && mouseEvent.targetNode() == m_clickNode && !dispatchMouseEvent(eventNames().clickEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true); if (m_lastScrollbarUnderMouse) - swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(mouseEvent); + swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent); - bool swallowMouseReleaseEvent = !swallowMouseUpEvent && handleMouseReleaseEvent(mev); + bool swallowMouseReleaseEvent = !swallowMouseUpEvent && handleMouseReleaseEvent(mouseEvent); invalidateClick(); return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent; } -static RenderLayer* layerForNode(Node* node) +static ScrollableArea* enclosingScrollableArea(Node* node) { - if (!node) - return 0; + for (auto ancestor = node; ancestor; ancestor = ancestor->parentOrShadowHostNode()) { + if (is<HTMLIFrameElement>(*ancestor) || is<HTMLHtmlElement>(*ancestor) || is<HTMLDocument>(*ancestor)) + return nullptr; - auto renderer = node->renderer(); - if (!renderer) - return 0; + auto renderer = ancestor->renderer(); + if (!renderer) + continue; - RenderLayer* layer = renderer->enclosingLayer(); - if (!layer) - return 0; + if (is<RenderListBox>(*renderer)) + return downcast<RenderListBox>(renderer); - return layer; + return renderer->enclosingLayer(); + } + + return nullptr; } bool EventHandler::mouseMoved(const PlatformMouseEvent& event) { + Ref<Frame> protectedFrame(m_frame); RefPtr<FrameView> protector(m_frame.view()); MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration); + if (m_frame.mainFrame().pageOverlayController().handleMouseEvent(event)) + return true; + HitTestResult hoveredNode = HitTestResult(LayoutPoint()); bool result = handleMouseMoveEvent(event, &hoveredNode); @@ -1737,17 +1840,17 @@ bool EventHandler::mouseMoved(const PlatformMouseEvent& event) if (!page) return result; - if (RenderLayer* layer = layerForNode(hoveredNode.innerNode())) { + if (auto scrolledArea = enclosingScrollableArea(hoveredNode.innerNode())) { if (FrameView* frameView = m_frame.view()) { - if (frameView->containsScrollableArea(layer)) - layer->mouseMovedInContentArea(); + if (frameView->containsScrollableArea(scrolledArea)) + scrolledArea->mouseMovedInContentArea(); } } if (FrameView* frameView = m_frame.view()) frameView->mouseMovedInContentArea(); - hoveredNode.setToNonShadowAncestor(); + hoveredNode.setToNonUserAgentShadowAncestor(); page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags()); page->chrome().setToolTip(hoveredNode); return result; @@ -1759,17 +1862,25 @@ bool EventHandler::passMouseMovedEventToScrollbars(const PlatformMouseEvent& eve return handleMouseMoveEvent(event, &hoveredNode, true); } -bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars) +bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& platformMouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars) { #if ENABLE(TOUCH_EVENTS) - bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent); + bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); if (defaultPrevented) return true; #endif + Ref<Frame> protectedFrame(m_frame); RefPtr<FrameView> protector(m_frame.view()); - - setLastKnownMousePosition(mouseEvent); + +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousemoveEvent); + return true; + } +#endif + + setLastKnownMousePosition(platformMouseEvent); if (m_hoverTimer.isActive()) m_hoverTimer.stop(); @@ -1778,26 +1889,26 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi m_cursorUpdateTimer.stop(); #endif +#if !ENABLE(IOS_TOUCH_EVENTS) cancelFakeMouseMoveEvent(); +#endif -#if ENABLE(SVG) if (m_svgPan) { - toSVGDocument(m_frame.document())->updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); + downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); return true; } -#endif if (m_frameSetBeingResized) - return !dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, mouseEvent, false); + return !dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, platformMouseEvent, false); // On iOS, our scrollbars are managed by UIKit. #if !PLATFORM(IOS) // Send events right to a scrollbar if the mouse is pressed. if (m_lastScrollbarUnderMouse && m_mousePressed) - return m_lastScrollbarUnderMouse->mouseMoved(mouseEvent); + return m_lastScrollbarUnderMouse->mouseMoved(platformMouseEvent); #endif - HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowShadowContent | HitTestRequest::AllowFrameScrollbars; + HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::AllowFrameScrollbars; if (m_mousePressed) hitType |= HitTestRequest::Active; else if (onlyUpdateScrollbars) { @@ -1814,49 +1925,48 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly; #endif HitTestRequest request(hitType); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); if (hoveredNode) - *hoveredNode = mev.hitTestResult(); + *hoveredNode = mouseEvent.hitTestResult(); if (m_resizeLayer && m_resizeLayer->inResizeMode()) - m_resizeLayer->resize(mouseEvent, m_offsetFromResizeCorner); + m_resizeLayer->resize(platformMouseEvent, m_offsetFromResizeCorner); else { - Scrollbar* scrollbar = mev.scrollbar(); - updateLastScrollbarUnderMouse(scrollbar, !m_mousePressed); + Scrollbar* scrollbar = mouseEvent.scrollbar(); + updateLastScrollbarUnderMouse(scrollbar, m_mousePressed ? SetOrClearLastScrollbar::Clear : SetOrClearLastScrollbar::Set); // On iOS, our scrollbars are managed by UIKit. #if !PLATFORM(IOS) if (!m_mousePressed && scrollbar) - scrollbar->mouseMoved(mouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. + scrollbar->mouseMoved(platformMouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. #endif - if (onlyUpdateScrollbars) + if (onlyUpdateScrollbars) { + updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true); return true; + } } bool swallowEvent = false; - RefPtr<Frame> newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mev); + RefPtr<Frame> newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent); // 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. if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(&m_frame) && m_lastMouseMoveEventSubframe != newSubframe) - passMouseMoveEventToSubframe(mev, m_lastMouseMoveEventSubframe.get()); + passMouseMoveEventToSubframe(mouseEvent, m_lastMouseMoveEventSubframe.get()); if (newSubframe) { // Update over/out state before passing the event to the subframe. - updateMouseEventTargetNode(mev.targetNode(), mouseEvent, true); + updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true); // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target // node to be detached from its FrameView, in which case the event should not be passed. if (newSubframe->view()) - swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode); + swallowEvent |= passMouseMoveEventToSubframe(mouseEvent, newSubframe.get(), hoveredNode); + } + + if (!newSubframe || mouseEvent.scrollbar()) { #if ENABLE(CURSOR_SUPPORT) - } else { - if (FrameView* view = m_frame.view()) { - OptionalCursor optionalCursor = selectCursor(mev.hitTestResult(), mouseEvent.shiftKey()); - if (optionalCursor.isCursorChange()) { - m_currentMouseCursor = optionalCursor.cursor(); - view->setCursor(m_currentMouseCursor); - } - } + if (auto* view = m_frame.view()) + updateCursor(*view, mouseEvent.hitTestResult(), platformMouseEvent.shiftKey()); #endif } @@ -1865,11 +1975,11 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (swallowEvent) return true; - swallowEvent = !dispatchMouseEvent(eventNames().mousemoveEvent, mev.targetNode(), false, 0, mouseEvent, true); + swallowEvent = !dispatchMouseEvent(eventNames().mousemoveEvent, mouseEvent.targetNode(), false, 0, platformMouseEvent, true); #if ENABLE(DRAG_SUPPORT) if (!swallowEvent) - swallowEvent = handleMouseDraggedEvent(mev); -#endif // ENABLE(DRAG_SUPPORT) + swallowEvent = handleMouseDraggedEvent(mouseEvent); +#endif return swallowEvent; } @@ -1877,102 +1987,153 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi void EventHandler::invalidateClick() { m_clickCount = 0; - m_clickNode = 0; + m_clickNode = nullptr; } -inline static bool mouseIsReleasedOnPressedElement(Node* targetNode, Node* clickNode) +static Node* targetNodeForClickEvent(Node* mousePressNode, Node* mouseReleaseNode) { - if (targetNode == clickNode) - return true; + if (!mousePressNode || !mouseReleaseNode) + return nullptr; - if (!targetNode) - return false; + if (mousePressNode == mouseReleaseNode) + return mouseReleaseNode; - ShadowRoot* containingShadowRoot = targetNode->containingShadowRoot(); - if (!containingShadowRoot) - return false; - - // FIXME: When an element in UA ShadowDOM (e.g. inner element in <input>) is clicked, - // we assume that the host element is clicked. This is necessary for implementing <input type="range"> etc. - // However, we should not check ShadowRoot type basically. - // https://bugs.webkit.org/show_bug.cgi?id=108047 - if (containingShadowRoot->type() != ShadowRoot::UserAgentShadowRoot) - return false; - - Node* adjustedTargetNode = targetNode->shadowHost(); - Node* adjustedClickNode = clickNode ? clickNode->shadowHost() : 0; - return adjustedTargetNode == adjustedClickNode; + Element* mouseReleaseShadowHost = mouseReleaseNode->shadowHost(); + if (mouseReleaseShadowHost && mouseReleaseShadowHost == mousePressNode->shadowHost()) { + // We want to dispatch the click to the shadow tree host element to give listeners the illusion that the + // shadom tree is a single element. For example, we want to give the illusion that <input type="range"> + // is a single element even though it is a composition of multiple shadom tree elements. + return mouseReleaseShadowHost; + } + return nullptr; } -bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) +bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& platformMouseEvent) { + Ref<Frame> protectedFrame(m_frame); RefPtr<FrameView> protector(m_frame.view()); m_frame.selection().setCaretBlinkingSuspended(false); +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent); + return true; + } +#endif + + if (m_frame.mainFrame().pageOverlayController().handleMouseEvent(platformMouseEvent)) + return true; + #if ENABLE(TOUCH_EVENTS) - bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent); + bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); if (defaultPrevented) return true; #endif - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); #if ENABLE(PAN_SCROLLING) - m_autoscrollController->handleMouseReleaseEvent(mouseEvent); + m_autoscrollController->handleMouseReleaseEvent(platformMouseEvent); #endif m_mousePressed = false; - setLastKnownMousePosition(mouseEvent); + setLastKnownMousePosition(platformMouseEvent); -#if ENABLE(SVG) if (m_svgPan) { m_svgPan = false; - toSVGDocument(m_frame.document())->updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); + downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); return true; } -#endif if (m_frameSetBeingResized) - return !dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, mouseEvent, false); + return !dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, platformMouseEvent, false); + + // If an immediate action began or was completed using this series of mouse events, then we should send mouseup to + // the DOM and return now so that we don't perform our own default behaviors. + if (m_immediateActionStage == ImmediateActionStage::ActionCompleted || m_immediateActionStage == ImmediateActionStage::ActionUpdated || m_immediateActionStage == ImmediateActionStage::ActionCancelledAfterUpdate) { + m_immediateActionStage = ImmediateActionStage::None; + return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), true, m_clickCount, platformMouseEvent, false); + } + m_immediateActionStage = ImmediateActionStage::None; if (m_lastScrollbarUnderMouse) { invalidateClick(); - m_lastScrollbarUnderMouse->mouseUp(mouseEvent); + m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent); bool cancelable = true; bool setUnder = false; - return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), cancelable, m_clickCount, mouseEvent, setUnder); + return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), cancelable, m_clickCount, platformMouseEvent, setUnder); } - HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent); - Frame* subframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mev); + HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); + Frame* subframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent); if (m_eventHandlerWillResetCapturingMouseEventsElement) m_capturingMouseEventsElement = nullptr; - if (subframe && passMouseReleaseEventToSubframe(mev, subframe)) + if (subframe && passMouseReleaseEventToSubframe(mouseEvent, subframe)) return true; - bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); + bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false); - bool contextMenuEvent = mouseEvent.button() == RightButton; + bool contextMenuEvent = platformMouseEvent.button() == RightButton; - bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && mouseIsReleasedOnPressedElement(mev.targetNode(), m_clickNode.get()) && !dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + Node* nodeToClick = targetNodeForClickEvent(m_clickNode.get(), mouseEvent.targetNode()); + bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && nodeToClick && !dispatchMouseEvent(eventNames().clickEvent, nodeToClick, true, m_clickCount, platformMouseEvent, true); if (m_resizeLayer) { m_resizeLayer->setInResizeMode(false); - m_resizeLayer = 0; + m_resizeLayer = nullptr; } bool swallowMouseReleaseEvent = false; if (!swallowMouseUpEvent) - swallowMouseReleaseEvent = handleMouseReleaseEvent(mev); + swallowMouseReleaseEvent = handleMouseReleaseEvent(mouseEvent); invalidateClick(); return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent; } -bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEvent) +#if ENABLE(MOUSE_FORCE_EVENTS) +bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& event) +{ + Ref<Frame> protectedFrame(m_frame); + RefPtr<FrameView> protector(m_frame.view()); + +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcechangedEvent); + if (event.type() == PlatformEvent::MouseForceDown) + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcedownEvent); + if (event.type() == PlatformEvent::MouseForceUp) + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforceupEvent); + return true; + } +#endif + + setLastKnownMousePosition(event); + + HitTestRequest::HitTestRequestType hitType = HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::Active; + + HitTestRequest request(hitType); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event); + + bool swallowedEvent = !dispatchMouseEvent(eventNames().webkitmouseforcechangedEvent, mouseEvent.targetNode(), false, 0, event, false); + if (event.type() == PlatformEvent::MouseForceDown) + swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforcedownEvent, mouseEvent.targetNode(), false, 0, event, false); + if (event.type() == PlatformEvent::MouseForceUp) + swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforceupEvent, mouseEvent.targetNode(), false, 0, event, false); + + return swallowedEvent; +} +#else +bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& ) +{ + return false; +} +#endif // #if ENABLE(MOUSE_FORCE_EVENTS) + +bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& platformMouseEvent) { // If the event was a middle click, attempt to copy global selection in after // the newly set caret position. @@ -1991,10 +2152,10 @@ bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEve // clears the text box. So it's important this happens after the event // handlers have been fired. #if PLATFORM(GTK) - if (mouseEvent.type() != PlatformEvent::MousePressed) + if (platformMouseEvent.type() != PlatformEvent::MousePressed) return false; #else - if (mouseEvent.type() != PlatformEvent::MouseReleased) + if (platformMouseEvent.type() != PlatformEvent::MouseReleased) return false; #endif @@ -2010,8 +2171,9 @@ bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEve #if ENABLE(DRAG_SUPPORT) -bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, Clipboard* clipboard) +bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer* dataTransfer) { + Ref<Frame> protectedFrame(m_frame); FrameView* view = m_frame.view(); // FIXME: We might want to dispatch a dragleave even if the view is gone. @@ -2019,29 +2181,25 @@ bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dra return false; view->disableLayerFlushThrottlingTemporarilyForInteraction(); - RefPtr<MouseEvent> me = MouseEvent::create(eventType, + Ref<MouseEvent> me = MouseEvent::create(eventType, true, true, event.timestamp(), m_frame.document()->defaultView(), 0, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(), #if ENABLE(POINTER_LOCK) event.movementDelta().x(), event.movementDelta().y(), #endif event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), - 0, 0, clipboard); + 0, 0, event.force(), NoTap, dataTransfer); - dragTarget.dispatchEvent(me.get(), IGNORE_EXCEPTION); + dragTarget.dispatchEvent(me); return me->defaultPrevented(); } static bool targetIsFrame(Node* target, Frame*& frame) { - if (!target) + if (!is<HTMLFrameElementBase>(target)) return false; - if (!target->hasTagName(frameTag) && !target->hasTagName(iframeTag)) - return false; - - frame = toHTMLFrameElementBase(target)->contentFrame(); - + frame = downcast<HTMLFrameElementBase>(*target).contentFrame(); return true; } @@ -2070,85 +2228,62 @@ static String convertDragOperationToDropZoneOperation(DragOperation operation) } } -static inline bool hasFileOfType(Clipboard& clipboard, const String& type) -{ - RefPtr<FileList> fileList = clipboard.files(); - for (unsigned i = 0; i < fileList->length(); i++) { - if (equalIgnoringCase(fileList->item(i)->type(), type)) - return true; - } - return false; -} - -static inline bool hasStringOfType(Clipboard& clipboard, const String& type) -{ - return !type.isNull() && clipboard.types().contains(type); -} - -static bool hasDropZoneType(Clipboard& clipboard, const String& keyword) +static bool hasDropZoneType(DataTransfer& dataTransfer, const String& keyword) { if (keyword.startsWith("file:")) - return hasFileOfType(clipboard, keyword.substring(5)); + return dataTransfer.hasFileOfType(keyword.substring(5)); if (keyword.startsWith("string:")) - return hasStringOfType(clipboard, keyword.substring(7)); + return dataTransfer.hasStringOfType(keyword.substring(7)); return false; } -static bool findDropZone(Node* target, Clipboard* clipboard) +static bool findDropZone(Node* target, DataTransfer* dataTransfer) { - Element* element = target->isElementNode() ? toElement(target) : target->parentElement(); + ASSERT(target); + Element* element = is<Element>(*target) ? downcast<Element>(target) : target->parentElement(); for (; element; element = element->parentElement()) { + SpaceSplitString keywords(element->attributeWithoutSynchronization(webkitdropzoneAttr), true); bool matched = false; - String dropZoneStr = element->fastGetAttribute(webkitdropzoneAttr); - - if (dropZoneStr.isEmpty()) - continue; - - dropZoneStr = dropZoneStr.lower(); - - SpaceSplitString keywords(dropZoneStr, false); - if (keywords.isEmpty()) - continue; - DragOperation dragOperation = DragOperationNone; - for (unsigned int i = 0; i < keywords.size(); i++) { + for (unsigned i = 0, size = keywords.size(); i < size; ++i) { DragOperation op = convertDropZoneOperationToDragOperation(keywords[i]); if (op != DragOperationNone) { if (dragOperation == DragOperationNone) dragOperation = op; } else - matched = matched || hasDropZoneType(*clipboard, keywords[i].string()); - + matched = matched || hasDropZoneType(*dataTransfer, keywords[i].string()); if (matched && dragOperation != DragOperationNone) break; } if (matched) { - clipboard->setDropEffect(convertDragOperationToDropZoneOperation(dragOperation)); + dataTransfer->setDropEffect(convertDragOperationToDropZoneOperation(dragOperation)); return true; } } return false; } -bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard) +bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer) { + Ref<Frame> protectedFrame(m_frame); + bool accept = false; if (!m_frame.view()) return false; - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, event); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event); RefPtr<Element> newTarget; - if (Node* targetNode = mev.targetNode()) { + if (Node* targetNode = mouseEvent.targetNode()) { // Drag events should never go to non-element nodes (following IE, and proper mouseover/out dispatch) - if (!targetNode->isElementNode()) + if (!is<Element>(*targetNode)) newTarget = targetNode->parentOrShadowHostElement(); else - newTarget = toElement(targetNode); + newTarget = downcast<Element>(targetNode); } m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp()); @@ -2162,23 +2297,23 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* Frame* targetFrame; if (targetIsFrame(newTarget.get(), targetFrame)) { if (targetFrame) - accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard); + accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer); } else if (newTarget) { // 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. if (dragState().source && dragState().shouldDispatchEvents) { // for now we don't care if event handler cancels default behavior, since there is none dispatchDragSrcEvent(eventNames().dragEvent, event); } - accept = dispatchDragEvent(eventNames().dragenterEvent, *newTarget, event, clipboard); + accept = dispatchDragEvent(eventNames().dragenterEvent, *newTarget, event, &dataTransfer); if (!accept) - accept = findDropZone(newTarget.get(), clipboard); + accept = findDropZone(newTarget.get(), &dataTransfer); } if (targetIsFrame(m_dragTarget.get(), targetFrame)) { if (targetFrame) - accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard); + accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer); } else if (m_dragTarget) - dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, clipboard); + dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, &dataTransfer); if (newTarget) { // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that @@ -2189,46 +2324,50 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* Frame* targetFrame; if (targetIsFrame(newTarget.get(), targetFrame)) { if (targetFrame) - accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard); + accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer); } else if (newTarget) { // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier. if (!m_shouldOnlyFireDragOverEvent && dragState().source && dragState().shouldDispatchEvents) { // for now we don't care if event handler cancels default behavior, since there is none dispatchDragSrcEvent(eventNames().dragEvent, event); } - accept = dispatchDragEvent(eventNames().dragoverEvent, *newTarget, event, clipboard); + accept = dispatchDragEvent(eventNames().dragoverEvent, *newTarget, event, &dataTransfer); if (!accept) - accept = findDropZone(newTarget.get(), clipboard); + accept = findDropZone(newTarget.get(), &dataTransfer); m_shouldOnlyFireDragOverEvent = false; } } - m_dragTarget = newTarget.release(); + m_dragTarget = WTFMove(newTarget); return accept; } -void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard) +void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer) { + Ref<Frame> protectedFrame(m_frame); + Frame* targetFrame; if (targetIsFrame(m_dragTarget.get(), targetFrame)) { if (targetFrame) - targetFrame->eventHandler().cancelDragAndDrop(event, clipboard); + targetFrame->eventHandler().cancelDragAndDrop(event, dataTransfer); } else if (m_dragTarget) { if (dragState().source && dragState().shouldDispatchEvents) dispatchDragSrcEvent(eventNames().dragEvent, event); - dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, clipboard); + dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, &dataTransfer); } clearDragState(); } -bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard) +bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer) { + Ref<Frame> protectedFrame(m_frame); + Frame* targetFrame; bool preventedDefault = false; if (targetIsFrame(m_dragTarget.get(), targetFrame)) { if (targetFrame) - preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, clipboard); + preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, dataTransfer); } else if (m_dragTarget) - preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, clipboard); + preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, &dataTransfer); clearDragState(); return preventedDefault; } @@ -2239,44 +2378,53 @@ void EventHandler::clearDragState() m_dragTarget = nullptr; m_capturingMouseEventsElement = nullptr; m_shouldOnlyFireDragOverEvent = false; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) m_sendingEventToSubview = false; #endif } + #endif // ENABLE(DRAG_SUPPORT) -void EventHandler::setCapturingMouseEventsElement(PassRefPtr<Element> element) +void EventHandler::setCapturingMouseEventsElement(Element* element) { m_capturingMouseEventsElement = element; m_eventHandlerWillResetCapturingMouseEventsElement = false; } -MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mev) +MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mouseEvent) { + Ref<Frame> protectedFrame(m_frame); ASSERT(m_frame.document()); - return m_frame.document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mev.position()), mev); + return m_frame.document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mouseEvent.position()), mouseEvent); } -#if ENABLE(SVG) -static inline SVGElementInstance* instanceAssociatedWithShadowTreeElement(Node* referenceNode) +static RenderElement* nearestCommonHoverAncestor(RenderElement* obj1, RenderElement* obj2) { - if (!referenceNode || !referenceNode->isSVGElement()) - return 0; + if (!obj1 || !obj2) + return nullptr; - ShadowRoot* shadowRoot = referenceNode->containingShadowRoot(); - if (!shadowRoot) - return 0; + for (RenderElement* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor()) { + for (RenderElement* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor()) { + if (currObj1 == currObj2) + return currObj1; + } + } - Element* shadowTreeParentElement = shadowRoot->hostElement(); - if (!shadowTreeParentElement || !shadowTreeParentElement->hasTagName(useTag)) - return 0; + return nullptr; +} - return toSVGUseElement(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode); +static bool hierarchyHasCapturingEventListeners(Element* element, const AtomicString& eventName) +{ + for (ContainerNode* curr = element; curr; curr = curr->parentOrShadowHostNode()) { + if (curr->hasCapturingEventListeners(eventName)) + return true; + } + return false; } -#endif -void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& mouseEvent, bool fireMouseOverOut) +void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& platformMouseEvent, bool fireMouseOverOut) { + Ref<Frame> protectedFrame(m_frame); Element* targetElement = nullptr; // If we're capturing, we always go right to that element. @@ -2284,52 +2432,17 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo targetElement = m_capturingMouseEventsElement.get(); else if (targetNode) { // If the target node is a non-element, dispatch on the parent. <rdar://problem/4196646> - if (!targetNode->isElementNode()) - targetElement = targetNode->parentOrShadowHostElement(); - else - targetElement = toElement(targetNode); + while (targetNode && !is<Element>(*targetNode)) + targetNode = targetNode->parentInComposedTree(); + targetElement = downcast<Element>(targetNode); } m_elementUnderMouse = targetElement; -#if ENABLE(SVG) - m_instanceUnderMouse = instanceAssociatedWithShadowTreeElement(targetElement); - - // <use> shadow tree elements may have been recloned, update node under mouse in any case - if (m_lastInstanceUnderMouse) { - SVGElement* lastCorrespondingElement = m_lastInstanceUnderMouse->correspondingElement(); - SVGElement* lastCorrespondingUseElement = m_lastInstanceUnderMouse->correspondingUseElement(); - - if (lastCorrespondingElement && lastCorrespondingUseElement) { - HashSet<SVGElementInstance*> instances = lastCorrespondingElement->instancesForElement(); - - // Locate the recloned shadow tree element for our corresponding instance - HashSet<SVGElementInstance*>::iterator end = instances.end(); - for (HashSet<SVGElementInstance*>::iterator it = instances.begin(); it != end; ++it) { - SVGElementInstance* instance = (*it); - ASSERT(instance->correspondingElement() == lastCorrespondingElement); - - if (instance == m_lastInstanceUnderMouse) - continue; - - if (instance->correspondingUseElement() != lastCorrespondingUseElement) - continue; - - SVGElement* shadowTreeElement = instance->shadowTreeElement(); - if (!shadowTreeElement->inDocument() || m_lastElementUnderMouse == shadowTreeElement) - continue; - - m_lastElementUnderMouse = shadowTreeElement; - m_lastInstanceUnderMouse = instance; - break; - } - } - } -#endif // Fire mouseout/mouseover if the mouse has shifted to a different node. if (fireMouseOverOut) { - RenderLayer* layerForLastNode = layerForNode(m_lastElementUnderMouse.get()); - RenderLayer* layerForNodeUnderMouse = layerForNode(m_elementUnderMouse.get()); + auto scrollableAreaForLastNode = enclosingScrollableArea(m_lastElementUnderMouse.get()); + auto scrollableAreaForNodeUnderMouse = enclosingScrollableArea(m_elementUnderMouse.get()); Page* page = m_frame.page(); if (m_lastElementUnderMouse && (!m_elementUnderMouse || &m_elementUnderMouse->document() != m_frame.document())) { @@ -2338,12 +2451,12 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo if (FrameView* frameView = frame->view()) frameView->mouseExitedContentArea(); } - } else if (page && (layerForLastNode && (!layerForNodeUnderMouse || layerForNodeUnderMouse != layerForLastNode))) { + } else if (page && (scrollableAreaForLastNode && (!scrollableAreaForNodeUnderMouse || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) { // The mouse has moved between layers. if (Frame* frame = m_lastElementUnderMouse->document().frame()) { if (FrameView* frameView = frame->view()) { - if (frameView->containsScrollableArea(layerForLastNode)) - layerForLastNode->mouseExitedContentArea(); + if (frameView->containsScrollableArea(scrollableAreaForLastNode)) + scrollableAreaForLastNode->mouseExitedContentArea(); } } } @@ -2354,12 +2467,12 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo if (FrameView* frameView = frame->view()) frameView->mouseEnteredContentArea(); } - } else if (page && (layerForNodeUnderMouse && (!layerForLastNode || layerForNodeUnderMouse != layerForLastNode))) { + } else if (page && (scrollableAreaForNodeUnderMouse && (!scrollableAreaForLastNode || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) { // The mouse has moved between layers. if (Frame* frame = m_elementUnderMouse->document().frame()) { if (FrameView* frameView = frame->view()) { - if (frameView->containsScrollableArea(layerForNodeUnderMouse)) - layerForNodeUnderMouse->mouseEnteredContentArea(); + if (frameView->containsScrollableArea(scrollableAreaForNodeUnderMouse)) + scrollableAreaForNodeUnderMouse->mouseEnteredContentArea(); } } } @@ -2367,96 +2480,129 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo if (m_lastElementUnderMouse && &m_lastElementUnderMouse->document() != m_frame.document()) { m_lastElementUnderMouse = nullptr; m_lastScrollbarUnderMouse = nullptr; -#if ENABLE(SVG) - m_lastInstanceUnderMouse = 0; -#endif } if (m_lastElementUnderMouse != m_elementUnderMouse) { - // send mouseout event to the old node + // mouseenter and mouseleave events are only dispatched if there is a capturing eventhandler on an ancestor + // or a normal eventhandler on the element itself (they don't bubble). + // This optimization is necessary since these events can cause O(n^2) capturing event-handler checks. + bool hasCapturingMouseEnterListener = hierarchyHasCapturingEventListeners(m_elementUnderMouse.get(), eventNames().mouseenterEvent); + bool hasCapturingMouseLeaveListener = hierarchyHasCapturingEventListeners(m_lastElementUnderMouse.get(), eventNames().mouseleaveEvent); + + RenderElement* oldHoverRenderer = m_lastElementUnderMouse ? m_lastElementUnderMouse->renderer() : nullptr; + RenderElement* newHoverRenderer = m_elementUnderMouse ? m_elementUnderMouse->renderer() : nullptr; + RenderElement* ancestor = nearestCommonHoverAncestor(oldHoverRenderer, newHoverRenderer); + + Vector<Ref<Element>, 32> leftElementsChain; + if (oldHoverRenderer) { + for (RenderElement* curr = oldHoverRenderer; curr && curr != ancestor; curr = curr->hoverAncestor()) { + if (Element* element = curr->element()) + leftElementsChain.append(*element); + } + } else { + // If the old hovered element is not null but it's renderer is, it was probably detached. + // In this case, the old hovered element (and its ancestors) must be updated, to ensure it's normal style is re-applied. + for (Element* element = m_lastElementUnderMouse.get(); element; element = element->parentElement()) + leftElementsChain.append(*element); + } + + Vector<Ref<Element>, 32> enteredElementsChain; + const Element* ancestorElement = ancestor ? ancestor->element() : nullptr; + for (RenderElement* curr = newHoverRenderer; curr; curr = curr->hoverAncestor()) { + if (Element *element = curr->element()) { + if (element == ancestorElement) + break; + enteredElementsChain.append(*element); + } + } + + // Send mouseout event to the old node. if (m_lastElementUnderMouse) - m_lastElementUnderMouse->dispatchMouseEvent(mouseEvent, eventNames().mouseoutEvent, 0, m_elementUnderMouse.get()); - // send mouseover event to the new node + m_lastElementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoutEvent, 0, m_elementUnderMouse.get()); + + // Send mouseleave to the node hierarchy no longer under the mouse. + for (auto& chain : leftElementsChain) { + if (hasCapturingMouseLeaveListener || chain->hasEventListeners(eventNames().mouseleaveEvent)) + chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseleaveEvent, 0, m_elementUnderMouse.get()); + } + + // Send mouseover event to the new node. if (m_elementUnderMouse) - m_elementUnderMouse->dispatchMouseEvent(mouseEvent, eventNames().mouseoverEvent, 0, m_lastElementUnderMouse.get()); + m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoverEvent, 0, m_lastElementUnderMouse.get()); + + // Send mouseleave event to the nodes hierarchy under the mouse. + for (auto& chain : enteredElementsChain) { + if (hasCapturingMouseEnterListener || chain->hasEventListeners(eventNames().mouseenterEvent)) + chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseenterEvent, 0, m_lastElementUnderMouse.get()); + } } m_lastElementUnderMouse = m_elementUnderMouse; -#if ENABLE(SVG) - m_lastInstanceUnderMouse = instanceAssociatedWithShadowTreeElement(m_elementUnderMouse.get()); -#endif } } -bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& mouseEvent, bool setUnder) +bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& platformMouseEvent, bool setUnder) { - if (FrameView* view = m_frame.view()) + Ref<Frame> protectedFrame(m_frame); + + if (auto* view = m_frame.view()) view->disableLayerFlushThrottlingTemporarilyForInteraction(); - updateMouseEventTargetNode(targetNode, mouseEvent, setUnder); + updateMouseEventTargetNode(targetNode, platformMouseEvent, setUnder); - bool swallowEvent = false; + if (m_elementUnderMouse && !m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventType, clickCount)) + return false; - if (m_elementUnderMouse) - swallowEvent = !(m_elementUnderMouse->dispatchMouseEvent(mouseEvent, eventType, clickCount)); + if (eventType != eventNames().mousedownEvent) + return true; - if (!swallowEvent && eventType == eventNames().mousedownEvent) { + // If clicking on a frame scrollbar, do not make any change to which element is focused. + auto* view = m_frame.view(); + if (view && view->scrollbarAtPoint(platformMouseEvent.position())) + return true; - // If clicking on a frame scrollbar, do not mess up with content focus. - if (FrameView* view = m_frame.view()) { - if (view->scrollbarAtPoint(mouseEvent.position())) - return true; - } + // The layout needs to be up to date to determine if an element is focusable. + m_frame.document()->updateLayoutIgnorePendingStylesheets(); - // The layout needs to be up to date to determine if an element is focusable. - m_frame.document()->updateLayoutIgnorePendingStylesheets(); - - // Blur current focus node when a link/button is clicked; this - // is expected by some sites that rely on onChange handlers running - // from form fields before the button click is processed. - - Element* element = m_elementUnderMouse.get(); - - // Walk up the DOM tree to search for an element to focus. - while (element) { - if (element->isMouseFocusable()) { - // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus a - // node on mouse down if it's selected and inside a focused node. It will be - // focused if the user does a mouseup over it, however, because the mouseup - // will set a selection inside it, which will call setFocuseNodeIfNeeded. - if (m_frame.selection().isRange() - && m_frame.selection().toNormalizedRange()->compareNode(element, IGNORE_EXCEPTION) == Range::NODE_INSIDE - && element->isDescendantOf(m_frame.document()->focusedElement())) - return true; - - break; - } - element = element->parentOrShadowHostElement(); - } + // Remove focus from the currently focused element when a link or button is clicked. + // This is expected by some sites that rely on change event handlers running + // from form fields before the button click is processed, behavior that was inherited + // from the user interface of Windows, where pushing a button moves focus to the button. - // Only change the focus when clicking scrollbars if it can transfered to a mouse focusable node. - if ((!element || !element->isMouseFocusable()) && isInsideScrollbar(mouseEvent.position())) - return false; + // Walk up the DOM tree to search for an element to focus. + Element* element; + for (element = m_elementUnderMouse.get(); element; element = element->parentOrShadowHostElement()) { + if (element->isMouseFocusable()) + break; + } - // If focus shift is blocked, we eat the event. Note we should never clear swallowEvent - // if the page already set it (e.g., by canceling default behavior). - if (Page* page = m_frame.page()) { - if (element && element->isMouseFocusable()) { - if (!page->focusController().setFocusedElement(element, &m_frame)) - swallowEvent = true; - } else if (!element || !element->focused()) { - if (!page->focusController().setFocusedElement(0, &m_frame)) - swallowEvent = true; - } + // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus an + // element on mouse down if it's selected and inside a focused element. It will be + // focused if the user does a mouseup over it, however, because the mouseup + // will set a selection inside it, which will also set the focused element. + if (element && m_frame.selection().isRange()) { + if (auto range = m_frame.selection().toNormalizedRange()) { + auto result = range->compareNode(*element); + if (!result.hasException() && result.releaseReturnValue() == Range::NODE_INSIDE && element->isDescendantOf(m_frame.document()->focusedElement())) + return true; } } - return !swallowEvent; + // Only change the focus when clicking scrollbars if it can be transferred to a mouse focusable node. + if (!element && isInsideScrollbar(platformMouseEvent.position())) + return false; + + // If focus shift is blocked, we eat the event. + auto* page = m_frame.page(); + if (page && !page->focusController().setFocusedElement(element, m_frame)) + return false; + + return true; } bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const { if (RenderView* renderView = m_frame.contentRenderer()) { - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(windowPoint); renderView->hitTest(request, result); return result.scrollbar(); @@ -2466,197 +2612,257 @@ bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const } #if !PLATFORM(GTK) + bool EventHandler::shouldTurnVerticalTicksIntoHorizontal(const HitTestResult&, const PlatformWheelEvent&) const { return false; } + #endif -void EventHandler::recordWheelEventDelta(const PlatformWheelEvent& event) +#if !PLATFORM(MAC) + +void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&, const HitTestResult&, RefPtr<Element>&, RefPtr<ContainerNode>&, WeakPtr<ScrollableArea>&, bool&) { - const size_t recentEventCount = 3; - - m_recentWheelEventDeltas.append(FloatSize(event.deltaX(), event.deltaY())); - if (m_recentWheelEventDeltas.size() > recentEventCount) - m_recentWheelEventDeltas.removeFirst(); } -static bool deltaIsPredominantlyVertical(const FloatSize& delta) +void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& event) { - return fabs(delta.height()) > fabs(delta.width()); + m_frame.mainFrame().wheelEventDeltaFilter()->updateFromDelta(FloatSize(event.deltaX(), event.deltaY())); } -EventHandler::DominantScrollGestureDirection EventHandler::dominantScrollGestureDirection() const +bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& event, ContainerNode*, const WeakPtr<ScrollableArea>&) { - bool allVertical = m_recentWheelEventDeltas.size(); - bool allHorizontal = m_recentWheelEventDeltas.size(); + Ref<Frame> protectedFrame(m_frame); - Deque<FloatSize>::const_iterator end = m_recentWheelEventDeltas.end(); - for (Deque<FloatSize>::const_iterator it = m_recentWheelEventDeltas.begin(); it != end; ++it) { - bool isVertical = deltaIsPredominantlyVertical(*it); - allVertical &= isVertical; - allHorizontal &= !isVertical; - } + // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed. + FrameView* view = m_frame.view(); - if (allVertical) - return DominantScrollDirectionVertical; + bool didHandleEvent = view ? view->wheelEvent(event) : false; + m_isHandlingWheelEvent = false; + return didHandleEvent; +} - if (allHorizontal) - return DominantScrollDirectionHorizontal; - - return DominantScrollDirectionNone; +bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode*) +{ + return true; } -bool EventHandler::handleWheelEvent(const PlatformWheelEvent& e) +void EventHandler::platformNotifyIfEndGesture(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&) { - Document* document = m_frame.document(); +} + +IntPoint EventHandler::effectiveMousePositionForSelectionAutoscroll() const +{ + return m_lastKnownMousePosition; +} + +void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&) +{ + clearLatchedState(); +} +#endif + +Widget* EventHandler::widgetForEventTarget(Element* eventTarget) +{ + if (!eventTarget) + return nullptr; - if (!document->renderView()) + auto* target = eventTarget->renderer(); + if (!is<RenderWidget>(target)) + return nullptr; + + return downcast<RenderWidget>(*target).widget(); +} + +static WeakPtr<Widget> widgetForElement(const Element& element) +{ + auto target = element.renderer(); + if (!is<RenderWidget>(target) || !downcast<RenderWidget>(*target).widget()) + return { }; + + return downcast<RenderWidget>(*target).widget()->createWeakPtr(); +} + +bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr<Widget>& widget, const WeakPtr<ScrollableArea>& scrollableArea, ContainerNode* scrollableContainer) +{ + m_isHandlingWheelEvent = false; + + // We do another check on the widget because the event handler can run JS which results in the frame getting destroyed. + if (!widget) return false; + if (scrollableArea) + scrollableArea->setScrolledProgrammatically(false); + + platformNotifyIfEndGesture(event, scrollableArea); + + if (!widget->platformWidget()) + return true; + + return platformCompletePlatformWidgetWheelEvent(event, *widget.get(), scrollableContainer); +} + +bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event) +{ + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) + return false; + + Ref<Frame> protectedFrame(m_frame); RefPtr<FrameView> protector(m_frame.view()); FrameView* view = m_frame.view(); if (!view) return false; +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedWheelEvent(event); + return true; + } +#endif + m_isHandlingWheelEvent = true; setFrameWasScrolledByUser(); - LayoutPoint vPoint = view->windowToContents(e.position()); - - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); - HitTestResult result(vPoint); - document->renderView()->hitTest(request, result); - - bool useLatchedWheelEventElement = e.useLatchedEventElement(); - Element* element = result.innerElement(); - - bool isOverWidget; - if (useLatchedWheelEventElement) { - if (!m_latchedWheelEventElement) { - m_latchedWheelEventElement = element; - m_widgetIsLatched = result.isOverWidget(); - } else - element = m_latchedWheelEventElement.get(); - - isOverWidget = m_widgetIsLatched; - } else { - if (m_latchedWheelEventElement) - m_latchedWheelEventElement = nullptr; - if (m_previousWheelScrolledElement) - m_previousWheelScrolledElement = nullptr; - - isOverWidget = result.isOverWidget(); - } + HitTestRequest request; + HitTestResult result(view->windowToContents(event.position())); + renderView->hitTest(request, result); - // FIXME: It should not be necessary to do this mutation here. - // Instead, the handlers should know convert vertical scrolls - // appropriately. - PlatformWheelEvent event = e; - if (m_baseEventType == PlatformEvent::NoType && shouldTurnVerticalTicksIntoHorizontal(result, e)) - event = event.copyTurningVerticalTicksIntoHorizontalTicks(); + RefPtr<Element> element = result.targetElement(); + RefPtr<ContainerNode> scrollableContainer; + WeakPtr<ScrollableArea> scrollableArea; + bool isOverWidget = result.isOverWidget(); + platformPrepareForWheelEvents(event, result, element, scrollableContainer, scrollableArea, isOverWidget); #if PLATFORM(MAC) - switch (event.phase()) { - case PlatformWheelEventPhaseBegan: - m_recentWheelEventDeltas.clear(); - m_inTrackingScrollGesturePhase = true; - break; - case PlatformWheelEventPhaseEnded: - m_inTrackingScrollGesturePhase = false; - break; - default: - break; - } + if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone) + m_frame.mainFrame().resetLatchingState(); #endif - recordWheelEventDelta(event); + // FIXME: It should not be necessary to do this mutation here. + // Instead, the handlers should know convert vertical scrolls appropriately. + PlatformWheelEvent adjustedEvent = event; + if (shouldTurnVerticalTicksIntoHorizontal(result, event)) + adjustedEvent = event.copyTurningVerticalTicksIntoHorizontalTicks(); + + platformRecordWheelEvent(adjustedEvent); if (element) { - // Figure out which view to send the event to. - RenderElement* target = element->renderer(); - - if (isOverWidget && target && target->isWidget()) { - Widget* widget = toRenderWidget(target)->widget(); - if (widget && passWheelEventToWidget(e, widget)) { - m_isHandlingWheelEvent = false; - return true; + if (isOverWidget) { + if (WeakPtr<Widget> widget = widgetForElement(*element)) { + if (widgetDidHandleWheelEvent(event, *widget.get())) + return completeWidgetWheelEvent(adjustedEvent, widget, scrollableArea, scrollableContainer.get()); } } - if (!element->dispatchWheelEvent(event)) { + if (!element->dispatchWheelEvent(adjustedEvent)) { m_isHandlingWheelEvent = false; + if (scrollableArea && scrollableArea->isScrolledProgrammatically()) { + // Web developer is controlling scrolling, so don't attempt to latch. + clearLatchedState(); + scrollableArea->setScrolledProgrammatically(false); + } + + platformNotifyIfEndGesture(adjustedEvent, scrollableArea); return true; } } + if (scrollableArea) + scrollableArea->setScrolledProgrammatically(false); - // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed. - view = m_frame.view(); - bool didHandleEvent = view ? view->wheelEvent(event) : false; - m_isHandlingWheelEvent = false; - return didHandleEvent; + bool handledEvent = platformCompleteWheelEvent(adjustedEvent, scrollableContainer.get(), scrollableArea); + platformNotifyIfEndGesture(adjustedEvent, scrollableArea); + return handledEvent; +} + +void EventHandler::clearLatchedState() +{ +#if PLATFORM(MAC) + m_frame.mainFrame().resetLatchingState(); +#endif + if (auto filter = m_frame.mainFrame().wheelEventDeltaFilter()) + filter->endFilteringDeltas(); } -void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent) +void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent& wheelEvent) { - if (!startNode || !wheelEvent) + if (!startNode) return; - Element* stopElement = m_previousWheelScrolledElement.get(); - ScrollGranularity granularity = wheelGranularityToScrollGranularity(wheelEvent->deltaMode()); + Ref<Frame> protectedFrame(m_frame); + + FloatSize filteredPlatformDelta(wheelEvent.deltaX(), wheelEvent.deltaY()); + FloatSize filteredVelocity; + if (const PlatformWheelEvent* platformWheelEvent = wheelEvent.wheelEvent()) { + filteredPlatformDelta.setWidth(platformWheelEvent->deltaX()); + filteredPlatformDelta.setHeight(platformWheelEvent->deltaY()); + } - DominantScrollGestureDirection dominantDirection = DominantScrollDirectionNone; - // Workaround for scrolling issues in iTunes (<rdar://problem/14758615>). - if (m_inTrackingScrollGesturePhase && applicationIsITunes()) - dominantDirection = dominantScrollGestureDirection(); +#if PLATFORM(MAC) + ScrollLatchingState* latchedState = m_frame.mainFrame().latchingState(); + Element* stopElement = latchedState ? latchedState->previousWheelScrolledElement() : nullptr; + + if (m_frame.mainFrame().wheelEventDeltaFilter()->isFilteringDeltas()) { + filteredPlatformDelta = m_frame.mainFrame().wheelEventDeltaFilter()->filteredDelta(); + filteredVelocity = m_frame.mainFrame().wheelEventDeltaFilter()->filteredVelocity(); + } +#else + Element* stopElement = nullptr; +#endif - // Break up into two scrolls if we need to. Diagonal movement on - // a MacBook pro is an example of a 2-dimensional mouse wheel event (where both deltaX and deltaY can be set). - if (dominantDirection != DominantScrollDirectionVertical && scrollNode(wheelEvent->deltaX(), granularity, ScrollRight, ScrollLeft, startNode, &stopElement, roundedIntPoint(wheelEvent->absoluteLocation()))) - wheelEvent->setDefaultHandled(); - if (dominantDirection != DominantScrollDirectionHorizontal && scrollNode(wheelEvent->deltaY(), granularity, ScrollDown, ScrollUp, startNode, &stopElement, roundedIntPoint(wheelEvent->absoluteLocation()))) - wheelEvent->setDefaultHandled(); + if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &stopElement, filteredPlatformDelta, filteredVelocity)) + wheelEvent.setDefaultHandled(); - if (!m_latchedWheelEventElement) - m_previousWheelScrolledElement = stopElement; +#if PLATFORM(MAC) + if (latchedState && !latchedState->wheelEventElement()) + latchedState->setPreviousWheelScrolledElement(stopElement); +#endif } #if ENABLE(CONTEXT_MENUS) bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event) { + Ref<Frame> protectedFrame(m_frame); + Document* doc = m_frame.document(); - FrameView* v = m_frame.view(); - if (!v) + FrameView* view = m_frame.view(); + if (!view) return false; - + // Clear mouse press state to avoid initiating a drag while context menu is up. m_mousePressed = false; bool swallowEvent; - LayoutPoint viewportPos = v->windowToContents(event.position()); - HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = doc->prepareMouseEvent(request, viewportPos, event); + LayoutPoint viewportPos = view->windowToContents(event.position()); + HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = doc->prepareMouseEvent(request, viewportPos, event); + + // Do not show context menus when clicking on scrollbars. + if (mouseEvent.scrollbar() || view->scrollbarAtPoint(event.position())) + return false; if (m_frame.editor().behavior().shouldSelectOnContextualMenuClick() && !m_frame.selection().contains(viewportPos) - && !mev.scrollbar() // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse. // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items // available for text selections. But only if we're above text. - && (m_frame.selection().isContentEditable() || (mev.targetNode() && mev.targetNode()->isTextNode()))) { + && (m_frame.selection().selection().isContentEditable() || (mouseEvent.targetNode() && mouseEvent.targetNode()->isTextNode()))) { m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection - selectClosestWordOrLinkFromMouseEvent(mev); + selectClosestContextualWordOrLinkFromMouseEvent(mouseEvent); } - swallowEvent = !dispatchMouseEvent(eventNames().contextmenuEvent, mev.targetNode(), true, 0, event, false); + swallowEvent = !dispatchMouseEvent(eventNames().contextmenuEvent, mouseEvent.targetNode(), true, 0, event, false); return swallowEvent; } bool EventHandler::sendContextMenuEventForKey() { + Ref<Frame> protectedFrame(m_frame); + FrameView* view = m_frame.view(); if (!view) return false; @@ -2670,7 +2876,7 @@ bool EventHandler::sendContextMenuEventForKey() static const int kContextMenuMargin = 1; -#if OS(WINDOWS) && !OS(WINCE) +#if OS(WINDOWS) int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT); #else int rightAligned = 0; @@ -2678,8 +2884,8 @@ bool EventHandler::sendContextMenuEventForKey() IntPoint location; Element* focusedElement = doc->focusedElement(); - FrameSelection& selection = m_frame.selection(); - Position start = selection.selection().start(); + const VisibleSelection& selection = m_frame.selection().selection(); + Position start = selection.start(); if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) { RefPtr<Range> selectionRange = selection.toNormalizedRange(); @@ -2693,8 +2899,9 @@ bool EventHandler::sendContextMenuEventForKey() RenderBoxModelObject* box = focusedElement->renderBoxModelObject(); if (!box) return false; - IntRect clippedRect = box->pixelSnappedAbsoluteClippedOverflowRect(); - location = IntPoint(clippedRect.x(), clippedRect.maxY() - 1); + + IntRect boundingBoxRect = box->absoluteBoundingBoxRect(true); + location = IntPoint(boundingBoxRect.x(), boundingBoxRect.maxY() - 1); } else { location = IntPoint( rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin, @@ -2713,7 +2920,7 @@ bool EventHandler::sendContextMenuEventForKey() // Use the focused node as the target for hover and active. HitTestResult result(position); result.setInnerNode(targetNode); - doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowShadowContent, result.innerElement()); + doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent, result.targetElement()); // The contextmenu event is a mouse event even when invoked using the keyboard. // This is required for web compatibility. @@ -2724,9 +2931,9 @@ bool EventHandler::sendContextMenuEventForKey() PlatformEvent::Type eventType = PlatformEvent::MousePressed; #endif - PlatformMouseEvent mouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime()); + PlatformMouseEvent platformMouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime(), ForceAtClick, NoTap); - return !dispatchMouseEvent(eventNames().contextmenuEvent, targetNode, true, 0, mouseEvent, false); + return sendContextMenuEvent(platformMouseEvent); } #endif // ENABLE(CONTEXT_MENUS) @@ -2746,31 +2953,33 @@ void EventHandler::scheduleCursorUpdate() void EventHandler::dispatchFakeMouseMoveEventSoon() { +#if !ENABLE(IOS_TOUCH_EVENTS) if (m_mousePressed) return; if (m_mousePositionIsUnknown) return; - if (!m_frame.settings().deviceSupportsMouse()) - return; + if (Page* page = m_frame.page()) { + if (!page->chrome().client().shouldDispatchFakeMouseMoveEvents()) + return; + } // If the content has ever taken longer than fakeMouseMoveShortInterval we // reschedule the timer and use a longer time. This will cause the content // to receive these moves only after the user is done scrolling, reducing // pauses during the scroll. - if (m_maxMouseMovedDuration > fakeMouseMoveDurationThreshold) { - if (m_fakeMouseMoveEventTimer.isActive()) - m_fakeMouseMoveEventTimer.stop(); - m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveLongInterval); - } else { - if (!m_fakeMouseMoveEventTimer.isActive()) - m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveShortInterval); - } + if (m_fakeMouseMoveEventTimer.isActive()) + m_fakeMouseMoveEventTimer.stop(); + m_fakeMouseMoveEventTimer.startOneShot(m_maxMouseMovedDuration > fakeMouseMoveDurationThreshold ? fakeMouseMoveLongInterval : fakeMouseMoveShortInterval); +#endif } void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad) { +#if ENABLE(IOS_TOUCH_EVENTS) + UNUSED_PARAM(quad); +#else FrameView* view = m_frame.view(); if (!view) return; @@ -2779,21 +2988,19 @@ void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad) return; dispatchFakeMouseMoveEventSoon(); +#endif } +#if !ENABLE(IOS_TOUCH_EVENTS) void EventHandler::cancelFakeMouseMoveEvent() { m_fakeMouseMoveEventTimer.stop(); } -void EventHandler::fakeMouseMoveEventTimerFired(Timer<EventHandler>& timer) +void EventHandler::fakeMouseMoveEventTimerFired() { - ASSERT_UNUSED(timer, &timer == &m_fakeMouseMoveEventTimer); ASSERT(!m_mousePressed); - if (!m_frame.settings().deviceSupportsMouse()) - return; - FrameView* view = m_frame.view(); if (!view) return; @@ -2806,9 +3013,10 @@ void EventHandler::fakeMouseMoveEventTimerFired(Timer<EventHandler>& timer) bool altKey; bool metaKey; PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey); - PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, currentTime()); + PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, currentTime(), 0, NoTap); mouseMoved(fakeMouseMoveEvent); } +#endif // !ENABLE(IOS_TOUCH_EVENTS) void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet) { @@ -2818,43 +3026,45 @@ void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet) void EventHandler::resizeLayerDestroyed() { ASSERT(m_resizeLayer); - m_resizeLayer = 0; + m_resizeLayer = nullptr; } -void EventHandler::hoverTimerFired(Timer<EventHandler>&) +void EventHandler::hoverTimerFired() { m_hoverTimer.stop(); ASSERT(m_frame.document()); - if (RenderView* renderer = m_frame.contentRenderer()) { + Ref<Frame> protectedFrame(m_frame); + + if (RenderView* renderView = m_frame.contentRenderer()) { if (FrameView* view = m_frame.view()) { - HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); - renderer->hitTest(request, result); - m_frame.document()->updateHoverActiveState(request, result.innerElement()); + renderView->hitTest(request, result); + m_frame.document()->updateHoverActiveState(request, result.targetElement()); } } } -bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& evt) +bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& event) { // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do. // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and // lower case variants are present in a document, the correct element is matched based on Shift key state. // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively. - ASSERT(!(accessKeyModifiers() & PlatformEvent::ShiftKey)); - if ((evt.modifiers() & ~PlatformEvent::ShiftKey) != accessKeyModifiers()) + ASSERT(!accessKeyModifiers().contains(PlatformEvent::Modifier::ShiftKey)); + + if ((event.modifiers() - PlatformEvent::Modifier::ShiftKey) != accessKeyModifiers()) return false; - String key = evt.unmodifiedText(); - Element* elem = m_frame.document()->getElementByAccessKey(key.lower()); - if (!elem) + Element* element = m_frame.document()->getElementByAccessKey(event.unmodifiedText()); + if (!element) return false; - elem->accessKeyAction(false); + element->accessKeyAction(false); return true; } -#if !PLATFORM(MAC) || PLATFORM(IOS) +#if !PLATFORM(MAC) bool EventHandler::needsKeyboardEventDisambiguationQuirks() const { return false; @@ -2883,17 +3093,57 @@ bool EventHandler::isKeyEventAllowedInFullScreen(const PlatformKeyboardEvent& ke } #endif -bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) +bool EventHandler::keyEvent(const PlatformKeyboardEvent& keyEvent) { + Document* topDocument = m_frame.document() ? &m_frame.document()->topDocument() : nullptr; + bool savedUserDidInteractWithPage = topDocument ? topDocument->userDidInteractWithPage() : false; + bool wasHandled = internalKeyEvent(keyEvent); + + // If the key event was not handled, do not treat it as user interaction with the page. + if (topDocument && !wasHandled) + topDocument->setUserDidInteractWithPage(savedUserDidInteractWithPage); + + return wasHandled; +} + +bool EventHandler::internalKeyEvent(const PlatformKeyboardEvent& initialKeyEvent) +{ + Ref<Frame> protectedFrame(m_frame); RefPtr<FrameView> protector(m_frame.view()); + LOG(Editing, "EventHandler %p keyEvent (text %s keyIdentifier %s)", this, initialKeyEvent.text().utf8().data(), initialKeyEvent.keyIdentifier().utf8().data()); + +#if ENABLE(POINTER_LOCK) + if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE && m_frame.page()->pointerLockController().element()) { + m_frame.page()->pointerLockController().requestPointerUnlockAndForceCursorVisible(); + } +#endif + + if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) { + if (auto* page = m_frame.page()) { + if (auto* validationMessageClient = page->validationMessageClient()) + validationMessageClient->hideAnyValidationMessage(); + } + } + #if ENABLE(FULLSCREEN_API) - if (m_frame.document()->webkitIsFullScreen() && !isKeyEventAllowedInFullScreen(initialKeyEvent)) - return false; + if (m_frame.document()->webkitIsFullScreen()) { + if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) { + m_frame.document()->webkitCancelFullScreen(); + return true; + } + + if (!isKeyEventAllowedInFullScreen(initialKeyEvent)) + return false; + } #endif - if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL) - capsLockStateMayHaveChanged(); + if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL) { + if (auto* element = m_frame.document()->focusedElement()) { + if (is<HTMLInputElement>(*element)) + downcast<HTMLInputElement>(*element).capsLockStateMayHaveChanged(); + } + } #if ENABLE(PAN_SCROLLING) if (m_frame.mainFrame().eventHandler().panScrollInProgress()) { @@ -2912,7 +3162,7 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) if (!element) return false; - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); UserTypingGestureIndicator typingGestureIndicator(m_frame); if (FrameView* view = m_frame.view()) @@ -2939,13 +3189,13 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) PlatformKeyboardEvent keyDownEvent = initialKeyEvent; if (keyDownEvent.type() != PlatformEvent::RawKeyDown) keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown, backwardCompatibilityMode); - RefPtr<KeyboardEvent> keydown = KeyboardEvent::create(keyDownEvent, m_frame.document()->defaultView()); + Ref<KeyboardEvent> keydown = KeyboardEvent::create(keyDownEvent, m_frame.document()->defaultView()); if (matchedAnAccessKey) keydown->setDefaultPrevented(true); keydown->setTarget(element); if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) { - element->dispatchEvent(keydown, IGNORE_EXCEPTION); + element->dispatchEvent(keydown); // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame. bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame(); return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; @@ -2966,8 +3216,11 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) keydown->setTarget(element); keydown->setDefaultHandled(); } + + if (accessibilityPreventsEventPropogation(keydown)) + keydown->stopPropagation(); - element->dispatchEvent(keydown, IGNORE_EXCEPTION); + element->dispatchEvent(keydown); // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame. bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame(); bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; @@ -2986,24 +3239,24 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char, backwardCompatibilityMode); if (keyPressEvent.text().isEmpty()) return keydownResult; - RefPtr<KeyboardEvent> keypress = KeyboardEvent::create(keyPressEvent, m_frame.document()->defaultView()); + Ref<KeyboardEvent> keypress = KeyboardEvent::create(keyPressEvent, m_frame.document()->defaultView()); keypress->setTarget(element); if (keydownResult) keypress->setDefaultPrevented(true); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) keypress->keypressCommands() = keydown->keypressCommands(); #endif - element->dispatchEvent(keypress, IGNORE_EXCEPTION); + element->dispatchEvent(keypress); return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled(); } static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier) { - DEFINE_STATIC_LOCAL(AtomicString, Down, ("Down", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, Up, ("Up", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, Left, ("Left", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, Right, ("Right", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> Down("Down", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> Up("Up", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> Left("Left", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> Right("Right", AtomicString::ConstructFromLiteral); FocusDirection retVal = FocusDirectionNone; @@ -3019,18 +3272,54 @@ static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier) return retVal; } -static void handleKeyboardSelectionMovement(FrameSelection& selection, KeyboardEvent* event) +static void setInitialKeyboardSelection(Frame& frame, SelectionDirection direction) { - if (!event) + Document* document = frame.document(); + if (!document) + return; + + FrameSelection& selection = frame.selection(); + + if (!selection.isNone()) return; - bool isOptioned = event->getModifierState("Alt"); - bool isCommanded = event->getModifierState("Meta"); + Element* focusedElement = document->focusedElement(); + VisiblePosition visiblePosition; + + switch (direction) { + case DirectionBackward: + case DirectionLeft: + if (focusedElement) + visiblePosition = VisiblePosition(positionBeforeNode(focusedElement)); + else + visiblePosition = endOfDocument(document); + break; + case DirectionForward: + case DirectionRight: + if (focusedElement) + visiblePosition = VisiblePosition(positionAfterNode(focusedElement)); + else + visiblePosition = startOfDocument(document); + break; + } + + AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false }); + selection.setSelection(visiblePosition, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent); +} +static void handleKeyboardSelectionMovement(Frame& frame, KeyboardEvent& event) +{ + FrameSelection& selection = frame.selection(); + + bool isCommanded = event.getModifierState("Meta"); + bool isOptioned = event.getModifierState("Alt"); + bool isSelection = !selection.isNone(); + + FrameSelection::EAlteration alternation = event.getModifierState("Shift") ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove; SelectionDirection direction = DirectionForward; TextGranularity granularity = CharacterGranularity; - switch (focusDirectionForKey(event->keyIdentifier())) { + switch (focusDirectionForKey(event.keyIdentifier())) { case FocusDirectionNone: return; case FocusDirectionForward: @@ -3055,42 +3344,68 @@ static void handleKeyboardSelectionMovement(FrameSelection& selection, KeyboardE break; } - FrameSelection::EAlteration alternation = event->getModifierState("Shift") ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove; - selection.modify(alternation, direction, granularity, UserTriggered); - event->setDefaultHandled(); + if (isSelection) + selection.modify(alternation, direction, granularity, UserTriggered); + else + setInitialKeyboardSelection(frame, direction); + + event.setDefaultHandled(); } -void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent* event) +void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent& event) { - if (event->type() == eventNames().keydownEvent) { + if (event.type() == eventNames().keydownEvent) { if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) - handleKeyboardSelectionMovement(m_frame.selection(), event); + handleKeyboardSelectionMovement(m_frame, event); } } -void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event) +bool EventHandler::accessibilityPreventsEventPropogation(KeyboardEvent& event) { - if (event->type() == eventNames().keydownEvent) { +#if PLATFORM(COCOA) + if (!AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) + return false; + + if (!m_frame.settings().preventKeyboardDOMEventDispatch()) + return false; + + // Check for key events that are relevant to accessibility: tab and arrows keys that change focus + if (event.keyIdentifier() == "U+0009") + return true; + FocusDirection direction = focusDirectionForKey(event.keyIdentifier()); + if (direction != FocusDirectionNone) + return true; +#else + UNUSED_PARAM(event); +#endif + return false; +} + +void EventHandler::defaultKeyboardEventHandler(KeyboardEvent& event) +{ + Ref<Frame> protectedFrame(m_frame); + + if (event.type() == eventNames().keydownEvent) { m_frame.editor().handleKeyboardEvent(event); - if (event->defaultHandled()) + if (event.defaultHandled()) return; - if (event->keyIdentifier() == "U+0009") + if (event.keyIdentifier() == "U+0009") defaultTabEventHandler(event); - else if (event->keyIdentifier() == "U+0008") + else if (event.keyIdentifier() == "U+0008") defaultBackspaceEventHandler(event); else { - FocusDirection direction = focusDirectionForKey(event->keyIdentifier()); + FocusDirection direction = focusDirectionForKey(event.keyIdentifier()); if (direction != FocusDirectionNone) defaultArrowEventHandler(direction, event); } handleKeyboardSelectionMovementForAccessibility(event); } - if (event->type() == eventNames().keypressEvent) { + if (event.type() == eventNames().keypressEvent) { m_frame.editor().handleKeyboardEvent(event); - if (event->defaultHandled()) + if (event.defaultHandled()) return; - if (event->charCode() == ' ') + if (event.charCode() == ' ') defaultSpaceEventHandler(event); } } @@ -3104,18 +3419,15 @@ bool EventHandler::dragHysteresisExceeded(const IntPoint& floatDragViewportLocat bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation) const { - FrameView* view = m_frame.view(); - if (!view) - return false; - IntPoint dragLocation = view->windowToContents(flooredIntPoint(dragViewportLocation)); - IntSize delta = dragLocation - m_mouseDownPos; - int threshold = GeneralDragHysteresis; switch (dragState().type) { case DragSourceActionSelection: threshold = TextDragHysteresis; break; case DragSourceActionImage: +#if ENABLE(ATTACHMENT_ELEMENT) + case DragSourceActionAttachment: +#endif threshold = ImageDragHysteresis; break; case DragSourceActionLink: @@ -3128,46 +3440,46 @@ bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation ASSERT_NOT_REACHED(); } - return abs(delta.width()) >= threshold || abs(delta.height()) >= threshold; + return mouseMovementExceedsThreshold(dragViewportLocation, threshold); } -void EventHandler::freeClipboard() +void EventHandler::invalidateDataTransfer() { - if (!dragState().clipboard) + if (!dragState().dataTransfer) return; - dragState().clipboard->setAccessPolicy(ClipboardNumb); - dragState().clipboard = 0; + dragState().dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); + dragState().dataTransfer = nullptr; } void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation) { // Send a hit test request so that RenderLayer gets a chance to update the :hover and :active pseudoclasses. - HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent); prepareMouseEvent(request, event); if (dragState().source && dragState().shouldDispatchEvents) { - dragState().clipboard->setDestinationOperation(operation); + dragState().dataTransfer->setDestinationOperation(operation); // For now we don't care if event handler cancels default behavior, since there is no default behavior. dispatchDragSrcEvent(eventNames().dragendEvent, event); } - freeClipboard(); - dragState().source = 0; + invalidateDataTransfer(); + dragState().source = nullptr; // In case the drag was ended due to an escape key press we need to ensure // that consecutive mousemove events don't reinitiate the drag and drop. m_mouseDownMayStartDrag = false; } -void EventHandler::updateDragStateAfterEditDragIfNeeded(Element* rootEditableElement) +void EventHandler::updateDragStateAfterEditDragIfNeeded(Element& rootEditableElement) { // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editable element. - if (dragState().source && !dragState().source->inDocument()) - dragState().source = rootEditableElement; + if (dragState().source && !dragState().source->isConnected()) + dragState().source = &rootEditableElement; } // Return value indicates if we should continue "default processing", i.e., whether event handler canceled. bool EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent& event) { - return !dispatchDragEvent(eventType, *dragState().source, event, dragState().clipboard.get()); + return !dispatchDragEvent(eventType, *dragState().source, event, dragState().dataTransfer.get()); } static bool ExactlyOneBitSet(DragSourceAction n) @@ -3186,6 +3498,8 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr return false; } + Ref<Frame> protectedFrame(m_frame); + if (eventLoopHandleMouseDragged(event)) return true; @@ -3195,11 +3509,11 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr dragState().shouldDispatchEvents = (updateDragSourceActionsAllowed() & DragSourceActionDHTML); // try to find an element that wants to be dragged - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(m_mouseDownPos); m_frame.contentRenderer()->hitTest(request, result); if (m_frame.page()) - dragState().source = m_frame.page()->dragController().draggableElement(&m_frame, result.innerElement(), m_mouseDownPos, dragState()); + dragState().source = m_frame.page()->dragController().draggableElement(&m_frame, result.targetElement(), m_mouseDownPos, dragState()); if (!dragState().source) m_mouseDownMayStartDrag = false; // no element is draggable @@ -3217,7 +3531,7 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr } else if (!(dragState().type & (DragSourceActionDHTML | DragSourceActionLink))) { // ... but only bail if we're not over an unselectable element. m_mouseDownMayStartDrag = false; - dragState().source = 0; + dragState().source = nullptr; // ... but if this was the first click in the window, we don't even want to start selection if (eventActivatedView(event.event())) m_mouseDownMayStartSelect = false; @@ -3236,9 +3550,16 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr if (!ExactlyOneBitSet(dragState().type)) { ASSERT((dragState().type & DragSourceActionSelection)); +#if ENABLE(ATTACHMENT_ELEMENT) + ASSERT((dragState().type & ~DragSourceActionSelection) == DragSourceActionDHTML + || (dragState().type & ~DragSourceActionSelection) == DragSourceActionImage + || (dragState().type & ~DragSourceActionSelection) == DragSourceActionAttachment + || (dragState().type & ~DragSourceActionSelection) == DragSourceActionLink); +#else ASSERT((dragState().type & ~DragSourceActionSelection) == DragSourceActionDHTML || (dragState().type & ~DragSourceActionSelection) == DragSourceActionImage || (dragState().type & ~DragSourceActionSelection) == DragSourceActionLink); +#endif dragState().type = DragSourceActionSelection; } @@ -3256,10 +3577,10 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr DragOperation srcOp = DragOperationNone; - // This does work only if we missed a dragEnd. Do it anyway, just to make sure the old clipboard gets numbed. - freeClipboard(); + // This does work only if we missed a dragEnd. Do it anyway, just to make sure the old dataTransfer gets numbed. + invalidateDataTransfer(); - dragState().clipboard = createDraggingClipboard(); + dragState().dataTransfer = createDraggingDataTransfer(); if (dragState().shouldDispatchEvents) { // Check to see if the is a DOM based drag. If it is, get the DOM specified drag image and offset. @@ -3268,30 +3589,33 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr // FIXME: This doesn't work correctly with transforms. FloatPoint absPos = renderer->localToAbsolute(); IntSize delta = m_mouseDownPos - roundedIntPoint(absPos); - dragState().clipboard->setDragImage(dragState().source.get(), delta.width(), delta.height()); + dragState().dataTransfer->setDragImage(dragState().source.get(), delta.width(), delta.height()); } else { // The renderer has disappeared, this can happen if the onStartDrag handler has hidden // the element in some way. In this case we just kill the drag. m_mouseDownMayStartDrag = false; - goto cleanupDrag; + invalidateDataTransfer(); + dragState().source = nullptr; + + return true; } } m_mouseDownMayStartDrag = dispatchDragSrcEvent(eventNames().dragstartEvent, m_mouseDown) - && !m_frame.selection().isInPasswordField(); + && !m_frame.selection().selection().isInPasswordField(); - // Invalidate clipboard here against anymore pasteboard writing for security. The drag + // Invalidate dataTransfer here against anymore pasteboard writing for security. The drag // image can still be changed as we drag, but not the pasteboard data. - dragState().clipboard->setAccessPolicy(ClipboardImageWritable); + dragState().dataTransfer->setAccessPolicy(DataTransferAccessPolicy::ImageWritable); if (m_mouseDownMayStartDrag) { // Gather values from DHTML element, if it set any. - srcOp = dragState().clipboard->sourceOperation(); + srcOp = dragState().dataTransfer->sourceOperation(); // Yuck, a draggedImage:moveTo: message can be fired as a result of kicking off the // drag with dragImage! Because of that dumb reentrancy, we may think we've not // started the drag when that happens. So we have to assume it's started before we kick it off. - dragState().clipboard->setDragHasStarted(); + dragState().dataTransfer->setDragHasStarted(); } } @@ -3311,23 +3635,37 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr } } -cleanupDrag: if (!m_mouseDownMayStartDrag) { // Something failed to start the drag, clean up. - freeClipboard(); - dragState().source = 0; + invalidateDataTransfer(); + dragState().source = nullptr; } // No more default handling (like selection), whether we're past the hysteresis bounds or not return true; } #endif // ENABLE(DRAG_SUPPORT) - + +bool EventHandler::mouseMovementExceedsThreshold(const FloatPoint& viewportLocation, int pointsThreshold) const +{ + FrameView* view = m_frame.view(); + if (!view) + return false; + IntPoint location = view->windowToContents(flooredIntPoint(viewportLocation)); + IntSize delta = location - m_mouseDownPos; + + return abs(delta.width()) >= pointsThreshold || abs(delta.height()) >= pointsThreshold; +} + bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEvent, TextEventInputType inputType) { + LOG(Editing, "EventHandler %p handleTextInputEvent (text %s)", this, text.utf8().data()); + // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline), // and avoid dispatching text input events from keydown default handlers. - ASSERT(!underlyingEvent || !underlyingEvent->isKeyboardEvent() || static_cast<KeyboardEvent*>(underlyingEvent)->type() == eventNames().keypressEvent); + ASSERT(!is<KeyboardEvent>(underlyingEvent) || downcast<KeyboardEvent>(*underlyingEvent).type() == eventNames().keypressEvent); + + Ref<Frame> protectedFrame(m_frame); EventTarget* target; if (underlyingEvent) @@ -3340,32 +3678,31 @@ bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve if (FrameView* view = m_frame.view()) view->disableLayerFlushThrottlingTemporarilyForInteraction(); - RefPtr<TextEvent> event = TextEvent::create(m_frame.document()->domWindow(), text, inputType); + Ref<TextEvent> event = TextEvent::create(m_frame.document()->domWindow(), text, inputType); event->setUnderlyingEvent(underlyingEvent); - target->dispatchEvent(event, IGNORE_EXCEPTION); + target->dispatchEvent(event); return event->defaultHandled(); } -bool EventHandler::isKeyboardOptionTab(KeyboardEvent* event) +bool EventHandler::isKeyboardOptionTab(KeyboardEvent& event) { - return event - && (event->type() == eventNames().keydownEvent || event->type() == eventNames().keypressEvent) - && event->altKey() - && event->keyIdentifier() == "U+0009"; + return (event.type() == eventNames().keydownEvent || event.type() == eventNames().keypressEvent) + && event.altKey() + && event.keyIdentifier() == "U+0009"; } -bool EventHandler::eventInvertsTabsToLinksClientCallResult(KeyboardEvent* event) +bool EventHandler::eventInvertsTabsToLinksClientCallResult(KeyboardEvent& event) { -#if PLATFORM(MAC) || PLATFORM(EFL) - return EventHandler::isKeyboardOptionTab(event); +#if PLATFORM(COCOA) + return isKeyboardOptionTab(event); #else UNUSED_PARAM(event); return false; #endif } -bool EventHandler::tabsToLinks(KeyboardEvent* event) const +bool EventHandler::tabsToLinks(KeyboardEvent& event) const { // FIXME: This function needs a better name. It can be called for keypresses other than Tab when spatial navigation is enabled. @@ -3377,23 +3714,25 @@ bool EventHandler::tabsToLinks(KeyboardEvent* event) const return eventInvertsTabsToLinksClientCallResult(event) ? !tabsToLinksClientCallResult : tabsToLinksClientCallResult; } -void EventHandler::defaultTextInputEventHandler(TextEvent* event) +void EventHandler::defaultTextInputEventHandler(TextEvent& event) { if (m_frame.editor().handleTextEvent(event)) - event->setDefaultHandled(); + event.setDefaultHandled(); } -void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event) +void EventHandler::defaultSpaceEventHandler(KeyboardEvent& event) { - ASSERT(event->type() == eventNames().keypressEvent); + Ref<Frame> protectedFrame(m_frame); + + ASSERT(event.type() == eventNames().keypressEvent); - if (event->ctrlKey() || event->metaKey() || event->altKey() || event->altGraphKey()) + if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey()) return; - ScrollLogicalDirection direction = event->shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward; + ScrollLogicalDirection direction = event.shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward; if (logicalScrollOverflow(direction, ScrollByPage)) { - event->setDefaultHandled(); + event.setDefaultHandled(); return; } @@ -3402,14 +3741,14 @@ void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event) return; if (view->logicalScroll(direction, ScrollByPage)) - event->setDefaultHandled(); + event.setDefaultHandled(); } -void EventHandler::defaultBackspaceEventHandler(KeyboardEvent* event) +void EventHandler::defaultBackspaceEventHandler(KeyboardEvent& event) { - ASSERT(event->type() == eventNames().keydownEvent); + ASSERT(event.type() == eventNames().keydownEvent); - if (event->ctrlKey() || event->metaKey() || event->altKey() || event->altGraphKey()) + if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey()) return; if (!m_frame.editor().behavior().shouldNavigateBackOnBackspace()) @@ -3424,21 +3763,21 @@ void EventHandler::defaultBackspaceEventHandler(KeyboardEvent* event) bool handledEvent = false; - if (event->shiftKey()) + if (event.shiftKey()) handledEvent = page->backForward().goForward(); else handledEvent = page->backForward().goBack(); if (handledEvent) - event->setDefaultHandled(); + event.setDefaultHandled(); } -void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent* event) +void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent& event) { - ASSERT(event->type() == eventNames().keydownEvent); + ASSERT(event.type() == eventNames().keydownEvent); - if (event->ctrlKey() || event->metaKey() || event->altGraphKey() || event->shiftKey()) + if (event.ctrlKey() || event.metaKey() || event.altGraphKey() || event.shiftKey()) return; Page* page = m_frame.page(); @@ -3454,15 +3793,17 @@ void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, Keybo return; if (page->focusController().advanceFocus(focusDirection, event)) - event->setDefaultHandled(); + event.setDefaultHandled(); } -void EventHandler::defaultTabEventHandler(KeyboardEvent* event) +void EventHandler::defaultTabEventHandler(KeyboardEvent& event) { - ASSERT(event->type() == eventNames().keydownEvent); + Ref<Frame> protectedFrame(m_frame); + + ASSERT(event.type() == eventNames().keydownEvent); // We should only advance focus on tabs if no special modifier keys are held down. - if (event->ctrlKey() || event->metaKey() || event->altGraphKey()) + if (event.ctrlKey() || event.metaKey() || event.altGraphKey()) return; Page* page = m_frame.page(); @@ -3471,29 +3812,19 @@ void EventHandler::defaultTabEventHandler(KeyboardEvent* event) if (!page->tabKeyCyclesThroughElements()) return; - FocusDirection focusDirection = event->shiftKey() ? FocusDirectionBackward : FocusDirectionForward; + FocusDirection focusDirection = event.shiftKey() ? FocusDirectionBackward : FocusDirectionForward; // Tabs can be used in design mode editing. if (m_frame.document()->inDesignMode()) return; if (page->focusController().advanceFocus(focusDirection, event)) - event->setDefaultHandled(); -} - -void EventHandler::capsLockStateMayHaveChanged() -{ - Document* d = m_frame.document(); - if (Element* element = d->focusedElement()) { - if (RenderObject* r = element->renderer()) { - if (r->isTextField()) - toRenderTextControlSingleLine(r)->capsLockStateMayHaveChanged(); - } - } + event.setDefaultHandled(); } void EventHandler::sendScrollEvent() { + Ref<Frame> protectedFrame(m_frame); setFrameWasScrolledByUser(); if (m_frame.view() && m_frame.document()) m_frame.document()->eventQueue().enqueueOrDispatchScrollEvent(*m_frame.document()); @@ -3506,30 +3837,27 @@ void EventHandler::setFrameWasScrolledByUser() v->setWasScrolledByUser(true); } -bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev, Scrollbar* scrollbar) +bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mouseEvent, Scrollbar* scrollbar) { if (!scrollbar || !scrollbar->enabled()) return false; setFrameWasScrolledByUser(); - return scrollbar->mouseDown(mev.event()); + return scrollbar->mouseDown(mouseEvent.event()); } -// If scrollbar (under mouse) is different from last, send a mouse exited. Set -// last to scrollbar if setLast is true; else set last to nullptr. -void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, bool setLast) +// If scrollbar (under mouse) is different from last, send a mouse exited. +void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, SetOrClearLastScrollbar setOrClear) { - if (m_lastScrollbarUnderMouse.get() != scrollbar) { + if (m_lastScrollbarUnderMouse != scrollbar) { // Send mouse exited to the old scrollbar. if (m_lastScrollbarUnderMouse) m_lastScrollbarUnderMouse->mouseExited(); // Send mouse entered if we're setting a new scrollbar. - if (scrollbar && setLast) + if (scrollbar && setOrClear == SetOrClearLastScrollbar::Set) { scrollbar->mouseEntered(); - - if (setLast && scrollbar) m_lastScrollbarUnderMouse = scrollbar->createWeakPtr(); - else + } else m_lastScrollbarUnderMouse = nullptr; } } @@ -3560,6 +3888,7 @@ static HitTestResult hitTestResultInFrame(Frame* frame, const LayoutPoint& point if (!frame || !frame->contentRenderer()) return result; + if (frame->view()) { IntRect rect = frame->view()->visibleContentRect(); if (!rect.contains(roundedIntPoint(point))) @@ -3571,6 +3900,8 @@ static HitTestResult hitTestResultInFrame(Frame* frame, const LayoutPoint& point bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) { + Ref<Frame> protectedFrame(m_frame); + // First build up the lists to use for the 'touches', 'targetTouches' and 'changedTouches' attributes // in the JS event. See http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/ // for an overview of how these lists fit together. @@ -3594,21 +3925,18 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) const Vector<PlatformTouchPoint>& points = event.touchPoints(); - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); - unsigned i; bool freshTouchEvents = true; bool allTouchReleased = true; - for (i = 0; i < points.size(); ++i) { - const PlatformTouchPoint& point = points[i]; + for (auto& point : points) { if (point.state() != PlatformTouchPoint::TouchPressed) freshTouchEvents = false; if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled) allTouchReleased = false; } - for (i = 0; i < points.size(); ++i) { - const PlatformTouchPoint& point = points[i]; + for (auto& point : points) { PlatformTouchPoint::State pointState = point.state(); LayoutPoint pagePoint = documentPointForWindowPoint(m_frame, point.pos()); @@ -3656,14 +3984,13 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) } else continue; - // FIXME: This code should use Element* instead of Node*. - Node* node = result.innerElement(); - ASSERT(node); + Element* element = result.targetElement(); + ASSERT(element); - if (InspectorInstrumentation::handleTouchEvent(m_frame.page(), node)) + if (element && InspectorInstrumentation::handleTouchEvent(m_frame, *element)) return true; - Document& doc = node->document(); + Document& doc = element->document(); // Record the originating touch document even if it does not have a touch listener. if (freshTouchEvents) { m_originatingTouchPointDocument = &doc; @@ -3671,8 +3998,8 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) } if (!doc.hasTouchEventHandlers()) continue; - m_originatingTouchPointTargets.set(touchPointTargetKey, node); - touchTarget = node; + m_originatingTouchPointTargets.set(touchPointTargetKey, element); + touchTarget = element; } else if (pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled) { // No need to perform a hit-test since we only need to unset :hover and :active states. if (!shouldGesturesTriggerActive() && allTouchReleased) @@ -3706,10 +4033,9 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) int adjustedPageX = lroundf(pagePoint.x() / scaleFactor); int adjustedPageY = lroundf(pagePoint.y() / scaleFactor); - RefPtr<Touch> touch = Touch::create(targetFrame, touchTarget.get(), point.id(), - point.screenPos().x(), point.screenPos().y(), - adjustedPageX, adjustedPageY, - point.radiusX(), point.radiusY(), point.rotationAngle(), point.force()); + auto touch = Touch::create(targetFrame, touchTarget.get(), point.id(), + point.screenPos().x(), point.screenPos().y(), adjustedPageX, adjustedPageY, + point.radiusX(), point.radiusY(), point.rotationAngle(), point.force()); // Ensure this target's touch list exists, even if it ends up empty, so it can always be passed to TouchEvent::Create below. TargetTouchesMap::iterator targetTouchesIterator = touchesByTarget.find(touchTarget.get()); @@ -3719,8 +4045,8 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) // touches and targetTouches should only contain information about touches still on the screen, so if this point is // released or cancelled it will only appear in the changedTouches list. if (pointState != PlatformTouchPoint::TouchReleased && pointState != PlatformTouchPoint::TouchCancelled) { - touches->append(touch); - targetTouchesIterator->value->append(touch); + touches->append(touch.copyRef()); + targetTouchesIterator->value->append(touch.copyRef()); } // Now build up the correct list for changedTouches. @@ -3733,13 +4059,13 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) ASSERT(pointState < PlatformTouchPoint::TouchStateEnd); if (!changedTouches[pointState].m_touches) changedTouches[pointState].m_touches = TouchList::create(); - changedTouches[pointState].m_touches->append(touch); + changedTouches[pointState].m_touches->append(WTFMove(touch)); changedTouches[pointState].m_targets.add(touchTarget); } } m_touchPressed = touches->length() > 0; if (allTouchReleased) - m_originatingTouchPointDocument.clear(); + m_originatingTouchPointDocument = nullptr; // Now iterate the changedTouches list and m_targets within it, sending events to the targets as required. bool swallowedEvent = false; @@ -3752,18 +4078,17 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) bool isTouchCancelEvent = (state == PlatformTouchPoint::TouchCancelled); RefPtr<TouchList>& effectiveTouches(isTouchCancelEvent ? emptyList : touches); const AtomicString& stateName(eventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state))); - const EventTargetSet& targetsForState = changedTouches[state].m_targets; - for (EventTargetSet::const_iterator it = targetsForState.begin(); it != targetsForState.end(); ++it) { - EventTarget* touchEventTarget = it->get(); + for (auto& taget : changedTouches[state].m_targets) { + EventTarget* touchEventTarget = taget.get(); RefPtr<TouchList> targetTouches(isTouchCancelEvent ? emptyList : touchesByTarget.get(touchEventTarget)); ASSERT(targetTouches); - RefPtr<TouchEvent> touchEvent = + Ref<TouchEvent> touchEvent = TouchEvent::create(effectiveTouches.get(), targetTouches.get(), changedTouches[state].m_touches.get(), stateName, touchEventTarget->toNode()->document().defaultView(), 0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey()); - touchEventTarget->toNode()->dispatchTouchEvent(touchEvent.get()); + touchEventTarget->toNode()->dispatchTouchEvent(touchEvent); swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled(); } } @@ -3773,29 +4098,29 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) #endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) #if ENABLE(TOUCH_EVENTS) -bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& event) +bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& platformMouseEvent) { #if ENABLE(IOS_TOUCH_EVENTS) - UNUSED_PARAM(event); + UNUSED_PARAM(platformMouseEvent); return false; #else if (!m_frame.settings().isTouchEventEmulationEnabled()) return false; - PlatformEvent::Type eventType = event.type(); + PlatformEvent::Type eventType = platformMouseEvent.type(); if (eventType != PlatformEvent::MouseMoved && eventType != PlatformEvent::MousePressed && eventType != PlatformEvent::MouseReleased) return false; - HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, event); - if (mev.scrollbar() || subframeForHitTestResult(mev)) + HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); + if (mouseEvent.scrollbar() || subframeForHitTestResult(mouseEvent)) return false; // The order is important. This check should follow the subframe test: http://webkit.org/b/111292. if (eventType == PlatformEvent::MouseMoved && !m_touchPressed) return true; - SyntheticSingleTouchEvent touchEvent(event); + SyntheticSingleTouchEvent touchEvent(platformMouseEvent); return handleTouchEvent(touchEvent); #endif } @@ -3808,4 +4133,9 @@ void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event) m_lastKnownMouseGlobalPosition = event.globalPosition(); } +void EventHandler::setImmediateActionStage(ImmediateActionStage stage) +{ + m_immediateActionStage = stage; +} + } // namespace WebCore |