Bug 1679204 - Consider to add signal to addEventListener, r=edgar
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Mon, 14 Dec 2020 15:45:15 +0000
changeset 560621 b3d0fba5fd8d624b5abf53df196aedae5e83a269
parent 560620 dff02098b9a627cce58cbfd8553597833c807456
child 560622 e45001cddaf486353a3ec9878be55f214ac9717d
push id38032
push usercsabou@mozilla.com
push dateTue, 15 Dec 2020 09:29:54 +0000
treeherdermozilla-central@f805f27183c3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedgar
bugs1679204, 26472
milestone85.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 1679204 - Consider to add signal to addEventListener, r=edgar This passes the tests which are in https://github.com/web-platform-tests/wpt/pull/26472 Because of complications in #include handling, AbortFollower needs to be in a different header file than AbortSignal, yet AbortSignalImpl needs to be available when AbortFollower is used. Another option would have been to make DOMEventTargetHelper.h a bit different and uninline some hot methods there or move them to another file, but that would have been equally bad and Abort* is used way less often. AbortFollower and AbortSignalImpl are thus just moved to a new header. Memory management is such that Listener in EventListenerManager owns the possible ListenerSignalFollower instance which follows the relevant signal. In order to be able remove event listener, ListenerSignalFollower has many similar fields as Listener. ListenerSignalFollower can't easily have just a pointer to Listener* since Listener isn't stored as a pointer in EventListenerManager. ListenerSignalFollower is cycle collectable so that Listener->ListenerSignalFollower can be traversed/unlinked and also strong pointers in ListenerSignalFollower itself can be traversed/unlinked. There is an XXX in the .webidl, since nullability of signal is unclear in the spec pr. Whether or not it ends up being nullable shouldn't change the actual C++ implementation. Differential Revision: https://phabricator.services.mozilla.com/D97938
dom/abort/AbortFollower.h
dom/abort/AbortSignal.h
dom/abort/moz.build
dom/events/DOMEventTargetHelper.cpp
dom/events/EventListenerManager.cpp
dom/events/EventListenerManager.h
dom/webidl/EventTarget.webidl
testing/web-platform/meta/dom/events/AddEventListenerOptions-signal.any.js.ini
new file mode 100644
--- /dev/null
+++ b/dom/abort/AbortFollower.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_AbortFollower_h
+#define mozilla_dom_AbortFollower_h
+
+#include "nsISupportsImpl.h"
+#include "nsTObserverArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class AbortSignal;
+class AbortSignalImpl;
+
+// This class must be implemented by objects who want to follow an
+// AbortSignalImpl.
+class AbortFollower : public nsISupports {
+ public:
+  virtual void RunAbortAlgorithm() = 0;
+
+  void Follow(AbortSignalImpl* aSignal);
+
+  void Unfollow();
+
+  bool IsFollowing() const;
+
+  AbortSignalImpl* Signal() const { return mFollowingSignal; }
+
+ protected:
+  // Subclasses of this class must call these Traverse and Unlink functions
+  // during corresponding cycle collection operations.
+  static void Traverse(AbortFollower* aFollower,
+                       nsCycleCollectionTraversalCallback& cb);
+
+  static void Unlink(AbortFollower* aFollower) { aFollower->Unfollow(); }
+
+  virtual ~AbortFollower();
+
+  friend class AbortSignalImpl;
+
+  RefPtr<AbortSignalImpl> mFollowingSignal;
+};
+
+class AbortSignalImpl : public nsISupports {
+ public:
+  explicit AbortSignalImpl(bool aAborted);
+
+  bool Aborted() const;
+
+  virtual void SignalAbort();
+
+ protected:
+  // Subclasses of this class must call these Traverse and Unlink functions
+  // during corresponding cycle collection operations.
+  static void Traverse(AbortSignalImpl* aSignal,
+                       nsCycleCollectionTraversalCallback& cb);
+
+  static void Unlink(AbortSignalImpl* aSignal) {
+    // To be filled in shortly.
+  }
+
+  virtual ~AbortSignalImpl() = default;
+
+ private:
+  friend class AbortFollower;
+
+  // Raw pointers.  |AbortFollower::Follow| adds to this array, and
+  // |AbortFollower::Unfollow| (also callbed by the destructor) will remove
+  // from this array.  Finally, calling |SignalAbort()| will (after running all
+  // abort algorithms) empty this and make all contained followers |Unfollow()|.
+  nsTObserverArray<AbortFollower*> mFollowers;
+
+  bool mAborted;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif  // mozilla_dom_AbortFollower_h
--- a/dom/abort/AbortSignal.h
+++ b/dom/abort/AbortSignal.h
@@ -2,87 +2,22 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_AbortSignal_h
 #define mozilla_dom_AbortSignal_h
 
+#include "mozilla/dom/AbortFollower.h"
 #include "mozilla/DOMEventTargetHelper.h"
-#include "nsISupportsImpl.h"
-#include "nsTObserverArray.h"
 
 namespace mozilla {
 namespace dom {
 
-class AbortSignal;
-class AbortSignalImpl;
-
-// This class must be implemented by objects who want to follow an
-// AbortSignalImpl.
-class AbortFollower : public nsISupports {
- public:
-  virtual void RunAbortAlgorithm() = 0;
-
-  void Follow(AbortSignalImpl* aSignal);
-
-  void Unfollow();
-
-  bool IsFollowing() const;
-
-  AbortSignalImpl* Signal() const { return mFollowingSignal; }
-
- protected:
-  // Subclasses of this class must call these Traverse and Unlink functions
-  // during corresponding cycle collection operations.
-  static void Traverse(AbortFollower* aFollower,
-                       nsCycleCollectionTraversalCallback& cb);
-
-  static void Unlink(AbortFollower* aFollower) { aFollower->Unfollow(); }
-
-  virtual ~AbortFollower();
-
-  friend class AbortSignalImpl;
-
-  RefPtr<AbortSignalImpl> mFollowingSignal;
-};
-
-class AbortSignalImpl : public nsISupports {
- public:
-  explicit AbortSignalImpl(bool aAborted);
-
-  bool Aborted() const;
-
-  virtual void SignalAbort();
-
- protected:
-  // Subclasses of this class must call these Traverse and Unlink functions
-  // during corresponding cycle collection operations.
-  static void Traverse(AbortSignalImpl* aSignal,
-                       nsCycleCollectionTraversalCallback& cb);
-
-  static void Unlink(AbortSignalImpl* aSignal) {
-    // To be filled in shortly.
-  }
-
-  virtual ~AbortSignalImpl() = default;
-
- private:
-  friend class AbortFollower;
-
-  // Raw pointers.  |AbortFollower::Follow| adds to this array, and
-  // |AbortFollower::Unfollow| (also callbed by the destructor) will remove
-  // from this array.  Finally, calling |SignalAbort()| will (after running all
-  // abort algorithms) empty this and make all contained followers |Unfollow()|.
-  nsTObserverArray<AbortFollower*> mFollowers;
-
-  bool mAborted;
-};
-
 // AbortSignal the spec concept includes the concept of a child signal
 // "following" a parent signal -- internally, adding abort steps to the parent
 // signal that will then signal abort on the child signal -- to propagate
 // signaling abort from one signal to another.  See
 // <https://dom.spec.whatwg.org/#abortsignal-follow>.
 //
 // This requires that AbortSignal also inherit from AbortFollower.
 //
--- a/dom/abort/moz.build
+++ b/dom/abort/moz.build
@@ -6,16 +6,17 @@
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM: Core & HTML")
 
 TEST_DIRS += ["tests"]
 
 EXPORTS.mozilla.dom += [
     "AbortController.h",
+    "AbortFollower.h",
     "AbortSignal.h",
 ]
 
 UNIFIED_SOURCES += [
     "AbortController.cpp",
     "AbortSignal.cpp",
 ]
 
--- a/dom/events/DOMEventTargetHelper.cpp
+++ b/dom/events/DOMEventTargetHelper.cpp
@@ -6,16 +6,17 @@
 
 #include "nsContentUtils.h"
 #include "mozilla/dom/Document.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
+#include "mozilla/EventListenerManager.h"
 #include "mozilla/Likely.h"
 #include "MainThreadUtils.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMEventTargetHelper)
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/HalSensor.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/JSEventHandler.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/PresShell.h"
+#include "mozilla/dom/AbortSignal.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/EventCallbackDebuggerNotification.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/LoadedScript.h"
 #include "mozilla/dom/PopupBlocker.h"
 #include "mozilla/dom/ScriptLoader.h"
@@ -167,16 +168,19 @@ inline void ImplCycleCollectionTraverse(
       name.AppendLiteral(" ");
     }
     CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(),
                              name.get(), aFlags);
   } else {
     CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
                              aFlags);
   }
