author | Olli Pettay <Olli.Pettay@helsinki.fi> |
Mon, 14 Dec 2020 15:45:15 +0000 | |
changeset 560621 | b3d0fba5fd8d624b5abf53df196aedae5e83a269 |
parent 560620 | dff02098b9a627cce58cbfd8553597833c807456 |
child 560622 | e45001cddaf486353a3ec9878be55f214ac9717d |
push id | 38032 |
push user | csabou@mozilla.com |
push date | Tue, 15 Dec 2020 09:29:54 +0000 |
treeherder | mozilla-central@f805f27183c3 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | edgar |
bugs | 1679204, 26472 |
milestone | 85.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
|
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 -