Bug 218415 - Add window.event. r=smaug.
authorHenri Sivonen <hsivonen@hsivonen.fi>
Wed, 04 Apr 2018 15:57:17 +0300
changeset 425162 5e32f9974bd52cc7ebd00c4d4fa256515b5ab951
parent 425161 705c9f7e55d416f436bb39fd3a4792af133ffa59
child 425163 6792d723a2b1d4f2ff2a702f7ae1f4c52fa32346
push id34237
push userapavel@mozilla.com
push dateThu, 05 Jul 2018 16:28:59 +0000
treeherdermozilla-central@ce32b59b5a49 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs218415
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 218415 - Add window.event. r=smaug. window.event is set on the wrong window when the target and the callback are from different realms and the callback is an XPCOM callback. MozReview-Commit-ID: HXeUIicdMuT
dom/base/FragmentOrElement.cpp
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
dom/base/nsIGlobalObject.cpp
dom/base/nsIGlobalObject.h
dom/base/nsPIDOMWindow.h
dom/events/EventDispatcher.cpp
dom/events/EventDispatcher.h
dom/events/EventListenerManager.cpp
dom/events/EventListenerManager.h
dom/webidl/Window.webidl
testing/web-platform/meta/dom/events/event-global-extra.window.js.ini
testing/web-platform/meta/dom/events/event-global.html.ini
testing/web-platform/meta/dom/interfaces.html.ini
testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track-inband.html.ini
testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html.ini
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -873,16 +873,20 @@ FindChromeAccessOnlySubtreeOwner(EventTa
 
 void
 nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
 {
   //FIXME! Document how this event retargeting works, Bug 329124.
   aVisitor.mCanHandle = true;
   aVisitor.mMayHaveListenerManager = HasListenerManager();
 
+  if (IsInShadowTree()) {
+    aVisitor.mItemInShadowTree = true;
+  }
+
   // Don't propagate mouseover and mouseout events when mouse is moving
   // inside chrome access only content.
   bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
   aVisitor.mRootOfClosedTree = isAnonForEvents;
   if ((aVisitor.mEvent->mMessage == eMouseOver ||
        aVisitor.mEvent->mMessage == eMouseOut ||
        aVisitor.mEvent->mMessage == ePointerOver ||
        aVisitor.mEvent->mMessage == ePointerOut) &&
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -913,18 +913,19 @@ nsGlobalWindowInner::nsGlobalWindowInner
     mIdleRequestExecutor(nullptr),
     mDialogAbuseCount(0),
     mAreDialogsEnabled(true),
     mObservingDidRefresh(false),
     mIteratingDocumentFlushedResolvers(false),
     mCanSkipCCGeneration(0),
     mBeforeUnloadListenerCount(0)
 {
+  mIsInnerWindow = true;
+
   AssertIsOnMainThread();
-
   nsLayoutStatics::AddRef();
 
   // Initialize the PRCList (this).
   PR_INIT_CLIST(this);
 
   if (aOuterWindow) {
     // |this| is an inner window, add this inner window to the outer
     // window list of inners.
@@ -3280,16 +3281,26 @@ nsGlobalWindowInner::SetOpener(JSContext
     }
     outer = win->GetOuterWindow();
   }
 
   SetOpenerWindow(outer, false);
 }
 
 void
+nsGlobalWindowInner::GetEvent(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval)
+{
+  if (mEvent) {
+    Unused << nsContentUtils::WrapNative(aCx, mEvent, aRetval);
+  } else {
+    aRetval.setUndefined();
+  }
+}
+
+void
 nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetStatusOuter, (aStatus), aError, );
 }
 
 void
 nsGlobalWindowInner::SetStatus(const nsAString& aStatus, ErrorResult& aError)
 {
@@ -8183,17 +8194,18 @@ nsPIDOMWindowInner::nsPIDOMWindowInner(n
   mMayHavePointerEnterLeaveEventListener(false),
   mAudioCaptured(false),
   mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0),
   mHasTriedToCacheTopInnerWindow(false),
   mNumOfIndexedDBDatabases(0),
-  mNumOfOpenWebSockets(0)
+  mNumOfOpenWebSockets(0),
+  mEvent(nullptr)
 {
   MOZ_ASSERT(aOuterWindow);
 }
 
 nsPIDOMWindowInner::~nsPIDOMWindowInner() {}
 
 #undef FORWARD_TO_OUTER
 #undef FORWARD_TO_OUTER_OR_THROW
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -666,16 +666,17 @@ protected:
   // Initializes the mWasOffline member variable
   void InitWasOffline();
 public:
   nsPIDOMWindowOuter* GetOpenerWindow(mozilla::ErrorResult& aError);
   void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
                  mozilla::ErrorResult& aError);
   void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
                  mozilla::ErrorResult& aError);
