Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit ff1b3ed

Browse files
Olli PettayOlli Pettay
authored andcommitted
Bug 1679204 - Consider to add signal to addEventListener, r=edgar
This passes the tests which are in web-platform-tests/wpt#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
1 parent 246f677 commit ff1b3ed

File tree

8 files changed

+195
-107
lines changed

8 files changed

+195
-107
lines changed

dom/abort/AbortFollower.h

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
#ifndef mozilla_dom_AbortFollower_h
8+
#define mozilla_dom_AbortFollower_h
9+
10+
#include "nsISupportsImpl.h"
11+
#include "nsTObserverArray.h"
12+
13+
namespace mozilla {
14+
namespace dom {
15+
16+
class AbortSignal;
17+
class AbortSignalImpl;
18+
19+
// This class must be implemented by objects who want to follow an
20+
// AbortSignalImpl.
21+
class AbortFollower : public nsISupports {
22+
public:
23+
virtual void RunAbortAlgorithm() = 0;
24+
25+
void Follow(AbortSignalImpl* aSignal);
26+
27+
void Unfollow();
28+
29+
bool IsFollowing() const;
30+
31+
AbortSignalImpl* Signal() const { return mFollowingSignal; }
32+
33+
protected:
34+
// Subclasses of this class must call these Traverse and Unlink functions
35+
// during corresponding cycle collection operations.
36+
static void Traverse(AbortFollower* aFollower,
37+
nsCycleCollectionTraversalCallback& cb);
38+
39+
static void Unlink(AbortFollower* aFollower) { aFollower->Unfollow(); }
40+
41+
virtual ~AbortFollower();
42+
43+
friend class AbortSignalImpl;
44+
45+
RefPtr<AbortSignalImpl> mFollowingSignal;
46+
};
47+
48+
class AbortSignalImpl : public nsISupports {
49+
public:
50+
explicit AbortSignalImpl(bool aAborted);
51+
52+
bool Aborted() const;
53+
54+
virtual void SignalAbort();
55+
56+
protected:
57+
// Subclasses of this class must call these Traverse and Unlink functions
58+
// during corresponding cycle collection operations.
59+
static void Traverse(AbortSignalImpl* aSignal,
60+
nsCycleCollectionTraversalCallback& cb);
61+
62+
static void Unlink(AbortSignalImpl* aSignal) {
63+
// To be filled in shortly.
64+
}
65+
66+
virtual ~AbortSignalImpl() = default;
67+
68+
private:
69+
friend class AbortFollower;
70+
71+
// Raw pointers. |AbortFollower::Follow| adds to this array, and
72+
// |AbortFollower::Unfollow| (also callbed by the destructor) will remove
73+
// from this array. Finally, calling |SignalAbort()| will (after running all
74+
// abort algorithms) empty this and make all contained followers |Unfollow()|.
75+
nsTObserverArray<AbortFollower*> mFollowers;
76+
77+
bool mAborted;
78+
};
79+
80+
} // namespace dom
81+
} // namespace mozilla
82+
83+
#endif // mozilla_dom_AbortFollower_h

dom/abort/AbortSignal.h

Lines changed: 1 addition & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,77 +7,12 @@
77
#ifndef mozilla_dom_AbortSignal_h
88
#define mozilla_dom_AbortSignal_h
99

10+
#include "mozilla/dom/AbortFollower.h"
1011
#include "mozilla/DOMEventTargetHelper.h"
11-
#include "nsISupportsImpl.h"
12-
#include "nsTObserverArray.h"
1312

