From a8602cc877cb4fc007655cd0f52b52b1e7165bc8 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 10 Jun 2020 21:08:58 +0000 Subject: Issue #1587 - Part 1: Implement FetchController/FetchSignal interface --- dom/fetch/FetchController.cpp | 110 +++++++++++++++++++++ dom/fetch/FetchController.h | 66 +++++++++++++ dom/fetch/FetchSignal.cpp | 72 ++++++++++++++ dom/fetch/FetchSignal.h | 47 +++++++++ dom/fetch/Request.h | 1 + dom/fetch/moz.build | 4 + .../mochitest/fetch/file_fetch_controller.html | 40 ++++++++ dom/tests/mochitest/fetch/mochitest.ini | 2 + .../mochitest/fetch/test_fetch_controller.html | 40 ++++++++ dom/webidl/FetchController.webidl | 15 +++ dom/webidl/FetchSignal.webidl | 13 +++ dom/webidl/Request.webidl | 3 + dom/webidl/moz.build | 2 + dom/workers/WorkerPrefs.h | 1 + 14 files changed, 416 insertions(+) create mode 100644 dom/fetch/FetchController.cpp create mode 100644 dom/fetch/FetchController.h create mode 100644 dom/fetch/FetchSignal.cpp create mode 100644 dom/fetch/FetchSignal.h create mode 100644 dom/tests/mochitest/fetch/file_fetch_controller.html create mode 100644 dom/tests/mochitest/fetch/test_fetch_controller.html create mode 100644 dom/webidl/FetchController.webidl create mode 100644 dom/webidl/FetchSignal.webidl (limited to 'dom') diff --git a/dom/fetch/FetchController.cpp b/dom/fetch/FetchController.cpp new file mode 100644 index 000000000..b3d8a4d9c --- /dev/null +++ b/dom/fetch/FetchController.cpp @@ -0,0 +1,110 @@ +/* -*- 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/. */ + +#include "FetchController.h" +#include "FetchSignal.h" +#include "mozilla/dom/FetchControllerBinding.h" +#include "WorkerPrivate.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FetchController, mGlobal, mSignal) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(FetchController) +NS_IMPL_CYCLE_COLLECTING_RELEASE(FetchController) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchController) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +/* static */ bool +FetchController::IsEnabled(JSContext* aCx, JSObject* aGlobal) +{ + if (NS_IsMainThread()) { + return Preferences::GetBool("dom.fetchController.enabled", false); + } + + using namespace workers; + + // Otherwise, check the pref via the WorkerPrivate + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); + if (!workerPrivate) { + return false; + } + + return workerPrivate->FetchControllerEnabled(); +} + +/* static */ already_AddRefed +FetchController::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr fetchController = new FetchController(global); + return fetchController.forget(); +} + +FetchController::FetchController(nsIGlobalObject* aGlobal) + : mGlobal(aGlobal) + , mAborted(false) +{} + +JSObject* +FetchController::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return FetchControllerBinding::Wrap(aCx, this, aGivenProto); +} + +nsIGlobalObject* +FetchController::GetParentObject() const +{ + return mGlobal; +} + +FetchSignal* +FetchController::Signal() +{ + if (!mSignal) { + mSignal = new FetchSignal(this, mAborted); + } + + return mSignal; +} + +void +FetchController::Abort() +{ + if (mAborted) { + return; + } + + mAborted = true; + + if (mSignal) { + mSignal->Abort(); + } +} + +void +FetchController::Follow(FetchSignal& aSignal) +{ + // TODO +} + +void +FetchController::Unfollow(FetchSignal& aSignal) +{ + // TODO +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/fetch/FetchController.h b/dom/fetch/FetchController.h new file mode 100644 index 000000000..854c6f974 --- /dev/null +++ b/dom/fetch/FetchController.h @@ -0,0 +1,66 @@ +/* -*- 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_FetchController_h +#define mozilla_dom_FetchController_h + +#include "mozilla/dom/BindingDeclarations.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "mozilla/ErrorResult.h" +#include "nsIGlobalObject.h" + +namespace mozilla { +namespace dom { + +class FetchSignal; + +class FetchController final : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FetchController) + + static bool + IsEnabled(JSContext* aCx, JSObject* aGlobal); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); + + explicit FetchController(nsIGlobalObject* aGlobal); + + JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + nsIGlobalObject* + GetParentObject() const; + + FetchSignal* + Signal(); + + void + Abort(); + + void + Follow(FetchSignal& aSignal); + + void + Unfollow(FetchSignal& aSignal); + +private: + ~FetchController() = default; + + nsCOMPtr mGlobal; + RefPtr mSignal; + + bool mAborted; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_FetchController_h diff --git a/dom/fetch/FetchSignal.cpp b/dom/fetch/FetchSignal.cpp new file mode 100644 index 000000000..4395dbcf2 --- /dev/null +++ b/dom/fetch/FetchSignal.cpp @@ -0,0 +1,72 @@ +/* -*- 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/. */ + +#include "FetchSignal.h" +#include "FetchController.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/FetchSignalBinding.h" + +namespace mozilla { +namespace dom { + +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_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FetchSignal, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mController) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FetchSignal) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(FetchSignal, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(FetchSignal, DOMEventTargetHelper) + +FetchSignal::FetchSignal(FetchController* aController, + bool aAborted) + : DOMEventTargetHelper(aController->GetParentObject()) + , mController(aController) + , mAborted(aAborted) +{} + +JSObject* +FetchSignal::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return FetchSignalBinding::Wrap(aCx, this, aGivenProto); +} + +bool +FetchSignal::Aborted() const +{ + return mAborted; +} + +void +FetchSignal::Abort() +{ + MOZ_ASSERT(!mAborted); + mAborted = true; + + EventInit init; + init.mBubbles = false; + init.mCancelable = false; + + // TODO which kind of event should we dispatch here? + + RefPtr event = + Event::Constructor(this, NS_LITERAL_STRING("abort"), init); + event->SetTrusted(true); + + DispatchDOMEvent(nullptr, event, nullptr, nullptr); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/fetch/FetchSignal.h b/dom/fetch/FetchSignal.h new file mode 100644 index 000000000..5bb16b834 --- /dev/null +++ b/dom/fetch/FetchSignal.h @@ -0,0 +1,47 @@ +/* -*- 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_FetchSignal_h +#define mozilla_dom_FetchSignal_h + +#include "mozilla/DOMEventTargetHelper.h" + +namespace mozilla { +namespace dom { + +class FetchController; + +class FetchSignal final : public DOMEventTargetHelper +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FetchSignal, DOMEventTargetHelper) + + FetchSignal(FetchController* aController, bool aAborted); + + JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + bool + Aborted() const; + + void + Abort(); + + IMPL_EVENT_HANDLER(abort); + +private: + ~FetchSignal() = default; + + RefPtr mController; + + bool mAborted; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_FetchSignal_h diff --git a/dom/fetch/Request.h b/dom/fetch/Request.h index d33c74812..56a75e5af 100644 --- a/dom/fetch/Request.h +++ b/dom/fetch/Request.h @@ -12,6 +12,7 @@ #include "nsWrapperCache.h" #include "mozilla/dom/Fetch.h" +#include "mozilla/dom/FetchSignal.h" #include "mozilla/dom/InternalRequest.h" // Required here due to certain WebIDL enums/classes being declared in both // files. diff --git a/dom/fetch/moz.build b/dom/fetch/moz.build index be820ab57..757f857f2 100644 --- a/dom/fetch/moz.build +++ b/dom/fetch/moz.build @@ -7,8 +7,10 @@ EXPORTS.mozilla.dom += [ 'ChannelInfo.h', 'Fetch.h', + 'FetchController.h', 'FetchDriver.h', 'FetchIPCTypes.h', + 'FetchSignal.h', 'FetchUtil.h', 'Headers.h', 'InternalHeaders.h', @@ -27,7 +29,9 @@ UNIFIED_SOURCES += [ SOURCES += [ 'ChannelInfo.cpp', + 'FetchController.cpp', 'FetchDriver.cpp', + 'FetchSignal.cpp', 'FetchUtil.cpp', 'Headers.cpp', 'InternalHeaders.cpp', diff --git a/dom/tests/mochitest/fetch/file_fetch_controller.html b/dom/tests/mochitest/fetch/file_fetch_controller.html new file mode 100644 index 000000000..6efa2fe0a --- /dev/null +++ b/dom/tests/mochitest/fetch/file_fetch_controller.html @@ -0,0 +1,40 @@ + diff --git a/dom/tests/mochitest/fetch/mochitest.ini b/dom/tests/mochitest/fetch/mochitest.ini index cf4477463..6dba08f98 100644 --- a/dom/tests/mochitest/fetch/mochitest.ini +++ b/dom/tests/mochitest/fetch/mochitest.ini @@ -1,6 +1,7 @@ [DEFAULT] support-files = fetch_test_framework.js + file_fetch_controller.html test_fetch_basic.js test_fetch_basic_http.js test_fetch_cors.js @@ -41,6 +42,7 @@ support-files = [test_fetch_basic_http.html] [test_fetch_basic_http_sw_reroute.html] [test_fetch_basic_http_sw_empty_reroute.html] +[test_fetch_controller.html] [test_fetch_cors.html] skip-if = toolkit == 'android' # Bug 1210282 [test_fetch_cors_sw_reroute.html] diff --git a/dom/tests/mochitest/fetch/test_fetch_controller.html b/dom/tests/mochitest/fetch/test_fetch_controller.html new file mode 100644 index 000000000..812fb9161 --- /dev/null +++ b/dom/tests/mochitest/fetch/test_fetch_controller.html @@ -0,0 +1,40 @@ + + + + + Test FetchController + + + + + + + + diff --git a/dom/webidl/FetchController.webidl b/dom/webidl/FetchController.webidl new file mode 100644 index 000000000..c5b1cc6da --- /dev/null +++ b/dom/webidl/FetchController.webidl @@ -0,0 +1,15 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +[Constructor(), Exposed=(Window,Worker), + Func="FetchController::IsEnabled"] +interface FetchController { + readonly attribute FetchSignal signal; + + void abort(); + void follow(FetchSignal signal); + void unfollow(FetchSignal signal); +}; diff --git a/dom/webidl/FetchSignal.webidl b/dom/webidl/FetchSignal.webidl new file mode 100644 index 000000000..965355c20 --- /dev/null +++ b/dom/webidl/FetchSignal.webidl @@ -0,0 +1,13 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +[Exposed=(Window,Worker), + Func="FetchController::IsEnabled"] +interface FetchSignal : EventTarget { + readonly attribute boolean aborted; + + attribute EventHandler onabort; +}; diff --git a/dom/webidl/Request.webidl b/dom/webidl/Request.webidl index e29c084d0..00497456a 100644 --- a/dom/webidl/Request.webidl +++ b/dom/webidl/Request.webidl @@ -47,6 +47,9 @@ dictionary RequestInit { RequestCache cache; RequestRedirect redirect; DOMString integrity; + + [Func="FetchController::IsEnabled"] + FetchSignal signal; }; // Gecko currently does not ship RequestContext, so please don't use it in IDL diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 45bf92a97..e21cd7530 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -141,7 +141,9 @@ WEBIDL_FILES = [ 'ExtendableMessageEvent.webidl', 'FakePluginTagInit.webidl', 'Fetch.webidl', + 'FetchController.webidl', 'FetchEvent.webidl', + 'FetchSignal.webidl', 'File.webidl', 'FileList.webidl', 'FileMode.webidl', diff --git a/dom/workers/WorkerPrefs.h b/dom/workers/WorkerPrefs.h index 9a1be4801..d3e018b62 100644 --- a/dom/workers/WorkerPrefs.h +++ b/dom/workers/WorkerPrefs.h @@ -39,6 +39,7 @@ WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED) WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED) WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED) WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK) +WORKER_SIMPLE_PREF("dom.fetchController.enabled", FetchControllerEnabled, FETCHCONTROLLER_ENABLED) WORKER_PREF("dom.workers.latestJSVersion", JSVersionChanged) WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) WORKER_PREF("general.appname.override", AppNameOverrideChanged) -- cgit v1.2.3 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 From 1ca6039fb86cfab374fa0f4d37d9a084aa810f28 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 10 Jun 2020 21:51:44 +0000 Subject: Issue #1587 - Part 3: Hook FetchSignal up to the Fetch API --- dom/fetch/Fetch.cpp | 158 ++++++++++++++++++--- dom/fetch/FetchDriver.cpp | 45 ++++-- dom/fetch/FetchDriver.h | 29 +++- dom/fetch/FetchSignal.cpp | 8 ++ dom/fetch/FetchSignal.h | 1 + .../mochitest/fetch/file_fetch_controller.html | 81 +++++++++++ 6 files changed, 292 insertions(+), 30 deletions(-) (limited to 'dom') diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index f944352e3..11e93205c 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -52,41 +52,137 @@ namespace dom { using namespace workers; +// This class helps the proxying of FetchSignal changes cross threads. +class FetchSignalProxy final : public FetchSignal::Follower +{ + // This is created and released on the main-thread. + RefPtr mSignalMainThread; + + // This value is used only for the creation of FetchSignal on the + // main-thread. They are not updated. + const bool mAborted; + + // This runnable propagates changes from the FetchSignal on workers to the + // FetchSignal on main-thread. + class FetchSignalProxyRunnable final : public Runnable + { + RefPtr mProxy; + + public: + explicit FetchSignalProxyRunnable(FetchSignalProxy* aProxy) + : mProxy(aProxy) + {} + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + FetchSignal* signal = mProxy->GetOrCreateSignalForMainThread(); + signal->Abort(); + return NS_OK; + } + }; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FetchSignalProxy) + + explicit FetchSignalProxy(FetchSignal* aSignal) + : mAborted(aSignal->Aborted()) + { + Follow(aSignal); + } + + void + Aborted() override + { + RefPtr runnable = + new FetchSignalProxyRunnable(this); + NS_DispatchToMainThread(runnable); + } + + FetchSignal* + GetOrCreateSignalForMainThread() + { + MOZ_ASSERT(NS_IsMainThread()); + if (!mSignalMainThread) { + mSignalMainThread = new FetchSignal(mAborted); + } + return mSignalMainThread; + } + + void + Shutdown() + { + Unfollow(); + } + +private: + ~FetchSignalProxy() + { + NS_ReleaseOnMainThread(mSignalMainThread.forget()); + } +}; + class WorkerFetchResolver final : public FetchDriverObserver { friend class MainThreadFetchRunnable; + friend class WorkerFetchResponseEndBase; friend class WorkerFetchResponseEndRunnable; friend class WorkerFetchResponseRunnable; RefPtr mPromiseProxy; + RefPtr mSignalProxy; + public: // Returns null if worker is shutting down. static already_AddRefed - Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise) + Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise, + FetchSignal* aSignal) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - RefPtr proxy = PromiseWorkerProxy::Create(aWorkerPrivate, aPromise); + RefPtr proxy = + PromiseWorkerProxy::Create(aWorkerPrivate, aPromise); if (!proxy) { return nullptr; } - RefPtr r = new WorkerFetchResolver(proxy); + RefPtr signalProxy; + if (aSignal) { + signalProxy = new FetchSignalProxy(aSignal); + } + + RefPtr r = new WorkerFetchResolver(proxy, signalProxy); return r.forget(); } + FetchSignal* + GetFetchSignal() + { + MOZ_ASSERT(NS_IsMainThread()); + + if (!mSignalProxy) { + return nullptr; + } + + return mSignalProxy->GetOrCreateSignalForMainThread(); + } + void OnResponseAvailableInternal(InternalResponse* aResponse) override; void - OnResponseEnd() override; + OnResponseEnd(FetchDriverObserver::EndReason eReason) override; private: - explicit WorkerFetchResolver(PromiseWorkerProxy* aProxy) + WorkerFetchResolver(PromiseWorkerProxy* aProxy, + FetchSignalProxy* aSignalProxy) : mPromiseProxy(aProxy) + , mSignalProxy(aSignalProxy) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(mPromiseProxy); + } ~WorkerFetchResolver() @@ -115,8 +211,12 @@ public: mDocument = aDocument; } - virtual void OnResponseEnd() override + void OnResponseEnd(FetchDriverObserver::EndReason aReason) override { + if (aReason == eAborted) { + mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + } + FlushConsoleReport(); } @@ -170,9 +270,11 @@ public: fetch->SetWorkerScript(spec); } + RefPtr signal = mResolver->GetFetchSignal(); + // ...but release it before calling Fetch, because mResolver's callback can // be called synchronously and they want the mutex, too. - return fetch->Fetch(mResolver); + return fetch->Fetch(signal, mResolver); } }; @@ -210,6 +312,12 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, RefPtr r = request->GetInternalRequest(); + RefPtr signal; + if (aInit.mSignal.WasPassed()) { + signal = &aInit.mSignal.Value(); + // Let's FetchDriver to deal with an already aborted signal. + } + if (NS_IsMainThread()) { nsCOMPtr window = do_QueryInterface(aGlobal); nsCOMPtr doc; @@ -240,7 +348,7 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, RefPtr fetch = new FetchDriver(r, principal, loadGroup); fetch->SetDocument(doc); resolver->SetDocument(doc); - aRv = fetch->Fetch(resolver); + aRv = fetch->Fetch(signal, resolver); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -252,7 +360,7 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, r->SetSkipServiceWorker(); } - RefPtr resolver = WorkerFetchResolver::Create(worker, p); + RefPtr resolver = WorkerFetchResolver::Create(worker, p, signal); if (!resolver) { NS_WARNING("Could not add WorkerFetchResolver workerHolder to worker"); aRv.Throw(NS_ERROR_DOM_ABORT_ERR); @@ -306,6 +414,7 @@ public: , mResolver(aResolver) , mInternalResponse(aResponse) { + MOZ_ASSERT(mResolver); } bool @@ -332,9 +441,13 @@ public: class WorkerFetchResponseEndBase { RefPtr mPromiseProxy; + RefPtr mSignalProxy; + public: - explicit WorkerFetchResponseEndBase(PromiseWorkerProxy* aPromiseProxy) + WorkerFetchResponseEndBase(PromiseWorkerProxy* aPromiseProxy, + FetchSignalProxy* aSignalProxy) : mPromiseProxy(aPromiseProxy) + , mSignalProxy(aSignalProxy) { MOZ_ASSERT(mPromiseProxy); } @@ -344,7 +457,16 @@ public: { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); + + RefPtr promise = mPromiseProxy->WorkerPromise(); + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + mPromiseProxy->CleanUp(); + + if (mSignalProxy) { + mSignalProxy->Shutdown(); + mSignalProxy = nullptr; + } } }; @@ -352,9 +474,10 @@ class WorkerFetchResponseEndRunnable final : public MainThreadWorkerRunnable , public WorkerFetchResponseEndBase { public: - explicit WorkerFetchResponseEndRunnable(PromiseWorkerProxy* aPromiseProxy) + WorkerFetchResponseEndRunnable(PromiseWorkerProxy* aPromiseProxy, + FetchSignalProxy* aSignalProxy) : MainThreadWorkerRunnable(aPromiseProxy->GetWorkerPrivate()) - , WorkerFetchResponseEndBase(aPromiseProxy) + , WorkerFetchResponseEndBase(aPromiseProxy, aSignalProxy) { } @@ -379,9 +502,10 @@ class WorkerFetchResponseEndControlRunnable final : public MainThreadWorkerContr , public WorkerFetchResponseEndBase { public: - explicit WorkerFetchResponseEndControlRunnable(PromiseWorkerProxy* aPromiseProxy) + WorkerFetchResponseEndControlRunnable(PromiseWorkerProxy* aPromiseProxy, + FetchSignalProxy* aSignalProxy) : MainThreadWorkerControlRunnable(aPromiseProxy->GetWorkerPrivate()) - , WorkerFetchResponseEndBase(aPromiseProxy) + , WorkerFetchResponseEndBase(aPromiseProxy, aSignalProxy) { } @@ -415,7 +539,7 @@ WorkerFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse) } void -WorkerFetchResolver::OnResponseEnd() +WorkerFetchResolver::OnResponseEnd(FetchDriverObserver::EndReason aReason) { AssertIsOnMainThread(); MutexAutoLock lock(mPromiseProxy->Lock()); @@ -426,11 +550,11 @@ WorkerFetchResolver::OnResponseEnd() FlushConsoleReport(); RefPtr r = - new WorkerFetchResponseEndRunnable(mPromiseProxy); + new WorkerFetchResponseEndRunnable(mPromiseProxy, mSignalProxy); if (!r->Dispatch()) { RefPtr cr = - new WorkerFetchResponseEndControlRunnable(mPromiseProxy); + new WorkerFetchResponseEndControlRunnable(mPromiseProxy, mSignalProxy); // This can fail if the worker thread is canceled or killed causing // the PromiseWorkerProxy to give up its WorkerHolder immediately, // allowing the worker thread to become Dead. diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 6294b0dc5..448ec64cd 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -67,7 +67,7 @@ FetchDriver::~FetchDriver() } nsresult -FetchDriver::Fetch(FetchDriverObserver* aObserver) +FetchDriver::Fetch(FetchSignal* aSignal, FetchDriverObserver* aObserver) { workers::AssertIsOnMainThread(); #ifdef DEBUG @@ -90,6 +90,18 @@ FetchDriver::Fetch(FetchDriverObserver* aObserver) } mRequest->SetPrincipalInfo(Move(principalInfo)); + + // If the signal is aborted, it's time to inform the observer and terminate + // the operation. + if (aSignal) { + if (aSignal->Aborted()) { + Aborted(); + return NS_OK; + } + + Follow(aSignal); + } + if (NS_FAILED(HttpFetch())) { FailWithNetworkError(); } @@ -114,11 +126,7 @@ FetchDriver::HttpFetch() nsAutoCString url; mRequest->GetURL(url); nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), - url, - nullptr, - nullptr, - ios); + rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr, ios); NS_ENSURE_SUCCESS(rv, rv); // Unsafe requests aren't allowed with when using no-core mode. @@ -380,6 +388,8 @@ FetchDriver::HttpFetch() NS_ENSURE_SUCCESS(rv, rv); // Step 4 onwards of "HTTP Fetch" is handled internally by Necko. + + mChannel = chan; return NS_OK; } already_AddRefed @@ -433,9 +443,11 @@ FetchDriver::FailWithNetworkError() #ifdef DEBUG mResponseAvailableCalled = true; #endif - mObserver->OnResponseEnd(); + mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking); mObserver = nullptr; } + + mChannel = nullptr; } namespace { @@ -777,10 +789,11 @@ FetchDriver::OnStopRequest(nsIRequest* aRequest, #endif } - mObserver->OnResponseEnd(); + mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking); mObserver = nullptr; } + mChannel = nullptr; return NS_OK; } @@ -921,5 +934,21 @@ FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const } } +void FetchDriver::Aborted() +{ + if (mObserver) { +#ifdef DEBUG + mResponseAvailableCalled = true; +#endif + mObserver->OnResponseEnd(FetchDriverObserver::eAborted); + mObserver = nullptr; + } + + if (mChannel) { + mChannel->Cancel(NS_BINDING_ABORTED); + mChannel = nullptr; + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/fetch/FetchDriver.h b/dom/fetch/FetchDriver.h index f74298a48..0ca9a34ee 100644 --- a/dom/fetch/FetchDriver.h +++ b/dom/fetch/FetchDriver.h @@ -12,6 +12,7 @@ #include "nsIStreamListener.h" #include "nsIThreadRetargetableStreamListener.h" #include "mozilla/ConsoleReportCollector.h" +#include "mozilla/dom/FetchSignal.h" #include "mozilla/dom/SRIMetadata.h" #include "mozilla/RefPtr.h" @@ -49,7 +50,14 @@ public: mGotResponseAvailable = true; OnResponseAvailableInternal(aResponse); } - virtual void OnResponseEnd() + + enum EndReason + { + eAborted, + eByNetworking, + }; + + virtual void OnResponseEnd(EndReason aReason) { }; nsIConsoleReportCollector* GetReporter() const @@ -58,6 +66,7 @@ public: } virtual void FlushConsoleReport() = 0; + protected: virtual ~FetchDriverObserver() { }; @@ -72,7 +81,8 @@ private: class FetchDriver final : public nsIStreamListener, public nsIChannelEventSink, public nsIInterfaceRequestor, - public nsIThreadRetargetableStreamListener + public nsIThreadRetargetableStreamListener, + public FetchSignal::Follower { public: NS_DECL_ISUPPORTS @@ -82,9 +92,12 @@ public: NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER - explicit FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal, - nsILoadGroup* aLoadGroup); - NS_IMETHOD Fetch(FetchDriverObserver* aObserver); + FetchDriver(InternalRequest* aRequest, + nsIPrincipal* aPrincipal, + nsILoadGroup* aLoadGroup); + + nsresult Fetch(FetchSignal* aSignal, + FetchDriverObserver* aObserver); void SetDocument(nsIDocument* aDocument); @@ -96,6 +109,11 @@ public: mWorkerScript = aWorkerScirpt; } + // FetchSignal::Follower + + void + Aborted() override; + private: nsCOMPtr mPrincipal; nsCOMPtr mLoadGroup; @@ -104,6 +122,7 @@ private: nsCOMPtr mPipeOutputStream; RefPtr mObserver; nsCOMPtr mDocument; + nsCOMPtr mChannel; nsAutoPtr mSRIDataVerifier; SRIMetadata mSRIMetadata; nsCString mWorkerScript; diff --git a/dom/fetch/FetchSignal.cpp b/dom/fetch/FetchSignal.cpp index 1924263e8..07ad6b53d 100644 --- a/dom/fetch/FetchSignal.cpp +++ b/dom/fetch/FetchSignal.cpp @@ -37,6 +37,10 @@ FetchSignal::FetchSignal(FetchController* aController, , mAborted(aAborted) {} +FetchSignal::FetchSignal(bool aAborted) + : mAborted(aAborted) +{} + JSObject* FetchSignal::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { @@ -95,6 +99,10 @@ FetchSignal::CanAcceptFollower(FetchSignal::Follower* aFollower) const { MOZ_DIAGNOSTIC_ASSERT(aFollower); + if (!mController) { + return true; + } + if (aFollower == mController) { return false; } diff --git a/dom/fetch/FetchSignal.h b/dom/fetch/FetchSignal.h index 5d2f13c68..4970f03de 100644 --- a/dom/fetch/FetchSignal.h +++ b/dom/fetch/FetchSignal.h @@ -40,6 +40,7 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FetchSignal, DOMEventTargetHelper) FetchSignal(FetchController* aController, bool aAborted); + explicit FetchSignal(bool aAborted); JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; diff --git a/dom/tests/mochitest/fetch/file_fetch_controller.html b/dom/tests/mochitest/fetch/file_fetch_controller.html index 791d21b2b..026ff16a8 100644 --- a/dom/tests/mochitest/fetch/file_fetch_controller.html +++ b/dom/tests/mochitest/fetch/file_fetch_controller.html @@ -84,13 +84,94 @@ function testAbortEvent() { fc.abort(); } +function testAbortedFetch() { + var fc = new FetchController(); + fc.abort(); + + fetch('data:,foo', { signal: fc.signal }).then(() => { + ok(false, "Fetch should not return a resolved promise"); + }, e => { + is(e.name, "AbortError", "We have an abort error"); + }).then(next); +} + +function testFetchAndAbort() { + var fc = new FetchController(); + + var p = fetch('data:,foo', { signal: fc.signal }); + fc.abort(); + + p.then(() => { + ok(false, "Fetch should not return a resolved promise"); + }, e => { + is(e.name, "AbortError", "We have an abort error"); + }).then(next); +} + +function testWorkerAbortedFetch() { + function worker() { + var fc = new FetchController(); + fc.abort(); + + fetch('data:,foo', { signal: fc.signal }).then(() => { + postMessage(false); + }, e => { + postMessage(e.name == "AbortError"); + }); + } + + var str = worker.toString(); + var content = str.substring(0, str.length - 1).split('\n').splice(1).join(' '); + var url = URL.createObjectURL(new Blob([content], { type: "application/javascript" })); + var w = new Worker(url); + w.onmessage = function(e) { + ok(e.data, "Abort + Fetch works in workers"); + next(); + } +} + +function testWorkerFetchAndAbort() { + function worker() { + var fc = new FetchController(); + + var p = fetch('data:,foo', { signal: fc.signal }); + fc.abort(); + + p.then(() => { + postMessage(false); + }, e => { + postMessage(e.name == "AbortError"); + }); + } + + var str = worker.toString(); + var content = str.substring(0, str.length - 1).split('\n').splice(1).join(' '); + var url = URL.createObjectURL(new Blob([content], { type: "application/javascript" })); + var w = new Worker(url); + w.onmessage = function(e) { + ok(e.data, "Abort + Fetch works in workers"); + next(); + } +} + var steps = [ + // Simple stuff testWebIDL, testUpdateData, + + // Following algorithm testFollowingOurself, testFollowingOther, testFollowingLoop, + + // Event propagation testAbortEvent, + + // fetch + signaling + testAbortedFetch, + testFetchAndAbort, + testWorkerAbortedFetch, + testWorkerFetchAndAbort, ]; function next() { -- cgit v1.2.3 From ec792e2ac7614b8e4281f496d288f20441b837bf Mon Sep 17 00:00:00 2001 From: Moonchild Date: Thu, 11 Jun 2020 08:22:04 +0000 Subject: Issue #1587 - Part 4: Implement FetchObserver --- dom/base/nsGkAtomList.h | 2 + dom/fetch/FetchObserver.cpp | 66 ++++++++++++++++++++++ dom/fetch/FetchObserver.h | 46 +++++++++++++++ dom/fetch/moz.build | 2 + dom/tests/mochitest/fetch/file_fetch_observer.html | 33 +++++++++++ dom/tests/mochitest/fetch/mochitest.ini | 2 + dom/tests/mochitest/fetch/test_fetch_observer.html | 40 +++++++++++++ dom/webidl/FetchObserver.webidl | 27 +++++++++ dom/webidl/Request.webidl | 3 + dom/webidl/moz.build | 1 + dom/workers/WorkerPrefs.h | 1 + 11 files changed, 223 insertions(+) create mode 100644 dom/fetch/FetchObserver.cpp create mode 100644 dom/fetch/FetchObserver.h create mode 100644 dom/tests/mochitest/fetch/file_fetch_observer.html create mode 100644 dom/tests/mochitest/fetch/test_fetch_observer.html create mode 100644 dom/webidl/FetchObserver.webidl (limited to 'dom') diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index af3077604..f4d2974cd 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -909,7 +909,9 @@ GK_ATOM(onreadystatechange, "onreadystatechange") GK_ATOM(onreceived, "onreceived") GK_ATOM(onremoteheld, "onremoteheld") GK_ATOM(onremoteresumed, "onremoteresumed") +GK_ATOM(onrequestprogress, "onrequestprogress") GK_ATOM(onresourcetimingbufferfull, "onresourcetimingbufferfull") +GK_ATOM(onresponseprogress, "onresponseprogress") GK_ATOM(onretrieving, "onretrieving") GK_ATOM(onRequest, "onRequest") GK_ATOM(onrequestmediaplaystatus, "onrequestmediaplaystatus") diff --git a/dom/fetch/FetchObserver.cpp b/dom/fetch/FetchObserver.cpp new file mode 100644 index 000000000..bc8c6fc2b --- /dev/null +++ b/dom/fetch/FetchObserver.cpp @@ -0,0 +1,66 @@ +/* -*- 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/. */ + +#include "FetchObserver.h" +#include "WorkerPrivate.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_CLASS(FetchObserver) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FetchObserver, + DOMEventTargetHelper) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FetchObserver, + DOMEventTargetHelper) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FetchObserver) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(FetchObserver, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(FetchObserver, DOMEventTargetHelper) + +/* static */ bool +FetchObserver::IsEnabled(JSContext* aCx, JSObject* aGlobal) +{ + if (NS_IsMainThread()) { + return Preferences::GetBool("dom.fetchObserver.enabled", false); + } + + using namespace workers; + + // Otherwise, check the pref via the WorkerPrivate + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); + if (!workerPrivate) { + return false; + } + + return workerPrivate->FetchObserverEnabled(); +} + +FetchObserver::FetchObserver(nsIGlobalObject* aGlobal, + FetchState aState) + : DOMEventTargetHelper(aGlobal) + , mState(aState) +{} + +JSObject* +FetchObserver::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return FetchObserverBinding::Wrap(aCx, this, aGivenProto); +} + +FetchState +FetchObserver::State() const +{ + return mState; +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/fetch/FetchObserver.h b/dom/fetch/FetchObserver.h new file mode 100644 index 000000000..81f8e7b09 --- /dev/null +++ b/dom/fetch/FetchObserver.h @@ -0,0 +1,46 @@ +/* -*- 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_FetchObserver_h +#define mozilla_dom_FetchObserver_h + +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/FetchObserverBinding.h" + +namespace mozilla { +namespace dom { + +class FetchObserver final : public DOMEventTargetHelper +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FetchObserver, DOMEventTargetHelper) + + static bool + IsEnabled(JSContext* aCx, JSObject* aGlobal); + + FetchObserver(nsIGlobalObject* aGlobal, FetchState aState); + + JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + FetchState + State() const; + + IMPL_EVENT_HANDLER(statechange); + IMPL_EVENT_HANDLER(requestprogress); + IMPL_EVENT_HANDLER(responseprogress); + +private: + ~FetchObserver() = default; + + FetchState mState; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_FetchObserver_h diff --git a/dom/fetch/moz.build b/dom/fetch/moz.build index 757f857f2..82fd99173 100644 --- a/dom/fetch/moz.build +++ b/dom/fetch/moz.build @@ -10,6 +10,7 @@ EXPORTS.mozilla.dom += [ 'FetchController.h', 'FetchDriver.h', 'FetchIPCTypes.h', + 'FetchObserver.h', 'FetchSignal.h', 'FetchUtil.h', 'Headers.h', @@ -31,6 +32,7 @@ SOURCES += [ 'ChannelInfo.cpp', 'FetchController.cpp', 'FetchDriver.cpp', + 'FetchObserver.cpp', 'FetchSignal.cpp', 'FetchUtil.cpp', 'Headers.cpp', diff --git a/dom/tests/mochitest/fetch/file_fetch_observer.html b/dom/tests/mochitest/fetch/file_fetch_observer.html new file mode 100644 index 000000000..97af584ec --- /dev/null +++ b/dom/tests/mochitest/fetch/file_fetch_observer.html @@ -0,0 +1,33 @@ + diff --git a/dom/tests/mochitest/fetch/mochitest.ini b/dom/tests/mochitest/fetch/mochitest.ini index 6dba08f98..60fa454f4 100644 --- a/dom/tests/mochitest/fetch/mochitest.ini +++ b/dom/tests/mochitest/fetch/mochitest.ini @@ -5,6 +5,7 @@ support-files = test_fetch_basic.js test_fetch_basic_http.js test_fetch_cors.js + file_fetch_observer.html test_formdataparsing.js test_headers_common.js test_request.js @@ -49,6 +50,7 @@ skip-if = toolkit == 'android' # Bug 1210282 skip-if = toolkit == 'android' # Bug 1210282 [test_fetch_cors_sw_empty_reroute.html] skip-if = toolkit == 'android' # Bug 1210282 +[test_fetch_observer.html] [test_formdataparsing.html] [test_formdataparsing_sw_reroute.html] [test_request.html] diff --git a/dom/tests/mochitest/fetch/test_fetch_observer.html b/dom/tests/mochitest/fetch/test_fetch_observer.html new file mode 100644 index 000000000..2b6c0362d --- /dev/null +++ b/dom/tests/mochitest/fetch/test_fetch_observer.html @@ -0,0 +1,40 @@ + + + + + Test FetchObserver + + + + + + + + diff --git a/dom/webidl/FetchObserver.webidl b/dom/webidl/FetchObserver.webidl new file mode 100644 index 000000000..eecd67e66 --- /dev/null +++ b/dom/webidl/FetchObserver.webidl @@ -0,0 +1,27 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +callback interface ObserverCallback { + void handleEvent(FetchObserver observer); +}; + +enum FetchState { + // Pending states + "requesting", "responding", + // Final states + "aborted", "errored", "complete" +}; + +[Exposed=(Window,Worker), + Func="FetchObserver::IsEnabled"] +interface FetchObserver : EventTarget { + readonly attribute FetchState state; + + // Events + attribute EventHandler onstatechange; + attribute EventHandler onrequestprogress; + attribute EventHandler onresponseprogress; +}; diff --git a/dom/webidl/Request.webidl b/dom/webidl/Request.webidl index 00497456a..8c6e33da3 100644 --- a/dom/webidl/Request.webidl +++ b/dom/webidl/Request.webidl @@ -50,6 +50,9 @@ dictionary RequestInit { [Func="FetchController::IsEnabled"] FetchSignal signal; + + [Func="FetchObserver::IsEnabled"] + ObserverCallback observe; }; // Gecko currently does not ship RequestContext, so please don't use it in IDL diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index e21cd7530..22075a443 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -143,6 +143,7 @@ WEBIDL_FILES = [ 'Fetch.webidl', 'FetchController.webidl', 'FetchEvent.webidl', + 'FetchObserver.webidl', 'FetchSignal.webidl', 'File.webidl', 'FileList.webidl', diff --git a/dom/workers/WorkerPrefs.h b/dom/workers/WorkerPrefs.h index d3e018b62..374f41ecf 100644 --- a/dom/workers/WorkerPrefs.h +++ b/dom/workers/WorkerPrefs.h @@ -40,6 +40,7 @@ WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTC WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED) WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK) WORKER_SIMPLE_PREF("dom.fetchController.enabled", FetchControllerEnabled, FETCHCONTROLLER_ENABLED) +WORKER_SIMPLE_PREF("dom.fetchObserver.enabled", FetchObserverEnabled, FETCHOBSERVER_ENABLED) WORKER_PREF("dom.workers.latestJSVersion", JSVersionChanged) WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) WORKER_PREF("general.appname.override", AppNameOverrideChanged) -- cgit v1.2.3 From fdc11c731c0d0176c4522f1eb8b1d390d6505c6d Mon Sep 17 00:00:00 2001 From: Moonchild Date: Thu, 11 Jun 2020 08:51:07 +0000 Subject: Issue #1587 - Part 5: Hook FetchObserver up to the Fetch API --- dom/fetch/Fetch.cpp | 170 ++++++++++++++++----- dom/fetch/FetchDriver.cpp | 37 +++++ dom/fetch/FetchDriver.h | 2 + dom/fetch/FetchObserver.cpp | 57 ++++++- dom/fetch/FetchObserver.h | 10 +- .../mochitest/fetch/file_fetch_controller.html | 40 +---- dom/tests/mochitest/fetch/file_fetch_observer.html | 115 +++++++++++++- dom/tests/mochitest/fetch/mochitest.ini | 2 + dom/tests/mochitest/fetch/slow.sjs | 11 ++ dom/tests/mochitest/fetch/test_fetch_observer.html | 3 +- .../mochitest/fetch/worker_fetch_controller.js | 27 ++++ 11 files changed, 399 insertions(+), 75 deletions(-) create mode 100644 dom/tests/mochitest/fetch/slow.sjs create mode 100644 dom/tests/mochitest/fetch/worker_fetch_controller.js (limited to 'dom') diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index 11e93205c..04aa7fd91 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -39,6 +39,7 @@ #include "mozilla/dom/URLSearchParams.h" #include "mozilla/dom/workers/ServiceWorkerManager.h" +#include "FetchObserver.h" #include "InternalRequest.h" #include "InternalResponse.h" @@ -126,18 +127,20 @@ private: class WorkerFetchResolver final : public FetchDriverObserver { friend class MainThreadFetchRunnable; + friend class WorkerDataAvailableRunnable; friend class WorkerFetchResponseEndBase; friend class WorkerFetchResponseEndRunnable; friend class WorkerFetchResponseRunnable; RefPtr mPromiseProxy; RefPtr mSignalProxy; + RefPtr mFetchObserver; public: // Returns null if worker is shutting down. static already_AddRefed Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise, - FetchSignal* aSignal) + FetchSignal* aSignal, FetchObserver* aObserver) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); @@ -152,7 +155,8 @@ public: signalProxy = new FetchSignalProxy(aSignal); } - RefPtr r = new WorkerFetchResolver(proxy, signalProxy); + RefPtr r = + new WorkerFetchResolver(proxy, signalProxy, aObserver); return r.forget(); } @@ -174,15 +178,19 @@ public: void OnResponseEnd(FetchDriverObserver::EndReason eReason) override; + void + OnDataAvailable() override; + private: WorkerFetchResolver(PromiseWorkerProxy* aProxy, - FetchSignalProxy* aSignalProxy) + FetchSignalProxy* aSignalProxy, + FetchObserver* aObserver) : mPromiseProxy(aProxy) , mSignalProxy(aSignalProxy) + , mFetchObserver(aObserver) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(mPromiseProxy); - } ~WorkerFetchResolver() @@ -196,12 +204,16 @@ class MainThreadFetchResolver final : public FetchDriverObserver { RefPtr mPromise; RefPtr mResponse; + RefPtr mFetchObserver; nsCOMPtr mDocument; NS_DECL_OWNINGTHREAD public: - explicit MainThreadFetchResolver(Promise* aPromise); + MainThreadFetchResolver(Promise* aPromise, FetchObserver* aObserver) + : mPromise(aPromise) + , mFetchObserver(aObserver) + {} void OnResponseAvailableInternal(InternalResponse* aResponse) override; @@ -217,9 +229,14 @@ public: mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); } + mFetchObserver = nullptr; + FlushConsoleReport(); } + void + OnDataAvailable() override; + private: ~MainThreadFetchResolver(); @@ -318,6 +335,12 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, // Let's FetchDriver to deal with an already aborted signal. } + RefPtr observer; + if (aInit.mObserve.WasPassed()) { + observer = new FetchObserver(aGlobal, signal); + aInit.mObserve.Value().HandleEvent(*observer); + } + if (NS_IsMainThread()) { nsCOMPtr window = do_QueryInterface(aGlobal); nsCOMPtr doc; @@ -344,7 +367,8 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, } } - RefPtr resolver = new MainThreadFetchResolver(p); + RefPtr resolver = + new MainThreadFetchResolver(p, observer); RefPtr fetch = new FetchDriver(r, principal, loadGroup); fetch->SetDocument(doc); resolver->SetDocument(doc); @@ -360,7 +384,8 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, r->SetSkipServiceWorker(); } - RefPtr resolver = WorkerFetchResolver::Create(worker, p, signal); + RefPtr resolver = + WorkerFetchResolver::Create(worker, p, signal, observer); if (!resolver) { NS_WARNING("Could not add WorkerFetchResolver workerHolder to worker"); aRv.Throw(NS_ERROR_DOM_ABORT_ERR); @@ -374,11 +399,6 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, return p.forget(); } -MainThreadFetchResolver::MainThreadFetchResolver(Promise* aPromise) - : mPromise(aPromise) -{ -} - void MainThreadFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse) { @@ -386,16 +406,39 @@ MainThreadFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse AssertIsOnMainThread(); if (aResponse->Type() != ResponseType::Error) { + if (mFetchObserver) { + mFetchObserver->SetState(FetchState::Complete); + } + nsCOMPtr go = mPromise->GetParentObject(); mResponse = new Response(go, aResponse); mPromise->MaybeResolve(mResponse); } else { + if (mFetchObserver) { + mFetchObserver->SetState(FetchState::Errored); + } + ErrorResult result; result.ThrowTypeError(); mPromise->MaybeReject(result); } } +void +MainThreadFetchResolver::OnDataAvailable() +{ + NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); + AssertIsOnMainThread(); + + if (!mFetchObserver) { + return; + } + + if (mFetchObserver->State() == FetchState::Requesting) { + mFetchObserver->SetState(FetchState::Responding); + } +} + MainThreadFetchResolver::~MainThreadFetchResolver() { NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); @@ -426,10 +469,18 @@ public: RefPtr promise = mResolver->mPromiseProxy->WorkerPromise(); if (mInternalResponse->Type() != ResponseType::Error) { + if (mResolver->mFetchObserver) { + mResolver->mFetchObserver->SetState(FetchState::Complete); + } + RefPtr global = aWorkerPrivate->GlobalScope(); RefPtr response = new Response(global, mInternalResponse); promise->MaybeResolve(response); } else { + if (mResolver->mFetchObserver) { + mResolver->mFetchObserver->SetState(FetchState::Errored); + } + ErrorResult result; result.ThrowTypeError(); promise->MaybeReject(result); @@ -438,18 +489,42 @@ public: } }; +class WorkerDataAvailableRunnable final : public MainThreadWorkerRunnable +{ + RefPtr mResolver; +public: + WorkerDataAvailableRunnable(WorkerPrivate* aWorkerPrivate, + WorkerFetchResolver* aResolver) + : MainThreadWorkerRunnable(aWorkerPrivate) + , mResolver(aResolver) + { + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + if (mResolver->mFetchObserver && + mResolver->mFetchObserver->State() == FetchState::Requesting) { + mResolver->mFetchObserver->SetState(FetchState::Responding); + } + + return true; + } +}; + class WorkerFetchResponseEndBase { - RefPtr mPromiseProxy; - RefPtr mSignalProxy; +protected: + RefPtr mResolver; public: - WorkerFetchResponseEndBase(PromiseWorkerProxy* aPromiseProxy, - FetchSignalProxy* aSignalProxy) - : mPromiseProxy(aPromiseProxy) - , mSignalProxy(aSignalProxy) + explicit WorkerFetchResponseEndBase(WorkerFetchResolver* aResolver) + : mResolver(aResolver) { - MOZ_ASSERT(mPromiseProxy); + MOZ_ASSERT(aResolver); } void @@ -458,14 +533,13 @@ public: MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - RefPtr promise = mPromiseProxy->WorkerPromise(); - promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + mResolver->mPromiseProxy->CleanUp(); - mPromiseProxy->CleanUp(); + mResolver->mFetchObserver = nullptr; - if (mSignalProxy) { - mSignalProxy->Shutdown(); - mSignalProxy = nullptr; + if (mResolver->mSignalProxy) { + mResolver->mSignalProxy->Shutdown(); + mResolver->mSignalProxy = nullptr; } } }; @@ -473,17 +547,26 @@ public: class WorkerFetchResponseEndRunnable final : public MainThreadWorkerRunnable , public WorkerFetchResponseEndBase { + FetchDriverObserver::EndReason mReason; + public: - WorkerFetchResponseEndRunnable(PromiseWorkerProxy* aPromiseProxy, - FetchSignalProxy* aSignalProxy) - : MainThreadWorkerRunnable(aPromiseProxy->GetWorkerPrivate()) - , WorkerFetchResponseEndBase(aPromiseProxy, aSignalProxy) + WorkerFetchResponseEndRunnable(WorkerPrivate* aWorkerPrivate, + WorkerFetchResolver* aResolver, + FetchDriverObserver::EndReason aReason) + : MainThreadWorkerRunnable(aWorkerPrivate) + , WorkerFetchResponseEndBase(aResolver) + , mReason(aReason) { } bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { + if (mReason == FetchDriverObserver::eAborted) { + RefPtr promise = mResolver->mPromiseProxy->WorkerPromise(); + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + } + WorkerRunInternal(aWorkerPrivate); return true; } @@ -502,10 +585,10 @@ class WorkerFetchResponseEndControlRunnable final : public MainThreadWorkerContr , public WorkerFetchResponseEndBase { public: - WorkerFetchResponseEndControlRunnable(PromiseWorkerProxy* aPromiseProxy, - FetchSignalProxy* aSignalProxy) - : MainThreadWorkerControlRunnable(aPromiseProxy->GetWorkerPrivate()) - , WorkerFetchResponseEndBase(aPromiseProxy, aSignalProxy) + WorkerFetchResponseEndControlRunnable(WorkerPrivate* aWorkerPrivate, + WorkerFetchResolver* aResolver) + : MainThreadWorkerControlRunnable(aWorkerPrivate) + , WorkerFetchResponseEndBase(aResolver) { } @@ -538,6 +621,21 @@ WorkerFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse) } } +void +WorkerFetchResolver::OnDataAvailable() +{ + AssertIsOnMainThread(); + + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { + return; + } + + RefPtr r = + new WorkerDataAvailableRunnable(mPromiseProxy->GetWorkerPrivate(), this); + Unused << r->Dispatch(); +} + void WorkerFetchResolver::OnResponseEnd(FetchDriverObserver::EndReason aReason) { @@ -550,11 +648,13 @@ WorkerFetchResolver::OnResponseEnd(FetchDriverObserver::EndReason aReason) FlushConsoleReport(); RefPtr r = - new WorkerFetchResponseEndRunnable(mPromiseProxy, mSignalProxy); + new WorkerFetchResponseEndRunnable(mPromiseProxy->GetWorkerPrivate(), + this, aReason); if (!r->Dispatch()) { RefPtr cr = - new WorkerFetchResponseEndControlRunnable(mPromiseProxy, mSignalProxy); + new WorkerFetchResponseEndControlRunnable(mPromiseProxy->GetWorkerPrivate(), + this); // This can fail if the worker thread is canceled or killed causing // the PromiseWorkerProxy to give up its WorkerHolder immediately, // allowing the worker thread to become Dead. diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 448ec64cd..e8d726ce1 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -667,6 +667,31 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, return NS_OK; } +namespace { + +// Runnable to call the observer OnDataAvailable on the main-thread. +class DataAvailableRunnable final : public Runnable +{ + RefPtr mObserver; + +public: + explicit DataAvailableRunnable(FetchDriverObserver* aObserver) + : mObserver(aObserver) + { + MOZ_ASSERT(aObserver); + } + + NS_IMETHOD + Run() override + { + mObserver->OnDataAvailable(); + mObserver = nullptr; + return NS_OK; + } +}; + +} // anonymous namespace + NS_IMETHODIMP FetchDriver::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, @@ -678,6 +703,18 @@ FetchDriver::OnDataAvailable(nsIRequest* aRequest, // called between OnStartRequest and OnStopRequest, so we don't need to worry // about races. + if (mObserver) { + if (NS_IsMainThread()) { + mObserver->OnDataAvailable(); + } else { + RefPtr runnable = new DataAvailableRunnable(mObserver); + nsresult rv = NS_DispatchToMainThread(runnable); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + } + uint32_t aRead; MOZ_ASSERT(mResponse); MOZ_ASSERT(mPipeOutputStream); diff --git a/dom/fetch/FetchDriver.h b/dom/fetch/FetchDriver.h index 0ca9a34ee..e942aa242 100644 --- a/dom/fetch/FetchDriver.h +++ b/dom/fetch/FetchDriver.h @@ -67,6 +67,8 @@ public: virtual void FlushConsoleReport() = 0; + virtual void OnDataAvailable() = 0; + protected: virtual ~FetchDriverObserver() { }; diff --git a/dom/fetch/FetchObserver.cpp b/dom/fetch/FetchObserver.cpp index bc8c6fc2b..982f0ad49 100644 --- a/dom/fetch/FetchObserver.cpp +++ b/dom/fetch/FetchObserver.cpp @@ -6,6 +6,7 @@ #include "FetchObserver.h" #include "WorkerPrivate.h" +#include "mozilla/dom/Event.h" namespace mozilla { namespace dom { @@ -45,10 +46,14 @@ FetchObserver::IsEnabled(JSContext* aCx, JSObject* aGlobal) } FetchObserver::FetchObserver(nsIGlobalObject* aGlobal, - FetchState aState) + FetchSignal* aSignal) : DOMEventTargetHelper(aGlobal) - , mState(aState) -{} + , mState(FetchState::Requesting) +{ + if (aSignal) { + Follow(aSignal); + } +} JSObject* FetchObserver::WrapObject(JSContext* aCx, JS::Handle aGivenProto) @@ -62,5 +67,51 @@ FetchObserver::State() const return mState; } +void +FetchObserver::Aborted() +{ + SetState(FetchState::Aborted); +} + +void +FetchObserver::SetState(FetchState aState) +{ + MOZ_ASSERT(mState < aState); + + if (mState == FetchState::Aborted || + mState == FetchState::Errored || + mState == FetchState::Complete) { + // We are already in a final state. + return; + } + + // We cannot pass from Requesting to Complete directly. + if (mState == FetchState::Requesting && + aState == FetchState::Complete) { + SetState(FetchState::Responding); + } + + mState = aState; + + if (mState == FetchState::Aborted || + mState == FetchState::Errored || + mState == FetchState::Complete) { + Unfollow(); + } + + EventInit init; + init.mBubbles = false; + init.mCancelable = false; + + // TODO which kind of event should we dispatch here? + + RefPtr event = + Event::Constructor(this, NS_LITERAL_STRING("statechange"), init); + event->SetTrusted(true); + + bool dummy; + DispatchEvent(event, &dummy); +} + } // dom namespace } // mozilla namespace diff --git a/dom/fetch/FetchObserver.h b/dom/fetch/FetchObserver.h index 81f8e7b09..45adf2ba1 100644 --- a/dom/fetch/FetchObserver.h +++ b/dom/fetch/FetchObserver.h @@ -9,11 +9,13 @@ #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/dom/FetchObserverBinding.h" +#include "mozilla/dom/FetchSignal.h" namespace mozilla { namespace dom { class FetchObserver final : public DOMEventTargetHelper + , public FetchSignal::Follower { public: NS_DECL_ISUPPORTS_INHERITED @@ -22,7 +24,7 @@ public: static bool IsEnabled(JSContext* aCx, JSObject* aGlobal); - FetchObserver(nsIGlobalObject* aGlobal, FetchState aState); + FetchObserver(nsIGlobalObject* aGlobal, FetchSignal* aSignal); JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; @@ -34,6 +36,12 @@ public: IMPL_EVENT_HANDLER(requestprogress); IMPL_EVENT_HANDLER(responseprogress); + void + Aborted() override; + + void + SetState(FetchState aState); + private: ~FetchObserver() = default; diff --git a/dom/tests/mochitest/fetch/file_fetch_controller.html b/dom/tests/mochitest/fetch/file_fetch_controller.html index 026ff16a8..e4137aac9 100644 --- a/dom/tests/mochitest/fetch/file_fetch_controller.html +++ b/dom/tests/mochitest/fetch/file_fetch_controller.html @@ -88,7 +88,7 @@ function testAbortedFetch() { var fc = new FetchController(); fc.abort(); - fetch('data:,foo', { signal: fc.signal }).then(() => { + fetch('slow.sjs', { signal: fc.signal }).then(() => { ok(false, "Fetch should not return a resolved promise"); }, e => { is(e.name, "AbortError", "We have an abort error"); @@ -98,7 +98,7 @@ function testAbortedFetch() { function testFetchAndAbort() { var fc = new FetchController(); - var p = fetch('data:,foo', { signal: fc.signal }); + var p = fetch('slow.sjs', { signal: fc.signal }); fc.abort(); p.then(() => { @@ -109,49 +109,21 @@ function testFetchAndAbort() { } function testWorkerAbortedFetch() { - function worker() { - var fc = new FetchController(); - fc.abort(); - - fetch('data:,foo', { signal: fc.signal }).then(() => { - postMessage(false); - }, e => { - postMessage(e.name == "AbortError"); - }); - } - - var str = worker.toString(); - var content = str.substring(0, str.length - 1).split('\n').splice(1).join(' '); - var url = URL.createObjectURL(new Blob([content], { type: "application/javascript" })); - var w = new Worker(url); + var w = new Worker('worker_fetch_controller.js'); w.onmessage = function(e) { ok(e.data, "Abort + Fetch works in workers"); next(); } + w.postMessage('testWorkerAbortedFetch'); } function testWorkerFetchAndAbort() { - function worker() { - var fc = new FetchController(); - - var p = fetch('data:,foo', { signal: fc.signal }); - fc.abort(); - - p.then(() => { - postMessage(false); - }, e => { - postMessage(e.name == "AbortError"); - }); - } - - var str = worker.toString(); - var content = str.substring(0, str.length - 1).split('\n').splice(1).join(' '); - var url = URL.createObjectURL(new Blob([content], { type: "application/javascript" })); - var w = new Worker(url); + var w = new Worker('worker_fetch_controller.js'); w.onmessage = function(e) { ok(e.data, "Abort + Fetch works in workers"); next(); } + w.postMessage('testWorkerFetchAndAbort'); } var steps = [ diff --git a/dom/tests/mochitest/fetch/file_fetch_observer.html b/dom/tests/mochitest/fetch/file_fetch_observer.html index 97af584ec..a172a18dc 100644 --- a/dom/tests/mochitest/fetch/file_fetch_observer.html +++ b/dom/tests/mochitest/fetch/file_fetch_observer.html @@ -10,12 +10,125 @@ function is(a, b, msg) { function testObserver() { ok("FetchObserver" in self, "We have a FetchObserver prototype"); - fetch('data:,foo', { observe: o => { + fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { observe: o => { + ok(!!o, "We have an observer"); + ok(o instanceof FetchObserver, "The correct object has been passed"); + is(o.state, "requesting", "By default the state is requesting"); + next(); }}); } +function testObserveAbort() { + var fc = new FetchController(); + + fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { + signal: fc.signal, + observe: o => { + o.onstatechange = () => { + ok(true, "StateChange event dispatched"); + if (o.state == "aborted") { + ok(true, "Aborted!"); + next(); + } + } + fc.abort(); + } + }); +} + +function testObserveComplete() { + var fc = new FetchController(); + + fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { + signal: fc.signal, + observe: o => { + o.onstatechange = () => { + ok(true, "StateChange event dispatched"); + if (o.state == "complete") { + ok(true, "Operation completed"); + next(); + } + } + } + }); +} + +function testObserveErrored() { + var fc = new FetchController(); + + fetch('foo: bar', { + signal: fc.signal, + observe: o => { + o.onstatechange = () => { + ok(true, "StateChange event dispatched"); + if (o.state == "errored") { + ok(true, "Operation completed"); + next(); + } + } + } + }); +} + +function testObserveResponding() { + var fc = new FetchController(); + + fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { + signal: fc.signal, + observe: o => { + o.onstatechange = () => { + if (o.state == "responding") { + ok(true, "We have responding events"); + next(); + } + } + } + }); +} + +function workify(worker) { + function methods() { + function ok(a, msg) { + postMessage( { type: 'check', state: !!a, message: msg }); + }; + function is(a, b, msg) { + postMessage( { type: 'check', state: a === b, message: msg }); + }; + function next() { + postMessage( { type: 'finish' }); + }; + } + + var str = methods.toString(); + var methodsContent = str.substring(0, str.length - 1).split('\n').splice(1).join('\n'); + + str = worker.toString(); + var workerContent = str.substring(0, str.length - 1).split('\n').splice(1).join('\n'); + + var content = methodsContent + workerContent; + var url = URL.createObjectURL(new Blob([content], { type: "application/javascript" })); + var w = new Worker(url); + w.onmessage = e => { + if (e.data.type == 'check') { + ok(e.data.state, "WORKER: " + e.data.message); + } else if (e.data.type == 'finish') { + next(); + } else { + ok(false, "Something went wrong"); + } + } +} + var steps = [ testObserver, + testObserveAbort, + function() { workify(testObserveAbort); }, + testObserveComplete, + function() { workify(testObserveComplete); }, + testObserveErrored, + function() { workify(testObserveErrored); }, + testObserveResponding, + function() { workify(testObserveResponding); }, ]; function next() { diff --git a/dom/tests/mochitest/fetch/mochitest.ini b/dom/tests/mochitest/fetch/mochitest.ini index 60fa454f4..7493ede50 100644 --- a/dom/tests/mochitest/fetch/mochitest.ini +++ b/dom/tests/mochitest/fetch/mochitest.ini @@ -17,11 +17,13 @@ support-files = reroute.html reroute.js reroute.js^headers^ + slow.sjs sw_reroute.js empty.js empty.js^headers^ worker_temporaryFileBlob.js common_temporaryFileBlob.js + worker_fetch_controller.js !/dom/xhr/tests/file_XHR_binary1.bin !/dom/xhr/tests/file_XHR_binary1.bin^headers^ !/dom/xhr/tests/file_XHR_binary2.bin diff --git a/dom/tests/mochitest/fetch/slow.sjs b/dom/tests/mochitest/fetch/slow.sjs new file mode 100644 index 000000000..feab0f1fc --- /dev/null +++ b/dom/tests/mochitest/fetch/slow.sjs @@ -0,0 +1,11 @@ +function handleRequest(request, response) +{ + response.processAsync(); + + timer = Components.classes["@mozilla.org/timer;1"]. + createInstance(Components.interfaces.nsITimer); + timer.init(function() { + response.write("Here the content. But slowly."); + response.finish(); + }, 1000, Components.interfaces.nsITimer.TYPE_ONE_SHOT); +} diff --git a/dom/tests/mochitest/fetch/test_fetch_observer.html b/dom/tests/mochitest/fetch/test_fetch_observer.html index 2b6c0362d..2af86977c 100644 --- a/dom/tests/mochitest/fetch/test_fetch_observer.html +++ b/dom/tests/mochitest/fetch/test_fetch_observer.html @@ -12,7 +12,8 @@ diff --git a/dom/abort/tests/mochitest.ini b/dom/abort/tests/mochitest.ini new file mode 100644 index 000000000..5ecc7048e --- /dev/null +++ b/dom/abort/tests/mochitest.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + file_fetch_controller.html + worker_fetch_controller.js + +[test_fetch_controller.html] diff --git a/dom/abort/tests/moz.build b/dom/abort/tests/moz.build new file mode 100644 index 000000000..8e5cb5d71 --- /dev/null +++ b/dom/abort/tests/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +MOCHITEST_MANIFESTS += ['mochitest.ini'] + diff --git a/dom/abort/tests/test_fetch_controller.html b/dom/abort/tests/test_fetch_controller.html new file mode 100644 index 000000000..812fb9161 --- /dev/null +++ b/dom/abort/tests/test_fetch_controller.html @@ -0,0 +1,40 @@ + + + + + Test FetchController + + + + + + + + diff --git a/dom/abort/tests/worker_fetch_controller.js b/dom/abort/tests/worker_fetch_controller.js new file mode 100644 index 000000000..6b008fea8 --- /dev/null +++ b/dom/abort/tests/worker_fetch_controller.js @@ -0,0 +1,27 @@ +function testWorkerAbortedFetch() { + var fc = new FetchController(); + fc.abort(); + + fetch('slow.sjs', { signal: fc.signal }).then(() => { + postMessage(false); + }, e => { + postMessage(e.name == "AbortError"); + }); +} + +function testWorkerFetchAndAbort() { + var fc = new FetchController(); + + var p = fetch('slow.sjs', { signal: fc.signal }); + fc.abort(); + + p.then(() => { + postMessage(false); + }, e => { + postMessage(e.name == "AbortError"); + }); +} + +onmessage = function(e) { + self[e.data](); +} diff --git a/dom/fetch/FetchController.cpp b/dom/fetch/FetchController.cpp deleted file mode 100644 index 2eb40b980..000000000 --- a/dom/fetch/FetchController.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* -*- 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/. */ - -#include "FetchController.h" -#include "FetchSignal.h" -#include "mozilla/dom/FetchControllerBinding.h" -#include "WorkerPrivate.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FetchController, mGlobal, mSignal, - mFollowingSignal) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(FetchController) -NS_IMPL_CYCLE_COLLECTING_RELEASE(FetchController) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchController) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -/* static */ bool -FetchController::IsEnabled(JSContext* aCx, JSObject* aGlobal) -{ - if (NS_IsMainThread()) { - return Preferences::GetBool("dom.fetchController.enabled", false); - } - - using namespace workers; - - // Otherwise, check the pref via the WorkerPrivate - WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); - if (!workerPrivate) { - return false; - } - - return workerPrivate->FetchControllerEnabled(); -} - -/* static */ already_AddRefed -FetchController::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) -{ - nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - RefPtr fetchController = new FetchController(global); - return fetchController.forget(); -} - -FetchController::FetchController(nsIGlobalObject* aGlobal) - : mGlobal(aGlobal) - , mAborted(false) -{} - -JSObject* -FetchController::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - return FetchControllerBinding::Wrap(aCx, this, aGivenProto); -} - -nsIGlobalObject* -FetchController::GetParentObject() const -{ - return mGlobal; -} - -FetchSignal* -FetchController::Signal() -{ - if (!mSignal) { - mSignal = new FetchSignal(this, mAborted); - } - - return mSignal; -} - -void -FetchController::Abort() -{ - if (mAborted) { - return; - } - - mAborted = true; - - if (mSignal) { - mSignal->Abort(); - } -} - -void -FetchController::Follow(FetchSignal& aSignal) -{ - FetchSignal::Follower::Follow(&aSignal); -} - -void -FetchController::Unfollow(FetchSignal& aSignal) -{ - if (mFollowingSignal != &aSignal) { - return; - } - - FetchSignal::Follower::Unfollow(); -} - -FetchSignal* -FetchController::Following() const -{ - return mFollowingSignal; -} - -void -FetchController::Aborted() -{ - Abort(); -} - -} // dom namespace -} // mozilla namespace diff --git a/dom/fetch/FetchController.h b/dom/fetch/FetchController.h deleted file mode 100644 index 7a0132dca..000000000 --- a/dom/fetch/FetchController.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -*- 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_FetchController_h -#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" -#include "nsIGlobalObject.h" - -namespace mozilla { -namespace dom { - -class FetchController final : public nsISupports - , public nsWrapperCache - , public FetchSignal::Follower -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FetchController) - - static bool - IsEnabled(JSContext* aCx, JSObject* aGlobal); - - static already_AddRefed - Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); - - explicit FetchController(nsIGlobalObject* aGlobal); - - JSObject* - WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - - nsIGlobalObject* - GetParentObject() const; - - FetchSignal* - Signal(); - - void - Abort(); - - void - Follow(FetchSignal& aSignal); - - void - Unfollow(FetchSignal& aSignal); - - FetchSignal* - Following() const; - - // FetchSignal::Follower - - void Aborted() override; - -private: - ~FetchController() = default; - - nsCOMPtr mGlobal; - RefPtr mSignal; - - bool mAborted; -}; - -} // dom namespace -} // mozilla namespace - -#endif // mozilla_dom_FetchController_h diff --git a/dom/fetch/FetchSignal.cpp b/dom/fetch/FetchSignal.cpp deleted file mode 100644 index 07ad6b53d..000000000 --- a/dom/fetch/FetchSignal.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* -*- 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/. */ - -#include "FetchSignal.h" -#include "FetchController.h" -#include "mozilla/dom/Event.h" -#include "mozilla/dom/FetchSignalBinding.h" - -namespace mozilla { -namespace dom { - -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_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FetchSignal, - DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mController) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FetchSignal) -NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) - -NS_IMPL_ADDREF_INHERITED(FetchSignal, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(FetchSignal, DOMEventTargetHelper) - -FetchSignal::FetchSignal(FetchController* aController, - bool aAborted) - : DOMEventTargetHelper(aController->GetParentObject()) - , mController(aController) - , mAborted(aAborted) -{} - -FetchSignal::FetchSignal(bool aAborted) - : mAborted(aAborted) -{} - -JSObject* -FetchSignal::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - return FetchSignalBinding::Wrap(aCx, this, aGivenProto); -} - -bool -FetchSignal::Aborted() const -{ - return mAborted; -} - -void -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; - - // TODO which kind of event should we dispatch here? - - RefPtr event = - Event::Constructor(this, NS_LITERAL_STRING("abort"), init); - event->SetTrusted(true); - - 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 (!mController) { - return true; - } - - 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 -} // mozilla namespace diff --git a/dom/fetch/FetchSignal.h b/dom/fetch/FetchSignal.h deleted file mode 100644 index 4970f03de..000000000 --- a/dom/fetch/FetchSignal.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- 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_FetchSignal_h -#define mozilla_dom_FetchSignal_h - -#include "mozilla/DOMEventTargetHelper.h" - -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) - - FetchSignal(FetchController* aController, bool aAborted); - explicit FetchSignal(bool aAborted); - - JSObject* - WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - - bool - Aborted() const; - - void - Abort(); - - 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; -}; - -} // dom namespace -} // mozilla namespace - -#endif // mozilla_dom_FetchSignal_h diff --git a/dom/fetch/moz.build b/dom/fetch/moz.build index 82fd99173..e2b466428 100644 --- a/dom/fetch/moz.build +++ b/dom/fetch/moz.build @@ -7,11 +7,9 @@ EXPORTS.mozilla.dom += [ 'ChannelInfo.h', 'Fetch.h', - 'FetchController.h', 'FetchDriver.h', 'FetchIPCTypes.h', 'FetchObserver.h', - 'FetchSignal.h', 'FetchUtil.h', 'Headers.h', 'InternalHeaders.h', @@ -30,10 +28,8 @@ UNIFIED_SOURCES += [ SOURCES += [ 'ChannelInfo.cpp', - 'FetchController.cpp', 'FetchDriver.cpp', 'FetchObserver.cpp', - 'FetchSignal.cpp', 'FetchUtil.cpp', 'Headers.cpp', 'InternalHeaders.cpp', diff --git a/dom/moz.build b/dom/moz.build index 89c539b4b..8a958982f 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -37,6 +37,7 @@ interfaces = [ DIRS += ['interfaces/' + i for i in interfaces] DIRS += [ + 'abort', 'animation', 'apps', 'base', diff --git a/dom/tests/mochitest/fetch/file_fetch_controller.html b/dom/tests/mochitest/fetch/file_fetch_controller.html deleted file mode 100644 index e4137aac9..000000000 --- a/dom/tests/mochitest/fetch/file_fetch_controller.html +++ /dev/null @@ -1,161 +0,0 @@ - diff --git a/dom/tests/mochitest/fetch/mochitest.ini b/dom/tests/mochitest/fetch/mochitest.ini index 7493ede50..a9447d0d9 100644 --- a/dom/tests/mochitest/fetch/mochitest.ini +++ b/dom/tests/mochitest/fetch/mochitest.ini @@ -1,7 +1,6 @@ [DEFAULT] support-files = fetch_test_framework.js - file_fetch_controller.html test_fetch_basic.js test_fetch_basic_http.js test_fetch_cors.js @@ -23,7 +22,6 @@ support-files = empty.js^headers^ worker_temporaryFileBlob.js common_temporaryFileBlob.js - worker_fetch_controller.js !/dom/xhr/tests/file_XHR_binary1.bin !/dom/xhr/tests/file_XHR_binary1.bin^headers^ !/dom/xhr/tests/file_XHR_binary2.bin @@ -45,7 +43,6 @@ support-files = [test_fetch_basic_http.html] [test_fetch_basic_http_sw_reroute.html] [test_fetch_basic_http_sw_empty_reroute.html] -[test_fetch_controller.html] [test_fetch_cors.html] skip-if = toolkit == 'android' # Bug 1210282 [test_fetch_cors_sw_reroute.html] diff --git a/dom/tests/mochitest/fetch/test_fetch_controller.html b/dom/tests/mochitest/fetch/test_fetch_controller.html deleted file mode 100644 index 812fb9161..000000000 --- a/dom/tests/mochitest/fetch/test_fetch_controller.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Test FetchController - - - - - - - - diff --git a/dom/tests/mochitest/fetch/worker_fetch_controller.js b/dom/tests/mochitest/fetch/worker_fetch_controller.js deleted file mode 100644 index 6b008fea8..000000000 --- a/dom/tests/mochitest/fetch/worker_fetch_controller.js +++ /dev/null @@ -1,27 +0,0 @@ -function testWorkerAbortedFetch() { - var fc = new FetchController(); - fc.abort(); - - fetch('slow.sjs', { signal: fc.signal }).then(() => { - postMessage(false); - }, e => { - postMessage(e.name == "AbortError"); - }); -} - -function testWorkerFetchAndAbort() { - var fc = new FetchController(); - - var p = fetch('slow.sjs', { signal: fc.signal }); - fc.abort(); - - p.then(() => { - postMessage(false); - }, e => { - postMessage(e.name == "AbortError"); - }); -} - -onmessage = function(e) { - self[e.data](); -} -- cgit v1.2.3 From e97a29a6569fac73e81cf46a0e682ac926ea3456 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Thu, 11 Jun 2020 23:20:52 +0000 Subject: Issue #1587 - Part 7: Rename FetchController to AbortController Also renames FetchSignal to AbortSignal. Includes renaming the various controlling prefs to enable. --- dom/abort/AbortController.cpp | 127 ++++++++++++++++ dom/abort/AbortController.h | 73 ++++++++++ dom/abort/AbortSignal.cpp | 151 +++++++++++++++++++ dom/abort/AbortSignal.h | 79 ++++++++++ dom/abort/FetchController.cpp | 127 ---------------- dom/abort/FetchController.h | 73 ---------- dom/abort/FetchSignal.cpp | 151 ------------------- dom/abort/FetchSignal.h | 79 ---------- dom/abort/moz.build | 8 +- dom/abort/tests/file_abort_controller.html | 161 +++++++++++++++++++++ dom/abort/tests/file_fetch_controller.html | 161 --------------------- dom/abort/tests/mochitest.ini | 4 +- dom/abort/tests/test_abort_controller.html | 40 +++++ dom/abort/tests/test_fetch_controller.html | 40 ----- dom/abort/tests/worker_abort_controller.js | 27 ++++ dom/abort/tests/worker_fetch_controller.js | 27 ---- dom/fetch/Fetch.cpp | 52 +++---- dom/fetch/FetchDriver.cpp | 2 +- dom/fetch/FetchDriver.h | 8 +- dom/fetch/FetchObserver.cpp | 2 +- dom/fetch/FetchObserver.h | 6 +- dom/fetch/Request.h | 2 +- dom/tests/mochitest/fetch/file_fetch_observer.html | 8 +- dom/webidl/AbortController.webidl | 15 ++ dom/webidl/AbortSignal.webidl | 13 ++ dom/webidl/FetchController.webidl | 15 -- dom/webidl/FetchSignal.webidl | 13 -- dom/webidl/Request.webidl | 4 +- dom/webidl/moz.build | 4 +- dom/workers/WorkerPrefs.h | 2 +- 30 files changed, 737 insertions(+), 737 deletions(-) create mode 100644 dom/abort/AbortController.cpp create mode 100644 dom/abort/AbortController.h create mode 100644 dom/abort/AbortSignal.cpp create mode 100644 dom/abort/AbortSignal.h delete mode 100644 dom/abort/FetchController.cpp delete mode 100644 dom/abort/FetchController.h delete mode 100644 dom/abort/FetchSignal.cpp delete mode 100644 dom/abort/FetchSignal.h create mode 100644 dom/abort/tests/file_abort_controller.html delete mode 100644 dom/abort/tests/file_fetch_controller.html create mode 100644 dom/abort/tests/test_abort_controller.html delete mode 100644 dom/abort/tests/test_fetch_controller.html create mode 100644 dom/abort/tests/worker_abort_controller.js delete mode 100644 dom/abort/tests/worker_fetch_controller.js create mode 100644 dom/webidl/AbortController.webidl create mode 100644 dom/webidl/AbortSignal.webidl delete mode 100644 dom/webidl/FetchController.webidl delete mode 100644 dom/webidl/FetchSignal.webidl (limited to 'dom') diff --git a/dom/abort/AbortController.cpp b/dom/abort/AbortController.cpp new file mode 100644 index 000000000..4043b7bf3 --- /dev/null +++ b/dom/abort/AbortController.cpp @@ -0,0 +1,127 @@ +/* -*- 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/. */ + +#include "AbortController.h" +#include "AbortSignal.h" +#include "mozilla/dom/AbortControllerBinding.h" +#include "WorkerPrivate.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AbortController, mGlobal, mSignal, + mFollowingSignal) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(AbortController) +NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortController) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortController) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +/* static */ bool +AbortController::IsEnabled(JSContext* aCx, JSObject* aGlobal) +{ + if (NS_IsMainThread()) { + return Preferences::GetBool("dom.abortController.enabled", false); + } + + using namespace workers; + + // Otherwise, check the pref via the WorkerPrivate + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); + if (!workerPrivate) { + return false; + } + + return workerPrivate->AbortControllerEnabled(); +} + +/* static */ already_AddRefed +AbortController::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr abortController = new AbortController(global); + return abortController.forget(); +} + +AbortController::AbortController(nsIGlobalObject* aGlobal) + : mGlobal(aGlobal) + , mAborted(false) +{} + +JSObject* +AbortController::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return AbortControllerBinding::Wrap(aCx, this, aGivenProto); +} + +nsIGlobalObject* +AbortController::GetParentObject() const +{ + return mGlobal; +} + +AbortSignal* +AbortController::Signal() +{ + if (!mSignal) { + mSignal = new AbortSignal(this, mAborted); + } + + return mSignal; +} + +void +AbortController::Abort() +{ + if (mAborted) { + return; + } + + mAborted = true; + + if (mSignal) { + mSignal->Abort(); + } +} + +void +AbortController::Follow(AbortSignal& aSignal) +{ + AbortSignal::Follower::Follow(&aSignal); +} + +void +AbortController::Unfollow(AbortSignal& aSignal) +{ + if (mFollowingSignal != &aSignal) { + return; + } + + AbortSignal::Follower::Unfollow(); +} + +AbortSignal* +AbortController::Following() const +{ + return mFollowingSignal; +} + +void +AbortController::Aborted() +{ + Abort(); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/abort/AbortController.h b/dom/abort/AbortController.h new file mode 100644 index 000000000..9ce4253c9 --- /dev/null +++ b/dom/abort/AbortController.h @@ -0,0 +1,73 @@ +/* -*- 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_AbortController_h +#define mozilla_dom_AbortController_h + +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/AbortSignal.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "mozilla/ErrorResult.h" +#include "nsIGlobalObject.h" + +namespace mozilla { +namespace dom { + +class AbortController final : public nsISupports + , public nsWrapperCache + , public AbortSignal::Follower +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbortController) + + static bool + IsEnabled(JSContext* aCx, JSObject* aGlobal); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); + + explicit AbortController(nsIGlobalObject* aGlobal); + + JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + nsIGlobalObject* + GetParentObject() const; + + AbortSignal* + Signal(); + + void + Abort(); + + void + Follow(AbortSignal& aSignal); + + void + Unfollow(AbortSignal& aSignal); + + AbortSignal* + Following() const; + + // AbortSignal::Follower + + void Aborted() override; + +private: + ~AbortController() = default; + + nsCOMPtr mGlobal; + RefPtr mSignal; + + bool mAborted; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_AbortController_h diff --git a/dom/abort/AbortSignal.cpp b/dom/abort/AbortSignal.cpp new file mode 100644 index 000000000..38b0a2492 --- /dev/null +++ b/dom/abort/AbortSignal.cpp @@ -0,0 +1,151 @@ +/* -*- 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/. */ + +#include "AbortSignal.h" +#include "AbortController.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/AbortSignalBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_CLASS(AbortSignal) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AbortSignal, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mController) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AbortSignal, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mController) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AbortSignal) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(AbortSignal, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(AbortSignal, DOMEventTargetHelper) + +AbortSignal::AbortSignal(AbortController* aController, + bool aAborted) + : DOMEventTargetHelper(aController->GetParentObject()) + , mController(aController) + , mAborted(aAborted) +{} + +AbortSignal::AbortSignal(bool aAborted) + : mAborted(aAborted) +{} + +JSObject* +AbortSignal::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return AbortSignalBinding::Wrap(aCx, this, aGivenProto); +} + +bool +AbortSignal::Aborted() const +{ + return mAborted; +} + +void +AbortSignal::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; + + // TODO which kind of event should we dispatch here? + + RefPtr event = + Event::Constructor(this, NS_LITERAL_STRING("abort"), init); + event->SetTrusted(true); + + bool dummy; + DispatchEvent(event, &dummy); +} + +void +AbortSignal::AddFollower(AbortSignal::Follower* aFollower) +{ + MOZ_DIAGNOSTIC_ASSERT(aFollower); + if (!mFollowers.Contains(aFollower)) { + mFollowers.AppendElement(aFollower); + } +} + +void +AbortSignal::RemoveFollower(AbortSignal::Follower* aFollower) +{ + MOZ_DIAGNOSTIC_ASSERT(aFollower); + mFollowers.RemoveElement(aFollower); +} + +bool +AbortSignal::CanAcceptFollower(AbortSignal::Follower* aFollower) const +{ + MOZ_DIAGNOSTIC_ASSERT(aFollower); + + if (!mController) { + return true; + } + + if (aFollower == mController) { + return false; + } + + AbortSignal* following = mController->Following(); + if (!following) { + return true; + } + + return following->CanAcceptFollower(aFollower); +} + +// AbortSignal::Follower +// ---------------------------------------------------------------------------- + +AbortSignal::Follower::~Follower() +{ + Unfollow(); +} + +void +AbortSignal::Follower::Follow(AbortSignal* aSignal) +{ + MOZ_DIAGNOSTIC_ASSERT(aSignal); + + if (!aSignal->CanAcceptFollower(this)) { + return; + } + + Unfollow(); + + mFollowingSignal = aSignal; + aSignal->AddFollower(this); +} + +void +AbortSignal::Follower::Unfollow() +{ + if (mFollowingSignal) { + mFollowingSignal->RemoveFollower(this); + mFollowingSignal = nullptr; + } +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/abort/AbortSignal.h b/dom/abort/AbortSignal.h new file mode 100644 index 000000000..c5045dbd6 --- /dev/null +++ b/dom/abort/AbortSignal.h @@ -0,0 +1,79 @@ +/* -*- 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_AbortSignal_h +#define mozilla_dom_AbortSignal_h + +#include "mozilla/DOMEventTargetHelper.h" + +namespace mozilla { +namespace dom { + +class AbortController; +class AbortSignal; + +class AbortSignal final : public DOMEventTargetHelper +{ +public: + // This class must be implemented by objects who want to follow a AbortSignal. + class Follower + { + public: + virtual void Aborted() = 0; + + protected: + virtual ~Follower(); + + void + Follow(AbortSignal* aSignal); + + void + Unfollow(); + + RefPtr mFollowingSignal; + }; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AbortSignal, DOMEventTargetHelper) + + AbortSignal(AbortController* aController, bool aAborted); + explicit AbortSignal(bool aAborted); + + JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + bool + Aborted() const; + + void + Abort(); + + IMPL_EVENT_HANDLER(abort); + + void + AddFollower(Follower* aFollower); + + void + RemoveFollower(Follower* aFollower); + + bool + CanAcceptFollower(Follower* aFollower) const; + +private: + ~AbortSignal() = default; + + RefPtr mController; + + // Raw pointers. Follower unregisters itself in the DTOR. + nsTArray mFollowers; + + bool mAborted; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_AbortSignal_h diff --git a/dom/abort/FetchController.cpp b/dom/abort/FetchController.cpp deleted file mode 100644 index 2eb40b980..000000000 --- a/dom/abort/FetchController.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* -*- 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/. */ - -#include "FetchController.h" -#include "FetchSignal.h" -#include "mozilla/dom/FetchControllerBinding.h" -#include "WorkerPrivate.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FetchController, mGlobal, mSignal, - mFollowingSignal) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(FetchController) -NS_IMPL_CYCLE_COLLECTING_RELEASE(FetchController) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchController) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -/* static */ bool -FetchController::IsEnabled(JSContext* aCx, JSObject* aGlobal) -{ - if (NS_IsMainThread()) { - return Preferences::GetBool("dom.fetchController.enabled", false); - } - - using namespace workers; - - // Otherwise, check the pref via the WorkerPrivate - WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); - if (!workerPrivate) { - return false; - } - - return workerPrivate->FetchControllerEnabled(); -} - -/* static */ already_AddRefed -FetchController::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) -{ - nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - RefPtr fetchController = new FetchController(global); - return fetchController.forget(); -} - -FetchController::FetchController(nsIGlobalObject* aGlobal) - : mGlobal(aGlobal) - , mAborted(false) -{} - -JSObject* -FetchController::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - return FetchControllerBinding::Wrap(aCx, this, aGivenProto); -} - -nsIGlobalObject* -FetchController::GetParentObject() const -{ - return mGlobal; -} - -FetchSignal* -FetchController::Signal() -{ - if (!mSignal) { - mSignal = new FetchSignal(this, mAborted); - } - - return mSignal; -} - -void -FetchController::Abort() -{ - if (mAborted) { - return; - } - - mAborted = true; - - if (mSignal) { - mSignal->Abort(); - } -} - -void -FetchController::Follow(FetchSignal& aSignal) -{ - FetchSignal::Follower::Follow(&aSignal); -} - -void -FetchController::Unfollow(FetchSignal& aSignal) -{ - if (mFollowingSignal != &aSignal) { - return; - } - - FetchSignal::Follower::Unfollow(); -} - -FetchSignal* -FetchController::Following() const -{ - return mFollowingSignal; -} - -void -FetchController::Aborted() -{ - Abort(); -} - -} // dom namespace -} // mozilla namespace diff --git a/dom/abort/FetchController.h b/dom/abort/FetchController.h deleted file mode 100644 index 7a0132dca..000000000 --- a/dom/abort/FetchController.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -*- 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_FetchController_h -#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" -#include "nsIGlobalObject.h" - -namespace mozilla { -namespace dom { - -class FetchController final : public nsISupports - , public nsWrapperCache - , public FetchSignal::Follower -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FetchController) - - static bool - IsEnabled(JSContext* aCx, JSObject* aGlobal); - - static already_AddRefed - Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); - - explicit FetchController(nsIGlobalObject* aGlobal); - - JSObject* - WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - - nsIGlobalObject* - GetParentObject() const; - - FetchSignal* - Signal(); - - void - Abort(); - - void - Follow(FetchSignal& aSignal); - - void - Unfollow(FetchSignal& aSignal); - - FetchSignal* - Following() const; - - // FetchSignal::Follower - - void Aborted() override; - -private: - ~FetchController() = default; - - nsCOMPtr mGlobal; - RefPtr mSignal; - - bool mAborted; -}; - -} // dom namespace -} // mozilla namespace - -#endif // mozilla_dom_FetchController_h diff --git a/dom/abort/FetchSignal.cpp b/dom/abort/FetchSignal.cpp deleted file mode 100644 index 07ad6b53d..000000000 --- a/dom/abort/FetchSignal.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* -*- 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/. */ - -#include "FetchSignal.h" -#include "FetchController.h" -#include "mozilla/dom/Event.h" -#include "mozilla/dom/FetchSignalBinding.h" - -namespace mozilla { -namespace dom { - -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_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FetchSignal, - DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mController) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FetchSignal) -NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) - -NS_IMPL_ADDREF_INHERITED(FetchSignal, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(FetchSignal, DOMEventTargetHelper) - -FetchSignal::FetchSignal(FetchController* aController, - bool aAborted) - : DOMEventTargetHelper(aController->GetParentObject()) - , mController(aController) - , mAborted(aAborted) -{} - -FetchSignal::FetchSignal(bool aAborted) - : mAborted(aAborted) -{} - -JSObject* -FetchSignal::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - return FetchSignalBinding::Wrap(aCx, this, aGivenProto); -} - -bool -FetchSignal::Aborted() const -{ - return mAborted; -} - -void -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; - - // TODO which kind of event should we dispatch here? - - RefPtr event = - Event::Constructor(this, NS_LITERAL_STRING("abort"), init); - event->SetTrusted(true); - - 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 (!mController) { - return true; - } - - 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 -} // mozilla namespace diff --git a/dom/abort/FetchSignal.h b/dom/abort/FetchSignal.h deleted file mode 100644 index 4970f03de..000000000 --- a/dom/abort/FetchSignal.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- 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_FetchSignal_h -#define mozilla_dom_FetchSignal_h - -#include "mozilla/DOMEventTargetHelper.h" - -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) - - FetchSignal(FetchController* aController, bool aAborted); - explicit FetchSignal(bool aAborted); - - JSObject* - WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - - bool - Aborted() const; - - void - Abort(); - - 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; -}; - -} // dom namespace -} // mozilla namespace - -#endif // mozilla_dom_FetchSignal_h diff --git a/dom/abort/moz.build b/dom/abort/moz.build index e7d8146e5..cb48ee15f 100644 --- a/dom/abort/moz.build +++ b/dom/abort/moz.build @@ -10,13 +10,13 @@ with Files("**"): TEST_DIRS += ['tests'] EXPORTS.mozilla.dom += [ - 'FetchController.h', - 'FetchSignal.h', + 'AbortController.h', + 'AbortSignal.h', ] UNIFIED_SOURCES += [ - 'FetchController.cpp', - 'FetchSignal.cpp', + 'AbortController.cpp', + 'AbortSignal.cpp', ] LOCAL_INCLUDES += [ diff --git a/dom/abort/tests/file_abort_controller.html b/dom/abort/tests/file_abort_controller.html new file mode 100644 index 000000000..e4137aac9 --- /dev/null +++ b/dom/abort/tests/file_abort_controller.html @@ -0,0 +1,161 @@ + diff --git a/dom/abort/tests/file_fetch_controller.html b/dom/abort/tests/file_fetch_controller.html deleted file mode 100644 index e4137aac9..000000000 --- a/dom/abort/tests/file_fetch_controller.html +++ /dev/null @@ -1,161 +0,0 @@ - diff --git a/dom/abort/tests/mochitest.ini b/dom/abort/tests/mochitest.ini index 5ecc7048e..c8cc95fda 100644 --- a/dom/abort/tests/mochitest.ini +++ b/dom/abort/tests/mochitest.ini @@ -1,6 +1,6 @@ [DEFAULT] support-files = - file_fetch_controller.html + file_abort_controller.html worker_fetch_controller.js -[test_fetch_controller.html] +[test_abort_controller.html] diff --git a/dom/abort/tests/test_abort_controller.html b/dom/abort/tests/test_abort_controller.html new file mode 100644 index 000000000..812fb9161 --- /dev/null +++ b/dom/abort/tests/test_abort_controller.html @@ -0,0 +1,40 @@ + + + + + Test FetchController + + + + + + + + diff --git a/dom/abort/tests/test_fetch_controller.html b/dom/abort/tests/test_fetch_controller.html deleted file mode 100644 index 812fb9161..000000000 --- a/dom/abort/tests/test_fetch_controller.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Test FetchController - - - - - - - - diff --git a/dom/abort/tests/worker_abort_controller.js b/dom/abort/tests/worker_abort_controller.js new file mode 100644 index 000000000..6fd1c7888 --- /dev/null +++ b/dom/abort/tests/worker_abort_controller.js @@ -0,0 +1,27 @@ +function testWorkerAbortedFetch() { + var fc = new AbortController(); + fc.abort(); + + fetch('slow.sjs', { signal: fc.signal }).then(() => { + postMessage(false); + }, e => { + postMessage(e.name == "AbortError"); + }); +} + +function testWorkerFetchAndAbort() { + var fc = new AbortController(); + + var p = fetch('slow.sjs', { signal: fc.signal }); + fc.abort(); + + p.then(() => { + postMessage(false); + }, e => { + postMessage(e.name == "AbortError"); + }); +} + +onmessage = function(e) { + self[e.data](); +} diff --git a/dom/abort/tests/worker_fetch_controller.js b/dom/abort/tests/worker_fetch_controller.js deleted file mode 100644 index 6b008fea8..000000000 --- a/dom/abort/tests/worker_fetch_controller.js +++ /dev/null @@ -1,27 +0,0 @@ -function testWorkerAbortedFetch() { - var fc = new FetchController(); - fc.abort(); - - fetch('slow.sjs', { signal: fc.signal }).then(() => { - postMessage(false); - }, e => { - postMessage(e.name == "AbortError"); - }); -} - -function testWorkerFetchAndAbort() { - var fc = new FetchController(); - - var p = fetch('slow.sjs', { signal: fc.signal }); - fc.abort(); - - p.then(() => { - postMessage(false); - }, e => { - postMessage(e.name == "AbortError"); - }); -} - -onmessage = function(e) { - self[e.data](); -} diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index 04aa7fd91..4dbe2de0a 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -53,24 +53,24 @@ namespace dom { using namespace workers; -// This class helps the proxying of FetchSignal changes cross threads. -class FetchSignalProxy final : public FetchSignal::Follower +// This class helps the proxying of AbortSignal changes cross threads. +class AbortSignalProxy final : public AbortSignal::Follower { // This is created and released on the main-thread. - RefPtr mSignalMainThread; + RefPtr mSignalMainThread; - // This value is used only for the creation of FetchSignal on the + // This value is used only for the creation of AbortSignal on the // main-thread. They are not updated. const bool mAborted; - // This runnable propagates changes from the FetchSignal on workers to the - // FetchSignal on main-thread. - class FetchSignalProxyRunnable final : public Runnable + // This runnable propagates changes from the AbortSignal on workers to the + // AbortSignal on main-thread. + class AbortSignalProxyRunnable final : public Runnable { - RefPtr mProxy; + RefPtr mProxy; public: - explicit FetchSignalProxyRunnable(FetchSignalProxy* aProxy) + explicit AbortSignalProxyRunnable(AbortSignalProxy* aProxy) : mProxy(aProxy) {} @@ -78,16 +78,16 @@ class FetchSignalProxy final : public FetchSignal::Follower Run() override { MOZ_ASSERT(NS_IsMainThread()); - FetchSignal* signal = mProxy->GetOrCreateSignalForMainThread(); + AbortSignal* signal = mProxy->GetOrCreateSignalForMainThread(); signal->Abort(); return NS_OK; } }; public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FetchSignalProxy) + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbortSignalProxy) - explicit FetchSignalProxy(FetchSignal* aSignal) + explicit AbortSignalProxy(AbortSignal* aSignal) : mAborted(aSignal->Aborted()) { Follow(aSignal); @@ -96,17 +96,17 @@ public: void Aborted() override { - RefPtr runnable = - new FetchSignalProxyRunnable(this); + RefPtr runnable = + new AbortSignalProxyRunnable(this); NS_DispatchToMainThread(runnable); } - FetchSignal* + AbortSignal* GetOrCreateSignalForMainThread() { MOZ_ASSERT(NS_IsMainThread()); if (!mSignalMainThread) { - mSignalMainThread = new FetchSignal(mAborted); + mSignalMainThread = new AbortSignal(mAborted); } return mSignalMainThread; } @@ -118,7 +118,7 @@ public: } private: - ~FetchSignalProxy() + ~AbortSignalProxy() { NS_ReleaseOnMainThread(mSignalMainThread.forget()); } @@ -133,14 +133,14 @@ class WorkerFetchResolver final : public FetchDriverObserver friend class WorkerFetchResponseRunnable; RefPtr mPromiseProxy; - RefPtr mSignalProxy; + RefPtr mSignalProxy; RefPtr mFetchObserver; public: // Returns null if worker is shutting down. static already_AddRefed Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise, - FetchSignal* aSignal, FetchObserver* aObserver) + AbortSignal* aSignal, FetchObserver* aObserver) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); @@ -150,9 +150,9 @@ public: return nullptr; } - RefPtr signalProxy; + RefPtr signalProxy; if (aSignal) { - signalProxy = new FetchSignalProxy(aSignal); + signalProxy = new AbortSignalProxy(aSignal); } RefPtr r = @@ -160,8 +160,8 @@ public: return r.forget(); } - FetchSignal* - GetFetchSignal() + AbortSignal* + GetAbortSignal() { MOZ_ASSERT(NS_IsMainThread()); @@ -183,7 +183,7 @@ public: private: WorkerFetchResolver(PromiseWorkerProxy* aProxy, - FetchSignalProxy* aSignalProxy, + AbortSignalProxy* aSignalProxy, FetchObserver* aObserver) : mPromiseProxy(aProxy) , mSignalProxy(aSignalProxy) @@ -287,7 +287,7 @@ public: fetch->SetWorkerScript(spec); } - RefPtr signal = mResolver->GetFetchSignal(); + RefPtr signal = mResolver->GetAbortSignal(); // ...but release it before calling Fetch, because mResolver's callback can // be called synchronously and they want the mutex, too. @@ -329,7 +329,7 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, RefPtr r = request->GetInternalRequest(); - RefPtr signal; + RefPtr signal; if (aInit.mSignal.WasPassed()) { signal = &aInit.mSignal.Value(); // Let's FetchDriver to deal with an already aborted signal. diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index e8d726ce1..067e32db4 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -67,7 +67,7 @@ FetchDriver::~FetchDriver() } nsresult -FetchDriver::Fetch(FetchSignal* aSignal, FetchDriverObserver* aObserver) +FetchDriver::Fetch(AbortSignal* aSignal, FetchDriverObserver* aObserver) { workers::AssertIsOnMainThread(); #ifdef DEBUG diff --git a/dom/fetch/FetchDriver.h b/dom/fetch/FetchDriver.h index e942aa242..57dffa5a7 100644 --- a/dom/fetch/FetchDriver.h +++ b/dom/fetch/FetchDriver.h @@ -12,7 +12,7 @@ #include "nsIStreamListener.h" #include "nsIThreadRetargetableStreamListener.h" #include "mozilla/ConsoleReportCollector.h" -#include "mozilla/dom/FetchSignal.h" +#include "mozilla/dom/AbortSignal.h" #include "mozilla/dom/SRIMetadata.h" #include "mozilla/RefPtr.h" @@ -84,7 +84,7 @@ class FetchDriver final : public nsIStreamListener, public nsIChannelEventSink, public nsIInterfaceRequestor, public nsIThreadRetargetableStreamListener, - public FetchSignal::Follower + public AbortSignal::Follower { public: NS_DECL_ISUPPORTS @@ -98,7 +98,7 @@ public: nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup); - nsresult Fetch(FetchSignal* aSignal, + nsresult Fetch(AbortSignal* aSignal, FetchDriverObserver* aObserver); void @@ -111,7 +111,7 @@ public: mWorkerScript = aWorkerScirpt; } - // FetchSignal::Follower + // AbortSignal::Follower void Aborted() override; diff --git a/dom/fetch/FetchObserver.cpp b/dom/fetch/FetchObserver.cpp index 982f0ad49..93d93773f 100644 --- a/dom/fetch/FetchObserver.cpp +++ b/dom/fetch/FetchObserver.cpp @@ -46,7 +46,7 @@ FetchObserver::IsEnabled(JSContext* aCx, JSObject* aGlobal) } FetchObserver::FetchObserver(nsIGlobalObject* aGlobal, - FetchSignal* aSignal) + AbortSignal* aSignal) : DOMEventTargetHelper(aGlobal) , mState(FetchState::Requesting) { diff --git a/dom/fetch/FetchObserver.h b/dom/fetch/FetchObserver.h index 45adf2ba1..5cd835b3d 100644 --- a/dom/fetch/FetchObserver.h +++ b/dom/fetch/FetchObserver.h @@ -9,13 +9,13 @@ #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/dom/FetchObserverBinding.h" -#include "mozilla/dom/FetchSignal.h" +#include "mozilla/dom/AbortSignal.h" namespace mozilla { namespace dom { class FetchObserver final : public DOMEventTargetHelper - , public FetchSignal::Follower + , public AbortSignal::Follower { public: NS_DECL_ISUPPORTS_INHERITED @@ -24,7 +24,7 @@ public: static bool IsEnabled(JSContext* aCx, JSObject* aGlobal); - FetchObserver(nsIGlobalObject* aGlobal, FetchSignal* aSignal); + FetchObserver(nsIGlobalObject* aGlobal, AbortSignal* aSignal); JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; diff --git a/dom/fetch/Request.h b/dom/fetch/Request.h index 56a75e5af..f6fe9be7b 100644 --- a/dom/fetch/Request.h +++ b/dom/fetch/Request.h @@ -12,7 +12,7 @@ #include "nsWrapperCache.h" #include "mozilla/dom/Fetch.h" -#include "mozilla/dom/FetchSignal.h" +#include "mozilla/dom/AbortSignal.h" #include "mozilla/dom/InternalRequest.h" // Required here due to certain WebIDL enums/classes being declared in both // files. diff --git a/dom/tests/mochitest/fetch/file_fetch_observer.html b/dom/tests/mochitest/fetch/file_fetch_observer.html index a172a18dc..668e609ec 100644 --- a/dom/tests/mochitest/fetch/file_fetch_observer.html +++ b/dom/tests/mochitest/fetch/file_fetch_observer.html @@ -19,7 +19,7 @@ function testObserver() { } function testObserveAbort() { - var fc = new FetchController(); + var fc = new AbortController(); fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { signal: fc.signal, @@ -37,7 +37,7 @@ function testObserveAbort() { } function testObserveComplete() { - var fc = new FetchController(); + var fc = new AbortController(); fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { signal: fc.signal, @@ -54,7 +54,7 @@ function testObserveComplete() { } function testObserveErrored() { - var fc = new FetchController(); + var fc = new AbortController(); fetch('foo: bar', { signal: fc.signal, @@ -71,7 +71,7 @@ function testObserveErrored() { } function testObserveResponding() { - var fc = new FetchController(); + var fc = new AbortController(); fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { signal: fc.signal, diff --git a/dom/webidl/AbortController.webidl b/dom/webidl/AbortController.webidl new file mode 100644 index 000000000..f5d8f317b --- /dev/null +++ b/dom/webidl/AbortController.webidl @@ -0,0 +1,15 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +[Constructor(), Exposed=(Window,Worker), + Func="AbortController::IsEnabled"] +interface AbortController { + readonly attribute AbortSignal signal; + + void abort(); + void follow(AbortSignal signal); + void unfollow(AbortSignal signal); +}; diff --git a/dom/webidl/AbortSignal.webidl b/dom/webidl/AbortSignal.webidl new file mode 100644 index 000000000..b4b03bb7e --- /dev/null +++ b/dom/webidl/AbortSignal.webidl @@ -0,0 +1,13 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +[Exposed=(Window,Worker), + Func="AbortController::IsEnabled"] +interface AbortSignal : EventTarget { + readonly attribute boolean aborted; + + attribute EventHandler onabort; +}; diff --git a/dom/webidl/FetchController.webidl b/dom/webidl/FetchController.webidl deleted file mode 100644 index c5b1cc6da..000000000 --- a/dom/webidl/FetchController.webidl +++ /dev/null @@ -1,15 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. - */ - -[Constructor(), Exposed=(Window,Worker), - Func="FetchController::IsEnabled"] -interface FetchController { - readonly attribute FetchSignal signal; - - void abort(); - void follow(FetchSignal signal); - void unfollow(FetchSignal signal); -}; diff --git a/dom/webidl/FetchSignal.webidl b/dom/webidl/FetchSignal.webidl deleted file mode 100644 index 965355c20..000000000 --- a/dom/webidl/FetchSignal.webidl +++ /dev/null @@ -1,13 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. - */ - -[Exposed=(Window,Worker), - Func="FetchController::IsEnabled"] -interface FetchSignal : EventTarget { - readonly attribute boolean aborted; - - attribute EventHandler onabort; -}; diff --git a/dom/webidl/Request.webidl b/dom/webidl/Request.webidl index 8c6e33da3..fe6a63ec0 100644 --- a/dom/webidl/Request.webidl +++ b/dom/webidl/Request.webidl @@ -48,8 +48,8 @@ dictionary RequestInit { RequestRedirect redirect; DOMString integrity; - [Func="FetchController::IsEnabled"] - FetchSignal signal; + [Func="AbortController::IsEnabled"] + AbortSignal signal; [Func="FetchObserver::IsEnabled"] ObserverCallback observe; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 22075a443..b2dcf8d8d 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -17,6 +17,8 @@ PREPROCESSED_WEBIDL_FILES = [ ] WEBIDL_FILES = [ + 'AbortController.webidl', + 'AbortSignal.webidl', 'AbstractWorker.webidl', 'AnalyserNode.webidl', 'Animatable.webidl', @@ -141,10 +143,8 @@ WEBIDL_FILES = [ 'ExtendableMessageEvent.webidl', 'FakePluginTagInit.webidl', 'Fetch.webidl', - 'FetchController.webidl', 'FetchEvent.webidl', 'FetchObserver.webidl', - 'FetchSignal.webidl', 'File.webidl', 'FileList.webidl', 'FileMode.webidl', diff --git a/dom/workers/WorkerPrefs.h b/dom/workers/WorkerPrefs.h index 374f41ecf..b552d8956 100644 --- a/dom/workers/WorkerPrefs.h +++ b/dom/workers/WorkerPrefs.h @@ -39,7 +39,7 @@ WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED) WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED) WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED) WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK) -WORKER_SIMPLE_PREF("dom.fetchController.enabled", FetchControllerEnabled, FETCHCONTROLLER_ENABLED) +WORKER_SIMPLE_PREF("dom.abortController.enabled", AbortControllerEnabled, ABORTCONTROLLER_ENABLED) WORKER_SIMPLE_PREF("dom.fetchObserver.enabled", FetchObserverEnabled, FETCHOBSERVER_ENABLED) WORKER_PREF("dom.workers.latestJSVersion", JSVersionChanged) WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) -- cgit v1.2.3 From 94c694d389ceb2c797d7ee1854cc84c87349c2f5 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Fri, 12 Jun 2020 01:08:26 +0000 Subject: Issue #1587 - Part 8: Remove controller follow/unfollow Since it didn't end up being in the final spec after all. --- dom/abort/AbortController.cpp | 31 +------------------ dom/abort/AbortController.h | 14 --------- dom/abort/AbortSignal.cpp | 27 ----------------- dom/abort/AbortSignal.h | 3 -- dom/abort/tests/file_abort_controller.html | 48 ------------------------------ dom/webidl/AbortController.webidl | 2 -- 6 files changed, 1 insertion(+), 124 deletions(-) (limited to 'dom') diff --git a/dom/abort/AbortController.cpp b/dom/abort/AbortController.cpp index 4043b7bf3..bd8159e7b 100644 --- a/dom/abort/AbortController.cpp +++ b/dom/abort/AbortController.cpp @@ -12,8 +12,7 @@ namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AbortController, mGlobal, mSignal, - mFollowingSignal) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AbortController, mGlobal, mSignal) NS_IMPL_CYCLE_COLLECTING_ADDREF(AbortController) NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortController) @@ -95,33 +94,5 @@ AbortController::Abort() } } -void -AbortController::Follow(AbortSignal& aSignal) -{ - AbortSignal::Follower::Follow(&aSignal); -} - -void -AbortController::Unfollow(AbortSignal& aSignal) -{ - if (mFollowingSignal != &aSignal) { - return; - } - - AbortSignal::Follower::Unfollow(); -} - -AbortSignal* -AbortController::Following() const -{ - return mFollowingSignal; -} - -void -AbortController::Aborted() -{ - Abort(); -} - } // dom namespace } // mozilla namespace diff --git a/dom/abort/AbortController.h b/dom/abort/AbortController.h index 9ce4253c9..0b99dc49c 100644 --- a/dom/abort/AbortController.h +++ b/dom/abort/AbortController.h @@ -19,7 +19,6 @@ namespace dom { class AbortController final : public nsISupports , public nsWrapperCache - , public AbortSignal::Follower { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -45,19 +44,6 @@ public: void Abort(); - void - Follow(AbortSignal& aSignal); - - void - Unfollow(AbortSignal& aSignal); - - AbortSignal* - Following() const; - - // AbortSignal::Follower - - void Aborted() override; - private: ~AbortController() = default; diff --git a/dom/abort/AbortSignal.cpp b/dom/abort/AbortSignal.cpp index 38b0a2492..20f36d2ab 100644 --- a/dom/abort/AbortSignal.cpp +++ b/dom/abort/AbortSignal.cpp @@ -68,8 +68,6 @@ AbortSignal::Abort() init.mBubbles = false; init.mCancelable = false; - // TODO which kind of event should we dispatch here? - RefPtr event = Event::Constructor(this, NS_LITERAL_STRING("abort"), init); event->SetTrusted(true); @@ -94,27 +92,6 @@ AbortSignal::RemoveFollower(AbortSignal::Follower* aFollower) mFollowers.RemoveElement(aFollower); } -bool -AbortSignal::CanAcceptFollower(AbortSignal::Follower* aFollower) const -{ - MOZ_DIAGNOSTIC_ASSERT(aFollower); - - if (!mController) { - return true; - } - - if (aFollower == mController) { - return false; - } - - AbortSignal* following = mController->Following(); - if (!following) { - return true; - } - - return following->CanAcceptFollower(aFollower); -} - // AbortSignal::Follower // ---------------------------------------------------------------------------- @@ -128,10 +105,6 @@ AbortSignal::Follower::Follow(AbortSignal* aSignal) { MOZ_DIAGNOSTIC_ASSERT(aSignal); - if (!aSignal->CanAcceptFollower(this)) { - return; - } - Unfollow(); mFollowingSignal = aSignal; diff --git a/dom/abort/AbortSignal.h b/dom/abort/AbortSignal.h index c5045dbd6..35e582942 100644 --- a/dom/abort/AbortSignal.h +++ b/dom/abort/AbortSignal.h @@ -59,9 +59,6 @@ public: void RemoveFollower(Follower* aFollower); - bool - CanAcceptFollower(Follower* aFollower) const; - private: ~AbortSignal() = default; diff --git a/dom/abort/tests/file_abort_controller.html b/dom/abort/tests/file_abort_controller.html index e4137aac9..3a15fa346 100644 --- a/dom/abort/tests/file_abort_controller.html +++ b/dom/abort/tests/file_abort_controller.html @@ -32,49 +32,6 @@ function testUpdateData() { 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) { @@ -131,11 +88,6 @@ var steps = [ testWebIDL, testUpdateData, - // Following algorithm - testFollowingOurself, - testFollowingOther, - testFollowingLoop, - // Event propagation testAbortEvent, diff --git a/dom/webidl/AbortController.webidl b/dom/webidl/AbortController.webidl index f5d8f317b..4e9124075 100644 --- a/dom/webidl/AbortController.webidl +++ b/dom/webidl/AbortController.webidl @@ -10,6 +10,4 @@ interface AbortController { readonly attribute AbortSignal signal; void abort(); - void follow(AbortSignal signal); - void unfollow(AbortSignal signal); }; -- cgit v1.2.3 From 1c97314804cff846def08b122efb32d514bd1a09 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Fri, 12 Jun 2020 01:46:48 +0000 Subject: Issue #1587 - Part 9: Immediately reject an already-aborted signal --- dom/fetch/Fetch.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'dom') diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index 4dbe2de0a..191f4cfc3 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -332,7 +332,12 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, RefPtr signal; if (aInit.mSignal.WasPassed()) { signal = &aInit.mSignal.Value(); - // Let's FetchDriver to deal with an already aborted signal. + } + + if (signal && signal->Aborted()) { + // An already aborted signal should reject immediately. + aRv.Throw(NS_ERROR_DOM_ABORT_ERR); + return nullptr; } RefPtr observer; -- cgit v1.2.3