diff options
Diffstat (limited to 'dom/abort')
-rw-r--r-- | dom/abort/AbortController.cpp | 67 | ||||
-rw-r--r-- | dom/abort/AbortController.h | 55 | ||||
-rw-r--r-- | dom/abort/AbortFollower.h | 83 | ||||
-rw-r--r-- | dom/abort/AbortSignal.cpp | 144 | ||||
-rw-r--r-- | dom/abort/AbortSignal.h | 55 | ||||
-rw-r--r-- | dom/abort/moz.build | 25 | ||||
-rw-r--r-- | dom/abort/tests/.eslintrc.js | 13 | ||||
-rw-r--r-- | dom/abort/tests/mochitest.ini | 10 | ||||
-rw-r--r-- | dom/abort/tests/moz.build | 9 | ||||
-rw-r--r-- | dom/abort/tests/slow.sjs | 11 | ||||
-rw-r--r-- | dom/abort/tests/test_abort_controller.html | 73 | ||||
-rw-r--r-- | dom/abort/tests/test_abort_controller_fetch.html | 80 | ||||
-rw-r--r-- | dom/abort/tests/test_event_listener_leaks.html | 43 | ||||
-rw-r--r-- | dom/abort/tests/unit/test_abort.js | 8 | ||||
-rw-r--r-- | dom/abort/tests/unit/xpcshell.ini | 5 | ||||
-rw-r--r-- | dom/abort/tests/worker_abort_controller_fetch.js | 33 |
16 files changed, 714 insertions, 0 deletions
diff --git a/dom/abort/AbortController.cpp b/dom/abort/AbortController.cpp new file mode 100644 index 0000000000..c5b4a743ee --- /dev/null +++ b/dom/abort/AbortController.cpp @@ -0,0 +1,67 @@ +/* -*- 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 "mozilla/dom/WorkerPrivate.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AbortController, mGlobal, mSignal) + +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 */ +already_AddRefed<AbortController> AbortController::Constructor( + const GlobalObject& aGlobal, ErrorResult& aRv) { + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr<AbortController> abortController = new AbortController(global); + return abortController.forget(); +} + +AbortController::AbortController(nsIGlobalObject* aGlobal) + : mGlobal(aGlobal), mAborted(false) {} + +JSObject* AbortController::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return AbortController_Binding::Wrap(aCx, this, aGivenProto); +} + +nsIGlobalObject* AbortController::GetParentObject() const { return mGlobal; } + +AbortSignal* AbortController::Signal() { + if (!mSignal) { + mSignal = new AbortSignal(mGlobal, mAborted); + } + + return mSignal; +} + +void AbortController::Abort() { + if (mAborted) { + return; + } + + mAborted = true; + + if (mSignal) { + mSignal->SignalAbort(); + } +} + +} // namespace mozilla::dom diff --git a/dom/abort/AbortController.h b/dom/abort/AbortController.h new file mode 100644 index 0000000000..c653f2a768 --- /dev/null +++ b/dom/abort/AbortController.h @@ -0,0 +1,55 @@ +/* -*- 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 "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" + +class nsIGlobalObject; + +namespace mozilla { +class ErrorResult; + +namespace dom { + +class AbortSignal; + +class AbortController final : public nsISupports, public nsWrapperCache { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbortController) + + static already_AddRefed<AbortController> Constructor( + const GlobalObject& aGlobal, ErrorResult& aRv); + + explicit AbortController(nsIGlobalObject* aGlobal); + + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + + nsIGlobalObject* GetParentObject() const; + + AbortSignal* Signal(); + + void Abort(); + + private: + ~AbortController() = default; + + nsCOMPtr<nsIGlobalObject> mGlobal; + RefPtr<AbortSignal> mSignal; + + bool mAborted; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_AbortController_h diff --git a/dom/abort/AbortFollower.h b/dom/abort/AbortFollower.h new file mode 100644 index 0000000000..8c0e46d803 --- /dev/null +++ b/dom/abort/AbortFollower.h @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_AbortFollower_h +#define mozilla_dom_AbortFollower_h + +#include "nsISupportsImpl.h" +#include "nsTObserverArray.h" + +namespace mozilla { +namespace dom { + +class AbortSignal; +class AbortSignalImpl; + +// This class must be implemented by objects who want to follow an +// AbortSignalImpl. +class AbortFollower : public nsISupports { + public: + virtual void RunAbortAlgorithm() = 0; + + void Follow(AbortSignalImpl* aSignal); + + void Unfollow(); + + bool IsFollowing() const; + + AbortSignalImpl* Signal() const { return mFollowingSignal; } + + protected: + // Subclasses of this class must call these Traverse and Unlink functions + // during corresponding cycle collection operations. + static void Traverse(AbortFollower* aFollower, + nsCycleCollectionTraversalCallback& cb); + + static void Unlink(AbortFollower* aFollower) { aFollower->Unfollow(); } + + virtual ~AbortFollower(); + + friend class AbortSignalImpl; + + RefPtr<AbortSignalImpl> mFollowingSignal; +}; + +class AbortSignalImpl : public nsISupports { + public: + explicit AbortSignalImpl(bool aAborted); + + bool Aborted() const; + + virtual void SignalAbort(); + + protected: + // Subclasses of this class must call these Traverse and Unlink functions + // during corresponding cycle collection operations. + static void Traverse(AbortSignalImpl* aSignal, + nsCycleCollectionTraversalCallback& cb); + + static void Unlink(AbortSignalImpl* aSignal) { + // To be filled in shortly. + } + + virtual ~AbortSignalImpl() = default; + + private: + friend class AbortFollower; + + // Raw pointers. |AbortFollower::Follow| adds to this array, and + // |AbortFollower::Unfollow| (also callbed by the destructor) will remove + // from this array. Finally, calling |SignalAbort()| will (after running all + // abort algorithms) empty this and make all contained followers |Unfollow()|. + nsTObserverArray<AbortFollower*> mFollowers; + + bool mAborted; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_AbortFollower_h diff --git a/dom/abort/AbortSignal.cpp b/dom/abort/AbortSignal.cpp new file mode 100644 index 0000000000..38458c9c28 --- /dev/null +++ b/dom/abort/AbortSignal.cpp @@ -0,0 +1,144 @@ +/* -*- 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 "mozilla/dom/Event.h" +#include "mozilla/dom/EventBinding.h" +#include "mozilla/dom/AbortSignalBinding.h" +#include "mozilla/RefPtr.h" + +namespace mozilla::dom { + +// AbortSignalImpl +// ---------------------------------------------------------------------------- + +AbortSignalImpl::AbortSignalImpl(bool aAborted) : mAborted(aAborted) {} + +bool AbortSignalImpl::Aborted() const { return mAborted; } + +// https://dom.spec.whatwg.org/#abortsignal-signal-abort steps 1-4 +void AbortSignalImpl::SignalAbort() { + // Step 1. + if (mAborted) { + return; + } + + // Step 2. + mAborted = true; + + // Step 3. + // When there are multiple followers, the follower removal algorithm + // https://dom.spec.whatwg.org/#abortsignal-remove could be invoked in an + // earlier algorithm to remove a later algorithm, so |mFollowers| must be a + // |nsTObserverArray| to defend against mutation. + for (RefPtr<AbortFollower> follower : mFollowers.ForwardRange()) { + MOZ_ASSERT(follower->mFollowingSignal == this); + follower->RunAbortAlgorithm(); + } + + // Step 4. + // Clear follower->signal links, then clear signal->follower links. + for (AbortFollower* follower : mFollowers.ForwardRange()) { + follower->mFollowingSignal = nullptr; + } + mFollowers.Clear(); +} + +/* static */ void AbortSignalImpl::Traverse( + AbortSignalImpl* aSignal, nsCycleCollectionTraversalCallback& cb) { + // To be filled in shortly. +} + +// AbortSignal +// ---------------------------------------------------------------------------- + +NS_IMPL_CYCLE_COLLECTION_CLASS(AbortSignal) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AbortSignal, + DOMEventTargetHelper) + AbortSignalImpl::Traverse(static_cast<AbortSignalImpl*>(tmp), cb); + AbortFollower::Traverse(static_cast<AbortFollower*>(tmp), cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AbortSignal, + DOMEventTargetHelper) + AbortSignalImpl::Unlink(static_cast<AbortSignalImpl*>(tmp)); + AbortFollower::Unlink(static_cast<AbortFollower*>(tmp)); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortSignal) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(AbortSignal, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(AbortSignal, DOMEventTargetHelper) + +AbortSignal::AbortSignal(nsIGlobalObject* aGlobalObject, bool aAborted) + : DOMEventTargetHelper(aGlobalObject), AbortSignalImpl(aAborted) {} + +JSObject* AbortSignal::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return AbortSignal_Binding::Wrap(aCx, this, aGivenProto); +} + +// https://dom.spec.whatwg.org/#abortsignal-signal-abort +void AbortSignal::SignalAbort() { + // Steps 1-4. + AbortSignalImpl::SignalAbort(); + + // Step 5. + EventInit init; + init.mBubbles = false; + init.mCancelable = false; + + RefPtr<Event> event = Event::Constructor(this, u"abort"_ns, init); + event->SetTrusted(true); + + DispatchEvent(*event); +} + +// AbortFollower +// ---------------------------------------------------------------------------- + +AbortFollower::~AbortFollower() { Unfollow(); } + +// https://dom.spec.whatwg.org/#abortsignal-add +void AbortFollower::Follow(AbortSignalImpl* aSignal) { + // Step 1. + if (aSignal->mAborted) { + return; + } + + MOZ_DIAGNOSTIC_ASSERT(aSignal); + + Unfollow(); + + // Step 2. + mFollowingSignal = aSignal; + MOZ_ASSERT(!aSignal->mFollowers.Contains(this)); + aSignal->mFollowers.AppendElement(this); +} + +// https://dom.spec.whatwg.org/#abortsignal-remove +void AbortFollower::Unfollow() { + if (mFollowingSignal) { + // |Unfollow| is called by cycle-collection unlink code that runs in no + // guaranteed order. So we can't, symmetric with |Follow| above, assert + // that |this| will be found in |mFollowingSignal->mFollowers|. + mFollowingSignal->mFollowers.RemoveElement(this); + mFollowingSignal = nullptr; + } +} + +bool AbortFollower::IsFollowing() const { return !!mFollowingSignal; } + +/* static */ void AbortFollower::Traverse( + AbortFollower* aFollower, nsCycleCollectionTraversalCallback& cb) { + ImplCycleCollectionTraverse(cb, aFollower->mFollowingSignal, + "mFollowingSignal", 0); +} + +} // namespace mozilla::dom diff --git a/dom/abort/AbortSignal.h b/dom/abort/AbortSignal.h new file mode 100644 index 0000000000..2b0b49d169 --- /dev/null +++ b/dom/abort/AbortSignal.h @@ -0,0 +1,55 @@ +/* -*- 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/dom/AbortFollower.h" +#include "mozilla/DOMEventTargetHelper.h" + +namespace mozilla { +namespace dom { + +// AbortSignal the spec concept includes the concept of a child signal +// "following" a parent signal -- internally, adding abort steps to the parent +// signal that will then signal abort on the child signal -- to propagate +// signaling abort from one signal to another. See +// <https://dom.spec.whatwg.org/#abortsignal-follow>. +// +// This requires that AbortSignal also inherit from AbortFollower. +// +// This ability to follow isn't directly exposed in the DOM; as of this writing +// it appears only to be used internally in the Fetch API. It might be a good +// idea to split AbortSignal into an implementation that can follow, and an +// implementation that can't, to provide this complexity only when it's needed. +class AbortSignal final : public DOMEventTargetHelper, + public AbortSignalImpl, + public AbortFollower { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AbortSignal, DOMEventTargetHelper) + + AbortSignal(nsIGlobalObject* aGlobalObject, bool aAborted); + + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + + IMPL_EVENT_HANDLER(abort); + + // AbortSignalImpl + void SignalAbort() override; + + // AbortFollower + void RunAbortAlgorithm() override { SignalAbort(); } + + private: + ~AbortSignal() = default; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_AbortSignal_h diff --git a/dom/abort/moz.build b/dom/abort/moz.build new file mode 100644 index 0000000000..eb5b25584a --- /dev/null +++ b/dom/abort/moz.build @@ -0,0 +1,25 @@ +# -*- 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/. + +with Files("**"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +TEST_DIRS += ["tests"] + +EXPORTS.mozilla.dom += [ + "AbortController.h", + "AbortFollower.h", + "AbortSignal.h", +] + +UNIFIED_SOURCES += [ + "AbortController.cpp", + "AbortSignal.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" diff --git a/dom/abort/tests/.eslintrc.js b/dom/abort/tests/.eslintrc.js new file mode 100644 index 0000000000..0bae1693ce --- /dev/null +++ b/dom/abort/tests/.eslintrc.js @@ -0,0 +1,13 @@ +"use strict"; + +module.exports = { + extends: ["plugin:mozilla/mochitest-test"], + plugins: ["mozilla"], + rules: { + "no-shadow": "off", + }, + globals: { + AbortController: true, + AbortSignal: true, + }, +}; diff --git a/dom/abort/tests/mochitest.ini b/dom/abort/tests/mochitest.ini new file mode 100644 index 0000000000..2d708e8aa2 --- /dev/null +++ b/dom/abort/tests/mochitest.ini @@ -0,0 +1,10 @@ +[DEFAULT] +support-files = + worker_abort_controller_fetch.js + slow.sjs + !/dom/events/test/event_leak_utils.js + +[test_abort_controller.html] +[test_abort_controller_fetch.html] +[test_event_listener_leaks.html] +skip-if = (os == "win" && processor == "aarch64") #bug 1535784 diff --git a/dom/abort/tests/moz.build b/dom/abort/tests/moz.build new file mode 100644 index 0000000000..af8d7033c8 --- /dev/null +++ b/dom/abort/tests/moz.build @@ -0,0 +1,9 @@ +# -*- 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"] + +XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell.ini"] diff --git a/dom/abort/tests/slow.sjs b/dom/abort/tests/slow.sjs new file mode 100644 index 0000000000..feab0f1fce --- /dev/null +++ b/dom/abort/tests/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/abort/tests/test_abort_controller.html b/dom/abort/tests/test_abort_controller.html new file mode 100644 index 0000000000..a7181711d5 --- /dev/null +++ b/dom/abort/tests/test_abort_controller.html @@ -0,0 +1,73 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test AbortController</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +function testWebIDL() { + ok("AbortController" in self, "We have a AbortController prototype"); + ok("AbortSignal" in self, "We have a AbortSignal prototype"); + + var ac = new AbortController(); + ok(!!ac, "AbortController can be created"); + ok(ac instanceof AbortController, "AbortController is a AbortController"); + + ok(!!ac.signal, "AbortController has a signal"); + ok(ac.signal instanceof AbortSignal, "abortSignal is a AbortSignal"); + is(ac.signal.aborted, false, "By default AbortSignal.aborted is false"); + next(); +} + +function testUpdateData() { + var ac = new AbortController(); + + is(ac.signal.aborted, false, "By default AbortSignal.aborted is false"); + + ac.abort(); + is(ac.signal.aborted, true, "Signal is aborted"); + + next(); +} + +function testAbortEvent() { + var ac = new AbortController(); + ac.signal.onabort = function(e) { + is(e.type, "abort", "Abort received"); + next(); + }; + ac.abort(); +} + +var steps = [ + // Simple stuff + testWebIDL, + testUpdateData, + + // Event propagation + testAbortEvent, +]; + +function next() { + if (!steps.length) { + SimpleTest.finish(); + return; + } + + var step = steps.shift(); + step(); +} + +SimpleTest.waitForExplicitFinish(); +next(); + +</script> +</body> +</html> diff --git a/dom/abort/tests/test_abort_controller_fetch.html b/dom/abort/tests/test_abort_controller_fetch.html new file mode 100644 index 0000000000..2342ecda89 --- /dev/null +++ b/dom/abort/tests/test_abort_controller_fetch.html @@ -0,0 +1,80 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Test AbortController in Fetch API</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +function testAbortedFetch() { + var ac = new AbortController(); + ac.abort(); + + fetch("slow.sjs", { signal: ac.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 ac = new AbortController(); + + var p = fetch("slow.sjs", { signal: ac.signal }); + ac.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() { + var w = new Worker("worker_abort_controller_fetch.js"); + w.onmessage = function(e) { + ok(e.data, "Abort + Fetch works in workers"); + next(); + }; + w.postMessage("testWorkerAbortedFetch"); +} + +function testWorkerFetchAndAbort() { + var w = new Worker("worker_abort_controller_fetch.js"); + w.onmessage = function(e) { + ok(e.data, "Abort + Fetch works in workers"); + next(); + }; + w.postMessage("testWorkerFetchAndAbort"); +} + +var steps = [ + // fetch + signaling + testAbortedFetch, + testFetchAndAbort, + testWorkerAbortedFetch, + testWorkerFetchAndAbort, +]; + +function next() { + if (!steps.length) { + SimpleTest.finish(); + return; + } + + var step = steps.shift(); + step(); +} + +SimpleTest.waitForExplicitFinish(); +next(); + +</script> +</body> +</html> diff --git a/dom/abort/tests/test_event_listener_leaks.html b/dom/abort/tests/test_event_listener_leaks.html new file mode 100644 index 0000000000..f9684e2309 --- /dev/null +++ b/dom/abort/tests/test_event_listener_leaks.html @@ -0,0 +1,43 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1450271 - Test AbortSignal event listener leak conditions</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/dom/events/test/event_leak_utils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<script class="testbody" type="text/javascript"> + +// Manipulate AbortSignal. Its important here that we create a +// listener callback from the DOM objects back to the frame's global +// in order to exercise the leak condition. +async function useAbortSignal(contentWindow) { + let controller = new contentWindow.AbortController(); + let signal = controller.signal; + signal.onabort = _ => { + contentWindow.abortCount += 1; + }; +} + +async function runTest() { + try { + await checkForEventListenerLeaks("AbortSignal", useAbortSignal); + } catch (e) { + ok(false, e); + } finally { + SimpleTest.finish(); + } +} + +SimpleTest.waitForExplicitFinish(); +addEventListener("load", runTest, { once: true }); +</script> +</pre> +</body> +</html> diff --git a/dom/abort/tests/unit/test_abort.js b/dom/abort/tests/unit/test_abort.js new file mode 100644 index 0000000000..c1586443cb --- /dev/null +++ b/dom/abort/tests/unit/test_abort.js @@ -0,0 +1,8 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + let ac = new AbortController(); + Assert.ok(ac instanceof AbortController); + Assert.ok(ac.signal instanceof AbortSignal); +} diff --git a/dom/abort/tests/unit/xpcshell.ini b/dom/abort/tests/unit/xpcshell.ini new file mode 100644 index 0000000000..219daa771a --- /dev/null +++ b/dom/abort/tests/unit/xpcshell.ini @@ -0,0 +1,5 @@ +[DEFAULT] +head = +support-files = + +[test_abort.js] diff --git a/dom/abort/tests/worker_abort_controller_fetch.js b/dom/abort/tests/worker_abort_controller_fetch.js new file mode 100644 index 0000000000..76d30b01bf --- /dev/null +++ b/dom/abort/tests/worker_abort_controller_fetch.js @@ -0,0 +1,33 @@ +function testWorkerAbortedFetch() { + var ac = new AbortController(); + ac.abort(); + + fetch("slow.sjs", { signal: ac.signal }).then( + () => { + postMessage(false); + }, + e => { + postMessage(e.name == "AbortError"); + } + ); +} + +function testWorkerFetchAndAbort() { + var ac = new AbortController(); + + var p = fetch("slow.sjs", { signal: ac.signal }); + ac.abort(); + + p.then( + () => { + postMessage(false); + }, + e => { + postMessage(e.name == "AbortError"); + } + ); +} + +self.onmessage = function(e) { + self[e.data](); +}; |