From be82b3331878fb2956d22d9bd3906fca3ab39751 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 10 Jun 2020 21:33:28 +0000 Subject: Issue #1587 - Part 2: Implement controller follow/unfollow --- dom/fetch/FetchController.cpp | 23 ++++++- dom/fetch/FetchController.h | 11 +++- dom/fetch/FetchSignal.cpp | 77 +++++++++++++++++++++- dom/fetch/FetchSignal.h | 31 +++++++++ .../mochitest/fetch/file_fetch_controller.html | 68 +++++++++++++++++++ 5 files changed, 202 insertions(+), 8 deletions(-) (limited to 'dom') diff --git a/dom/fetch/FetchController.cpp b/dom/fetch/FetchController.cpp index b3d8a4d9c..2eb40b980 100644 --- a/dom/fetch/FetchController.cpp +++ b/dom/fetch/FetchController.cpp @@ -12,7 +12,8 @@ namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FetchController, mGlobal, mSignal) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FetchController, mGlobal, mSignal, + mFollowingSignal) NS_IMPL_CYCLE_COLLECTING_ADDREF(FetchController) NS_IMPL_CYCLE_COLLECTING_RELEASE(FetchController) @@ -97,13 +98,29 @@ FetchController::Abort() void FetchController::Follow(FetchSignal& aSignal) { - // TODO + FetchSignal::Follower::Follow(&aSignal); } void FetchController::Unfollow(FetchSignal& aSignal) { - // TODO + if (mFollowingSignal != &aSignal) { + return; + } + + FetchSignal::Follower::Unfollow(); +} + +FetchSignal* +FetchController::Following() const +{ + return mFollowingSignal; +} + +void +FetchController::Aborted() +{ + Abort(); } } // dom namespace diff --git a/dom/fetch/FetchController.h b/dom/fetch/FetchController.h index 854c6f974..7a0132dca 100644 --- a/dom/fetch/FetchController.h +++ b/dom/fetch/FetchController.h @@ -8,6 +8,7 @@ #define mozilla_dom_FetchController_h #include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/FetchSignal.h" #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" #include "mozilla/ErrorResult.h" @@ -16,10 +17,9 @@ namespace mozilla { namespace dom { -class FetchSignal; - class FetchController final : public nsISupports , public nsWrapperCache + , public FetchSignal::Follower { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -51,6 +51,13 @@ public: void Unfollow(FetchSignal& aSignal); + FetchSignal* + Following() const; + + // FetchSignal::Follower + + void Aborted() override; + private: ~FetchController() = default; diff --git a/dom/fetch/FetchSignal.cpp b/dom/fetch/FetchSignal.cpp index 4395dbcf2..1924263e8 100644 --- a/dom/fetch/FetchSignal.cpp +++ b/dom/fetch/FetchSignal.cpp @@ -16,12 +16,12 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(FetchSignal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FetchSignal, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mController) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mController) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FetchSignal, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mController) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mController) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FetchSignal) @@ -55,6 +55,11 @@ FetchSignal::Abort() MOZ_ASSERT(!mAborted); mAborted = true; + // Let's inform the followers. + for (uint32_t i = 0; i < mFollowers.Length(); ++i) { + mFollowers[i]->Aborted(); + } + EventInit init; init.mBubbles = false; init.mCancelable = false; @@ -65,7 +70,73 @@ FetchSignal::Abort() Event::Constructor(this, NS_LITERAL_STRING("abort"), init); event->SetTrusted(true); - DispatchDOMEvent(nullptr, event, nullptr, nullptr); + bool dummy; + DispatchEvent(event, &dummy); +} + +void +FetchSignal::AddFollower(FetchSignal::Follower* aFollower) +{ + MOZ_DIAGNOSTIC_ASSERT(aFollower); + if (!mFollowers.Contains(aFollower)) { + mFollowers.AppendElement(aFollower); + } +} + +void +FetchSignal::RemoveFollower(FetchSignal::Follower* aFollower) +{ + MOZ_DIAGNOSTIC_ASSERT(aFollower); + mFollowers.RemoveElement(aFollower); +} + +bool +FetchSignal::CanAcceptFollower(FetchSignal::Follower* aFollower) const +{ + MOZ_DIAGNOSTIC_ASSERT(aFollower); + + if (aFollower == mController) { + return false; + } + + FetchSignal* following = mController->Following(); + if (!following) { + return true; + } + + return following->CanAcceptFollower(aFollower); +} + +// FetchSignal::Follower +// ---------------------------------------------------------------------------- + +FetchSignal::Follower::~Follower() +{ + Unfollow(); +} + +void +FetchSignal::Follower::Follow(FetchSignal* aSignal) +{ + MOZ_DIAGNOSTIC_ASSERT(aSignal); + + if (!aSignal->CanAcceptFollower(this)) { + return; + } + + Unfollow(); + + mFollowingSignal = aSignal; + aSignal->AddFollower(this); +} + +void +FetchSignal::Follower::Unfollow() +{ + if (mFollowingSignal) { + mFollowingSignal->RemoveFollower(this); + mFollowingSignal = nullptr; + } } } // dom namespace diff --git a/dom/fetch/FetchSignal.h b/dom/fetch/FetchSignal.h index 5bb16b834..5d2f13c68 100644 --- a/dom/fetch/FetchSignal.h +++ b/dom/fetch/FetchSignal.h @@ -13,10 +13,29 @@ namespace mozilla { namespace dom { class FetchController; +class FetchSignal; class FetchSignal final : public DOMEventTargetHelper { public: + // This class must be implemented by objects who want to follow a FetchSignal. + class Follower + { + public: + virtual void Aborted() = 0; + + protected: + virtual ~Follower(); + + void + Follow(FetchSignal* aSignal); + + void + Unfollow(); + + RefPtr mFollowingSignal; + }; + NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FetchSignal, DOMEventTargetHelper) @@ -33,11 +52,23 @@ public: IMPL_EVENT_HANDLER(abort); + void + AddFollower(Follower* aFollower); + + void + RemoveFollower(Follower* aFollower); + + bool + CanAcceptFollower(Follower* aFollower) const; + private: ~FetchSignal() = default; RefPtr mController; + // Raw pointers. Follower unregisters itself in the DTOR. + nsTArray mFollowers; + bool mAborted; }; diff --git a/dom/tests/mochitest/fetch/file_fetch_controller.html b/dom/tests/mochitest/fetch/file_fetch_controller.html index 6efa2fe0a..791d21b2b 100644 --- a/dom/tests/mochitest/fetch/file_fetch_controller.html +++ b/dom/tests/mochitest/fetch/file_fetch_controller.html @@ -21,8 +21,76 @@ function testWebIDL() { next(); } +function testUpdateData() { + var fc = new FetchController(); + + is(fc.signal.aborted, false, "By default FetchSignal.aborted is false"); + + fc.abort(); + is(fc.signal.aborted, true, "Signal is aborted"); + + next(); +} + +function testFollowingOurself() { + // Let's follow ourself + var fc = new FetchController(); + fc.follow(fc.signal); + + fc.abort(); + is(fc.signal.aborted, true, "Signal is aborted"); + + next(); +} + +function testFollowingOther() { + // Let's follow another one + var fc1 = new FetchController(); + var fc2 = new FetchController(); + fc1.follow(fc2.signal); + + fc2.abort(); + + is(fc1.signal.aborted, true, "Signal is aborted"); + is(fc2.signal.aborted, true, "Signal is aborted"); + + next(); +} + +function testFollowingLoop() { + // fc1 -> fc2 -> fc3 -> fc1 + var fc1 = new FetchController(); + var fc2 = new FetchController(); + var fc3 = new FetchController(); + fc1.follow(fc2.signal); + fc2.follow(fc3.signal); + fc3.follow(fc1.signal); + + fc3.abort(); + + is(fc1.signal.aborted, true, "Signal is aborted"); + is(fc2.signal.aborted, true, "Signal is aborted"); + is(fc3.signal.aborted, true, "Signal is aborted"); + + next(); +} + +function testAbortEvent() { + var fc = new FetchController(); + fc.signal.onabort = function(e) { + is(e.type, "abort", "Abort received"); + next(); + } + fc.abort(); +} + var steps = [ testWebIDL, + testUpdateData, + testFollowingOurself, + testFollowingOther, + testFollowingLoop, + testAbortEvent, ]; function next() { -- cgit v1.2.3