+  void GetEvent(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
   already_AddRefed<nsPIDOMWindowOuter> GetParent(mozilla::ErrorResult& aError);
   nsPIDOMWindowOuter* GetScriptableParent() override;
   nsPIDOMWindowOuter* GetScriptableParentOrNull() override;
   mozilla::dom::Element*
   GetFrameElement(nsIPrincipal& aSubjectPrincipal,
                   mozilla::ErrorResult& aError);
   mozilla::dom::Element* GetFrameElement() override;
   already_AddRefed<nsPIDOMWindowOuter>
--- a/dom/base/nsIGlobalObject.cpp
+++ b/dom/base/nsIGlobalObject.cpp
@@ -6,16 +6,17 @@
 
 #include "nsIGlobalObject.h"
 
 #include "mozilla/dom/BlobURLProtocolHandler.h"
 #include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
+#include "nsGlobalWindowInner.h"
 
 using mozilla::MallocSizeOf;
 using mozilla::Maybe;
 using mozilla::DOMEventTargetHelper;
 using mozilla::dom::BlobURLProtocolHandler;
 using mozilla::dom::ClientInfo;
 using mozilla::dom::ServiceWorker;
 using mozilla::dom::ServiceWorkerDescriptor;
@@ -216,14 +217,23 @@ nsIGlobalObject::GetServiceWorkerRegistr
 
 RefPtr<ServiceWorkerRegistration>
 nsIGlobalObject::GetOrCreateServiceWorkerRegistration(const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   MOZ_DIAGNOSTIC_ASSERT(false, "this global should not have any service worker registrations");
   return nullptr;
 }
 
+nsPIDOMWindowInner*
+nsIGlobalObject::AsInnerWindow()
+{
+  if (MOZ_LIKELY(mIsInnerWindow)) {
+    return static_cast<nsPIDOMWindowInner*>(static_cast<nsGlobalWindowInner*>(this)); 
+  }
+  return nullptr;
+}
+
 size_t
 nsIGlobalObject::ShallowSizeOfExcludingThis(MallocSizeOf aSizeOf) const
 {
   size_t rtn = mHostObjectURIs.ShallowSizeOfExcludingThis(aSizeOf);
   return rtn;
 }
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -21,16 +21,17 @@
 
 // Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
 #define NS_IGLOBALOBJECT_IID \
 { 0x11afa8be, 0xd997, 0x4e07, \
 { 0xa6, 0xa3, 0x6f, 0x87, 0x2e, 0xc3, 0xee, 0x7f } }
 
 class nsCycleCollectionTraversalCallback;
 class nsIPrincipal;
+class nsPIDOMWindowInner;
 
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class ServiceWorker;
 class ServiceWorkerRegistration;
 class ServiceWorkerRegistrationDescriptor;
 } // namespace dom
@@ -43,18 +44,22 @@ class nsIGlobalObject : public nsISuppor
 
   // Raw pointers to bound DETH objects.  These are added by
   // AddEventTargetObject().
   mozilla::LinkedList<mozilla::DOMEventTargetHelper> mEventTargetObjects;
 
   bool mIsDying;
 
 protected:
+
+  bool mIsInnerWindow;
+
   nsIGlobalObject()
    : mIsDying(false)
+   , mIsInnerWindow(false)
   {}
 
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IGLOBALOBJECT_IID)
 
   /**
    * This check is added to deal with Promise microtask queues. On the main
    * thread, we do not impose restrictions about when script stops running or
@@ -125,16 +130,18 @@ public:
   virtual RefPtr<mozilla::dom::ServiceWorkerRegistration>
   GetServiceWorkerRegistration(const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor) const;
 
   // Get the DOM object for the given descriptor or attempt to create one.
   // Creation can still fail and return nullptr during shutdown, etc.
   virtual RefPtr<mozilla::dom::ServiceWorkerRegistration>
   GetOrCreateServiceWorkerRegistration(const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor);
 
+  // Returns a pointer to this object as an inner window if this is one or nullptr otherwise.
+  nsPIDOMWindowInner* AsInnerWindow();
 protected:
   virtual ~nsIGlobalObject();
 
   void
   StartDying()
   {
     mIsDying = true;
   }
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -246,16 +246,27 @@ public:
    * Call this to indicate that some node (this window, its document,
    * or content in that document) has a Pointerenter/leave event listener.
    */
   void SetHasPointerEnterLeaveEventListeners()
   {
     mMayHavePointerEnterLeaveEventListener = true;
   }
 
