Index: third_party/WebKit/Source/core/input/GestureManager.cpp |
diff --git a/third_party/WebKit/Source/core/input/GestureManager.cpp b/third_party/WebKit/Source/core/input/GestureManager.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6ea4e40daedfc66c5c96ff6b065128a7785bb149 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/input/GestureManager.cpp |
@@ -0,0 +1,318 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "core/input/GestureManager.h" |
+ |
+#include "core/dom/Document.h" |
+#include "core/editing/SelectionController.h" |
+#include "core/events/GestureEvent.h" |
+#include "core/frame/FrameHost.h" |
+#include "core/frame/FrameView.h" |
+#include "core/frame/Settings.h" |
+#include "core/frame/VisualViewport.h" |
+#include "core/input/EventHandler.h" |
+#include "core/page/ChromeClient.h" |
+#include "core/page/Page.h" |
+ |
+namespace blink { |
+ |
+GestureManager::GestureManager(LocalFrame* frame, ScrollManager* scrollManager, |
+ PointerEventManager* pointerEventManager, |
+ SelectionController* selectionController) |
+: m_frame(frame) |
+, m_scrollManager(scrollManager) |
dtapuska
2016/07/13 12:19:28
This looks like a wacky indent. Is this what git c
Navid Zolghadr
2016/07/13 16:28:14
Done.
|
+, m_pointerEventManager(pointerEventManager) |
+, m_selectionController(selectionController) |
+{ |
+ clear(); |
+} |
+ |
+GestureManager::~GestureManager() |
+{ |
+} |
+ |
+void GestureManager::clear() |
+{ |
+ m_suppressMouseEventsFromGestures = false; |
+ m_longTapShouldInvokeContextMenu = false; |
+ m_lastShowPressTimestamp = 0; |
+} |
+ |
+DEFINE_TRACE(GestureManager) |
+{ |
+ visitor->trace(m_frame); |
+ visitor->trace(m_selectionController); |
+} |
+ |
+HitTestRequest::HitTestRequestType GestureManager::getHitTypeForGestureType(PlatformEvent::EventType type) |
+{ |
+ HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent; |
+ switch (type) { |
+ case PlatformEvent::GestureShowPress: |
+ case PlatformEvent::GestureTapUnconfirmed: |
+ return hitType | HitTestRequest::Active; |
+ case PlatformEvent::GestureTapDownCancel: |
+ // A TapDownCancel received when no element is active shouldn't really be changing hover state. |
+ if (!m_frame->document()->activeHoverElement()) |
+ hitType |= HitTestRequest::ReadOnly; |
+ return hitType | HitTestRequest::Release; |
+ case PlatformEvent::GestureTap: |
+ return hitType | HitTestRequest::Release; |
+ case PlatformEvent::GestureTapDown: |
+ case PlatformEvent::GestureLongPress: |
+ case PlatformEvent::GestureLongTap: |
+ case PlatformEvent::GestureTwoFingerTap: |
+ // FIXME: Shouldn't LongTap and TwoFingerTap clear the Active state? |
+ return hitType | HitTestRequest::Active | HitTestRequest::ReadOnly; |
+ default: |
+ NOTREACHED(); |
+ return hitType | HitTestRequest::Active | HitTestRequest::ReadOnly; |
+ } |
+} |
+ |
+WebInputEventResult GestureManager::handleGestureEventInFrame(const GestureEventWithHitTestResults& targetedEvent) |
+{ |
+ DCHECK(!targetedEvent.event().isScrollEvent()); |
+ |
+ Node* eventTarget = targetedEvent.hitTestResult().innerNode(); |
+ const PlatformGestureEvent& gestureEvent = targetedEvent.event(); |
+ |
+ if (m_scrollManager->canHandleGestureEvent(targetedEvent)) |
+ return WebInputEventResult::HandledSuppressed; |
+ |
+ if (eventTarget) { |
+ GestureEvent* gestureDomEvent = GestureEvent::create(eventTarget->document().domWindow(), gestureEvent); |
+ if (gestureDomEvent) { |
+ DispatchEventResult gestureDomEventResult = eventTarget->dispatchEvent(gestureDomEvent); |
+ if (gestureDomEventResult != DispatchEventResult::NotCanceled) { |
+ DCHECK(gestureDomEventResult != DispatchEventResult::CanceledByEventHandler); |
+ return EventHandler::toWebInputEventResult(gestureDomEventResult); |
+ } |
+ } |
+ } |
+ |
+ switch (gestureEvent.type()) { |
+ case PlatformEvent::GestureTapDown: |
+ return handleGestureTapDown(targetedEvent); |
+ case PlatformEvent::GestureTap: |
+ return handleGestureTap(targetedEvent); |
+ case PlatformEvent::GestureShowPress: |
+ return handleGestureShowPress(); |
+ case PlatformEvent::GestureLongPress: |
+ return handleGestureLongPress(targetedEvent); |
+ case PlatformEvent::GestureLongTap: |
+ return handleGestureLongTap(targetedEvent); |
+ case PlatformEvent::GestureTwoFingerTap: |
+ return m_frame->eventHandler().sendContextMenuEventForGesture(targetedEvent); |
+ case PlatformEvent::GesturePinchBegin: |
+ case PlatformEvent::GesturePinchEnd: |
+ case PlatformEvent::GesturePinchUpdate: |
+ case PlatformEvent::GestureTapDownCancel: |
+ case PlatformEvent::GestureTapUnconfirmed: |
+ break; |
+ default: |
+ NOTREACHED(); |
+ } |
+ |
+ return WebInputEventResult::NotHandled; |
+} |
+ |
+WebInputEventResult GestureManager::handleGestureTapDown(const GestureEventWithHitTestResults& targetedEvent) |
+{ |
+ m_suppressMouseEventsFromGestures = |
+ m_pointerEventManager->primaryPointerdownCanceled(targetedEvent.event().uniqueTouchEventId()); |
+ return WebInputEventResult::NotHandled; |
+} |
+ |
+WebInputEventResult GestureManager::handleGestureTap(const GestureEventWithHitTestResults& targetedEvent) |
+{ |
+ FrameView* frameView(m_frame->view()); |
+ const PlatformGestureEvent& gestureEvent = targetedEvent.event(); |
+ HitTestRequest::HitTestRequestType hitType = getHitTypeForGestureType(gestureEvent.type()); |
+ uint64_t preDispatchDomTreeVersion = m_frame->document()->domTreeVersion(); |
+ uint64_t preDispatchStyleVersion = m_frame->document()->styleVersion(); |
+ |
+ UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); |
+ |
+ HitTestResult currentHitTest = targetedEvent.hitTestResult(); |
+ |
+ // We use the adjusted position so the application isn't surprised to see a event with |
+ // co-ordinates outside the target's bounds. |
+ IntPoint adjustedPoint = frameView->rootFrameToContents(gestureEvent.position()); |
+ |
+ const unsigned modifiers = gestureEvent.getModifiers(); |
+ |
+ if (!m_suppressMouseEventsFromGestures) { |
+ PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.globalPosition(), |
+ NoButton, PlatformEvent::MouseMoved, /* clickCount */ 0, |
+ static_cast<PlatformEvent::Modifiers>(modifiers), |
+ PlatformMouseEvent::FromTouch, gestureEvent.timestamp(), WebPointerProperties::PointerType::Mouse); |
+ m_frame->eventHandler().dispatchMouseEvent(EventTypeNames::mousemove, currentHitTest.innerNode(), 0, fakeMouseMove); |
+ } |
+ |
+ // Do a new hit-test in case the mousemove event changed the DOM. |
+ // Note that if the original hit test wasn't over an element (eg. was over a scrollbar) we |
+ // don't want to re-hit-test because it may be in the wrong frame (and there's no way the page |
+ // could have seen the event anyway). |
+ // Also note that the position of the frame may have changed, so we need to recompute the content |
+ // co-ordinates (updating layout/style as hitTestResultAtPoint normally would). |
+ // FIXME: Use a hit-test cache to avoid unnecessary hit tests. http://crbug.com/398920 |
+ if (currentHitTest.innerNode()) { |
+ LocalFrame* mainFrame = m_frame->localFrameRoot(); |
+ if (mainFrame && mainFrame->view()) |
+ mainFrame->view()->updateLifecycleToCompositingCleanPlusScrolling(); |
+ adjustedPoint = frameView->rootFrameToContents(gestureEvent.position()); |
+ currentHitTest = EventHandler::hitTestResultInFrame(m_frame, adjustedPoint, hitType); |
+ } |
+ |
+ // Capture data for showUnhandledTapUIIfNeeded. |
+ Node* tappedNode = currentHitTest.innerNode(); |
+ IntPoint tappedPosition = gestureEvent.position(); |
+ Node* tappedNonTextNode = tappedNode; |
+ |
+ if (tappedNonTextNode && tappedNonTextNode->isTextNode()) |
+ tappedNonTextNode = FlatTreeTraversal::parent(*tappedNonTextNode); |
+ |
+ m_frame->eventHandler().setClickNode(tappedNonTextNode); |
+ |
+ PlatformMouseEvent fakeMouseDown(gestureEvent.position(), gestureEvent.globalPosition(), |
+ LeftButton, PlatformEvent::MousePressed, gestureEvent.tapCount(), |
+ static_cast<PlatformEvent::Modifiers>(modifiers | PlatformEvent::LeftButtonDown), |
+ PlatformMouseEvent::FromTouch, gestureEvent.timestamp(), WebPointerProperties::PointerType::Mouse); |
+ |
+ // TODO(mustaq): We suppress MEs plus all it's side effects. What would that |
+ // mean for for TEs? What's the right balance here? crbug.com/617255 |
+ WebInputEventResult mouseDownEventResult = WebInputEventResult::HandledSuppressed; |
+ if (!m_suppressMouseEventsFromGestures) { |
+ mouseDownEventResult = m_frame->eventHandler().dispatchMouseEvent(EventTypeNames::mousedown, currentHitTest.innerNode(), gestureEvent.tapCount(), fakeMouseDown); |
+ m_selectionController->initializeSelectionState(); |
+ if (mouseDownEventResult == WebInputEventResult::NotHandled) |
+ mouseDownEventResult = m_frame->eventHandler().handleMouseFocus(MouseEventWithHitTestResults(fakeMouseDown, currentHitTest), InputDeviceCapabilities::firesTouchEventsSourceCapabilities()); |
+ if (mouseDownEventResult == WebInputEventResult::NotHandled) |
+ mouseDownEventResult = m_frame->eventHandler().handleMousePressEvent(MouseEventWithHitTestResults(fakeMouseDown, currentHitTest)); |
+ } |
+ |
+ if (currentHitTest.innerNode()) { |
+ DCHECK(gestureEvent.type() == PlatformEvent::GestureTap); |
+ HitTestResult result = currentHitTest; |
+ result.setToShadowHostIfInUserAgentShadowRoot(); |
+ m_frame->chromeClient().onMouseDown(result.innerNode()); |
+ } |
+ |
+ // FIXME: Use a hit-test cache to avoid unnecessary hit tests. http://crbug.com/398920 |
+ if (currentHitTest.innerNode()) { |
+ LocalFrame* mainFrame = m_frame->localFrameRoot(); |
+ if (mainFrame && mainFrame->view()) |
+ mainFrame->view()->updateAllLifecyclePhases(); |
+ adjustedPoint = frameView->rootFrameToContents(gestureEvent.position()); |
+ currentHitTest = EventHandler::hitTestResultInFrame(m_frame, adjustedPoint, hitType); |
+ } |
+ |
+ PlatformMouseEvent fakeMouseUp(gestureEvent.position(), gestureEvent.globalPosition(), |
+ LeftButton, PlatformEvent::MouseReleased, gestureEvent.tapCount(), |
+ static_cast<PlatformEvent::Modifiers>(modifiers), |
+ PlatformMouseEvent::FromTouch, gestureEvent.timestamp(), WebPointerProperties::PointerType::Mouse); |
+ WebInputEventResult mouseUpEventResult = m_suppressMouseEventsFromGestures |
+ ? WebInputEventResult::HandledSuppressed |
+ : m_frame->eventHandler().dispatchMouseEvent(EventTypeNames::mouseup, currentHitTest.innerNode(), gestureEvent.tapCount(), fakeMouseUp); |
+ |
+ WebInputEventResult clickEventResult = WebInputEventResult::NotHandled; |
+ if (tappedNonTextNode) { |
+ if (currentHitTest.innerNode()) { |
+ // Updates distribution because a mouseup (or mousedown) event listener can make the |
+ // tree dirty at dispatchMouseEvent() invocation above. |
+ // Unless distribution is updated, commonAncestor would hit DCHECK. |
+ // Both tappedNonTextNode and currentHitTest.innerNode()) don't need to be updated |
+ // because commonAncestor() will exit early if their documents are different. |
+ tappedNonTextNode->updateDistribution(); |
+ Node* clickTargetNode = currentHitTest.innerNode()->commonAncestor(*tappedNonTextNode, EventHandler::parentForClickEvent); |
+ clickEventResult = m_frame->eventHandler().dispatchMouseEvent(EventTypeNames::click, clickTargetNode, gestureEvent.tapCount(), fakeMouseUp); |
+ } |
+ m_frame->eventHandler().setClickNode(nullptr); |
+ } |
+ |
+ if (mouseUpEventResult == WebInputEventResult::NotHandled) |
+ mouseUpEventResult = m_frame->eventHandler().handleMouseReleaseEvent(MouseEventWithHitTestResults(fakeMouseUp, currentHitTest)); |
+ m_frame->eventHandler().clearDragHeuristicState(); |
+ |
+ WebInputEventResult eventResult = EventHandler::mergeEventResult(EventHandler::mergeEventResult(mouseDownEventResult, mouseUpEventResult), clickEventResult); |
+ if (eventResult == WebInputEventResult::NotHandled && tappedNode && m_frame->page()) { |
+ bool domTreeChanged = preDispatchDomTreeVersion != m_frame->document()->domTreeVersion(); |
+ bool styleChanged = preDispatchStyleVersion != m_frame->document()->styleVersion(); |
+ |
+ IntPoint tappedPositionInViewport = frameHost()->visualViewport().rootFrameToViewport(tappedPosition); |
+ m_frame->chromeClient().showUnhandledTapUIIfNeeded(tappedPositionInViewport, tappedNode, domTreeChanged || styleChanged); |
+ } |
+ return eventResult; |
+} |
+ |
+WebInputEventResult GestureManager::handleGestureLongPress(const GestureEventWithHitTestResults& targetedEvent) |
+{ |
+ const PlatformGestureEvent& gestureEvent = targetedEvent.event(); |
+ |
+ // 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->eventHandler().handleDragDropIfPossible(targetedEvent)) { |
+ m_longTapShouldInvokeContextMenu = true; |
+ return WebInputEventResult::HandledSystem; |
+ } |
+ IntPoint hitTestPoint = m_frame->view()->rootFrameToContents(gestureEvent.position()); |
+ HitTestResult result = m_frame->eventHandler().hitTestResultAtPoint(hitTestPoint); |
+ if (m_selectionController->handleGestureLongPress(gestureEvent, result)) { |
+ m_frame->eventHandler().focusDocumentView(); |
+ return WebInputEventResult::HandledSystem; |
+ } |
+ |
+ return m_frame->eventHandler().sendContextMenuEventForGesture(targetedEvent); |
+} |
+ |
+WebInputEventResult GestureManager::handleGestureLongTap(const GestureEventWithHitTestResults& targetedEvent) |
+{ |
+#if !OS(ANDROID) |
+ if (m_longTapShouldInvokeContextMenu) { |
+ m_longTapShouldInvokeContextMenu = false; |
+ return m_frame->eventHandler().sendContextMenuEventForGesture(targetedEvent); |
+ } |
+#endif |
+ return WebInputEventResult::NotHandled; |
+} |
+ |
+WebInputEventResult GestureManager::handleGestureShowPress() |
+{ |
+ m_lastShowPressTimestamp = WTF::monotonicallyIncreasingTime(); |
+ |
+ FrameView* view = m_frame->view(); |
+ if (!view) |
+ return WebInputEventResult::NotHandled; |
+ if (ScrollAnimatorBase* scrollAnimator = view->existingScrollAnimator()) |
+ scrollAnimator->cancelAnimation(); |
+ const FrameView::ScrollableAreaSet* areas = view->scrollableAreas(); |
+ if (!areas) |
+ return WebInputEventResult::NotHandled; |
+ for (const ScrollableArea* scrollableArea : *areas) { |
+ ScrollAnimatorBase* animator = scrollableArea->existingScrollAnimator(); |
+ if (animator) |
+ animator->cancelAnimation(); |
+ } |
+ return WebInputEventResult::NotHandled; |
+} |
+ |
+ |
+FrameHost* GestureManager::frameHost() const |
+{ |
+ if (!m_frame->page()) |
+ return nullptr; |
+ |
+ return &m_frame->page()->frameHost(); |
+} |
+ |
+double GestureManager::getLastShowPressTimestamp() |
+{ |
+ return m_lastShowPressTimestamp; |
+} |
+ |
+} // namespace blink |