Chromium Code Reviews| Index: Source/core/page/EventHandler.cpp |
| diff --git a/Source/core/page/EventHandler.cpp b/Source/core/page/EventHandler.cpp |
| index 7ed9d780f4dcd874f5b5f62e8dc7dbb014564e37..877be689337ac9c3d27895b589886cf63208ea8f 100644 |
| --- a/Source/core/page/EventHandler.cpp |
| +++ b/Source/core/page/EventHandler.cpp |
| @@ -53,10 +53,12 @@ |
| #include "core/fetch/ImageResource.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/LocalFrame.h" |
| +#include "core/frame/Settings.h" |
| #include "core/html/HTMLDialogElement.h" |
| #include "core/html/HTMLFrameElementBase.h" |
| #include "core/html/HTMLFrameSetElement.h" |
| #include "core/html/HTMLInputElement.h" |
| +#include "core/inspector/InspectorController.h" |
| #include "core/loader/FrameLoader.h" |
| #include "core/loader/FrameLoaderClient.h" |
| #include "core/page/AutoscrollController.h" |
| @@ -66,12 +68,10 @@ |
| #include "core/page/DragController.h" |
| #include "core/page/DragState.h" |
| #include "core/page/EditorClient.h" |
| +#include "core/page/EventWithHitTestResults.h" |
| #include "core/page/FocusController.h" |
| #include "core/page/FrameTree.h" |
| -#include "core/inspector/InspectorController.h" |
| -#include "core/page/MouseEventWithHitTestResults.h" |
| #include "core/page/Page.h" |
| -#include "core/frame/Settings.h" |
| #include "core/page/SpatialNavigation.h" |
| #include "core/page/TouchAdjustment.h" |
| #include "core/rendering/HitTestRequest.h" |
| @@ -221,9 +221,8 @@ EventHandler::EventHandler(LocalFrame* frame) |
| , m_widgetIsLatched(false) |
| , m_touchPressed(false) |
| , m_scrollGestureHandlingNode(nullptr) |
| - , m_lastHitTestResultOverWidget(false) |
| + , m_lastGestureScrollOverWidget(false) |
| , m_maxMouseMovedDuration(0) |
| - , m_baseEventType(PlatformEvent::NoType) |
| , m_didStartDrag(false) |
| , m_longTapShouldInvokeContextMenu(false) |
| , m_activeIntervalTimer(this, &EventHandler::activeIntervalTimerFired) |
| @@ -295,11 +294,10 @@ void EventHandler::clear() |
| m_touchSequenceDocument.clear(); |
| m_touchSequenceUserGestureToken.clear(); |
| m_scrollGestureHandlingNode = nullptr; |
| - m_lastHitTestResultOverWidget = false; |
| + m_lastGestureScrollOverWidget = false; |
| m_previousGestureScrolledNode = nullptr; |
| m_scrollbarHandlingScrollGesture = nullptr; |
| m_maxMouseMovedDuration = 0; |
| - m_baseEventType = PlatformEvent::NoType; |
| m_didStartDrag = false; |
| m_touchPressed = false; |
| m_mouseDownMayStartSelect = false; |
| @@ -2061,123 +2059,138 @@ bool EventHandler::handleGestureShowPress() |
| bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent) |
| { |
| - IntPoint adjustedPoint = gestureEvent.position(); |
| - RefPtr<LocalFrame> subframe = nullptr; |
| - switch (gestureEvent.type()) { |
| - case PlatformEvent::GestureScrollBegin: |
| - case PlatformEvent::GestureScrollUpdate: |
| - case PlatformEvent::GestureScrollUpdateWithoutPropagation: |
| - case PlatformEvent::GestureScrollEnd: |
| - case PlatformEvent::GestureFlingStart: |
| - // Handle directly in main frame |
| - break; |
| + TRACE_EVENT0("input", "EventHandler::handleGestureEvent"); |
| + |
| + // Propagation to inner frames is handled below this function. |
| + ASSERT(m_frame == m_frame->localFrameRoot()); |
| + |
| + // Scrolling-related gesture events invoke EventHandler recursively for each frame down |
| + // the chain, doing a single-frame hit-test per frame. This matches handleWheelEvent. |
| + // Perhaps we could simplify things by rewriting scroll handling to work inner frame |
| + // out, and then unify with other gesture events. |
| + if (gestureEvent.isScrollEvent()) { |
| + return handleGestureScrollEvent(gestureEvent); |
| + } |
|
esprehn
2014/06/27 08:33:58
no braces
Rick Byers
2014/06/27 15:38:23
Done.
|
| + |
| + // Non-scrolling related gesture events instead do a single cross-frame hit-test and |
| + // jump directly to the inner most frame. This matches handleMousePressEvent etc. |
| + |
| + // Hit test across all frames and do touch adjustment as necessary for the event type. |
| + GestureEventWithHitTestResults targetedEvent = targetGestureEvent(gestureEvent); |
| + ASSERT(!(targetedEvent.hitTestResult().isOverWidget())); |
|
esprehn
2014/06/27 08:33:58
remove extra braces
Rick Byers
2014/06/27 15:38:24
Done.
|
| + |
| + // Route to the correct frame. |
| + if (LocalFrame* innerFrame = targetedEvent.hitTestResult().innerNodeFrame()) |
| + return innerFrame->eventHandler().handleGestureEventInFrame(targetedEvent); |
| + |
| + // No hit test result, handle in root instance. Perhaps we should just return false instead? |
| + return handleGestureEventInFrame(targetedEvent); |
| +} |
| + |
| +bool EventHandler::handleGestureEventInFrame(const GestureEventWithHitTestResults& targetedEvent) |
| +{ |
| + ASSERT(!targetedEvent.event().isScrollEvent()); |
| + |
| + RefPtrWillBeRawPtr<Node> eventTarget = targetedEvent.hitTestResult().targetNode(); |
| + RefPtr<Scrollbar> scrollbar = targetedEvent.hitTestResult().scrollbar(); |
| + const PlatformGestureEvent& gestureEvent = targetedEvent.event(); |
| + if (!scrollbar) { |
| + FrameView* view = m_frame->view(); |
| + scrollbar = view ? view->scrollbarAtPoint(gestureEvent.position()) : 0; |
| + } |
| + |
| + if (scrollbar) { |
| + bool eventSwallowed = scrollbar->gestureEvent(gestureEvent); |
| + if (gestureEvent.type() == PlatformEvent::GestureTapDown && eventSwallowed) { |
|
esprehn
2014/06/27 08:33:58
no braces
Rick Byers
2014/06/27 15:38:23
Done.
|
| + m_scrollbarHandlingScrollGesture = scrollbar; |
| + } |
| + if (eventSwallowed) |
| + return true; |
| + } |
| + |
| + if (eventTarget && eventTarget->dispatchGestureEvent(gestureEvent)) |
| + return true; |
| + |
| + switch (gestureEvent.type()) { |
| case PlatformEvent::GestureTap: |
| - case PlatformEvent::GestureTapUnconfirmed: |
| - case PlatformEvent::GestureTapDown: |
| + return handleGestureTap(targetedEvent); |
| case PlatformEvent::GestureShowPress: |
| - case PlatformEvent::GestureTapDownCancel: |
| - case PlatformEvent::GestureTwoFingerTap: |
| + return handleGestureShowPress(); |
| case PlatformEvent::GestureLongPress: |
| + return handleGestureLongPress(targetedEvent); |
| case PlatformEvent::GestureLongTap: |
| + return handleGestureLongTap(targetedEvent); |
| + case PlatformEvent::GestureTwoFingerTap: |
| + return sendContextMenuEventForGesture(targetedEvent); |
| + case PlatformEvent::GestureTapDown: |
| case PlatformEvent::GesturePinchBegin: |
| case PlatformEvent::GesturePinchEnd: |
| case PlatformEvent::GesturePinchUpdate: |
| - adjustGesturePosition(gestureEvent, adjustedPoint); |
| - subframe = getSubFrameForGestureEvent(adjustedPoint, gestureEvent); |
| - if (subframe) |
| - return subframe->eventHandler().handleGestureEvent(gestureEvent); |
| + case PlatformEvent::GestureTapDownCancel: |
| + case PlatformEvent::GestureTapUnconfirmed: |
| break; |
| - |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| + return false; |
| +} |
| + |
| +bool EventHandler::handleGestureScrollEvent(const PlatformGestureEvent& gestureEvent) |
| +{ |
| RefPtrWillBeRawPtr<Node> eventTarget = nullptr; |
| RefPtr<Scrollbar> scrollbar; |
| - if (gestureEvent.type() == PlatformEvent::GestureScrollEnd |
| - || gestureEvent.type() == PlatformEvent::GestureScrollUpdate |
| - || gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation |
| - || gestureEvent.type() == PlatformEvent::GestureFlingStart) { |
| + if (gestureEvent.type() != PlatformEvent::GestureScrollBegin) { |
| scrollbar = m_scrollbarHandlingScrollGesture.get(); |
| eventTarget = m_scrollGestureHandlingNode.get(); |
| } |
| - HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent; |
| - double activeInterval = 0; |
| - bool shouldKeepActiveForMinInterval = false; |
| - if (gestureEvent.type() == PlatformEvent::GestureShowPress |
| - || gestureEvent.type() == PlatformEvent::GestureTapUnconfirmed) { |
| - hitType |= HitTestRequest::Active; |
| - } else if (gestureEvent.type() == PlatformEvent::GestureTapDownCancel) { |
| - hitType |= HitTestRequest::Release; |
| - // A TapDownCancel received when no element is active shouldn't really be changing hover state. |
| - if (!m_frame->document()->activeHoverElement()) |
| - hitType |= HitTestRequest::ReadOnly; |
| - } else if (gestureEvent.type() == PlatformEvent::GestureTap) { |
| - hitType |= HitTestRequest::Release; |
| - // If the Tap is received very shortly after ShowPress, we want to |
| - // delay clearing of the active state so that it's visible to the user |
| - // for at least a couple of frames. |
| - activeInterval = WTF::currentTime() - m_lastShowPressTimestamp; |
| - shouldKeepActiveForMinInterval = m_lastShowPressTimestamp && activeInterval < minimumActiveInterval; |
| - if (shouldKeepActiveForMinInterval) |
| - hitType |= HitTestRequest::ReadOnly; |
| - } |
| - else |
| - hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly; |
| + if (!eventTarget) { |
| + Document* document = m_frame->document(); |
| + if (!document->renderView()) |
| + return false; |
| - if ((!scrollbar && !eventTarget) || !(hitType & HitTestRequest::ReadOnly)) { |
| - IntPoint hitTestPoint = m_frame->view()->windowToContents(adjustedPoint); |
| - HitTestResult result = hitTestResultAtPoint(hitTestPoint, hitType | HitTestRequest::AllowFrameScrollbars); |
| + FrameView* view = m_frame->view(); |
| + if (!view) |
|
esprehn
2014/06/27 08:33:58
You don't need this null check, if you have a Rend
Rick Byers
2014/06/27 15:38:23
Done.
|
| + return false; |
| - if (shouldKeepActiveForMinInterval) { |
| - m_lastDeferredTapElement = result.innerElement(); |
| - m_activeIntervalTimer.startOneShot(minimumActiveInterval - activeInterval, FROM_HERE); |
| - } |
| + LayoutPoint viewPoint = view->windowToContents(gestureEvent.position()); |
| + HitTestRequest request(HitTestRequest::ReadOnly); |
| + HitTestResult result(viewPoint); |
| + document->renderView()->hitTest(request, result); |
|
esprehn
2014/06/27 08:33:58
What updated the layout before this call?
Rick Byers
2014/06/27 15:38:24
Nothing, as far as I can see, but that was the cas
|
| - eventTarget = result.targetNode(); |
| - if (!scrollbar) { |
| - FrameView* view = m_frame->view(); |
| - scrollbar = view ? view->scrollbarAtPoint(gestureEvent.position()) : 0; |
| - } |
| + eventTarget = result.innerNode(); |
| + |
| + m_lastGestureScrollOverWidget = result.isOverWidget(); |
| + m_scrollGestureHandlingNode = eventTarget; |
| + m_previousGestureScrolledNode = nullptr; |
| + |
| + if (!scrollbar) |
| + scrollbar = view->scrollbarAtPoint(gestureEvent.position()); |
| if (!scrollbar) |
| scrollbar = result.scrollbar(); |
| } |
| if (scrollbar) { |
| bool eventSwallowed = scrollbar->gestureEvent(gestureEvent); |
| - if (gestureEvent.type() == PlatformEvent::GestureTapDown && eventSwallowed) { |
| - m_scrollbarHandlingScrollGesture = scrollbar; |
| - } else if (gestureEvent.type() == PlatformEvent::GestureScrollEnd |
| + if (gestureEvent.type() == PlatformEvent::GestureScrollEnd |
| || gestureEvent.type() == PlatformEvent::GestureFlingStart |
| || !eventSwallowed) { |
| m_scrollbarHandlingScrollGesture = nullptr; |
| } |
| - |
| if (eventSwallowed) |
| return true; |
| } |
| if (eventTarget) { |
| - bool eventSwallowed = false; |
| - if (handleScrollGestureOnResizer(eventTarget.get(), gestureEvent)) |
| - eventSwallowed = true; |
| - else |
| + bool eventSwallowed = handleScrollGestureOnResizer(eventTarget.get(), gestureEvent); |
| + if (!eventSwallowed) |
| eventSwallowed = eventTarget->dispatchGestureEvent(gestureEvent); |
| - if (gestureEvent.type() == PlatformEvent::GestureScrollBegin || gestureEvent.type() == PlatformEvent::GestureScrollEnd) { |
| - if (eventSwallowed) |
| - m_scrollGestureHandlingNode = eventTarget; |
| - } |
| - |
| if (eventSwallowed) |
| return true; |
| } |
| - // FIXME: A more general scroll system (https://bugs.webkit.org/show_bug.cgi?id=80596) will |
| - // eliminate the need for this. |
| - TemporaryChange<PlatformEvent::Type> baseEventType(m_baseEventType, gestureEvent.type()); |
| - |
| switch (gestureEvent.type()) { |
| case PlatformEvent::GestureScrollBegin: |
| return handleGestureScrollBegin(gestureEvent); |
| @@ -2186,33 +2199,18 @@ bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent) |
| return handleGestureScrollUpdate(gestureEvent); |
| case PlatformEvent::GestureScrollEnd: |
| return handleGestureScrollEnd(gestureEvent); |
| - case PlatformEvent::GestureTap: |
| - return handleGestureTap(gestureEvent, adjustedPoint); |
| - case PlatformEvent::GestureShowPress: |
| - return handleGestureShowPress(); |
| - case PlatformEvent::GestureLongPress: |
| - return handleGestureLongPress(gestureEvent, adjustedPoint); |
| - case PlatformEvent::GestureLongTap: |
| - return handleGestureLongTap(gestureEvent, adjustedPoint); |
| - case PlatformEvent::GestureTwoFingerTap: |
| - return handleGestureTwoFingerTap(gestureEvent, adjustedPoint); |
| - case PlatformEvent::GestureTapDown: |
| - case PlatformEvent::GesturePinchBegin: |
| - case PlatformEvent::GesturePinchEnd: |
| - case PlatformEvent::GesturePinchUpdate: |
| - case PlatformEvent::GestureTapDownCancel: |
| - case PlatformEvent::GestureTapUnconfirmed: |
| case PlatformEvent::GestureFlingStart: |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| - |
| return false; |
| } |
| -bool EventHandler::handleGestureTap(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint) |
| +bool EventHandler::handleGestureTap(const GestureEventWithHitTestResults& targetedEvent) |
| { |
| + const PlatformGestureEvent& gestureEvent = targetedEvent.event(); |
| + |
| // FIXME: Refactor this code to not hit test multiple times. We use the adjusted position to ensure that the correct node is targeted by the later redundant hit tests. |
| unsigned modifierFlags = 0; |
| @@ -2226,6 +2224,8 @@ bool EventHandler::handleGestureTap(const PlatformGestureEvent& gestureEvent, co |
| modifierFlags |= PlatformEvent::ShiftKey; |
| PlatformEvent::Modifiers modifiers = static_cast<PlatformEvent::Modifiers>(modifierFlags); |
| + IntPoint adjustedPoint = gestureEvent.position(); |
| + |
| PlatformMouseEvent fakeMouseMove(adjustedPoint, gestureEvent.globalPosition(), |
| NoButton, PlatformEvent::MouseMoved, /* clickCount */ 0, |
| modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp()); |
| @@ -2245,8 +2245,15 @@ bool EventHandler::handleGestureTap(const PlatformGestureEvent& gestureEvent, co |
| return defaultPrevented; |
| } |
| -bool EventHandler::handleGestureLongPress(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint) |
| +bool EventHandler::handleGestureLongPress(const GestureEventWithHitTestResults& targetedEvent) |
| { |
| + const PlatformGestureEvent& gestureEvent = targetedEvent.event(); |
| + IntPoint adjustedPoint = gestureEvent.position(); |
| + |
| + // FIXME: Ideally we should try to remove the extra mouse-specific hit-tests here (re-using the |
| + // supplied HitTestResult), but that will require some overhaul of the touch drag-and-drop code |
| + // and LongPress is such a special scenario that it's unlikely to matter much in practice. |
| + |
| m_longTapShouldInvokeContextMenu = false; |
| if (m_frame->settings() && m_frame->settings()->touchDragDropEnabled() && m_frame->view()) { |
| PlatformMouseEvent mouseDownEvent(adjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, 1, |
| @@ -2285,15 +2292,15 @@ bool EventHandler::handleGestureLongPress(const PlatformGestureEvent& gestureEve |
| } |
| } |
| } |
| - return sendContextMenuEventForGesture(gestureEvent); |
| + return sendContextMenuEventForGesture(targetedEvent); |
| } |
| -bool EventHandler::handleGestureLongTap(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint) |
| +bool EventHandler::handleGestureLongTap(const GestureEventWithHitTestResults& targetedEvent) |
| { |
| #if !OS(ANDROID) |
| if (m_longTapShouldInvokeContextMenu) { |
| m_longTapShouldInvokeContextMenu = false; |
| - return sendContextMenuEventForGesture(gestureEvent); |
| + return sendContextMenuEventForGesture(targetedEvent); |
| } |
| #endif |
| return false; |
| @@ -2326,29 +2333,22 @@ bool EventHandler::handleScrollGestureOnResizer(Node* eventTarget, const Platfor |
| return false; |
| } |
| -bool EventHandler::handleGestureTwoFingerTap(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint) |
| +bool EventHandler::passScrollGestureEventToWidget(const PlatformGestureEvent& gestureEvent, RenderObject* renderer) |
| { |
| - return sendContextMenuEventForGesture(gestureEvent); |
| -} |
| + ASSERT(gestureEvent.isScrollEvent()); |
| -bool EventHandler::passGestureEventToWidget(const PlatformGestureEvent& gestureEvent, Widget* widget) |
| -{ |
| - if (!widget) |
| + if (!m_lastGestureScrollOverWidget) |
| return false; |
| - if (!widget->isFrameView()) |
| + if (!renderer || !renderer->isWidget()) |
| return false; |
| - return toFrameView(widget)->frame().eventHandler().handleGestureEvent(gestureEvent); |
| -} |
| + Widget* widget = toRenderWidget(renderer)->widget(); |
| -bool EventHandler::passGestureEventToWidgetIfPossible(const PlatformGestureEvent& gestureEvent, RenderObject* renderer) |
| -{ |
| - if (m_lastHitTestResultOverWidget && renderer && renderer->isWidget()) { |
| - Widget* widget = toRenderWidget(renderer)->widget(); |
| - return widget && passGestureEventToWidget(gestureEvent, widget); |
| - } |
| - return false; |
| + if (!widget->isFrameView()) |
| + return false; |
| + |
| + return toFrameView(widget)->frame().eventHandler().handleGestureScrollEvent(gestureEvent); |
| } |
| bool EventHandler::handleGestureScrollEnd(const PlatformGestureEvent& gestureEvent) { |
| @@ -2356,7 +2356,7 @@ bool EventHandler::handleGestureScrollEnd(const PlatformGestureEvent& gestureEve |
| clearGestureScrollNodes(); |
| if (node) |
| - passGestureEventToWidgetIfPossible(gestureEvent, node->renderer()); |
| + passScrollGestureEventToWidget(gestureEvent, node->renderer()); |
| return false; |
| } |
| @@ -2371,15 +2371,6 @@ bool EventHandler::handleGestureScrollBegin(const PlatformGestureEvent& gestureE |
| if (!view) |
| return false; |
| - LayoutPoint viewPoint = view->windowToContents(gestureEvent.position()); |
| - HitTestRequest request(HitTestRequest::ReadOnly); |
| - HitTestResult result(viewPoint); |
| - document->renderView()->hitTest(request, result); |
| - |
| - m_lastHitTestResultOverWidget = result.isOverWidget(); |
| - m_scrollGestureHandlingNode = result.innerNode(); |
| - m_previousGestureScrolledNode = nullptr; |
| - |
| // If there's no renderer on the node, send the event to the nearest ancestor with a renderer. |
| // Needed for <option> and <optgroup> elements so we can touch scroll <select>s |
| while (m_scrollGestureHandlingNode && !m_scrollGestureHandlingNode->renderer()) |
| @@ -2388,7 +2379,7 @@ bool EventHandler::handleGestureScrollBegin(const PlatformGestureEvent& gestureE |
| if (!m_scrollGestureHandlingNode) |
| return false; |
| - passGestureEventToWidgetIfPossible(gestureEvent, m_scrollGestureHandlingNode->renderer()); |
| + passScrollGestureEventToWidget(gestureEvent, m_scrollGestureHandlingNode->renderer()); |
| return true; |
| } |
| @@ -2417,7 +2408,7 @@ bool EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gesture |
| bool scrollShouldNotPropagate = gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation; |
| // Try to send the event to the correct view. |
| - if (passGestureEventToWidgetIfPossible(gestureEvent, renderer)) { |
| + if (passScrollGestureEventToWidget(gestureEvent, renderer)) { |
| if(scrollShouldNotPropagate) |
| m_previousGestureScrolledNode = m_scrollGestureHandlingNode; |
| @@ -2467,15 +2458,6 @@ bool EventHandler::sendScrollEventToView(const PlatformGestureEvent& gestureEven |
| return scrolledFrame; |
| } |
| -LocalFrame* EventHandler::getSubFrameForGestureEvent(const IntPoint& touchAdjustedPoint, const PlatformGestureEvent& gestureEvent) |
| -{ |
| - PlatformMouseEvent mouseDown(touchAdjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, 1, |
| - gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp()); |
| - HitTestRequest request(HitTestRequest::ReadOnly); |
| - MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseDown); |
| - return subframeForHitTestResult(mev); |
| -} |
| - |
| void EventHandler::clearGestureScrollNodes() |
| { |
| m_scrollGestureHandlingNode = nullptr; |
| @@ -2494,10 +2476,12 @@ bool EventHandler::shouldApplyTouchAdjustment(const PlatformGestureEvent& event) |
| return !event.area().isEmpty(); |
| } |
| -bool EventHandler::bestClickableNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode) |
| +bool EventHandler::bestClickableNodeForHitTestResult(const HitTestResult& result, IntPoint& targetPoint, Node*& targetNode) |
| { |
| - IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter); |
| - HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius); |
| + // FIXME: Unify this with the other best* functions which are very similar. |
| + |
| + TRACE_EVENT0("input", "EventHandler::bestClickableNodeForHitTestResult"); |
| + ASSERT(result.isRectBasedTest()); |
| // If the touch is over a scrollbar, don't adjust the touch point since touch adjustment only takes into account |
| // DOM nodes so a touch over a scrollbar will be adjusted towards nearby nodes. This leads to things like textarea |
| @@ -2505,7 +2489,9 @@ bool EventHandler::bestClickableNodeForTouchPoint(const IntPoint& touchCenter, c |
| if (result.scrollbar()) |
| return false; |
| - IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius); |
| + IntPoint touchCenter = m_frame->view()->contentsToWindow(result.roundedPointInMainFrame()); |
| + IntRect touchRect = m_frame->view()->contentsToWindow(result.hitTestLocation().boundingBox()); |
| + |
| WillBeHeapVector<RefPtrWillBeMember<Node>, 11> nodes; |
| copyToVector(result.rectBasedTestResult(), nodes); |
| @@ -2523,12 +2509,11 @@ bool EventHandler::bestClickableNodeForTouchPoint(const IntPoint& touchCenter, c |
| return success; |
| } |
| -bool EventHandler::bestContextMenuNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode) |
| +bool EventHandler::bestContextMenuNodeForHitTestResult(const HitTestResult& result, IntPoint& targetPoint, Node*& targetNode) |
| { |
| - IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter); |
| - HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius); |
| - |
| - IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius); |
| + ASSERT(result.isRectBasedTest()); |
| + IntPoint touchCenter = m_frame->view()->contentsToWindow(result.roundedPointInMainFrame()); |
| + IntRect touchRect = m_frame->view()->contentsToWindow(result.hitTestLocation().boundingBox()); |
| WillBeHeapVector<RefPtrWillBeMember<Node>, 11> nodes; |
| copyToVector(result.rectBasedTestResult(), nodes); |
| @@ -2549,28 +2534,91 @@ bool EventHandler::bestZoomableAreaForTouchPoint(const IntPoint& touchCenter, co |
| return findBestZoomableArea(targetNode, targetArea, touchCenter, touchRect, WillBeHeapVector<RefPtrWillBeMember<Node> >(nodes)); |
| } |
| -void EventHandler::adjustGesturePosition(const PlatformGestureEvent& gestureEvent, IntPoint& adjustedPoint) |
| +GestureEventWithHitTestResults EventHandler::targetGestureEvent(const PlatformGestureEvent& gestureEvent, bool readOnly) |
| { |
| - if (!shouldApplyTouchAdjustment(gestureEvent)) |
| - return; |
| + ASSERT(m_frame == m_frame->localFrameRoot()); |
| + // Scrolling events get hit tested per frame (like wheel events do). |
| + ASSERT(!gestureEvent.isScrollEvent()); |
| - Node* targetNode = 0; |
| - switch (gestureEvent.type()) { |
| - case PlatformEvent::GestureTap: |
| - case PlatformEvent::GestureTapUnconfirmed: |
| - case PlatformEvent::GestureTapDown: |
| - case PlatformEvent::GestureShowPress: |
| - bestClickableNodeForTouchPoint(gestureEvent.position(), IntSize(gestureEvent.area().width() / 2, gestureEvent.area().height() / 2), adjustedPoint, targetNode); |
| - break; |
| - case PlatformEvent::GestureLongPress: |
| - case PlatformEvent::GestureLongTap: |
| - case PlatformEvent::GestureTwoFingerTap: |
| - bestContextMenuNodeForTouchPoint(gestureEvent.position(), IntSize(gestureEvent.area().width() / 2, gestureEvent.area().height() / 2), adjustedPoint, targetNode); |
| - break; |
| - default: |
| - // FIXME: Implement handling for other types as needed. |
| - ASSERT_NOT_REACHED(); |
| + HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent | HitTestRequest::AllowFrameScrollbars; |
| + double activeInterval = 0; |
| + bool shouldKeepActiveForMinInterval = false; |
| + if (readOnly) { |
| + hitType |= HitTestRequest::ReadOnly; |
| + } else if (gestureEvent.type() == PlatformEvent::GestureShowPress |
| + || gestureEvent.type() == PlatformEvent::GestureTapUnconfirmed) { |
| + hitType |= HitTestRequest::Active; |
| + } else if (gestureEvent.type() == PlatformEvent::GestureTapDownCancel) { |
| + hitType |= HitTestRequest::Release; |
| + // A TapDownCancel received when no element is active shouldn't really be changing hover state. |
| + if (!m_frame->document()->activeHoverElement()) |
| + hitType |= HitTestRequest::ReadOnly; |
| + } else if (gestureEvent.type() == PlatformEvent::GestureTap) { |
| + hitType |= HitTestRequest::Release; |
| + // If the Tap is received very shortly after ShowPress, we want to |
| + // delay clearing of the active state so that it's visible to the user |
| + // for at least a couple of frames. |
| + activeInterval = WTF::currentTime() - m_lastShowPressTimestamp; |
| + shouldKeepActiveForMinInterval = m_lastShowPressTimestamp && activeInterval < minimumActiveInterval; |
| + if (shouldKeepActiveForMinInterval) |
| + hitType |= HitTestRequest::ReadOnly; |
| + } else { |
| + hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly; |
| + } |
| + |
| + // Perform the rect-based hit-test. Note that we don't yet apply hover/active state here |
| + // because we need to resolve touch adjustment first so that we apply hover/active it to |
| + // the final adjusted node. |
| + IntPoint hitTestPoint = m_frame->view()->windowToContents(gestureEvent.position()); |
| + IntSize touchRadius = gestureEvent.area(); |
| + touchRadius.scale(1.f / 2); |
| + HitTestResult hitTestResult = hitTestResultAtPoint(hitTestPoint, hitType | HitTestRequest::ReadOnly, touchRadius); |
| + |
| + // Adjust the location of the gesture to the most likely nearby node, as appropriate for the |
| + // type of event. |
| + PlatformGestureEvent adjustedEvent = gestureEvent; |
| + if (shouldApplyTouchAdjustment(gestureEvent)) { |
|
esprehn
2014/06/27 08:33:58
We should consider breaking this stuff up into sma
Rick Byers
2014/06/27 15:38:23
Done. Added getHitTypeForGestureType and applyTou
|
| + Node* adjustedNode = 0; |
| + IntPoint adjustedPoint = gestureEvent.position(); |
| + IntSize radius = gestureEvent.area(); |
| + radius.scale(1.f / 2); |
| + bool adjusted = false; |
| + switch (gestureEvent.type()) { |
| + case PlatformEvent::GestureTap: |
| + case PlatformEvent::GestureTapUnconfirmed: |
| + case PlatformEvent::GestureTapDown: |
| + case PlatformEvent::GestureShowPress: |
| + adjusted = bestClickableNodeForHitTestResult(hitTestResult, adjustedPoint, adjustedNode); |
|
esprehn
2014/06/27 08:33:58
If this was a helper you could just do
return bes
Rick Byers
2014/06/27 15:38:24
I've got a plan to unify the best*ForHitTestResult
|
| + break; |
| + case PlatformEvent::GestureLongPress: |
| + case PlatformEvent::GestureLongTap: |
| + case PlatformEvent::GestureTwoFingerTap: |
| + adjusted = bestContextMenuNodeForHitTestResult(hitTestResult, adjustedPoint, adjustedNode); |
| + break; |
| + default: |
| + // FIXME: Implement handling for other types as needed. |
| + ASSERT_NOT_REACHED(); |
| + } |
| + |
| + if (adjusted) { |
|
esprehn
2014/06/27 08:33:58
If it was in a helper you could just write:
if (a
Rick Byers
2014/06/27 15:38:24
Again I think I can make this even simpler, but it
|
| + hitTestResult.resolveRectBasedTest(adjustedNode, m_frame->view()->windowToContents(adjustedPoint)); |
| + adjustedEvent.applyTouchAdjustment(adjustedPoint); |
| + } |
| } |
| + |
| + // Now apply hover/active state to the final target. |
| + // FIXME: This is supposed to send mouseenter/mouseleave events, but doesn't because we |
| + // aren't passing a PlatformMouseEvent. |
| + HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent); |
| + if (!request.readOnly()) |
| + m_frame->document()->updateHoverActiveState(request, hitTestResult.innerElement()); |
| + |
| + if (shouldKeepActiveForMinInterval) { |
| + m_lastDeferredTapElement = hitTestResult.innerElement(); |
| + m_activeIntervalTimer.startOneShot(minimumActiveInterval - activeInterval, FROM_HERE); |
| + } |
| + |
| + return GestureEventWithHitTestResults(adjustedEvent, hitTestResult); |
| } |
| bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event) |
| @@ -2677,7 +2725,7 @@ bool EventHandler::sendContextMenuEventForKey() |
| return sendContextMenuEvent(mouseEvent); |
| } |
| -bool EventHandler::sendContextMenuEventForGesture(const PlatformGestureEvent& event) |
| +bool EventHandler::sendContextMenuEventForGesture(const GestureEventWithHitTestResults& targetedEvent) |
| { |
| #if OS(WIN) |
| PlatformEvent::Type eventType = PlatformEvent::MouseReleased; |
| @@ -2685,11 +2733,10 @@ bool EventHandler::sendContextMenuEventForGesture(const PlatformGestureEvent& ev |
| PlatformEvent::Type eventType = PlatformEvent::MousePressed; |
| #endif |
| - IntPoint adjustedPoint = event.position(); |
| - adjustGesturePosition(event, adjustedPoint); |
| - PlatformMouseEvent mouseEvent(adjustedPoint, event.globalPosition(), RightButton, eventType, 1, false, false, false, false, WTF::currentTime()); |
| + PlatformMouseEvent mouseEvent(targetedEvent.event().position(), targetedEvent.event().globalPosition(), RightButton, eventType, 1, false, false, false, false, WTF::currentTime()); |
| // To simulate right-click behavior, we send a right mouse down and then |
| // context menu event. |
| + // FIXME: Send HitTestResults to avoid redundant hit tests. |
| handleMousePressEvent(mouseEvent); |
| return sendContextMenuEvent(mouseEvent); |
| // We do not need to send a corresponding mouse release because in case of |