diff options
author | Allan Sandfeld Jensen <allan.jensen@digia.com> | 2013-09-13 12:51:20 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 20:50:05 +0200 |
commit | d441d6f39bb846989d95bcf5caf387b42414718d (patch) | |
tree | e367e64a75991c554930278175d403c072de6bb8 /Source/WebCore/page/EventHandler.cpp | |
parent | 0060b2994c07842f4c59de64b5e3e430525c4b90 (diff) | |
download | qtwebkit-d441d6f39bb846989d95bcf5caf387b42414718d.tar.gz |
Import Qt5x2 branch of QtWebkit for Qt 5.2
Importing a new snapshot of webkit.
Change-Id: I2d01ad12cdc8af8cb015387641120a9d7ea5f10c
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@digia.com>
Diffstat (limited to 'Source/WebCore/page/EventHandler.cpp')
-rw-r--r-- | Source/WebCore/page/EventHandler.cpp | 1379 |
1 files changed, 744 insertions, 635 deletions
diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp index 22d616e0f..c2a172a8d 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 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) * @@ -29,10 +29,10 @@ #include "EventHandler.h" #include "AXObjectCache.h" +#include "AutoscrollController.h" #include "CachedImage.h" #include "Chrome.h" #include "ChromeClient.h" -#include "ComposedShadowTreeWalker.h" #include "Cursor.h" #include "CursorList.h" #include "Document.h" @@ -42,6 +42,8 @@ #include "Editor.h" #include "EditorClient.h" #include "EventNames.h" +#include "EventPathWalker.h" +#include "ExceptionCodePlaceholder.h" #include "FloatPoint.h" #include "FloatRect.h" #include "FocusController.h" @@ -77,7 +79,6 @@ #include "Settings.h" #include "ShadowRoot.h" #include "SpatialNavigation.h" -#include "StaticHashSetNodeList.h" #include "StyleCachedImage.h" #include "TextEvent.h" #include "TextIterator.h" @@ -111,6 +112,10 @@ #include "TouchList.h" #endif +#if ENABLE(CSS_IMAGE_SET) +#include "StyleCachedImageSet.h" +#endif + namespace WebCore { using namespace HTMLNames; @@ -133,20 +138,23 @@ const int CompositionEventKeyCode = 229; using namespace SVGNames; #endif -// When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth -const double autoscrollInterval = 0.05; +// 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. +const double fakeMouseMoveShortInterval = 0.1; +const double fakeMouseMoveLongInterval = 0.250; -// The amount of time to wait before sending a fake mouse event, triggered during a scroll. -const double fakeMouseMoveMinimumInterval = 0.1; -// Amount to increase the fake mouse event throttling when the running average exceeds the delay. -// Picked fairly arbitrarily. -const double fakeMouseMoveIntervalIncrease = 0.05; -const double fakeMouseMoveRunningAverageCount = 10; -// Decrease the fakeMouseMoveInterval when the current delay is >2x the running average, -// but only decrease to 3/4 the current delay to avoid too much thrashing. -// Not sure this distinction really matters in practice. -const double fakeMouseMoveIntervalReductionLimit = 0.5; -const double fakeMouseMoveIntervalReductionFraction = 0.75; +// 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; +#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 +// dividing cursor sizes (limited above) by the scale. +const double minimumCursorScale = 0.001; +#endif enum NoCursorChangeType { NoCursorChange }; @@ -163,28 +171,21 @@ private: Cursor m_cursor; }; -class RunningAverageDurationTracker { +class MaximumDurationTracker { public: - RunningAverageDurationTracker(double* average, unsigned numberOfRunsToTrack) - : m_average(average) - , m_numberOfRunsToTrack(numberOfRunsToTrack) + explicit MaximumDurationTracker(double *maxDuration) + : m_maxDuration(maxDuration) , m_start(monotonicallyIncreasingTime()) { } - ~RunningAverageDurationTracker() + ~MaximumDurationTracker() { - double duration = monotonicallyIncreasingTime() - m_start; - if (!*m_average) { - *m_average = duration; - return; - } - *m_average = (*m_average * (m_numberOfRunsToTrack - 1) + (duration)) / m_numberOfRunsToTrack; + *m_maxDuration = max(*m_maxDuration, monotonicallyIncreasingTime() - m_start); } private: - double* m_average; - unsigned m_numberOfRunsToTrack; + double* m_maxDuration; double m_start; }; @@ -255,17 +256,18 @@ public: }; #endif -static inline ScrollGranularity wheelGranularityToScrollGranularity(WheelEvent::Granularity granularity) +static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned deltaMode) { - switch (granularity) { - case WheelEvent::Page: + switch (deltaMode) { + case WheelEvent::DOM_DELTA_PAGE: return ScrollByPage; - case WheelEvent::Line: + case WheelEvent::DOM_DELTA_LINE: return ScrollByLine; - case WheelEvent::Pixel: + case WheelEvent::DOM_DELTA_PIXEL: + return ScrollByPixel; + default: return ScrollByPixel; } - return ScrollByPixel; } static inline bool scrollNode(float delta, ScrollGranularity granularity, ScrollDirection positiveDirection, ScrollDirection negativeDirection, Node* node, Node** stopNode) @@ -279,31 +281,13 @@ static inline bool scrollNode(float delta, ScrollGranularity granularity, Scroll return enclosingBox->scroll(delta < 0 ? negativeDirection : positiveDirection, granularity, absDelta, stopNode); } -static Node* closestScrollableNodeInDocumentIfPossible(Node* node) -{ - for (Node* scrollableNode = node; scrollableNode; scrollableNode = scrollableNode->parentNode()) { - if (scrollableNode->isDocumentNode()) - break; - RenderObject* renderer = scrollableNode->renderer(); - if (renderer && renderer->isBox() && toRenderBox(renderer)->canBeScrolledAndHasScrollableArea()) - return scrollableNode; - } - return node; -} - -#if ENABLE(GESTURE_EVENTS) static inline bool shouldGesturesTriggerActive() { // If the platform we're on supports GestureTapDown and GestureTapCancel then we'll // rely on them to set the active state. Unfortunately there's no generic way to // know in advance what event types are supported. -#if PLATFORM(CHROMIUM) && !OS(ANDROID) - return true; -#else return false; -#endif } -#endif #if !PLATFORM(MAC) @@ -332,22 +316,19 @@ EventHandler::EventHandler(Frame* frame) #endif , m_mouseDownWasSingleClickInSelection(false) , m_selectionInitiationState(HaveNotStartedSelection) - , m_panScrollInProgress(false) - , m_panScrollButtonPressed(false) - , m_springLoadedPanScrollInProgress(false) , m_hoverTimer(this, &EventHandler::hoverTimerFired) - , m_autoscrollTimer(this, &EventHandler::autoscrollTimerFired) - , m_autoscrollRenderer(0) - , m_autoscrollInProgress(false) + , m_cursorUpdateTimer(this, &EventHandler::cursorUpdateTimerFired) + , m_autoscrollController(adoptPtr(new AutoscrollController)) , m_mouseDownMayStartAutoscroll(false) , m_mouseDownWasInSubframe(false) - , m_fakeMouseMoveEventTimer(this, &EventHandler::fakeMouseMoveEventTimerFired, fakeMouseMoveMinimumInterval) + , 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) @@ -356,18 +337,30 @@ EventHandler::EventHandler(Frame* frame) , m_activationEventNumber(-1) #endif #if ENABLE(TOUCH_EVENTS) + , m_originatingTouchPointTargetKey(0) , m_touchPressed(false) #endif - , m_mouseMovedDurationRunningAverage(0) +#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) +#endif { } EventHandler::~EventHandler() { ASSERT(!m_fakeMouseMoveEventTimer.isActive()); +#if ENABLE(CURSOR_VISIBILITY) + ASSERT(!m_autoHideCursorTimer.isActive()); +#endif } #if ENABLE(DRAG_SUPPORT) @@ -381,7 +374,11 @@ DragState& EventHandler::dragState() void EventHandler::clear() { m_hoverTimer.stop(); + m_cursorUpdateTimer.stop(); m_fakeMouseMoveEventTimer.stop(); +#if ENABLE(CURSOR_VISIBILITY) + cancelAutoHideCursorTimer(); +#endif m_resizeLayer = 0; m_nodeUnderMouse = 0; m_lastNodeUnderMouse = 0; @@ -398,8 +395,9 @@ void EventHandler::clear() m_dragTarget = 0; m_shouldOnlyFireDragOverEvent = false; #endif - m_currentMousePosition = IntPoint(); - m_currentMouseGlobalPosition = IntPoint(); + m_mousePositionIsUnknown = true; + m_lastKnownMousePosition = IntPoint(); + m_lastKnownMouseGlobalPosition = IntPoint(); m_mousePressNode = 0; m_mousePressed = false; m_capturesDragging = false; @@ -408,12 +406,16 @@ void EventHandler::clear() m_previousWheelScrolledNode = 0; #if ENABLE(TOUCH_EVENTS) m_originatingTouchPointTargets.clear(); + m_originatingTouchPointDocument.clear(); + m_originatingTouchPointTargetKey = 0; #endif #if ENABLE(GESTURE_EVENTS) m_scrollGestureHandlingNode = 0; + m_lastHitTestResultOverWidget = false; + m_previousGestureScrolledNode = 0; m_scrollbarHandlingScrollGesture = 0; #endif - m_mouseMovedDurationRunningAverage = 0; + m_maxMouseMovedDuration = 0; m_baseEventType = PlatformEvent::NoType; m_didStartDrag = false; m_didLongPressInvokeContextMenu = false; @@ -440,7 +442,25 @@ static inline bool dispatchSelectStart(Node* node) return node->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)); } -bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& newSelection, TextGranularity granularity) +static VisibleSelection expandSelectionToRespectUserSelectAll(Node* targetNode, const VisibleSelection& selection) +{ +#if ENABLE(USERSELECT_ALL) + Node* rootUserSelectAll = Position::rootUserSelectAllForNode(targetNode); + if (!rootUserSelectAll) + return selection; + + VisibleSelection newSelection(selection); + newSelection.setBase(positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary)); + newSelection.setExtent(positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary)); + + return newSelection; +#else + UNUSED_PARAM(targetNode); + return selection; +#endif +} + +bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity) { if (Position::nodeIsUserSelectNone(targetNode)) return false; @@ -448,13 +468,6 @@ bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targe if (!dispatchSelectStart(targetNode)) return false; - VisibleSelection selection(newSelection); -#if ENABLE(USERSELECT_ALL) - if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(targetNode)) { - selection.setBase(positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary)); - selection.setExtent(positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary)); - } -#endif if (selection.isRange()) m_selectionInitiationState = ExtendedSelection; else { @@ -482,7 +495,7 @@ void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& resul if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange()) newSelection.appendTrailingWhitespace(); - updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, WordGranularity); + updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), WordGranularity); } } @@ -490,7 +503,7 @@ void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestRe { if (m_mouseDownMayStartSelect) { selectClosestWordFromHitTestResult(result.hitTestResult(), - (result.event().clickCount() == 2 && m_frame->editor()->isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace); + (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace); } } @@ -508,7 +521,7 @@ void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHit if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(URLElement)) newSelection = VisibleSelection::selectionFromContentsOfNode(URLElement); - updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, WordGranularity); + updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), WordGranularity); } } @@ -546,7 +559,7 @@ bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestR newSelection.expandUsingGranularity(ParagraphGranularity); } - return updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, ParagraphGranularity); + return updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), ParagraphGranularity); } static int textDistance(const Position& start, const Position& end) @@ -584,8 +597,15 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR TextGranularity granularity = CharacterGranularity; if (extendSelection && newSelection.isCaretOrRange()) { - ASSERT(m_frame->settings()); - if (m_frame->settings()->editingBehaviorType() == EditingMacBehavior) { + VisibleSelection selectionInUserSelectAll = expandSelectionToRespectUserSelectAll(innerNode, VisibleSelection(pos)); + if (selectionInUserSelectAll.isRange()) { + if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0) + pos = selectionInUserSelectAll.start(); + else if (comparePositions(newSelection.end(), selectionInUserSelectAll.end()) < 0) + pos = selectionInUserSelectAll.end(); + } + + 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(); @@ -604,8 +624,8 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR newSelection.expandUsingGranularity(m_frame->selection()->granularity()); } } else - newSelection = VisibleSelection(visiblePos); - + newSelection = expandSelectionToRespectUserSelectAll(innerNode, visiblePos); + bool handled = updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, granularity); if (event.event().button() == MiddleButton) { @@ -620,17 +640,14 @@ static inline bool canMouseDownStartSelect(Node* node) if (!node || !node->renderer()) return true; - if (!node->canStartSelection()) - return false; - - return true; + return node->canStartSelection() || Position::nodeIsUserSelectAll(node); } bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event) { #if ENABLE(DRAG_SUPPORT) // Reset drag state. - dragState().m_dragSrc = 0; + dragState().source = 0; #endif cancelFakeMouseMoveEvent(); @@ -645,8 +662,8 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve bool singleClick = event.event().clickCount() <= 1; // If we got the event back, that must mean it wasn't prevented, - // so it's allowed to start a drag or selection. - m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode()); + // so it's allowed to start a drag or selection if it wasn't in a scrollbar. + m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode()) && !event.scrollbar(); #if ENABLE(DRAG_SUPPORT) // Careful that the drag starting logic stays in sync with eventMayStartDrag() @@ -662,10 +679,10 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve #if ENABLE(SVG) if (m_frame->document()->isSVGDocument() - && static_cast<SVGDocument*>(m_frame->document())->zoomAndPanEnabled()) { + && toSVGDocument(m_frame->document())->zoomAndPanEnabled()) { if (event.event().shiftKey() && singleClick) { m_svgPan = true; - static_cast<SVGDocument*>(m_frame->document())->startPan(m_frame->view()->windowToContents(event.event().position())); + toSVGDocument(m_frame->document())->startPan(m_frame->view()->windowToContents(event.event().position())); return true; } } @@ -700,46 +717,26 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve return swallowEvent; } -// There are two kinds of renderer that can autoscroll. -static bool canAutoscroll(RenderObject* renderer) -{ - if (!renderer->isBox()) - return false; - - // Check for a box that can be scrolled in its own right. - if (toRenderBox(renderer)->canBeScrolledAndHasScrollableArea()) - return true; - - // Check for a box that represents the top level of a web page. - // This can be scrolled by calling Chrome::scrollRectIntoView. - // This only has an effect on the Mac platform in applications - // that put web views into scrolling containers, such as Mac OS X Mail. - // The code for this is in RenderLayer::scrollRectToVisible. - if (renderer->node() != renderer->document()) - return false; - Frame* frame = renderer->frame(); - if (!frame) - return false; - Page* page = frame->page(); - return page && page->mainFrame() == frame; -} - #if ENABLE(DRAG_SUPPORT) bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event) { - if (handleDrag(event, ShouldCheckDragHysteresis)) - return true; - if (!m_mousePressed) return false; + if (handleDrag(event, ShouldCheckDragHysteresis)) + return true; + Node* targetNode = event.targetNode(); if (event.event().button() != LeftButton || !targetNode) return false; RenderObject* renderer = targetNode->renderer(); if (!renderer) { - renderer = targetNode->parentNode() ? targetNode->parentNode()->renderer() : 0; + Node* parent = EventPathWalker::parent(targetNode); + if (!parent) + return false; + + renderer = parent->renderer(); if (!renderer || !renderer->isListBox()) return false; } @@ -750,25 +747,13 @@ bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& e m_mouseDownMayStartDrag = false; - if (m_mouseDownMayStartAutoscroll && !m_panScrollInProgress) { - // Find a renderer that can autoscroll. - while (renderer && !canAutoscroll(renderer)) { - if (!renderer->parent() && renderer->node() == renderer->document() && renderer->document()->ownerElement()) - renderer = renderer->document()->ownerElement()->renderer(); - else - renderer = renderer->parent(); - } - - if (renderer) { - m_autoscrollInProgress = true; - handleAutoscroll(renderer); - } - + if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) { + m_autoscrollController->startAutoscrollForSelection(renderer); m_mouseDownMayStartAutoscroll = false; } if (m_selectionInitiationState != ExtendedSelection) { - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); HitTestResult result(m_mouseDownPos); m_frame->document()->renderView()->hitTest(request, result); @@ -799,11 +784,11 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const return false; updateDragSourceActionsAllowed(); - HitTestRequest request(HitTestRequest::ReadOnly); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); HitTestResult result(view->windowToContents(event.position())); m_frame->contentRenderer()->hitTest(request, result); DragState state; - return result.innerNode() && page->dragController()->draggableNode(m_frame, result.innerNode(), result.roundedPointInInnerNodeFrame(), state); + return result.innerElement() && page->dragController()->draggableElement(m_frame, result.innerElement(), result.roundedPointInInnerNodeFrame(), state); } void EventHandler::updateSelectionForMouseDrag() @@ -815,10 +800,8 @@ void EventHandler::updateSelectionForMouseDrag() if (!renderer) return; - HitTestRequest request(HitTestRequest::ReadOnly | - HitTestRequest::Active | - HitTestRequest::Move); - HitTestResult result(view->windowToContents(m_currentMousePosition)); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowShadowContent); + HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); renderer->hitTest(request, result); updateSelectionForMouseDrag(result); } @@ -931,7 +914,7 @@ bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event) bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event) { - if (m_autoscrollInProgress) + if (autoscrollInProgress()) stopAutoscrollTimer(); if (handleMouseUp(event)) @@ -939,7 +922,6 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e // Used to prevent mouseMoveEvent from initiating a drag before // the mouse is pressed again. - m_frame->selection()->setCaretBlinkingSuspended(false); m_mousePressed = false; m_capturesDragging = false; #if ENABLE(DRAG_SUPPORT) @@ -986,132 +968,46 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e return handled; } -void EventHandler::handleAutoscroll(RenderObject* renderer) -{ - // We don't want to trigger the autoscroll or the panScroll if it's already active - if (m_autoscrollTimer.isActive()) - return; - - setAutoscrollRenderer(renderer); - #if ENABLE(PAN_SCROLLING) - if (m_panScrollInProgress) { - m_panScrollStartPos = currentMousePosition(); - if (FrameView* view = m_frame->view()) - view->addPanScrollIcon(m_panScrollStartPos); - // If we're not in the top frame we notify it that we doing a panScroll. - if (Page* page = m_frame->page()) { - Frame* mainFrame = page->mainFrame(); - if (m_frame != mainFrame) - mainFrame->eventHandler()->m_panScrollInProgress = true; - } - } -#endif - - startAutoscrollTimer(); -} -void EventHandler::autoscrollTimerFired(Timer<EventHandler>*) +void EventHandler::didPanScrollStart() { - RenderObject* r = autoscrollRenderer(); - if (!r || !r->isBox()) { - stopAutoscrollTimer(); - return; - } - - if (m_autoscrollInProgress) { - if (!m_mousePressed) { - stopAutoscrollTimer(); - return; - } - toRenderBox(r)->autoscroll(); - } else { - // we verify that the main frame hasn't received the order to stop the panScroll - if (Page* page = m_frame->page()) { - if (!page->mainFrame()->eventHandler()->m_panScrollInProgress) { - stopAutoscrollTimer(); - return; - } - } -#if ENABLE(PAN_SCROLLING) - updatePanScrollState(); - toRenderBox(r)->panScroll(m_panScrollStartPos); -#endif - } + m_autoscrollController->didPanScrollStart(); } -#if ENABLE(PAN_SCROLLING) - -void EventHandler::startPanScrolling(RenderObject* renderer) +void EventHandler::didPanScrollStop() { - m_panScrollInProgress = true; - m_panScrollButtonPressed = true; - handleAutoscroll(renderer); - invalidateClick(); + m_autoscrollController->didPanScrollStop(); } -void EventHandler::updatePanScrollState() +void EventHandler::startPanScrolling(RenderObject* renderer) { - FrameView* view = m_frame->view(); - if (!view) + if (!renderer->isBox()) return; - - // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll - // So we don't want to change the cursor over this area - bool east = m_panScrollStartPos.x() < (m_currentMousePosition.x() - ScrollView::noPanScrollRadius); - bool west = m_panScrollStartPos.x() > (m_currentMousePosition.x() + ScrollView::noPanScrollRadius); - bool north = m_panScrollStartPos.y() > (m_currentMousePosition.y() + ScrollView::noPanScrollRadius); - bool south = m_panScrollStartPos.y() < (m_currentMousePosition.y() - ScrollView::noPanScrollRadius); - - if ((east || west || north || south) && m_panScrollButtonPressed) - m_springLoadedPanScrollInProgress = true; - - if (north) { - if (east) - view->setCursor(northEastPanningCursor()); - else if (west) - view->setCursor(northWestPanningCursor()); - else - view->setCursor(northPanningCursor()); - } else if (south) { - if (east) - view->setCursor(southEastPanningCursor()); - else if (west) - view->setCursor(southWestPanningCursor()); - else - view->setCursor(southPanningCursor()); - } else if (east) - view->setCursor(eastPanningCursor()); - else if (west) - view->setCursor(westPanningCursor()); - else - view->setCursor(middlePanningCursor()); + m_autoscrollController->startPanScrolling(toRenderBox(renderer), lastKnownMousePosition()); + invalidateClick(); } #endif // ENABLE(PAN_SCROLLING) RenderObject* EventHandler::autoscrollRenderer() const { - return m_autoscrollRenderer; + return m_autoscrollController->autoscrollRenderer(); } void EventHandler::updateAutoscrollRenderer() { - if (!m_autoscrollRenderer) - return; - - HitTestResult hitTest = hitTestResultAtPoint(m_panScrollStartPos, true); - - if (Node* nodeAtPoint = hitTest.innerNode()) - m_autoscrollRenderer = nodeAtPoint->renderer(); + m_autoscrollController->updateAutoscrollRenderer(); +} - while (m_autoscrollRenderer && !canAutoscroll(m_autoscrollRenderer)) - m_autoscrollRenderer = m_autoscrollRenderer->parent(); +bool EventHandler::autoscrollInProgress() const +{ + return m_autoscrollController->autoscrollInProgress(); } -void EventHandler::setAutoscrollRenderer(RenderObject* renderer) +bool EventHandler::panScrollInProgress() const { - m_autoscrollRenderer = renderer; + return m_autoscrollController->panScrollInProgress(); } #if ENABLE(DRAG_SUPPORT) @@ -1132,7 +1028,7 @@ DragSourceAction EventHandler::updateDragSourceActionsAllowed() const } #endif // ENABLE(DRAG_SUPPORT) -HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, bool allowShadowContent, bool ignoreClipping, HitTestScrollbars testScrollbars, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) +HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) { // We always send hitTestResultAtPoint to the main frame if we have one, // otherwise we might hit areas that are obscured by higher frames. @@ -1143,7 +1039,7 @@ HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, bool FrameView* mainView = mainFrame->view(); if (frameView && mainView) { IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point))); - return mainFrame->eventHandler()->hitTestResultAtPoint(mainFramePoint, allowShadowContent, ignoreClipping, testScrollbars, hitType, padding); + return mainFrame->eventHandler()->hitTestResultAtPoint(mainFramePoint, hitType, padding); } } } @@ -1152,89 +1048,22 @@ HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, bool if (!m_frame->contentRenderer()) return result; - if (ignoreClipping) - hitType |= HitTestRequest::IgnoreClipping; - if (allowShadowContent) - hitType |= HitTestRequest::AllowShadowContent; - HitTestRequest request(hitType); - m_frame->contentRenderer()->hitTest(request, result); - while (true) { - Node* n = result.innerNode(); - if (!result.isOverWidget() || !n || !n->renderer() || !n->renderer()->isWidget()) - break; - RenderWidget* renderWidget = toRenderWidget(n->renderer()); - Widget* widget = renderWidget->widget(); - if (!widget || !widget->isFrameView()) - break; - Frame* frame = static_cast<HTMLFrameElementBase*>(n)->contentFrame(); - if (!frame || !frame->contentRenderer()) - break; - FrameView* view = static_cast<FrameView*>(widget); - LayoutPoint widgetPoint(result.localPoint().x() + view->scrollX() - renderWidget->borderLeft() - renderWidget->paddingLeft(), - result.localPoint().y() + view->scrollY() - renderWidget->borderTop() - renderWidget->paddingTop()); - HitTestResult widgetHitTestResult(widgetPoint, padding.height(), padding.width(), padding.height(), padding.width()); - widgetHitTestResult.setPointInMainFrame(result.pointInMainFrame()); - frame->contentRenderer()->hitTest(request, widgetHitTestResult); - result = widgetHitTestResult; - - if (testScrollbars == ShouldHitTestScrollbars) { - Scrollbar* eventScrollbar = view->scrollbarAtPoint(roundedIntPoint(point)); - if (eventScrollbar) - result.setScrollbar(eventScrollbar); - } - } + // 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); + if (!request.readOnly()) + m_frame->document()->updateHoverActiveState(request, result.innerElement()); - if (!allowShadowContent) + if (request.disallowsShadowContent()) result.setToNonShadowAncestor(); return result; } - -void EventHandler::startAutoscrollTimer() -{ - m_autoscrollTimer.startRepeating(autoscrollInterval); -} - void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed) { - if (m_autoscrollInProgress) { - if (m_mouseDownWasInSubframe) { - if (Frame* subframe = subframeForTargetNode(m_mousePressNode.get())) - subframe->eventHandler()->stopAutoscrollTimer(rendererIsBeingDestroyed); - return; - } - } - - if (autoscrollRenderer()) { - if (!rendererIsBeingDestroyed && (m_autoscrollInProgress || m_panScrollInProgress)) - toRenderBox(autoscrollRenderer())->stopAutoscroll(); -#if ENABLE(PAN_SCROLLING) - if (m_panScrollInProgress) { - if (FrameView* view = m_frame->view()) { - view->removePanScrollIcon(); - view->setCursor(pointerCursor()); - } - } -#endif - - setAutoscrollRenderer(0); - } - - m_autoscrollTimer.stop(); - - m_panScrollInProgress = false; - m_springLoadedPanScrollInProgress = false; - - // If we're not in the top frame we notify it that we are not doing a panScroll any more. - if (Page* page = m_frame->page()) { - Frame* mainFrame = page->mainFrame(); - if (m_frame != mainFrame) - mainFrame->eventHandler()->m_panScrollInProgress = false; - } - - m_autoscrollInProgress = false; + m_autoscrollController->stopAutoscrollTimer(rendererIsBeingDestroyed); } Node* EventHandler::mousePressNode() const @@ -1252,7 +1081,7 @@ bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity g Node* node = startingNode; if (!node) - node = m_frame->document()->focusedNode(); + node = m_frame->document()->focusedElement(); if (!node) node = m_mousePressNode.get(); @@ -1273,7 +1102,7 @@ bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, Scrol Node* node = startingNode; if (!node) - node = m_frame->document()->focusedNode(); + node = m_frame->document()->focusedElement(); if (!node) node = m_mousePressNode.get(); @@ -1335,9 +1164,9 @@ bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, Sc return frame->eventHandler()->logicalScrollRecursively(direction, granularity, m_frame->ownerElement()); } -IntPoint EventHandler::currentMousePosition() const +IntPoint EventHandler::lastKnownMousePosition() const { - return m_currentMousePosition; + return m_lastKnownMousePosition; } Frame* EventHandler::subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult) @@ -1360,12 +1189,12 @@ Frame* EventHandler::subframeForTargetNode(Node* node) if (!widget || !widget->isFrameView()) return 0; - return static_cast<FrameView*>(widget)->frame(); + return toFrameView(widget)->frame(); } static bool isSubmitImage(Node* node) { - return node && node->hasTagName(inputTag) && static_cast<HTMLInputElement*>(node)->isImageButton(); + return node && isHTMLInputElement(node) && toHTMLInputElement(node)->isImageButton(); } // Returns true if the node's editable block is not current focused for editing @@ -1409,7 +1238,50 @@ bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey) return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled)); } -OptionalCursor EventHandler::selectCursor(const MouseEventWithHitTestResults& event, Scrollbar* scrollbar) +void EventHandler::cursorUpdateTimerFired(Timer<EventHandler>*) +{ + ASSERT(m_frame); + ASSERT(m_frame->document()); + + updateCursor(); +} + +void EventHandler::updateCursor() +{ + if (m_mousePositionIsUnknown) + return; + + FrameView* view = m_frame->view(); + if (!view) + return; + + RenderView* renderView = view->renderView(); + if (!renderView) + return; + + if (!view->shouldSetCursor()) + return; + + bool shiftKey; + bool ctrlKey; + bool altKey; + bool metaKey; + PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey); + + m_frame->document()->updateLayout(); + + HitTestRequest request(HitTestRequest::ReadOnly); + HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); + renderView->hitTest(request, result); + + OptionalCursor optionalCursor = selectCursor(result, shiftKey); + if (optionalCursor.isCursorChange()) { + m_currentMouseCursor = optionalCursor.cursor(); + view->setCursor(m_currentMouseCursor); + } +} + +OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shiftKey) { if (m_resizeLayer && m_resizeLayer->inResizeMode()) return NoCursorChange; @@ -1417,15 +1289,28 @@ OptionalCursor EventHandler::selectCursor(const MouseEventWithHitTestResults& ev Page* page = m_frame->page(); if (!page) return NoCursorChange; - if (page->mainFrame()->eventHandler()->m_panScrollInProgress) +#if ENABLE(PAN_SCROLLING) + if (page->mainFrame()->eventHandler()->panScrollInProgress()) + return NoCursorChange; +#endif + + Node* node = result.targetNode(); + if (!node) return NoCursorChange; - Node* node = event.targetNode(); - RenderObject* renderer = node ? node->renderer() : 0; + RenderObject* renderer = node->renderer(); RenderStyle* style = renderer ? renderer->style() : 0; 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); + startAutoHideCursorTimer(); + } 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 @@ -1437,7 +1322,7 @@ OptionalCursor EventHandler::selectCursor(const MouseEventWithHitTestResults& ev if (renderer) { Cursor overrideCursor; - switch (renderer->getCursor(roundedIntPoint(event.localPoint()), overrideCursor)) { + switch (renderer->getCursor(roundedIntPoint(result.localPoint()), overrideCursor)) { case SetCursorBasedOnStyle: break; case SetCursor: @@ -1450,37 +1335,53 @@ OptionalCursor EventHandler::selectCursor(const MouseEventWithHitTestResults& ev if (style && style->cursors()) { const CursorList* cursors = style->cursors(); for (unsigned i = 0; i < cursors->size(); ++i) { - CachedImage* cimage = 0; - StyleImage* image = (*cursors)[i].image(); - if (image && image->isCachedImage()) - cimage = static_cast<StyleCachedImage*>(image)->cachedImage(); - if (!cimage) + StyleImage* styleImage = (*cursors)[i].image(); + if (!styleImage) continue; + CachedImage* cachedImage = styleImage->cachedImage(); + if (!cachedImage) + continue; + float scale = styleImage->imageScaleFactor(); + // Get hotspot and convert from logical pixels to physical pixels. IntPoint hotSpot = (*cursors)[i].hotSpot(); - // Limit the size of cursors so that they cannot be used to cover UI elements in chrome. - IntSize size = cimage->imageForRenderer(renderer)->size(); - if (size.width() > 128 || size.height() > 128) + hotSpot.scale(scale, scale); + IntSize size = cachedImage->imageForRenderer(renderer)->size(); + if (cachedImage->errorOccurred()) + continue; + // Limit the size of cursors (in UI pixels) so that they cannot be + // used to cover UI elements in chrome. + size.scale(1 / scale); + if (size.width() > maximumCursorSize || size.height() > maximumCursorSize) + continue; + + Image* image = cachedImage->imageForRenderer(renderer); +#if ENABLE(MOUSE_CURSOR_SCALE) + // Ensure no overflow possible in calculations above. + if (scale < minimumCursorScale) continue; - if (!cimage->errorOccurred()) - return Cursor(cimage->imageForRenderer(renderer), hotSpot); + return Cursor(image, hotSpot, scale); +#else + ASSERT(scale == 1); + return Cursor(image, hotSpot); +#endif // ENABLE(MOUSE_CURSOR_SCALE) } } switch (style ? style->cursor() : CURSOR_AUTO) { case CURSOR_AUTO: { - bool editable = (node && node->rendererIsEditable()); + bool editable = node->rendererIsEditable(); - if (useHandCursor(node, event.isOverLink(), event.event().shiftKey())) + if (useHandCursor(node, result.isOverLink(), shiftKey)) return handCursor(); bool inResizer = false; if (renderer) { if (RenderLayer* layer = renderer->enclosingLayer()) { if (FrameView* view = m_frame->view()) - inResizer = layer->isPointInResizeControl(view->windowToContents(event.event().position())); + inResizer = layer->isPointInResizeControl(view->windowToContents(roundedIntPoint(result.localPoint()))); } } - if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !scrollbar) + if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar()) return iBeam; return pointerCursor(); } @@ -1558,6 +1459,37 @@ OptionalCursor EventHandler::selectCursor(const MouseEventWithHitTestResults& ev return pointerCursor(); } +#if ENABLE(CURSOR_VISIBILITY) +void EventHandler::startAutoHideCursorTimer() +{ + Page* page = m_frame->page(); + if (!page) + return; + + m_autoHideCursorTimer.startOneShot(page->settings()->timeWithoutMouseMovementBeforeHidingControls()); + + // 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(); +} + +void EventHandler::cancelAutoHideCursorTimer() +{ + if (m_autoHideCursorTimer.isActive()) + m_autoHideCursorTimer.stop(); +} + +void EventHandler::autoHideCursorTimerFired(Timer<EventHandler>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_autoHideCursorTimer); + m_currentMouseCursor = noneCursor(); + FrameView* view = m_frame->view(); + if (view && view->isActive()) + view->setCursor(m_currentMouseCursor); +} +#endif + static LayoutPoint documentPointForWindowPoint(Frame* frame, const IntPoint& windowPoint) { FrameView* view = frame->view(); @@ -1589,8 +1521,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) cancelFakeMouseMoveEvent(); m_mousePressed = true; m_capturesDragging = true; - m_currentMousePosition = mouseEvent.position(); - m_currentMouseGlobalPosition = mouseEvent.globalPosition(); + setLastKnownMousePosition(mouseEvent); m_mouseDownTimestamp = mouseEvent.timestamp(); #if ENABLE(DRAG_SUPPORT) m_mouseDownMayStartDrag = false; @@ -1605,8 +1536,8 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) } m_mouseDownWasInSubframe = false; - HitTestRequest request(HitTestRequest::Active); - // Save the document point we generate in case the window coordinate is invalidated by what happens + 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); @@ -1633,10 +1564,9 @@ 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_panScrollInProgress to false on return. - bool isPanScrollInProgress = m_frame->page() && m_frame->page()->mainFrame()->eventHandler()->m_panScrollInProgress; - if (isPanScrollInProgress || m_autoscrollInProgress) - stopAutoscrollTimer(); + // because it will set m_autoscrollType to NoAutoscroll on return. + bool isPanScrollInProgress = m_frame->page() && m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress(); + stopAutoscrollTimer(); if (isPanScrollInProgress) { // We invalidate the click when exiting pan scrolling so that we don't inadvertently navigate // away from the current page (e.g. the click was on a hyperlink). See <rdar://problem/6095023>. @@ -1669,7 +1599,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) // 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 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; @@ -1688,8 +1618,8 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) // 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() && toShadowRoot(mev.targetNode())->host()->hasTagName(inputTag)) { - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); + 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); } @@ -1714,14 +1644,15 @@ bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& mouseEv { RefPtr<FrameView> protector(m_frame->view()); + m_frame->selection()->setCaretBlinkingSuspended(false); + UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); // We get this instead of a second mouse-up m_mousePressed = false; - m_currentMousePosition = mouseEvent.position(); - m_currentMouseGlobalPosition = mouseEvent.globalPosition(); + setLastKnownMousePosition(mouseEvent); - HitTestRequest request(HitTestRequest::Active); + HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent); Frame* subframe = subframeForHitTestResult(mev); if (m_eventHandlerWillResetCapturingMouseEventsNode) @@ -1763,15 +1694,7 @@ static RenderLayer* layerForNode(Node* node) bool EventHandler::mouseMoved(const PlatformMouseEvent& event) { RefPtr<FrameView> protector(m_frame->view()); - RunningAverageDurationTracker durationTracker(&m_mouseMovedDurationRunningAverage, fakeMouseMoveRunningAverageCount); - - -#if ENABLE(TOUCH_EVENTS) - // FIXME: this should be moved elsewhere to also be able to dispatch touchcancel events. - bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(event); - if (defaultPrevented) - return true; -#endif + MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration); HitTestResult hoveredNode = HitTestResult(LayoutPoint()); bool result = handleMouseMoveEvent(event, &hoveredNode); @@ -1791,8 +1714,8 @@ bool EventHandler::mouseMoved(const PlatformMouseEvent& event) frameView->mouseMovedInContentArea(); hoveredNode.setToNonShadowAncestor(); - page->chrome()->mouseDidMoveOverElement(hoveredNode, event.modifierFlags()); - page->chrome()->setToolTip(hoveredNode); + page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags()); + page->chrome().setToolTip(hoveredNode); return result; } @@ -1812,18 +1735,26 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (!m_frame) return false; +#if ENABLE(TOUCH_EVENTS) + bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent); + if (defaultPrevented) + return true; +#endif + RefPtr<FrameView> protector(m_frame->view()); - m_currentMousePosition = mouseEvent.position(); - m_currentMouseGlobalPosition = mouseEvent.globalPosition(); + + setLastKnownMousePosition(mouseEvent); if (m_hoverTimer.isActive()) m_hoverTimer.stop(); + m_cursorUpdateTimer.stop(); + cancelFakeMouseMoveEvent(); #if ENABLE(SVG) if (m_svgPan) { - static_cast<SVGDocument*>(m_frame->document())->updatePan(m_frame->view()->windowToContents(m_currentMousePosition)); + toSVGDocument(m_frame->document())->updatePan(m_frame->view()->windowToContents(m_lastKnownMousePosition)); return true; } #endif @@ -1835,7 +1766,7 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (m_lastScrollbarUnderMouse && m_mousePressed) return m_lastScrollbarUnderMouse->mouseMoved(mouseEvent); - HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move; + HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowShadowContent | HitTestRequest::AllowFrameScrollbars; if (m_mousePressed) hitType |= HitTestRequest::Active; else if (onlyUpdateScrollbars) { @@ -1856,18 +1787,13 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (hoveredNode) *hoveredNode = mev.hitTestResult(); - Scrollbar* scrollbar = 0; - if (m_resizeLayer && m_resizeLayer->inResizeMode()) m_resizeLayer->resize(mouseEvent, m_offsetFromResizeCorner); else { - if (FrameView* view = m_frame->view()) - scrollbar = view->scrollbarAtPoint(mouseEvent.position()); - - if (!scrollbar) - scrollbar = mev.scrollbar(); - + Scrollbar* scrollbar = mev.scrollbar(); updateLastScrollbarUnderMouse(scrollbar, !m_mousePressed); + if (!m_mousePressed && scrollbar) + scrollbar->mouseMoved(mouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. if (onlyUpdateScrollbars) return true; } @@ -1888,10 +1814,8 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (newSubframe->view()) swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode); } else { - if (scrollbar && !m_mousePressed) - scrollbar->mouseMoved(mouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. if (FrameView* view = m_frame->view()) { - OptionalCursor optionalCursor = selectCursor(mev, scrollbar); + OptionalCursor optionalCursor = selectCursor(mev.hitTestResult(), mouseEvent.shiftKey()); if (optionalCursor.isCursorChange()) { m_currentMouseCursor = optionalCursor.cursor(); view->setCursor(m_currentMouseCursor); @@ -1919,10 +1843,36 @@ void EventHandler::invalidateClick() m_clickNode = 0; } +inline static bool mouseIsReleasedOnPressedElement(Node* targetNode, Node* clickNode) +{ + 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; + + Node* adjustedTargetNode = targetNode->shadowHost(); + Node* adjustedClickNode = clickNode ? clickNode->shadowHost() : 0; + return adjustedTargetNode == adjustedClickNode; +} + bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) { RefPtr<FrameView> protector(m_frame->view()); + m_frame->selection()->setCaretBlinkingSuspended(false); + #if ENABLE(TOUCH_EVENTS) bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent); if (defaultPrevented) @@ -1932,20 +1882,16 @@ bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); #if ENABLE(PAN_SCROLLING) - if (mouseEvent.button() == MiddleButton) - m_panScrollButtonPressed = false; - if (m_springLoadedPanScrollInProgress) - stopAutoscrollTimer(); + m_autoscrollController->handleMouseReleaseEvent(mouseEvent); #endif m_mousePressed = false; - m_currentMousePosition = mouseEvent.position(); - m_currentMouseGlobalPosition = mouseEvent.globalPosition(); + setLastKnownMousePosition(mouseEvent); #if ENABLE(SVG) if (m_svgPan) { m_svgPan = false; - static_cast<SVGDocument*>(m_frame->document())->updatePan(m_frame->view()->windowToContents(m_currentMousePosition)); + toSVGDocument(m_frame->document())->updatePan(m_frame->view()->windowToContents(m_lastKnownMousePosition)); return true; } #endif @@ -1955,10 +1901,13 @@ bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) if (m_lastScrollbarUnderMouse) { invalidateClick(); - return m_lastScrollbarUnderMouse->mouseUp(mouseEvent); + m_lastScrollbarUnderMouse->mouseUp(mouseEvent); + bool cancelable = true; + bool setUnder = false; + return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastNodeUnderMouse.get(), cancelable, m_clickCount, mouseEvent, setUnder); } - HitTestRequest request(HitTestRequest::Release); + 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) @@ -1968,12 +1917,9 @@ bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); - Node* clickTarget = mev.targetNode(); - if (clickTarget) - clickTarget = clickTarget->shadowAncestorNode(); - Node* adjustedClickNode = m_clickNode ? m_clickNode->shadowAncestorNode() : 0; + bool contextMenuEvent = mouseEvent.button() == RightButton; - bool swallowClickEvent = m_clickCount > 0 && mouseEvent.button() != RightButton && clickTarget == adjustedClickNode && !dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && mouseIsReleasedOnPressedElement(mev.targetNode(), m_clickNode.get()) && !dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); if (m_resizeLayer) { m_resizeLayer->setInResizeMode(false); @@ -2015,10 +1961,12 @@ bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEve return false; #endif + if (!m_frame->page()) + return false; 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; } @@ -2035,7 +1983,7 @@ bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTa view->resetDeferredRepaintDelay(); RefPtr<MouseEvent> me = MouseEvent::create(eventType, - true, true, m_frame->document()->defaultView(), + 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(), @@ -2043,8 +1991,7 @@ bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTa event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), 0, 0, clipboard); - ExceptionCode ec; - dragTarget->dispatchEvent(me.get(), ec); + dragTarget->dispatchEvent(me.get(), IGNORE_EXCEPTION); return me->defaultPrevented(); } @@ -2056,7 +2003,7 @@ static bool targetIsFrame(Node* target, Frame*& frame) if (!target->hasTagName(frameTag) && !target->hasTagName(iframeTag)) return false; - frame = static_cast<HTMLFrameElementBase*>(target)->contentFrame(); + frame = toHTMLFrameElementBase(target)->contentFrame(); return true; } @@ -2104,13 +2051,15 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* if (!m_frame->view()) return false; - HitTestRequest request(HitTestRequest::ReadOnly); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); MouseEventWithHitTestResults mev = 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 = newTarget->parentNode(); + newTarget = EventPathWalker::parent(newTarget.get()); + + m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp()); if (m_dragTarget != newTarget) { // FIXME: this ordering was explicitly chosen to match WinIE. However, @@ -2124,7 +2073,7 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* accept = targetFrame->eventHandler()->updateDragAndDrop(event, clipboard); } 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().m_dragSrc && dragState().shouldDispatchEvents()) { + 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); } @@ -2151,7 +2100,7 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* accept = targetFrame->eventHandler()->updateDragAndDrop(event, clipboard); } 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().m_dragSrc && dragState().shouldDispatchEvents()) { + 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); } @@ -2173,7 +2122,7 @@ void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, Clipboard* if (targetFrame) targetFrame->eventHandler()->cancelDragAndDrop(event, clipboard); } else if (m_dragTarget.get()) { - if (dragState().m_dragSrc && dragState().shouldDispatchEvents()) + if (dragState().source && dragState().shouldDispatchEvents) dispatchDragSrcEvent(eventNames().dragEvent, event); dispatchDragEvent(eventNames().dragleaveEvent, m_dragTarget.get(), event, clipboard); } @@ -2195,6 +2144,7 @@ bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, Clipboard void EventHandler::clearDragState() { + stopAutoscrollTimer(); m_dragTarget = 0; m_capturingMouseEventsNode = 0; m_shouldOnlyFireDragOverEvent = false; @@ -2224,7 +2174,7 @@ static inline SVGElementInstance* instanceAssociatedWithShadowTreeElement(Node* if (!referenceNode || !referenceNode->isSVGElement()) return 0; - ShadowRoot* shadowRoot = referenceNode->shadowRoot(); + ShadowRoot* shadowRoot = referenceNode->containingShadowRoot(); if (!shadowRoot) return 0; @@ -2232,7 +2182,7 @@ static inline SVGElementInstance* instanceAssociatedWithShadowTreeElement(Node* if (!shadowTreeParentElement || !shadowTreeParentElement->hasTagName(useTag)) return 0; - return static_cast<SVGUseElement*>(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode); + return toSVGUseElement(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode); } #endif @@ -2245,11 +2195,8 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo result = m_capturingMouseEventsNode.get(); else { // If the target node is a text node, dispatch on the parent node - rdar://4196646 - if (result && result->isTextNode()) { - AncestorChainWalker walker(result); - walker.parent(); - result = walker.get(); - } + if (result && result->isTextNode()) + result = EventPathWalker::parent(result); } m_nodeUnderMouse = result; #if ENABLE(SVG) @@ -2374,35 +2321,42 @@ bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe // 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. - Node* node = m_nodeUnderMouse.get(); - // Walk up the DOM tree to search for a node to focus. - while (node) { - if (node->isMouseFocusable()) { + Element* element; + if (m_nodeUnderMouse) + element = m_nodeUnderMouse->isElementNode() ? toElement(m_nodeUnderMouse.get()) : m_nodeUnderMouse->parentOrShadowHostElement(); + else + element = 0; + + // Walk up the DOM tree to search for an element to focus. + while (element) { + if (element->isMouseFocusable()) { // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus a // node on mouse down if it's selected and inside a focused node. It will be // focused if the user does a mouseup over it, however, because the mouseup // will set a selection inside it, which will call setFocuseNodeIfNeeded. - ExceptionCode ec = 0; - Node* n = node->isShadowRoot() ? toShadowRoot(node)->host() : node; if (m_frame->selection()->isRange() - && m_frame->selection()->toNormalizedRange()->compareNode(n, ec) == Range::NODE_INSIDE - && n->isDescendantOf(m_frame->document()->focusedNode())) + && m_frame->selection()->toNormalizedRange()->compareNode(element, IGNORE_EXCEPTION) == Range::NODE_INSIDE + && element->isDescendantOf(m_frame->document()->focusedElement())) return true; break; } - node = node->parentOrHostNode(); + element = element->parentOrShadowHostElement(); } + // Only change the focus when clicking scrollbars if it can transfered to a mouse focusable node. + if ((!element || !element->isMouseFocusable()) && isInsideScrollbar(mouseEvent.position())) + return false; + // 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 (node && node->isMouseFocusable()) { - if (!page->focusController()->setFocusedNode(node, m_frame)) + if (element && element->isMouseFocusable()) { + if (!page->focusController()->setFocusedElement(element, m_frame)) swallowEvent = true; - } else if (!node || !node->focused()) { - if (!page->focusController()->setFocusedNode(0, m_frame)) + } else if (!element || !element->focused()) { + if (!page->focusController()->setFocusedElement(0, m_frame)) swallowEvent = true; } } @@ -2411,7 +2365,19 @@ bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe return !swallowEvent; } -#if !PLATFORM(GTK) && !(PLATFORM(CHROMIUM) && (OS(UNIX) && !OS(DARWIN))) +bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const +{ + if (RenderView* renderView = m_frame->contentRenderer()) { + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); + HitTestResult result(windowPoint); + renderView->hitTest(request, result); + return result.scrollbar(); + } + + return false; +} + +#if !PLATFORM(GTK) bool EventHandler::shouldTurnVerticalTicksIntoHorizontal(const HitTestResult&, const PlatformWheelEvent&) const { return false; @@ -2431,25 +2397,31 @@ bool EventHandler::handleWheelEvent(const PlatformWheelEvent& e) FrameView* view = m_frame->view(); if (!view) return false; + + m_isHandlingWheelEvent = true; setFrameWasScrolledByUser(); LayoutPoint vPoint = view->windowToContents(e.position()); - Node* node; - bool isOverWidget; - - HitTestRequest request(HitTestRequest::ReadOnly); + 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 = closestScrollableNodeInDocumentIfPossible(result.innerNode()); + m_latchedWheelEventNode = node; m_widgetIsLatched = result.isOverWidget(); - } + } else + node = m_latchedWheelEventNode.get(); - node = m_latchedWheelEventNode.get(); isOverWidget = m_widgetIsLatched; } else { if (m_latchedWheelEventNode) @@ -2457,7 +2429,6 @@ bool EventHandler::handleWheelEvent(const PlatformWheelEvent& e) if (m_previousWheelScrolledNode) m_previousWheelScrolledNode = 0; - node = result.innerNode(); isOverWidget = result.isOverWidget(); } @@ -2474,21 +2445,24 @@ bool EventHandler::handleWheelEvent(const PlatformWheelEvent& e) if (isOverWidget && target && target->isWidget()) { Widget* widget = toRenderWidget(target)->widget(); - if (widget && passWheelEventToWidget(e, widget)) + if (widget && passWheelEventToWidget(e, widget)) { + m_isHandlingWheelEvent = false; return true; + } } - if (node && !node->dispatchWheelEvent(event)) + 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(); - if (!view) - return false; - - return view->wheelEvent(event); + bool didHandleEvent = view ? view->wheelEvent(event) : false; + m_isHandlingWheelEvent = false; + return didHandleEvent; } void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent) @@ -2497,7 +2471,7 @@ void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEv return; Node* stopNode = m_previousWheelScrolledNode.get(); - ScrollGranularity granularity = wheelGranularityToScrollGranularity(wheelEvent->granularity()); + 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). @@ -2533,14 +2507,11 @@ bool EventHandler::handleGestureTapDown() bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent) { - // We don't use DoubleTap at the moment, it's mostly redundant with tap since tap now contains - // a tap count. FIXME: We should probably remove GestureDoubleTap (http://wkb.ug/93045). - if (gestureEvent.type() == PlatformEvent::GestureDoubleTap) - return false; - Node* eventTarget = 0; Scrollbar* scrollbar = 0; - if (gestureEvent.type() == PlatformEvent::GestureScrollEnd || gestureEvent.type() == PlatformEvent::GestureScrollUpdate) { + if (gestureEvent.type() == PlatformEvent::GestureScrollEnd + || gestureEvent.type() == PlatformEvent::GestureScrollUpdate + || gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation) { scrollbar = m_scrollbarHandlingScrollGesture.get(); eventTarget = m_scrollGestureHandlingNode.get(); } @@ -2552,8 +2523,13 @@ bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent) adjustGesturePosition(gestureEvent, adjustedPoint); #endif hitType |= HitTestRequest::Active; - } else if (gestureEvent.type() == PlatformEvent::GestureTap || gestureEvent.type() == PlatformEvent::GestureTapDownCancel) + } 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; @@ -2562,12 +2538,8 @@ bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent) if ((!scrollbar && !eventTarget) || !(hitType & HitTestRequest::ReadOnly)) { IntPoint hitTestPoint = m_frame->view()->windowToContents(adjustedPoint); - HitTestResult result = hitTestResultAtPoint(hitTestPoint, false, false, ShouldHitTestScrollbars, hitType); + HitTestResult result = hitTestResultAtPoint(hitTestPoint, hitType | HitTestRequest::AllowFrameScrollbars); eventTarget = result.targetNode(); - if (!scrollbar) { - FrameView* view = m_frame->view(); - scrollbar = view ? view->scrollbarAtPoint(gestureEvent.position()) : 0; - } if (!scrollbar) scrollbar = result.scrollbar(); } @@ -2585,12 +2557,9 @@ bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent) if (eventTarget) { bool eventSwallowed = eventTarget->dispatchGestureEvent(gestureEvent); - - if (gestureEvent.type() == PlatformEvent::GestureScrollBegin) { + if (gestureEvent.type() == PlatformEvent::GestureScrollBegin || gestureEvent.type() == PlatformEvent::GestureScrollEnd) { if (eventSwallowed) m_scrollGestureHandlingNode = eventTarget; - else - m_scrollGestureHandlingNode = 0; } if (eventSwallowed) @@ -2603,9 +2572,13 @@ bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent) switch (gestureEvent.type()) { case PlatformEvent::GestureScrollBegin: - return handleGestureScrollCore(gestureEvent, ScrollByPixelWheelEvent, false); + 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: @@ -2616,11 +2589,6 @@ bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent) return handleGestureLongTap(gestureEvent); case PlatformEvent::GestureTwoFingerTap: return handleGestureTwoFingerTap(gestureEvent); - case PlatformEvent::GestureScrollEnd: - case PlatformEvent::GestureDoubleTap: - case PlatformEvent::GesturePinchBegin: - case PlatformEvent::GesturePinchEnd: - case PlatformEvent::GesturePinchUpdate: case PlatformEvent::GestureTapDownCancel: break; default: @@ -2674,9 +2642,14 @@ bool EventHandler::handleGestureLongPress(const PlatformGestureEvent& gestureEve 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 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; @@ -2687,25 +2660,15 @@ bool EventHandler::handleGestureLongPress(const PlatformGestureEvent& gestureEve bool EventHandler::handleGestureLongTap(const PlatformGestureEvent& gestureEvent) { -#if ENABLE(CONTEXT_MENUS) && !OS(ANDROID) +#if ENABLE(CONTEXT_MENUS) if (!m_didLongPressInvokeContextMenu) return sendContextMenuEventForGesture(gestureEvent); -#endif +#endif // ENABLE(CONTEXT_MENUS) return false; } bool EventHandler::handleGestureForTextSelectionOrContextMenu(const PlatformGestureEvent& gestureEvent) { -#if OS(ANDROID) - IntPoint hitTestPoint = m_frame->view()->windowToContents(gestureEvent.position()); - HitTestResult result = hitTestResultAtPoint(hitTestPoint, true); - Node* innerNode = result.targetNode(); - if (!result.isLiveLink() && innerNode && (innerNode->isContentEditable() || innerNode->isTextNode())) { - selectClosestWordFromHitTestResult(result, DontAppendTrailingWhitespace); - if (m_frame->selection()->isRange()) - return true; - } -#endif #if ENABLE(CONTEXT_MENUS) m_didLongPressInvokeContextMenu = (gestureEvent.type() == PlatformEvent::GestureLongPress); return sendContextMenuEventForGesture(gestureEvent); @@ -2719,30 +2682,136 @@ bool EventHandler::handleGestureTwoFingerTap(const PlatformGestureEvent& gesture return handleGestureForTextSelectionOrContextMenu(gestureEvent); } -bool EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gestureEvent) +bool EventHandler::passGestureEventToWidget(const PlatformGestureEvent& gestureEvent, Widget* widget) { - return handleGestureScrollCore(gestureEvent, ScrollByPixelWheelEvent, true); + if (!widget) + return false; + + if (!widget->isFrameView()) + return false; + + return toFrameView(widget)->frame()->eventHandler()->handleGestureEvent(gestureEvent); } -bool EventHandler::isScrollbarHandlingGestures() const +bool EventHandler::passGestureEventToWidgetIfPossible(const PlatformGestureEvent& gestureEvent, RenderObject* renderer) { - return m_scrollbarHandlingScrollGesture.get(); + 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(); + 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; + } + + // Otherwise try to scroll the view. + return sendScrollEventToView(gestureEvent, delta); } -bool EventHandler::handleGestureScrollCore(const PlatformGestureEvent& gestureEvent, PlatformWheelEventGranularity granularity, bool latchedWheel) +bool EventHandler::sendScrollEventToView(const PlatformGestureEvent& gestureEvent, const FloatSize& scaledDelta) { - const float tickDivisor = (float)WheelEvent::tickMultiplier; + FrameView* view = m_frame->view(); + if (!view) + return false; + + 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, - gestureEvent.deltaX(), gestureEvent.deltaY(), gestureEvent.deltaX() / tickDivisor, gestureEvent.deltaY() / tickDivisor, - granularity, + scaledDelta.width(), scaledDelta.height(), + scaledDelta.width() / tickDivisor, scaledDelta.height() / tickDivisor, + ScrollByPixelWheelEvent, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey()); - syntheticWheelEvent.setUseLatchedEventNode(latchedWheel); - return handleWheelEvent(syntheticWheelEvent); -} +#if PLATFORM(MAC) + syntheticWheelEvent.setHasPreciseScrollingDeltas(true); #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 { @@ -2754,43 +2823,37 @@ bool EventHandler::shouldApplyTouchAdjustment(const PlatformGestureEvent& event) bool EventHandler::bestClickableNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode) { - HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active; IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter); - HitTestResult result = hitTestResultAtPoint(hitTestPoint, /*allowShadowContent*/ true, /*ignoreClipping*/ false, DontHitTestScrollbars, hitType, touchRadius); + HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius); IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius); - RefPtr<StaticHashSetNodeList> nodeList = StaticHashSetNodeList::adopt(result.rectBasedTestResult()); // 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, *nodeList.get()); + bool success = findBestClickableCandidate(targetNode, targetPoint, touchCenter, touchRect, result.rectBasedTestResult()); if (success && targetNode) - targetNode = targetNode->shadowAncestorNode(); + targetNode = targetNode->deprecatedShadowAncestorNode(); return success; } bool EventHandler::bestContextMenuNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode) { - HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active; IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter); - HitTestResult result = hitTestResultAtPoint(hitTestPoint, /*allowShadowContent*/ true, /*ignoreClipping*/ false, DontHitTestScrollbars, hitType, touchRadius); + HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius); IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius); - RefPtr<StaticHashSetNodeList> nodeList = StaticHashSetNodeList::adopt(result.rectBasedTestResult()); - return findBestContextMenuCandidate(targetNode, targetPoint, touchCenter, touchRect, *nodeList.get()); + return findBestContextMenuCandidate(targetNode, targetPoint, touchCenter, touchRect, result.rectBasedTestResult()); } bool EventHandler::bestZoomableAreaForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntRect& targetArea, Node*& targetNode) { - HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active; IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter); - HitTestResult result = hitTestResultAtPoint(hitTestPoint, /*allowShadowContent*/ false, /*ignoreClipping*/ false, DontHitTestScrollbars, hitType, touchRadius); + HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent, touchRadius); IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius); - RefPtr<StaticHashSetNodeList> nodeList = StaticHashSetNodeList::adopt(result.rectBasedTestResult()); - return findBestZoomableArea(targetNode, targetArea, touchCenter, touchRect, *nodeList.get()); + return findBestZoomableArea(targetNode, targetArea, touchCenter, touchRect, result.rectBasedTestResult()); } bool EventHandler::adjustGesturePosition(const PlatformGestureEvent& gestureEvent, IntPoint& adjustedPoint) @@ -2829,11 +2892,12 @@ bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event) m_mousePressed = false; bool swallowEvent; LayoutPoint viewportPos = v->windowToContents(event.position()); - HitTestRequest request(HitTestRequest::Active); + HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); MouseEventWithHitTestResults mev = doc->prepareMouseEvent(request, viewportPos, event); - if (m_frame->editor()->behavior().shouldSelectOnContextualMenuClick() + if (m_frame->editor().behavior().shouldSelectOnContextualMenuClick() && !m_frame->selection()->contains(viewportPos) + && !mev.scrollbar() // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse. // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items // available for text selections. But only if we're above text. @@ -2869,20 +2933,20 @@ bool EventHandler::sendContextMenuEventForKey() #endif IntPoint location; - Node* focusedNode = doc->focusedNode(); + Element* focusedElement = doc->focusedElement(); FrameSelection* selection = m_frame->selection(); Position start = selection->selection().start(); if (start.deprecatedNode() && (selection->rootEditableElement() || selection->isRange())) { RefPtr<Range> selectionRange = selection->toNormalizedRange(); - IntRect firstRect = m_frame->editor()->firstRectForRange(selectionRange.get()); + 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. int y = firstRect.maxY() ? firstRect.maxY() - 1 : 0; location = IntPoint(x, y); - } else if (focusedNode) { - RenderBoxModelObject* box = focusedNode->renderBoxModelObject(); + } else if (focusedElement) { + RenderBoxModelObject* box = focusedElement->renderBoxModelObject(); if (!box) return false; IntRect clippedRect = box->pixelSnappedAbsoluteClippedOverflowRect(); @@ -2898,17 +2962,15 @@ bool EventHandler::sendContextMenuEventForKey() IntPoint position = view->contentsToRootView(location); IntPoint globalPosition = view->hostWindow()->rootViewToScreen(IntRect(position, IntSize())).location(); - Node* targetNode = doc->focusedNode(); + Node* targetNode = doc->focusedElement(); if (!targetNode) targetNode = doc; // Use the focused node as the target for hover and active. HitTestResult result(position); result.setInnerNode(targetNode); - HitTestRequest request(HitTestRequest::Active); - doc->updateHoverActiveState(request, result); - doc->updateStyleIfNeeded(); - + doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowShadowContent, result.innerElement()); + // The contextmenu event is a mouse event even when invoked using the keyboard. // This is required for web compatibility. @@ -2953,24 +3015,36 @@ void EventHandler::scheduleHoverStateUpdate() m_hoverTimer.startOneShot(0); } +void EventHandler::scheduleCursorUpdate() +{ + if (!m_cursorUpdateTimer.isActive()) + m_cursorUpdateTimer.startOneShot(cursorUpdateInterval); +} + void EventHandler::dispatchFakeMouseMoveEventSoon() { if (m_mousePressed) return; + if (m_mousePositionIsUnknown) + return; + Settings* settings = m_frame->settings(); if (settings && !settings->deviceSupportsMouse()) return; - // Adjust the mouse move throttling so that it's roughly around our running average of the duration of mousemove events. - // This will cause the content to receive these moves only after the user is done scrolling, reducing pauses during the scroll. - // This will only measure the duration of the mousemove event though (not for example layouts), - // so maintain at least a minimum interval. - if (m_mouseMovedDurationRunningAverage > m_fakeMouseMoveEventTimer.delay()) - m_fakeMouseMoveEventTimer.setDelay(m_mouseMovedDurationRunningAverage + fakeMouseMoveIntervalIncrease); - else if (m_mouseMovedDurationRunningAverage < fakeMouseMoveIntervalReductionLimit * m_fakeMouseMoveEventTimer.delay()) - m_fakeMouseMoveEventTimer.setDelay(max(fakeMouseMoveMinimumInterval, fakeMouseMoveIntervalReductionFraction * m_fakeMouseMoveEventTimer.delay())); - m_fakeMouseMoveEventTimer.restart(); + // 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); + } } void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad) @@ -2979,7 +3053,7 @@ void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad) if (!view) return; - if (!quad.containsPoint(view->windowToContents(m_currentMousePosition))) + if (!quad.containsPoint(view->windowToContents(m_lastKnownMousePosition))) return; dispatchFakeMouseMoveEventSoon(); @@ -2990,7 +3064,7 @@ void EventHandler::cancelFakeMouseMoveEvent() m_fakeMouseMoveEventTimer.stop(); } -void EventHandler::fakeMouseMoveEventTimerFired(DeferrableOneShotTimer<EventHandler>* timer) +void EventHandler::fakeMouseMoveEventTimerFired(Timer<EventHandler>* timer) { ASSERT_UNUSED(timer, timer == &m_fakeMouseMoveEventTimer); ASSERT(!m_mousePressed); @@ -3011,7 +3085,7 @@ void EventHandler::fakeMouseMoveEventTimerFired(DeferrableOneShotTimer<EventHand bool altKey; bool metaKey; PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey); - PlatformMouseEvent fakeMouseMoveEvent(m_currentMousePosition, m_currentMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, currentTime()); + PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, currentTime()); mouseMoved(fakeMouseMoveEvent); } @@ -3035,10 +3109,10 @@ void EventHandler::hoverTimerFired(Timer<EventHandler>*) if (RenderView* renderer = m_frame->contentRenderer()) { if (FrameView* view = m_frame->view()) { - HitTestRequest request(HitTestRequest::Move); - HitTestResult result(view->windowToContents(m_currentMousePosition)); + HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowShadowContent); + HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); renderer->hitTest(request, result); - m_frame->document()->updateStyleIfNeeded(); + m_frame->document()->updateHoverActiveState(request, result.innerElement()); } } } @@ -3103,7 +3177,7 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) #if ENABLE(PAN_SCROLLING) if (Page* page = m_frame->page()) { - if (page->mainFrame()->eventHandler()->m_panScrollInProgress) { + 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(); @@ -3144,7 +3218,6 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) bool backwardCompatibilityMode = needsKeyboardEventDisambiguationQuirks(); - ExceptionCode ec; PlatformKeyboardEvent keyDownEvent = initialKeyEvent; if (keyDownEvent.type() != PlatformEvent::RawKeyDown) keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown, backwardCompatibilityMode); @@ -3154,7 +3227,7 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) keydown->setTarget(node); if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) { - node->dispatchEvent(keydown, ec); + node->dispatchEvent(keydown, IGNORE_EXCEPTION); // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame. bool changedFocusedFrame = m_frame->page() && m_frame != m_frame->page()->focusController()->focusedOrMainFrame(); return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; @@ -3165,7 +3238,7 @@ 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.get()); bool handledByInputMethod = keydown->defaultHandled(); @@ -3176,7 +3249,7 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) keydown->setDefaultHandled(); } - node->dispatchEvent(keydown, ec); + node->dispatchEvent(keydown, IGNORE_EXCEPTION); // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame. bool changedFocusedFrame = m_frame->page() && m_frame != m_frame->page()->focusController()->focusedOrMainFrame(); bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; @@ -3202,7 +3275,7 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) #if PLATFORM(MAC) keypress->keypressCommands() = keydown->keypressCommands(); #endif - node->dispatchEvent(keypress, ec); + node->dispatchEvent(keypress, IGNORE_EXCEPTION); return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled(); } @@ -3272,7 +3345,7 @@ static void handleKeyboardSelectionMovement(FrameSelection* selection, KeyboardE 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") @@ -3290,7 +3363,7 @@ void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event) handleKeyboardSelectionMovement(m_frame->selection(), event); } if (event->type() == eventNames().keypressEvent) { - m_frame->editor()->handleKeyboardEvent(event); + m_frame->editor().handleKeyboardEvent(event); if (event->defaultHandled()) return; if (event->charCode() == ' ') @@ -3314,7 +3387,7 @@ bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation IntSize delta = dragLocation - m_mouseDownPos; int threshold = GeneralDragHysteresis; - switch (dragState().m_dragType) { + switch (dragState().type) { case DragSourceActionSelection: threshold = TextDragHysteresis; break; @@ -3336,23 +3409,25 @@ bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation void EventHandler::freeClipboard() { - if (dragState().m_dragClipboard) - dragState().m_dragClipboard->setAccessPolicy(ClipboardNumb); + if (!dragState().clipboard) + return; + dragState().clipboard->setAccessPolicy(ClipboardNumb); + dragState().clipboard = 0; } void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation) { // Send a hit test request so that RenderLayer gets a chance to update the :hover and :active pseudoclasses. - HitTestRequest request(HitTestRequest::Release); + HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowShadowContent); prepareMouseEvent(request, event); - if (dragState().m_dragSrc && dragState().shouldDispatchEvents()) { - dragState().m_dragClipboard->setDestinationOperation(operation); - // for now we don't care if event handler cancels default behavior, since there is none + if (dragState().source && dragState().shouldDispatchEvents) { + dragState().clipboard->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().m_dragSrc = 0; + dragState().source = 0; // 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; @@ -3360,15 +3435,15 @@ void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperat void EventHandler::updateDragStateAfterEditDragIfNeeded(Element* rootEditableElement) { - // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editble element. - if (dragState().m_dragSrc && !dragState().m_dragSrc->inDocument()) - dragState().m_dragSrc = rootEditableElement; + // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editable element. + if (dragState().source && !dragState().source->inDocument()) + dragState().source = rootEditableElement; } -// returns if we should continue "default processing", i.e., whether eventhandler canceled +// 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().m_dragSrc.get(), event, dragState().m_dragClipboard.get()); + return !dispatchDragEvent(eventType, dragState().source.get(), event, dragState().clipboard.get()); } static bool ExactlyOneBitSet(DragSourceAction n) @@ -3392,48 +3467,42 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr // Careful that the drag starting logic stays in sync with eventMayStartDrag() - if (m_mouseDownMayStartDrag && !dragState().m_dragSrc) { - dragState().m_eventDispatchPolicy = (updateDragSourceActionsAllowed() & DragSourceActionDHTML) ? DragState::DispatchEvents: DragState::DoNotDispatchEvents; + if (m_mouseDownMayStartDrag && !dragState().source) { + dragState().shouldDispatchEvents = (updateDragSourceActionsAllowed() & DragSourceActionDHTML); // try to find an element that wants to be dragged - HitTestRequest request(HitTestRequest::ReadOnly); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); HitTestResult result(m_mouseDownPos); m_frame->contentRenderer()->hitTest(request, result); - Node* node = result.innerNode(); - if (node && m_frame->page()) - dragState().m_dragSrc = m_frame->page()->dragController()->draggableNode(m_frame, node, m_mouseDownPos, dragState()); - else - dragState().m_dragSrc = 0; + if (m_frame->page()) + dragState().source = m_frame->page()->dragController()->draggableElement(m_frame, result.innerElement(), m_mouseDownPos, dragState()); - if (!dragState().m_dragSrc) + if (!dragState().source) m_mouseDownMayStartDrag = false; // no element is draggable else - m_dragMayStartSelectionInstead = (dragState().m_dragType & DragSourceActionSelection); + m_dragMayStartSelectionInstead = (dragState().type & DragSourceActionSelection); } // For drags starting in the selection, the user must wait between the mousedown and mousedrag, // or else we bail on the dragging stuff and allow selection to occur - if (m_mouseDownMayStartDrag && m_dragMayStartSelectionInstead && (dragState().m_dragType & DragSourceActionSelection) && event.event().timestamp() - m_mouseDownTimestamp < TextDragDelay) { + if (m_mouseDownMayStartDrag && m_dragMayStartSelectionInstead && (dragState().type & DragSourceActionSelection) && event.event().timestamp() - m_mouseDownTimestamp < TextDragDelay) { ASSERT(event.event().type() == PlatformEvent::MouseMoved); - if ((dragState().m_dragType & DragSourceActionImage)) { + if ((dragState().type & DragSourceActionImage)) { // ... unless the mouse is over an image, then we start dragging just the image - dragState().m_dragType = DragSourceActionImage; - } else if (!(dragState().m_dragType & (DragSourceActionDHTML | DragSourceActionLink))) { + dragState().type = DragSourceActionImage; + } else if (!(dragState().type & (DragSourceActionDHTML | DragSourceActionLink))) { // ... but only bail if we're not over an unselectable element. m_mouseDownMayStartDrag = false; - dragState().m_dragSrc = 0; + dragState().source = 0; // ... 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; } else { // Prevent the following case from occuring: // 1. User starts a drag immediately after mouse down over an unselectable element. - // 2. We enter this block and decided that since we're over an unselectable element, - // don't cancel the drag. - // 3. The drag gets resolved as a potential selection drag below /but/ we haven't - // exceeded the drag hysteresis yet. - // 4. We enter this block again, and since it's now marked as a selection drag, we - // cancel the drag. + // 2. We enter this block and decided that since we're over an unselectable element, don't cancel the drag. + // 3. The drag gets resolved as a potential selection drag below /but/ we haven't exceeded the drag hysteresis yet. + // 4. We enter this block again, and since it's now marked as a selection drag, we cancel the drag. m_dragMayStartSelectionInstead = false; } } @@ -3441,12 +3510,12 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr if (!m_mouseDownMayStartDrag) return !mouseDownMayStartSelect() && !m_mouseDownMayStartAutoscroll; - if (!ExactlyOneBitSet(dragState().m_dragType)) { - ASSERT((dragState().m_dragType & DragSourceActionSelection)); - ASSERT((dragState().m_dragType & ~DragSourceActionSelection) == DragSourceActionDHTML - || (dragState().m_dragType & ~DragSourceActionSelection) == DragSourceActionImage - || (dragState().m_dragType & ~DragSourceActionSelection) == DragSourceActionLink); - dragState().m_dragType = DragSourceActionSelection; + if (!ExactlyOneBitSet(dragState().type)) { + ASSERT((dragState().type & DragSourceActionSelection)); + ASSERT((dragState().type & ~DragSourceActionSelection) == DragSourceActionDHTML + || (dragState().type & ~DragSourceActionSelection) == DragSourceActionImage + || (dragState().type & ~DragSourceActionSelection) == DragSourceActionLink); + dragState().type = DragSourceActionSelection; } // We are starting a text/image/url drag, so the cursor should be an arrow @@ -3463,19 +3532,19 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr DragOperation srcOp = DragOperationNone; - freeClipboard(); // would only happen if we missed a dragEnd. Do it anyway, just - // to make sure it gets numbified - dragState().m_dragClipboard = createDraggingClipboard(); + // This does work only if we missed a dragEnd. Do it anyway, just to make sure the old clipboard gets numbed. + freeClipboard(); + + dragState().clipboard = createDraggingClipboard(); - if (dragState().shouldDispatchEvents()) { - // Check to see if the is a DOM based drag, if it is get the DOM specified drag - // image and offset - if (dragState().m_dragType == DragSourceActionDHTML) { - if (RenderObject* renderer = dragState().m_dragSrc->renderer()) { + if (dragState().shouldDispatchEvents) { + // Check to see if the is a DOM based drag. If it is, get the DOM specified drag image and offset. + if (dragState().type == DragSourceActionDHTML) { + if (RenderObject* renderer = dragState().source->renderer()) { // FIXME: This doesn't work correctly with transforms. FloatPoint absPos = renderer->localToAbsolute(); IntSize delta = m_mouseDownPos - roundedIntPoint(absPos); - dragState().m_dragClipboard->setDragImageElement(dragState().m_dragSrc.get(), toPoint(delta)); + dragState().clipboard->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. @@ -3489,17 +3558,16 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr // Invalidate clipboard here against anymore pasteboard writing for security. The drag // image can still be changed as we drag, but not the pasteboard data. - dragState().m_dragClipboard->setAccessPolicy(ClipboardImageWritable); + dragState().clipboard->setAccessPolicy(ClipboardImageWritable); if (m_mouseDownMayStartDrag) { - // gather values from DHTML element, if it set any - srcOp = dragState().m_dragClipboard->sourceOperation(); + // Gather values from DHTML element, if it set any. + srcOp = dragState().clipboard->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().m_dragClipboard->setDragHasStarted(); + // 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(); } } @@ -3507,15 +3575,14 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr Page* page = m_frame->page(); DragController* dragController = page ? page->dragController() : 0; m_didStartDrag = dragController && dragController->startDrag(m_frame, dragState(), srcOp, event.event(), m_mouseDownPos); - // In WebKit2 we could reenter this code and start another drag. - // On OS X this causes problems with the ownership of the pasteboard - // and the promised types. + // 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) { m_mouseDownMayStartDrag = false; return true; } - if (dragState().shouldDispatchEvents()) { - // Drag was canned at the last minute - we owe m_dragSrc a DRAGEND event + if (dragState().source && dragState().shouldDispatchEvents) { + // Drag was canned at the last minute. We owe dragSource a dragend event. dispatchDragSrcEvent(eventNames().dragendEvent, event.event()); m_mouseDownMayStartDrag = false; } @@ -3523,9 +3590,9 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr cleanupDrag: if (!m_mouseDownMayStartDrag) { - // something failed to start the drag, cleanup + // Something failed to start the drag, clean up. freeClipboard(); - dragState().m_dragSrc = 0; + dragState().source = 0; } // No more default handling (like selection), whether we're past the hysteresis bounds or not @@ -3556,8 +3623,7 @@ bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve RefPtr<TextEvent> event = TextEvent::create(m_frame->document()->domWindow(), text, inputType); event->setUnderlyingEvent(underlyingEvent); - ExceptionCode ec; - target->dispatchEvent(event, ec); + target->dispatchEvent(event, IGNORE_EXCEPTION); return event->defaultHandled(); } @@ -3586,23 +3652,16 @@ bool EventHandler::tabsToLinks(KeyboardEvent* event) const 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(); } -#if PLATFORM(QT) -// Qt handles the space event in platform-specific WebKit code. -// Eventually it would be good to eliminate that and use the code here instead. -void EventHandler::defaultSpaceEventHandler(KeyboardEvent*) -{ -} -#else void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event) { @@ -3625,8 +3684,6 @@ void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event) event->setDefaultHandled(); } -#endif - void EventHandler::defaultBackspaceEventHandler(KeyboardEvent* event) { ASSERT(event->type() == eventNames().keydownEvent); @@ -3634,7 +3691,7 @@ 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(); @@ -3706,19 +3763,14 @@ void EventHandler::defaultTabEventHandler(KeyboardEvent* event) void EventHandler::capsLockStateMayHaveChanged() { Document* d = m_frame->document(); - if (Node* node = d->focusedNode()) { - if (RenderObject* r = node->renderer()) { + if (Element* element = d->focusedElement()) { + if (RenderObject* r = element->renderer()) { if (r->isTextField()) toRenderTextControlSingleLine(r)->capsLockStateMayHaveChanged(); } } } -void EventHandler::sendResizeEvent() -{ - m_frame->document()->enqueueWindowEvent(Event::create(eventNames().resizeEvent, false, false)); -} - void EventHandler::sendScrollEvent() { setFrameWasScrolledByUser(); @@ -3779,6 +3831,21 @@ static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State } } +HitTestResult EventHandler::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))) + return result; + } + frame->contentRenderer()->hitTest(HitTestRequest(hitType), result); + return result; +} + bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) { // First build up the lists to use for the 'touches', 'targetTouches' and 'changedTouches' attributes @@ -3806,7 +3873,18 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); - for (unsigned i = 0; i < points.size(); ++i) { + unsigned i; + bool freshTouchEvents = true; + bool allTouchReleased = true; + for (i = 0; i < points.size(); ++i) { + const PlatformTouchPoint& point = points[i]; + 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]; PlatformTouchPoint::State pointState = point.state(); LayoutPoint pagePoint = documentPointForWindowPoint(m_frame, point.pos()); @@ -3836,37 +3914,54 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) break; } -#if ENABLE(GESTURE_EVENTS) if (shouldGesturesTriggerActive()) hitType |= HitTestRequest::ReadOnly; -#endif // Increment the platform touch id by 1 to avoid storing a key of 0 in the hashmap. unsigned touchPointTargetKey = point.id() + 1; RefPtr<EventTarget> touchTarget; if (pointState == PlatformTouchPoint::TouchPressed) { - HitTestResult result = hitTestResultAtPoint(pagePoint, /*allowShadowContent*/ false, false, DontHitTestScrollbars, hitType); + HitTestResult result; + if (freshTouchEvents) { + result = hitTestResultAtPoint(pagePoint, hitType); + m_originatingTouchPointTargetKey = touchPointTargetKey; + } else if (m_originatingTouchPointDocument.get() && m_originatingTouchPointDocument->frame()) { + 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(); ASSERT(node); - // Touch events should not go to text nodes if (node->isTextNode()) - node = node->parentNode(); + node = EventPathWalker::parent(node); if (InspectorInstrumentation::handleTouchEvent(m_frame->page(), node)) return true; Document* doc = node->document(); + // Record the originating touch document even if it does not have a touch listener. + if (freshTouchEvents) { + m_originatingTouchPointDocument = doc; + freshTouchEvents = false; + } if (!doc) continue; - if (!doc->touchEventHandlerCount()) + if (!doc->hasTouchEventHandlers()) continue; - m_originatingTouchPointTargets.set(touchPointTargetKey, node); touchTarget = node; } else if (pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled) { - // We only perform a hittest on release or cancel to unset :active or :hover state. - hitTestResultAtPoint(pagePoint, /*allowShadowContent*/ false, false, DontHitTestScrollbars, hitType); + // 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); + if (touchPointTargetKey == m_originatingTouchPointTargetKey) + m_originatingTouchPointTargetKey = 0; + // The target should be the original target for this touch, so get it from the hashmap. As it's a release or cancel // we also remove it from the map. touchTarget = m_originatingTouchPointTargets.take(touchPointTargetKey); @@ -3879,7 +3974,7 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) Document* doc = touchTarget->toNode()->document(); if (!doc) continue; - if (!doc->touchEventHandlerCount()) + if (!doc->hasTouchEventHandlers()) continue; Frame* targetFrame = doc->frame(); if (!targetFrame) @@ -3927,6 +4022,8 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) } } m_touchPressed = touches->length() > 0; + if (allTouchReleased) + m_originatingTouchPointDocument.clear(); // Now iterate the changedTouches list and m_targets within it, sending events to the targets as required. bool swallowedEvent = false; @@ -3950,8 +4047,7 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) 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()); - ExceptionCode ec = 0; - touchEventTarget->dispatchEvent(touchEvent.get(), ec); + touchEventTarget->toNode()->dispatchTouchEvent(touchEvent.get()); swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled(); } } @@ -3968,13 +4064,26 @@ bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent if (eventType != PlatformEvent::MouseMoved && eventType != PlatformEvent::MousePressed && eventType != PlatformEvent::MouseReleased) return false; - if (eventType == PlatformEvent::MouseMoved && !m_touchPressed) + HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); + MouseEventWithHitTestResults mev = prepareMouseEvent(request, event); + if (mev.scrollbar() || subframeForHitTestResult(mev)) 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); return handleTouchEvent(touchEvent); } #endif +void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event) +{ + m_mousePositionIsUnknown = false; + m_lastKnownMousePosition = event.position(); + m_lastKnownMouseGlobalPosition = event.globalPosition(); } + +} // namespace WebCore |