+  // Sets the event for window.event. Does NOT take ownership, so
+  // the caller is responsible for clearing the event before the
+  // event gets deallocated. Pass nullptr to set window.event to
+  // undefined. Returns the previous value.
+  mozilla::dom::Event* SetEvent(mozilla::dom::Event* aEvent)
+  {
+    mozilla::dom::Event* old = mEvent;
+    mEvent = aEvent;
+    return old;
+  }
+
   /**
    * Check whether this window is a secure context.
    */
   bool IsSecureContext() const;
   bool IsSecureContextIfOpenerIgnored() const;
 
   // Calling suspend should prevent any asynchronous tasks from
   // executing javascript for this window.  This means setTimeout,
@@ -700,16 +711,20 @@ protected:
 
   // The number of open WebSockets.
   uint32_t mNumOfOpenWebSockets;
 
   // If we're in the process of requesting permission for this window to
   // play audible media, or we've already been granted permission by the
   // user, this is non-null, and encapsulates the request.
   RefPtr<mozilla::AutoplayRequest> mAutoplayRequest;
+
+  // The event dispatch code sets and unsets this while keeping
+  // the event object alive.
+  mozilla::dom::Event* mEvent;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
 
 class nsPIDOMWindowOuter : public mozIDOMWindowProxy
 {
 protected:
   explicit nsPIDOMWindowOuter();
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -316,16 +316,26 @@ public:
     mFlags.mRootOfClosedTree = aSet;
   }
 
   bool IsRootOfClosedTree()
   {
     return mFlags.mRootOfClosedTree;
   }
 
+  void SetItemInShadowTree(bool aSet)
+  {
+    mFlags.mItemInShadowTree = aSet;
+  }
+
+  bool IsItemInShadowTree()
+  {
+    return mFlags.mItemInShadowTree;
+  }
+
   void SetIsSlotInClosedTree(bool aSet)
   {
     mFlags.mIsSlotInClosedTree = aSet;
   }
 
   bool IsSlotInClosedTree()
   {
     return mFlags.mIsSlotInClosedTree;
@@ -403,17 +413,18 @@ public:
       mManager = mTarget->GetExistingListenerManager();
     }
     if (mManager) {
       NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr,
                    "CurrentTarget should be null!");
       mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent,
                             &aVisitor.mDOMEvent,
                             CurrentTarget(),
-                            &aVisitor.mEventStatus);
+                            &aVisitor.mEventStatus,
+                            IsItemInShadowTree());
       NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr,
                    "CurrentTarget should be null!");
     }
   }
 
   /**
    * Copies mItemFlags and mItemData to aVisitor and calls PostHandleEvent.
    */
@@ -438,16 +449,17 @@ private:
     bool mForceContentDispatch : 1;
     bool mWantsWillHandleEvent : 1;
     bool mMayHaveManager : 1;
     bool mChechedIfChrome : 1;
     bool mIsChromeContent : 1;
     bool mWantsPreHandleEvent : 1;
     bool mPreHandleEventOnly : 1;
     bool mRootOfClosedTree : 1;
+    bool mItemInShadowTree : 1;
     bool mIsSlotInClosedTree : 1;
     bool mIsChromeHandler : 1;
   private:
     typedef uint32_t RawFlags;
     void SetRawFlags(RawFlags aRawFlags)
     {
       static_assert(sizeof(EventTargetChainFlags) <= sizeof(RawFlags),
         "EventTargetChainFlags must not be bigger than the RawFlags");
@@ -480,16 +492,17 @@ EventTargetChainItem::GetEventTargetPare
   aVisitor.Reset();
   mTarget->GetEventTargetParent(aVisitor);
   SetForceContentDispatch(aVisitor.mForceContentDispatch);
   SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent);
   SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager);
   SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
   SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
   SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
