Bug 218415 - Add window.event. r?smaug. draft
authorHenri Sivonen <hsivonen@hsivonen.fi>
Wed, 04 Apr 2018 15:57:17 +0300
changeset 814410 7e9940964f40097c7e8bcd984dc0a69fa80eb76e
parent 814087 b1fcf4dda09949b96a82474f0606fac85ab45974
push id115189
push userbmo:hsivonen@hsivonen.fi
push dateThu, 05 Jul 2018 08:08:15 +0000
reviewerssmaug
bugs218415
milestone63.0a1
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.
@@ -3283,16 +3284,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)
 {
@@ -8202,17 +8213,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
@@ -1156,27 +1156,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();
   }
@@ -1259,16 +1286,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);
@@ -1292,16 +1325,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
@@ -46,16 +46,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
-