1413
namespace mozilla {
1514
namespace dom {
1615

17-
class AbortSignal;
18-
class AbortSignalImpl;
19-
20-
// This class must be implemented by objects who want to follow an
21-
// AbortSignalImpl.
22-
class AbortFollower : public nsISupports {
23-
public:
24-
virtual void RunAbortAlgorithm() = 0;
25-
26-
void Follow(AbortSignalImpl* aSignal);
27-
28-
void Unfollow();
29-
30-
bool IsFollowing() const;
31-
32-
AbortSignalImpl* Signal() const { return mFollowingSignal; }
33-
34-
protected:
35-
// Subclasses of this class must call these Traverse and Unlink functions
36-
// during corresponding cycle collection operations.
37-
static void Traverse(AbortFollower* aFollower,
38-
nsCycleCollectionTraversalCallback& cb);
39-
40-
static void Unlink(AbortFollower* aFollower) { aFollower->Unfollow(); }
41-
42-
virtual ~AbortFollower();
43-
44-
friend class AbortSignalImpl;
45-
46-
RefPtr<AbortSignalImpl> mFollowingSignal;
47-
};
48-
49-
class AbortSignalImpl : public nsISupports {
50-
public:
51-
explicit AbortSignalImpl(bool aAborted);
52-
53-
bool Aborted() const;
54-
55-
virtual void SignalAbort();
56-
57-
protected:
58-
// Subclasses of this class must call these Traverse and Unlink functions
59-
// during corresponding cycle collection operations.
60-
static void Traverse(AbortSignalImpl* aSignal,
61-
nsCycleCollectionTraversalCallback& cb);
62-
63-
static void Unlink(AbortSignalImpl* aSignal) {
64-
// To be filled in shortly.
65-
}
66-
67-
virtual ~AbortSignalImpl() = default;
68-
69-
private:
70-
friend class AbortFollower;
71-
72-
// Raw pointers. |AbortFollower::Follow| adds to this array, and
73-
// |AbortFollower::Unfollow| (also callbed by the destructor) will remove
74-
// from this array. Finally, calling |SignalAbort()| will (after running all
75-
// abort algorithms) empty this and make all contained followers |Unfollow()|.
76-
nsTObserverArray<AbortFollower*> mFollowers;
77-
78-
bool mAborted;
79-
};
80-
8116
// AbortSignal the spec concept includes the concept of a child signal
8217
// "following" a parent signal -- internally, adding abort steps to the parent
8318
// signal that will then signal abort on the child signal -- to propagate

dom/abort/moz.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ TEST_DIRS += ["tests"]
1111

1212
EXPORTS.mozilla.dom += [
1313
"AbortController.h",
14+
"AbortFollower.h",
1415
"AbortSignal.h",
1516
]
1617

dom/events/DOMEventTargetHelper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "mozilla/DOMEventTargetHelper.h"
1212
#include "mozilla/EventDispatcher.h"
1313
#include "mozilla/EventListenerManager.h"
14+
#include "mozilla/EventListenerManager.h"
1415
#include "mozilla/Likely.h"
1516
#include "MainThreadUtils.h"
1617

dom/events/EventListenerManager.cpp

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "mozilla/MemoryReporting.h"
2020
#include "mozilla/Preferences.h"
2121
#include "mozilla/PresShell.h"
22+
#include "mozilla/dom/AbortSignal.h"
2223
#include "mozilla/dom/BindingUtils.h"
2324
#include "mozilla/dom/EventCallbackDebuggerNotification.h"
2425
#include "mozilla/dom/Element.h"
@@ -172,6 +173,9 @@ inline void ImplCycleCollectionTraverse(
172173
CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
173174
aFlags);
174175
}
176+
177+
CycleCollectionNoteChild(aCallback, aField.mSignalFollower.get(),
178+
"mSignalFollower", aFlags);
175179
}
176180