+
+  CycleCollectionNoteChild(aCallback, aField.mSignalFollower.get(),
+                           "mSignalFollower", aFlags);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
@@ -200,24 +204,28 @@ already_AddRefed<nsPIDOMWindowInner>
 EventListenerManager::GetTargetAsInnerWindow() const {
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mTarget);
   return window.forget();
 }
 
 void EventListenerManager::AddEventListenerInternal(
     EventListenerHolder aListenerHolder, EventMessage aEventMessage,
     nsAtom* aTypeAtom, const EventListenerFlags& aFlags, bool aHandler,
-    bool aAllEvents) {
+    bool aAllEvents, AbortSignal* aSignal) {
   MOZ_ASSERT((aEventMessage && aTypeAtom) || aAllEvents,  // all-events listener
              "Missing type");
 
   if (!aListenerHolder || mClearingListeners) {
     return;
   }
 
+  if (aSignal && aSignal->Aborted()) {
+    return;
+  }
+
   // Since there is no public API to call us with an EventListenerHolder, we
   // know that there's an EventListenerHolder on the stack holding a strong ref
   // to the listener.
 
   Listener* listener;
   uint32_t count = mListeners.Length();
   for (uint32_t i = 0; i < count; i++) {
     listener = &mListeners.ElementAt(i);
@@ -250,16 +258,21 @@ void EventListenerManager::AddEventListe
     listener->mListenerType = Listener::eJSEventListener;
   } else if (aListenerHolder.HasWebIDLCallback()) {
     listener->mListenerType = Listener::eWebIDLListener;
   } else {
     listener->mListenerType = Listener::eNativeListener;
   }
   listener->mListener = std::move(aListenerHolder);
 
+  if (aSignal) {
+    listener->mSignalFollower = new ListenerSignalFollower(this, listener);
+    listener->mSignalFollower->Follow(aSignal);
+  }
+
   if (aFlags.mInSystemGroup) {
     mMayHaveSystemGroupListeners = true;
   }
   if (aFlags.mCapture) {
     mMayHaveCapturingListeners = true;
   }
 
   if (aEventMessage == eAfterPaint) {
@@ -682,29 +695,31 @@ void EventListenerManager::MaybeMarkPass
   if (!IsRootEventTaget(mTarget)) {
     return;
   }
   aFlags.mPassive = true;
 }
 
 void EventListenerManager::AddEventListenerByType(
     EventListenerHolder aListenerHolder, const nsAString& aType,
-    const EventListenerFlags& aFlags, const Optional<bool>& aPassive) {
+    const EventListenerFlags& aFlags, const Optional<bool>& aPassive,
+    AbortSignal* aSignal) {
   RefPtr<nsAtom> atom;
   EventMessage message =
       GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
 
   EventListenerFlags flags = aFlags;
   if (aPassive.WasPassed()) {
     flags.mPassive = aPassive.Value();
   } else {
     MaybeMarkPassive(message, flags);
   }
 
-  AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags);
+  AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags,
+                           false, false, aSignal);
 }
 
 void EventListenerManager::RemoveEventListenerByType(
     EventListenerHolder aListenerHolder, const nsAString& aType,
     const EventListenerFlags& aFlags) {
   RefPtr<nsAtom> atom;
   EventMessage message =
       GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
@@ -1365,30 +1380,36 @@ void EventListenerManager::AddEventListe
 }
 
 void EventListenerManager::AddEventListener(
     const nsAString& aType, EventListenerHolder aListenerHolder,
     const dom::AddEventListenerOptionsOrBoolean& aOptions,
     bool aWantsUntrusted) {
   EventListenerFlags flags;
   Optional<bool> passive;
+  AbortSignal* signal = nullptr;
   if (aOptions.IsBoolean()) {
     flags.mCapture = aOptions.GetAsBoolean();
   } else {
     const auto& options = aOptions.GetAsAddEventListenerOptions();
     flags.mCapture = options.mCapture;
     flags.mInSystemGroup = options.mMozSystemGroup;
     flags.mOnce = options.mOnce;
     if (options.mPassive.WasPassed()) {
       passive.Construct(options.mPassive.Value());
     }
+
+    if (options.mSignal.WasPassed()) {
+      signal = options.mSignal.Value();
+    }
   }
+
   flags.mAllowUntrustedEvents = aWantsUntrusted;
   return AddEventListenerByType(std::move(aListenerHolder), aType, flags,
-                                passive);
+                                passive, signal);
 }
 
 void EventListenerManager::RemoveEventListener(
     const nsAString& aType, EventListenerHolder aListenerHolder,
     bool aUseCapture) {
   EventListenerFlags flags;
   flags.mCapture = aUseCapture;
   RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
@@ -1784,9 +1805,52 @@ EventListenerManager::GetScriptGlobalAnd
     return nullptr;
   }
 
   doc.forget(aDoc);
   nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(win);
   return global.forget();
 }
 
+EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
+    EventListenerManager* aListenerManager,
+    EventListenerManager::Listener* aListener)
+    : dom::AbortFollower(),
+      mListenerManager(aListenerManager),
+      mListener(aListener->mListener.Clone()),
+      mTypeAtom(aListener->mTypeAtom),
+      mEventMessage(aListener->mEventMessage),
+      mAllEvents(aListener->mAllEvents),
+      mFlags(aListener->mFlags){};
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
+    EventListenerManager::ListenerSignalFollower)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
+  AbortFollower::Traverse(static_cast<AbortFollower*>(tmp), cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
+    EventListenerManager::ListenerSignalFollower)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
+  AbortFollower::Unlink(static_cast<AbortFollower*>(tmp));
+  tmp->mListenerManager = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
+    EventListenerManager::ListenerSignalFollower)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
+  if (mListenerManager) {
+    RefPtr<EventListenerManager> elm = mListenerManager;
+    mListenerManager = nullptr;
+    elm->RemoveEventListenerInternal(std::move(mListener), mEventMessage,
+                                     mTypeAtom, mFlags, mAllEvents);
+  }
+}
+
 }  // namespace mozilla
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_EventListenerManager_h_
 #define mozilla_EventListenerManager_h_
 
 #include "mozilla/BasicEvents.h"
