OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "core/input/GestureManager.h" | |
6 | |
7 #include "core/dom/Document.h" | |
8 #include "core/editing/SelectionController.h" | |
9 #include "core/events/GestureEvent.h" | |
10 #include "core/frame/FrameHost.h" | |
11 #include "core/frame/FrameView.h" | |
12 #include "core/frame/Settings.h" | |
13 #include "core/frame/VisualViewport.h" | |
14 #include "core/input/EventHandler.h" | |
15 #include "core/page/ChromeClient.h" | |
16 #include "core/page/Page.h" | |
17 | |
18 namespace blink { | |
19 | |
20 GestureManager::GestureManager(LocalFrame* frame, ScrollManager* scrollManager, | |
21 PointerEventManager* pointerEventManager, | |
22 SelectionController* selectionController) | |
23 : m_frame(frame) | |
24 , 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.
| |
25 , m_pointerEventManager(pointerEventManager) | |
26 , m_selectionController(selectionController) | |
27 { | |
28 clear(); | |
29 } | |
30 | |
31 GestureManager::~GestureManager() | |
32 { | |
33 } | |
34 | |
35 void GestureManager::clear() | |
36 { | |
37 m_suppressMouseEventsFromGestures = false; | |
38 m_longTapShouldInvokeContextMenu = false; | |
39 m_lastShowPressTimestamp = 0; | |
40 } | |
41 | |
42 DEFINE_TRACE(GestureManager) | |
43 { | |
44 visitor->trace(m_frame); | |
45 visitor->trace(m_selectionController); | |
46 } | |
47 | |
48 HitTestRequest::HitTestRequestType GestureManager::getHitTypeForGestureType(Plat formEvent::EventType type) | |
49 { | |
50 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent; | |
51 switch (type) { | |
52 case PlatformEvent::GestureShowPress: | |
53 case PlatformEvent::GestureTapUnconfirmed: | |
54 return hitType | HitTestRequest::Active; | |
55 case PlatformEvent::GestureTapDownCancel: | |
56 // A TapDownCancel received when no element is active shouldn't really b e changing hover state. | |
57 if (!m_frame->document()->activeHoverElement()) | |
58 hitType |= HitTestRequest::ReadOnly; | |
59 return hitType | HitTestRequest::Release; | |
60 case PlatformEvent::GestureTap: | |
61 return hitType | HitTestRequest::Release; | |
62 case PlatformEvent::GestureTapDown: | |
63 case PlatformEvent::GestureLongPress: | |
64 case PlatformEvent::GestureLongTap: | |
65 case PlatformEvent::GestureTwoFingerTap: | |
66 // FIXME: Shouldn't LongTap and TwoFingerTap clear the Active state? | |
67 return hitType | HitTestRequest::Active | HitTestRequest::ReadOnly; | |
68 default: | |
69 NOTREACHED(); | |
70 return hitType | HitTestRequest::Active | HitTestRequest::ReadOnly; | |
71 } | |
72 } | |
73 | |
74 WebInputEventResult GestureManager::handleGestureEventInFrame(const GestureEvent WithHitTestResults& targetedEvent) | |
75 { | |
76 DCHECK(!targetedEvent.event().isScrollEvent()); | |
77 | |
78 Node* eventTarget = targetedEvent.hitTestResult().innerNode(); | |
79 const PlatformGestureEvent& gestureEvent = targetedEvent.event(); | |
80 | |
81 if (m_scrollManager->canHandleGestureEvent(targetedEvent)) | |
82 return WebInputEventResult::HandledSuppressed; | |
83 | |
84 if (eventTarget) { | |
85 GestureEvent* gestureDomEvent = GestureEvent::create(eventTarget->docume nt().domWindow(), gestureEvent); | |
86 if (gestureDomEvent) { | |
87 DispatchEventResult gestureDomEventResult = eventTarget->dispatchEve nt(gestureDomEvent); | |
88 if (gestureDomEventResult != DispatchEventResult::NotCanceled) { | |
89 DCHECK(gestureDomEventResult != DispatchEventResult::CanceledByE ventHandler); | |
90 return EventHandler::toWebInputEventResult(gestureDomEventResult ); | |
91 } | |
92 } | |
93 } | |
94 | |
95 switch (gestureEvent.type()) { | |
96 case PlatformEvent::GestureTapDown: | |
97 return handleGestureTapDown(targetedEvent); | |
98 case PlatformEvent::GestureTap: | |
99 return handleGestureTap(targetedEvent); | |
100 case PlatformEvent::GestureShowPress: | |
101 return handleGestureShowPress(); | |
102 case PlatformEvent::GestureLongPress: | |
103 return handleGestureLongPress(targetedEvent); | |
104 case PlatformEvent::GestureLongTap: | |
105 return handleGestureLongTap(targetedEvent); | |
106 case PlatformEvent::GestureTwoFingerTap: | |
107 return m_frame->eventHandler().sendContextMenuEventForGesture(targetedEv ent); | |
108 case PlatformEvent::GesturePinchBegin: | |
109 case PlatformEvent::GesturePinchEnd: | |
110 case PlatformEvent::GesturePinchUpdate: | |
111 case PlatformEvent::GestureTapDownCancel: | |
112 case PlatformEvent::GestureTapUnconfirmed: | |
113 break; | |
114 default: | |
115 NOTREACHED(); | |
116 } | |
117 | |
118 return WebInputEventResult::NotHandled; | |
119 } | |
120 | |
121 WebInputEventResult GestureManager::handleGestureTapDown(const GestureEventWithH itTestResults& targetedEvent) | |
122 { | |
123 m_suppressMouseEventsFromGestures = | |
124 m_pointerEventManager->primaryPointerdownCanceled(targetedEvent.event(). uniqueTouchEventId()); | |
125 return WebInputEventResult::NotHandled; | |
126 } | |
127 | |
128 WebInputEventResult GestureManager::handleGestureTap(const GestureEventWithHitTe stResults& targetedEvent) | |
129 { | |
130 FrameView* frameView(m_frame->view()); | |
131 const PlatformGestureEvent& gestureEvent = targetedEvent.event(); | |
132 HitTestRequest::HitTestRequestType hitType = getHitTypeForGestureType(gestur eEvent.type()); | |
133 uint64_t preDispatchDomTreeVersion = m_frame->document()->domTreeVersion(); | |
134 uint64_t preDispatchStyleVersion = m_frame->document()->styleVersion(); | |
135 | |
136 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); | |
137 | |
138 HitTestResult currentHitTest = targetedEvent.hitTestResult(); | |
139 | |
140 // We use the adjusted position so the application isn't surprised to see a event with | |
141 // co-ordinates outside the target's bounds. | |
142 IntPoint adjustedPoint = frameView->rootFrameToContents(gestureEvent.positio n()); | |
143 | |
144 const unsigned modifiers = gestureEvent.getModifiers(); | |
145 | |
146 if (!m_suppressMouseEventsFromGestures) { | |
147 PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.g lobalPosition(), | |
148 NoButton, PlatformEvent::MouseMoved, /* clickCount */ 0, | |
149 static_cast<PlatformEvent::Modifiers>(modifiers), | |
150 PlatformMouseEvent::FromTouch, gestureEvent.timestamp(), WebPointerP roperties::PointerType::Mouse); | |
151 m_frame->eventHandler().dispatchMouseEvent(EventTypeNames::mousemove, cu rrentHitTest.innerNode(), 0, fakeMouseMove); | |
152 } | |
153 | |
154 // Do a new hit-test in case the mousemove event changed the DOM. | |
155 // Note that if the original hit test wasn't over an element (eg. was over a scrollbar) we | |
156 // don't want to re-hit-test because it may be in the wrong frame (and there 's no way the page | |
157 // could have seen the event anyway). | |
158 // Also note that the position of the frame may have changed, so we need to recompute the content | |
159 // co-ordinates (updating layout/style as hitTestResultAtPoint normally woul d). | |
160 // FIXME: Use a hit-test cache to avoid unnecessary hit tests. http://crbug. com/398920 | |
161 if (currentHitTest.innerNode()) { | |
162 LocalFrame* mainFrame = m_frame->localFrameRoot(); | |
163 if (mainFrame && mainFrame->view()) | |
164 mainFrame->view()->updateLifecycleToCompositingCleanPlusScrolling(); | |
165 adjustedPoint = frameView->rootFrameToContents(gestureEvent.position()); | |
166 currentHitTest = EventHandler::hitTestResultInFrame(m_frame, adjustedPoi nt, hitType); | |
167 } | |
168 | |
169 // Capture data for showUnhandledTapUIIfNeeded. | |
170 Node* tappedNode = currentHitTest.innerNode(); | |
171 IntPoint tappedPosition = gestureEvent.position(); | |
172 Node* tappedNonTextNode = tappedNode; | |
173 | |
174 if (tappedNonTextNode && tappedNonTextNode->isTextNode()) | |
175 tappedNonTextNode = FlatTreeTraversal::parent(*tappedNonTextNode); | |
176 | |
177 m_frame->eventHandler().setClickNode(tappedNonTextNode); | |
178 | |
179 PlatformMouseEvent fakeMouseDown(gestureEvent.position(), gestureEvent.globa lPosition(), | |
180 LeftButton, PlatformEvent::MousePressed, gestureEvent.tapCount(), | |
181 static_cast<PlatformEvent::Modifiers>(modifiers | PlatformEvent::LeftBut tonDown), | |
182 PlatformMouseEvent::FromTouch, gestureEvent.timestamp(), WebPointerPrope rties::PointerType::Mouse); | |
183 | |
184 // TODO(mustaq): We suppress MEs plus all it's side effects. What would that | |
185 // mean for for TEs? What's the right balance here? crbug.com/617255 | |
186 WebInputEventResult mouseDownEventResult = WebInputEventResult::HandledSuppr essed; | |
187 if (!m_suppressMouseEventsFromGestures) { | |
188 mouseDownEventResult = m_frame->eventHandler().dispatchMouseEvent(EventT ypeNames::mousedown, currentHitTest.innerNode(), gestureEvent.tapCount(), fakeMo useDown); | |
189 m_selectionController->initializeSelectionState(); | |
190 if (mouseDownEventResult == WebInputEventResult::NotHandled) | |
191 mouseDownEventResult = m_frame->eventHandler().handleMouseFocus(Mous eEventWithHitTestResults(fakeMouseDown, currentHitTest), InputDeviceCapabilities ::firesTouchEventsSourceCapabilities()); | |
192 if (mouseDownEventResult == WebInputEventResult::NotHandled) | |
193 mouseDownEventResult = m_frame->eventHandler().handleMousePressEvent (MouseEventWithHitTestResults(fakeMouseDown, currentHitTest)); | |
194 } | |
195 | |
196 if (currentHitTest.innerNode()) { | |
197 DCHECK(gestureEvent.type() == PlatformEvent::GestureTap); | |
198 HitTestResult result = currentHitTest; | |
199 result.setToShadowHostIfInUserAgentShadowRoot(); | |
200 m_frame->chromeClient().onMouseDown(result.innerNode()); | |
201 } | |
202 | |
203 // FIXME: Use a hit-test cache to avoid unnecessary hit tests. http://crbug. com/398920 | |
204 if (currentHitTest.innerNode()) { | |
205 LocalFrame* mainFrame = m_frame->localFrameRoot(); | |
206 if (mainFrame && mainFrame->view()) | |
207 mainFrame->view()->updateAllLifecyclePhases(); | |
208 adjustedPoint = frameView->rootFrameToContents(gestureEvent.position()); | |
209 currentHitTest = EventHandler::hitTestResultInFrame(m_frame, adjustedPoi nt, hitType); | |
210 } | |
211 | |
212 PlatformMouseEvent fakeMouseUp(gestureEvent.position(), gestureEvent.globalP osition(), | |
213 LeftButton, PlatformEvent::MouseReleased, gestureEvent.tapCount(), | |
214 static_cast<PlatformEvent::Modifiers>(modifiers), | |
215 PlatformMouseEvent::FromTouch, gestureEvent.timestamp(), WebPointerPrope rties::PointerType::Mouse); | |
216 WebInputEventResult mouseUpEventResult = m_suppressMouseEventsFromGestures | |
217 ? WebInputEventResult::HandledSuppressed | |
218 : m_frame->eventHandler().dispatchMouseEvent(EventTypeNames::mouseup, cu rrentHitTest.innerNode(), gestureEvent.tapCount(), fakeMouseUp); | |
219 | |
220 WebInputEventResult clickEventResult = WebInputEventResult::NotHandled; | |
221 if (tappedNonTextNode) { | |
222 if (currentHitTest.innerNode()) { | |
223 // Updates distribution because a mouseup (or mousedown) event liste ner can make the | |
224 // tree dirty at dispatchMouseEvent() invocation above. | |
225 // Unless distribution is updated, commonAncestor would hit DCHECK. | |
226 // Both tappedNonTextNode and currentHitTest.innerNode()) don't need to be updated | |
227 // because commonAncestor() will exit early if their documents are d ifferent. | |
228 tappedNonTextNode->updateDistribution(); | |
229 Node* clickTargetNode = currentHitTest.innerNode()->commonAncestor(* tappedNonTextNode, EventHandler::parentForClickEvent); | |
230 clickEventResult = m_frame->eventHandler().dispatchMouseEvent(EventT ypeNames::click, clickTargetNode, gestureEvent.tapCount(), fakeMouseUp); | |
231 } | |
232 m_frame->eventHandler().setClickNode(nullptr); | |
233 } | |
234 | |
235 if (mouseUpEventResult == WebInputEventResult::NotHandled) | |
236 mouseUpEventResult = m_frame->eventHandler().handleMouseReleaseEvent(Mou seEventWithHitTestResults(fakeMouseUp, currentHitTest)); | |
237 m_frame->eventHandler().clearDragHeuristicState(); | |
238 | |
239 WebInputEventResult eventResult = EventHandler::mergeEventResult(EventHandle r::mergeEventResult(mouseDownEventResult, mouseUpEventResult), clickEventResult) ; | |
240 if (eventResult == WebInputEventResult::NotHandled && tappedNode && m_frame- >page()) { | |
241 bool domTreeChanged = preDispatchDomTreeVersion != m_frame->document()-> domTreeVersion(); | |
242 bool styleChanged = preDispatchStyleVersion != m_frame->document()->styl eVersion(); | |
243 | |
244 IntPoint tappedPositionInViewport = frameHost()->visualViewport().rootFr ameToViewport(tappedPosition); | |
245 m_frame->chromeClient().showUnhandledTapUIIfNeeded(tappedPositionInViewp ort, tappedNode, domTreeChanged || styleChanged); | |
246 } | |
247 return eventResult; | |
248 } | |
249 | |
250 WebInputEventResult GestureManager::handleGestureLongPress(const GestureEventWit hHitTestResults& targetedEvent) | |
251 { | |
252 const PlatformGestureEvent& gestureEvent = targetedEvent.event(); | |
253 | |
254 // FIXME: Ideally we should try to remove the extra mouse-specific hit-tests here (re-using the | |
255 // supplied HitTestResult), but that will require some overhaul of the touch drag-and-drop code | |
256 // and LongPress is such a special scenario that it's unlikely to matter muc h in practice. | |
257 | |
258 m_longTapShouldInvokeContextMenu = false; | |
259 if (m_frame->eventHandler().handleDragDropIfPossible(targetedEvent)) { | |
260 m_longTapShouldInvokeContextMenu = true; | |
261 return WebInputEventResult::HandledSystem; | |
262 } | |
263 IntPoint hitTestPoint = m_frame->view()->rootFrameToContents(gestureEvent.po sition()); | |
264 HitTestResult result = m_frame->eventHandler().hitTestResultAtPoint(hitTestP oint); | |
265 if (m_selectionController->handleGestureLongPress(gestureEvent, result)) { | |
266 m_frame->eventHandler().focusDocumentView(); | |
267 return WebInputEventResult::HandledSystem; | |
268 } | |
269 | |
270 return m_frame->eventHandler().sendContextMenuEventForGesture(targetedEvent) ; | |
271 } | |
272 | |
273 WebInputEventResult GestureManager::handleGestureLongTap(const GestureEventWithH itTestResults& targetedEvent) | |
274 { | |
275 #if !OS(ANDROID) | |
276 if (m_longTapShouldInvokeContextMenu) { | |
277 m_longTapShouldInvokeContextMenu = false; | |
278 return m_frame->eventHandler().sendContextMenuEventForGesture(targetedEv ent); | |
279 } | |
280 #endif | |
281 return WebInputEventResult::NotHandled; | |
282 } | |
283 | |
284 WebInputEventResult GestureManager::handleGestureShowPress() | |
285 { | |
286 m_lastShowPressTimestamp = WTF::monotonicallyIncreasingTime(); | |
287 | |
288 FrameView* view = m_frame->view(); | |
289 if (!view) | |
290 return WebInputEventResult::NotHandled; | |
291 if (ScrollAnimatorBase* scrollAnimator = view->existingScrollAnimator()) | |
292 scrollAnimator->cancelAnimation(); | |
293 const FrameView::ScrollableAreaSet* areas = view->scrollableAreas(); | |
294 if (!areas) | |
295 return WebInputEventResult::NotHandled; | |
296 for (const ScrollableArea* scrollableArea : *areas) { | |
297 ScrollAnimatorBase* animator = scrollableArea->existingScrollAnimator(); | |
298 if (animator) | |
299 animator->cancelAnimation(); | |
300 } | |
301 return WebInputEventResult::NotHandled; | |
302 } | |
303 | |
304 | |
305 FrameHost* GestureManager::frameHost() const | |
306 { | |
307 if (!m_frame->page()) | |
308 return nullptr; | |
309 | |
310 return &m_frame->page()->frameHost(); | |
311 } | |
312 | |
313 double GestureManager::getLastShowPressTimestamp() | |
314 { | |
315 return m_lastShowPressTimestamp; | |
316 } | |
317 | |
318 } // namespace blink | |
OLD | NEW |