Event.cpp (31235B)
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 "Event.h" 8 9 #include "AccessCheck.h" 10 #include "base/basictypes.h" 11 #include "ipc/IPCMessageUtils.h" 12 #include "ipc/IPCMessageUtilsSpecializations.h" 13 #include "mozilla/BasePrincipal.h" 14 #include "mozilla/ContentEvents.h" 15 #include "mozilla/DOMEventTargetHelper.h" 16 #include "mozilla/EventDispatcher.h" 17 #include "mozilla/EventStateManager.h" 18 #include "mozilla/MiscEvents.h" 19 #include "mozilla/MouseEvents.h" 20 #include "mozilla/PointerLockManager.h" 21 #include "mozilla/Preferences.h" 22 #include "mozilla/PresShell.h" 23 #include "mozilla/SVGOuterSVGFrame.h" 24 #include "mozilla/SVGUtils.h" 25 #include "mozilla/ScrollContainerFrame.h" 26 #include "mozilla/StaticPrefs_dom.h" 27 #include "mozilla/TextEvents.h" 28 #include "mozilla/TouchEvents.h" 29 #include "mozilla/ViewportUtils.h" 30 #include "mozilla/dom/Document.h" 31 #include "mozilla/dom/DocumentInlines.h" 32 #include "mozilla/dom/FragmentOrElement.h" 33 #include "mozilla/dom/Performance.h" 34 #include "mozilla/dom/ShadowRoot.h" 35 #include "mozilla/dom/WorkerPrivate.h" 36 #include "mozilla/dom/WorkerScope.h" 37 #include "nsCOMPtr.h" 38 #include "nsContentUtils.h" 39 #include "nsDeviceContext.h" 40 #include "nsError.h" 41 #include "nsGlobalWindowInner.h" 42 #include "nsIContent.h" 43 #include "nsIContentInlines.h" 44 #include "nsIFrame.h" 45 #include "nsJSEnvironment.h" 46 #include "nsLayoutUtils.h" 47 #include "nsPIWindowRoot.h" 48 #include "nsRFPService.h" 49 50 namespace mozilla::dom { 51 52 Event::Event(EventTarget* aOwner, nsPresContext* aPresContext, 53 WidgetEvent* aEvent) { 54 ConstructorInit(aOwner, aPresContext, aEvent); 55 } 56 57 Event::Event(nsPIDOMWindowInner* aParent) { 58 ConstructorInit(nsGlobalWindowInner::Cast(aParent), nullptr, nullptr); 59 } 60 61 void Event::ConstructorInit(EventTarget* aOwner, nsPresContext* aPresContext, 62 WidgetEvent* aEvent) { 63 SetOwner(aOwner); 64 mIsMainThreadEvent = NS_IsMainThread(); 65 if (mIsMainThreadEvent) { 66 mRefCnt.SetIsOnMainThread(); 67 } 68 69 mPrivateDataDuplicated = false; 70 mWantsPopupControlCheck = false; 71 72 if (aEvent) { 73 mEvent = aEvent; 74 mEventIsInternal = false; 75 } else { 76 mEventIsInternal = true; 77 /* 78 A derived class might want to allocate its own type of aEvent 79 (derived from WidgetEvent). To do this, it should take care to pass 80 a non-nullptr aEvent to this ctor, e.g.: 81 82 FooEvent::FooEvent(..., WidgetEvent* aEvent) 83 : Event(..., aEvent ? aEvent : new WidgetEvent()) 84 85 Then, to override the mEventIsInternal assignments done by the 86 base ctor, it should do this in its own ctor: 87 88 FooEvent::FooEvent(..., WidgetEvent* aEvent) 89 ... 90 { 91 ... 92 if (aEvent) { 93 mEventIsInternal = false; 94 } 95 else { 96 mEventIsInternal = true; 97 } 98 ... 99 } 100 */ 101 mEvent = new WidgetEvent(false, eVoidEvent); 102 } 103 104 InitPresContextData(aPresContext); 105 } 106 107 void Event::InitPresContextData(nsPresContext* aPresContext) { 108 mPresContext = aPresContext; 109 // Get the explicit original target (if it's anonymous make it null) 110 { 111 nsIContent* content = GetTargetFromFrame(); 112 if (content && !content->IsInNativeAnonymousSubtree()) { 113 mExplicitOriginalTarget = content; 114 } else { 115 mExplicitOriginalTarget = nullptr; 116 } 117 } 118 } 119 120 Event::~Event() { 121 NS_ASSERT_OWNINGTHREAD(Event); 122 123 if (mEventIsInternal && mEvent) { 124 delete mEvent; 125 } 126 } 127 128 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Event) 129 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 130 NS_INTERFACE_MAP_ENTRY(nsISupports) 131 NS_INTERFACE_MAP_ENTRY(Event) 132 NS_INTERFACE_MAP_END 133 134 NS_IMPL_CYCLE_COLLECTING_ADDREF(Event) 135 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Event, LastRelease()) 136 137 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Event) 138 139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event) 140 if (tmp->mEventIsInternal) { 141 tmp->mEvent->mTarget = nullptr; 142 tmp->mEvent->mCurrentTarget = nullptr; 143 tmp->mEvent->mOriginalTarget = nullptr; 144 tmp->mEvent->mRelatedTarget = nullptr; 145 tmp->mEvent->mOriginalRelatedTarget = nullptr; 146 switch (tmp->mEvent->mClass) { 147 case eDragEventClass: { 148 WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); 149 dragEvent->mDataTransfer = nullptr; 150 break; 151 } 152 case eClipboardEventClass: 153 tmp->mEvent->AsClipboardEvent()->mClipboardData = nullptr; 154 break; 155 case eEditorInputEventClass: { 156 InternalEditorInputEvent* inputEvent = 157 tmp->mEvent->AsEditorInputEvent(); 158 inputEvent->mDataTransfer = nullptr; 159 inputEvent->mTargetRanges.Clear(); 160 break; 161 } 162 default: 163 break; 164 } 165 166 if (WidgetMouseEvent* mouseEvent = tmp->mEvent->AsMouseEvent()) { 167 mouseEvent->mClickTarget = nullptr; 168 mouseEvent->mTriggerEvent = nullptr; 169 } 170 } 171 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExplicitOriginalTarget); 172 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner); 173 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 174 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 175 176 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event) 177 if (tmp->mEventIsInternal) { 178 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mTarget) 179 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mCurrentTarget) 180 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalTarget) 181 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mRelatedTarget) 182 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalRelatedTarget); 183 switch (tmp->mEvent->mClass) { 184 case eDragEventClass: { 185 WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); 186 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mDataTransfer"); 187 cb.NoteXPCOMChild(dragEvent->mDataTransfer); 188 break; 189 } 190 case eClipboardEventClass: 191 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mClipboardData"); 192 cb.NoteXPCOMChild(tmp->mEvent->AsClipboardEvent()->mClipboardData); 193 break; 194 case eEditorInputEventClass: 195 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mDataTransfer"); 196 cb.NoteXPCOMChild(tmp->mEvent->AsEditorInputEvent()->mDataTransfer); 197 NS_IMPL_CYCLE_COLLECTION_TRAVERSE( 198 mEvent->AsEditorInputEvent()->mTargetRanges); 199 break; 200 default: 201 break; 202 } 203 204 if (WidgetMouseEvent* mouseEvent = tmp->mEvent->AsMouseEvent()) { 205 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mClickTarget"); 206 cb.NoteXPCOMChild(mouseEvent->mClickTarget); 207 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mTriggerEvent"); 208 cb.NoteXPCOMChild(mouseEvent->mTriggerEvent); 209 } 210 } 211 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget) 212 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) 213 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 214 215 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Event) 216 if (tmp->HasKnownLiveWrapper()) { 217 if (tmp->mEventIsInternal) { 218 if (WidgetEvent* event = tmp->mEvent) { 219 auto mark = [](EventTarget* aTarget) { 220 if (!aTarget) { 221 return; 222 } 223 if (nsINode* node = aTarget->GetAsNode()) { 224 FragmentOrElement::MarkNodeChildren(node); 225 if (node->HasKnownLiveWrapper()) { 226 // Use CanSkip to possibly mark more nodes to be certainly alive. 227 FragmentOrElement::CanSkip(node, true); 228 } 229 } 230 }; 231 232 mark(event->mTarget); 233 mark(event->mCurrentTarget); 234 mark(event->mOriginalTarget); 235 mark(event->mRelatedTarget); 236 mark(event->mOriginalRelatedTarget); 237 } 238 } 239 return true; 240 } 241 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 242 243 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Event) 244 return tmp->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp); 245 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 246 247 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Event) 248 return tmp->HasKnownLiveWrapper(); 249 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 250 251 void Event::LastRelease() { 252 nsISupports* supports = nullptr; 253 QueryInterface(NS_GET_IID(nsCycleCollectionISupports), 254 reinterpret_cast<void**>(&supports)); 255 nsXPCOMCycleCollectionParticipant* p = nullptr; 256 CallQueryInterface(this, &p); 257 p->Unlink(supports); 258 } 259 260 JSObject* Event::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 261 return WrapObjectInternal(aCx, aGivenProto); 262 } 263 264 JSObject* Event::WrapObjectInternal(JSContext* aCx, 265 JS::Handle<JSObject*> aGivenProto) { 266 return Event_Binding::Wrap(aCx, this, aGivenProto); 267 } 268 269 void Event::GetType(nsAString& aType) const { 270 GetWidgetEventType(mEvent, aType); 271 } 272 273 EventTarget* Event::GetTarget() const { return mEvent->GetDOMEventTarget(); } 274 275 already_AddRefed<Document> Event::GetDocument() const { 276 nsCOMPtr<EventTarget> eventTarget = GetTarget(); 277 278 if (!eventTarget) { 279 return nullptr; 280 } 281 282 nsIGlobalObject* global = eventTarget->GetOwnerGlobal(); 283 if (!global) { 284 return nullptr; 285 } 286 287 nsPIDOMWindowInner* win = global->GetAsInnerWindow(); 288 if (!win) { 289 return nullptr; 290 } 291 292 nsCOMPtr<Document> doc; 293 doc = win->GetExtantDoc(); 294 295 return doc.forget(); 296 } 297 298 EventTarget* Event::GetCurrentTarget() const { 299 return mEvent->GetCurrentDOMEventTarget(); 300 } 301 302 void Event::ComposedPath(nsTArray<RefPtr<EventTarget>>& aPath) { 303 EventDispatcher::GetComposedPathFor(mEvent, aPath); 304 } 305 306 // 307 // Get the actual event target node (may have been retargeted for mouse events) 308 // 309 nsIContent* Event::GetTargetFromFrame() { 310 if (!mPresContext) { 311 return nullptr; 312 } 313 314 // Get the mTarget frame (have to get the ESM first) 315 nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); 316 if (!targetFrame) { 317 return nullptr; 318 } 319 320 // get the real content 321 return targetFrame->GetContentForEvent(mEvent); 322 } 323 324 EventTarget* Event::GetExplicitOriginalTarget() const { 325 if (mExplicitOriginalTarget) { 326 return mExplicitOriginalTarget; 327 } 328 return GetTarget(); 329 } 330 331 EventTarget* Event::GetOriginalTarget() const { 332 return mEvent->GetOriginalDOMEventTarget(); 333 } 334 335 EventTarget* Event::GetOriginalTarget(CallerType aCallerType) const { 336 if (aCallerType == CallerType::System || nsContentUtils::IsCallerUAWidget()) { 337 return GetOriginalTarget(); 338 } 339 340 EventTarget* et = mEvent->GetOriginalDOMEventTarget(); 341 nsIContent* content = nsIContent::FromEventTargetOrNull(et); 342 if (!content) { 343 return et; 344 } 345 return content->FindFirstNonChromeOnlyAccessContent(); 346 } 347 348 EventTarget* Event::GetComposedTarget() const { 349 EventTarget* et = GetOriginalTarget(); 350 nsIContent* content = nsIContent::FromEventTargetOrNull(et); 351 if (!content) { 352 return et; 353 } 354 nsIContent* nonChrome = content->FindFirstNonChromeOnlyAccessContent(); 355 return nonChrome ? static_cast<EventTarget*>(nonChrome) 356 : static_cast<EventTarget*>(content->GetComposedDoc()); 357 } 358 359 void Event::SetTrusted(bool aTrusted) { mEvent->mFlags.mIsTrusted = aTrusted; } 360 361 bool Event::ShouldIgnoreChromeEventTargetListener() const { 362 MOZ_ASSERT(NS_IsMainThread()); 363 if (!XRE_IsParentProcess()) { 364 return false; 365 } 366 if (EventTarget* currentTarget = GetCurrentTarget(); 367 NS_WARN_IF(!currentTarget) || !currentTarget->IsRootWindow()) { 368 return false; 369 } 370 EventTarget* et = GetOriginalTarget(); 371 if (NS_WARN_IF(!et)) { 372 return false; 373 } 374 nsIGlobalObject* global = et->GetOwnerGlobal(); 375 if (NS_WARN_IF(!global)) { 376 return false; 377 } 378 nsPIDOMWindowInner* win = global->GetAsInnerWindow(); 379 if (NS_WARN_IF(!win)) { 380 return false; 381 } 382 BrowsingContext* bc = win->GetBrowsingContext(); 383 if (NS_WARN_IF(!bc)) { 384 return false; 385 } 386 // If this is a content event on an nsWindowRoot, then we also handle this in 387 // InProcessBrowserChildMessageManager, so we can ignore this event. 388 return bc->IsContent(); 389 } 390 391 bool Event::Init(mozilla::dom::EventTarget* aGlobal) { 392 if (!mIsMainThreadEvent) { 393 return IsCurrentThreadRunningChromeWorker(); 394 } 395 bool trusted = false; 396 if (aGlobal) { 397 if (nsPIDOMWindowInner* w = aGlobal->GetAsInnerWindow()) { 398 if (Document* d = w->GetExtantDoc()) { 399 trusted = nsContentUtils::IsChromeDoc(d); 400 if (nsPresContext* presContext = d->GetPresContext()) { 401 InitPresContextData(presContext); 402 } 403 } 404 } 405 } 406 return trusted; 407 } 408 409 // static 410 already_AddRefed<Event> Event::Constructor(const GlobalObject& aGlobal, 411 const nsAString& aType, 412 const EventInit& aParam) { 413 nsCOMPtr<mozilla::dom::EventTarget> t = 414 do_QueryInterface(aGlobal.GetAsSupports()); 415 return Constructor(t, aType, aParam); 416 } 417 418 // static 419 already_AddRefed<Event> Event::Constructor(EventTarget* aEventTarget, 420 const nsAString& aType, 421 const EventInit& aParam) { 422 RefPtr<Event> e = new Event(aEventTarget, nullptr, nullptr); 423 bool trusted = e->Init(aEventTarget); 424 e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable); 425 e->SetTrusted(trusted); 426 e->SetComposed(aParam.mComposed); 427 return e.forget(); 428 } 429 430 uint16_t Event::EventPhase() const { 431 if ((mEvent->mCurrentTarget && mEvent->mCurrentTarget == mEvent->mTarget) || 432 mEvent->mFlags.mInTargetPhase) { 433 return Event_Binding::AT_TARGET; 434 } 435 if (mEvent->mFlags.mInCapturePhase) { 436 return Event_Binding::CAPTURING_PHASE; 437 } 438 if (mEvent->mFlags.mInBubblingPhase) { 439 return Event_Binding::BUBBLING_PHASE; 440 } 441 return Event_Binding::NONE; 442 } 443 444 void Event::StopPropagation() { mEvent->StopPropagation(); } 445 446 void Event::StopImmediatePropagation() { mEvent->StopImmediatePropagation(); } 447 448 void Event::StopCrossProcessForwarding() { 449 mEvent->StopCrossProcessForwarding(); 450 } 451 452 void Event::PreventDefault() { 453 // This method is called only from C++ code which must handle default action 454 // of this event. So, pass true always. 455 PreventDefaultInternal(true); 456 } 457 458 void Event::PreventDefault(JSContext* aCx, CallerType aCallerType) { 459 // Note that at handling default action, another event may be dispatched. 460 // Then, JS in content mey be call preventDefault() 461 // even in the event is in system event group. Therefore, don't refer 462 // mInSystemGroup here. 463 nsIPrincipal* principal = 464 mIsMainThreadEvent ? nsContentUtils::SubjectPrincipal(aCx) : nullptr; 465 466 PreventDefaultInternal(aCallerType == CallerType::System, principal); 467 } 468 469 void Event::PreventDefaultInternal(bool aCalledByDefaultHandler, 470 nsIPrincipal* aPrincipal) { 471 if (mEvent->mFlags.mInPassiveListener) { 472 if (mOwner) { 473 if (nsPIDOMWindowInner* win = mOwner->GetAsInnerWindow()) { 474 if (Document* doc = win->GetExtantDoc()) { 475 if (!doc->HasWarnedAbout( 476 Document::ePreventDefaultFromPassiveListener)) { 477 AutoTArray<nsString, 1> params; 478 GetType(*params.AppendElement()); 479 doc->WarnOnceAbout(Document::ePreventDefaultFromPassiveListener, 480 false, params); 481 } 482 } 483 } 484 } 485 return; 486 } 487 if (!mEvent->mFlags.mCancelable) { 488 return; 489 } 490 491 mEvent->PreventDefault(aCalledByDefaultHandler, aPrincipal); 492 493 if (!IsTrusted()) { 494 return; 495 } 496 497 if (mEvent->mClass == eDragEventClass) { 498 UpdateDefaultPreventedOnContentForDragEvent(); 499 } 500 } 501 502 void Event::UpdateDefaultPreventedOnContentForDragEvent() { 503 WidgetDragEvent* dragEvent = mEvent->AsDragEvent(); 504 if (!dragEvent) { 505 return; 506 } 507 508 nsIPrincipal* principal = nullptr; 509 // Since we now have HTMLEditorEventListener registered on nsWindowRoot, 510 // mCurrentTarget could be nsWindowRoot, so we need to use 511 // mTarget if that's the case. 512 MOZ_ASSERT_IF(dragEvent->mInHTMLEditorEventListener, 513 mEvent->mCurrentTarget->IsRootWindow()); 514 EventTarget* target = dragEvent->mInHTMLEditorEventListener 515 ? mEvent->mTarget 516 : mEvent->mCurrentTarget; 517 518 nsINode* node = nsINode::FromEventTargetOrNull(target); 519 if (node) { 520 principal = node->NodePrincipal(); 521 } else { 522 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(target); 523 if (sop) { 524 principal = sop->GetPrincipal(); 525 } 526 } 527 if (principal && !principal->IsSystemPrincipal()) { 528 dragEvent->mDefaultPreventedOnContent = true; 529 } 530 } 531 532 void Event::SetEventType(const nsAString& aEventTypeArg) { 533 mEvent->mSpecifiedEventTypeString.Truncate(); 534 if (mIsMainThreadEvent) { 535 EventClassID classID = mEvent->mClass; 536 if (classID == eMouseEventClass) { 537 // Some pointer event types were changed from MouseEvent. For backward 538 // compatibility, we need to handle untrusted events of them created with 539 // MouseEvent instance in some places. 540 if (aEventTypeArg.EqualsLiteral(u"click") || 541 aEventTypeArg.EqualsLiteral(u"auxclick") || 542 aEventTypeArg.EqualsLiteral(u"contextmenu")) { 543 classID = ePointerEventClass; 544 } 545 } 546 mEvent->mSpecifiedEventType = nsContentUtils::GetEventMessageAndAtom( 547 aEventTypeArg, classID, &(mEvent->mMessage)); 548 mEvent->SetDefaultComposed(); 549 } else { 550 mEvent->mSpecifiedEventType = NS_Atomize(u"on"_ns + aEventTypeArg); 551 mEvent->mMessage = eUnidentifiedEvent; 552 mEvent->SetComposed(aEventTypeArg); 553 } 554 mEvent->SetDefaultComposedInNativeAnonymousContent(); 555 } 556 557 already_AddRefed<EventTarget> Event::EnsureWebAccessibleRelatedTarget( 558 EventTarget* aRelatedTarget) { 559 nsCOMPtr<EventTarget> relatedTarget = aRelatedTarget; 560 if (relatedTarget) { 561 nsIContent* content = nsIContent::FromEventTarget(relatedTarget); 562 if (content && content->ChromeOnlyAccess() && 563 !nsContentUtils::CanAccessNativeAnon()) { 564 content = content->FindFirstNonChromeOnlyAccessContent(); 565 relatedTarget = content; 566 } 567 568 if (relatedTarget) { 569 relatedTarget = relatedTarget->GetTargetForDOMEvent(); 570 } 571 } 572 return relatedTarget.forget(); 573 } 574 575 void Event::InitEvent(const nsAString& aEventTypeArg, 576 mozilla::CanBubble aCanBubbleArg, 577 mozilla::Cancelable aCancelableArg, 578 mozilla::Composed aComposedArg) { 579 // Make sure this event isn't already being dispatched. 580 NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched); 581 582 if (IsTrusted()) { 583 // Ensure the caller is permitted to dispatch trusted DOM events. 584 if (!nsContentUtils::ThreadsafeIsCallerChrome()) { 585 SetTrusted(false); 586 } 587 } 588 589 SetEventType(aEventTypeArg); 590 591 mEvent->mFlags.mBubbles = aCanBubbleArg == CanBubble::eYes; 592 mEvent->mFlags.mCancelable = aCancelableArg == Cancelable::eYes; 593 if (aComposedArg != Composed::eDefault) { 594 mEvent->mFlags.mComposed = aComposedArg == Composed::eYes; 595 } 596 597 mEvent->mFlags.mDefaultPrevented = false; 598 mEvent->mFlags.mDefaultPreventedByContent = false; 599 mEvent->mFlags.mDefaultPreventedByChrome = false; 600 mEvent->mFlags.mPropagationStopped = false; 601 mEvent->mFlags.mImmediatePropagationStopped = false; 602 603 // Clearing the old targets, so that the event is targeted correctly when 604 // re-dispatching it. 605 mEvent->mTarget = nullptr; 606 mEvent->mOriginalTarget = nullptr; 607 } 608 609 void Event::DuplicatePrivateData() { 610 NS_ASSERTION(mEvent, "No WidgetEvent for Event duplication!"); 611 if (mEventIsInternal) { 612 return; 613 } 614 615 mEvent = mEvent->Duplicate(); 616 mPresContext = nullptr; 617 mEventIsInternal = true; 618 mPrivateDataDuplicated = true; 619 } 620 621 void Event::SetTarget(EventTarget* aTarget) { mEvent->mTarget = aTarget; } 622 623 bool Event::IsDispatchStopped() { return mEvent->PropagationStopped(); } 624 625 WidgetEvent* Event::WidgetEventPtr() { return mEvent; } 626 627 // static 628 Maybe<CSSDoublePoint> Event::GetScreenCoords( 629 nsPresContext* aPresContext, WidgetEvent* aEvent, 630 const LayoutDeviceDoublePoint& aWidgetOrScreenRelativePoint) { 631 if (PointerLockManager::IsLocked()) { 632 return Some(EventStateManager::sLastScreenPoint); 633 } 634 635 if (!aEvent || !aEvent->DOMEventSupportsCoords()) { 636 return Nothing(); 637 } 638 639 // Doing a straight conversion from LayoutDeviceDoublePoint to CSSDoublePoint 640 // seem incorrect, but it is needed to maintain legacy functionality. 641 const WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent(); 642 if (MOZ_UNLIKELY(!aPresContext) || !(guiEvent && guiEvent->mWidget)) { 643 // XXX aPresContext is usually available. Then, we can know the latest 644 // scale of the document. Should we apply it? 645 return Some(CSSDoublePoint(aWidgetOrScreenRelativePoint.x, 646 aWidgetOrScreenRelativePoint.y)); 647 } 648 649 // (Potentially) transform the point from the coordinate space of an 650 // out-of-process iframe to the coordinate space of the native 651 // window. The transform can only be applied to a point whose components 652 // are floating-point values, so convert the integer point first, then 653 // transform, and then round the result back to an integer point. 654 const LayoutDeviceIntPoint topLevelPoint = LayoutDeviceIntPoint::Round( 655 guiEvent->mWidget->WidgetToTopLevelWidgetTransform().TransformPoint( 656 aWidgetOrScreenRelativePoint)); 657 const CSSPoint pt = CSSPixel::FromAppUnits( 658 LayoutDevicePixel::ToAppUnits( 659 topLevelPoint, aPresContext->DeviceContext()->AppUnitsPerDevPixel()) + 660 LayoutDevicePixel::ToAppUnits( 661 guiEvent->mWidget->TopLevelWidgetToScreenOffset(), 662 aPresContext->DeviceContext()->AppUnitsPerDevPixel())); 663 return Some(CSSDoublePoint(pt.x, pt.y)); 664 } 665 666 // static 667 CSSDoublePoint Event::GetPageCoords( 668 nsPresContext* aPresContext, WidgetEvent* aEvent, 669 const LayoutDeviceDoublePoint& aWidgetOrScreenRelativePoint, 670 const CSSDoublePoint& aDefaultClientPoint) { 671 const CSSDoublePoint clientCoords = Event::GetClientCoords( 672 aPresContext, aEvent, aWidgetOrScreenRelativePoint, aDefaultClientPoint); 673 674 // If there is some scrolling, add scroll info to client point. 675 const CSSPoint scrollPoint = CSSPixel::FromAppUnits([&]() { 676 if (aPresContext && aPresContext->GetPresShell()) { 677 if (const ScrollContainerFrame* const sf = 678 aPresContext->PresShell()->GetRootScrollContainerFrame()) { 679 return sf->GetScrollPosition(); 680 } 681 } 682 return nsPoint{}; 683 }()); 684 return clientCoords + CSSDoublePoint(scrollPoint.x, scrollPoint.y); 685 } 686 687 // static 688 CSSDoublePoint Event::GetClientCoords( 689 nsPresContext* aPresContext, WidgetEvent* aEvent, 690 const LayoutDeviceDoublePoint& aWidgetOrScreenRelativePoint, 691 const CSSDoublePoint& aDefaultClientPoint) { 692 if (PointerLockManager::IsLocked()) { 693 return EventStateManager::sLastClientPoint; 694 } 695 696 if (MOZ_UNLIKELY(!aPresContext) || MOZ_UNLIKELY(!aEvent) || 697 !aEvent->DOMEventSupportsCoords() || 698 MOZ_UNLIKELY(!aEvent->AsGUIEvent()->mWidget)) { 699 return aDefaultClientPoint; 700 } 701 702 const PresShell* const presShell = aPresContext->GetPresShell(); 703 if (MOZ_UNLIKELY(!presShell)) { 704 return CSSDoublePoint(0, 0); 705 } 706 // XXX Why don't we flush pending notifications before computing the offset 707 // from the root frame? 708 const nsIFrame* const rootFrame = presShell->GetRootFrame(); 709 if (MOZ_UNLIKELY(!rootFrame)) { 710 return CSSDoublePoint(0, 0); 711 } 712 const CSSPoint pt = 713 CSSPixel::FromAppUnits(nsLayoutUtils::GetEventCoordinatesRelativeTo( 714 aEvent, LayoutDeviceIntPoint::Round(aWidgetOrScreenRelativePoint), 715 RelativeTo{rootFrame})); 716 return CSSDoublePoint(pt.x, pt.y); 717 } 718 719 // static 720 nsIFrame* Event::GetPrimaryFrameOfEventTarget(const nsPresContext& aPresContext, 721 const WidgetEvent& aEvent) { 722 const nsCOMPtr<nsIContent> content = 723 nsIContent::FromEventTargetOrNull(aEvent.mTarget); 724 if (!content) { 725 return nullptr; 726 } 727 // XXX Even after the event target content is moved to different document, we 728 // may get its primary frame. In this case, should we return nullptr here? 729 nsIFrame* const frame = content->GetPrimaryFrame(FlushType::Layout); 730 if (MOZ_UNLIKELY(!frame || frame->PresContext() != &aPresContext)) { 731 return nullptr; 732 } 733 // For compat, see https://github.com/w3c/csswg-drafts/issues/1508. In SVG 734 // we just return the coordinates of the outer SVG box. This is all kinda 735 // unfortunate. 736 if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) && 737 StaticPrefs::dom_events_offset_in_svg_relative_to_svg_root()) { 738 return SVGUtils::GetOuterSVGFrame(frame); 739 } 740 return frame; 741 } 742 743 // static 744 CSSDoublePoint Event::GetOffsetCoords( 745 nsPresContext* aPresContext, WidgetEvent* aEvent, 746 const LayoutDeviceDoublePoint& aWidgetOrScreenRelativePoint, 747 const CSSDoublePoint& aDefaultClientPoint) { 748 if (!aEvent->mTarget) { 749 return GetPageCoords(aPresContext, aEvent, aWidgetOrScreenRelativePoint, 750 aDefaultClientPoint); 751 } 752 if (!nsIContent::FromEventTarget(aEvent->mTarget) || !aPresContext) { 753 return CSSDoublePoint(); 754 } 755 const nsIFrame* const frame = 756 GetPrimaryFrameOfEventTarget(*aPresContext, *aEvent); 757 if (MOZ_UNLIKELY(!frame)) { 758 return CSSDoublePoint(); 759 } 760 MOZ_ASSERT(aPresContext->PresShell()->GetRootFrame()); 761 const CSSDoublePoint clientCoords = GetClientCoords( 762 aPresContext, aEvent, aWidgetOrScreenRelativePoint, aDefaultClientPoint); 763 nsPoint ptInAppUnits = CSSPixel::ToAppUnits(CSSPoint( 764 static_cast<float>(clientCoords.x), static_cast<float>(clientCoords.y))); 765 if (nsLayoutUtils::TransformPoint( 766 RelativeTo{aPresContext->PresShell()->GetRootFrame()}, 767 RelativeTo{frame}, 768 ptInAppUnits) != nsLayoutUtils::TRANSFORM_SUCCEEDED) { 769 return CSSDoublePoint(); 770 } 771 ptInAppUnits -= frame->GetPaddingRectRelativeToSelf().TopLeft(); 772 const CSSPoint pt = CSSPixel::FromAppUnits(ptInAppUnits); 773 return CSSDoublePoint(pt.x, pt.y); 774 } 775 776 // To be called ONLY by Event::GetType (which has the additional 777 // logic for handling user-defined events). 778 // static 779 const char16_t* Event::GetEventName(EventMessage aEventType) { 780 switch (aEventType) { 781 #define MESSAGE_TO_EVENT(name_, _message, _type, _struct) \ 782 case _message: \ 783 return u"" #name_; 784 #include "mozilla/EventNameList.h" 785 #undef MESSAGE_TO_EVENT 786 default: 787 break; 788 } 789 // XXXldb We can hit this case for WidgetEvent objects that we didn't 790 // create and that are not user defined events since this function and 791 // SetEventType are incomplete. (But fixing that requires fixing the 792 // arrays in nsEventListenerManager too, since the events for which 793 // this is a problem generally *are* created by Event.) 794 return nullptr; 795 } 796 797 bool Event::DefaultPrevented(CallerType aCallerType) const { 798 NS_ENSURE_TRUE(mEvent, false); 799 800 // If preventDefault() has never been called, just return false. 801 if (!mEvent->DefaultPrevented()) { 802 return false; 803 } 804 805 // If preventDefault() has been called by content, return true. Otherwise, 806 // i.e., preventDefault() has been called by chrome, return true only when 807 // this is called by chrome. 808 return mEvent->DefaultPreventedByContent() || 809 aCallerType == CallerType::System; 810 } 811 812 bool Event::ReturnValue(CallerType aCallerType) const { 813 return !DefaultPrevented(aCallerType); 814 } 815 816 void Event::SetReturnValue(bool aReturnValue, CallerType aCallerType) { 817 if (!aReturnValue) { 818 PreventDefaultInternal(aCallerType == CallerType::System); 819 } 820 } 821 822 double Event::TimeStamp() { 823 if (mEvent->mTimeStamp.IsNull()) { 824 return 0.0; 825 } 826 827 if (mIsMainThreadEvent) { 828 if (NS_WARN_IF(!mOwner)) { 829 return 0.0; 830 } 831 832 nsPIDOMWindowInner* win = mOwner->GetAsInnerWindow(); 833 if (NS_WARN_IF(!win)) { 834 return 0.0; 835 } 836 837 Performance* perf = win->GetPerformance(); 838 if (NS_WARN_IF(!perf)) { 839 return 0.0; 840 } 841 842 double ret = 843 perf->GetDOMTiming()->TimeStampToDOMHighRes(mEvent->mTimeStamp); 844 MOZ_ASSERT(mOwner->PrincipalOrNull()); 845 846 return nsRFPService::ReduceTimePrecisionAsMSecs( 847 ret, perf->GetRandomTimelineSeed(), perf->GetRTPCallerType()); 848 } 849 850 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 851 MOZ_ASSERT(workerPrivate); 852 853 double ret = workerPrivate->TimeStampToDOMHighRes(mEvent->mTimeStamp); 854 855 return nsRFPService::ReduceTimePrecisionAsMSecs( 856 ret, workerPrivate->GetRandomTimelineSeed(), 857 workerPrivate->GlobalScope()->GetRTPCallerType()); 858 } 859 860 void Event::Serialize(IPC::MessageWriter* aWriter, 861 bool aSerializeInterfaceType) { 862 if (aSerializeInterfaceType) { 863 IPC::WriteParam(aWriter, u"event"_ns); 864 } 865 866 nsString type; 867 GetType(type); 868 IPC::WriteParam(aWriter, type); 869 870 IPC::WriteParam(aWriter, Bubbles()); 871 IPC::WriteParam(aWriter, Cancelable()); 872 IPC::WriteParam(aWriter, IsTrusted()); 873 IPC::WriteParam(aWriter, Composed()); 874 875 // No timestamp serialization for now! 876 } 877 878 bool Event::Deserialize(IPC::MessageReader* aReader) { 879 nsString type; 880 NS_ENSURE_TRUE(IPC::ReadParam(aReader, &type), false); 881 882 bool bubbles = false; 883 NS_ENSURE_TRUE(IPC::ReadParam(aReader, &bubbles), false); 884 885 bool cancelable = false; 886 NS_ENSURE_TRUE(IPC::ReadParam(aReader, &cancelable), false); 887 888 bool trusted = false; 889 NS_ENSURE_TRUE(IPC::ReadParam(aReader, &trusted), false); 890 891 bool composed = false; 892 NS_ENSURE_TRUE(IPC::ReadParam(aReader, &composed), false); 893 894 InitEvent(type, bubbles, cancelable); 895 SetTrusted(trusted); 896 SetComposed(composed); 897 898 return true; 899 } 900 901 void Event::SetOwner(EventTarget* aOwner) { 902 mOwner = nullptr; 903 904 if (!aOwner) { 905 return; 906 } 907 908 if (nsINode* n = aOwner->GetAsNode()) { 909 mOwner = n->OwnerDoc()->GetScopeObject(); 910 return; 911 } 912 913 if (nsPIDOMWindowInner* w = aOwner->GetAsInnerWindow()) { 914 mOwner = w->AsGlobal(); 915 return; 916 } 917 918 nsCOMPtr<DOMEventTargetHelper> eth = do_QueryInterface(aOwner); 919 if (eth) { 920 mOwner = eth->GetParentObject(); 921 return; 922 } 923 924 #ifdef DEBUG 925 nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(aOwner); 926 MOZ_ASSERT(root, "Unexpected EventTarget!"); 927 #endif 928 } 929 930 void Event::GetWidgetEventType(WidgetEvent* aEvent, nsAString& aType) { 931 if (!aEvent->mSpecifiedEventTypeString.IsEmpty()) { 932 aType = aEvent->mSpecifiedEventTypeString; 933 return; 934 } 935 936 const char16_t* name = GetEventName(aEvent->mMessage); 937 938 if (name) { 939 aType.AssignLiteral(name, nsString::char_traits::length(name)); 940 return; 941 } else if (aEvent->mMessage == eUnidentifiedEvent && 942 aEvent->mSpecifiedEventType) { 943 // Remove "on" 944 aType = Substring(nsDependentAtomString(aEvent->mSpecifiedEventType), 2); 945 aEvent->mSpecifiedEventTypeString = aType; 946 return; 947 } 948 949 aType.Truncate(); 950 } 951 952 bool Event::IsDragExitEnabled(JSContext* aCx, JSObject* aGlobal) { 953 return StaticPrefs::dom_event_dragexit_enabled() || 954 nsContentUtils::IsSystemCaller(aCx); 955 } 956 957 } // namespace mozilla::dom 958 959 using namespace mozilla; 960 using namespace mozilla::dom; 961 962 already_AddRefed<Event> NS_NewDOMEvent(EventTarget* aOwner, 963 nsPresContext* aPresContext, 964 WidgetEvent* aEvent) { 965 RefPtr<Event> it = new Event(aOwner, aPresContext, aEvent); 966 return it.forget(); 967 }