+#include "mozilla/dom/AbortFollower.h"
 #include "mozilla/dom/EventListenerBinding.h"
 #include "mozilla/JSEventHandler.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMEventListener.h"
 #include "nsTObserverArray.h"
@@ -23,16 +24,17 @@ class nsPIDOMWindowInner;
 class JSTracer;
 
 struct EventTypeData;
 
 namespace mozilla {
 
 class ELMCreationDetector;
 class EventListenerManager;
+class ListenerSignalFollower;
 
 namespace dom {
 class Event;
 class EventTarget;
 class Element;
 }  // namespace dom
 
 typedef dom::CallbackObjectHolder<dom::EventListener, nsIDOMEventListener>
@@ -164,17 +166,46 @@ class EventListenerManagerBase {
 /*
  * Event listener manager
  */
 
 class EventListenerManager final : public EventListenerManagerBase {
   ~EventListenerManager();
 
  public:
+  struct Listener;
+  class ListenerSignalFollower : public dom::AbortFollower {
+   public:
+    explicit ListenerSignalFollower(EventListenerManager* aListenerManager,
+                                    Listener* aListener);
+
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_CLASS(ListenerSignalFollower)
+
+    void RunAbortAlgorithm() override;
+
+    void Disconnect() {
+      mListenerManager = nullptr;
+      mListener.Reset();
+      Unfollow();
+    }
+
+   protected:
+    ~ListenerSignalFollower() = default;
+
+    EventListenerManager* mListenerManager;
+    EventListenerHolder mListener;
+    RefPtr<nsAtom> mTypeAtom;
+    EventMessage mEventMessage;
+    bool mAllEvents;
+    EventListenerFlags mFlags;
+  };
+
   struct Listener {
+    RefPtr<ListenerSignalFollower> mSignalFollower;
     EventListenerHolder mListener;
     RefPtr<nsAtom> mTypeAtom;
     EventMessage mEventMessage;
 
     enum ListenerType : uint8_t {
       // No listener.
       eNoListener,
       // A generic C++ implementation of nsIDOMEventListener.
@@ -203,17 +234,18 @@ class EventListenerManager final : publi
         : mEventMessage(eVoidEvent),
           mListenerType(eNoListener),
           mListenerIsHandler(false),
           mHandlerIsString(false),
           mAllEvents(false),
           mIsChrome(false) {}
 
     Listener(Listener&& aOther)
-        : mListener(std::move(aOther.mListener)),
+        : mSignalFollower(std::move(aOther.mSignalFollower)),
+          mListener(std::move(aOther.mListener)),
           mTypeAtom(std::move(aOther.mTypeAtom)),
           mEventMessage(aOther.mEventMessage),
           mListenerType(aOther.mListenerType),
           mListenerIsHandler(aOther.mListenerIsHandler),
           mHandlerIsString(aOther.mHandlerIsString),
           mAllEvents(aOther.mAllEvents),
           mIsChrome(aOther.mIsChrome) {
       aOther.mEventMessage = eVoidEvent;
@@ -224,16 +256,19 @@ class EventListenerManager final : publi
       aOther.mIsChrome = false;
     }
 
     ~Listener() {
       if ((mListenerType == eJSEventListener) && mListener) {
         static_cast<JSEventHandler*>(mListener.GetXPCOMCallback())
             ->Disconnect();
       }
+      if (mSignalFollower) {
+        mSignalFollower->Disconnect();
+      }
     }
 
     MOZ_ALWAYS_INLINE bool IsListening(const WidgetEvent* aEvent) const {
       if (mFlags.mInSystemGroup != aEvent->mFlags.mInSystemGroup) {
         return false;
       }
       // FIXME Should check !mFlags.mCapture when the event is in target
       //       phase because capture phase event listeners should not be fired.
@@ -287,17 +322,18 @@ class EventListenerManager final : publi
   void AddEventListenerByType(dom::EventListener* aListener,
                               const nsAString& type,
                               const EventListenerFlags& aFlags) {
     AddEventListenerByType(EventListenerHolder(aListener), type, aFlags);
   }
   void AddEventListenerByType(
       EventListenerHolder aListener, const nsAString& type,
       const EventListenerFlags& aFlags,
-      const dom::Optional<bool>& aPassive = dom::Optional<bool>());
+      const dom::Optional<bool>& aPassive = dom::Optional<bool>(),
+      dom::AbortSignal* aSignal = nullptr);
   void RemoveEventListenerByType(nsIDOMEventListener* aListener,
                                  const nsAString& type,
                                  const EventListenerFlags& aFlags) {
     RemoveEventListenerByType(EventListenerHolder(aListener), type, aFlags);
   }
   void RemoveEventListenerByType(dom::EventListener* aListener,
                                  const nsAString& type,
                                  const EventListenerFlags& aFlags) {
@@ -593,17 +629,18 @@ class EventListenerManager final : publi
                            EventListenerHolder aListener,
                            const dom::EventListenerOptionsOrBoolean& aOptions);
   void RemoveEventListener(const nsAString& aType,
                            EventListenerHolder aListener, bool aUseCapture);
 
   void AddEventListenerInternal(EventListenerHolder aListener,
                                 EventMessage aEventMessage, nsAtom* aTypeAtom,
                                 const EventListenerFlags& aFlags,
-                                bool aHandler = false, bool aAllEvents = false);
+                                bool aHandler = false, bool aAllEvents = false,
+                                dom::AbortSignal* aSignal = nullptr);
   void RemoveEventListenerInternal(EventListenerHolder aListener,
                                    EventMessage aEventMessage,
                                    nsAtom* aUserType,
                                    const EventListenerFlags& aFlags,
                                    bool aAllEvents = false);
   void RemoveAllListenersSilently();
   void NotifyEventListenerRemoved(nsAtom* aUserType);
   const EventTypeData* GetTypeDataForIID(const nsIID& aIID);
--- a/dom/webidl/EventTarget.webidl
+++ b/dom/webidl/EventTarget.webidl
@@ -16,16 +16,17 @@ dictionary EventListenerOptions {
   /* Setting to true make the listener be added to the system group. */
   [Func="ThreadSafeIsChromeOrUAWidget"]
   boolean mozSystemGroup = false;
 };
 
 dictionary AddEventListenerOptions : EventListenerOptions {
   boolean passive;
   boolean once = false;
+  AbortSignal? signal; //XXX Spec PR is unclear whether this should be nullable.
   [ChromeOnly]
   boolean wantUntrusted;
 };
 
 [Exposed=(Window,Worker,WorkerDebugger,AudioWorklet)]
 interface EventTarget {
   [Throws]
   constructor();
deleted file mode 100644
--- a/testing/web-platform/meta/dom/events/AddEventListenerOptions-signal.any.js.ini
+++ /dev/null
@@ -1,34 +0,0 @@
-[AddEventListenerOptions-signal.any.html]
-  [Passing an AbortSignal to addEventListener works with the once flag]
-    expected: FAIL
-
-  [Passing an AbortSignal to addEventListener works with the capture flag]
-    expected: FAIL
-
-  [Aborting from a listener does not call future listeners]
-    expected: FAIL
-
-  [Passing an AbortSignal to multiple listeners]
-    expected: FAIL
-
-  [Passing an AbortSignal to addEventListener options should allow removing a listener]
-    expected: FAIL
-
-
-[AddEventListenerOptions-signal.any.worker.html]
-  expected: ERROR
-  [Passing an AbortSignal to addEventListener works with the once flag]
-    expected: FAIL
-
-  [Passing an AbortSignal to addEventListener works with the capture flag]
-    expected: FAIL
-
-  [Aborting from a listener does not call future listeners]
-    expected: FAIL
-
-  [Passing an AbortSignal to multiple listeners]
-    expected: FAIL
-
-  [Passing an AbortSignal to addEventListener options should allow removing a listener]
-    expected: FAIL
-