EventListenerManager.cpp (81156B)
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 // Microsoft's API Name hackery sucks 8 #undef CreateEvent 9 10 #include "mozilla/EventListenerManager.h" 11 12 #include "EventListenerService.h" 13 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin 14 #include "js/EnvironmentChain.h" // JS::EnvironmentChain 15 #include "js/loader/LoadedScript.h" 16 #include "js/loader/ScriptFetchOptions.h" 17 #include "mozilla/Assertions.h" 18 #include "mozilla/BasicEvents.h" 19 #include "mozilla/BinarySearch.h" 20 #include "mozilla/CycleCollectedJSRuntime.h" 21 #include "mozilla/DOMEventTargetHelper.h" 22 #include "mozilla/EventDispatcher.h" 23 #include "mozilla/HalSensor.h" 24 #include "mozilla/JSEventHandler.h" 25 #include "mozilla/Maybe.h" 26 #include "mozilla/MemoryReporting.h" 27 #include "mozilla/Preferences.h" 28 #include "mozilla/PresShell.h" 29 #include "mozilla/ScopeExit.h" 30 #include "mozilla/StaticPrefs_dom.h" 31 #include "mozilla/TimeStamp.h" 32 #include "mozilla/dom/AbortSignal.h" 33 #include "mozilla/dom/BindingUtils.h" 34 #include "mozilla/dom/ChromeUtils.h" 35 #include "mozilla/dom/Document.h" 36 #include "mozilla/dom/Element.h" 37 #include "mozilla/dom/Event.h" 38 #include "mozilla/dom/EventCallbackDebuggerNotification.h" 39 #include "mozilla/dom/EventTargetBinding.h" 40 #include "mozilla/dom/PolicyContainer.h" 41 #include "mozilla/dom/PopupBlocker.h" 42 #include "mozilla/dom/RequestBinding.h" 43 #include "mozilla/dom/ScriptLoader.h" 44 #include "mozilla/dom/ScriptSettings.h" 45 #include "mozilla/dom/TouchEvent.h" 46 #include "mozilla/dom/UserActivation.h" 47 #include "nsCOMPtr.h" 48 #include "nsContentUtils.h" 49 #include "nsDOMCID.h" 50 #include "nsDisplayList.h" 51 #include "nsError.h" 52 #include "nsGenericHTMLElement.h" 53 #include "nsGkAtoms.h" 54 #include "nsIContent.h" 55 #include "nsIContentSecurityPolicy.h" 56 #include "nsIFrame.h" 57 #include "nsIScriptGlobalObject.h" 58 #include "nsISupports.h" 59 #include "nsJSUtils.h" 60 #include "nsNameSpaceManager.h" 61 #include "nsPIDOMWindow.h" 62 #include "nsPIWindowRoot.h" 63 #include "nsPrintfCString.h" 64 #include "nsSandboxFlags.h" 65 #include "nsScreen.h" 66 #include "xpcpublic.h" 67 68 namespace mozilla { 69 70 using namespace dom; 71 using namespace hal; 72 73 class ListenerMapEntryComparator { 74 public: 75 explicit ListenerMapEntryComparator(nsAtom* aTarget) 76 : mAddressOfEventType(reinterpret_cast<uintptr_t>(aTarget)) {} 77 78 int operator()( 79 const EventListenerManager::EventListenerMapEntry& aEntry) const { 80 uintptr_t value = reinterpret_cast<uintptr_t>(aEntry.mTypeAtom.get()); 81 if (mAddressOfEventType == value) { 82 return 0; 83 } 84 85 if (mAddressOfEventType < value) { 86 return -1; 87 } 88 89 return 1; 90 } 91 92 private: 93 const uintptr_t mAddressOfEventType; // the address of the atom, can be 0 94 }; 95 96 uint32_t EventListenerManager::sMainThreadCreatedCount = 0; 97 98 EventListenerManagerBase::EventListenerManagerBase() 99 : mMayHaveDOMActivateEventListener(false), 100 mMayHaveCapturingListeners(false), 101 mMayHaveSystemGroupListeners(false), 102 mMayHaveTouchEventListener(false), 103 mMayHaveMouseEnterLeaveEventListener(false), 104 mMayHavePointerEnterLeaveEventListener(false), 105 mMayHavePointerRawUpdateEventListener(false), 106 mMayHaveSelectionChangeEventListener(false), 107 mMayHaveFormSelectEventListener(false), 108 mMayHaveTransitionEventListener(false), 109 mMayHaveSMILTimeEventListener(false), 110 mClearingListeners(false), 111 mIsMainThreadELM(NS_IsMainThread()), 112 mMayHaveListenersForUntrustedEvents(false) { 113 ClearNoListenersForEvents(); 114 static_assert(sizeof(EventListenerManagerBase) == sizeof(uint64_t), 115 "Keep the size of EventListenerManagerBase size compact!"); 116 } 117 118 EventListenerManager::EventListenerManager(EventTarget* aTarget) 119 : mTarget(aTarget) { 120 NS_ASSERTION(aTarget, "unexpected null pointer"); 121 122 if (mIsMainThreadELM) { 123 mRefCnt.SetIsOnMainThread(); 124 ++sMainThreadCreatedCount; 125 } 126 } 127 128 EventListenerManager::~EventListenerManager() { 129 // If your code fails this assertion, a possible reason is that 130 // a class did not call our Disconnect() manually. Note that 131 // this class can have Disconnect called in one of two ways: 132 // if it is part of a cycle, then in Unlink() (such a cycle 133 // would be with one of the listeners, not mTarget which is weak). 134 // If not part of a cycle, then Disconnect must be called manually, 135 // typically from the destructor of the owner class (mTarget). 136 // XXX azakai: Is there any reason to not just call Disconnect 137 // from right here, if not previously called? 138 NS_ASSERTION(!mTarget, "didn't call Disconnect"); 139 RemoveAllListenersSilently(); 140 } 141 142 void EventListenerManager::RemoveAllListenersSilently() { 143 if (mClearingListeners) { 144 return; 145 } 146 mClearingListeners = true; 147 mListenerMap.Clear(); 148 mClearingListeners = false; 149 } 150 151 inline void ImplCycleCollectionTraverse( 152 nsCycleCollectionTraversalCallback& aCallback, 153 EventListenerManager::EventListenerMap& aField, const char* aName, 154 uint32_t aFlags = 0) { 155 if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) { 156 nsAutoCString name; 157 name.AppendASCII(aName); 158 name.AppendLiteral(" mEntries[i] event="); 159 size_t entryPrefixLen = name.Length(); 160 for (const auto& entry : aField.mEntries) { 161 if (entry.mTypeAtom) { 162 name.Replace(entryPrefixLen, name.Length() - entryPrefixLen, 163 nsAtomCString(entry.mTypeAtom)); 164 } else { 165 name.Replace(entryPrefixLen, name.Length() - entryPrefixLen, 166 "(all)"_ns); 167 } 168 ImplCycleCollectionTraverse(aCallback, *entry.mListeners, name.get()); 169 } 170 } else { 171 for (const auto& entry : aField.mEntries) { 172 ImplCycleCollectionTraverse(aCallback, *entry.mListeners, 173 ".mEntries[i].mListeners"); 174 } 175 } 176 } 177 178 inline void ImplCycleCollectionTraverse( 179 nsCycleCollectionTraversalCallback& aCallback, 180 EventListenerManager::Listener& aField, const char* aName, 181 unsigned aFlags) { 182 if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) { 183 nsAutoCString name; 184 name.AppendASCII(aName); 185 name.AppendLiteral(" listenerType="); 186 name.AppendInt(aField.mListenerType); 187 name.AppendLiteral(" "); 188 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), 189 name.get(), aFlags); 190 } else { 191 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName, 192 aFlags); 193 } 194 195 CycleCollectionNoteChild(aCallback, aField.mSignalFollower.get(), 196 "mSignalFollower", aFlags); 197 } 198 199 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager) 200 201 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager) 202 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerMap); 203 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 204 205 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager) 206 tmp->Disconnect(); 207 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 208 209 nsPIDOMWindowInner* EventListenerManager::GetInnerWindowForTarget() { 210 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) { 211 // XXX sXBL/XBL2 issue -- do we really want the owner here? What 212 // if that's the XBL document? 213 return node->OwnerDoc()->GetInnerWindow(); 214 } 215 216 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow(); 217 return window; 218 } 219 220 already_AddRefed<nsPIDOMWindowInner> 221 EventListenerManager::GetTargetAsInnerWindow() const { 222 nsCOMPtr<nsPIDOMWindowInner> window = 223 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget); 224 return window.forget(); 225 } 226 227 static mozilla::LazyLogModule sSlowChromeLog("SlowChromeEvent"); 228 229 static void LogForChromeEvent(nsPIDOMWindowInner* aWindow, const char* aMsg) { 230 if (!MOZ_LOG_TEST(sSlowChromeLog, LogLevel::Info)) { 231 return; 232 } 233 if (!nsContentUtils::IsChromeDoc(aWindow->GetExtantDoc())) { 234 return; 235 } 236 237 if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) { 238 JS::AutoFilename filename; 239 uint32_t lineNum = 0; 240 JS::ColumnNumberOneOrigin columnNum; 241 JS::DescribeScriptedCaller(&filename, cx, &lineNum, &columnNum); 242 MOZ_LOG(sSlowChromeLog, LogLevel::Info, 243 ("%s %s:%u:%u", aMsg, filename.get(), lineNum, 244 columnNum.oneOriginValue())); 245 } else { 246 MOZ_LOG(sSlowChromeLog, LogLevel::Info, ("%s", aMsg)); 247 } 248 } 249 250 void EventListenerManager::AddEventListenerInternal( 251 EventListenerHolder aListenerHolder, EventMessage aEventMessage, 252 nsAtom* aTypeAtom, const EventListenerFlags& aFlags, bool aHandler, 253 bool aAllEvents, AbortSignal* aSignal) { 254 MOZ_ASSERT((aEventMessage && aTypeAtom) || aAllEvents, // all-events listener 255 "Missing type"); 256 MOZ_ASSERT_IF( 257 aEventMessage != eUnidentifiedEvent && !aAllEvents, 258 aTypeAtom == nsContentUtils::GetEventTypeFromMessage(aEventMessage)); 259 260 if (!aListenerHolder || mClearingListeners) { 261 return; 262 } 263 264 if (aSignal && aSignal->Aborted()) { 265 return; 266 } 267 268 // Since there is no public API to call us with an EventListenerHolder, we 269 // know that there's an EventListenerHolder on the stack holding a strong ref 270 // to the listener. 271 272 RefPtr<ListenerArray> listeners = 273 aAllEvents ? mListenerMap.GetOrCreateListenersForAllEvents() 274 : mListenerMap.GetOrCreateListenersForType(aTypeAtom); 275 276 for (const Listener& listener : listeners->NonObservingRange()) { 277 // mListener == aListenerHolder is the last one, since it can be a bit slow. 278 if (listener.mListenerIsHandler == aHandler && 279 listener.mFlags.EqualsForAddition(aFlags) && 280 listener.mListener == aListenerHolder) { 281 return; 282 } 283 } 284 285 ClearNoListenersForEvents(); 286 mNoListenerForEventAtom = nullptr; 287 288 Listener* listener = listeners->AppendElement(); 289 listener->mFlags = aFlags; 290 listener->mListenerIsHandler = aHandler; 291 listener->mHandlerIsString = false; 292 listener->mAllEvents = aAllEvents; 293 294 if (listener->mFlags.mAllowUntrustedEvents) { 295 mMayHaveListenersForUntrustedEvents = true; 296 } 297 298 // Detect the type of event listener. 299 if (aFlags.mListenerIsJSListener) { 300 MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback()); 301 listener->mListenerType = Listener::eJSEventListener; 302 } else if (aListenerHolder.HasWebIDLCallback()) { 303 listener->mListenerType = Listener::eWebIDLListener; 304 } else { 305 listener->mListenerType = Listener::eNativeListener; 306 } 307 listener->mListener = std::move(aListenerHolder); 308 309 if (aSignal) { 310 listener->mSignalFollower = 311 new ListenerSignalFollower(this, listener, aTypeAtom); 312 listener->mSignalFollower->Follow(aSignal); 313 } 314 315 if (aFlags.mInSystemGroup) { 316 mMayHaveSystemGroupListeners = true; 317 } 318 if (aFlags.mCapture) { 319 mMayHaveCapturingListeners = true; 320 } 321 322 // Events which are not supported in the running environment is mapped to 323 // eUnidentifiedEvent. Then, we need to consider the proper event message 324 // with comparing the atom. 325 { 326 EventMessage resolvedEventMessage = aEventMessage; 327 if (resolvedEventMessage == eUnidentifiedEvent && aTypeAtom->IsStatic()) { 328 // TouchEvents are registered only when 329 // nsContentUtils::InitializeTouchEventTable() is called. 330 if (aTypeAtom == nsGkAtoms::ontouchstart) { 331 resolvedEventMessage = eTouchStart; 332 } else if (aTypeAtom == nsGkAtoms::ontouchend) { 333 resolvedEventMessage = eTouchEnd; 334 } else if (aTypeAtom == nsGkAtoms::ontouchmove) { 335 resolvedEventMessage = eTouchMove; 336 } else if (aTypeAtom == nsGkAtoms::ontouchcancel) { 337 resolvedEventMessage = eTouchCancel; 338 } 339 } 340 341 switch (resolvedEventMessage) { 342 case eLegacyDOMActivate: 343 mMayHaveDOMActivateEventListener = true; 344 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 345 window->SetHasDOMActivateEventListeners(); 346 } 347 break; 348 case ePointerEnter: 349 case ePointerLeave: 350 mMayHavePointerEnterLeaveEventListener = true; 351 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 352 LogForChromeEvent( 353 window, 354 "Please do not use pointerenter/leave events in chrome. " 355 "They are slower than pointerover/out!"); 356 window->SetHasPointerEnterLeaveEventListeners(); 357 } 358 break; 359 case ePointerRawUpdate: 360 if (!StaticPrefs::dom_event_pointer_rawupdate_enabled()) { 361 break; 362 } 363 mMayHavePointerRawUpdateEventListener = true; 364 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 365 LogForChromeEvent( 366 window, "Please do not use pointerrawupdate event in chrome."); 367 window->MaybeSetHasPointerRawUpdateEventListeners(); 368 } 369 break; 370 case eGamepadButtonDown: 371 case eGamepadButtonUp: 372 case eGamepadAxisMove: 373 case eGamepadConnected: 374 case eGamepadDisconnected: 375 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 376 window->SetHasGamepadEventListener(); 377 } 378 break; 379 case eDeviceOrientation: 380 case eDeviceOrientationAbsolute: 381 case eUserProximity: 382 case eDeviceLight: 383 case eDeviceMotion: 384 #if defined(MOZ_WIDGET_ANDROID) 385 case eOrientationChange: 386 #endif // #if defined(MOZ_WIDGET_ANDROID) 387 EnableDevice(aTypeAtom); 388 break; 389 case eTouchStart: 390 case eTouchEnd: 391 case eTouchMove: 392 case eTouchCancel: 393 mMayHaveTouchEventListener = true; 394 // we don't want touchevent listeners added by scrollbars to flip this 395 // flag so we ignore listeners created with system event flag 396 if (!aFlags.mInSystemGroup) { 397 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 398 window->SetHasTouchEventListeners(); 399 } 400 } 401 break; 402 case eMouseEnter: 403 case eMouseLeave: 404 mMayHaveMouseEnterLeaveEventListener = true; 405 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 406 LogForChromeEvent( 407 window, 408 "Please do not use mouseenter/leave events in chrome. " 409 "They are slower than mouseover/out!"); 410 window->SetHasMouseEnterLeaveEventListeners(); 411 } 412 break; 413 case eEditorBeforeInput: 414 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 415 window->SetHasBeforeInputEventListenersForTelemetry(); 416 } 417 break; 418 case eSelectionChange: 419 mMayHaveSelectionChangeEventListener = true; 420 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 421 window->SetHasSelectionChangeEventListeners(); 422 } 423 break; 424 case eFormSelect: 425 mMayHaveFormSelectEventListener = true; 426 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 427 window->SetHasFormSelectEventListeners(); 428 } 429 break; 430 case eLegacyMouseLineOrPageScroll: 431 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 432 if (Document* doc = window->GetExtantDoc()) { 433 doc->SetUseCounter(eUseCounter_custom_ondommousescroll); 434 } 435 } 436 break; 437 case eLegacyMousePixelScroll: 438 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 439 if (Document* doc = window->GetExtantDoc()) { 440 doc->SetUseCounter(eUseCounter_custom_onmozmousepixelscroll); 441 } 442 } 443 break; 444 case eTransitionStart: 445 case eTransitionRun: 446 case eTransitionEnd: 447 case eTransitionCancel: 448 case eWebkitTransitionEnd: 449 mMayHaveTransitionEventListener = true; 450 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 451 window->SetHasTransitionEventListeners(); 452 } 453 break; 454 case eSMILBeginEvent: 455 case eSMILEndEvent: 456 case eSMILRepeatEvent: 457 mMayHaveSMILTimeEventListener = true; 458 if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { 459 window->SetHasSMILTimeEventListeners(); 460 } 461 break; 462 case eFormCheckboxStateChange: 463 nsContentUtils::SetMayHaveFormCheckboxStateChangeListeners(); 464 break; 465 case eFormRadioStateChange: 466 nsContentUtils::SetMayHaveFormRadioStateChangeListeners(); 467 break; 468 case eMozOrientationChange: 469 if (nsScreen* screen = mTarget->GetAsScreen()) { 470 if (nsPIDOMWindowOuter* outer = screen->GetOuter()) { 471 if (Document* doc = outer->GetExtantDoc()) { 472 doc->WarnOnceAbout( 473 DeprecatedOperations::eMozorientationchangeDeprecated); 474 } 475 } 476 } 477 break; 478 default: 479 // XXX Use NS_ASSERTION here to print resolvedEventMessage since 480 // MOZ_ASSERT can take only string literal, not pointer to 481 // characters. 482 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerenter, 483 nsPrintfCString("resolvedEventMessage=%s", 484 ToChar(resolvedEventMessage)) 485 .get()); 486 NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerleave, 487 nsPrintfCString("resolvedEventMessage=%s", 488 ToChar(resolvedEventMessage)) 489 .get()); 490 NS_ASSERTION( 491 resolvedEventMessage < eGamepadEventFirst || 492 resolvedEventMessage > eGamepadEventLast, 493 nsPrintfCString("You added new gamepad event, but it's not " 494 "handled above, resolvedEventMessage=%s", 495 ToChar(resolvedEventMessage)) 496 .get()); 497 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientation, 498 nsPrintfCString("resolvedEventMessage=%s", 499 ToChar(resolvedEventMessage)) 500 .get()); 501 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientationabsolute, 502 nsPrintfCString("resolvedEventMessage=%s", 503 ToChar(resolvedEventMessage)) 504 .get()); 505 NS_ASSERTION(aTypeAtom != nsGkAtoms::onuserproximity, 506 nsPrintfCString("resolvedEventMessage=%s", 507 ToChar(resolvedEventMessage)) 508 .get()); 509 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicelight, 510 nsPrintfCString("resolvedEventMessage=%s", 511 ToChar(resolvedEventMessage)) 512 .get()); 513 NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicemotion, 514 nsPrintfCString("resolvedEventMessage=%s", 515 ToChar(resolvedEventMessage)) 516 .get()); 517 #if defined(MOZ_WIDGET_ANDROID) 518 NS_ASSERTION(aTypeAtom != nsGkAtoms::onorientationchange, 519 nsPrintfCString("resolvedEventMessage=%s", 520 ToChar(resolvedEventMessage)) 521 .get()); 522 #endif // #if defined(MOZ_WIDGET_ANDROID) 523 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchstart, 524 nsPrintfCString("resolvedEventMessage=%s", 525 ToChar(resolvedEventMessage)) 526 .get()); 527 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchend, 528 nsPrintfCString("resolvedEventMessage=%s", 529 ToChar(resolvedEventMessage)) 530 .get()); 531 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchmove, 532 nsPrintfCString("resolvedEventMessage=%s", 533 ToChar(resolvedEventMessage)) 534 .get()); 535 NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchcancel, 536 nsPrintfCString("resolvedEventMessage=%s", 537 ToChar(resolvedEventMessage)) 538 .get()); 539 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseenter, 540 nsPrintfCString("resolvedEventMessage=%s", 541 ToChar(resolvedEventMessage)) 542 .get()); 543 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseleave, 544 nsPrintfCString("resolvedEventMessage=%s", 545 ToChar(resolvedEventMessage)) 546 .get()); 547 NS_ASSERTION(aTypeAtom != nsGkAtoms::onbeforeinput, 548 nsPrintfCString("resolvedEventMessage=%s", 549 ToChar(resolvedEventMessage)) 550 .get()); 551 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselectionchange, 552 nsPrintfCString("resolvedEventMessage=%s", 553 ToChar(resolvedEventMessage)) 554 .get()); 555 NS_ASSERTION(aTypeAtom != nsGkAtoms::onselect, 556 nsPrintfCString("resolvedEventMessage=%s", 557 ToChar(resolvedEventMessage)) 558 .get()); 559 NS_ASSERTION(aTypeAtom != nsGkAtoms::onDOMMouseScroll, 560 nsPrintfCString("resolvedEventMessage=%s", 561 ToChar(resolvedEventMessage)) 562 .get()); 563 NS_ASSERTION(aTypeAtom != nsGkAtoms::onMozMousePixelScroll, 564 nsPrintfCString("resolvedEventMessage=%s", 565 ToChar(resolvedEventMessage)) 566 .get()); 567 NS_ASSERTION(aTypeAtom != nsGkAtoms::onmozorientationchange, 568 nsPrintfCString("resolvedEventMessage=%s", 569 ToChar(resolvedEventMessage)) 570 .get()); 571 break; 572 } 573 } 574 575 if (mIsMainThreadELM && !aFlags.mPassive && IsApzAwareEvent(aTypeAtom)) { 576 ProcessApzAwareEventListenerAdd(); 577 } 578 579 if (mTarget) { 580 mTarget->EventListenerAdded(aTypeAtom); 581 } 582 583 if (mIsMainThreadELM && mTarget) { 584 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, 585 aTypeAtom); 586 } 587 } 588 589 void EventListenerManager::ProcessApzAwareEventListenerAdd() { 590 Document* doc = nullptr; 591 592 // Mark the node as having apz aware listeners 593 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) { 594 node->SetMayBeApzAware(); 595 doc = node->OwnerDoc(); 596 } 597 598 // Schedule a paint so event regions on the layer tree gets updated 599 if (!doc) { 600 if (nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow()) { 601 doc = window->GetExtantDoc(); 602 } 603 } 604 if (!doc) { 605 if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(mTarget)) { 606 if (nsPIDOMWindowInner* window = helper->GetOwnerWindow()) { 607 doc = window->GetExtantDoc(); 608 } 609 } 610 } 611 612 if (doc && gfxPlatform::AsyncPanZoomEnabled()) { 613 PresShell* presShell = doc->GetPresShell(); 614 if (presShell) { 615 nsIFrame* f = presShell->GetRootFrame(); 616 if (f) { 617 f->SchedulePaint(); 618 } 619 } 620 } 621 } 622 623 bool EventListenerManager::IsDeviceType(nsAtom* aTypeAtom) { 624 return aTypeAtom == nsGkAtoms::ondeviceorientation || 625 aTypeAtom == nsGkAtoms::ondeviceorientationabsolute || 626 aTypeAtom == nsGkAtoms::ondevicemotion || 627 aTypeAtom == nsGkAtoms::ondevicelight 628 #if defined(MOZ_WIDGET_ANDROID) 629 || aTypeAtom == nsGkAtoms::onorientationchange 630 #endif 631 || aTypeAtom == nsGkAtoms::onuserproximity; 632 } 633 634 void EventListenerManager::EnableDevice(nsAtom* aTypeAtom) { 635 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow(); 636 if (!window) { 637 return; 638 } 639 640 if (aTypeAtom == nsGkAtoms::ondeviceorientation) { 641 #ifdef MOZ_WIDGET_ANDROID 642 // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if 643 // unavailable on device. 644 window->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR); 645 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR); 646 #else 647 window->EnableDeviceSensor(SENSOR_ORIENTATION); 648 #endif 649 return; 650 } 651 652 if (aTypeAtom == nsGkAtoms::ondeviceorientationabsolute) { 653 #ifdef MOZ_WIDGET_ANDROID 654 // Falls back to SENSOR_ORIENTATION if unavailable on device. 655 window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR); 656 #else 657 window->EnableDeviceSensor(SENSOR_ORIENTATION); 658 #endif 659 return; 660 } 661 662 if (aTypeAtom == nsGkAtoms::onuserproximity) { 663 window->EnableDeviceSensor(SENSOR_PROXIMITY); 664 return; 665 } 666 667 if (aTypeAtom == nsGkAtoms::ondevicelight) { 668 window->EnableDeviceSensor(SENSOR_LIGHT); 669 return; 670 } 671 672 if (aTypeAtom == nsGkAtoms::ondevicemotion) { 673 window->EnableDeviceSensor(SENSOR_ACCELERATION); 674 window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION); 675 window->EnableDeviceSensor(SENSOR_GYROSCOPE); 676 return; 677 } 678 679 #if defined(MOZ_WIDGET_ANDROID) 680 if (aTypeAtom == nsGkAtoms::onorientationchange) { 681 window->EnableOrientationChangeListener(); 682 return; 683 } 684 #endif 685 686 NS_WARNING("Enabling an unknown device sensor."); 687 } 688 689 void EventListenerManager::DisableDevice(nsAtom* aTypeAtom) { 690 nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow(); 691 if (!window) { 692 return; 693 } 694 695 if (aTypeAtom == nsGkAtoms::ondeviceorientation) { 696 #ifdef MOZ_WIDGET_ANDROID 697 // Disable all potential fallback sensors. 698 window->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR); 699 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR); 700 #endif 701 window->DisableDeviceSensor(SENSOR_ORIENTATION); 702 return; 703 } 704 705 if (aTypeAtom == nsGkAtoms::ondeviceorientationabsolute) { 706 #ifdef MOZ_WIDGET_ANDROID 707 window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR); 708 #endif 709 window->DisableDeviceSensor(SENSOR_ORIENTATION); 710 return; 711 } 712 713 if (aTypeAtom == nsGkAtoms::ondevicemotion) { 714 window->DisableDeviceSensor(SENSOR_ACCELERATION); 715 window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION); 716 window->DisableDeviceSensor(SENSOR_GYROSCOPE); 717 return; 718 } 719 720 if (aTypeAtom == nsGkAtoms::onuserproximity) { 721 window->DisableDeviceSensor(SENSOR_PROXIMITY); 722 return; 723 } 724 725 if (aTypeAtom == nsGkAtoms::ondevicelight) { 726 window->DisableDeviceSensor(SENSOR_LIGHT); 727 return; 728 } 729 730 #if defined(MOZ_WIDGET_ANDROID) 731 if (aTypeAtom == nsGkAtoms::onorientationchange) { 732 window->DisableOrientationChangeListener(); 733 return; 734 } 735 #endif 736 737 NS_WARNING("Disabling an unknown device sensor."); 738 } 739 740 void EventListenerManager::NotifyEventListenerRemoved(nsAtom* aUserType) { 741 // If the following code is changed, other callsites of EventListenerRemoved 742 // and NotifyAboutMainThreadListenerChange should be changed too. 743 ClearNoListenersForEvents(); 744 mNoListenerForEventAtom = nullptr; 745 if (mTarget) { 746 mTarget->EventListenerRemoved(aUserType); 747 } 748 if (mIsMainThreadELM && mTarget) { 749 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, 750 aUserType); 751 } 752 } 753 754 void EventListenerManager::RemoveEventListenerInternal( 755 EventListenerHolder aListenerHolder, nsAtom* aUserType, 756 const EventListenerFlags& aFlags, bool aAllEvents) { 757 if (!aListenerHolder || (!aUserType && !aAllEvents) || mClearingListeners) { 758 return; 759 } 760 761 Maybe<size_t> entryIndex = aAllEvents 762 ? mListenerMap.EntryIndexForAllEvents() 763 : mListenerMap.EntryIndexForType(aUserType); 764 if (!entryIndex) { 765 return; 766 } 767 768 ListenerArray& listenerArray = *mListenerMap.mEntries[*entryIndex].mListeners; 769 770 Maybe<uint32_t> listenerIndex = [&]() -> Maybe<uint32_t> { 771 uint32_t count = listenerArray.Length(); 772 for (uint32_t i = 0; i < count; ++i) { 773 Listener* listener = &listenerArray.ElementAt(i); 774 if (listener->mListener == aListenerHolder && 775 listener->mFlags.EqualsForRemoval(aFlags)) { 776 return Some(i); 777 } 778 } 779 return Nothing(); 780 }(); 781 782 if (!listenerIndex) { 783 return; 784 } 785 786 listenerArray.RemoveElementAt(*listenerIndex); 787 if (listenerArray.IsEmpty()) { 788 mListenerMap.mEntries.RemoveElementAt(*entryIndex); 789 } 790 791 RefPtr<EventListenerManager> kungFuDeathGrip(this); 792 if (!aAllEvents) { 793 NotifyEventListenerRemoved(aUserType); 794 if (IsDeviceType(aUserType)) { 795 DisableDevice(aUserType); 796 } 797 } 798 799 // XXX Should we clear mMayHavePointerRawUpdateEventListener if the last 800 // pointerrawupdate event listener is removed? If so, nsPIDOMWindowInner 801 // needs to count how may event listener managers had some pointerrawupdate 802 // event listener. If we've notified the window of having a pointerrawupdate 803 // event listener, some behavior is changed because pointerrawupdate event 804 // dispatcher needs to handle some things before dispatching an event to the 805 // DOM. However, it is expected that web apps using `pointerrawupdate` don't 806 // remove the event listeners. 807 } 808 809 static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage) { 810 if (aMessage == eTouchStart || aMessage == eTouchMove || aMessage == eWheel || 811 aMessage == eLegacyMouseLineOrPageScroll || 812 aMessage == eLegacyMousePixelScroll) { 813 return true; 814 } 815 return false; 816 } 817 818 static bool IsRootEventTarget(EventTarget* aTarget) { 819 if (!aTarget) { 820 return false; 821 } 822 if (aTarget->IsInnerWindow()) { 823 return true; 824 } 825 const nsINode* node = nsINode::FromEventTarget(aTarget); 826 if (!node) { 827 return false; 828 } 829 Document* doc = node->OwnerDoc(); 830 return node == doc || node == doc->GetRootElement() || node == doc->GetBody(); 831 } 832 833 void EventListenerManager::MaybeMarkPassive(EventMessage aMessage, 834 EventListenerFlags& aFlags) { 835 if (!mIsMainThreadELM) { 836 return; 837 } 838 if (!IsDefaultPassiveWhenOnRoot(aMessage)) { 839 return; 840 } 841 if (!IsRootEventTarget(mTarget)) { 842 return; 843 } 844 aFlags.mPassive = true; 845 } 846 847 void EventListenerManager::AddEventListenerByType( 848 EventListenerHolder aListenerHolder, const nsAString& aType, 849 const EventListenerFlags& aFlags, const Optional<bool>& aPassive, 850 AbortSignal* aSignal) { 851 RefPtr<nsAtom> atom; 852 EventMessage message = 853 GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom)); 854 855 EventListenerFlags flags = aFlags; 856 if (aPassive.WasPassed()) { 857 flags.mPassive = aPassive.Value(); 858 } else { 859 MaybeMarkPassive(message, flags); 860 } 861 862 AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags, 863 false, false, aSignal); 864 } 865 866 void EventListenerManager::RemoveEventListenerByType( 867 EventListenerHolder aListenerHolder, const nsAString& aType, 868 const EventListenerFlags& aFlags) { 869 RefPtr<nsAtom> atom; 870 (void)GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom)); 871 RemoveEventListenerInternal(std::move(aListenerHolder), atom, aFlags); 872 } 873 874 EventListenerManager::Listener* EventListenerManager::FindEventHandler( 875 nsAtom* aTypeAtom) { 876 // Run through the listeners for this type and see if a script 877 // listener is registered 878 RefPtr<ListenerArray> listeners = mListenerMap.GetListenersForType(aTypeAtom); 879 if (!listeners) { 880 return nullptr; 881 } 882 883 uint32_t count = listeners->Length(); 884 for (uint32_t i = 0; i < count; ++i) { 885 Listener* listener = &listeners->ElementAt(i); 886 if (listener->mListenerIsHandler) { 887 return listener; 888 } 889 } 890 return nullptr; 891 } 892 893 EventListenerManager::Listener* EventListenerManager::SetEventHandlerInternal( 894 nsAtom* aName, const TypedEventHandler& aTypedHandler, 895 bool aPermitUntrustedEvents) { 896 MOZ_ASSERT(aName); 897 898 EventMessage eventMessage = GetEventMessage(aName); 899 Listener* listener = FindEventHandler(aName); 900 901 if (!listener) { 902 // If we didn't find a script listener or no listeners existed 903 // create and add a new one. 904 EventListenerFlags flags; 905 flags.mListenerIsJSListener = true; 906 MaybeMarkPassive(eventMessage, flags); 907 908 nsCOMPtr<JSEventHandler> jsEventHandler; 909 NS_NewJSEventHandler(mTarget, aName, aTypedHandler, 910 getter_AddRefs(jsEventHandler)); 911 AddEventListenerInternal(EventListenerHolder(jsEventHandler), eventMessage, 912 aName, flags, true); 913 914 listener = FindEventHandler(aName); 915 } else { 916 JSEventHandler* jsEventHandler = listener->GetJSEventHandler(); 917 MOZ_ASSERT(jsEventHandler, 918 "How can we have an event handler with no JSEventHandler?"); 919 920 bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler; 921 // Possibly the same listener, but update still the context and scope. 922 jsEventHandler->SetHandler(aTypedHandler); 923 if (mTarget && !same) { 924 mTarget->EventListenerRemoved(aName); 925 mTarget->EventListenerAdded(aName); 926 } 927 if (mIsMainThreadELM && mTarget) { 928 EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, aName); 929 } 930 } 931 932 // Set flag to indicate possible need for compilation later 933 listener->mHandlerIsString = !aTypedHandler.HasEventHandler(); 934 if (aPermitUntrustedEvents) { 935 listener->mFlags.mAllowUntrustedEvents = true; 936 mMayHaveListenersForUntrustedEvents = true; 937 } 938 939 return listener; 940 } 941 942 nsresult EventListenerManager::SetEventHandler(nsAtom* aName, 943 const nsAString& aBody, 944 bool aDeferCompilation, 945 bool aPermitUntrustedEvents, 946 Element* aElement) { 947 auto removeEventHandler = MakeScopeExit([&] { RemoveEventHandler(aName); }); 948 949 nsCOMPtr<Document> doc; 950 nsCOMPtr<nsIScriptGlobalObject> global = 951 GetScriptGlobalAndDocument(getter_AddRefs(doc)); 952 953 if (!global) { 954 // This can happen; for example this document might have been 955 // loaded as data. 956 return NS_OK; 957 } 958 959 nsresult rv = NS_OK; 960 // return early preventing the event listener from being added 961 // 'doc' is fetched above 962 if (doc) { 963 // Don't allow adding an event listener if the document is sandboxed 964 // without 'allow-scripts'. 965 if (doc->HasScriptsBlockedBySandbox()) { 966 return NS_ERROR_DOM_SECURITY_ERR; 967 } 968 969 // Perform CSP check 970 nsCOMPtr<nsIContentSecurityPolicy> csp = 971 PolicyContainer::GetCSP(doc->GetPolicyContainer()); 972 uint32_t lineNum = 0; 973 JS::ColumnNumberOneOrigin columnNum; 974 975 JSContext* cx = nsContentUtils::GetCurrentJSContext(); 976 if (cx) { 977 JS::DescribeScriptedCaller(nullptr, cx, &lineNum, &columnNum); 978 } 979 980 if (csp) { 981 bool allowsInlineScript = true; 982 rv = csp->GetAllowsInline( 983 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE, 984 true, // aHasUnsafeHash 985 u""_ns, // aNonce 986 true, // aParserCreated (true because attribute event handler) 987 aElement, 988 nullptr, // nsICSPEventListener 989 aBody, lineNum, columnNum.oneOriginValue(), &allowsInlineScript); 990 NS_ENSURE_SUCCESS(rv, rv); 991 992 // return early if CSP wants us to block inline scripts 993 if (!allowsInlineScript) { 994 return NS_OK; 995 } 996 } 997 } 998 999 // This might be the first reference to this language in the global 1000 // We must init the language before we attempt to fetch its context. 1001 if (NS_FAILED(global->EnsureScriptEnvironment())) { 1002 NS_WARNING("Failed to setup script environment for this language"); 1003 // but fall through and let the inevitable failure below handle it. 1004 } 1005 1006 nsIScriptContext* context = global->GetScriptContext(); 1007 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE); 1008 NS_ENSURE_STATE(global->HasJSGlobal()); 1009 1010 removeEventHandler.release(); 1011 1012 Listener* listener = SetEventHandlerInternal(aName, TypedEventHandler(), 1013 aPermitUntrustedEvents); 1014 1015 if (!aDeferCompilation) { 1016 return CompileEventHandlerInternal(listener, aName, &aBody, aElement); 1017 } 1018 1019 return NS_OK; 1020 } 1021 1022 void EventListenerManager::RemoveEventHandler(nsAtom* aName) { 1023 if (mClearingListeners) { 1024 return; 1025 } 1026 1027 Maybe<size_t> entryIndex = mListenerMap.EntryIndexForType(aName); 1028 if (!entryIndex) { 1029 return; 1030 } 1031 1032 ListenerArray& listenerArray = *mListenerMap.mEntries[*entryIndex].mListeners; 1033 1034 Maybe<uint32_t> listenerIndex = [&]() -> Maybe<uint32_t> { 1035 uint32_t count = listenerArray.Length(); 1036 for (uint32_t i = 0; i < count; ++i) { 1037 Listener* listener = &listenerArray.ElementAt(i); 1038 if (listener->mListenerIsHandler) { 1039 return Some(i); 1040 } 1041 } 1042 return Nothing(); 1043 }(); 1044 1045 if (!listenerIndex) { 1046 return; 1047 } 1048 1049 listenerArray.RemoveElementAt(*listenerIndex); 1050 if (listenerArray.IsEmpty()) { 1051 mListenerMap.mEntries.RemoveElementAt(*entryIndex); 1052 } 1053 1054 RefPtr<EventListenerManager> kungFuDeathGrip(this); 1055 NotifyEventListenerRemoved(aName); 1056 if (IsDeviceType(aName)) { 1057 DisableDevice(aName); 1058 } 1059 } 1060 1061 nsresult EventListenerManager::CompileEventHandlerInternal( 1062 Listener* aListener, nsAtom* aTypeAtom, const nsAString* aBody, 1063 Element* aElement) { 1064 MOZ_ASSERT(aListener->GetJSEventHandler()); 1065 MOZ_ASSERT(aListener->mHandlerIsString, 1066 "Why are we compiling a non-string JS listener?"); 1067 JSEventHandler* jsEventHandler = aListener->GetJSEventHandler(); 1068 MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(), 1069 "What is there to compile?"); 1070 1071 nsresult result = NS_OK; 1072 nsCOMPtr<Document> doc; 1073 nsCOMPtr<nsIScriptGlobalObject> global = 1074 GetScriptGlobalAndDocument(getter_AddRefs(doc)); 1075 NS_ENSURE_STATE(global); 1076 1077 // Activate JSAPI, and make sure that exceptions are reported on the right 1078 // Window. 1079 AutoJSAPI jsapi; 1080 if (NS_WARN_IF(!jsapi.Init(global))) { 1081 return NS_ERROR_UNEXPECTED; 1082 } 1083 JSContext* cx = jsapi.cx(); 1084 1085 nsAtom* attrName = aTypeAtom; 1086 1087 // Flag us as not a string so we don't keep trying to compile strings which 1088 // can't be compiled. 1089 aListener->mHandlerIsString = false; 1090 1091 // mTarget may not be an Element if it's a window and we're 1092 // getting an inline event listener forwarded from <html:body> or 1093 // <html:frameset> or <xul:window> or the like. 1094 // XXX I don't like that we have to reference content from 1095 // here. The alternative is to store the event handler string on 1096 // the JSEventHandler itself, and that still doesn't address 1097 // the arg names issue. 1098 RefPtr<Element> element = Element::FromEventTargetOrNull(mTarget); 1099 MOZ_ASSERT(element || aBody, "Where will we get our body?"); 1100 nsAutoString handlerBody; 1101 const nsAString* body = aBody; 1102 if (!aBody) { 1103 if (aTypeAtom == nsGkAtoms::onSVGLoad) { 1104 attrName = nsGkAtoms::onload; 1105 } else if (aTypeAtom == nsGkAtoms::onSVGScroll) { 1106 attrName = nsGkAtoms::onscroll; 1107 } else if (aTypeAtom == nsGkAtoms::onbeginEvent) { 1108 attrName = nsGkAtoms::onbegin; 1109 } else if (aTypeAtom == nsGkAtoms::onrepeatEvent) { 1110 attrName = nsGkAtoms::onrepeat; 1111 } else if (aTypeAtom == nsGkAtoms::onendEvent) { 1112 attrName = nsGkAtoms::onend; 1113 } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationEnd) { 1114 attrName = nsGkAtoms::onwebkitanimationend; 1115 } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationIteration) { 1116 attrName = nsGkAtoms::onwebkitanimationiteration; 1117 } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationStart) { 1118 attrName = nsGkAtoms::onwebkitanimationstart; 1119 } else if (aTypeAtom == nsGkAtoms::onwebkitTransitionEnd) { 1120 attrName = nsGkAtoms::onwebkittransitionend; 1121 } 1122 1123 element->GetAttr(attrName, handlerBody); 1124 body = &handlerBody; 1125 aElement = element; 1126 } 1127 aListener = nullptr; 1128 1129 nsAutoCString url("-moz-evil:lying-event-listener"_ns); 1130 MOZ_ASSERT(body); 1131 MOZ_ASSERT(aElement); 1132 nsIURI* uri = aElement->OwnerDoc()->GetDocumentURI(); 1133 if (uri) { 1134 uri->GetSpec(url); 1135 } 1136 1137 nsCOMPtr<nsPIDOMWindowInner> win = 1138 nsPIDOMWindowInner::FromEventTargetOrNull(mTarget); 1139 uint32_t argCount; 1140 const char** argNames; 1141 nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), aTypeAtom, win, 1142 &argCount, &argNames); 1143 1144 // Wrap the event target, so that we can use it as the scope for the event 1145 // handler. Note that mTarget is different from aElement in the <body> case, 1146 // where mTarget is a Window. 1147 // 1148 // The wrapScope doesn't really matter here, because the target will create 1149 // its reflector in the proper scope, and then we'll enter that realm. 1150 JS::Rooted<JSObject*> wrapScope(cx, global->GetGlobalJSObject()); 1151 JS::Rooted<JS::Value> v(cx); 1152 { 1153 JSAutoRealm ar(cx, wrapScope); 1154 nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v, 1155 /* aAllowWrapping = */ false); 1156 if (NS_WARN_IF(NS_FAILED(rv))) { 1157 return rv; 1158 } 1159 } 1160 1161 JS::Rooted<JSObject*> target(cx, &v.toObject()); 1162 JSAutoRealm ar(cx, target); 1163 1164 // Now that we've entered the realm we actually care about, create our 1165 // environment chain. Note that we start with |element|, not aElement, 1166 // because mTarget is different from aElement in the <body> case, where 1167 // mTarget is a Window, and in that case we do not want the environment chain 1168 // to include the body or the document. 1169 JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::Yes); 1170 if (!nsJSUtils::GetEnvironmentChainForElement(cx, element, envChain)) { 1171 return NS_ERROR_OUT_OF_MEMORY; 1172 } 1173 1174 nsDependentAtomString str(attrName); 1175 // Most of our names are short enough that we don't even have to malloc 1176 // the JS string stuff, so don't worry about playing games with 1177 // refcounting XPCOM stringbuffers. 1178 JS::Rooted<JSString*> jsStr( 1179 cx, JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length())); 1180 NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY); 1181 1182 // Get the reflector for |aElement|, so that we can pass to setElement. 1183 if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, aElement, &v))) { 1184 return NS_ERROR_FAILURE; 1185 } 1186 1187 RefPtr<JS::loader::ScriptFetchOptions> fetchOptions = 1188 new JS::loader::ScriptFetchOptions( 1189 CORS_NONE, /* aNonce = */ u""_ns, RequestPriority::Auto, 1190 JS::loader::ParserMetadata::NotParserInserted, 1191 aElement->OwnerDoc()->NodePrincipal()); 1192 1193 RefPtr<JS::loader::EventScript> eventScript = new JS::loader::EventScript( 1194 aElement->OwnerDoc()->GetReferrerPolicy(), fetchOptions, uri); 1195 1196 JS::CompileOptions options(cx); 1197 // Use line 0 to make the function body starts from line 1. 1198 options.setIntroductionType("eventHandler") 1199 .setFileAndLine(url.get(), 0) 1200 .setDeferDebugMetadata(true); 1201 1202 JS::Rooted<JSObject*> handler(cx); 1203 result = nsJSUtils::CompileFunction(jsapi, envChain, options, 1204 nsAtomCString(aTypeAtom), argCount, 1205 argNames, *body, handler.address()); 1206 NS_ENSURE_SUCCESS(result, result); 1207 NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE); 1208 1209 JS::Rooted<JS::Value> privateValue(cx, JS::PrivateValue(eventScript)); 1210 result = nsJSUtils::UpdateFunctionDebugMetadata(jsapi, handler, options, 1211 jsStr, privateValue); 1212 NS_ENSURE_SUCCESS(result, result); 1213 1214 MOZ_ASSERT(js::IsObjectInContextCompartment(handler, cx)); 1215 JS::Rooted<JSObject*> handlerGlobal(cx, JS::CurrentGlobalOrNull(cx)); 1216 1217 if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) { 1218 RefPtr<OnErrorEventHandlerNonNull> handlerCallback = 1219 new OnErrorEventHandlerNonNull(static_cast<JSContext*>(nullptr), 1220 handler, handlerGlobal, 1221 /* aIncumbentGlobal = */ nullptr); 1222 jsEventHandler->SetHandler(handlerCallback); 1223 } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) { 1224 RefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback = 1225 new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext*>(nullptr), 1226 handler, handlerGlobal, 1227 /* aIncumbentGlobal = */ nullptr); 1228 jsEventHandler->SetHandler(handlerCallback); 1229 } else { 1230 RefPtr<EventHandlerNonNull> handlerCallback = new EventHandlerNonNull( 1231 static_cast<JSContext*>(nullptr), handler, handlerGlobal, 1232 /* aIncumbentGlobal = */ nullptr); 1233 jsEventHandler->SetHandler(handlerCallback); 1234 } 1235 1236 return result; 1237 } 1238 1239 bool EventListenerManager::HandleEventSingleListener( 1240 Listener* aListener, nsAtom* aTypeAtom, WidgetEvent* aEvent, 1241 Event* aDOMEvent, EventTarget* aCurrentTarget, bool aItemInShadowTree) { 1242 if (!aEvent->mCurrentTarget) { 1243 aEvent->mCurrentTarget = aCurrentTarget->GetTargetForDOMEvent(); 1244 if (!aEvent->mCurrentTarget) { 1245 return false; 1246 } 1247 } 1248 1249 aEvent->mFlags.mInPassiveListener = aListener->mFlags.mPassive; 1250 1251 nsCOMPtr<nsPIDOMWindowInner> innerWindow = 1252 WindowFromListener(aListener, aTypeAtom, aItemInShadowTree); 1253 mozilla::dom::Event* oldWindowEvent = nullptr; 1254 if (innerWindow) { 1255 oldWindowEvent = innerWindow->SetEvent(aDOMEvent); 1256 } 1257 1258 nsresult result = NS_OK; 1259 1260 // strong ref 1261 EventListenerHolder listenerHolder(aListener->mListener.Clone()); 1262 1263 // If this is a script handler and we haven't yet 1264 // compiled the event handler itself 1265 if ((aListener->mListenerType == Listener::eJSEventListener) && 1266 aListener->mHandlerIsString) { 1267 result = 1268 CompileEventHandlerInternal(aListener, aTypeAtom, nullptr, nullptr); 1269 aListener = nullptr; 1270 } 1271 1272 if (NS_SUCCEEDED(result)) { 1273 Maybe<EventCallbackDebuggerNotificationGuard> dbgGuard; 1274 if (dom::ChromeUtils::IsDevToolsOpened() || profiler_is_active()) { 1275 dbgGuard.emplace(aCurrentTarget, aDOMEvent); 1276 } 1277 nsAutoMicroTask mt; 1278 1279 // Event::currentTarget is set in EventDispatcher. 1280 if (listenerHolder.HasWebIDLCallback()) { 1281 ErrorResult rv; 1282 listenerHolder.GetWebIDLCallback()->HandleEvent(aCurrentTarget, 1283 *aDOMEvent, rv); 1284 result = rv.StealNSResult(); 1285 } else { 1286 // listenerHolder is holding a stack ref here. 1287 result = MOZ_KnownLive(listenerHolder.GetXPCOMCallback()) 1288 ->HandleEvent(aDOMEvent); 1289 } 1290 } 1291 1292 if (innerWindow) { 1293 (void)innerWindow->SetEvent(oldWindowEvent); 1294 } 1295 1296 if (NS_FAILED(result)) { 1297 aEvent->mFlags.mExceptionWasRaised = true; 1298 } 1299 aEvent->mFlags.mInPassiveListener = false; 1300 return !aEvent->mFlags.mImmediatePropagationStopped; 1301 } 1302 1303 /* static */ EventMessage EventListenerManager::GetLegacyEventMessage( 1304 EventMessage aEventMessage) { 1305 // webkit-prefixed legacy events: 1306 if (aEventMessage == eTransitionEnd) { 1307 return eWebkitTransitionEnd; 1308 } 1309 if (aEventMessage == eAnimationStart) { 1310 return eWebkitAnimationStart; 1311 } 1312 if (aEventMessage == eAnimationEnd) { 1313 return eWebkitAnimationEnd; 1314 } 1315 if (aEventMessage == eAnimationIteration) { 1316 return eWebkitAnimationIteration; 1317 } 1318 1319 switch (aEventMessage) { 1320 case eFullscreenChange: 1321 return eMozFullscreenChange; 1322 case eFullscreenError: 1323 return eMozFullscreenError; 1324 default: 1325 return aEventMessage; 1326 } 1327 } 1328 1329 EventMessage EventListenerManager::GetEventMessage(nsAtom* aEventName) const { 1330 if (mIsMainThreadELM) { 1331 return nsContentUtils::GetEventMessage(aEventName); 1332 } 1333 1334 // The nsContentUtils event message hashtables aren't threadsafe, so just fall 1335 // back to eUnidentifiedEvent. 1336 return eUnidentifiedEvent; 1337 } 1338 1339 EventMessage EventListenerManager::GetEventMessageAndAtomForListener( 1340 const nsAString& aType, nsAtom** aAtom) { 1341 if (mIsMainThreadELM) { 1342 return nsContentUtils::GetEventMessageAndAtomForListener(aType, aAtom); 1343 } 1344 1345 *aAtom = NS_Atomize(u"on"_ns + aType).take(); 1346 return eUnidentifiedEvent; 1347 } 1348 1349 already_AddRefed<nsPIDOMWindowInner> EventListenerManager::WindowFromListener( 1350 Listener* aListener, nsAtom* aTypeAtom, bool aItemInShadowTree) { 1351 nsCOMPtr<nsPIDOMWindowInner> innerWindow; 1352 if (!aItemInShadowTree) { 1353 if (aListener->mListener.HasWebIDLCallback()) { 1354 CallbackObject* callback = aListener->mListener.GetWebIDLCallback(); 1355 nsIGlobalObject* global = nullptr; 1356 if (callback) { 1357 global = callback->IncumbentGlobalOrNull(); 1358 } 1359 if (global) { 1360 innerWindow = global->GetAsInnerWindow(); // Can be nullptr 1361 } 1362 } else if (mTarget) { 1363 // This ensures `window.event` can be set properly for 1364 // nsWindowRoot to handle KeyPress event. 1365 if (aListener && aTypeAtom == nsGkAtoms::onkeypress && 1366 mTarget->IsRootWindow()) { 1367 nsPIWindowRoot* root = mTarget->AsWindowRoot(); 1368 if (nsPIDOMWindowOuter* outerWindow = root->GetWindow()) { 1369 innerWindow = outerWindow->GetCurrentInnerWindow(); 1370 } 1371 } else { 1372 // Can't get the global from 1373 // listener->mListener.GetXPCOMCallback(). 1374 // In most cases, it would be the same as for 1375 // the target, so let's do that. 1376 if (nsIGlobalObject* global = mTarget->GetOwnerGlobal()) { 1377 innerWindow = global->GetAsInnerWindow(); 1378 } 1379 } 1380 } 1381 } 1382 return innerWindow.forget(); 1383 } 1384 1385 Maybe<size_t> EventListenerManager::EventListenerMap::EntryIndexForType( 1386 nsAtom* aTypeAtom) const { 1387 MOZ_ASSERT(aTypeAtom); 1388 1389 size_t matchIndexOrInsertionPoint = 0; 1390 bool foundMatch = BinarySearchIf(mEntries, 0, mEntries.Length(), 1391 ListenerMapEntryComparator(aTypeAtom), 1392 &matchIndexOrInsertionPoint); 1393 return foundMatch ? Some(matchIndexOrInsertionPoint) : Nothing(); 1394 } 1395 1396 Maybe<size_t> EventListenerManager::EventListenerMap::EntryIndexForAllEvents() 1397 const { 1398 // If we have an entry for "all events listeners", it'll be at the beginning 1399 // of the list and its type atom will be null. 1400 return !mEntries.IsEmpty() && mEntries[0].mTypeAtom == nullptr ? Some(0) 1401 : Nothing(); 1402 } 1403 1404 RefPtr<EventListenerManager::ListenerArray> 1405 EventListenerManager::EventListenerMap::GetListenersForType( 1406 nsAtom* aTypeAtom) const { 1407 Maybe<size_t> index = EntryIndexForType(aTypeAtom); 1408 return index ? mEntries[*index].mListeners : nullptr; 1409 } 1410 1411 RefPtr<EventListenerManager::ListenerArray> 1412 EventListenerManager::EventListenerMap::GetListenersForAllEvents() const { 1413 Maybe<size_t> index = EntryIndexForAllEvents(); 1414 return index ? mEntries[*index].mListeners : nullptr; 1415 } 1416 1417 RefPtr<EventListenerManager::ListenerArray> 1418 EventListenerManager::EventListenerMap::GetOrCreateListenersForType( 1419 nsAtom* aTypeAtom) { 1420 MOZ_ASSERT(aTypeAtom); 1421 size_t matchIndexOrInsertionPoint = 0; 1422 bool foundMatch = BinarySearchIf(mEntries, 0, mEntries.Length(), 1423 ListenerMapEntryComparator(aTypeAtom), 1424 &matchIndexOrInsertionPoint); 1425 if (foundMatch) { 1426 return mEntries[matchIndexOrInsertionPoint].mListeners; 1427 } 1428 RefPtr<ListenerArray> listeners = MakeRefPtr<ListenerArray>(); 1429 mEntries.InsertElementAt(matchIndexOrInsertionPoint, 1430 EventListenerMapEntry{aTypeAtom, listeners}); 1431 1432 return listeners; 1433 } 1434 1435 RefPtr<EventListenerManager::ListenerArray> 1436 EventListenerManager::EventListenerMap::GetOrCreateListenersForAllEvents() { 1437 RefPtr<ListenerArray> listeners = GetListenersForAllEvents(); 1438 if (!listeners) { 1439 listeners = MakeRefPtr<ListenerArray>(); 1440 mEntries.InsertElementAt(0, EventListenerMapEntry{nullptr, listeners}); 1441 } 1442 return listeners; 1443 } 1444 1445 void EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, 1446 WidgetEvent* aEvent, 1447 Event** aDOMEvent, 1448 EventTarget* aCurrentTarget, 1449 nsEventStatus* aEventStatus, 1450 bool aItemInShadowTree) { 1451 MOZ_ASSERT_IF(aEvent->mMessage != eUnidentifiedEvent, mIsMainThreadELM); 1452 1453 // Set the value of the internal PreventDefault flag properly based on 1454 // aEventStatus 1455 if (!aEvent->DefaultPrevented() && 1456 *aEventStatus == nsEventStatus_eConsumeNoDefault) { 1457 // Assume that if only aEventStatus claims that the event has already been 1458 // consumed, the consumer is default event handler. 1459 aEvent->PreventDefault(); 1460 } 1461 1462 if (aEvent->mFlags.mImmediatePropagationStopped) { 1463 return; 1464 } 1465 1466 Maybe<AutoHandlingUserInputStatePusher> userInputStatePusher; 1467 Maybe<AutoPopupStatePusher> popupStatePusher; 1468 if (mIsMainThreadELM) { 1469 userInputStatePusher.emplace(UserActivation::IsUserInteractionEvent(aEvent), 1470 aEvent); 1471 popupStatePusher.emplace( 1472 PopupBlocker::GetEventPopupControlState(aEvent, *aDOMEvent)); 1473 } 1474 1475 RefPtr<nsAtom> typeAtom = nsContentUtils::GetEventType(aEvent); 1476 if (!typeAtom) { 1477 // Some messages don't have a corresponding type atom, e.g. 1478 // eMouseEnterIntoWidget. These events can't have a listener, so we 1479 // can stop here. 1480 return; 1481 } 1482 1483 EventMessage eventMessage = aEvent->mMessage; 1484 bool hasAnyListenerForEventType = false; 1485 1486 // First, notify any "all events" listeners. 1487 if (RefPtr<ListenerArray> listenersForAllEvents = 1488 mListenerMap.GetListenersForAllEvents()) { 1489 HandleEventWithListenerArray(listenersForAllEvents, typeAtom, eventMessage, 1490 aPresContext, aEvent, aDOMEvent, 1491 aCurrentTarget, aItemInShadowTree); 1492 hasAnyListenerForEventType = true; 1493 } 1494 1495 // Now look for listeners for typeAtom, and call them if we have any. 1496 bool hasAnyListenerMatchingGroup = false; 1497 if (RefPtr<ListenerArray> listeners = 1498 mListenerMap.GetListenersForType(typeAtom)) { 1499 hasAnyListenerMatchingGroup = HandleEventWithListenerArray( 1500 listeners, typeAtom, eventMessage, aPresContext, aEvent, aDOMEvent, 1501 aCurrentTarget, aItemInShadowTree); 1502 hasAnyListenerForEventType = true; 1503 } 1504 1505 if (!hasAnyListenerMatchingGroup && aEvent->IsTrusted()) { 1506 // If we didn't find any matching listeners, and our event has a legacy 1507 // version, check the listeners for the legacy version. 1508 EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage); 1509 if (legacyEventMessage != eventMessage) { 1510 MOZ_ASSERT( 1511 GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage, 1512 "Legacy event messages should not themselves have legacy versions"); 1513 RefPtr<nsAtom> legacyTypeAtom = 1514 nsContentUtils::GetEventTypeFromMessage(legacyEventMessage); 1515 if (RefPtr<ListenerArray> legacyListeners = 1516 mListenerMap.GetListenersForType(legacyTypeAtom)) { 1517 HandleEventWithListenerArray( 1518 legacyListeners, legacyTypeAtom, legacyEventMessage, aPresContext, 1519 aEvent, aDOMEvent, aCurrentTarget, aItemInShadowTree); 1520 hasAnyListenerForEventType = true; 1521 } 1522 } 1523 } 1524 1525 aEvent->mCurrentTarget = nullptr; 1526 1527 if (mIsMainThreadELM && !hasAnyListenerForEventType) { 1528 if (aEvent->mMessage != eUnidentifiedEvent) { 1529 mNoListenerForEvents[2] = mNoListenerForEvents[1]; 1530 mNoListenerForEvents[1] = mNoListenerForEvents[0]; 1531 mNoListenerForEvents[0] = aEvent->mMessage; 1532 } else { 1533 mNoListenerForEventAtom = aEvent->mSpecifiedEventType; 1534 } 1535 } 1536 1537 if (aEvent->DefaultPrevented()) { 1538 *aEventStatus = nsEventStatus_eConsumeNoDefault; 1539 } 1540 } 1541 1542 bool EventListenerManager::HandleEventWithListenerArray( 1543 ListenerArray* aListeners, nsAtom* aTypeAtom, EventMessage aEventMessage, 1544 nsPresContext* aPresContext, WidgetEvent* aEvent, Event** aDOMEvent, 1545 EventTarget* aCurrentTarget, bool aItemInShadowTree) { 1546 auto ensureDOMEvent = [&]() { 1547 if (!*aDOMEvent) { 1548 // Lazily create the DOM event. 1549 // This is tiny bit slow, but happens only once per event. 1550 // Similar code also in EventDispatcher. 1551 nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget; 1552 RefPtr<Event> event = 1553 EventDispatcher::CreateEvent(et, aPresContext, aEvent, u""_ns); 1554 event.forget(aDOMEvent); 1555 } 1556 return *aDOMEvent != nullptr; 1557 }; 1558 1559 Maybe<EventMessageAutoOverride> eventMessageAutoOverride; 1560 bool isOverridingEventMessage = aEvent->mMessage != aEventMessage; 1561 bool hasAnyListenerMatchingGroup = false; 1562 bool didReplaceOnceListener = false; 1563 1564 for (Listener& listenerRef : aListeners->EndLimitedRange()) { 1565 Listener* listener = &listenerRef; 1566 if (!ListenerCanHandle(listener, aEvent)) { 1567 continue; 1568 } 1569 hasAnyListenerMatchingGroup = true; 1570 1571 // Check that the phase is same in event and event listener. Also check 1572 // that the event is trusted or that the listener allows untrusted events. 1573 if (!listener->MatchesEventPhase(aEvent) || 1574 !listener->AllowsEventTrustedness(aEvent)) { 1575 continue; 1576 } 1577 1578 Maybe<Listener> listenerHolder; 1579 if (listener->mFlags.mOnce) { 1580 // Move the listener to the stack before handling the event. 1581 // The order is important, otherwise the listener could be 1582 // called again inside the listener. 1583 listenerHolder.emplace(std::move(*listener)); 1584 listener = listenerHolder.ptr(); 1585 didReplaceOnceListener = true; 1586 } 1587 if (ensureDOMEvent()) { 1588 if (isOverridingEventMessage && !eventMessageAutoOverride) { 1589 // Override the domEvent's event-message (its .type) until we 1590 // finish traversing listeners (when eventMessageAutoOverride 1591 // destructs). 1592 eventMessageAutoOverride.emplace(*aDOMEvent, aEventMessage); 1593 } 1594 if (!HandleEventSingleListener(listener, aTypeAtom, aEvent, *aDOMEvent, 1595 aCurrentTarget, aItemInShadowTree)) { 1596 break; 1597 } 1598 } 1599 } 1600 1601 if (didReplaceOnceListener) { 1602 // If there are any once listeners replaced with a placeholder during the 1603 // loop above, we need to clean up them here. Note that this could clear 1604 // once listeners handled in some outer level as well, but that should not 1605 // affect the result. 1606 size_t oldLength = aListeners->Length(); 1607 aListeners->NonObservingRemoveElementsBy([](const Listener& aListener) { 1608 return aListener.mListenerType == Listener::eNoListener; 1609 }); 1610 size_t newLength = aListeners->Length(); 1611 if (newLength == 0) { 1612 // Remove the entry that has now become empty. 1613 mListenerMap.mEntries.RemoveElementsBy([](EventListenerMapEntry& entry) { 1614 return entry.mListeners->IsEmpty(); 1615 }); 1616 } 1617 if (newLength < oldLength) { 1618 // Call NotifyEventListenerRemoved once for every removed listener. 1619 size_t removedCount = oldLength - newLength; 1620 for (size_t i = 0; i < removedCount; i++) { 1621 NotifyEventListenerRemoved(aTypeAtom); 1622 } 1623 if (IsDeviceType(aTypeAtom)) { 1624 // Call DisableDevice once for every removed listener. 1625 for (size_t i = 0; i < removedCount; i++) { 1626 DisableDevice(aTypeAtom); 1627 } 1628 } 1629 } 1630 } 1631 1632 return hasAnyListenerMatchingGroup; 1633 } 1634 1635 void EventListenerManager::Disconnect() { 1636 mTarget = nullptr; 1637 RemoveAllListenersSilently(); 1638 } 1639 1640 void EventListenerManager::AddEventListener(const nsAString& aType, 1641 EventListenerHolder aListenerHolder, 1642 bool aUseCapture, 1643 bool aWantsUntrusted) { 1644 EventListenerFlags flags; 1645 flags.mCapture = aUseCapture; 1646 flags.mAllowUntrustedEvents = aWantsUntrusted; 1647 return AddEventListenerByType(std::move(aListenerHolder), aType, flags); 1648 } 1649 1650 void EventListenerManager::AddEventListener( 1651 const nsAString& aType, EventListenerHolder aListenerHolder, 1652 const dom::AddEventListenerOptionsOrBoolean& aOptions, 1653 bool aWantsUntrusted) { 1654 EventListenerFlags flags; 1655 Optional<bool> passive; 1656 AbortSignal* signal = nullptr; 1657 if (aOptions.IsBoolean()) { 1658 flags.mCapture = aOptions.GetAsBoolean(); 1659 } else { 1660 const auto& options = aOptions.GetAsAddEventListenerOptions(); 1661 flags.mCapture = options.mCapture; 1662 flags.mInSystemGroup = options.mMozSystemGroup; 1663 flags.mOnce = options.mOnce; 1664 if (options.mPassive.WasPassed()) { 1665 passive.Construct(options.mPassive.Value()); 1666 } 1667 1668 if (options.mSignal.WasPassed()) { 1669 signal = &options.mSignal.Value(); 1670 } 1671 } 1672 1673 flags.mAllowUntrustedEvents = aWantsUntrusted; 1674 return AddEventListenerByType(std::move(aListenerHolder), aType, flags, 1675 passive, signal); 1676 } 1677 1678 void EventListenerManager::RemoveEventListener( 1679 const nsAString& aType, EventListenerHolder aListenerHolder, 1680 bool aUseCapture) { 1681 EventListenerFlags flags; 1682 flags.mCapture = aUseCapture; 1683 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags); 1684 } 1685 1686 void EventListenerManager::RemoveEventListener( 1687 const nsAString& aType, EventListenerHolder aListenerHolder, 1688 const dom::EventListenerOptionsOrBoolean& aOptions) { 1689 EventListenerFlags flags; 1690 if (aOptions.IsBoolean()) { 1691 flags.mCapture = aOptions.GetAsBoolean(); 1692 } else { 1693 const auto& options = aOptions.GetAsEventListenerOptions(); 1694 flags.mCapture = options.mCapture; 1695 flags.mInSystemGroup = options.mMozSystemGroup; 1696 } 1697 RemoveEventListenerByType(std::move(aListenerHolder), aType, flags); 1698 } 1699 1700 void EventListenerManager::AddListenerForAllEvents(EventListener* aDOMListener, 1701 bool aUseCapture, 1702 bool aWantsUntrusted, 1703 bool aSystemEventGroup) { 1704 EventListenerFlags flags; 1705 flags.mCapture = aUseCapture; 1706 flags.mAllowUntrustedEvents = aWantsUntrusted; 1707 flags.mInSystemGroup = aSystemEventGroup; 1708 AddEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents, 1709 nullptr, flags, false, true); 1710 } 1711 1712 void EventListenerManager::RemoveListenerForAllEvents( 1713 EventListener* aDOMListener, bool aUseCapture, bool aSystemEventGroup) { 1714 EventListenerFlags flags; 1715 flags.mCapture = aUseCapture; 1716 flags.mInSystemGroup = aSystemEventGroup; 1717 RemoveEventListenerInternal(EventListenerHolder(aDOMListener), nullptr, flags, 1718 true); 1719 } 1720 1721 bool EventListenerManager::HasListenersFor(const nsAString& aEventName) const { 1722 RefPtr<nsAtom> atom = NS_Atomize(u"on"_ns + aEventName); 1723 return HasListenersFor(atom); 1724 } 1725 1726 bool EventListenerManager::HasListenersFor(nsAtom* aEventNameWithOn) const { 1727 return HasListenersForInternal(aEventNameWithOn, false); 1728 } 1729 1730 bool EventListenerManager::HasNonPassiveListenersFor( 1731 const WidgetEvent* aEvent) const { 1732 if (RefPtr<nsAtom> typeAtom = nsContentUtils::GetEventType(aEvent)) { 1733 if (const auto& listeners = mListenerMap.GetListenersForType(typeAtom)) { 1734 for (const Listener& listener : listeners->NonObservingRange()) { 1735 if (!listener.mFlags.mPassive && ListenerCanHandle(&listener, aEvent)) { 1736 return true; 1737 } 1738 } 1739 } 1740 1741 // After dispatching wheel, legacy mouse scroll events are dispatched 1742 // and listeners on those can also default prevent the behavior. 1743 if (aEvent->mMessage == eWheel) { 1744 if (const auto& listeners = 1745 mListenerMap.GetListenersForType(nsGkAtoms::onDOMMouseScroll)) { 1746 for (const Listener& listener : listeners->NonObservingRange()) { 1747 if (!listener.mFlags.mPassive && 1748 ListenerCanHandle(&listener, aEvent)) { 1749 return true; 1750 } 1751 } 1752 } 1753 if (const auto& listeners = mListenerMap.GetListenersForType( 1754 nsGkAtoms::onMozMousePixelScroll)) { 1755 for (const Listener& listener : listeners->NonObservingRange()) { 1756 if (!listener.mFlags.mPassive && 1757 ListenerCanHandle(&listener, aEvent)) { 1758 return true; 1759 } 1760 } 1761 } 1762 } 1763 } 1764 1765 return false; 1766 } 1767 1768 bool EventListenerManager::ListenerCanHandle(const Listener* aListener, 1769 const WidgetEvent* aEvent) const { 1770 if (aListener->mListenerType == Listener::eNoListener) { 1771 // The listener is a placeholder value of a removed "once" listener. 1772 return false; 1773 } 1774 if (!aListener->mEnabled) { 1775 // The listener has been disabled, for example by devtools. 1776 return false; 1777 } 1778 if (!aListener->MatchesEventGroup(aEvent)) { 1779 return false; 1780 } 1781 1782 return true; 1783 } 1784 1785 bool EventListenerManager::HasNonSystemGroupListenersFor( 1786 nsAtom* aEventNameWithOn) const { 1787 return HasListenersForInternal(aEventNameWithOn, true); 1788 } 1789 1790 bool EventListenerManager::HasListenersForInternal( 1791 nsAtom* aEventNameWithOn, bool aIgnoreSystemGroup) const { 1792 #ifdef DEBUG 1793 nsAutoString name; 1794 aEventNameWithOn->ToString(name); 1795 #endif 1796 NS_ASSERTION(StringBeginsWith(name, u"on"_ns), 1797 "Event name does not start with 'on'"); 1798 RefPtr<ListenerArray> listeners = 1799 mListenerMap.GetListenersForType(aEventNameWithOn); 1800 if (!listeners) { 1801 return false; 1802 } 1803 1804 MOZ_ASSERT(!listeners->IsEmpty()); 1805 1806 if (!aIgnoreSystemGroup) { 1807 return true; 1808 } 1809 1810 // Check if any non-system-group listeners exist in `listeners`. 1811 for (const auto& listener : listeners->NonObservingRange()) { 1812 if (!listener.mFlags.mInSystemGroup) { 1813 return true; 1814 } 1815 } 1816 1817 return false; 1818 } 1819 1820 bool EventListenerManager::HasListeners() const { 1821 return !mListenerMap.IsEmpty(); 1822 } 1823 1824 nsresult EventListenerManager::GetListenerInfo( 1825 nsTArray<RefPtr<nsIEventListenerInfo>>& aList) { 1826 nsCOMPtr<EventTarget> target = mTarget; 1827 NS_ENSURE_STATE(target); 1828 aList.Clear(); 1829 for (const auto& entry : mListenerMap.mEntries) { 1830 for (const Listener& listener : entry.mListeners->ForwardRange()) { 1831 // If this is a script handler and we haven't yet 1832 // compiled the event handler itself go ahead and compile it 1833 if (listener.mListenerType == Listener::eJSEventListener && 1834 listener.mHandlerIsString) { 1835 CompileEventHandlerInternal(const_cast<Listener*>(&listener), 1836 entry.mTypeAtom, nullptr, nullptr); 1837 } 1838 nsAutoString eventType; 1839 if (listener.mAllEvents) { 1840 eventType.SetIsVoid(true); 1841 } else if (listener.mListenerType == Listener::eNoListener) { 1842 continue; 1843 } else { 1844 eventType.Assign(Substring(nsDependentAtomString(entry.mTypeAtom), 2)); 1845 } 1846 1847 JS::Rooted<JSObject*> callback(RootingCx()); 1848 JS::Rooted<JSObject*> callbackGlobal(RootingCx()); 1849 if (JSEventHandler* handler = listener.GetJSEventHandler()) { 1850 if (handler->GetTypedEventHandler().HasEventHandler()) { 1851 CallbackFunction* callbackFun = handler->GetTypedEventHandler().Ptr(); 1852 callback = callbackFun->CallableOrNull(); 1853 callbackGlobal = callbackFun->CallbackGlobalOrNull(); 1854 if (!callback) { 1855 // This will be null for cross-compartment event listeners 1856 // which have been destroyed. 1857 continue; 1858 } 1859 } 1860 } else if (listener.mListenerType == Listener::eWebIDLListener) { 1861 EventListener* listenerCallback = 1862 listener.mListener.GetWebIDLCallback(); 1863 callback = listenerCallback->CallbackOrNull(); 1864 callbackGlobal = listenerCallback->CallbackGlobalOrNull(); 1865 if (!callback) { 1866 // This will be null for cross-compartment event listeners 1867 // which have been destroyed. 1868 continue; 1869 } 1870 } 1871 1872 RefPtr<EventListenerInfo> info = new EventListenerInfo( 1873 this, eventType, callback, callbackGlobal, listener.mFlags.mCapture, 1874 listener.mFlags.mAllowUntrustedEvents, listener.mFlags.mInSystemGroup, 1875 listener.mListenerIsHandler); 1876 aList.AppendElement(info.forget()); 1877 } 1878 } 1879 return NS_OK; 1880 } 1881 1882 EventListenerManager::Listener* EventListenerManager::GetListenerFor( 1883 nsAString& aType, JSObject* aListener, bool aCapturing, 1884 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler) { 1885 NS_ENSURE_TRUE(aListener, nullptr); 1886 1887 RefPtr<ListenerArray> listeners = ([&]() -> RefPtr<ListenerArray> { 1888 if (aType.IsVoid()) { 1889 return mListenerMap.GetListenersForAllEvents(); 1890 } 1891 1892 for (auto& mapEntry : mListenerMap.mEntries) { 1893 if (RefPtr<nsAtom> typeAtom = mapEntry.mTypeAtom) { 1894 if (Substring(nsDependentAtomString(typeAtom), 2).Equals(aType)) { 1895 return mapEntry.mListeners; 1896 } 1897 } 1898 } 1899 1900 return nullptr; 1901 })(); 1902 1903 if (!listeners) { 1904 return nullptr; 1905 } 1906 1907 for (Listener& listener : listeners->ForwardRange()) { 1908 if (listener.mListenerType == Listener::eNoListener) { 1909 continue; 1910 } 1911 1912 if (listener.mFlags.mCapture != aCapturing || 1913 listener.mFlags.mAllowUntrustedEvents != aAllowsUntrusted || 1914 listener.mFlags.mInSystemGroup != aInSystemEventGroup) { 1915 continue; 1916 } 1917 1918 if (aIsHandler) { 1919 if (JSEventHandler* handler = listener.GetJSEventHandler()) { 1920 if (handler->GetTypedEventHandler().HasEventHandler()) { 1921 if (handler->GetTypedEventHandler().Ptr()->CallableOrNull() == 1922 aListener) { 1923 return &listener; 1924 } 1925 } 1926 } 1927 } else if (listener.mListenerType == Listener::eWebIDLListener && 1928 listener.mListener.GetWebIDLCallback()->CallbackOrNull() == 1929 aListener) { 1930 return &listener; 1931 } 1932 } 1933 return nullptr; 1934 } 1935 1936 nsresult EventListenerManager::IsListenerEnabled( 1937 nsAString& aType, JSObject* aListener, bool aCapturing, 1938 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler, 1939 bool* aEnabled) { 1940 Listener* listener = 1941 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted, 1942 aInSystemEventGroup, aIsHandler); 1943 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE); 1944 *aEnabled = listener->mEnabled; 1945 return NS_OK; 1946 } 1947 1948 nsresult EventListenerManager::SetListenerEnabled( 1949 nsAString& aType, JSObject* aListener, bool aCapturing, 1950 bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler, 1951 bool aEnabled) { 1952 Listener* listener = 1953 GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted, 1954 aInSystemEventGroup, aIsHandler); 1955 NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE); 1956 listener->mEnabled = aEnabled; 1957 if (aEnabled) { 1958 // We may have enabled some listener, clear the cache for which events 1959 // we don't have listeners. 1960 ClearNoListenersForEvents(); 1961 mNoListenerForEventAtom = nullptr; 1962 } 1963 return NS_OK; 1964 } 1965 1966 bool EventListenerManager::HasUnloadListeners() { 1967 return mListenerMap.GetListenersForType(nsGkAtoms::onunload) != nullptr; 1968 } 1969 1970 bool EventListenerManager::HasBeforeUnloadListeners() { 1971 return mListenerMap.GetListenersForType(nsGkAtoms::onbeforeunload) != nullptr; 1972 } 1973 1974 void EventListenerManager::SetEventHandler(nsAtom* aEventName, 1975 EventHandlerNonNull* aHandler) { 1976 if (!aHandler) { 1977 RemoveEventHandler(aEventName); 1978 return; 1979 } 1980 1981 // Untrusted events are always permitted for non-chrome script 1982 // handlers. 1983 SetEventHandlerInternal( 1984 aEventName, TypedEventHandler(aHandler), 1985 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome()); 1986 } 1987 1988 void EventListenerManager::SetEventHandler( 1989 OnErrorEventHandlerNonNull* aHandler) { 1990 if (!aHandler) { 1991 RemoveEventHandler(nsGkAtoms::onerror); 1992 return; 1993 } 1994 1995 // Untrusted events are always permitted on workers and for non-chrome script 1996 // on the main thread. 1997 bool allowUntrusted = !mIsMainThreadELM || !nsContentUtils::IsCallerChrome(); 1998 1999 SetEventHandlerInternal(nsGkAtoms::onerror, TypedEventHandler(aHandler), 2000 allowUntrusted); 2001 } 2002 2003 void EventListenerManager::SetEventHandler( 2004 OnBeforeUnloadEventHandlerNonNull* aHandler) { 2005 if (!aHandler) { 2006 RemoveEventHandler(nsGkAtoms::onbeforeunload); 2007 return; 2008 } 2009 2010 // Untrusted events are always permitted for non-chrome script 2011 // handlers. 2012 SetEventHandlerInternal( 2013 nsGkAtoms::onbeforeunload, TypedEventHandler(aHandler), 2014 !mIsMainThreadELM || !nsContentUtils::IsCallerChrome()); 2015 } 2016 2017 const TypedEventHandler* EventListenerManager::GetTypedEventHandler( 2018 nsAtom* aEventName) { 2019 Listener* listener = FindEventHandler(aEventName); 2020 2021 if (!listener) { 2022 return nullptr; 2023 } 2024 2025 JSEventHandler* jsEventHandler = listener->GetJSEventHandler(); 2026 2027 if (listener->mHandlerIsString) { 2028 CompileEventHandlerInternal(listener, aEventName, nullptr, nullptr); 2029 } 2030 2031 const TypedEventHandler& typedHandler = 2032 jsEventHandler->GetTypedEventHandler(); 2033 return typedHandler.HasEventHandler() ? &typedHandler : nullptr; 2034 } 2035 2036 size_t EventListenerManager::SizeOfIncludingThis( 2037 MallocSizeOf aMallocSizeOf) const { 2038 return aMallocSizeOf(this) + mListenerMap.SizeOfExcludingThis(aMallocSizeOf); 2039 } 2040 2041 size_t EventListenerManager::EventListenerMap::SizeOfExcludingThis( 2042 MallocSizeOf aMallocSizeOf) const { 2043 size_t n = mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf); 2044 for (const auto& entry : mEntries) { 2045 n += entry.SizeOfExcludingThis(aMallocSizeOf); 2046 } 2047 return n; 2048 } 2049 2050 size_t EventListenerManager::EventListenerMapEntry::SizeOfExcludingThis( 2051 MallocSizeOf aMallocSizeOf) const { 2052 return mListeners->SizeOfIncludingThis(aMallocSizeOf); 2053 } 2054 2055 size_t EventListenerManager::ListenerArray::SizeOfIncludingThis( 2056 MallocSizeOf aMallocSizeOf) const { 2057 size_t n = aMallocSizeOf(this); 2058 n += ShallowSizeOfExcludingThis(aMallocSizeOf); 2059 for (const auto& listener : NonObservingRange()) { 2060 JSEventHandler* jsEventHandler = listener.GetJSEventHandler(); 2061 if (jsEventHandler) { 2062 n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf); 2063 } 2064 } 2065 return n; 2066 } 2067 2068 uint32_t EventListenerManager::ListenerCount() const { 2069 uint32_t count = 0; 2070 for (const auto& entry : mListenerMap.mEntries) { 2071 count += entry.mListeners->Length(); 2072 } 2073 return count; 2074 } 2075 2076 void EventListenerManager::MarkForCC() { 2077 for (const auto& entry : mListenerMap.mEntries) { 2078 for (const auto& listener : entry.mListeners->NonObservingRange()) { 2079 JSEventHandler* jsEventHandler = listener.GetJSEventHandler(); 2080 if (jsEventHandler) { 2081 const TypedEventHandler& typedHandler = 2082 jsEventHandler->GetTypedEventHandler(); 2083 if (typedHandler.HasEventHandler()) { 2084 typedHandler.Ptr()->MarkForCC(); 2085 } 2086 } else if (listener.mListenerType == Listener::eWebIDLListener) { 2087 listener.mListener.GetWebIDLCallback()->MarkForCC(); 2088 } 2089 } 2090 } 2091 if (mRefCnt.IsPurple()) { 2092 mRefCnt.RemovePurple(); 2093 } 2094 } 2095 2096 void EventListenerManager::TraceListeners(JSTracer* aTrc) { 2097 for (const auto& entry : mListenerMap.mEntries) { 2098 for (const auto& listener : entry.mListeners->NonObservingRange()) { 2099 JSEventHandler* jsEventHandler = listener.GetJSEventHandler(); 2100 if (jsEventHandler) { 2101 const TypedEventHandler& typedHandler = 2102 jsEventHandler->GetTypedEventHandler(); 2103 if (typedHandler.HasEventHandler()) { 2104 mozilla::TraceScriptHolder(typedHandler.Ptr(), aTrc); 2105 } 2106 } else if (listener.mListenerType == Listener::eWebIDLListener) { 2107 mozilla::TraceScriptHolder(listener.mListener.GetWebIDLCallback(), 2108 aTrc); 2109 } 2110 // We might have eWrappedJSListener, but that is the legacy type for 2111 // JS implemented event listeners, and trickier to handle here. 2112 } 2113 } 2114 } 2115 2116 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() { 2117 for (const auto& entry : mListenerMap.mEntries) { 2118 if (entry.mTypeAtom != nsGkAtoms::onkeydown && 2119 entry.mTypeAtom != nsGkAtoms::onkeypress && 2120 entry.mTypeAtom != nsGkAtoms::onkeyup) { 2121 continue; 2122 } 2123 for (const auto& listener : entry.mListeners->NonObservingRange()) { 2124 if (!listener.mFlags.mInSystemGroup && 2125 listener.mFlags.mAllowUntrustedEvents) { 2126 return true; 2127 } 2128 } 2129 } 2130 return false; 2131 } 2132 2133 bool EventListenerManager:: 2134 HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() { 2135 for (const auto& entry : mListenerMap.mEntries) { 2136 if (entry.mTypeAtom != nsGkAtoms::onkeydown && 2137 entry.mTypeAtom != nsGkAtoms::onkeypress && 2138 entry.mTypeAtom != nsGkAtoms::onkeyup) { 2139 continue; 2140 } 2141 for (const auto& listener : entry.mListeners->NonObservingRange()) { 2142 if (!listener.mFlags.mPassive && !listener.mFlags.mInSystemGroup && 2143 listener.mFlags.mAllowUntrustedEvents) { 2144 return true; 2145 } 2146 } 2147 } 2148 return false; 2149 } 2150 2151 bool EventListenerManager::HasApzAwareListeners() { 2152 if (!mIsMainThreadELM) { 2153 return false; 2154 } 2155 2156 for (const auto& entry : mListenerMap.mEntries) { 2157 if (!IsApzAwareEvent(entry.mTypeAtom)) { 2158 continue; 2159 } 2160 for (const auto& listener : entry.mListeners->NonObservingRange()) { 2161 if (!listener.mFlags.mPassive) { 2162 return true; 2163 } 2164 } 2165 } 2166 return false; 2167 } 2168 2169 static bool IsWheelEventType(nsAtom* aEvent) { 2170 if (aEvent == nsGkAtoms::onwheel || aEvent == nsGkAtoms::onDOMMouseScroll || 2171 aEvent == nsGkAtoms::onmousewheel || 2172 aEvent == nsGkAtoms::onMozMousePixelScroll) { 2173 return true; 2174 } 2175 return false; 2176 } 2177 2178 bool EventListenerManager::IsApzAwareEvent(nsAtom* aEvent) { 2179 if (IsWheelEventType(aEvent)) { 2180 return true; 2181 } 2182 // In theory we should schedule a repaint if the touch event pref changes, 2183 // because the event regions might be out of date. In practice that seems like 2184 // overkill because users generally shouldn't be flipping this pref, much 2185 // less expecting touch listeners on the page to immediately start preventing 2186 // scrolling without so much as a repaint. Tests that we write can work 2187 // around this constraint easily enough. 2188 if (aEvent == nsGkAtoms::ontouchstart || aEvent == nsGkAtoms::ontouchmove) { 2189 return TouchEvent::PrefEnabled( 2190 nsContentUtils::GetDocShellForEventTarget(mTarget)); 2191 } 2192 return false; 2193 } 2194 2195 bool EventListenerManager::HasNonPassiveWheelListener() { 2196 MOZ_ASSERT(NS_IsMainThread()); 2197 for (const auto& entry : mListenerMap.mEntries) { 2198 if (!IsWheelEventType(entry.mTypeAtom)) { 2199 continue; 2200 } 2201 for (const auto& listener : entry.mListeners->NonObservingRange()) { 2202 if (!listener.mFlags.mPassive) { 2203 return true; 2204 } 2205 } 2206 } 2207 return false; 2208 } 2209 2210 void EventListenerManager::RemoveAllListeners() { 2211 while (!mListenerMap.IsEmpty()) { 2212 size_t entryIndex = mListenerMap.mEntries.Length() - 1; 2213 EventListenerMapEntry& entry = mListenerMap.mEntries[entryIndex]; 2214 RefPtr<nsAtom> type = entry.mTypeAtom; 2215 MOZ_ASSERT(!entry.mListeners->IsEmpty()); 2216 size_t idx = entry.mListeners->Length() - 1; 2217 entry.mListeners->RemoveElementAt(idx); 2218 if (entry.mListeners->IsEmpty()) { 2219 mListenerMap.mEntries.RemoveElementAt(entryIndex); 2220 } 2221 NotifyEventListenerRemoved(type); 2222 if (IsDeviceType(type)) { 2223 DisableDevice(type); 2224 } 2225 } 2226 } 2227 2228 already_AddRefed<nsIScriptGlobalObject> 2229 EventListenerManager::GetScriptGlobalAndDocument(Document** aDoc) { 2230 nsCOMPtr<Document> doc; 2231 nsCOMPtr<nsPIDOMWindowInner> win; 2232 if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) { 2233 // Try to get context from doc 2234 doc = node->OwnerDoc(); 2235 if (doc->IsLoadedAsData()) { 2236 return nullptr; 2237 } 2238 2239 win = do_QueryInterface(doc->GetScopeObject()); 2240 } else if ((win = GetTargetAsInnerWindow())) { 2241 doc = win->GetExtantDoc(); 2242 } 2243 2244 if (!win || !win->IsCurrentInnerWindow()) { 2245 return nullptr; 2246 } 2247 2248 doc.forget(aDoc); 2249 nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(win); 2250 return global.forget(); 2251 } 2252 2253 EventListenerManager::ListenerSignalFollower::ListenerSignalFollower( 2254 EventListenerManager* aListenerManager, 2255 EventListenerManager::Listener* aListener, nsAtom* aTypeAtom) 2256 : dom::AbortFollower(), 2257 mListenerManager(aListenerManager), 2258 mListener(aListener->mListener.Clone()), 2259 mTypeAtom(aTypeAtom), 2260 mAllEvents(aListener->mAllEvents), 2261 mFlags(aListener->mFlags) {}; 2262 2263 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower) 2264 2265 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower) 2266 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower) 2267 2268 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN( 2269 EventListenerManager::ListenerSignalFollower) 2270 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener) 2271 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 2272 2273 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN( 2274 EventListenerManager::ListenerSignalFollower) 2275 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener) 2276 tmp->mListenerManager = nullptr; 2277 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 2278 2279 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( 2280 EventListenerManager::ListenerSignalFollower) 2281 NS_INTERFACE_MAP_ENTRY(nsISupports) 2282 NS_INTERFACE_MAP_END 2283 2284 void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() { 2285 if (mListenerManager) { 2286 RefPtr<EventListenerManager> elm = mListenerManager; 2287 mListenerManager = nullptr; 2288 elm->RemoveEventListenerInternal(std::move(mListener), mTypeAtom, mFlags, 2289 mAllEvents); 2290 } 2291 } 2292 2293 } // namespace mozilla