+  SetItemInShadowTree(aVisitor.mItemInShadowTree);
   SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
   SetRetargetedTouchTarget(std::move(aVisitor.mRetargetedTouchTargets));
   mItemFlags = aVisitor.mItemFlags;
   mItemData = aVisitor.mItemData;
 }
 
 void
 EventTargetChainItem::PreHandleEvent(EventChainVisitor& aVisitor)
--- a/dom/events/EventDispatcher.h
+++ b/dom/events/EventDispatcher.h
@@ -123,16 +123,17 @@ public:
     , mAutomaticChromeDispatch(true)
     , mForceContentDispatch(false)
     , mRelatedTargetIsInAnon(false)
     , mOriginalTargetIsInAnon(aIsInAnon)
     , mWantsWillHandleEvent(false)
     , mMayHaveListenerManager(true)
     , mWantsPreHandleEvent(false)
     , mRootOfClosedTree(false)
+    , mItemInShadowTree(false)
     , mParentIsSlotInClosedTree(false)
     , mParentIsChromeHandler(false)
     , mRelatedTargetRetargetedInCurrentScope(false)
     , mParentTarget(nullptr)
     , mEventTargetAtParent(nullptr)
     , mRetargetedRelatedTarget(nullptr)
     , mTargetInKnownToBeHandledScope(aTargetInKnownToBeHandledScope)
   {
@@ -144,16 +145,17 @@ public:
     mItemData = nullptr;
     mCanHandle = true;
     mAutomaticChromeDispatch = true;
     mForceContentDispatch = false;
     mWantsWillHandleEvent = false;
     mMayHaveListenerManager = true;
     mWantsPreHandleEvent = false;
     mRootOfClosedTree = false;
+    mItemInShadowTree = false;
     mParentIsSlotInClosedTree = false;
     mParentIsChromeHandler = false;
     // Note, we don't clear mRelatedTargetRetargetedInCurrentScope explicitly,
     // since it is used during event path creation to indicate whether
     // relatedTarget may need to be retargeted.
     mParentTarget = nullptr;
     mEventTargetAtParent = nullptr;
     mRetargetedRelatedTarget = nullptr;
@@ -233,16 +235,22 @@ public:
 
   /**
    * True if the current target is either closed ShadowRoot or root of
    * chrome only access tree (for example native anonymous content).
    */
   bool mRootOfClosedTree;
 
   /**
+   * If target is node and its root is a shadow root.
+   * https://dom.spec.whatwg.org/#event-path-item-in-shadow-tree
+   */
+  bool mItemInShadowTree;
+
+  /**
    * True if mParentTarget is HTMLSlotElement in a closed shadow tree and the
    * current target is assigned to that slot.
    */
   bool mParentIsSlotInClosedTree;
 
   /**
    * True if mParentTarget is a chrome handler in the event path.
    */
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -1157,27 +1157,54 @@ EventListenerManager::GetLegacyEventMess
       return eMozFullscreenChange;
     case eFullscreenError:
       return eMozFullscreenError;
     default:
       return aEventMessage;
   }
 }
 
+already_AddRefed<nsPIDOMWindowInner>
+EventListenerManager::WindowFromListener(Listener* aListener,
+                                         bool aItemInShadowTree)
+{
+  nsCOMPtr<nsPIDOMWindowInner> innerWindow;
+  if (!aItemInShadowTree) {
+    if (aListener->mListener.HasWebIDLCallback()) {
+      CallbackObject* callback = aListener->mListener.GetWebIDLCallback();
+      nsIGlobalObject* global = nullptr;
+      if (callback) {
+        global = callback->IncumbentGlobalOrNull();
+      }
+      if (global) {
+        innerWindow = global->AsInnerWindow(); // Can be nullptr
+      }
+    } else {
+      // Can't get the global from
+      // listener->mListener.GetXPCOMCallback().
+      // In most cases, it would be the same as for
+      // the target, so let's do that.
+      innerWindow = GetInnerWindowForTarget(); // Can be nullptr
+    }
+  }
+  return innerWindow.forget();
+}
+
 /**
 * Causes a check for event listeners and processing by them if they exist.
 * @param an event listener
 */
 
 void
 EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
                                           WidgetEvent* aEvent,
                                           Event** aDOMEvent,
                                           EventTarget* aCurrentTarget,
