OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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/PointerEventManager.h" |
| 6 |
| 7 #include "core/dom/shadow/FlatTreeTraversal.h" |
| 8 #include "core/events/MouseEvent.h" |
| 9 #include "core/input/EventHandler.h" |
| 10 |
| 11 namespace blink { |
| 12 |
| 13 namespace { |
| 14 |
| 15 const AtomicString& pointerEventNameForTouchPointState(PlatformTouchPoint::State
state) |
| 16 { |
| 17 switch (state) { |
| 18 case PlatformTouchPoint::TouchReleased: |
| 19 return EventTypeNames::pointerup; |
| 20 case PlatformTouchPoint::TouchCancelled: |
| 21 return EventTypeNames::pointercancel; |
| 22 case PlatformTouchPoint::TouchPressed: |
| 23 return EventTypeNames::pointerdown; |
| 24 case PlatformTouchPoint::TouchMoved: |
| 25 return EventTypeNames::pointermove; |
| 26 case PlatformTouchPoint::TouchStationary: |
| 27 // Fall through to default |
| 28 default: |
| 29 ASSERT_NOT_REACHED(); |
| 30 return emptyAtom; |
| 31 } |
| 32 } |
| 33 |
| 34 const AtomicString& pointerEventNameForMouseEventName(const AtomicString& mouseE
ventName) |
| 35 { |
| 36 #define RETURN_CORRESPONDING_PE_NAME(eventSuffix) \ |
| 37 if (mouseEventName == EventTypeNames::mouse##eventSuffix) {\ |
| 38 return EventTypeNames::pointer##eventSuffix;\ |
| 39 } |
| 40 |
| 41 RETURN_CORRESPONDING_PE_NAME(down); |
| 42 RETURN_CORRESPONDING_PE_NAME(enter); |
| 43 RETURN_CORRESPONDING_PE_NAME(leave); |
| 44 RETURN_CORRESPONDING_PE_NAME(move); |
| 45 RETURN_CORRESPONDING_PE_NAME(out); |
| 46 RETURN_CORRESPONDING_PE_NAME(over); |
| 47 RETURN_CORRESPONDING_PE_NAME(up); |
| 48 |
| 49 #undef RETURN_CORRESPONDING_PE_NAME |
| 50 |
| 51 ASSERT_NOT_REACHED(); |
| 52 return emptyAtom; |
| 53 } |
| 54 |
| 55 bool isInDocument(PassRefPtrWillBeRawPtr<EventTarget> n) |
| 56 { |
| 57 return n && n->toNode() && n->toNode()->inDocument(); |
| 58 } |
| 59 |
| 60 WebInputEventResult dispatchPointerEvent( |
| 61 PassRefPtrWillBeRawPtr<EventTarget> target, |
| 62 PassRefPtrWillBeRawPtr<PointerEvent> pointerevent, |
| 63 bool checkForListener = false) |
| 64 { |
| 65 if (!RuntimeEnabledFeatures::pointerEventEnabled()) |
| 66 return WebInputEventResult::NotHandled; |
| 67 if (!checkForListener || target->hasEventListeners(pointerevent->type())) { |
| 68 bool dispatchResult = target->dispatchEvent(pointerevent); |
| 69 return EventHandler::eventToEventResult(pointerevent, dispatchResult); |
| 70 } |
| 71 return WebInputEventResult::NotHandled; |
| 72 } |
| 73 |
| 74 WebInputEventResult dispatchMouseEvent( |
| 75 PassRefPtrWillBeRawPtr<EventTarget> target, |
| 76 const AtomicString& mouseEventType, |
| 77 const PlatformMouseEvent& mouseEvent, |
| 78 PassRefPtrWillBeRawPtr<EventTarget> relatedTarget, |
| 79 int detail = 0, |
| 80 bool checkForListener = false) |
| 81 { |
| 82 if (target->toNode() |
| 83 && (!checkForListener || target->hasEventListeners(mouseEventType))) { |
| 84 RefPtrWillBeRawPtr<Node> targetNode = target->toNode(); |
| 85 RefPtrWillBeRawPtr<MouseEvent> event = MouseEvent::create(mouseEventType
, |
| 86 targetNode->document().domWindow(), mouseEvent, detail, |
| 87 relatedTarget ? relatedTarget->toNode() : nullptr); |
| 88 bool res = target->dispatchEvent(event); |
| 89 return EventHandler::eventToEventResult(event, res); |
| 90 } |
| 91 return WebInputEventResult::NotHandled; |
| 92 } |
| 93 |
| 94 } // namespace |
| 95 |
| 96 PassRefPtrWillBeRawPtr<Node> PointerEventManager::getEffectiveTargetForPointerEv
ent( |
| 97 PassRefPtrWillBeRawPtr<Node> target, |
| 98 PassRefPtrWillBeRawPtr<PointerEvent> pointerEvent) |
| 99 { |
| 100 // TODO(nzolghadr): Add APIs to set the capturing nodes and return the corre
ct node here |
| 101 (void) pointerEvent; |
| 102 return target; |
| 103 } |
| 104 |
| 105 // Sends node transition events (pointer|mouse)(out|leave|over|enter) to the cor
responding targets |
| 106 void PointerEventManager::sendNodeTransitionEvents( |
| 107 PassRefPtrWillBeRawPtr<Node> exitedNode, |
| 108 PassRefPtrWillBeRawPtr<Node> enteredNode, |
| 109 const PlatformMouseEvent& mouseEvent, |
| 110 PassRefPtrWillBeRawPtr<AbstractView> view) |
| 111 { |
| 112 // Pointer event type does not matter as it will be overridden in the sendNo
deTransitionEvents |
| 113 sendNodeTransitionEvents(exitedNode, enteredNode, |
| 114 m_pointerEventFactory.create(EventTypeNames::pointerout, mouseEvent, nul
lptr, view), |
| 115 mouseEvent, true); |
| 116 } |
| 117 |
| 118 void PointerEventManager::sendNodeTransitionEvents( |
| 119 PassRefPtrWillBeRawPtr<EventTarget> exitedTarget, |
| 120 PassRefPtrWillBeRawPtr<EventTarget> enteredTarget, |
| 121 PassRefPtrWillBeRawPtr<PointerEvent> pointerEvent, |
| 122 const PlatformMouseEvent& mouseEvent, bool sendMouseEvent) |
| 123 { |
| 124 if (exitedTarget == enteredTarget) |
| 125 return; |
| 126 |
| 127 // Dispatch pointerout/mouseout events |
| 128 if (isInDocument(exitedTarget)) { |
| 129 dispatchPointerEvent(exitedTarget, m_pointerEventFactory.create( |
| 130 pointerEvent, EventTypeNames::pointerout, enteredTarget)); |
| 131 if (sendMouseEvent) { |
| 132 dispatchMouseEvent(exitedTarget, |
| 133 EventTypeNames::mouseout, mouseEvent, enteredTarget); |
| 134 } |
| 135 } |
| 136 |
| 137 // Create lists of all exited/entered ancestors, locate the common ancestor |
| 138 WillBeHeapVector<RefPtrWillBeMember<Node>, 32> exitedAncestors; |
| 139 WillBeHeapVector<RefPtrWillBeMember<Node>, 32> enteredAncestors; |
| 140 if (isInDocument(exitedTarget)) { |
| 141 RefPtrWillBeRawPtr<Node> exitedNode = exitedTarget->toNode(); |
| 142 exitedNode->updateDistribution(); |
| 143 for (RefPtrWillBeRawPtr<Node> node = exitedNode; node; node = FlatTreeTr
aversal::parent(*node)) |
| 144 exitedAncestors.append(node); |
| 145 } |
| 146 |
| 147 if (isInDocument(enteredTarget)) { |
| 148 RefPtrWillBeRawPtr<Node> enteredNode = enteredTarget->toNode(); |
| 149 enteredNode->updateDistribution(); |
| 150 for (RefPtrWillBeRawPtr<Node> node = enteredNode; node; node = FlatTreeT
raversal::parent(*node)) |
| 151 enteredAncestors.append(node); |
| 152 } |
| 153 |
| 154 // A note on mouseenter and mouseleave: These are non-bubbling events, and t
hey are dispatched if there |
| 155 // is a capturing event handler on an ancestor or a normal event handler on
the element itself. This special |
| 156 // handling is necessary to avoid O(n^2) capturing event handler checks. |
| 157 // |
| 158 // Note, however, that this optimization can possibly cause some unanswere
d/missing/redundant mouseenter or |
| 159 // mouseleave events in certain contrived eventhandling scenarios, e.g., whe
n: |
| 160 // - the mouseleave handler for a node sets the only capturing-mouseleave-li
stener in its ancestor, or |
| 161 // - DOM mods in any mouseenter/mouseleave handler changes the common ancest
or of exited & entered nodes, etc. |
| 162 // We think the spec specifies a "frozen" state to avoid such corner cases (
check the discussion on "candidate event |
| 163 // listeners" at http://www.w3.org/TR/uievents), but our code below preserve
s one such behavior from past only to |
| 164 // match Firefox and IE behavior. |
| 165 // |
| 166 // TODO(mustaq): Confirm spec conformance, double-check with other browsers. |
| 167 |
| 168 size_t numExitedAncestors = exitedAncestors.size(); |
| 169 size_t numEnteredAncestors = enteredAncestors.size(); |
| 170 |
| 171 size_t exitedAncestorIndex = numExitedAncestors; |
| 172 size_t enteredAncestorIndex = numEnteredAncestors; |
| 173 for (size_t i = 0; i < numExitedAncestors; i++) { |
| 174 for (size_t j = 0; j < numEnteredAncestors; j++) { |
| 175 if (exitedAncestors[i] == enteredAncestors[j]) { |
| 176 exitedAncestorIndex = i; |
| 177 enteredAncestorIndex = j; |
| 178 break; |
| 179 } |
| 180 } |
| 181 if (exitedAncestorIndex < exitedAncestors.size()) |
| 182 break; |
| 183 } |
| 184 |
| 185 bool exitedNodeHasCapturingAncestor = false; |
| 186 for (size_t j = 0; j < exitedAncestors.size(); j++) { |
| 187 if (exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::mouse
leave) |
| 188 || (RuntimeEnabledFeatures::pointerEventEnabled() |
| 189 && exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::po
interleave))) |
| 190 exitedNodeHasCapturingAncestor = true; |
| 191 } |
| 192 |
| 193 // Dispatch pointerleave/mouseleave events, in child-to-parent order. |
| 194 for (size_t j = 0; j < exitedAncestorIndex; j++) { |
| 195 dispatchPointerEvent(exitedAncestors[j].get(), |
| 196 m_pointerEventFactory.create( |
| 197 pointerEvent, EventTypeNames::pointerleave, enteredTarget), |
| 198 !exitedNodeHasCapturingAncestor); |
| 199 if (sendMouseEvent) { |
| 200 dispatchMouseEvent(exitedAncestors[j].get(), |
| 201 EventTypeNames::mouseleave, mouseEvent, enteredTarget, |
| 202 0, !exitedNodeHasCapturingAncestor); |
| 203 } |
| 204 } |
| 205 |
| 206 // Dispatch pointerover/mouseover. |
| 207 if (isInDocument(enteredTarget)) { |
| 208 dispatchPointerEvent(enteredTarget, m_pointerEventFactory.create( |
| 209 pointerEvent, EventTypeNames::pointerover, exitedTarget)); |
| 210 if (sendMouseEvent) { |
| 211 dispatchMouseEvent(enteredTarget, |
| 212 EventTypeNames::mouseover, mouseEvent, exitedTarget); |
| 213 } |
| 214 } |
| 215 |
| 216 // Defer locating capturing pointeenter/mouseenter listener until /after/ di
spatching the leave events because |
| 217 // the leave handlers might set a capturing enter handler. |
| 218 bool enteredNodeHasCapturingAncestor = false; |
| 219 for (size_t i = 0; i < enteredAncestors.size(); i++) { |
| 220 if (enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::mous
eenter) |
| 221 || (RuntimeEnabledFeatures::pointerEventEnabled() |
| 222 && enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::p
ointerenter))) |
| 223 enteredNodeHasCapturingAncestor = true; |
| 224 } |
| 225 |
| 226 // Dispatch pointerenter/mouseenter events, in parent-to-child order. |
| 227 for (size_t i = enteredAncestorIndex; i > 0; i--) { |
| 228 dispatchPointerEvent(enteredAncestors[i-1].get(), |
| 229 m_pointerEventFactory.create( |
| 230 pointerEvent, EventTypeNames::pointerenter, exitedTarget), |
| 231 !enteredNodeHasCapturingAncestor); |
| 232 if (sendMouseEvent) { |
| 233 dispatchMouseEvent(enteredAncestors[i-1].get(), |
| 234 EventTypeNames::mouseenter, mouseEvent, exitedTarget, |
| 235 0, !enteredNodeHasCapturingAncestor); |
| 236 } |
| 237 } |
| 238 } |
| 239 |
| 240 void PointerEventManager::setNodeUnderPointer( |
| 241 PassRefPtrWillBeRawPtr<PointerEvent> pointerevent, |
| 242 PassRefPtrWillBeRawPtr<EventTarget> target) |
| 243 { |
| 244 if (m_nodeUnderPointer.contains(pointerevent->pointerId())) { |
| 245 sendNodeTransitionEvents(m_nodeUnderPointer.get( |
| 246 pointerevent->pointerId()), target, pointerevent); |
| 247 if (!target) |
| 248 m_nodeUnderPointer.remove(pointerevent->pointerId()); |
| 249 else |
| 250 m_nodeUnderPointer.set(pointerevent->pointerId(), target); |
| 251 } else if (target) { |
| 252 sendNodeTransitionEvents(nullptr, target, pointerevent); |
| 253 m_nodeUnderPointer.add(pointerevent->pointerId(), target); |
| 254 } |
| 255 } |
| 256 |
| 257 void PointerEventManager::sendTouchCancelPointerEvent(PassRefPtrWillBeRawPtr<Eve
ntTarget> target, |
| 258 const PlatformTouchPoint& point) |
| 259 { |
| 260 RefPtrWillBeRawPtr<PointerEvent> pointerEvent = |
| 261 m_pointerEventFactory.createPointerCancel(point); |
| 262 |
| 263 // TODO(nzolghadr): crbug.com/579553 dealing with implicit touch capturing v
s pointer event capturing |
| 264 target->dispatchEvent(pointerEvent.get()); |
| 265 |
| 266 m_pointerEventFactory.remove(pointerEvent); |
| 267 setNodeUnderPointer(pointerEvent, nullptr); |
| 268 } |
| 269 |
| 270 WebInputEventResult PointerEventManager::sendTouchPointerEvent( |
| 271 PassRefPtrWillBeRawPtr<EventTarget> target, |
| 272 const PlatformTouchPoint& touchPoint, PlatformEvent::Modifiers modifiers, |
| 273 const double width, const double height, |
| 274 const double clientX, const double clientY) |
| 275 { |
| 276 RefPtrWillBeRawPtr<PointerEvent> pointerEvent = |
| 277 m_pointerEventFactory.create( |
| 278 pointerEventNameForTouchPointState(touchPoint.state()), |
| 279 touchPoint, modifiers, width, height, clientX, clientY); |
| 280 |
| 281 setNodeUnderPointer(pointerEvent, target); |
| 282 |
| 283 // TODO(nzolghadr): crbug.com/579553 dealing with implicit touch capturing v
s pointer event capturing |
| 284 WebInputEventResult result = dispatchPointerEvent(target, pointerEvent.get()
); |
| 285 |
| 286 if (touchPoint.state() == PlatformTouchPoint::TouchReleased |
| 287 || touchPoint.state() == PlatformTouchPoint::TouchCancelled) { |
| 288 m_pointerEventFactory.remove(pointerEvent); |
| 289 setNodeUnderPointer(pointerEvent, nullptr); |
| 290 } |
| 291 |
| 292 return result; |
| 293 } |
| 294 |
| 295 WebInputEventResult PointerEventManager::sendMousePointerEvent( |
| 296 PassRefPtrWillBeRawPtr<Node> target, const AtomicString& mouseEventType, |
| 297 int clickCount, const PlatformMouseEvent& mouseEvent, |
| 298 PassRefPtrWillBeRawPtr<Node> relatedTarget, |
| 299 PassRefPtrWillBeRawPtr<AbstractView> view) |
| 300 { |
| 301 AtomicString pointerEventType = |
| 302 pointerEventNameForMouseEventName(mouseEventType); |
| 303 unsigned short pointerButtonsPressed = |
| 304 MouseEvent::platformModifiersToButtons(mouseEvent.modifiers()); |
| 305 |
| 306 // Make sure chorded buttons fire pointermove instead of pointerup/pointerdo
wn. |
| 307 if ((pointerEventType == EventTypeNames::pointerdown && (pointerButtonsPress
ed & ~MouseEvent::buttonToButtons(mouseEvent.button())) != 0) |
| 308 || (pointerEventType == EventTypeNames::pointerup && pointerButtonsPress
ed != 0)) |
| 309 pointerEventType = EventTypeNames::pointermove; |
| 310 |
| 311 RefPtrWillBeRawPtr<PointerEvent> pointerEvent = |
| 312 m_pointerEventFactory.create(pointerEventType, mouseEvent, |
| 313 relatedTarget, view); |
| 314 |
| 315 RefPtrWillBeRawPtr<Node> effectiveTarget = |
| 316 getEffectiveTargetForPointerEvent(target, pointerEvent); |
| 317 |
| 318 WebInputEventResult result = |
| 319 dispatchPointerEvent(effectiveTarget, pointerEvent); |
| 320 |
| 321 if (result != WebInputEventResult::NotHandled |
| 322 && pointerEventType == EventTypeNames::pointerdown) |
| 323 m_preventMouseEventForPointerTypeMouse = true; |
| 324 |
| 325 if (!m_preventMouseEventForPointerTypeMouse) { |
| 326 result = EventHandler::mergeEventResult(result, |
| 327 dispatchMouseEvent(effectiveTarget, mouseEventType, mouseEvent, |
| 328 nullptr, clickCount)); |
| 329 } |
| 330 |
| 331 return result; |
| 332 } |
| 333 |
| 334 PointerEventManager::PointerEventManager() |
| 335 { |
| 336 clear(); |
| 337 } |
| 338 |
| 339 PointerEventManager::~PointerEventManager() |
| 340 { |
| 341 } |
| 342 |
| 343 void PointerEventManager::clear() |
| 344 { |
| 345 m_preventMouseEventForPointerTypeMouse = false; |
| 346 m_pointerEventFactory.clear(); |
| 347 m_nodeUnderPointer.clear(); |
| 348 } |
| 349 |
| 350 void PointerEventManager::conditionallyEnableMouseEventForPointerTypeMouse( |
| 351 unsigned modifiers) |
| 352 { |
| 353 |
| 354 if (MouseEvent::platformModifiersToButtons(modifiers) == |
| 355 static_cast<unsigned short>(MouseEvent::Buttons::None)) |
| 356 m_preventMouseEventForPointerTypeMouse = false; |
| 357 } |
| 358 |
| 359 DEFINE_TRACE(PointerEventManager) |
| 360 { |
| 361 visitor->trace(m_nodeUnderPointer); |
| 362 } |
| 363 |
| 364 |
| 365 } // namespace blink |
OLD | NEW |