diff options
author | Konstantin Tokarev <annulen@yandex.ru> | 2016-08-25 19:20:41 +0300 |
---|---|---|
committer | Konstantin Tokarev <annulen@yandex.ru> | 2017-02-02 12:30:55 +0000 |
commit | 6882a04fb36642862b11efe514251d32070c3d65 (patch) | |
tree | b7959826000b061fd5ccc7512035c7478742f7b0 /Source/WebCore/page/EventHandler.cpp | |
parent | ab6df191029eeeb0b0f16f127d553265659f739e (diff) | |
download | qtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz |
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f
Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/WebCore/page/EventHandler.cpp')
-rw-r--r-- | Source/WebCore/page/EventHandler.cpp | 2678 |
1 files changed, 1271 insertions, 1407 deletions
diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp index fd9bb59aa..f13749ba0 100644 --- a/Source/WebCore/page/EventHandler.cpp +++ b/Source/WebCore/page/EventHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) * Copyright (C) 2015 The Qt Company Ltd * @@ -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 @@ -30,6 +30,7 @@ #include "AXObjectCache.h" #include "AutoscrollController.h" +#include "BackForwardController.h" #include "CachedImage.h" #include "Chrome.h" #include "ChromeClient.h" @@ -42,12 +43,11 @@ #include "Editor.h" #include "EditorClient.h" #include "EventNames.h" -#include "EventPathWalker.h" #include "ExceptionCodePlaceholder.h" +#include "FileList.h" #include "FloatPoint.h" #include "FloatRect.h" #include "FocusController.h" -#include "Frame.h" #include "FrameLoader.h" #include "FrameSelection.h" #include "FrameTree.h" @@ -62,19 +62,26 @@ #include "Image.h" #include "InspectorInstrumentation.h" #include "KeyboardEvent.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 "ScrollAnimator.h" +#include "RuntimeApplicationChecks.h" +#include "SVGDocument.h" +#include "SVGNames.h" +#include "ScrollLatchingState.h" #include "Scrollbar.h" #include "Settings.h" #include "ShadowRoot.h" @@ -84,36 +91,35 @@ #include "TextIterator.h" #include "UserGestureIndicator.h" #include "UserTypingGestureIndicator.h" +#include "VisibleUnits.h" #include "WheelEvent.h" #include "WindowsKeyboardCodes.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(GESTURE_EVENTS) -#include "PlatformGestureEvent.h" -#endif - -#if ENABLE(TOUCH_ADJUSTMENT) -#include "TouchAdjustment.h" +#if ENABLE(CSS_IMAGE_SET) +#include "StyleCachedImageSet.h" #endif -#if ENABLE(SVG) -#include "SVGDocument.h" -#include "SVGElementInstance.h" -#include "SVGNames.h" -#include "SVGUseElement.h" +#if ENABLE(IOS_TOUCH_EVENTS) +#include "PlatformTouchEventIOS.h" #endif #if ENABLE(TOUCH_EVENTS) -#include "PlatformTouchEvent.h" #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 namespace WebCore { @@ -130,25 +136,38 @@ const int TextDragHysteresis = 3; const int GeneralDragHysteresis = 3; #endif // ENABLE(DRAG_SUPPORT) +#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) +const float GestureUnknown = 0; +#endif + +#if ENABLE(IOS_TOUCH_EVENTS) +// FIXME: Share this constant with EventHandler and SliderThumbElement. +const unsigned InvalidTouchIdentifier = 0; +#endif + // Match key code of composition keydown event on windows. // 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 quickly enough, -// otherwise the long interval is used. +// 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.250; +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 // Set to 50Hz, no need to be faster than common screen refresh rate const double cursorUpdateInterval = 0.02; const int maximumCursorSize = 128; +#endif + #if ENABLE(MOUSE_CURSOR_SCALE) // It's pretty unlikely that a scale of less than one would ever be used. But all we really // need to ensure here is that the scale isn't so small that integer overflow can occur when @@ -181,7 +200,7 @@ public: ~MaximumDurationTracker() { - *m_maxDuration = max(*m_maxDuration, monotonicallyIncreasingTime() - m_start); + *m_maxDuration = std::max(*m_maxDuration, monotonicallyIncreasingTime() - m_start); } private: @@ -189,7 +208,7 @@ private: double m_start; }; -#if ENABLE(TOUCH_EVENTS) +#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) class SyntheticTouchPoint : public PlatformTouchPoint { public: @@ -254,7 +273,7 @@ public: m_touchPoints.append(SyntheticTouchPoint(event)); } }; -#endif +#endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned deltaMode) { @@ -270,17 +289,65 @@ static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned del } } -static inline bool scrollNode(float delta, ScrollGranularity granularity, ScrollDirection positiveDirection, ScrollDirection negativeDirection, Node* node, Node** stopNode) +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) +{ + 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, stopNode); + + 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) + scrollingWasHandled = boxLayer->handleWheelEvent(platformEvent->copyWithDeltas(filteredPlatformDelta.width(), filteredPlatformDelta.height())); + 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)) static inline bool shouldGesturesTriggerActive() { // If the platform we're on supports GestureTapDown and GestureTapCancel then we'll @@ -288,8 +355,9 @@ static inline bool shouldGesturesTriggerActive() // know in advance what event types are supported. return false; } +#endif -#if !PLATFORM(MAC) +#if !PLATFORM(COCOA) inline bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&) { @@ -305,59 +373,27 @@ inline bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTes #endif -EventHandler::EventHandler(Frame* frame) +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_cursorUpdateTimer(this, &EventHandler::cursorUpdateTimerFired) - , 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_eventHandlerWillResetCapturingMouseEventsNode(0) - , m_clickCount(0) - , m_mousePositionIsUnknown(true) - , m_mouseDownTimestamp(0) - , m_widgetIsLatched(false) -#if PLATFORM(MAC) - , m_mouseDownView(nil) - , m_sendingEventToSubview(false) - , m_activationEventNumber(-1) + , m_hoverTimer(*this, &EventHandler::hoverTimerFired) +#if ENABLE(CURSOR_SUPPORT) + , m_cursorUpdateTimer(*this, &EventHandler::cursorUpdateTimerFired) +#endif + , m_autoscrollController(std::make_unique<AutoscrollController>()) +#if !ENABLE(IOS_TOUCH_EVENTS) + , m_fakeMouseMoveEventTimer(*this, &EventHandler::fakeMouseMoveEventTimerFired) #endif -#if ENABLE(TOUCH_EVENTS) - , m_originatingTouchPointTargetKey(0) - , m_touchPressed(false) -#endif -#if ENABLE(GESTURE_EVENTS) - , m_scrollGestureHandlingNode(0) - , m_lastHitTestResultOverWidget(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 @@ -366,7 +402,7 @@ EventHandler::~EventHandler() #if ENABLE(DRAG_SUPPORT) DragState& EventHandler::dragState() { - DEFINE_STATIC_LOCAL(DragState, state, ()); + static NeverDestroyed<DragState> state; return state; } #endif // ENABLE(DRAG_SUPPORT) @@ -374,64 +410,69 @@ DragState& EventHandler::dragState() void EventHandler::clear() { m_hoverTimer.stop(); +#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_nodeUnderMouse = 0; - m_lastNodeUnderMouse = 0; -#if ENABLE(SVG) - m_instanceUnderMouse = 0; - m_lastInstanceUnderMouse = 0; -#endif - m_lastMouseMoveEventSubframe = 0; - m_lastScrollbarUnderMouse = 0; + m_resizeLayer = nullptr; + m_elementUnderMouse = nullptr; + m_lastElementUnderMouse = nullptr; + m_lastMouseMoveEventSubframe = nullptr; + m_lastScrollbarUnderMouse = nullptr; m_clickCount = 0; - m_clickNode = 0; - m_frameSetBeingResized = 0; + m_clickNode = nullptr; +#if ENABLE(IOS_GESTURE_EVENTS) + m_gestureInitialRotation = GestureUnknown; +#endif +#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) + m_gestureInitialDiameter = GestureUnknown; + m_gestureLastDiameter = GestureUnknown; + m_gestureLastRotation = GestureUnknown; + m_gestureTargets.clear(); +#endif +#if ENABLE(IOS_TOUCH_EVENTS) + m_touches.clear(); + m_firstTouchID = InvalidTouchIdentifier; + m_touchEventTargetSubframe = nullptr; +#endif + 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_capturingMouseEventsNode = 0; - m_latchedWheelEventNode = 0; - m_previousWheelScrolledNode = 0; -#if ENABLE(TOUCH_EVENTS) + m_capturingMouseEventsElement = nullptr; + clearLatchedState(); +#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) m_originatingTouchPointTargets.clear(); - m_originatingTouchPointDocument.clear(); + m_originatingTouchPointDocument = nullptr; m_originatingTouchPointTargetKey = 0; #endif -#if ENABLE(GESTURE_EVENTS) - m_scrollGestureHandlingNode = 0; - m_lastHitTestResultOverWidget = false; - m_previousGestureScrolledNode = 0; - m_scrollbarHandlingScrollGesture = 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) +static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection) { - ASSERT(selection); - if (selection->selection() != newSelection && selection->shouldChangeSelection(newSelection)) - selection->setSelection(newSelection); + if (selection.selection() != newSelection && selection.shouldChangeSelection(newSelection)) + selection.setSelection(newSelection); } static inline bool dispatchSelectStart(Node* node) @@ -442,22 +483,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) @@ -475,18 +524,18 @@ bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targe m_selectionInitiationState = PlacedCaret; } - m_frame->selection()->setNonDirectionalSelectionIfNeeded(selection, granularity); + m_frame.selection().setSelectionByMouseIfDifferent(selection, granularity); return true; } void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace) { - Node* innerNode = result.targetNode(); + Node* targetNode = result.targetNode(); VisibleSelection newSelection; - if (innerNode && innerNode->renderer()) { - VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint())); + if (targetNode && targetNode->renderer()) { + VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); if (pos.isNotNull()) { newSelection = VisibleSelection(pos); newSelection.expandUsingGranularity(WordGranularity); @@ -495,33 +544,73 @@ void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& resul if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange()) newSelection.appendTrailingWhitespace(); - updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, 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) { - if (!result.hitTestResult().isLiveLink()) - return selectClosestWordFromMouseEvent(result); + 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) +{ + Element* urlElement = result.hitTestResult().URLElement(); + if (!urlElement || !isDraggableLink(*urlElement)) { + if (Node* targetNode = result.targetNode()) { + if (isEditableNode(*targetNode)) + return selectClosestWordFromMouseEvent(result); + } + + return selectClosestContextualWordFromMouseEvent(result); + } - Node* innerNode = result.targetNode(); + Node* targetNode = result.targetNode(); - if (innerNode && innerNode->renderer() && m_mouseDownMayStartSelect) { + if (targetNode && targetNode->renderer() && m_mouseDownMayStartSelect) { VisibleSelection newSelection; - Element* URLElement = result.hitTestResult().URLElement(); - VisiblePosition pos(innerNode->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(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), WordGranularity); + updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); } } @@ -530,7 +619,7 @@ bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestR if (event.event().button() != LeftButton) return false; - if (m_frame->selection()->isRange()) + if (m_frame.selection().isRange()) // A double-click when range is already selected // should not change the selection. So, do not call // selectClosestWordFromMouseEvent, but do set @@ -548,31 +637,31 @@ bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestR if (event.event().button() != LeftButton) return false; - Node* innerNode = event.targetNode(); - if (!(innerNode && innerNode->renderer() && m_mouseDownMayStartSelect)) + Node* targetNode = event.targetNode(); + if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect)) return false; VisibleSelection newSelection; - VisiblePosition pos(innerNode->renderer()->positionForPoint(event.localPoint())); + VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr)); if (pos.isNotNull()) { newSelection = VisibleSelection(pos); newSelection.expandUsingGranularity(ParagraphGranularity); } - return updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), ParagraphGranularity); + return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), ParagraphGranularity); } static int textDistance(const Position& start, const Position& end) { - RefPtr<Range> range = Range::create(start.anchorNode()->document(), start, end); - return TextIterator::rangeLength(range.get(), true); + RefPtr<Range> range = Range::create(start.anchorNode()->document(), start, end); + return TextIterator::rangeLength(range.get(), true); } bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event) { - m_frame->document()->updateLayoutIgnorePendingStylesheets(); - Node* innerNode = event.targetNode(); - if (!(innerNode && innerNode->renderer() && m_mouseDownMayStartSelect)) + m_frame.document()->updateLayoutIgnorePendingStylesheets(); + Node* targetNode = event.targetNode(); + if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect)) return false; // Extend the selection if the Shift key is down, unless the click is in a link. @@ -580,24 +669,24 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR // Don't restart the selection when the mouse is pressed on an // existing selection so we can allow for text dragging. - if (FrameView* view = m_frame->view()) { + if (FrameView* view = m_frame.view()) { LayoutPoint vPoint = view->windowToContents(event.event().position()); - if (!extendSelection && m_frame->selection()->contains(vPoint)) { + if (!extendSelection && m_frame.selection().contains(vPoint)) { m_mouseDownWasSingleClickInSelection = true; return false; } } - VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(event.localPoint())); + VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr)); if (visiblePos.isNull()) - visiblePos = VisiblePosition(firstPositionInOrBeforeNode(innerNode), DOWNSTREAM); + visiblePos = VisiblePosition(firstPositionInOrBeforeNode(targetNode), DOWNSTREAM); Position pos = visiblePos.deepEquivalent(); - VisibleSelection newSelection = m_frame->selection()->selection(); + VisibleSelection newSelection = m_frame.selection().selection(); TextGranularity granularity = CharacterGranularity; if (extendSelection && newSelection.isCaretOrRange()) { - VisibleSelection selectionInUserSelectAll = expandSelectionToRespectUserSelectAll(innerNode, VisibleSelection(pos)); + VisibleSelection selectionInUserSelectAll = expandSelectionToRespectSelectOnMouseDown(*targetNode, VisibleSelection(pos)); if (selectionInUserSelectAll.isRange()) { if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0) pos = selectionInUserSelectAll.start(); @@ -605,7 +694,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR pos = selectionInUserSelectAll.end(); } - if (!m_frame->editor().behavior().shouldConsiderSelectionAsDirectional() && pos.isNotNull()) { + if (!m_frame.editor().behavior().shouldConsiderSelectionAsDirectional() && pos.isNotNull()) { // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection // was created right-to-left Position start = newSelection.start(); @@ -619,14 +708,14 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR } else newSelection.setExtent(pos); - if (m_frame->selection()->granularity() != CharacterGranularity) { - granularity = m_frame->selection()->granularity(); - newSelection.expandUsingGranularity(m_frame->selection()->granularity()); + if (m_frame.selection().granularity() != CharacterGranularity) { + granularity = m_frame.selection().granularity(); + newSelection.expandUsingGranularity(m_frame.selection().granularity()); } } else - newSelection = expandSelectionToRespectUserSelectAll(innerNode, visiblePos); + newSelection = expandSelectionToRespectSelectOnMouseDown(*targetNode, visiblePos); - bool handled = updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, granularity); + bool handled = updateSelectionForMouseDownDispatchingSelectStart(targetNode, newSelection, granularity); if (event.event().button() == MiddleButton) { // Ignore handled, since we want to paste to where the caret was placed anyway. @@ -647,14 +736,16 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve { #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(); + m_frame.document()->updateLayoutIgnorePendingStylesheets(); - if (ScrollView* scrollView = m_frame->view()) { + if (ScrollView* scrollView = m_frame.view()) { if (scrollView->isPointInScrollbarCorner(event.event().position())) return false; } @@ -667,35 +758,38 @@ 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. if (singleClick) focusDocumentView(); - Node* innerNode = event.targetNode(); - - m_mousePressNode = innerNode; + m_mousePressNode = event.targetNode(); #if ENABLE(DRAG_SUPPORT) m_dragStartPos = event.event().position(); #endif @@ -732,7 +826,7 @@ bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& e RenderObject* renderer = targetNode->renderer(); if (!renderer) { - Node* parent = EventPathWalker::parent(targetNode); + Element* parent = targetNode->parentOrShadowHostElement(); if (!parent) return false; @@ -741,7 +835,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 @@ -753,9 +847,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); } @@ -768,47 +861,47 @@ 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(); + + FrameView* view = m_frame.view(); if (!view) return false; - Page* page = m_frame->page(); + Page* page = m_frame.page(); if (!page) return false; updateDragSourceActionsAllowed(); HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); 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); + return result.innerElement() && page->dragController().draggableElement(&m_frame, result.innerElement(), result.roundedPointInInnerNodeFrame(), state); } void EventHandler::updateSelectionForMouseDrag() { - FrameView* view = m_frame->view(); + 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); 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()) @@ -819,11 +912,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) @@ -835,7 +928,7 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul if (!target) return; - VisiblePosition targetPosition = selectionExtentRespectingEditingBoundary(m_frame->selection()->selection(), hitTestResult.localPoint(), target); + VisiblePosition targetPosition = selectionExtentRespectingEditingBoundary(m_frame.selection().selection(), hitTestResult.localPoint(), target); // Don't modify the selection if we're not on a node. if (targetPosition.isNull()) @@ -843,9 +936,8 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul // Restart the selection if this is the first mouse move. This work is usually // done in handleMousePressEvent, but not if the mouse press was on an existing selection. - VisibleSelection newSelection = m_frame->selection()->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()) @@ -853,7 +945,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; @@ -871,11 +962,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_dragStartPos, 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_dragStartPos, nullptr)) < 0) newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTarget).upstream(CanCrossEditingBoundary)); else if (rootUserSelectAllForTarget && m_mousePressNode->renderer()) newSelection.setExtent(positionAfterNode(rootUserSelectAllForTarget).downstream(CanCrossEditingBoundary)); @@ -886,17 +977,17 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul newSelection.setExtent(targetPosition); #endif - if (m_frame->selection()->granularity() != CharacterGranularity) - newSelection.expandUsingGranularity(m_frame->selection()->granularity()); + 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) void EventHandler::lostMouseCapture() { - m_frame->selection()->setCaretBlinkingSuspended(false); + m_frame.selection().setCaretBlinkingSuspended(false); } bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event) @@ -941,25 +1032,21 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e #if ENABLE(DRAG_SUPPORT) && m_dragStartPos == event.event().position() #endif - && m_frame->selection()->isRange() + && m_frame.selection().isRange() && event.event().button() != RightButton) { VisibleSelection newSelection; Node* node = event.targetNode(); - bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled(); - if (node && (caretBrowsing || node->rendererIsEditable()) && node->renderer()) { - VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint()); + bool caretBrowsing = m_frame.settings().caretBrowsingEnabled(); + if (node && node->renderer() && (caretBrowsing || node->hasEditableStyle())) { + VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint(), nullptr); newSelection = VisibleSelection(pos); } - setSelectionIfNeeded(m_frame->selection(), newSelection); + setSelectionIfNeeded(m_frame.selection(), newSelection); handled = true; } - m_frame->selection()->notifyRendererOfSelectionChange(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; @@ -980,17 +1067,19 @@ void EventHandler::didPanScrollStop() m_autoscrollController->didPanScrollStop(); } -void EventHandler::startPanScrolling(RenderObject* renderer) +void EventHandler::startPanScrolling(RenderElement* renderer) { - if (!renderer->isBox()) +#if !PLATFORM(IOS) + if (!is<RenderBox>(*renderer)) return; - m_autoscrollController->startPanScrolling(toRenderBox(renderer), lastKnownMousePosition()); + m_autoscrollController->startPanScrolling(downcast<RenderBox>(renderer), lastKnownMousePosition()); invalidateClick(); +#endif } #endif // ENABLE(PAN_SCROLLING) -RenderObject* EventHandler::autoscrollRenderer() const +RenderBox* EventHandler::autoscrollRenderer() const { return m_autoscrollController->autoscrollRenderer(); } @@ -1013,18 +1102,15 @@ bool EventHandler::panScrollInProgress() const #if ENABLE(DRAG_SUPPORT) DragSourceAction EventHandler::updateDragSourceActionsAllowed() const { - if (!m_frame) - return DragSourceActionNone; - - Page* page = m_frame->page(); + Page* page = m_frame.page(); if (!page) return DragSourceActionNone; - FrameView* view = m_frame->view(); + FrameView* view = m_frame.view(); if (!view) return DragSourceActionNone; - return page->dragController()->delegateDragSourceAction(view->contentsToRootView(m_mouseDownPos)); + return page->dragController().delegateDragSourceAction(view->contentsToRootView(m_mouseDownPos)); } #endif // ENABLE(DRAG_SUPPORT) @@ -1032,28 +1118,29 @@ HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTe { // We always send hitTestResultAtPoint to the main frame if we have one, // otherwise we might hit areas that are obscured by higher frames. - if (Page* page = m_frame->page()) { - Frame* mainFrame = page->mainFrame(); - if (m_frame != mainFrame) { - FrameView* frameView = m_frame->view(); - FrameView* mainView = mainFrame->view(); - if (frameView && mainView) { - IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point))); - return mainFrame->eventHandler()->hitTestResultAtPoint(mainFramePoint, hitType, padding); - } + if (!m_frame.isMainFrame()) { + Frame& mainFrame = m_frame.mainFrame(); + FrameView* frameView = m_frame.view(); + FrameView* mainView = mainFrame.view(); + if (frameView && mainView) { + IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point))); + return mainFrame.eventHandler().hitTestResultAtPoint(mainFramePoint, hitType, padding); } } HitTestResult result(point, padding.height(), padding.width(), padding.height(), padding.width()); - if (!m_frame->contentRenderer()) + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) return result; - + + // We should always start hittesting a clean tree. + renderView->document().updateLayoutIgnorePendingStylesheets(); // 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.innerElement()); if (request.disallowsShadowContent()) result.setToNonShadowAncestor(); @@ -1066,29 +1153,19 @@ 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; if (!node) - node = m_frame->document()->focusedElement(); + node = m_frame.document()->focusedElement(); if (!node) node = m_mousePressNode.get(); if (node) { - RenderObject* r = node->renderer(); - if (r && !r->isListBox() && r->enclosingBox()->scroll(direction, granularity)) { + auto r = node->renderer(); + if (r && !r->isListBox() && r->enclosingBox().scroll(direction, granularity)) { setFrameWasScrolledByUser(); return true; } @@ -1102,14 +1179,14 @@ bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, Scrol Node* node = startingNode; if (!node) - node = m_frame->document()->focusedElement(); + node = m_frame.document()->focusedElement(); if (!node) node = m_mousePressNode.get(); if (node) { - RenderObject* r = node->renderer(); - if (r && !r->isListBox() && r->enclosingBox()->logicalScroll(direction, granularity)) { + auto r = node->renderer(); + if (r && !r->isListBox() && r->enclosingBox().logicalScroll(direction, granularity)) { setFrameWasScrolledByUser(); return true; } @@ -1122,31 +1199,31 @@ bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularit { // 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(); + m_frame.document()->updateLayoutIgnorePendingStylesheets(); if (scrollOverflow(direction, granularity, startingNode)) return true; - Frame* frame = m_frame; + Frame* frame = &m_frame; FrameView* view = frame->view(); if (view && view->scroll(direction, granularity)) return true; - frame = frame->tree()->parent(); + frame = frame->tree().parent(); if (!frame) return false; - return frame->eventHandler()->scrollRecursively(direction, granularity, m_frame->ownerElement()); + return frame->eventHandler().scrollRecursively(direction, granularity, m_frame.ownerElement()); } bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode) { // 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(); + m_frame.document()->updateLayoutIgnorePendingStylesheets(); if (logicalScrollOverflow(direction, granularity, startingNode)) return true; - Frame* frame = m_frame; + Frame* frame = &m_frame; 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; @@ -1157,11 +1234,11 @@ bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, Sc if (scrolled) return true; - frame = frame->tree()->parent(); + frame = frame->tree().parent(); if (!frame) return false; - return frame->eventHandler()->logicalScrollRecursively(direction, granularity, m_frame->ownerElement()); + return frame->eventHandler().logicalScrollRecursively(direction, granularity, m_frame.ownerElement()); } IntPoint EventHandler::lastKnownMousePosition() const @@ -1179,28 +1256,29 @@ Frame* EventHandler::subframeForHitTestResult(const MouseEventWithHitTestResults Frame* EventHandler::subframeForTargetNode(Node* node) { if (!node) - return 0; + return nullptr; - RenderObject* renderer = node->renderer(); - if (!renderer || !renderer->isWidget()) - return 0; + auto renderer = node->renderer(); + 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(Node* node, Frame* frame) +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) @@ -1208,13 +1286,12 @@ bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey) if (!node) return false; - bool editable = node->rendererIsEditable(); + bool editable = node->hasEditableStyle(); bool editableLinkEnabled = false; // If the link is editable, then we need to check the settings to see whether or not the link should be followed if (editable) { - ASSERT(m_frame->settings()); - switch (m_frame->settings()->editableLinkBehavior()) { + switch (m_frame.settings().editableLinkBehavior()) { default: case EditableLinkDefaultBehavior: case EditableLinkAlwaysLive: @@ -1226,7 +1303,7 @@ bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey) break; case EditableLinkLiveWhenNotFocused: - editableLinkEnabled = nodeIsNotBeingEdited(node, m_frame) || shiftKey; + editableLinkEnabled = nodeIsNotBeingEdited(*node, m_frame) || shiftKey; break; case EditableLinkOnlyLiveWithShiftKey: @@ -1238,11 +1315,9 @@ 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); - ASSERT(m_frame->document()); - + ASSERT(m_frame.document()); updateCursor(); } @@ -1251,7 +1326,7 @@ void EventHandler::updateCursor() if (m_mousePositionIsUnknown) return; - FrameView* view = m_frame->view(); + FrameView* view = m_frame.view(); if (!view) return; @@ -1268,9 +1343,7 @@ 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); @@ -1286,40 +1359,38 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif if (m_resizeLayer && m_resizeLayer->inResizeMode()) return NoCursorChange; - Page* page = m_frame->page(); - if (!page) + if (!m_frame.page()) return NoCursorChange; + #if ENABLE(PAN_SCROLLING) - if (page->mainFrame()->eventHandler()->panScrollInProgress()) + if (m_frame.mainFrame().eventHandler().panScrollInProgress()) return NoCursorChange; #endif + // 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; - RenderObject* renderer = node->renderer(); - RenderStyle* style = renderer ? renderer->style() : 0; + auto renderer = node->renderer(); + RenderStyle* 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 - // During selection, use an I-beam no matter what we're over. - // 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_capturingMouseEventsNode) - return iBeam; - if (renderer) { Cursor overrideCursor; switch (renderer->getCursor(roundedIntPoint(result.localPoint()), overrideCursor)) { @@ -1344,8 +1415,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 @@ -1367,9 +1437,20 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif } } - switch (style ? style->cursor() : CURSOR_AUTO) { - case CURSOR_AUTO: { - bool editable = node->rendererIsEditable(); + // 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)) return handCursor(); @@ -1377,101 +1458,105 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif bool inResizer = false; if (renderer) { if (RenderLayer* layer = renderer->enclosingLayer()) { - if (FrameView* view = m_frame->view()) + if (FrameView* view = m_frame.view()) inResizer = layer->isPointInResizeControl(view->windowToContents(roundedIntPoint(result.localPoint()))); } } + 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(); } +#endif // ENABLE(CURSOR_SUPPORT) #if ENABLE(CURSOR_VISIBILITY) void EventHandler::startAutoHideCursorTimer() { - Page* page = m_frame->page(); + Page* page = m_frame.page(); if (!page) return; - m_autoHideCursorTimer.startOneShot(page->settings()->timeWithoutMouseMovementBeforeHidingControls()); + 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() @@ -1480,56 +1565,70 @@ 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(); + FrameView* view = m_frame.view(); if (view && view->isActive()) view->setCursor(m_currentMouseCursor); } #endif -static LayoutPoint documentPointForWindowPoint(Frame* frame, const IntPoint& windowPoint) +static LayoutPoint documentPointForWindowPoint(Frame& frame, const IntPoint& windowPoint) { - FrameView* view = frame->view(); + FrameView* view = frame.view(); // FIXME: Is it really OK to use the wrong coordinates here when view is 0? // Historically the code would just crash; this is clearly no worse than that. 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) { - RefPtr<FrameView> protector(m_frame->view()); + RefPtr<FrameView> protector(m_frame.view()); - if (InspectorInstrumentation::handleMousePress(m_frame->page())) { + if (InspectorInstrumentation::handleMousePress(m_frame)) { invalidateClick(); return true; } + 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(DefinitelyProcessingUserGesture, m_frame.document()); // FIXME (bug 68185): this call should be made at another abstraction layer - m_frame->loader()->resetMultipleFormSubmissionProtection(); - + 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()); + if (FrameView* view = m_frame.view()) + m_mouseDownPos = view->windowToContents(platformMouseEvent.position()); else { invalidateClick(); return false; @@ -1539,24 +1638,24 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); // 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(); + m_mousePressNode = mouseEvent.targetNode(); - RefPtr<Frame> subframe = subframeForHitTestResult(mev); - if (subframe && passMousePressEventToSubframe(mev, subframe.get())) { + 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(); + m_capturesDragging = subframe->eventHandler().capturesDragging(); if (m_mousePressed && m_capturesDragging) { - m_capturingMouseEventsNode = mev.targetNode(); - m_eventHandlerWillResetCapturingMouseEventsNode = true; + m_capturingMouseEventsElement = subframe->ownerElement(); + m_eventHandlerWillResetCapturingMouseEventsElement = true; } invalidateClick(); return true; @@ -1565,7 +1664,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) #if ENABLE(PAN_SCROLLING) // We store whether pan scrolling is in progress before calling stopAutoscrollTimer() // because it will set m_autoscrollType to NoAutoscroll on return. - bool isPanScrollInProgress = m_frame->page() && m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress(); + bool isPanScrollInProgress = m_frame.mainFrame().eventHandler().panScrollInProgress(); stopAutoscrollTimer(); if (isPanScrollInProgress) { // We invalidate the click when exiting pan scrolling so that we don't inadvertently navigate @@ -1575,12 +1674,17 @@ 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(); + return false; + } - if (FrameView* view = m_frame->view()) { + 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; @@ -1590,127 +1694,123 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) } } - m_frame->selection()->setCaretBlinkingSuspended(true); + 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()) - m_lastScrollbarUnderMouse = 0; + if (mouseEvent.scrollbar()) { + const bool wasLastScrollBar = mouseEvent.scrollbar() == m_lastScrollbarUnderMouse.get(); + mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent); + if (wasLastScrollBar && mouseEvent.scrollbar() != m_lastScrollbarUnderMouse.get()) + 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())->host())) { - 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(); + if (is<ShadowRoot>(*mouseEvent.targetNode()) && is<HTMLInputElement>(*downcast<ShadowRoot>(*mouseEvent.targetNode()).host())) + mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent); + } - updateLastScrollbarUnderMouse(scrollbar, true); + Scrollbar* scrollbar = scrollbarForMouseEvent(mouseEvent, m_frame.view()); + updateLastScrollbarUnderMouse(scrollbar, true); - if (scrollbar && passMousePressEventToScrollbar(mev, scrollbar)) + bool passedToScrollbar = scrollbar && passMousePressEventToScrollbar(mouseEvent, 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) { - RefPtr<FrameView> protector(m_frame->view()); + RefPtr<FrameView> protector(m_frame.view()); - m_frame->selection()->setCaretBlinkingSuspended(false); + m_frame.selection().setCaretBlinkingSuspended(false); - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture, m_frame.document()); // 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); - if (m_eventHandlerWillResetCapturingMouseEventsNode) - m_capturingMouseEventsNode = 0; - if (subframe && passMousePressEventToSubframe(mev, subframe)) + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); + Frame* subframe = subframeForHitTestResult(mouseEvent); + if (m_eventHandlerWillResetCapturingMouseEventsElement) + m_capturingMouseEventsElement = nullptr; + 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; + return nullptr; - RenderObject* renderer = node->renderer(); - if (!renderer) - return 0; + for (auto element = node; element; element = element->parentOrShadowHostNode()) { + if (is<HTMLIFrameElement>(*element) || is<HTMLHtmlElement>(*element) || is<HTMLDocument>(*element)) + return nullptr; - RenderLayer* layer = renderer->enclosingLayer(); - if (!layer) - return 0; + auto renderer = element->renderer(); + if (!renderer) + continue; + + if (is<RenderListBox>(*renderer)) + return downcast<RenderListBox>(renderer); - return layer; + return renderer->enclosingLayer(); + } + + return nullptr; } bool EventHandler::mouseMoved(const PlatformMouseEvent& event) { - RefPtr<FrameView> protector(m_frame->view()); + 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); - Page* page = m_frame->page(); + Page* page = m_frame.page(); if (!page) return result; - if (RenderLayer* layer = layerForNode(hoveredNode.innerNode())) { - if (FrameView* frameView = m_frame->view()) { - if (frameView->containsScrollableArea(layer)) - layer->mouseMovedInContentArea(); + if (auto scrolledArea = enclosingScrollableArea(hoveredNode.innerNode())) { + if (FrameView* frameView = m_frame.view()) { + if (frameView->containsScrollableArea(scrolledArea)) + scrolledArea->mouseMovedInContentArea(); } } - if (FrameView* frameView = m_frame->view()) + if (FrameView* frameView = m_frame.view()) frameView->mouseMovedInContentArea(); hoveredNode.setToNonShadowAncestor(); @@ -1725,46 +1825,43 @@ 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) { - // in Radar 3703768 we saw frequent crashes apparently due to the - // part being null here, which seems impossible, so check for nil - // but also assert so that we can try to figure this out in debug - // builds, if it happens. - ASSERT(m_frame); - if (!m_frame) - return false; - #if ENABLE(TOUCH_EVENTS) - bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent); + bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); if (defaultPrevented) return true; #endif - RefPtr<FrameView> protector(m_frame->view()); + RefPtr<FrameView> protector(m_frame.view()); - setLastKnownMousePosition(mouseEvent); + setLastKnownMousePosition(platformMouseEvent); if (m_hoverTimer.isActive()) m_hoverTimer.stop(); +#if ENABLE(CURSOR_SUPPORT) 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; if (m_mousePressed) @@ -1777,50 +1874,58 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi hitType |= HitTestRequest::ReadOnly; } -#if ENABLE(TOUCH_EVENTS) +#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) // Treat any mouse move events as readonly if the user is currently touching the screen. if (m_touchPressed) 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(); + Scrollbar* scrollbar = mouseEvent.scrollbar(); updateLastScrollbarUnderMouse(scrollbar, !m_mousePressed); + + // 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. - if (onlyUpdateScrollbars) + scrollbar->mouseMoved(platformMouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. +#endif + if (onlyUpdateScrollbars) { + updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true); return true; + } } bool swallowEvent = false; - RefPtr<Frame> newSubframe = m_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.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()); + if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(&m_frame) && m_lastMouseMoveEventSubframe != newSubframe) + 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 ENABLE(CURSOR_SUPPORT) } else { - if (FrameView* view = m_frame->view()) { - OptionalCursor optionalCursor = selectCursor(mev.hitTestResult(), mouseEvent.shiftKey()); + if (FrameView* view = m_frame.view()) { + OptionalCursor optionalCursor = selectCursor(mouseEvent.hitTestResult(), platformMouseEvent.shiftKey()); if (optionalCursor.isCursorChange()) { m_currentMouseCursor = optionalCursor.cursor(); view->setCursor(m_currentMouseCursor); } } +#endif } m_lastMouseMoveEventSubframe = newSubframe; @@ -1828,10 +1933,10 @@ 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); + swallowEvent = handleMouseDraggedEvent(mouseEvent); #endif // ENABLE(DRAG_SUPPORT) return swallowEvent; @@ -1840,102 +1945,116 @@ 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) +bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& platformMouseEvent) { - if (targetNode == clickNode) - return true; - - if (!targetNode) - return false; - - 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; + RefPtr<FrameView> protector(m_frame.view()); - Node* adjustedTargetNode = targetNode->shadowHost(); - Node* adjustedClickNode = clickNode ? clickNode->shadowHost() : 0; - return adjustedTargetNode == adjustedClickNode; -} + m_frame.selection().setCaretBlinkingSuspended(false); -bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) -{ - RefPtr<FrameView> protector(m_frame->view()); - - m_frame->selection()->setCaretBlinkingSuspended(false); + 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(DefinitelyProcessingUserGesture, 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_lastNodeUnderMouse.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_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev); - if (m_eventHandlerWillResetCapturingMouseEventsNode) - m_capturingMouseEventsNode = 0; - if (subframe && passMouseReleaseEventToSubframe(mev, subframe)) + 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(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* targetNode = mouseEvent.targetNode(); + Node* nodeToClick = (m_clickNode && targetNode) ? commonAncestorCrossingShadowBoundary(*m_clickNode, *targetNode) : nullptr; + 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) +{ + RefPtr<FrameView> protector(m_frame.view()); + + setLastKnownMousePosition(event); + + HitTestRequest::HitTestRequestType hitType = HitTestRequest::DisallowShadowContent | 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. @@ -1954,110 +2073,136 @@ 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 - if (!m_frame->page()) + if (!m_frame.page()) return false; - Frame* focusFrame = m_frame->page()->focusController()->focusedOrMainFrame(); + Frame& focusFrame = m_frame.page()->focusController().focusedOrMainFrame(); // Do not paste here if the focus was moved somewhere else. - if (m_frame == focusFrame && m_frame->editor().client()->supportsGlobalSelection()) - return m_frame->editor().command(ASCIILiteral("PasteGlobalSelection")).execute(); + if (&m_frame == &focusFrame && m_frame.editor().client()->supportsGlobalSelection()) + return m_frame.editor().command(ASCIILiteral("PasteGlobalSelection")).execute(); return false; } - #if ENABLE(DRAG_SUPPORT) -bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTarget, const PlatformMouseEvent& event, Clipboard* clipboard) + +bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer* dataTransfer) { - FrameView* view = m_frame->view(); + FrameView* view = m_frame.view(); // FIXME: We might want to dispatch a dragleave even if the view is gone. if (!view) return false; - view->resetDeferredRepaintDelay(); - RefPtr<MouseEvent> me = MouseEvent::create(eventType, - true, true, event.timestamp(), m_frame->document()->defaultView(), + view->disableLayerFlushThrottlingTemporarilyForInteraction(); + 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(), 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 = downcast<HTMLFrameElementBase>(*target).contentFrame(); + return true; +} - frame = toHTMLFrameElementBase(target)->contentFrame(); +static DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation) +{ + if (dragOperation == "copy") + return DragOperationCopy; + if (dragOperation == "move") + return DragOperationMove; + if (dragOperation == "link") + return DragOperationLink; + return DragOperationNone; +} - return true; +static String convertDragOperationToDropZoneOperation(DragOperation operation) +{ + switch (operation) { + case DragOperationCopy: + return ASCIILiteral("copy"); + case DragOperationMove: + return ASCIILiteral("move"); + case DragOperationLink: + return ASCIILiteral("link"); + default: + return ASCIILiteral("copy"); + } } -static bool findDropZone(Node* target, Clipboard* clipboard) +static bool hasDropZoneType(DataTransfer& dataTransfer, const String& keyword) { - Element* element = target->isElementNode() ? toElement(target) : target->parentElement(); + if (keyword.startsWith("file:")) + return dataTransfer.hasFileOfType(keyword.substring(5)); + + if (keyword.startsWith("string:")) + return dataTransfer.hasStringOfType(keyword.substring(7)); + + return false; +} + +static bool findDropZone(Node* target, DataTransfer* dataTransfer) +{ + ASSERT(target); + Element* element = is<Element>(*target) ? downcast<Element>(target) : target->parentElement(); for (; element; element = element->parentElement()) { + SpaceSplitString keywords(element->fastGetAttribute(webkitdropzoneAttr), true); bool matched = false; - String dropZoneStr = element->fastGetAttribute(webkitdropzoneAttr); - - if (dropZoneStr.isEmpty()) - continue; - - dropZoneStr.makeLower(); - - SpaceSplitString keywords(dropZoneStr, false); - if (keywords.isNull()) - 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 || clipboard->hasDropZoneType(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) { bool accept = false; - if (!m_frame->view()) + if (!m_frame.view()) return false; HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, event); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event); - // Drag events should never go to text nodes (following IE, and proper mouseover/out dispatch) - RefPtr<Node> newTarget = mev.targetNode(); - if (newTarget && newTarget->isTextNode()) - newTarget = EventPathWalker::parent(newTarget.get()); + RefPtr<Element> newTarget; + if (Node* targetNode = mouseEvent.targetNode()) { + // Drag events should never go to non-element nodes (following IE, and proper mouseover/out dispatch) + if (!is<Element>(*targetNode)) + newTarget = targetNode->parentOrShadowHostElement(); + else + newTarget = downcast<Element>(targetNode); + } m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp()); @@ -2070,23 +2215,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.get(), 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.get(), 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 @@ -2097,47 +2242,46 @@ 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.get(), 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; - + m_dragTarget = newTarget.release(); return accept; } -void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard) +void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, DataTransfer* dataTransfer) { Frame* targetFrame; if (targetIsFrame(m_dragTarget.get(), targetFrame)) { if (targetFrame) - targetFrame->eventHandler()->cancelDragAndDrop(event, clipboard); - } else if (m_dragTarget.get()) { + targetFrame->eventHandler().cancelDragAndDrop(event, dataTransfer); + } else if (m_dragTarget) { if (dragState().source && dragState().shouldDispatchEvents) dispatchDragSrcEvent(eventNames().dragEvent, event); - dispatchDragEvent(eventNames().dragleaveEvent, m_dragTarget.get(), 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) { Frame* targetFrame; bool preventedDefault = false; if (targetIsFrame(m_dragTarget.get(), targetFrame)) { if (targetFrame) - preventedDefault = targetFrame->eventHandler()->performDragAndDrop(event, clipboard); - } else if (m_dragTarget.get()) - preventedDefault = dispatchDragEvent(eventNames().dropEvent, m_dragTarget.get(), event, clipboard); + preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, dataTransfer); + } else if (m_dragTarget) + preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, dataTransfer); clearDragState(); return preventedDefault; } @@ -2145,188 +2289,197 @@ bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, Clipboard void EventHandler::clearDragState() { stopAutoscrollTimer(); - m_dragTarget = 0; - m_capturingMouseEventsNode = 0; + 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::setCapturingMouseEventsNode(PassRefPtr<Node> n) +void EventHandler::setCapturingMouseEventsElement(PassRefPtr<Element> element) { - m_capturingMouseEventsNode = n; - m_eventHandlerWillResetCapturingMouseEventsNode = false; + m_capturingMouseEventsElement = element; + m_eventHandlerWillResetCapturingMouseEventsElement = false; } -MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mev) +MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mouseEvent) { - ASSERT(m_frame); - ASSERT(m_frame->document()); - - return m_frame->document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mev.position()), mev); + ASSERT(m_frame.document()); + 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->host(); - 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) { - Node* result = targetNode; + Element* targetElement = nullptr; - // If we're capturing, we always go right to that node. - if (m_capturingMouseEventsNode) - result = m_capturingMouseEventsNode.get(); - else { - // If the target node is a text node, dispatch on the parent node - rdar://4196646 - if (result && result->isTextNode()) - result = EventPathWalker::parent(result); + // If we're capturing, we always go right to that element. + if (m_capturingMouseEventsElement) + targetElement = m_capturingMouseEventsElement.get(); + else if (targetNode) { + // If the target node is a non-element, dispatch on the parent. <rdar://problem/4196646> + if (!is<Element>(*targetNode)) + targetElement = targetNode->parentOrShadowHostElement(); + else + targetElement = downcast<Element>(targetNode); } - m_nodeUnderMouse = result; -#if ENABLE(SVG) - m_instanceUnderMouse = instanceAssociatedWithShadowTreeElement(result); - - // <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_lastNodeUnderMouse == shadowTreeElement) - continue; - - m_lastNodeUnderMouse = shadowTreeElement; - m_lastInstanceUnderMouse = instance; - break; - } - } - } -#endif + m_elementUnderMouse = targetElement; // Fire mouseout/mouseover if the mouse has shifted to a different node. if (fireMouseOverOut) { - RenderLayer* layerForLastNode = layerForNode(m_lastNodeUnderMouse.get()); - RenderLayer* layerForNodeUnderMouse = layerForNode(m_nodeUnderMouse.get()); - Page* page = m_frame->page(); + auto scrollableAreaForLastNode = enclosingScrollableArea(m_lastElementUnderMouse.get()); + auto scrollableAreaForNodeUnderMouse = enclosingScrollableArea(m_elementUnderMouse.get()); + Page* page = m_frame.page(); - if (m_lastNodeUnderMouse && (!m_nodeUnderMouse || m_nodeUnderMouse->document() != m_frame->document())) { + if (m_lastElementUnderMouse && (!m_elementUnderMouse || &m_elementUnderMouse->document() != m_frame.document())) { // The mouse has moved between frames. - if (Frame* frame = m_lastNodeUnderMouse->document()->frame()) { + if (Frame* frame = m_lastElementUnderMouse->document().frame()) { 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_lastNodeUnderMouse->document()->frame()) { + if (Frame* frame = m_lastElementUnderMouse->document().frame()) { if (FrameView* frameView = frame->view()) { - if (frameView->containsScrollableArea(layerForLastNode)) - layerForLastNode->mouseExitedContentArea(); + if (frameView->containsScrollableArea(scrollableAreaForLastNode)) + scrollableAreaForLastNode->mouseExitedContentArea(); } } } - if (m_nodeUnderMouse && (!m_lastNodeUnderMouse || m_lastNodeUnderMouse->document() != m_frame->document())) { + if (m_elementUnderMouse && (!m_lastElementUnderMouse || &m_lastElementUnderMouse->document() != m_frame.document())) { // The mouse has moved between frames. - if (Frame* frame = m_nodeUnderMouse->document()->frame()) { + if (Frame* frame = m_elementUnderMouse->document().frame()) { 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_nodeUnderMouse->document()->frame()) { + if (Frame* frame = m_elementUnderMouse->document().frame()) { if (FrameView* frameView = frame->view()) { - if (frameView->containsScrollableArea(layerForNodeUnderMouse)) - layerForNodeUnderMouse->mouseEnteredContentArea(); + if (frameView->containsScrollableArea(scrollableAreaForNodeUnderMouse)) + scrollableAreaForNodeUnderMouse->mouseEnteredContentArea(); } } } - if (m_lastNodeUnderMouse && m_lastNodeUnderMouse->document() != m_frame->document()) { - m_lastNodeUnderMouse = 0; - m_lastScrollbarUnderMouse = 0; -#if ENABLE(SVG) - m_lastInstanceUnderMouse = 0; -#endif + if (m_lastElementUnderMouse && &m_lastElementUnderMouse->document() != m_frame.document()) { + m_lastElementUnderMouse = nullptr; + m_lastScrollbarUnderMouse = nullptr; } - if (m_lastNodeUnderMouse != m_nodeUnderMouse) { - // send mouseout event to the old node - if (m_lastNodeUnderMouse) - m_lastNodeUnderMouse->dispatchMouseEvent(mouseEvent, eventNames().mouseoutEvent, 0, m_nodeUnderMouse.get()); - // send mouseover event to the new node - if (m_nodeUnderMouse) - m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, eventNames().mouseoverEvent, 0, m_lastNodeUnderMouse.get()); + if (m_lastElementUnderMouse != m_elementUnderMouse) { + // 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(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(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_lastNodeUnderMouse = m_nodeUnderMouse; -#if ENABLE(SVG) - m_lastInstanceUnderMouse = instanceAssociatedWithShadowTreeElement(m_nodeUnderMouse.get()); -#endif + m_lastElementUnderMouse = m_elementUnderMouse; } } -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()) - view->resetDeferredRepaintDelay(); + if (FrameView* view = m_frame.view()) + view->disableLayerFlushThrottlingTemporarilyForInteraction(); - updateMouseEventTargetNode(targetNode, mouseEvent, setUnder); + updateMouseEventTargetNode(targetNode, platformMouseEvent, setUnder); bool swallowEvent = false; - if (m_nodeUnderMouse) - swallowEvent = !(m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, eventType, clickCount)); + if (m_elementUnderMouse) + swallowEvent = !(m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventType, clickCount)); if (!swallowEvent && eventType == eventNames().mousedownEvent) { // If clicking on a frame scrollbar, do not mess up with content focus. - if (FrameView* view = m_frame->view()) { - if (view->scrollbarAtPoint(mouseEvent.position())) + if (FrameView* view = m_frame.view()) { + if (view->scrollbarAtPoint(platformMouseEvent.position())) return true; } // The layout needs to be up to date to determine if an element is focusable. - m_frame->document()->updateLayoutIgnorePendingStylesheets(); + 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; - if (m_nodeUnderMouse) - element = m_nodeUnderMouse->isElementNode() ? toElement(m_nodeUnderMouse.get()) : m_nodeUnderMouse->parentOrShadowHostElement(); - else - element = 0; + Element* element = m_elementUnderMouse.get(); // Walk up the DOM tree to search for an element to focus. while (element) { @@ -2335,9 +2488,9 @@ bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe // 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())) + 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; @@ -2346,17 +2499,17 @@ bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe } // Only change the focus when clicking scrollbars if it can transfered to a mouse focusable node. - if ((!element || !element->isMouseFocusable()) && isInsideScrollbar(mouseEvent.position())) + if ((!element || !element->isMouseFocusable()) && isInsideScrollbar(platformMouseEvent.position())) return false; // 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 (Page* page = m_frame.page()) { if (element && element->isMouseFocusable()) { - if (!page->focusController()->setFocusedElement(element, m_frame)) + if (!page->focusController().setFocusedElement(element, &m_frame)) swallowEvent = true; } else if (!element || !element->focused()) { - if (!page->focusController()->setFocusedElement(0, m_frame)) + if (!page->focusController().setFocusedElement(0, &m_frame)) swallowEvent = true; } } @@ -2367,7 +2520,7 @@ bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const { - if (RenderView* renderView = m_frame->contentRenderer()) { + if (RenderView* renderView = m_frame.contentRenderer()) { HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); HitTestResult result(windowPoint); renderView->hitTest(request, result); @@ -2378,546 +2531,203 @@ bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const } #if !PLATFORM(GTK) + bool EventHandler::shouldTurnVerticalTicksIntoHorizontal(const HitTestResult&, const PlatformWheelEvent&) const { return false; } -#endif - -bool EventHandler::handleWheelEvent(const PlatformWheelEvent& e) -{ - Document* doc = m_frame->document(); - - RenderObject* docRenderer = doc->renderer(); - if (!docRenderer) - return false; - - RefPtr<FrameView> protector(m_frame->view()); - - FrameView* view = m_frame->view(); - if (!view) - return false; - - m_isHandlingWheelEvent = true; - setFrameWasScrolledByUser(); - LayoutPoint vPoint = view->windowToContents(e.position()); - - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); - HitTestResult result(vPoint); - doc->renderView()->hitTest(request, result); - - bool useLatchedWheelEventNode = e.useLatchedEventNode(); - - // FIXME: Is the following code different from just calling innerElement? - Node* node = result.innerNode(); - // Wheel events should not dispatch to text nodes. - if (node && node->isTextNode()) - node = EventPathWalker::parent(node); - - bool isOverWidget; - if (useLatchedWheelEventNode) { - if (!m_latchedWheelEventNode) { - m_latchedWheelEventNode = node; - m_widgetIsLatched = result.isOverWidget(); - } else - node = m_latchedWheelEventNode.get(); - - isOverWidget = m_widgetIsLatched; - } else { - if (m_latchedWheelEventNode) - m_latchedWheelEventNode = 0; - if (m_previousWheelScrolledNode) - m_previousWheelScrolledNode = 0; - - isOverWidget = result.isOverWidget(); - } - - // 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(); - - if (node) { - // Figure out which view to send the event to. - RenderObject* target = node->renderer(); - - if (isOverWidget && target && target->isWidget()) { - Widget* widget = toRenderWidget(target)->widget(); - if (widget && passWheelEventToWidget(e, widget)) { - m_isHandlingWheelEvent = false; - return true; - } - } - - if (node && !node->dispatchWheelEvent(event)) { - m_isHandlingWheelEvent = false; - return true; - } - } - - // 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; -} +#endif -void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent) -{ - if (!startNode || !wheelEvent) - return; - - Node* stopNode = m_previousWheelScrolledNode.get(); - ScrollGranularity granularity = wheelGranularityToScrollGranularity(wheelEvent->deltaMode()); - - // 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 (scrollNode(wheelEvent->rawDeltaX(), granularity, ScrollLeft, ScrollRight, startNode, &stopNode)) - wheelEvent->setDefaultHandled(); - - if (scrollNode(wheelEvent->rawDeltaY(), granularity, ScrollUp, ScrollDown, startNode, &stopNode)) - wheelEvent->setDefaultHandled(); - - if (!m_latchedWheelEventNode) - m_previousWheelScrolledNode = stopNode; -} +#if !PLATFORM(MAC) -#if ENABLE(GESTURE_EVENTS) -bool EventHandler::handleGestureTapDown() +void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&, const HitTestResult&, RefPtr<Element>&, RefPtr<ContainerNode>&, ScrollableArea*&, bool&) { - FrameView* view = m_frame->view(); - if (!view) - return false; - if (ScrollAnimator* scrollAnimator = view->existingScrollAnimator()) - scrollAnimator->cancelAnimations(); - const FrameView::ScrollableAreaSet* areas = view->scrollableAreas(); - if (!areas) - return false; - for (FrameView::ScrollableAreaSet::const_iterator it = areas->begin(); it != areas->end(); ++it) { - ScrollableArea* sa = *it; - ScrollAnimator* animator = sa->scrollAnimator(); - if (animator) - animator->cancelAnimations(); - } - return false; } -bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent) +void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& event) { - Node* eventTarget = 0; - Scrollbar* scrollbar = 0; - if (gestureEvent.type() == PlatformEvent::GestureScrollEnd - || gestureEvent.type() == PlatformEvent::GestureScrollUpdate - || gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation) { - scrollbar = m_scrollbarHandlingScrollGesture.get(); - eventTarget = m_scrollGestureHandlingNode.get(); - } - - IntPoint adjustedPoint = gestureEvent.position(); - HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent; - if (gestureEvent.type() == PlatformEvent::GestureTapDown) { -#if ENABLE(TOUCH_ADJUSTMENT) - adjustGesturePosition(gestureEvent, adjustedPoint); -#endif - hitType |= HitTestRequest::Active; - } else if (gestureEvent.type() == PlatformEvent::GestureTapDownCancel) - hitType |= HitTestRequest::Release; - else if (gestureEvent.type() == PlatformEvent::GestureTap) { - // The mouseup event synthesized for this gesture will clear the active state of the - // targeted node, so performing a ReadOnly hit test here is fine. - hitType |= HitTestRequest::ReadOnly; - } - else - hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly; - - if (!shouldGesturesTriggerActive()) - hitType |= HitTestRequest::ReadOnly; - - if ((!scrollbar && !eventTarget) || !(hitType & HitTestRequest::ReadOnly)) { - IntPoint hitTestPoint = m_frame->view()->windowToContents(adjustedPoint); - HitTestResult result = hitTestResultAtPoint(hitTestPoint, hitType | HitTestRequest::AllowFrameScrollbars); - eventTarget = result.targetNode(); - if (!scrollbar) - scrollbar = result.scrollbar(); - } - - if (scrollbar) { - bool eventSwallowed = scrollbar->gestureEvent(gestureEvent); - if (gestureEvent.type() == PlatformEvent::GestureScrollBegin && eventSwallowed) - m_scrollbarHandlingScrollGesture = scrollbar; - else if (gestureEvent.type() == PlatformEvent::GestureScrollEnd || !eventSwallowed) - m_scrollbarHandlingScrollGesture = 0; - - if (eventSwallowed) - return true; - } - - if (eventTarget) { - bool eventSwallowed = eventTarget->dispatchGestureEvent(gestureEvent); - if (gestureEvent.type() == PlatformEvent::GestureScrollBegin || gestureEvent.type() == PlatformEvent::GestureScrollEnd) { - if (eventSwallowed) - m_scrollGestureHandlingNode = eventTarget; - } - - if (eventSwallowed) - return true; - } - - // FIXME: A more general scroll system (https://bugs.webkit.org/show_bug.cgi?id=80596) will - // eliminate the need for this. - TemporaryChange<PlatformEvent::Type> baseEventType(m_baseEventType, gestureEvent.type()); - - switch (gestureEvent.type()) { - case PlatformEvent::GestureScrollBegin: - return handleGestureScrollBegin(gestureEvent); - case PlatformEvent::GestureScrollUpdate: - case PlatformEvent::GestureScrollUpdateWithoutPropagation: - return handleGestureScrollUpdate(gestureEvent); - case PlatformEvent::GestureScrollEnd: - clearGestureScrollNodes(); - return true; - case PlatformEvent::GestureTap: - return handleGestureTap(gestureEvent); - case PlatformEvent::GestureTapDown: - return handleGestureTapDown(); - case PlatformEvent::GestureLongPress: - return handleGestureLongPress(gestureEvent); - case PlatformEvent::GestureLongTap: - return handleGestureLongTap(gestureEvent); - case PlatformEvent::GestureTwoFingerTap: - return handleGestureTwoFingerTap(gestureEvent); - case PlatformEvent::GestureTapDownCancel: - break; - default: - ASSERT_NOT_REACHED(); - } - - return false; + m_frame.mainFrame().wheelEventDeltaFilter()->updateFromDelta(FloatSize(event.deltaX(), event.deltaY())); } -bool EventHandler::handleGestureTap(const PlatformGestureEvent& gestureEvent) +bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& event, ContainerNode*, ScrollableArea*) { - // FIXME: Refactor this code to not hit test multiple times. We use the adjusted position to ensure that the correct node is targeted by the later redundant hit tests. - IntPoint adjustedPoint = gestureEvent.position(); -#if ENABLE(TOUCH_ADJUSTMENT) - adjustGesturePosition(gestureEvent, adjustedPoint); -#endif - - PlatformMouseEvent fakeMouseMove(adjustedPoint, gestureEvent.globalPosition(), - NoButton, PlatformEvent::MouseMoved, /* clickCount */ 0, - gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp()); - mouseMoved(fakeMouseMove); - - int tapCount = 1; - // FIXME: deletaX is overloaded to mean different things for different gestures. - // http://wkb.ug/93123 - if (gestureEvent.deltaX() > 0) - tapCount = static_cast<int>(gestureEvent.deltaX()); - - bool defaultPrevented = false; - PlatformMouseEvent fakeMouseDown(adjustedPoint, gestureEvent.globalPosition(), - LeftButton, PlatformEvent::MousePressed, tapCount, - gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp()); - defaultPrevented |= handleMousePressEvent(fakeMouseDown); - - PlatformMouseEvent fakeMouseUp(adjustedPoint, gestureEvent.globalPosition(), - LeftButton, PlatformEvent::MouseReleased, tapCount, - gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp()); - defaultPrevented |= handleMouseReleaseEvent(fakeMouseUp); - - return defaultPrevented; + // 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(); + + bool didHandleEvent = view ? view->wheelEvent(event) : false; + m_isHandlingWheelEvent = false; + return didHandleEvent; } -bool EventHandler::handleGestureLongPress(const PlatformGestureEvent& gestureEvent) +bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode*) { -#if ENABLE(DRAG_SUPPORT) - if (m_frame->settings() && m_frame->settings()->touchDragDropEnabled()) { - IntPoint adjustedPoint = gestureEvent.position(); -#if ENABLE(TOUCH_ADJUSTMENT) - adjustGesturePosition(gestureEvent, adjustedPoint); -#endif - PlatformMouseEvent mouseDownEvent(adjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, 0, false, false, false, false, WTF::currentTime()); - handleMousePressEvent(mouseDownEvent); - PlatformMouseEvent mouseDragEvent(adjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MouseMoved, 0, false, false, false, false, WTF::currentTime()); - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseDragEvent); - m_didStartDrag = false; - RefPtr<Frame> subframe = subframeForHitTestResult(mev); - if (subframe && !m_mouseDownMayStartDrag) { - if (subframe->eventHandler()->handleGestureLongPress(gestureEvent)) - return true; - } - handleDrag(mev, DontCheckDragHysteresis); - if (m_didStartDrag) - return true; - } -#endif - return handleGestureForTextSelectionOrContextMenu(gestureEvent); + return true; } -bool EventHandler::handleGestureLongTap(const PlatformGestureEvent& gestureEvent) +void EventHandler::platformNotifyIfEndGesture(const PlatformWheelEvent&, ScrollableArea*) { -#if ENABLE(CONTEXT_MENUS) - if (!m_didLongPressInvokeContextMenu) - return sendContextMenuEventForGesture(gestureEvent); -#endif // ENABLE(CONTEXT_MENUS) - return false; } -bool EventHandler::handleGestureForTextSelectionOrContextMenu(const PlatformGestureEvent& gestureEvent) -{ -#if ENABLE(CONTEXT_MENUS) - m_didLongPressInvokeContextMenu = (gestureEvent.type() == PlatformEvent::GestureLongPress); - return sendContextMenuEventForGesture(gestureEvent); -#else - return false; #endif -} -bool EventHandler::handleGestureTwoFingerTap(const PlatformGestureEvent& gestureEvent) +bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event) { - return handleGestureForTextSelectionOrContextMenu(gestureEvent); -} - -bool EventHandler::passGestureEventToWidget(const PlatformGestureEvent& gestureEvent, Widget* widget) -{ - if (!widget) - return false; - - if (!widget->isFrameView()) + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) return false; - return toFrameView(widget)->frame()->eventHandler()->handleGestureEvent(gestureEvent); -} + RefPtr<FrameView> protector(m_frame.view()); -bool EventHandler::passGestureEventToWidgetIfPossible(const PlatformGestureEvent& gestureEvent, RenderObject* renderer) -{ - if (m_lastHitTestResultOverWidget && renderer && renderer->isWidget()) { - Widget* widget = toRenderWidget(renderer)->widget(); - return widget && passGestureEventToWidget(gestureEvent, widget); - } - return false; -} - -bool EventHandler::handleGestureScrollBegin(const PlatformGestureEvent& gestureEvent) -{ - Document* document = m_frame->document(); - RenderObject* documentRenderer = document->renderer(); - if (!documentRenderer) - return false; - - FrameView* view = m_frame->view(); + FrameView* view = m_frame.view(); if (!view) return false; - LayoutPoint viewPoint = view->windowToContents(gestureEvent.position()); - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); - HitTestResult result(viewPoint); - document->renderView()->hitTest(request, result); - - m_lastHitTestResultOverWidget = result.isOverWidget(); - m_scrollGestureHandlingNode = result.innerNode(); - m_previousGestureScrolledNode = 0; - - Node* node = m_scrollGestureHandlingNode.get(); - if (node) - passGestureEventToWidgetIfPossible(gestureEvent, node->renderer()); - - return node && node->renderer(); -} - -bool EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gestureEvent) -{ - FloatSize delta(gestureEvent.deltaX(), gestureEvent.deltaY()); - if (delta.isZero()) - return false; - - const float scaleFactor = m_frame->pageZoomFactor() * m_frame->frameScaleFactor(); - delta.scale(1 / scaleFactor, 1 / scaleFactor); - - Node* node = m_scrollGestureHandlingNode.get(); - if (!node) - return sendScrollEventToView(gestureEvent, delta); - - // Ignore this event if the targeted node does not have a valid renderer. - RenderObject* renderer = node->renderer(); - if (!renderer) - return false; - - RefPtr<FrameView> protector(m_frame->view()); - - // Try to send the event to the correct view. - if (passGestureEventToWidgetIfPossible(gestureEvent, renderer)) - return true; - - Node* stopNode = 0; - bool scrollShouldNotPropagate = gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation; - if (scrollShouldNotPropagate) - stopNode = m_previousGestureScrolledNode.get(); - - // First try to scroll the closest scrollable RenderBox ancestor of |node|. - ScrollGranularity granularity = ScrollByPixel; - bool horizontalScroll = scrollNode(delta.width(), granularity, ScrollLeft, ScrollRight, node, &stopNode); - bool verticalScroll = scrollNode(delta.height(), granularity, ScrollUp, ScrollDown, node, &stopNode); - - if (scrollShouldNotPropagate) - m_previousGestureScrolledNode = stopNode; - - if (horizontalScroll || verticalScroll) { - setFrameWasScrolledByUser(); - return true; - } + m_isHandlingWheelEvent = true; + setFrameWasScrolledByUser(); - // Otherwise try to scroll the view. - return sendScrollEventToView(gestureEvent, delta); -} + HitTestRequest request; + HitTestResult result(view->windowToContents(event.position())); + renderView->hitTest(request, result); -bool EventHandler::sendScrollEventToView(const PlatformGestureEvent& gestureEvent, const FloatSize& scaledDelta) -{ - FrameView* view = m_frame->view(); - if (!view) - return false; + RefPtr<Element> element = result.innerElement(); + RefPtr<ContainerNode> scrollableContainer; + ScrollableArea* scrollableArea = nullptr; + bool isOverWidget = result.isOverWidget(); + platformPrepareForWheelEvents(event, result, element, scrollableContainer, scrollableArea, isOverWidget); - const float tickDivisor = static_cast<float>(WheelEvent::TickMultiplier); - IntPoint point(gestureEvent.position().x(), gestureEvent.position().y()); - IntPoint globalPoint(gestureEvent.globalPosition().x(), gestureEvent.globalPosition().y()); - PlatformWheelEvent syntheticWheelEvent(point, globalPoint, - scaledDelta.width(), scaledDelta.height(), - scaledDelta.width() / tickDivisor, scaledDelta.height() / tickDivisor, - ScrollByPixelWheelEvent, - gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey()); #if PLATFORM(MAC) - syntheticWheelEvent.setHasPreciseScrollingDeltas(true); + if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone) + m_frame.mainFrame().resetLatchingState(); #endif - bool scrolledFrame = view->wheelEvent(syntheticWheelEvent); - if (scrolledFrame) - setFrameWasScrolledByUser(); - - return scrolledFrame; -} - -void EventHandler::clearGestureScrollNodes() -{ - m_scrollGestureHandlingNode = 0; - m_previousGestureScrolledNode = 0; -} - -bool EventHandler::isScrollbarHandlingGestures() const -{ - return m_scrollbarHandlingScrollGesture.get(); -} -#endif // ENABLE(GESTURE_EVENTS) - -#if ENABLE(TOUCH_ADJUSTMENT) -bool EventHandler::shouldApplyTouchAdjustment(const PlatformGestureEvent& event) const -{ - if (m_frame->settings() && !m_frame->settings()->touchAdjustmentEnabled()) - return false; - return !event.area().isEmpty(); -} - - -bool EventHandler::bestClickableNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode) -{ - IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter); - HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius); + // FIXME: It should not be necessary to do this mutation here. + // Instead, the handlers should know convert vertical scrolls appropriately. + PlatformWheelEvent adjustedEvent = event; + if (m_baseEventType == PlatformEvent::NoType && shouldTurnVerticalTicksIntoHorizontal(result, event)) + adjustedEvent = event.copyTurningVerticalTicksIntoHorizontalTicks(); + + platformRecordWheelEvent(adjustedEvent); + + if (element) { + if (isOverWidget) { + RenderElement* target = element->renderer(); + if (is<RenderWidget>(target)) { + Widget* widget = downcast<RenderWidget>(*target).widget(); + if (widget && passWheelEventToWidget(event, *widget)) { + m_isHandlingWheelEvent = false; + if (scrollableArea) + scrollableArea->setScrolledProgrammatically(false); + platformNotifyIfEndGesture(adjustedEvent, scrollableArea); + if (!widget->platformWidget()) + return true; + return platformCompletePlatformWidgetWheelEvent(event, *widget, scrollableContainer.get()); + } + } + } - IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius); + 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); + } - // FIXME: Should be able to handle targetNode being a shadow DOM node to avoid performing uncessary hit tests - // in the case where further processing on the node is required. Returning the shadow ancestor prevents a - // regression in touchadjustment/html-label.html. Some refinement is required to testing/internals to - // handle targetNode being a shadow DOM node. - bool success = findBestClickableCandidate(targetNode, targetPoint, touchCenter, touchRect, result.rectBasedTestResult()); - if (success && targetNode) - targetNode = targetNode->deprecatedShadowAncestorNode(); - return success; -} + platformNotifyIfEndGesture(adjustedEvent, scrollableArea); + return true; + } + } -bool EventHandler::bestContextMenuNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode) -{ - IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter); - HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius); + if (scrollableArea) + scrollableArea->setScrolledProgrammatically(false); - IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius); - return findBestContextMenuCandidate(targetNode, targetPoint, touchCenter, touchRect, result.rectBasedTestResult()); + bool handledEvent = platformCompleteWheelEvent(adjustedEvent, scrollableContainer.get(), scrollableArea); + platformNotifyIfEndGesture(adjustedEvent, scrollableArea); + return handledEvent; } -bool EventHandler::bestZoomableAreaForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntRect& targetArea, Node*& targetNode) +void EventHandler::clearLatchedState() { - IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter); - HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent, touchRadius); - - IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius); - return findBestZoomableArea(targetNode, targetArea, touchCenter, touchRect, result.rectBasedTestResult()); +#if PLATFORM(MAC) + m_frame.mainFrame().resetLatchingState(); +#endif + if (auto filter = m_frame.mainFrame().wheelEventDeltaFilter()) + filter->endFilteringDeltas(); } -bool EventHandler::adjustGesturePosition(const PlatformGestureEvent& gestureEvent, IntPoint& adjustedPoint) +void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent) { - if (!shouldApplyTouchAdjustment(gestureEvent)) - return false; - - Node* targetNode = 0; - switch (gestureEvent.type()) { - case PlatformEvent::GestureTap: - case PlatformEvent::GestureTapDown: - bestClickableNodeForTouchPoint(gestureEvent.position(), IntSize(gestureEvent.area().width() / 2, gestureEvent.area().height() / 2), adjustedPoint, targetNode); - break; - case PlatformEvent::GestureLongPress: - case PlatformEvent::GestureLongTap: - case PlatformEvent::GestureTwoFingerTap: - bestContextMenuNodeForTouchPoint(gestureEvent.position(), IntSize(gestureEvent.area().width() / 2, gestureEvent.area().height() / 2), adjustedPoint, targetNode); - break; - default: - // FIXME: Implement handling for other types as needed. - ASSERT_NOT_REACHED(); + if (!startNode || !wheelEvent) + return; + + FloatSize filteredPlatformDelta(wheelEvent->deltaX(), wheelEvent->deltaY()); + if (const PlatformWheelEvent* platformWheelEvent = wheelEvent->wheelEvent()) { + filteredPlatformDelta.setWidth(platformWheelEvent->deltaX()); + filteredPlatformDelta.setHeight(platformWheelEvent->deltaY()); } - return targetNode; -} + +#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(); +#else + Element* stopElement = nullptr; #endif + + + if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &stopElement, filteredPlatformDelta)) + wheelEvent->setDefaultHandled(); + +#if PLATFORM(MAC) + if (latchedState && !latchedState->wheelEventElement()) + latchedState->setPreviousWheelScrolledElement(stopElement); +#endif +} #if ENABLE(CONTEXT_MENUS) bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event) { - Document* doc = m_frame->document(); - FrameView* v = m_frame->view(); - if (!v) + Document* doc = m_frame.document(); + 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()); + LayoutPoint viewportPos = view->windowToContents(event.position()); HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = doc->prepareMouseEvent(request, viewportPos, event); + MouseEventWithHitTestResults mouseEvent = doc->prepareMouseEvent(request, viewportPos, event); - if (m_frame->editor().behavior().shouldSelectOnContextualMenuClick() - && !m_frame->selection()->contains(viewportPos) - && !mev.scrollbar() + // 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) // 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() { - FrameView* view = m_frame->view(); + FrameView* view = m_frame.view(); if (!view) return false; - Document* doc = m_frame->document(); + Document* doc = m_frame.document(); if (!doc) return false; @@ -2926,7 +2736,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; @@ -2934,12 +2744,12 @@ 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(); - IntRect firstRect = m_frame->editor().firstRectForRange(selectionRange.get()); + if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) { + RefPtr<Range> selectionRange = selection.toNormalizedRange(); + IntRect firstRect = m_frame.editor().firstRectForRange(selectionRange.get()); int x = rightAligned ? firstRect.maxX() : firstRect.x(); // In a multiline edit, firstRect.maxY() would endup on the next line, so -1. @@ -2957,7 +2767,7 @@ bool EventHandler::sendContextMenuEventForKey() kContextMenuMargin); } - m_frame->view()->setCursor(pointerCursor()); + m_frame.view()->setCursor(pointerCursor()); IntPoint position = view->contentsToRootView(location); IntPoint globalPosition = view->hostWindow()->rootViewToScreen(IntRect(position, IntSize())).location(); @@ -2980,33 +2790,10 @@ bool EventHandler::sendContextMenuEventForKey() PlatformEvent::Type eventType = PlatformEvent::MousePressed; #endif - PlatformMouseEvent mouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime()); - - return !dispatchMouseEvent(eventNames().contextmenuEvent, targetNode, true, 0, mouseEvent, false); -} - -#if ENABLE(GESTURE_EVENTS) -bool EventHandler::sendContextMenuEventForGesture(const PlatformGestureEvent& event) -{ -#if OS(WINDOWS) - PlatformEvent::Type eventType = PlatformEvent::MouseReleased; -#else - PlatformEvent::Type eventType = PlatformEvent::MousePressed; -#endif + PlatformMouseEvent platformMouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime(), ForceAtClick); - IntPoint adjustedPoint = event.position(); -#if ENABLE(TOUCH_ADJUSTMENT) - adjustGesturePosition(event, adjustedPoint); -#endif - PlatformMouseEvent mouseEvent(adjustedPoint, event.globalPosition(), RightButton, eventType, 1, false, false, false, false, WTF::currentTime()); - // To simulate right-click behavior, we send a right mouse down and then - // context menu event. - handleMousePressEvent(mouseEvent); - return sendContextMenuEvent(mouseEvent); - // We do not need to send a corresponding mouse release because in case of - // right-click, the context menu takes capture and consumes all events. + return !dispatchMouseEvent(eventNames().contextmenuEvent, targetNode, true, 0, platformMouseEvent, false); } -#endif // ENABLE(GESTURE_EVENTS) #endif // ENABLE(CONTEXT_MENUS) void EventHandler::scheduleHoverStateUpdate() @@ -3015,41 +2802,44 @@ void EventHandler::scheduleHoverStateUpdate() m_hoverTimer.startOneShot(0); } +#if ENABLE(CURSOR_SUPPORT) void EventHandler::scheduleCursorUpdate() { if (!m_cursorUpdateTimer.isActive()) m_cursorUpdateTimer.startOneShot(cursorUpdateInterval); } +#endif void EventHandler::dispatchFakeMouseMoveEventSoon() { +#if !ENABLE(IOS_TOUCH_EVENTS) if (m_mousePressed) return; if (m_mousePositionIsUnknown) return; - Settings* settings = m_frame->settings(); - if (settings && !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 > fakeMouseMoveShortInterval) { - 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) { - FrameView* view = m_frame->view(); +#if ENABLE(IOS_TOUCH_EVENTS) + UNUSED_PARAM(quad); +#else + FrameView* view = m_frame.view(); if (!view) return; @@ -3057,27 +2847,24 @@ 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); - Settings* settings = m_frame->settings(); - if (settings && !settings->deviceSupportsMouse()) - return; - - FrameView* view = m_frame->view(); + FrameView* view = m_frame.view(); if (!view) return; - if (!m_frame->page() || !m_frame->page()->isOnscreen() || !m_frame->page()->focusController()->isActive()) + if (!m_frame.page() || !m_frame.page()->isVisible() || !m_frame.page()->focusController().isActive()) return; bool shiftKey; @@ -3085,9 +2872,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); mouseMoved(fakeMouseMoveEvent); } +#endif // !ENABLE(IOS_TOUCH_EVENTS) void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet) { @@ -3097,40 +2885,38 @@ 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); - ASSERT(m_frame->document()); + ASSERT(m_frame.document()); - if (RenderView* renderer = m_frame->contentRenderer()) { - if (FrameView* view = m_frame->view()) { + if (RenderView* renderView = m_frame.contentRenderer()) { + if (FrameView* view = m_frame.view()) { HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowShadowContent); 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.innerElement()); } } } -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()) + if ((event.modifiers() & ~PlatformEvent::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; } @@ -3144,7 +2930,7 @@ bool EventHandler::needsKeyboardEventDisambiguationQuirks() const #if ENABLE(FULLSCREEN_API) bool EventHandler::isKeyEventAllowedInFullScreen(const PlatformKeyboardEvent& keyEvent) const { - Document* document = m_frame->document(); + Document* document = m_frame.document(); if (document->webkitFullScreenKeyboardInputAllowed()) return true; @@ -3165,43 +2951,45 @@ bool EventHandler::isKeyEventAllowedInFullScreen(const PlatformKeyboardEvent& ke bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) { - RefPtr<FrameView> protector(m_frame->view()); + RefPtr<FrameView> protector(m_frame.view()); #if ENABLE(FULLSCREEN_API) - if (m_frame->document()->webkitIsFullScreen() && !isKeyEventAllowedInFullScreen(initialKeyEvent)) + if (m_frame.document()->webkitIsFullScreen() && !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 (Page* page = m_frame->page()) { - if (page->mainFrame()->eventHandler()->panScrollInProgress()) { - // If a key is pressed while the panScroll is in progress then we want to stop - if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent.type() == PlatformEvent::RawKeyDown) - stopAutoscrollTimer(); + if (m_frame.mainFrame().eventHandler().panScrollInProgress()) { + // If a key is pressed while the panScroll is in progress then we want to stop + if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent.type() == PlatformEvent::RawKeyDown) + stopAutoscrollTimer(); - // If we were in panscroll mode, we swallow the key event - return true; - } + // If we were in panscroll mode, we swallow the key event + return true; } #endif // Check for cases where we are too early for events -- possible unmatched key up // from pressing return in the location bar. - RefPtr<Node> node = eventTargetNodeForDocument(m_frame->document()); - if (!node) + RefPtr<Element> element = eventTargetElementForDocument(m_frame.document()); + if (!element) return false; - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture, m_frame.document()); UserTypingGestureIndicator typingGestureIndicator(m_frame); - if (FrameView* view = m_frame->view()) - view->resetDeferredRepaintDelay(); + if (FrameView* view = m_frame.view()) + view->disableLayerFlushThrottlingTemporarilyForInteraction(); // FIXME (bug 68185): this call should be made at another abstraction layer - m_frame->loader()->resetMultipleFormSubmissionProtection(); + m_frame.loader().resetMultipleFormSubmissionProtection(); // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match. // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict @@ -3214,22 +3002,22 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch. if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char) - return !node->dispatchKeyEvent(initialKeyEvent); + return !element->dispatchKeyEvent(initialKeyEvent); bool backwardCompatibilityMode = needsKeyboardEventDisambiguationQuirks(); 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(node); + keydown->setTarget(element); if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) { - node->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(); + bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame(); return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; } @@ -3238,29 +3026,32 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) // in order to match IE: // 1. preventing default handling of keydown and keypress events has no effect on IM input; // 2. if an input method handles the event, its keyCode is set to 229 in keydown event. - m_frame->editor().handleInputMethodKeydown(keydown.get()); + m_frame.editor().handleInputMethodKeydown(keydown.ptr()); bool handledByInputMethod = keydown->defaultHandled(); if (handledByInputMethod) { keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode); - keydown = KeyboardEvent::create(keyDownEvent, m_frame->document()->defaultView()); - keydown->setTarget(node); + keydown = KeyboardEvent::create(keyDownEvent, m_frame.document()->defaultView()); + keydown->setTarget(element); keydown->setDefaultHandled(); } + + if (accessibilityPreventsEventPropogation(keydown)) + keydown->stopPropagation(); - node->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 changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame(); bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; if (handledByInputMethod || (keydownResult && !backwardCompatibilityMode)) return keydownResult; - // Focus may have changed during keydown handling, so refetch node. - // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original node. + // Focus may have changed during keydown handling, so refetch element. + // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original element. if (!keydownResult) { - node = eventTargetNodeForDocument(m_frame->document()); - if (!node) + element = eventTargetElementForDocument(m_frame.document()); + if (!element) return false; } @@ -3268,24 +3059,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()); - keypress->setTarget(node); + 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 - node->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; @@ -3301,14 +3092,53 @@ static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier) return retVal; } -static void handleKeyboardSelectionMovement(FrameSelection* selection, KeyboardEvent* event) +static void setInitialKeyboardSelection(Frame& frame, SelectionDirection direction) +{ + Document* document = frame.document(); + if (!document) + return; + + FrameSelection& selection = frame.selection(); + + if (!selection.isNone()) + return; + + 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) { if (!event) return; - bool isOptioned = event->getModifierState("Alt"); + 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; @@ -3337,15 +3167,47 @@ static void handleKeyboardSelectionMovement(FrameSelection* selection, KeyboardE break; } - FrameSelection::EAlteration alternation = event->getModifierState("Shift") ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove; - selection->modify(alternation, direction, granularity, UserTriggered); + if (isSelection) + selection.modify(alternation, direction, granularity, UserTriggered); + else + setInitialKeyboardSelection(frame, direction); + event->setDefaultHandled(); } - + +void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent* event) +{ + if (event->type() == eventNames().keydownEvent) { + if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) + handleKeyboardSelectionMovement(m_frame, event); + } +} + +bool EventHandler::accessibilityPreventsEventPropogation(KeyboardEvent& event) +{ +#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) { if (event->type() == eventNames().keydownEvent) { - m_frame->editor().handleKeyboardEvent(event); + m_frame.editor().handleKeyboardEvent(event); if (event->defaultHandled()) return; if (event->keyIdentifier() == "U+0009") @@ -3358,12 +3220,10 @@ void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event) defaultArrowEventHandler(direction, event); } - // provides KB navigation and selection for enhanced accessibility users - if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) - handleKeyboardSelectionMovement(m_frame->selection(), event); + handleKeyboardSelectionMovementForAccessibility(event); } if (event->type() == eventNames().keypressEvent) { - m_frame->editor().handleKeyboardEvent(event); + m_frame.editor().handleKeyboardEvent(event); if (event->defaultHandled()) return; if (event->charCode() == ' ') @@ -3380,18 +3240,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: @@ -3404,15 +3261,15 @@ 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::freeDataTransfer() { - 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) @@ -3422,12 +3279,12 @@ void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperat 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; + freeDataTransfer(); + 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; @@ -3443,7 +3300,7 @@ void EventHandler::updateDragStateAfterEditDragIfNeeded(Element* rootEditableEle // 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.get(), event, dragState().clipboard.get()); + return !dispatchDragEvent(eventType, *dragState().source, event, dragState().dataTransfer.get()); } static bool ExactlyOneBitSet(DragSourceAction n) @@ -3473,9 +3330,9 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr // try to find an element that wants to be dragged HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); 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()); + m_frame.contentRenderer()->hitTest(request, result); + if (m_frame.page()) + dragState().source = m_frame.page()->dragController().draggableElement(&m_frame, result.innerElement(), m_mouseDownPos, dragState()); if (!dragState().source) m_mouseDownMayStartDrag = false; // no element is draggable @@ -3493,7 +3350,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; @@ -3512,14 +3369,21 @@ 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; } // We are starting a text/image/url drag, so the cursor should be an arrow - if (FrameView* view = m_frame->view()) { + if (FrameView* view = m_frame.view()) { // FIXME <rdar://7577595>: Custom cursors aren't supported during drag and drop (default to pointer). view->setCursor(pointerCursor()); } @@ -3532,10 +3396,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. + freeDataTransfer(); - 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. @@ -3544,7 +3408,7 @@ 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. @@ -3554,27 +3418,26 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr } 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(); } } if (m_mouseDownMayStartDrag) { - Page* page = m_frame->page(); - DragController* dragController = page ? page->dragController() : 0; - m_didStartDrag = dragController && dragController->startDrag(m_frame, dragState(), srcOp, event.event(), m_mouseDownPos); + Page* page = m_frame.page(); + m_didStartDrag = page && page->dragController().startDrag(m_frame, dragState(), srcOp, event.event(), m_mouseDownPos); // In WebKit2 we could re-enter this code and start another drag. // On OS X this causes problems with the ownership of the pasteboard and the promised types. if (m_didStartDrag) { @@ -3591,39 +3454,47 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr cleanupDrag: if (!m_mouseDownMayStartDrag) { // Something failed to start the drag, clean up. - freeClipboard(); - dragState().source = 0; + freeDataTransfer(); + 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) { // 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); - - if (!m_frame) - return false; + ASSERT(!is<KeyboardEvent>(underlyingEvent) || downcast<KeyboardEvent>(*underlyingEvent).type() == eventNames().keypressEvent); EventTarget* target; if (underlyingEvent) target = underlyingEvent->target(); else - target = eventTargetNodeForDocument(m_frame->document()); + target = eventTargetElementForDocument(m_frame.document()); if (!target) return false; - if (FrameView* view = m_frame->view()) - view->resetDeferredRepaintDelay(); + 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(); } @@ -3637,9 +3508,10 @@ bool EventHandler::isKeyboardOptionTab(KeyboardEvent* event) bool EventHandler::eventInvertsTabsToLinksClientCallResult(KeyboardEvent* event) { -#if PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(EFL) +#if PLATFORM(COCOA) || PLATFORM(QT) || PLATFORM(EFL) return EventHandler::isKeyboardOptionTab(event); #else + UNUSED_PARAM(event); return false; #endif } @@ -3648,17 +3520,17 @@ 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. - Page* page = m_frame->page(); + Page* page = m_frame.page(); if (!page) return false; - bool tabsToLinksClientCallResult = page->chrome().client()->keyboardUIMode() & KeyboardAccessTabsToLinks; + bool tabsToLinksClientCallResult = page->chrome().client().keyboardUIMode() & KeyboardAccessTabsToLinks; return eventInvertsTabsToLinksClientCallResult(event) ? !tabsToLinksClientCallResult : tabsToLinksClientCallResult; } void EventHandler::defaultTextInputEventHandler(TextEvent* event) { - if (m_frame->editor().handleTextEvent(event)) + if (m_frame.editor().handleTextEvent(event)) event->setDefaultHandled(); } @@ -3676,7 +3548,7 @@ void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event) return; } - FrameView* view = m_frame->view(); + FrameView* view = m_frame.view(); if (!view) return; @@ -3691,22 +3563,22 @@ void EventHandler::defaultBackspaceEventHandler(KeyboardEvent* event) if (event->ctrlKey() || event->metaKey() || event->altKey() || event->altGraphKey()) return; - if (!m_frame->editor().behavior().shouldNavigateBackOnBackspace()) + if (!m_frame.editor().behavior().shouldNavigateBackOnBackspace()) return; - Page* page = m_frame->page(); + Page* page = m_frame.page(); if (!page) return; - if (!m_frame->settings()->backspaceKeyNavigationEnabled()) + if (!m_frame.settings().backspaceKeyNavigationEnabled()) return; bool handledEvent = false; if (event->shiftKey()) - handledEvent = page->goForward(); + handledEvent = page->backForward().goForward(); else - handledEvent = page->goBack(); + handledEvent = page->backForward().goBack(); if (handledEvent) event->setDefaultHandled(); @@ -3720,19 +3592,19 @@ void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, Keybo if (event->ctrlKey() || event->metaKey() || event->altGraphKey() || event->shiftKey()) return; - Page* page = m_frame->page(); + Page* page = m_frame.page(); if (!page) return; - if (!isSpatialNavigationEnabled(m_frame)) + if (!isSpatialNavigationEnabled(&m_frame)) return; // Arrows and other possible directional navigation keys can be used in design // mode editing. - if (m_frame->document()->inDesignMode()) + if (m_frame.document()->inDesignMode()) return; - if (page->focusController()->advanceFocus(focusDirection, event)) + if (page->focusController().advanceFocus(focusDirection, event)) event->setDefaultHandled(); } @@ -3744,7 +3616,7 @@ void EventHandler::defaultTabEventHandler(KeyboardEvent* event) if (event->ctrlKey() || event->metaKey() || event->altGraphKey()) return; - Page* page = m_frame->page(); + Page* page = m_frame.page(); if (!page) return; if (!page->tabKeyCyclesThroughElements()) @@ -3753,51 +3625,40 @@ void EventHandler::defaultTabEventHandler(KeyboardEvent* event) FocusDirection focusDirection = event->shiftKey() ? FocusDirectionBackward : FocusDirectionForward; // Tabs can be used in design mode editing. - if (m_frame->document()->inDesignMode()) + if (m_frame.document()->inDesignMode()) return; - if (page->focusController()->advanceFocus(focusDirection, event)) + 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(); - } - } -} - void EventHandler::sendScrollEvent() { setFrameWasScrolledByUser(); - if (m_frame->view() && m_frame->document()) - m_frame->document()->eventQueue()->enqueueOrDispatchScrollEvent(m_frame->document(), DocumentEventQueue::ScrollEventDocumentTarget); + if (m_frame.view() && m_frame.document()) + m_frame.document()->eventQueue().enqueueOrDispatchScrollEvent(*m_frame.document()); } void EventHandler::setFrameWasScrolledByUser() { - FrameView* v = m_frame->view(); + FrameView* v = m_frame.view(); if (v) 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 0. +// last to scrollbar if setLast is true; else set last to nullptr. void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, bool setLast) { - if (m_lastScrollbarUnderMouse != scrollbar) { + if (m_lastScrollbarUnderMouse.get() != scrollbar) { // Send mouse exited to the old scrollbar. if (m_lastScrollbarUnderMouse) m_lastScrollbarUnderMouse->mouseExited(); @@ -3806,12 +3667,14 @@ void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, bool setL if (scrollbar && setLast) scrollbar->mouseEntered(); - m_lastScrollbarUnderMouse = setLast ? scrollbar : 0; + if (setLast && scrollbar) + m_lastScrollbarUnderMouse = scrollbar->createWeakPtr(); + else + m_lastScrollbarUnderMouse = nullptr; } } -#if ENABLE(TOUCH_EVENTS) - +#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State state) { switch (state) { @@ -3831,12 +3694,13 @@ static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State } } -HitTestResult EventHandler::hitTestResultInFrame(Frame* frame, const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType) +static HitTestResult hitTestResultInFrame(Frame* frame, const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType) { HitTestResult result(point); if (!frame || !frame->contentRenderer()) return result; + if (frame->view()) { IntRect rect = frame->view()->visibleContentRect(); if (!rect.contains(roundedIntPoint(point))) @@ -3857,11 +3721,11 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) // A different view on the 'touches' list above, filtered and grouped by event target. Used for the // 'targetTouches' list in the JS event. - typedef HashMap<EventTarget*, RefPtr<TouchList> > TargetTouchesMap; + typedef HashMap<EventTarget*, RefPtr<TouchList>> TargetTouchesMap; TargetTouchesMap touchesByTarget; // Array of touches per state, used to assemble the 'changedTouches' list in the JS event. - typedef HashSet<RefPtr<EventTarget> > EventTargetSet; + typedef HashSet<RefPtr<EventTarget>> EventTargetSet; struct { // The touches corresponding to the particular change state this struct instance represents. RefPtr<TouchList> m_touches; @@ -3871,21 +3735,18 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) const Vector<PlatformTouchPoint>& points = event.touchPoints(); - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture, 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()); @@ -3926,39 +3787,34 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) result = hitTestResultAtPoint(pagePoint, hitType); m_originatingTouchPointTargetKey = touchPointTargetKey; } else if (m_originatingTouchPointDocument.get() && m_originatingTouchPointDocument->frame()) { - LayoutPoint pagePointInOriginatingDocument = documentPointForWindowPoint(m_originatingTouchPointDocument->frame(), point.pos()); + LayoutPoint pagePointInOriginatingDocument = documentPointForWindowPoint(*m_originatingTouchPointDocument->frame(), point.pos()); result = hitTestResultInFrame(m_originatingTouchPointDocument->frame(), pagePointInOriginatingDocument, hitType); if (!result.innerNode()) continue; } else continue; - // FIXME: Is the following code different from just calling innerElement? - Node* node = result.innerNode(); + // FIXME: This code should use Element* instead of Node*. + Node* node = result.innerElement(); ASSERT(node); - // Touch events should not go to text nodes - if (node->isTextNode()) - node = EventPathWalker::parent(node); - if (InspectorInstrumentation::handleTouchEvent(m_frame->page(), node)) + if (node && InspectorInstrumentation::handleTouchEvent(m_frame, *node)) return true; - Document* doc = node->document(); + Document& doc = node->document(); // Record the originating touch document even if it does not have a touch listener. if (freshTouchEvents) { - m_originatingTouchPointDocument = doc; + m_originatingTouchPointDocument = &doc; freshTouchEvents = false; } - if (!doc) - continue; - if (!doc->hasTouchEventHandlers()) + if (!doc.hasTouchEventHandlers()) continue; m_originatingTouchPointTargets.set(touchPointTargetKey, node); touchTarget = node; } 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) - m_frame->document()->updateHoverActiveState(hitType, 0); + m_frame.document()->updateHoverActiveState(hitType, 0); if (touchPointTargetKey == m_originatingTouchPointTargetKey) m_originatingTouchPointTargetKey = 0; @@ -3971,18 +3827,16 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) if (!touchTarget.get()) continue; - Document* doc = touchTarget->toNode()->document(); - if (!doc) - continue; - if (!doc->hasTouchEventHandlers()) + Document& doc = touchTarget->toNode()->document(); + if (!doc.hasTouchEventHandlers()) continue; - Frame* targetFrame = doc->frame(); + Frame* targetFrame = doc.frame(); if (!targetFrame) continue; - if (m_frame != targetFrame) { + if (&m_frame != targetFrame) { // pagePoint should always be relative to the target elements containing frame. - pagePoint = documentPointForWindowPoint(targetFrame, point.pos()); + pagePoint = documentPointForWindowPoint(*targetFrame, point.pos()); } float scaleFactor = targetFrame->pageZoomFactor() * targetFrame->frameScaleFactor(); @@ -4023,7 +3877,7 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) } 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; @@ -4036,48 +3890,53 @@ 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()); + stateName, touchEventTarget->toNode()->document().defaultView(), + 0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey()); + touchEventTarget->toNode()->dispatchTouchEvent(touchEvent); swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled(); } } return swallowedEvent; } +#endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) -bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& event) +#if ENABLE(TOUCH_EVENTS) +bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& platformMouseEvent) { - if (!m_frame || !m_frame->settings() || !m_frame->settings()->isTouchEventEmulationEnabled()) +#if ENABLE(IOS_TOUCH_EVENTS) + 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)) + 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 +} +#endif // ENABLE(TOUCH_EVENTS) void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event) { @@ -4086,4 +3945,9 @@ void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event) m_lastKnownMouseGlobalPosition = event.globalPosition(); } +void EventHandler::setImmediateActionStage(ImmediateActionStage stage) +{ + m_immediateActionStage = stage; +} + } // namespace WebCore |