177181
NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager)
@@ -205,14 +209,18 @@ EventListenerManager::GetTargetAsInnerWindow() const {
205209
void EventListenerManager::AddEventListenerInternal(
206210
EventListenerHolder aListenerHolder, EventMessage aEventMessage,
207211
nsAtom* aTypeAtom, const EventListenerFlags& aFlags, bool aHandler,
208-
bool aAllEvents) {
212+
bool aAllEvents, AbortSignal* aSignal) {
209213
MOZ_ASSERT((aEventMessage && aTypeAtom) || aAllEvents, // all-events listener
210214
"Missing type");
211215

212216
if (!aListenerHolder || mClearingListeners) {
213217
return;
214218
}
215219

220+
if (aSignal && aSignal->Aborted()) {
221+
return;
222+
}
223+
216224
// Since there is no public API to call us with an EventListenerHolder, we
217225
// know that there's an EventListenerHolder on the stack holding a strong ref
218226
// to the listener.
@@ -255,6 +263,11 @@ void EventListenerManager::AddEventListenerInternal(
255263
}
256264
listener->mListener = std::move(aListenerHolder);
257265

266+
if (aSignal) {
267+
listener->mSignalFollower = new ListenerSignalFollower(this, listener);
268+
listener->mSignalFollower->Follow(aSignal);
269+
}
270+
258271
if (aFlags.mInSystemGroup) {
259272
mMayHaveSystemGroupListeners = true;
260273
}
@@ -687,7 +700,8 @@ void EventListenerManager::MaybeMarkPassive(EventMessage aMessage,
687700

688701
void EventListenerManager::AddEventListenerByType(
689702
EventListenerHolder aListenerHolder, const nsAString& aType,
690-
const EventListenerFlags& aFlags, const Optional<bool>& aPassive) {
703+
const EventListenerFlags& aFlags, const Optional<bool>& aPassive,
704+
AbortSignal* aSignal) {
691705
RefPtr<nsAtom> atom;
692706
EventMessage message =
693707
GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
@@ -699,7 +713,8 @@ void EventListenerManager::AddEventListenerByType(
699713
MaybeMarkPassive(message, flags);
700714
}
701715

702-
AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags);
716+
AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags,
717+
false, false, aSignal);
703718
}
704719

705720
void EventListenerManager::RemoveEventListenerByType(
@@ -1370,6 +1385,7 @@ void EventListenerManager::AddEventListener(
13701385
bool aWantsUntrusted) {
13711386
EventListenerFlags flags;
13721387
Optional<bool> passive;
1388+
AbortSignal* signal = nullptr;
13731389
if (aOptions.IsBoolean()) {
13741390
flags.mCapture = aOptions.GetAsBoolean();
13751391
} else {
@@ -1380,10 +1396,15 @@ void EventListenerManager::AddEventListener(
13801396
if (options.mPassive.WasPassed()) {
13811397
passive.Construct(options.mPassive.Value());
13821398
}
1399+
1400+
if (options.mSignal.WasPassed()) {
1401+
signal = options.mSignal.Value();
1402+
}
13831403
}
1404+
13841405
flags.mAllowUntrustedEvents = aWantsUntrusted;
13851406
return AddEventListenerByType(std::move(aListenerHolder), aType, flags,
1386-
passive);
1407+
passive, signal);
13871408
}
13881409

13891410
void EventListenerManager::RemoveEventListener(
@@ -1789,4 +1810,47 @@ EventListenerManager::GetScriptGlobalAndDocument(Document** aDoc) {
17891810
return global.forget();
17901811
}
17911812

1813+
EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
1814+
EventListenerManager* aListenerManager,
1815+
EventListenerManager::Listener* aListener)
1816+
: dom::AbortFollower(),
1817+
mListenerManager(aListenerManager),
1818+
mListener(aListener->mListener.Clone()),
1819+
mTypeAtom(aListener->mTypeAtom),
1820+
mEventMessage(aListener->mEventMessage),
1821+
mAllEvents(aListener->mAllEvents),
1822+
mFlags(aListener->mFlags){};
1823+
1824+
NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower)
1825+
1826+
NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower)
1827+
NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower)
1828+
1829+
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
1830+
EventListenerManager::ListenerSignalFollower)
1831+
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
1832+
AbortFollower::Traverse(static_cast<AbortFollower*>(tmp), cb);
1833+
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1834+
1835+
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
1836+
EventListenerManager::ListenerSignalFollower)
1837+
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
1838+
AbortFollower::Unlink(static_cast<AbortFollower*>(tmp));
1839+
tmp->mListenerManager = nullptr;
1840+
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1841+
1842+
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
1843+
EventListenerManager::ListenerSignalFollower)
1844+
NS_INTERFACE_MAP_ENTRY(nsISupports)
1845+
NS_INTERFACE_MAP_END
1846+
1847+
void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
1848+
if (mListenerManager) {
1849+
RefPtr<EventListenerManager> elm = mListenerManager;
1850+
mListenerManager = nullptr;
1851+
elm->RemoveEventListenerInternal(std::move(mListener), mEventMessage,
1852+
mTypeAtom, mFlags, mAllEvents);
1853+
}
1854+
}
1855+
17921856
} // namespace mozilla

0 commit comments

Comments
 (0)