-                                          nsEventStatus* aEventStatus)
+                                          nsEventStatus* aEventStatus,
+                                          bool aItemInShadowTree)
 {
   //Set the value of the internal PreventDefault flag properly based on aEventStatus
   if (!aEvent->DefaultPrevented() &&
       *aEventStatus == nsEventStatus_eConsumeNoDefault) {
     // Assume that if only aEventStatus claims that the event has already been
     // consumed, the consumer is default event handler.
     aEvent->PreventDefault();
   }
@@ -1260,16 +1287,22 @@ EventListenerManager::HandleEventInterna
               // The order is important, otherwise the listener could be
               // called again inside the listener.
               listenerHolder.emplace(std::move(*listener));
               listener = listenerHolder.ptr();
               hasRemovedListener = true;
             }
 
             nsresult rv = NS_OK;
+            nsCOMPtr<nsPIDOMWindowInner> innerWindow =
+              WindowFromListener(listener, aItemInShadowTree);
+            mozilla::dom::Event* oldWindowEvent = nullptr;
+            if (innerWindow) {
+              oldWindowEvent = innerWindow->SetEvent(*aDOMEvent);
+            }
 #ifdef MOZ_GECKO_PROFILER
             if (profiler_is_active()) {
               // Add a profiler label and a profiler marker for the actual
               // dispatch of the event.
               // This is a very hot code path, so we need to make sure not to
               // do this extra work when we're not profiling.
               nsAutoString typeStr;
               (*aDOMEvent)->GetType(typeStr);
@@ -1293,16 +1326,19 @@ EventListenerManager::HandleEventInterna
                                                   aEvent->mTimeStamp,
                                                   "DOMEvent",
                                                   TRACING_INTERVAL_END));
             } else
 #endif
             {
               rv = HandleEventSubType(listener, *aDOMEvent, aCurrentTarget);
             }
+            if (innerWindow) {
+              Unused << innerWindow->SetEvent(oldWindowEvent);
+            }
 
             if (NS_FAILED(rv)) {
               aEvent->mFlags.mExceptionWasRaised = true;
             }
             aEvent->mFlags.mInPassiveListener = false;
 
             if (needsEndEventMarker) {
               timelines->AddMarkerForDocShell(
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -364,17 +364,18 @@ public:
    * Remove the current "inline" event listener for aName.
    */
   void RemoveEventHandler(nsAtom *aName, const nsAString& aTypeString);
 
   void HandleEvent(nsPresContext* aPresContext,
                    WidgetEvent* aEvent,
                    dom::Event** aDOMEvent,
                    dom::EventTarget* aCurrentTarget,
-                   nsEventStatus* aEventStatus)
+                   nsEventStatus* aEventStatus,
+                   bool aItemInShadowTree)
   {
     if (mListeners.IsEmpty() || aEvent->PropagationStopped()) {
       return;
     }
 
     if (!mMayHaveCapturingListeners && !aEvent->mFlags.mInBubblingPhase) {
       return;
     }
@@ -385,17 +386,17 @@ public:
 
     // Check if we already know that there is no event listener for the event.
     if (mNoListenerForEvent == aEvent->mMessage &&
         (mNoListenerForEvent != eUnidentifiedEvent ||
          mNoListenerForEventAtom == aEvent->mSpecifiedEventType)) {
       return;
     }
     HandleEventInternal(aPresContext, aEvent, aDOMEvent, aCurrentTarget,
-                        aEventStatus);
+                        aEventStatus, aItemInShadowTree);
   }
 
   /**
    * Tells the event listener manager that its target (which owns it) is
    * no longer using it (and could go away).
    */
   void Disconnect();
 
