PointerEventHandler.cpp (62128B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "PointerEventHandler.h" 8 9 #include "EventStateManager.h" 10 #include "PointerEvent.h" 11 #include "PointerLockManager.h" 12 #include "mozilla/ConnectedAncestorTracker.h" 13 #include "mozilla/PresShell.h" 14 #include "mozilla/StaticPrefs_dom.h" 15 #include "mozilla/StaticPrefs_ui.h" 16 #include "mozilla/dom/BrowserChild.h" 17 #include "mozilla/dom/BrowserParent.h" 18 #include "mozilla/dom/Document.h" 19 #include "mozilla/dom/DocumentInlines.h" 20 #include "mozilla/dom/MouseEventBinding.h" 21 #include "nsIContentInlines.h" 22 #include "nsIFrame.h" 23 #include "nsIWeakReferenceUtils.h" 24 #include "nsRFPService.h" 25 #include "nsUserCharacteristics.h" 26 27 namespace mozilla { 28 // MouseLocation logs the mouse location and when/where enqueued synthesized 29 // mouse move is flushed. If you don't need all mouse location recording at 30 // eMouseMove, you can use MouseLocation:3,sync. Then, it's logged once per 31 // 50 times. Otherwise, if you need to log all eMouseMove locations, you can 32 // use MouseLocation:5,sync. 33 // Note that this is actually available only on debug builds for saving the 34 // runtime cost on opt builds. 35 LazyLogModule gLogMouseLocation("MouseLocation"); 36 // PointerLocation logs all pointer locations and when/where enqueued 37 // synthesized pointer move is flushed. If you don't need all pointer location 38 // recording at ePointerMove, you can use PointerLocation:3,sync. Then, it's 39 // logged once per 50 times. Otherwise, if you need to log all ePointerMove 40 // locations, you can use PointerLocation:5,sync. 41 // Note that this is actually available only on debug builds for saving the 42 // runtime cost on opt builds. 43 LazyLogModule gLogPointerLocation("PointerLocation"); 44 // Log the updates of sActivePointersIds. 45 LazyLogModule gLogActivePointers("ActivePointers"); 46 47 using namespace dom; 48 49 Maybe<int32_t> PointerEventHandler::sSpoofedPointerId; 50 StaticAutoPtr<PointerInfo> PointerEventHandler::sLastMouseInfo; 51 StaticRefPtr<nsIWeakReference> PointerEventHandler::sLastMousePresShell; 52 Maybe<uint32_t> PointerEventHandler::sLastPointerId; 53 54 // Keeps a map between pointerId and element that currently capturing pointer 55 // with such pointerId. If pointerId is absent in this map then nobody is 56 // capturing it. Additionally keep information about pending capturing content. 57 static nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>* 58 sPointerCaptureList; 59 60 // Keeps information about pointers such as pointerId, activeState, pointerType, 61 // primaryState 62 static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds; 63 64 const UniquePtr<PointerInfo>& PointerEventHandler::InsertOrUpdateActivePointer( 65 uint32_t aPointerId, UniquePtr<PointerInfo>&& aNewPointerInfo, 66 EventMessage aEventMessage, const char* aCallerName) { 67 const bool logIt = [&]() { 68 if (MOZ_LIKELY(!MOZ_LOG_TEST(gLogActivePointers, LogLevel::Info))) { 69 return false; 70 } 71 const PointerInfo* prevPointerInfo = sActivePointersIds->Get(aPointerId); 72 return !prevPointerInfo || 73 !prevPointerInfo->EqualsBasicPointerData(*aNewPointerInfo); 74 }(); 75 76 const UniquePtr<PointerInfo>& pointerInfo = 77 sActivePointersIds->InsertOrUpdate( 78 aPointerId, std::forward<UniquePtr<PointerInfo>>(aNewPointerInfo)); 79 if (MOZ_UNLIKELY(logIt)) { 80 MOZ_LOG( 81 gLogActivePointers, LogLevel::Info, 82 ("InsertOrUpdate: { pointerId=%u, active: %s, inputSource: %s, " 83 "primary: %s, fromTouchEvent: %s, synthesizedForTests: %s }, %s in " 84 "%s", 85 aPointerId, pointerInfo->mIsActive ? "Yes" : "No", 86 InputSourceToString(pointerInfo->mInputSource).get(), 87 pointerInfo->mIsPrimary ? "Yes" : "No", 88 pointerInfo->mFromTouchEvent ? "Yes" : "No", 89 pointerInfo->mIsSynthesizedForTests ? "Yes" : "No", 90 ToChar(aEventMessage), aCallerName)); 91 } 92 return pointerInfo; 93 } 94 95 void PointerEventHandler::RemoveActivePointer(uint32_t aPointerId, 96 EventMessage aEventMessage, 97 const char* aCallerName) { 98 MOZ_ASSERT_IF(sLastPointerId, *sLastPointerId != aPointerId); 99 100 sActivePointersIds->Remove(aPointerId); 101 MOZ_LOG( 102 gLogActivePointers, LogLevel::Info, 103 ("Remove: { pointerId=%u }, %s in %s, remaining %u pointers", aPointerId, 104 ToChar(aEventMessage), aCallerName, sActivePointersIds->Count())); 105 } 106 107 // Keeps track of which BrowserParent requested pointer capture for a pointer 108 // id. 109 static nsTHashMap<nsUint32HashKey, BrowserParent*>* 110 sPointerCaptureRemoteTargetTable = nullptr; 111 112 // Keep the capturing element at dispatching the last pointer up event to 113 // consider the following click, auxclick or contextmenu event target. 114 static StaticRefPtr<nsIWeakReference> 115 sPointerCapturingElementAtLastPointerUpEvent; 116 117 /* static */ 118 void PointerEventHandler::InitializeStatics() { 119 MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!"); 120 sPointerCaptureList = 121 new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>; 122 sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>; 123 if (XRE_IsParentProcess()) { 124 sPointerCaptureRemoteTargetTable = 125 new nsTHashMap<nsUint32HashKey, BrowserParent*>; 126 } 127 } 128 129 /* static */ 130 void PointerEventHandler::ReleaseStatics() { 131 MOZ_ASSERT(sPointerCaptureList, "ReleaseStatics called without Initialize!"); 132 delete sPointerCaptureList; 133 sPointerCaptureList = nullptr; 134 delete sActivePointersIds; 135 sActivePointersIds = nullptr; 136 sPointerCapturingElementAtLastPointerUpEvent = nullptr; 137 if (sPointerCaptureRemoteTargetTable) { 138 MOZ_ASSERT(XRE_IsParentProcess()); 139 delete sPointerCaptureRemoteTargetTable; 140 sPointerCaptureRemoteTargetTable = nullptr; 141 } 142 sLastMouseInfo = nullptr; 143 sLastMousePresShell = nullptr; 144 } 145 146 /* static */ 147 bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() { 148 return StaticPrefs::dom_w3c_pointer_events_implicit_capture(); 149 } 150 151 /* static */ 152 bool PointerEventHandler::ShouldDispatchClickEventOnCapturingElement( 153 const WidgetGUIEvent* aSourceEvent /* = nullptr */) { 154 if (!StaticPrefs:: 155 dom_w3c_pointer_events_dispatch_click_on_pointer_capturing_element()) { 156 return false; 157 } 158 if (!aSourceEvent || 159 !StaticPrefs:: 160 dom_w3c_pointer_events_dispatch_click_on_pointer_capturing_element_except_touch()) { 161 return true; 162 } 163 MOZ_ASSERT(aSourceEvent->mMessage == eMouseUp || 164 aSourceEvent->mMessage == ePointerUp || 165 aSourceEvent->mMessage == eTouchEnd); 166 // Pointer Events defines that `click` event's userEvent is the preceding 167 // `pointerup`. However, Chrome does not follow treat it as so when the 168 // `click` is caused by a tap. For the compatibility with Chrome, we should 169 // stop comforming to the spec until Chrome conforms to that. 170 if (aSourceEvent->mClass == eTouchEventClass) { 171 return false; 172 } 173 const WidgetMouseEvent* const sourceMouseEvent = aSourceEvent->AsMouseEvent(); 174 return sourceMouseEvent && 175 sourceMouseEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH; 176 } 177 178 /* static */ 179 void PointerEventHandler::RecordPointerState( 180 const nsPoint& aRefPoint, const WidgetMouseEvent& aMouseEvent) { 181 MOZ_ASSERT_IF(aMouseEvent.mMessage == eMouseMove || 182 aMouseEvent.mMessage == ePointerMove, 183 aMouseEvent.IsReal()); 184 185 PointerInfo* pointerInfo = sActivePointersIds->Get(aMouseEvent.pointerId); 186 if (!pointerInfo) { 187 // If there is no pointer info (i.e., no last pointer state too) and the 188 // input device is not stationary or the caller wants to clear the last 189 // state, we need to do nothing. 190 if (!aMouseEvent.InputSourceSupportsHover() || 191 aRefPoint == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { 192 return; 193 } 194 // If there is no PointerInfo, we need to add an inactive PointeInfo to 195 // store the state. 196 pointerInfo = InsertOrUpdateActivePointer( 197 aMouseEvent.pointerId, 198 MakeUnique<PointerInfo>( 199 PointerInfo::Active::No, aMouseEvent.mInputSource, 200 PointerInfo::Primary::Yes, 201 PointerInfo::FromTouchEvent::No, nullptr, nullptr, 202 static_cast<PointerInfo::SynthesizeForTests>( 203 aMouseEvent.mFlags.mIsSynthesizedForTests)), 204 aMouseEvent.mMessage, __func__) 205 .get(); 206 } 207 // If the input source is a stationary device and the point is defined, we may 208 // need to dispatch synthesized ePointerMove at the pointer later. So, in 209 // that case, we should store the data. 210 if (aMouseEvent.InputSourceSupportsHover() && 211 aRefPoint != nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { 212 pointerInfo->RecordLastState(aRefPoint, aMouseEvent); 213 #ifdef DEBUG 214 if (MOZ_LOG_TEST(gLogPointerLocation, LogLevel::Info)) { 215 static uint32_t sFrequentMessageCount = 0; 216 const bool isFrequentMessage = aMouseEvent.mMessage == ePointerMove; 217 if (!isFrequentMessage || 218 MOZ_LOG_TEST(gLogPointerLocation, LogLevel::Verbose) || 219 !(sFrequentMessageCount % 50)) { 220 MOZ_LOG( 221 gLogPointerLocation, 222 isFrequentMessage ? LogLevel::Debug : LogLevel::Info, 223 ("got %s on widget:%p at {%d, %d} (pointerId=%u, source=%s)\n", 224 ToChar(aMouseEvent.mMessage), aMouseEvent.mWidget.get(), 225 sLastMouseInfo->mLastRefPointInRootDoc.x, 226 sLastMouseInfo->mLastRefPointInRootDoc.y, aMouseEvent.pointerId, 227 InputSourceToString(aMouseEvent.mInputSource).get())); 228 } 229 if (isFrequentMessage) { 230 sFrequentMessageCount++; 231 } else { 232 // Let's log the next ePointerMove after the other messages. 233 sFrequentMessageCount = 0; 234 } 235 } 236 #endif // #ifdef DEBUG 237 } 238 // Otherwise, i.e., if it's not a stationary device or the caller wants to 239 // forget the point, we should clear the last position to abort to synthesize 240 // ePointerMove. 241 else { 242 pointerInfo->ClearLastState(); 243 MOZ_LOG_DEBUG_ONLY( 244 gLogPointerLocation, LogLevel::Info, 245 ("got %s on widget:%p, pointer location is cleared (pointerId=%u, " 246 "source=%s)\n", 247 ToChar(aMouseEvent.mMessage), aMouseEvent.mWidget.get(), 248 aMouseEvent.pointerId, 249 InputSourceToString(aMouseEvent.mInputSource).get())); 250 } 251 } 252 253 /* static */ 254 void PointerEventHandler::RecordMouseState( 255 PresShell& aRootPresShell, const WidgetMouseEvent& aMouseEvent) { 256 MOZ_ASSERT(aRootPresShell.IsRoot()); 257 if (!sLastMouseInfo) { 258 sLastMouseInfo = new PointerInfo(); 259 } 260 sLastMousePresShell = do_GetWeakReference(&aRootPresShell); 261 sLastMouseInfo->mLastRefPointInRootDoc = 262 aRootPresShell.GetEventLocation(aMouseEvent); 263 sLastMouseInfo->mLastTargetGuid = 264 layers::InputAPZContext::GetTargetLayerGuid(); 265 // FIXME: Don't trust the synthesized for tests flag of drag events. 266 if (aMouseEvent.mClass != eDragEventClass) { 267 sLastMouseInfo->mInputSource = aMouseEvent.mInputSource; 268 sLastMouseInfo->mIsSynthesizedForTests = 269 aMouseEvent.mFlags.mIsSynthesizedForTests; 270 } 271 #ifdef DEBUG 272 if (MOZ_LOG_TEST(gLogMouseLocation, LogLevel::Info)) { 273 static uint32_t sFrequentMessageCount = 0; 274 const bool isFrequentMessage = 275 aMouseEvent.mMessage == eMouseMove || aMouseEvent.mMessage == eDragOver; 276 if (!isFrequentMessage || 277 MOZ_LOG_TEST(gLogMouseLocation, LogLevel::Verbose) || 278 !(sFrequentMessageCount % 50)) { 279 MOZ_LOG( 280 gLogMouseLocation, 281 isFrequentMessage ? LogLevel::Debug : LogLevel::Info, 282 ("[ps=%p]got %s on widget:%p at {%d, %d} (pointerId=%u, source=%s)\n", 283 &aRootPresShell, ToChar(aMouseEvent.mMessage), 284 aMouseEvent.mWidget.get(), sLastMouseInfo->mLastRefPointInRootDoc.x, 285 sLastMouseInfo->mLastRefPointInRootDoc.y, aMouseEvent.pointerId, 286 InputSourceToString(aMouseEvent.mInputSource).get())); 287 } 288 if (isFrequentMessage) { 289 sFrequentMessageCount++; 290 } else { 291 // Let's log the next eMouseMove or eDragOver after the other 292 // messages. 293 sFrequentMessageCount = 0; 294 } 295 } 296 #endif // #ifdef DEBUG 297 } 298 299 /* static */ 300 void PointerEventHandler::ClearMouseState(PresShell& aRootPresShell, 301 const WidgetMouseEvent& aMouseEvent) { 302 MOZ_ASSERT(aRootPresShell.IsRoot()); 303 const RefPtr<PresShell> lastMousePresShell = 304 do_QueryReferent(sLastMousePresShell); 305 if (lastMousePresShell != &aRootPresShell) { 306 return; 307 } 308 sLastMouseInfo->ClearLastState(); 309 sLastMouseInfo->mLastTargetGuid = 310 layers::InputAPZContext::GetTargetLayerGuid(); 311 sLastMouseInfo->mInputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN; 312 sLastMouseInfo->mIsSynthesizedForTests = 313 aMouseEvent.mFlags.mIsSynthesizedForTests; 314 MOZ_LOG_DEBUG_ONLY(gLogMouseLocation, LogLevel::Info, 315 ("[ps=%p]got %s on widget:%p, mouse location is cleared " 316 "(pointerId=%u, source=%s)\n", 317 &aRootPresShell, ToChar(aMouseEvent.mMessage), 318 aMouseEvent.mWidget.get(), aMouseEvent.pointerId, 319 InputSourceToString(aMouseEvent.mInputSource).get())); 320 } 321 322 /* static */ 323 LazyLogModule& PointerEventHandler::MouseLocationLogRef() { 324 return gLogMouseLocation; 325 } 326 327 /* static */ 328 LazyLogModule& PointerEventHandler::PointerLocationLogRef() { 329 return gLogPointerLocation; 330 } 331 332 /* static */ 333 void PointerEventHandler::UpdatePointerActiveState(WidgetMouseEvent* aEvent, 334 nsIContent* aTargetContent) { 335 if (!aEvent) { 336 return; 337 } 338 switch (aEvent->mMessage) { 339 case eMouseEnterIntoWidget: { 340 const PointerInfo* const pointerInfo = GetPointerInfo(aEvent->pointerId); 341 if (aEvent->mFlags.mIsSynthesizedForTests) { 342 if (pointerInfo && !pointerInfo->mIsSynthesizedForTests) { 343 // Do not overwrite the PointerInfo which is set by user input with 344 // synthesized pointer move. 345 return; 346 } 347 } 348 349 // Do not update the last pointerId with eMouseEnterIntoWidget because it 350 // may be dispatched by widget when it receives a native event which is 351 // not required, e.g., when the pointer is not moved actually. Let's 352 // update sLastPointerId with the following ePointerMove, etc which should 353 // be dispatched immediately. Note that anyway EventStateManager does not 354 // handle eMouseEnterIntoWidget directly, it expects that new event is 355 // coming. 356 357 // In this case we have to know information about available mouse pointers 358 InsertOrUpdateActivePointer( 359 aEvent->pointerId, 360 MakeUnique<PointerInfo>(PointerInfo::Active::No, aEvent->mInputSource, 361 PointerInfo::Primary::Yes, 362 PointerInfo::FromTouchEvent::No, nullptr, 363 pointerInfo, 364 static_cast<PointerInfo::SynthesizeForTests>( 365 aEvent->mFlags.mIsSynthesizedForTests)), 366 aEvent->mMessage, __func__); 367 MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId); 368 break; 369 } 370 case ePointerMove: { 371 if (aEvent->IsReal()) { 372 UpdateLastPointerId(aEvent->pointerId, aEvent->mMessage); 373 } 374 // If the event is a synthesized mouse event, we should register the 375 // pointerId for the test if the pointer is not there. 376 if (!aEvent->mFlags.mIsSynthesizedForTests || 377 aEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_MOUSE) { 378 return; 379 } 380 const PointerInfo* const pointerInfo = GetPointerInfo(aEvent->pointerId); 381 if (pointerInfo) { 382 return; 383 } 384 InsertOrUpdateActivePointer( 385 aEvent->pointerId, 386 MakeUnique<PointerInfo>( 387 PointerInfo::Active::No, MouseEvent_Binding::MOZ_SOURCE_MOUSE, 388 PointerInfo::Primary::Yes, PointerInfo::FromTouchEvent::No, 389 nullptr, pointerInfo, PointerInfo::SynthesizeForTests::Yes), 390 aEvent->mMessage, __func__); 391 return; 392 } 393 case ePointerDown: 394 UpdateLastPointerId(aEvent->pointerId, aEvent->mMessage); 395 sPointerCapturingElementAtLastPointerUpEvent = nullptr; 396 // In this case we switch pointer to active state 397 if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { 398 // XXXedgar, test could possibly synthesize a mousedown event on a 399 // coordinate outside the browser window and cause aTargetContent to be 400 // nullptr, not sure if this also happens on real usage. 401 InsertOrUpdateActivePointer( 402 pointerEvent->pointerId, 403 MakeUnique<PointerInfo>( 404 PointerInfo::Active::Yes, *pointerEvent, 405 aTargetContent ? aTargetContent->OwnerDoc() : nullptr, 406 GetPointerInfo(aEvent->pointerId)), 407 pointerEvent->mMessage, __func__); 408 MaybeCacheSpoofedPointerID(pointerEvent->mInputSource, 409 pointerEvent->pointerId); 410 } 411 break; 412 case ePointerCancel: 413 // pointercancel means a pointer is unlikely to continue to produce 414 // pointer events. In that case, we should turn off active state or remove 415 // the pointer from active pointers. 416 case ePointerUp: 417 // In this case we remove information about pointer or turn off active 418 // state 419 if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { 420 if (pointerEvent->mInputSource != 421 MouseEvent_Binding::MOZ_SOURCE_TOUCH) { 422 UpdateLastPointerId(aEvent->pointerId, aEvent->mMessage); 423 InsertOrUpdateActivePointer( 424 pointerEvent->pointerId, 425 MakeUnique<PointerInfo>(PointerInfo::Active::No, *pointerEvent, 426 nullptr, 427 GetPointerInfo(aEvent->pointerId)), 428 pointerEvent->mMessage, __func__); 429 } else { 430 MaybeForgetLastPointerId(aEvent->pointerId, aEvent->mMessage); 431 // XXX If the PointerInfo is registered with same pointerId as actual 432 // pointer and the event is synthesized for tests, we unregister the 433 // pointer unexpectedly here. However, it should be rare and 434 // currently, we use only pointerId for the key. Therefore, we cannot 435 // do nothing without changing the key. 436 RemoveActivePointer(aEvent->pointerId, aEvent->mMessage, __func__); 437 } 438 } 439 break; 440 case eMouseExitFromWidget: 441 if (aEvent->mFlags.mIsSynthesizedForTests) { 442 const PointerInfo* const pointerInfo = 443 GetPointerInfo(aEvent->pointerId); 444 if (pointerInfo && !pointerInfo->mIsSynthesizedForTests) { 445 // Do not remove the PointerInfo which is set by user input with 446 // synthesized pointer move. 447 return; 448 } 449 } 450 MaybeForgetLastPointerId(aEvent->pointerId, aEvent->mMessage); 451 // In this case we have to remove information about disappeared mouse 452 // pointers 453 RemoveActivePointer(aEvent->pointerId, aEvent->mMessage, __func__); 454 break; 455 default: 456 MOZ_ASSERT_UNREACHABLE("event has invalid type"); 457 break; 458 } 459 } 460 461 /* static */ 462 void PointerEventHandler::RequestPointerCaptureById(uint32_t aPointerId, 463 Element* aElement) { 464 SetPointerCaptureById(aPointerId, aElement); 465 466 if (BrowserChild* browserChild = 467 BrowserChild::GetFrom(aElement->OwnerDoc()->GetDocShell())) { 468 browserChild->SendRequestPointerCapture( 469 aPointerId, 470 [aPointerId](bool aSuccess) { 471 if (!aSuccess) { 472 PointerEventHandler::ReleasePointerCaptureById(aPointerId); 473 } 474 }, 475 [](mozilla::ipc::ResponseRejectReason) {}); 476 } 477 } 478 479 /* static */ 480 void PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId, 481 Element* aElement) { 482 MOZ_ASSERT(aElement); 483 sPointerCaptureList->WithEntryHandle(aPointerId, [&](auto&& entry) { 484 if (entry) { 485 entry.Data()->mPendingElement = aElement; 486 } else { 487 entry.Insert(MakeUnique<PointerCaptureInfo>(aElement)); 488 } 489 }); 490 } 491 492 /* static */ 493 PointerCaptureInfo* PointerEventHandler::GetPointerCaptureInfo( 494 uint32_t aPointerId) { 495 PointerCaptureInfo* pointerCaptureInfo = nullptr; 496 sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo); 497 return pointerCaptureInfo; 498 } 499 500 /* static */ 501 void PointerEventHandler::ReleasePointerCaptureById(uint32_t aPointerId) { 502 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId); 503 if (pointerCaptureInfo) { 504 if (Element* pendingElement = pointerCaptureInfo->mPendingElement) { 505 if (BrowserChild* browserChild = BrowserChild::GetFrom( 506 pendingElement->OwnerDoc()->GetDocShell())) { 507 browserChild->SendReleasePointerCapture(aPointerId); 508 } 509 } 510 pointerCaptureInfo->mPendingElement = nullptr; 511 } 512 } 513 514 /* static */ 515 void PointerEventHandler::ReleaseAllPointerCapture() { 516 for (const auto& entry : *sPointerCaptureList) { 517 PointerCaptureInfo* data = entry.GetWeak(); 518 if (data && data->mPendingElement) { 519 ReleasePointerCaptureById(entry.GetKey()); 520 } 521 } 522 } 523 524 /* static */ 525 bool PointerEventHandler::SetPointerCaptureRemoteTarget( 526 uint32_t aPointerId, dom::BrowserParent* aBrowserParent) { 527 MOZ_ASSERT(XRE_IsParentProcess()); 528 MOZ_ASSERT(sPointerCaptureRemoteTargetTable); 529 MOZ_ASSERT(aBrowserParent); 530 531 if (PointerLockManager::GetLockedRemoteTarget()) { 532 return false; 533 } 534 535 BrowserParent* currentRemoteTarget = 536 PointerEventHandler::GetPointerCapturingRemoteTarget(aPointerId); 537 if (currentRemoteTarget && currentRemoteTarget != aBrowserParent) { 538 return false; 539 } 540 541 sPointerCaptureRemoteTargetTable->InsertOrUpdate(aPointerId, aBrowserParent); 542 return true; 543 } 544 545 /* static */ 546 void PointerEventHandler::ReleasePointerCaptureRemoteTarget( 547 BrowserParent* aBrowserParent) { 548 MOZ_ASSERT(XRE_IsParentProcess()); 549 MOZ_ASSERT(sPointerCaptureRemoteTargetTable); 550 MOZ_ASSERT(aBrowserParent); 551 552 sPointerCaptureRemoteTargetTable->RemoveIf([aBrowserParent]( 553 const auto& iter) { 554 BrowserParent* browserParent = iter.Data(); 555 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?"); 556 557 return aBrowserParent == browserParent; 558 }); 559 } 560 561 /* static */ 562 void PointerEventHandler::ReleasePointerCaptureRemoteTarget( 563 uint32_t aPointerId) { 564 MOZ_ASSERT(XRE_IsParentProcess()); 565 MOZ_ASSERT(sPointerCaptureRemoteTargetTable); 566 567 sPointerCaptureRemoteTargetTable->Remove(aPointerId); 568 } 569 570 /* static */ 571 BrowserParent* PointerEventHandler::GetPointerCapturingRemoteTarget( 572 uint32_t aPointerId) { 573 MOZ_ASSERT(XRE_IsParentProcess()); 574 MOZ_ASSERT(sPointerCaptureRemoteTargetTable); 575 576 return sPointerCaptureRemoteTargetTable->Get(aPointerId); 577 } 578 579 /* static */ 580 void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() { 581 MOZ_ASSERT(XRE_IsParentProcess()); 582 MOZ_ASSERT(sPointerCaptureRemoteTargetTable); 583 584 for (auto iter = sPointerCaptureRemoteTargetTable->Iter(); !iter.Done(); 585 iter.Next()) { 586 BrowserParent* browserParent = iter.Data(); 587 MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?"); 588 589 (void)browserParent->SendReleaseAllPointerCapture(); 590 iter.Remove(); 591 } 592 } 593 594 /* static */ 595 const PointerInfo* PointerEventHandler::GetPointerInfo(uint32_t aPointerId) { 596 return sActivePointersIds->Get(aPointerId); 597 } 598 599 /* static */ 600 const PointerInfo* PointerEventHandler::GetLastMouseInfo( 601 const PresShell* aRootPresShell /* = nullptr */) { 602 if (!sLastMousePresShell || !sLastMouseInfo) { 603 return nullptr; 604 } 605 if (aRootPresShell) { 606 const RefPtr<PresShell> lastMousePresShell = 607 do_QueryReferent(sLastMousePresShell); 608 if (lastMousePresShell != aRootPresShell) { 609 return nullptr; 610 } 611 } 612 return sLastMouseInfo; 613 } 614 615 /* static */ 616 void PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent* aEvent) { 617 switch (aEvent->mClass) { 618 case eMouseEventClass: 619 ProcessPointerCaptureForMouse(aEvent->AsMouseEvent()); 620 break; 621 case eTouchEventClass: 622 ProcessPointerCaptureForTouch(aEvent->AsTouchEvent()); 623 break; 624 default: 625 break; 626 } 627 } 628 629 /* static */ 630 void PointerEventHandler::ProcessPointerCaptureForMouse( 631 WidgetMouseEvent* aEvent) { 632 if (!ShouldGeneratePointerEventFromMouse(aEvent)) { 633 return; 634 } 635 636 PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId); 637 if (!info || info->mPendingElement == info->mOverrideElement) { 638 return; 639 } 640 WidgetPointerEvent localEvent(*aEvent); 641 InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent); 642 CheckPointerCaptureState(&localEvent); 643 } 644 645 /* static */ 646 void PointerEventHandler::ProcessPointerCaptureForTouch( 647 WidgetTouchEvent* aEvent) { 648 if (!ShouldGeneratePointerEventFromTouch(aEvent)) { 649 return; 650 } 651 652 for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) { 653 Touch* touch = aEvent->mTouches[i]; 654 if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) { 655 continue; 656 } 657 PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier()); 658 if (!info || info->mPendingElement == info->mOverrideElement) { 659 continue; 660 } 661 WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget); 662 InitPointerEventFromTouch(event, *aEvent, *touch); 663 CheckPointerCaptureState(&event); 664 } 665 } 666 667 /* static */ 668 void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) { 669 // Handle pending pointer capture before any pointer events except 670 // gotpointercapture / lostpointercapture. 671 if (!aEvent) { 672 return; 673 } 674 MOZ_ASSERT(aEvent->mClass == ePointerEventClass); 675 676 PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId); 677 678 if (!captureInfo || 679 captureInfo->mPendingElement == captureInfo->mOverrideElement) { 680 return; 681 } 682 683 const RefPtr<Element> overrideElement = captureInfo->mOverrideElement; 684 RefPtr<Element> pendingElement = captureInfo->mPendingElement; 685 686 // Update captureInfo before dispatching event since sPointerCaptureList may 687 // be changed in the pointer event listener. 688 captureInfo->mOverrideElement = captureInfo->mPendingElement; 689 if (captureInfo->Empty()) { 690 sPointerCaptureList->Remove(aEvent->pointerId); 691 captureInfo = nullptr; 692 } 693 694 if (overrideElement) { 695 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent, 696 overrideElement); 697 // A `lostpointercapture` event listener may have removed the new pointer 698 // capture element from the tree. Then, we shouldn't dispatch 699 // `gotpointercapture` on the node. 700 if (pendingElement && !pendingElement->IsInComposedDoc()) { 701 // We won't dispatch `gotpointercapture`, so, we should never fire 702 // `lostpointercapture` on it at processing the next pending pointer 703 // capture. 704 if ((captureInfo = GetPointerCaptureInfo(aEvent->pointerId)) && 705 captureInfo->mOverrideElement == pendingElement) { 706 captureInfo->mOverrideElement = nullptr; 707 if (captureInfo->Empty()) { 708 sPointerCaptureList->Remove(aEvent->pointerId); 709 captureInfo = nullptr; 710 } 711 } 712 pendingElement = nullptr; 713 } else { 714 captureInfo = nullptr; // Maybe destroyed 715 } 716 } 717 if (pendingElement) { 718 DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent, 719 pendingElement); 720 captureInfo = nullptr; // Maybe destroyed 721 } 722 723 // If nobody captures the pointer and the pointer will not be removed, we need 724 // to dispatch pointer boundary events if the pointer will keep hovering over 725 // somewhere even after the pointer is up. 726 // XXX Do we need to check whether there is new pending pointer capture 727 // element? But if there is, what should we do? 728 if (overrideElement && !pendingElement && aEvent->mWidget && 729 aEvent->mMessage != ePointerCancel && 730 (aEvent->mMessage != ePointerUp || aEvent->InputSourceSupportsHover())) { 731 aEvent->mSynthesizeMoveAfterDispatch = true; 732 } 733 } 734 735 /* static */ 736 void PointerEventHandler::SynthesizeMoveToDispatchBoundaryEvents( 737 const WidgetMouseEvent* aEvent) { 738 nsCOMPtr<nsIWidget> widget = aEvent->mWidget; 739 if (NS_WARN_IF(!widget)) { 740 return; 741 } 742 Maybe<WidgetMouseEvent> mouseMoveEvent; 743 Maybe<WidgetPointerEvent> pointerMoveEvent; 744 if (aEvent->mClass == eMouseEventClass) { 745 mouseMoveEvent.emplace(true, eMouseMove, aEvent->mWidget, 746 WidgetMouseEvent::eSynthesized); 747 } else if (aEvent->mClass == ePointerEventClass) { 748 pointerMoveEvent.emplace(true, ePointerMove, aEvent->mWidget); 749 pointerMoveEvent->mReason = WidgetMouseEvent::eSynthesized; 750 751 const WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent(); 752 MOZ_ASSERT(pointerEvent); 753 pointerMoveEvent->mIsPrimary = pointerEvent->mIsPrimary; 754 pointerMoveEvent->mFromTouchEvent = pointerEvent->mFromTouchEvent; 755 pointerMoveEvent->mWidth = pointerEvent->mWidth; 756 pointerMoveEvent->mHeight = pointerEvent->mHeight; 757 } else { 758 MOZ_ASSERT_UNREACHABLE( 759 "The event must be WidgetMouseEvent or WidgetPointerEvent"); 760 } 761 WidgetMouseEvent& event = 762 mouseMoveEvent ? mouseMoveEvent.ref() : pointerMoveEvent.ref(); 763 event.mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests; 764 event.mIgnoreCapturingContent = true; 765 event.mRefPoint = aEvent->mRefPoint; 766 event.mInputSource = aEvent->mInputSource; 767 event.mButtons = aEvent->mButtons; 768 event.mModifiers = aEvent->mModifiers; 769 event.convertToPointer = false; 770 event.AssignPointerHelperData(*aEvent); 771 772 // XXX If the pointer is already over a document in different process, we 773 // cannot synthesize the pointermove/mousemove on the document since 774 // dispatching events to the parent process is currently allowed only in 775 // automation. 776 widget->DispatchEvent(&event); 777 } 778 779 /* static */ 780 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame, 781 WidgetEvent* aEvent) { 782 MOZ_ASSERT(aEvent->mMessage == ePointerDown); 783 if (!aFrame || !IsPointerEventImplicitCaptureForTouchEnabled()) { 784 return; 785 } 786 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent(); 787 NS_WARNING_ASSERTION(pointerEvent, 788 "Call ImplicitlyCapturePointer with non-pointer event"); 789 if (!pointerEvent->mFromTouchEvent) { 790 // We only implicitly capture the pointer for touch device. 791 return; 792 } 793 nsIContent* target = aFrame->GetContentForEvent(aEvent); 794 while (target && !target->IsElement()) { 795 target = target->GetParent(); 796 } 797 if (NS_WARN_IF(!target)) { 798 return; 799 } 800 RequestPointerCaptureById(pointerEvent->pointerId, target->AsElement()); 801 } 802 803 /* static */ 804 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent) { 805 MOZ_ASSERT(aEvent); 806 if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) { 807 return; 808 } 809 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent(); 810 ReleasePointerCaptureById(pointerEvent->pointerId); 811 CheckPointerCaptureState(pointerEvent); 812 } 813 814 /* static */ 815 void PointerEventHandler::MaybeImplicitlyReleasePointerCapture( 816 WidgetGUIEvent* aEvent) { 817 MOZ_ASSERT(aEvent); 818 const EventMessage pointerEventMessage = 819 PointerEventHandler::ToPointerEventMessage(aEvent); 820 if (pointerEventMessage != ePointerUp && 821 pointerEventMessage != ePointerCancel) { 822 return; 823 } 824 PointerEventHandler::MaybeProcessPointerCapture(aEvent); 825 } 826 827 /* static */ 828 Element* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId) { 829 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId); 830 if (pointerCaptureInfo) { 831 return pointerCaptureInfo->mOverrideElement; 832 } 833 return nullptr; 834 } 835 836 /* static */ 837 Element* PointerEventHandler::GetPendingPointerCapturingElement( 838 uint32_t aPointerId) { 839 PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId); 840 if (pointerCaptureInfo) { 841 return pointerCaptureInfo->mPendingElement; 842 } 843 return nullptr; 844 } 845 846 /* static */ 847 Element* PointerEventHandler::GetPointerCapturingElement( 848 const WidgetGUIEvent* aEvent) { 849 return GetPointerCapturingElementInternal(CapturingState::Override, aEvent); 850 } 851 852 /* static */ 853 Element* PointerEventHandler::GetPendingPointerCapturingElement( 854 const WidgetGUIEvent* aEvent) { 855 return GetPointerCapturingElementInternal(CapturingState::Pending, aEvent); 856 } 857 858 /* static */ 859 Element* PointerEventHandler::GetPointerCapturingElementInternal( 860 CapturingState aCapturingState, const WidgetGUIEvent* aEvent) { 861 if ((aEvent->mClass != ePointerEventClass && 862 aEvent->mClass != eMouseEventClass) || 863 aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) { 864 // Pointer capture should only be applied to all pointer events and mouse 865 // events except ePointerDown and eMouseDown; 866 return nullptr; 867 } 868 869 // PointerEventHandler may synthesize ePointerMove event before releasing the 870 // mouse capture (it's done by a default handler of eMouseUp) after handling 871 // ePointerUp. Then, we need to dispatch pointer boundary events for the 872 // element under the pointer to emulate a pointer move after a pointer 873 // capture. Therefore, we need to ignore the capturing element if the event 874 // dispatcher requests it. 875 if (aEvent->ShouldIgnoreCapturingContent()) { 876 return nullptr; 877 } 878 879 const WidgetMouseEvent* const mouseEvent = aEvent->AsMouseEvent(); 880 if (!mouseEvent) { 881 return nullptr; 882 } 883 return aCapturingState == CapturingState::Pending 884 ? GetPendingPointerCapturingElement(mouseEvent->pointerId) 885 : GetPointerCapturingElement(mouseEvent->pointerId); 886 } 887 888 /* static */ 889 RefPtr<Element> 890 PointerEventHandler::GetPointerCapturingElementAtLastPointerUp() { 891 return do_QueryReferent(sPointerCapturingElementAtLastPointerUpEvent); 892 } 893 894 void PointerEventHandler::ReleasePointerCapturingElementAtLastPointerUp() { 895 sPointerCapturingElementAtLastPointerUpEvent = nullptr; 896 } 897 898 /* static */ 899 void PointerEventHandler::SetPointerCapturingElementAtLastPointerUp( 900 nsWeakPtr&& aPointerCapturingElement) { 901 sPointerCapturingElementAtLastPointerUpEvent = 902 aPointerCapturingElement.forget(); 903 } 904 905 /* static */ 906 void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent) { 907 MOZ_ASSERT(aContent); 908 // We should check that aChild does not contain pointer capturing elements. 909 // If it does we should release the pointer capture for the elements. 910 if (!sPointerCaptureList->IsEmpty() && aContent->IsElement()) { 911 for (const auto& entry : *sPointerCaptureList) { 912 PointerCaptureInfo* data = entry.GetWeak(); 913 if (data && data->mPendingElement && 914 data->mPendingElement->IsInclusiveDescendantOf(aContent)) { 915 ReleasePointerCaptureById(entry.GetKey()); 916 } 917 } 918 } 919 } 920 921 /* static */ 922 void PointerEventHandler::PreHandlePointerEventsPreventDefault( 923 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) { 924 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) { 925 return; 926 } 927 PointerInfo* pointerInfo = nullptr; 928 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) || 929 !pointerInfo) { 930 // The PointerInfo for active pointer should be added for normal cases. But 931 // in some cases, we may receive mouse events before adding PointerInfo in 932 // sActivePointersIds. (e.g. receive mousemove before 933 // eMouseEnterIntoWidget). In these cases, we could ignore them because they 934 // are not the events between a DefaultPrevented pointerdown and the 935 // corresponding pointerup. 936 return; 937 } 938 if (!pointerInfo->mPreventMouseEventByContent) { 939 return; 940 } 941 aMouseOrTouchEvent->PreventDefault(false); 942 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true; 943 if (aPointerEvent->mMessage == ePointerUp) { 944 pointerInfo->mPreventMouseEventByContent = false; 945 } 946 } 947 948 /* static */ 949 void PointerEventHandler::PostHandlePointerEventsPreventDefault( 950 WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) { 951 if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown || 952 !aPointerEvent->DefaultPreventedByContent()) { 953 return; 954 } 955 PointerInfo* pointerInfo = nullptr; 956 if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) || 957 !pointerInfo) { 958 // We already added the PointerInfo for active pointer when 959 // PresShell::HandleEvent handling pointerdown event. 960 #ifdef DEBUG 961 MOZ_CRASH("Got ePointerDown w/o active pointer info!!"); 962 #endif // #ifdef DEBUG 963 return; 964 } 965 // PreventDefault only applied for active pointers. 966 if (!pointerInfo->mIsActive) { 967 return; 968 } 969 aMouseOrTouchEvent->PreventDefault(false); 970 aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true; 971 pointerInfo->mPreventMouseEventByContent = true; 972 } 973 974 /* static */ 975 void PointerEventHandler::InitPointerEventFromMouse( 976 WidgetPointerEvent* aPointerEvent, const WidgetMouseEvent* aMouseEvent, 977 EventMessage aMessage) { 978 MOZ_ASSERT(aPointerEvent); 979 MOZ_ASSERT(aMouseEvent); 980 aPointerEvent->pointerId = aMouseEvent->pointerId; 981 aPointerEvent->mInputSource = aMouseEvent->mInputSource; 982 aPointerEvent->mMessage = aMessage; 983 aPointerEvent->mButton = aMouseEvent->mMessage == eMouseMove 984 ? MouseButton::eNotPressed 985 : aMouseEvent->mButton; 986 987 aPointerEvent->mButtons = aMouseEvent->mButtons; 988 aPointerEvent->mPressure = aMouseEvent->ComputeMouseButtonPressure(); 989 } 990 991 /* static */ 992 void PointerEventHandler::InitPointerEventFromTouch( 993 WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent, 994 const mozilla::dom::Touch& aTouch) { 995 // Use mButton/mButtons only when mButton got a value (from pen input) 996 int16_t button = aTouchEvent.mMessage == eTouchRawUpdate || 997 aTouchEvent.mMessage == eTouchMove 998 ? MouseButton::eNotPressed 999 : aTouchEvent.mButton != MouseButton::eNotPressed 1000 ? aTouchEvent.mButton 1001 : MouseButton::ePrimary; 1002 int16_t buttons = aTouchEvent.mMessage == eTouchEnd 1003 ? MouseButtonsFlag::eNoButtons 1004 : aTouchEvent.mButton != MouseButton::eNotPressed 1005 ? aTouchEvent.mButtons 1006 : MouseButtonsFlag::ePrimaryFlag; 1007 1008 // XXX: This doesn't support multi pen scenario (bug 1904865) 1009 if (aTouchEvent.mInputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH) { 1010 // Only the first touch would be the primary pointer. 1011 aPointerEvent.mIsPrimary = 1012 aTouchEvent.mMessage == eTouchStart 1013 ? !HasActiveTouchPointer() 1014 : GetPointerPrimaryState(aTouch.Identifier()); 1015 } 1016 aPointerEvent.pointerId = aTouch.Identifier(); 1017 aPointerEvent.mRefPoint = aTouch.mRefPoint; 1018 aPointerEvent.mModifiers = aTouchEvent.mModifiers; 1019 aPointerEvent.mWidth = aTouch.RadiusX(CallerType::System); 1020 aPointerEvent.mHeight = aTouch.RadiusY(CallerType::System); 1021 aPointerEvent.tiltX = aTouch.tiltX; 1022 aPointerEvent.tiltY = aTouch.tiltY; 1023 aPointerEvent.twist = aTouch.twist; 1024 aPointerEvent.mTimeStamp = aTouchEvent.mTimeStamp; 1025 aPointerEvent.mFlags = aTouchEvent.mFlags; 1026 aPointerEvent.mButton = button; 1027 aPointerEvent.mButtons = buttons; 1028 aPointerEvent.mInputSource = aTouchEvent.mInputSource; 1029 aPointerEvent.mFromTouchEvent = true; 1030 aPointerEvent.mPressure = aTouch.mForce; 1031 } 1032 1033 /* static */ 1034 void PointerEventHandler::InitCoalescedEventFromPointerEvent( 1035 WidgetPointerEvent& aCoalescedEvent, 1036 const WidgetPointerEvent& aSourceEvent) { 1037 aCoalescedEvent.mFlags.mCancelable = false; 1038 aCoalescedEvent.mFlags.mBubbles = false; 1039 1040 aCoalescedEvent.mTimeStamp = aSourceEvent.mTimeStamp; 1041 aCoalescedEvent.mRefPoint = aSourceEvent.mRefPoint; 1042 aCoalescedEvent.mModifiers = aSourceEvent.mModifiers; 1043 1044 // WidgetMouseEventBase 1045 aCoalescedEvent.mButton = aSourceEvent.mButton; 1046 aCoalescedEvent.mButtons = aSourceEvent.mButtons; 1047 aCoalescedEvent.mPressure = aSourceEvent.mPressure; 1048 aCoalescedEvent.mInputSource = aSourceEvent.mInputSource; 1049 1050 // pointerId, tiltX, tiltY, twist, tangentialPressure and convertToPointer. 1051 aCoalescedEvent.AssignPointerHelperData(aSourceEvent); 1052 1053 // WidgetPointerEvent 1054 aCoalescedEvent.mWidth = aSourceEvent.mWidth; 1055 aCoalescedEvent.mHeight = aSourceEvent.mHeight; 1056 aCoalescedEvent.mIsPrimary = aSourceEvent.mIsPrimary; 1057 aCoalescedEvent.mFromTouchEvent = aSourceEvent.mFromTouchEvent; 1058 } 1059 1060 /* static */ 1061 EventMessage PointerEventHandler::ToPointerEventMessage( 1062 const WidgetGUIEvent* aMouseOrTouchEvent) { 1063 MOZ_ASSERT(aMouseOrTouchEvent); 1064 1065 switch (aMouseOrTouchEvent->mMessage) { 1066 case eMouseRawUpdate: 1067 case eTouchRawUpdate: 1068 return ePointerRawUpdate; 1069 case eMouseMove: 1070 return ePointerMove; 1071 case eMouseUp: 1072 return aMouseOrTouchEvent->AsMouseEvent()->mButtons ? ePointerMove 1073 : ePointerUp; 1074 case eMouseDown: { 1075 const WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent(); 1076 return mouseEvent->mButtons & ~nsContentUtils::GetButtonsFlagForButton( 1077 mouseEvent->mButton) 1078 ? ePointerMove 1079 : ePointerDown; 1080 } 1081 case eTouchMove: 1082 return ePointerMove; 1083 case eTouchEnd: 1084 return ePointerUp; 1085 case eTouchStart: 1086 return ePointerDown; 1087 case eTouchCancel: 1088 case eTouchPointerCancel: 1089 return ePointerCancel; 1090 default: 1091 return eVoidEvent; 1092 } 1093 } 1094 1095 /* static */ 1096 bool PointerEventHandler::NeedToDispatchPointerRawUpdate( 1097 const Document* aDocument) { 1098 const nsPIDOMWindowInner* const innerWindow = 1099 aDocument ? aDocument->GetInnerWindow() : nullptr; 1100 return innerWindow && innerWindow->HasPointerRawUpdateEventListeners() && 1101 innerWindow->IsSecureContext(); 1102 } 1103 1104 /* static */ 1105 nsresult PointerEventHandler::DispatchPointerEventWithTarget( 1106 EventMessage aPointerEventMessage, 1107 const WidgetMouseEvent& aMouseOrPointerEvent, 1108 const AutoWeakFrame& aTargetWeakFrame, nsIContent* aTargetContent, 1109 nsEventStatus* aStatus /* = nullptr */) { 1110 Maybe<WidgetPointerEvent> pointerEvent; 1111 if (aMouseOrPointerEvent.mClass == ePointerEventClass) { 1112 pointerEvent.emplace(aPointerEventMessage, 1113 *aMouseOrPointerEvent.AsPointerEvent()); 1114 } else { 1115 pointerEvent.emplace(aMouseOrPointerEvent); 1116 PointerEventHandler::InitPointerEventFromMouse( 1117 pointerEvent.ptr(), &aMouseOrPointerEvent, ePointerCancel); 1118 } 1119 pointerEvent->convertToPointer = false; 1120 1121 return DispatchPointerEventWithTarget(pointerEvent.ref(), aTargetWeakFrame, 1122 aTargetContent, aStatus); 1123 } 1124 1125 /* static */ 1126 nsresult PointerEventHandler::DispatchPointerEventWithTarget( 1127 EventMessage aPointerEventMessage, const WidgetTouchEvent& aTouchEvent, 1128 size_t aTouchIndex, const AutoWeakFrame& aTargetWeakFrame, 1129 nsIContent* aTargetContent, nsEventStatus* aStatus /* = nullptr */) { 1130 WidgetPointerEvent pointerEvent(aTouchEvent.IsTrusted(), aPointerEventMessage, 1131 aTouchEvent.mWidget); 1132 PointerEventHandler::InitPointerEventFromTouch( 1133 pointerEvent, aTouchEvent, *aTouchEvent.mTouches[aTouchIndex]); 1134 pointerEvent.convertToPointer = false; 1135 1136 return DispatchPointerEventWithTarget(pointerEvent, aTargetWeakFrame, 1137 aTargetContent, aStatus); 1138 } 1139 1140 /* static */ 1141 nsresult PointerEventHandler::DispatchPointerEventWithTarget( 1142 WidgetPointerEvent& aPointerEvent, const AutoWeakFrame& aTargetWeakFrame, 1143 nsIContent* aTargetContent, nsEventStatus* aStatus /* = nullptr */) { 1144 if (aStatus) { 1145 *aStatus = nsEventStatus_eIgnore; 1146 } 1147 1148 AutoWeakFrame targetWeakFrame(aTargetWeakFrame); 1149 nsCOMPtr<nsIContent> targetContent = aTargetContent; 1150 if (targetWeakFrame) { 1151 MOZ_ASSERT_IF( 1152 targetContent, 1153 targetContent == targetWeakFrame->GetContentForEvent(&aPointerEvent)); 1154 if (!targetContent) { 1155 targetContent = targetWeakFrame->GetContentForEvent(&aPointerEvent); 1156 if (NS_WARN_IF(!targetContent)) { 1157 return NS_ERROR_FAILURE; 1158 } 1159 } 1160 } else if (NS_WARN_IF(!targetContent)) { 1161 return NS_ERROR_FAILURE; 1162 } 1163 const RefPtr<PresShell> presShell = 1164 targetWeakFrame ? targetWeakFrame->PresShell() 1165 : targetContent->OwnerDoc()->GetPresShell(); 1166 if (NS_WARN_IF(!presShell)) { 1167 return NS_ERROR_FAILURE; 1168 } 1169 1170 // If the event is not a gotpointercapture, lostpointercapture, click, 1171 // auxclick or contextmenu event, run the process pending pointer capture 1172 // steps for this PointerEvent. 1173 switch (aPointerEvent.mMessage) { 1174 case ePointerGotCapture: 1175 case ePointerLostCapture: 1176 case ePointerClick: 1177 case ePointerAuxClick: 1178 case eContextMenu: 1179 break; 1180 default: { 1181 Maybe<AutoConnectedAncestorTracker> trackTargetContent; 1182 if (targetContent->IsInComposedDoc()) { 1183 trackTargetContent.emplace(*targetContent); 1184 } 1185 CheckPointerCaptureState(&aPointerEvent); 1186 // If the event target was disconnected from the document, we should use 1187 // its connected ancestor as the target of aPointerEvent. 1188 if (trackTargetContent && trackTargetContent->ContentWasRemoved()) { 1189 MOZ_ASSERT(!targetWeakFrame); 1190 targetContent = trackTargetContent->GetConnectedContent(); 1191 if (NS_WARN_IF(!targetContent)) { 1192 targetWeakFrame = nullptr; 1193 // FIXME: If we lost the target content with dispatching 1194 // ePointerGotCapture or ePointerLostCapture event, we may need to 1195 // retarget the pointer event to the Document. 1196 return NS_ERROR_FAILURE; 1197 } 1198 } 1199 break; 1200 } 1201 } 1202 1203 // The active document of the pointerId and pointer capture for the pointerId 1204 // should be handled by EventStateManager::PreHandleEvent() immediately before 1205 // dispatching the event to the DOM. 1206 1207 // Pointer boundary events should be handled by 1208 // EventStateManager::PreHandleEvent() too. 1209 1210 nsEventStatus dummyStatus = nsEventStatus_eIgnore; 1211 nsresult rv = presShell->HandleEventWithTarget( 1212 &aPointerEvent, targetWeakFrame, targetContent, 1213 aStatus ? aStatus : &dummyStatus); 1214 1215 // EventStateManager must have store the last `pointerover` target at handling 1216 // the pointer boundary events. So, we need to do nothing here. 1217 1218 return rv; 1219 } 1220 1221 /* static */ 1222 void PointerEventHandler::DispatchPointerFromMouseOrTouch( 1223 PresShell* aShell, nsIFrame* aEventTargetFrame, 1224 nsIContent* aEventTargetContent, Element* aPointerCapturingElement, 1225 WidgetGUIEvent* aMouseOrTouchEvent, bool aDontRetargetEvents, 1226 nsEventStatus* aStatus, 1227 nsIContent** aMouseOrTouchEventTarget /* = nullptr */) { 1228 MOZ_ASSERT(aEventTargetFrame || aEventTargetContent); 1229 MOZ_ASSERT(aMouseOrTouchEvent); 1230 1231 nsWeakPtr pointerCapturingElementWeak = 1232 do_GetWeakReference(aPointerCapturingElement); 1233 EventMessage pointerMessage = eVoidEvent; 1234 if (aMouseOrTouchEvent->mClass == eMouseEventClass) { 1235 WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent(); 1236 // Don't dispatch pointer events caused by a mouse when simulating touch 1237 // devices in RDM. 1238 Document* doc = aShell->GetDocument(); 1239 if (!doc) { 1240 return; 1241 } 1242 1243 BrowsingContext* bc = doc->GetBrowsingContext(); 1244 if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled && 1245 bc->Top()->InRDMPane()) { 1246 return; 1247 } 1248 1249 // If it is not mouse then it is likely will come as touch event. 1250 if (!mouseEvent->convertToPointer) { 1251 return; 1252 } 1253 1254 // Normal synthesized mouse move events are marked as "not convert to 1255 // pointer" by PresShell::ProcessSynthMouseOrPointerMoveEvent(). However: 1256 // 1. if the event is synthesized via nsIDOMWindowUtils, it's not marked as 1257 // so because there is no synthesized pointer move dispatcher. So, we need 1258 // to dispatch synthesized pointer move from here. This path may be used by 1259 // mochitests which check the synthesized mouse/pointer boundary event 1260 // behavior. 1261 // 2. if the event comes from another process and our content will be moved 1262 // underneath the mouse cursor. In this case, we should handle preceding 1263 // ePointerMove. 1264 // FIXME: In the latter case, we may need to synthesize ePointerMove for the 1265 // other pointers too. 1266 if (mouseEvent->IsSynthesized()) { 1267 if (!StaticPrefs:: 1268 dom_event_pointer_boundary_dispatch_when_layout_change() || 1269 !mouseEvent->InputSourceSupportsHover()) { 1270 return; 1271 } 1272 // So, if the pointer is captured, we don't need to dispatch pointer 1273 // boundary events since pointer boundary events should be fired before 1274 // gotpointercapture. 1275 PointerCaptureInfo* const captureInfo = 1276 GetPointerCaptureInfo(mouseEvent->pointerId); 1277 if (captureInfo && captureInfo->mOverrideElement) { 1278 return; 1279 } 1280 } 1281 1282 pointerMessage = PointerEventHandler::ToPointerEventMessage(mouseEvent); 1283 if (pointerMessage == eVoidEvent) { 1284 return; 1285 } 1286 #ifdef DEBUG 1287 if (pointerMessage == ePointerRawUpdate) { 1288 const nsIContent* const targetContent = 1289 aEventTargetContent ? aEventTargetContent 1290 : aEventTargetFrame->GetContent(); 1291 NS_ASSERTION(targetContent, "Where do we want to try to dispatch?"); 1292 if (targetContent) { 1293 NS_ASSERTION( 1294 targetContent->IsInComposedDoc(), 1295 nsPrintfCString("Do we want to dispatch ePointerRawUpdate onto " 1296 "disconnected content? (targetContent=%s)", 1297 ToString(*targetContent).c_str()) 1298 .get()); 1299 if (!NeedToDispatchPointerRawUpdate(targetContent->OwnerDoc())) { 1300 NS_ASSERTION( 1301 false, 1302 nsPrintfCString( 1303 "Did we fail to retarget the document? (targetContent=%s)", 1304 ToString(*targetContent).c_str()) 1305 .get()); 1306 } 1307 } 1308 } 1309 #endif // #ifdef DEBUG 1310 WidgetPointerEvent event(*mouseEvent); 1311 InitPointerEventFromMouse(&event, mouseEvent, pointerMessage); 1312 event.convertToPointer = mouseEvent->convertToPointer = false; 1313 RefPtr<PresShell> shell(aShell); 1314 if (!aEventTargetFrame) { 1315 shell = PresShell::GetShellForEventTarget(nullptr, aEventTargetContent); 1316 if (!shell) { 1317 return; 1318 } 1319 } 1320 PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); 1321 // Dispatch pointer event to the same target which is found by the 1322 // corresponding mouse event. 1323 shell->HandleEventWithTarget(&event, aEventTargetFrame, aEventTargetContent, 1324 aStatus, true, aMouseOrTouchEventTarget); 1325 PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); 1326 // If pointer capture is released, we need to synthesize eMouseMove to 1327 // dispatch mouse boundary events later. 1328 mouseEvent->mSynthesizeMoveAfterDispatch |= 1329 event.mSynthesizeMoveAfterDispatch; 1330 } else if (aMouseOrTouchEvent->mClass == eTouchEventClass) { 1331 WidgetTouchEvent* touchEvent = aMouseOrTouchEvent->AsTouchEvent(); 1332 // loop over all touches and dispatch pointer events on each touch 1333 // copy the event 1334 pointerMessage = PointerEventHandler::ToPointerEventMessage(touchEvent); 1335 if (pointerMessage == eVoidEvent) { 1336 return; 1337 } 1338 // If the touch is a single tap release, we will dispatch click or auxclick 1339 // event later unless it's suppressed. The event target should be the 1340 // pointer capturing element right now, i.e., at dispatching ePointerUp. 1341 // Although we cannot know whether the touch is a single tap here, we should 1342 // store the last touch pointer capturing element. If this is not a single 1343 // tap end, the stored element will be ignored due to not dispatching click 1344 // nor auxclick. 1345 if (touchEvent->mMessage == eTouchEnd && 1346 touchEvent->mTouches.Length() == 1) { 1347 MOZ_ASSERT(!pointerCapturingElementWeak); 1348 pointerCapturingElementWeak = do_GetWeakReference( 1349 GetPointerCapturingElement(touchEvent->mTouches[0]->Identifier())); 1350 } 1351 RefPtr<PresShell> shell(aShell); 1352 for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) { 1353 Touch* touch = touchEvent->mTouches[i]; 1354 if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) { 1355 continue; 1356 } 1357 1358 WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage, 1359 touchEvent->mWidget); 1360 1361 InitPointerEventFromTouch(event, *touchEvent, *touch); 1362 event.convertToPointer = touch->convertToPointer = false; 1363 event.mCoalescedWidgetEvents = touch->mCoalescedWidgetEvents; 1364 if (aMouseOrTouchEvent->mMessage == eTouchStart) { 1365 // We already did hit test for touchstart in PresShell. We should 1366 // dispatch pointerdown to the same target as touchstart. 1367 nsCOMPtr<nsIContent> content = 1368 nsIContent::FromEventTargetOrNull(touch->mTarget); 1369 if (!content) { 1370 continue; 1371 } 1372 1373 nsIFrame* frame = content->GetPrimaryFrame(); 1374 shell = PresShell::GetShellForEventTarget(frame, content); 1375 if (!shell) { 1376 continue; 1377 } 1378 1379 PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); 1380 shell->HandleEventWithTarget(&event, frame, content, aStatus, true, 1381 aMouseOrTouchEventTarget); 1382 PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); 1383 } else { 1384 // We didn't hit test for other touch events. Spec doesn't mention that 1385 // all pointer events should be dispatched to the same target as their 1386 // corresponding touch events. Call PresShell::HandleEvent so that we do 1387 // hit test for pointer events. 1388 // FIXME: If aDontRetargetEvents is false and the event is fired on 1389 // different document, we cannot track the pointer event target when 1390 // it's removed from the tree. 1391 PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); 1392 shell->HandleEvent(aEventTargetFrame, &event, aDontRetargetEvents, 1393 aStatus); 1394 PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); 1395 } 1396 } 1397 } 1398 // If we dispatched an ePointerUp event while an element capturing the 1399 // pointer, we should keep storing it to consider click, auxclick and 1400 // contextmenu event target later. 1401 if (!aShell->IsDestroying() && pointerMessage == ePointerUp && 1402 pointerCapturingElementWeak) { 1403 SetPointerCapturingElementAtLastPointerUp( 1404 std::move(pointerCapturingElementWeak)); 1405 } 1406 } 1407 1408 /* static */ 1409 void PointerEventHandler::NotifyDestroyPresContext( 1410 nsPresContext* aPresContext) { 1411 // Clean up pointer capture info 1412 for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) { 1413 PointerCaptureInfo* data = iter.UserData(); 1414 MOZ_ASSERT(data, "how could we have a null PointerCaptureInfo here?"); 1415 if (data->mPendingElement && 1416 data->mPendingElement->GetPresContext(Element::eForComposedDoc) == 1417 aPresContext) { 1418 data->mPendingElement = nullptr; 1419 } 1420 if (data->mOverrideElement && 1421 data->mOverrideElement->GetPresContext(Element::eForComposedDoc) == 1422 aPresContext) { 1423 data->mOverrideElement = nullptr; 1424 } 1425 if (data->Empty()) { 1426 iter.Remove(); 1427 } 1428 } 1429 if (const RefPtr<Element> capturingElementAtLastPointerUp = 1430 GetPointerCapturingElementAtLastPointerUp()) { 1431 // The pointer capturing element may belong to different document from the 1432 // destroying nsPresContext. Check whether the composed document's 1433 // nsPresContext is the destroying one or not. 1434 if (capturingElementAtLastPointerUp->GetPresContext( 1435 Element::eForComposedDoc) == aPresContext) { 1436 ReleasePointerCapturingElementAtLastPointerUp(); 1437 } 1438 } 1439 // Clean up active pointer info 1440 for (auto iter = sActivePointersIds->Iter(); !iter.Done(); iter.Next()) { 1441 PointerInfo* data = iter.UserData(); 1442 MOZ_ASSERT(data, "how could we have a null PointerInfo here?"); 1443 if (data->mActiveDocument && 1444 data->mActiveDocument->GetPresContext() == aPresContext) { 1445 iter.Remove(); 1446 } 1447 } 1448 } 1449 1450 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) { 1451 // We shouldn't start a drag session if the event is synthesized one because 1452 // aEvent doesn't have enough information for initializing the ePointerCancel. 1453 if (aEvent.IsSynthesized()) { 1454 return false; 1455 } 1456 // And we should not start with raw update events, which should be used only 1457 // for notifying web apps of the pointer state changes ASAP. 1458 if (aEvent.mMessage == ePointerRawUpdate) { 1459 return false; 1460 } 1461 MOZ_ASSERT(aEvent.mMessage != eMouseRawUpdate); 1462 #ifdef XP_WIN 1463 if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) { 1464 // WM_POINTER does not support drag and drop, see bug 1692277 1465 return (aEvent.mInputSource != dom::MouseEvent_Binding::MOZ_SOURCE_PEN && 1466 aEvent.mReason != WidgetMouseEvent::eSynthesized); // bug 1692151 1467 } 1468 #endif 1469 return true; 1470 } 1471 1472 /* static */ 1473 uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId) { 1474 PointerInfo* pointerInfo = nullptr; 1475 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) { 1476 return pointerInfo->mInputSource; 1477 } 1478 return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN; 1479 } 1480 1481 /* static */ 1482 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) { 1483 PointerInfo* pointerInfo = nullptr; 1484 if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) { 1485 return pointerInfo->mIsPrimary; 1486 } 1487 return false; 1488 } 1489 1490 /* static */ 1491 bool PointerEventHandler::HasActiveTouchPointer() { 1492 for (auto iter = sActivePointersIds->ConstIter(); !iter.Done(); iter.Next()) { 1493 if (iter.Data()->mFromTouchEvent) { 1494 return true; 1495 } 1496 } 1497 return false; 1498 } 1499 1500 /* static */ 1501 void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent( 1502 bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent, 1503 Element* aCaptureTarget) { 1504 // Don't allow uncomposed element to capture a pointer. 1505 if (NS_WARN_IF(aIsGotCapture && !aCaptureTarget->IsInComposedDoc())) { 1506 return; 1507 } 1508 const OwningNonNull<Document> targetDoc = *aCaptureTarget->OwnerDoc(); 1509 const RefPtr<PresShell> presShell = targetDoc->GetPresShell(); 1510 if (NS_WARN_IF(!presShell || presShell->IsDestroying())) { 1511 return; 1512 } 1513 1514 if (!aIsGotCapture && !aCaptureTarget->IsInComposedDoc()) { 1515 // If the capturing element was removed from the DOM tree, fire 1516 // ePointerLostCapture at the document. 1517 PointerEventInit init; 1518 init.mPointerId = aPointerEvent->pointerId; 1519 init.mBubbles = true; 1520 init.mComposed = true; 1521 ConvertPointerTypeToString(aPointerEvent->mInputSource, init.mPointerType); 1522 init.mIsPrimary = aPointerEvent->mIsPrimary; 1523 RefPtr<PointerEvent> event; 1524 event = PointerEvent::Constructor(aCaptureTarget, u"lostpointercapture"_ns, 1525 init); 1526 targetDoc->DispatchEvent(*event); 1527 return; 1528 } 1529 nsEventStatus status = nsEventStatus_eIgnore; 1530 WidgetPointerEvent localEvent( 1531 aPointerEvent->IsTrusted(), 1532 aIsGotCapture ? ePointerGotCapture : ePointerLostCapture, 1533 aPointerEvent->mWidget); 1534 1535 localEvent.AssignPointerEventData(*aPointerEvent, /* aCopyTargets */ true, 1536 /* aCopyCoalescedEvents */ false); 1537 DebugOnly<nsresult> rv = presShell->HandleEventWithTarget( 1538 &localEvent, aCaptureTarget->GetPrimaryFrame(), aCaptureTarget, &status); 1539 1540 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 1541 "DispatchGotOrLostPointerCaptureEvent failed"); 1542 } 1543 1544 /* static */ 1545 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource, 1546 uint32_t aPointerId) { 1547 if (sSpoofedPointerId.isSome() || aInputSource != SPOOFED_POINTER_INTERFACE) { 1548 return; 1549 } 1550 1551 sSpoofedPointerId.emplace(aPointerId); 1552 } 1553 1554 void PointerEventHandler::UpdateLastPointerId(uint32_t aPointerId, 1555 EventMessage aEventMessage) { 1556 MOZ_ASSERT(aEventMessage != eMouseEnterIntoWidget); 1557 1558 if (sLastPointerId && *sLastPointerId == aPointerId) { 1559 return; 1560 } 1561 MOZ_LOG_DEBUG_ONLY( 1562 EventStateManager::MouseCursorUpdateLogRef(), LogLevel::Info, 1563 ("PointerEventHandler::UpdateLastPointerId(): " 1564 "Last pointerId (%s) is changed to %u when %s", 1565 ToString(sLastPointerId).c_str(), aPointerId, ToChar(aEventMessage))); 1566 sLastPointerId = Some(aPointerId); 1567 } 1568 1569 void PointerEventHandler::MaybeForgetLastPointerId(uint32_t aPointerId, 1570 EventMessage aEventMessage) { 1571 if (!sLastPointerId || *sLastPointerId != aPointerId) { 1572 return; 1573 } 1574 sLastPointerId.reset(); 1575 MOZ_LOG_DEBUG_ONLY(EventStateManager::MouseCursorUpdateLogRef(), 1576 LogLevel::Info, 1577 ("PointerEventHandler::MaybeForgetLastPointerId(): " 1578 "Last pointerId (%u) is changed to Nothing when %s", 1579 aPointerId, ToChar(aEventMessage))); 1580 } 1581 1582 } // namespace mozilla