@@ -494,17 +495,18 @@ public:
   bool IsApzAwareListener(Listener* aListener);
   bool IsApzAwareEvent(nsAtom* aEvent);
 
 protected:
   void HandleEventInternal(nsPresContext* aPresContext,
                            WidgetEvent* aEvent,
                            dom::Event** aDOMEvent,
                            dom::EventTarget* aCurrentTarget,
-                           nsEventStatus* aEventStatus);
+                           nsEventStatus* aEventStatus,
+                           bool aItemInShadowTree);
 
   nsresult HandleEventSubType(Listener* aListener,
                               dom::Event* aDOMEvent,
                               dom::EventTarget* aCurrentTarget);
 
   /**
    * If the given EventMessage has a legacy version that we support, then this
    * function returns that legacy version. Otherwise, this function simply
@@ -585,16 +587,20 @@ public:
 
   dom::OnBeforeUnloadEventHandlerNonNull* GetOnBeforeUnloadEventHandler()
   {
     const TypedEventHandler* typedHandler =
       GetTypedEventHandler(nsGkAtoms::onbeforeunload, EmptyString());
     return typedHandler ? typedHandler->OnBeforeUnloadEventHandler() : nullptr;
   }
 
+private:
+  already_AddRefed<nsPIDOMWindowInner> WindowFromListener(Listener* aListener,
+                                                          bool aItemInShadowTree);
+
 protected:
   /**
    * Helper method for implementing the various Get*EventHandler above.  Will
    * return null if we don't have an event handler for this event name.
    */
   const TypedEventHandler* GetTypedEventHandler(nsAtom* aEventName,
                                                 const nsAString& aTypeString);
 
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -45,16 +45,17 @@ interface XULControllers;
   [Replaceable, Throws] readonly attribute BarProp statusbar;
   [Replaceable, Throws] readonly attribute BarProp toolbar;
   [Throws] attribute DOMString status;
   [Throws, CrossOriginCallable] void close();
   [Throws, CrossOriginReadable] readonly attribute boolean closed;
   [Throws] void stop();
   [Throws, CrossOriginCallable] void focus();
   [Throws, CrossOriginCallable] void blur();
+  [Replaceable] readonly attribute any event;
 
   // other browsing contexts
   [Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy frames;
   [Replaceable, CrossOriginReadable] readonly attribute unsigned long length;
   //[Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy top;
   [Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy? top;
   [Throws, CrossOriginReadable] attribute any opener;
   //[Throws] readonly attribute WindowProxy parent;
--- a/testing/web-platform/meta/dom/events/event-global-extra.window.js.ini
+++ b/testing/web-platform/meta/dom/events/event-global-extra.window.js.ini
@@ -1,25 +1,8 @@
 [event-global-extra.window.html]
-  [window.event for constructors from another global: function EventTarget() {\n    [native code\]\n}]
-    expected: FAIL
-
-  [window.event for constructors from another global: function XMLHttpRequest() {\n    [native code\]\n}]
-    expected: FAIL
-
-  [window.event and element from another document]
-    expected: FAIL
-
-  [window.event and moving an element post-dispatch]
-    expected: FAIL
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
 
   [window.event should not be affected by nodes moving post-dispatch]
     expected: FAIL
 
   [Listener from a different global]
     expected: FAIL
-
-  [window.event for constructors from another global: EventTarget]
-    expected: FAIL
-
-  [window.event for constructors from another global: XMLHttpRequest]
-    expected: FAIL
-
--- a/testing/web-platform/meta/dom/events/event-global.html.ini
+++ b/testing/web-platform/meta/dom/events/event-global.html.ini
@@ -1,19 +1,2 @@
 [event-global.html]
-  [event exists on window, which is initially set to undefined]
-    expected: FAIL
-
-  [window.event is only defined during dispatch]
-    expected: FAIL
-
-  [window.event is undefined if the target is in a shadow tree (event dispatched outside shadow tree)]
-    expected: FAIL
-
-  [window.event is undefined if the target is in a shadow tree (event dispatched inside shadow tree)]
-    expected: FAIL
-
-  [window.event is set to the current event during dispatch]
-    expected: FAIL
-
-  [window.event is set to the current event, which is the event passed to dispatch]
-    expected: FAIL
-
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
--- a/testing/web-platform/meta/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/dom/interfaces.html.ini
@@ -94,11 +94,8 @@
     expected: FAIL
 
   [Event interface: new Event("foo") must inherit property "returnValue" with the proper type]
     expected: FAIL
 
   [Event interface: new CustomEvent("foo") must inherit property "returnValue" with the proper type]
     expected: FAIL
 
-  [Window interface: attribute event]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track-inband.html.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track-inband.html.ini
@@ -1,4 +1,3 @@
 [track-remove-track-inband.html]
-  [Tests that the 'removetrack' event is NOT fired for inband TextTrack on a failed load.]
-    expected: FAIL
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1473478
 
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html.ini
@@ -1,7 +1,3 @@
 [track-remove-track.html]
-  [Tests that the 'removetrack' event is fired when an out-of-band TextTrack is removed.]
-    expected: FAIL
-
   [Tests that the 'removetrack' event is NOT fired for inband TextTrack on a failed load.]
     expected: FAIL
-