зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1737771
- Implement AbortSignal.reason r=mgaudet,smaug
Differential Revision: https://phabricator.services.mozilla.com/D132281
This commit is contained in:
Родитель
aaa7b140bc
Коммит
712c0d58c7
|
@ -6,12 +6,27 @@
|
|||
|
||||
#include "AbortController.h"
|
||||
#include "AbortSignal.h"
|
||||
#include "js/Value.h"
|
||||
#include "mozilla/dom/AbortControllerBinding.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AbortController, mGlobal, mSignal)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mSignal)
|
||||
tmp->mReason.setUndefined();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mSignal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReason)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortController)
|
||||
|
@ -35,7 +50,9 @@ already_AddRefed<AbortController> AbortController::Constructor(
|
|||
}
|
||||
|
||||
AbortController::AbortController(nsIGlobalObject* aGlobal)
|
||||
: mGlobal(aGlobal), mAborted(false) {}
|
||||
: mGlobal(aGlobal), mAborted(false), mReason(JS::UndefinedHandleValue) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
JSObject* AbortController::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
|
@ -46,13 +63,14 @@ nsIGlobalObject* AbortController::GetParentObject() const { return mGlobal; }
|
|||
|
||||
AbortSignal* AbortController::Signal() {
|
||||
if (!mSignal) {
|
||||
mSignal = new AbortSignal(mGlobal, mAborted);
|
||||
JS::Rooted<JS::Value> reason(RootingCx(), mReason);
|
||||
mSignal = new AbortSignal(mGlobal, mAborted, reason);
|
||||
}
|
||||
|
||||
return mSignal;
|
||||
}
|
||||
|
||||
void AbortController::Abort() {
|
||||
void AbortController::Abort(JSContext* aCx, JS::Handle<JS::Value> aReason) {
|
||||
if (mAborted) {
|
||||
return;
|
||||
}
|
||||
|
@ -60,8 +78,12 @@ void AbortController::Abort() {
|
|||
mAborted = true;
|
||||
|
||||
if (mSignal) {
|
||||
mSignal->SignalAbort();
|
||||
mSignal->SignalAbort(aReason);
|
||||
} else {
|
||||
mReason = aReason;
|
||||
}
|
||||
}
|
||||
|
||||
AbortController::~AbortController() { mozilla::DropJSObjects(this); }
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
@ -38,15 +38,16 @@ class AbortController final : public nsISupports, public nsWrapperCache {
|
|||
|
||||
AbortSignal* Signal();
|
||||
|
||||
void Abort();
|
||||
void Abort(JSContext* aCx, JS::Handle<JS::Value> aReason);
|
||||
|
||||
private:
|
||||
~AbortController() = default;
|
||||
~AbortController();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
RefPtr<AbortSignal> mSignal;
|
||||
|
||||
bool mAborted;
|
||||
JS::Heap<JS::Value> mReason;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef mozilla_dom_AbortFollower_h
|
||||
#define mozilla_dom_AbortFollower_h
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
|
@ -47,11 +48,16 @@ class AbortFollower : public nsISupports {
|
|||
|
||||
class AbortSignalImpl : public nsISupports {
|
||||
public:
|
||||
explicit AbortSignalImpl(bool aAborted);
|
||||
explicit AbortSignalImpl(bool aAborted, JS::Handle<JS::Value> aReason);
|
||||
|
||||
bool Aborted() const;
|
||||
|
||||
virtual void SignalAbort();
|
||||
// Web IDL Layer
|
||||
void GetReason(JSContext* aCx, JS::MutableHandle<JS::Value> aReason);
|
||||
// Helper for other DOM code
|
||||
JS::Value RawReason() const;
|
||||
|
||||
virtual void SignalAbort(JS::Handle<JS::Value> aReason);
|
||||
|
||||
protected:
|
||||
// Subclasses of this class must call these Traverse and Unlink functions
|
||||
|
@ -59,15 +65,17 @@ class AbortSignalImpl : public nsISupports {
|
|||
static void Traverse(AbortSignalImpl* aSignal,
|
||||
nsCycleCollectionTraversalCallback& cb);
|
||||
|
||||
static void Unlink(AbortSignalImpl* aSignal) {
|
||||
// To be filled in shortly.
|
||||
}
|
||||
static void Unlink(AbortSignalImpl* aSignal);
|
||||
|
||||
virtual ~AbortSignalImpl() = default;
|
||||
|
||||
JS::Heap<JS::Value> mReason;
|
||||
|
||||
private:
|
||||
friend class AbortFollower;
|
||||
|
||||
void MaybeAssignAbortError(JSContext* aCx);
|
||||
|
||||
// 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
|
||||
|
|
|
@ -6,22 +6,39 @@
|
|||
|
||||
#include "AbortSignal.h"
|
||||
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/EventBinding.h"
|
||||
#include "mozilla/dom/AbortSignalBinding.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
// AbortSignalImpl
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
AbortSignalImpl::AbortSignalImpl(bool aAborted) : mAborted(aAborted) {}
|
||||
AbortSignalImpl::AbortSignalImpl(bool aAborted, JS::Handle<JS::Value> aReason)
|
||||
: mReason(aReason), mAborted(aAborted) {
|
||||
MOZ_ASSERT_IF(!mReason.isUndefined(), mAborted);
|
||||
}
|
||||
|
||||
bool AbortSignalImpl::Aborted() const { return mAborted; }
|
||||
|
||||
void AbortSignalImpl::GetReason(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aReason) {
|
||||
if (!mAborted) {
|
||||
return;
|
||||
}
|
||||
MaybeAssignAbortError(aCx);
|
||||
aReason.set(mReason);
|
||||
}
|
||||
|
||||
JS::Value AbortSignalImpl::RawReason() const { return mReason.get(); }
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal-signal-abort steps 1-4
|
||||
void AbortSignalImpl::SignalAbort() {
|
||||
void AbortSignalImpl::SignalAbort(JS::Handle<JS::Value> aReason) {
|
||||
// Step 1.
|
||||
if (mAborted) {
|
||||
return;
|
||||
|
@ -29,6 +46,7 @@ void AbortSignalImpl::SignalAbort() {
|
|||
|
||||
// Step 2.
|
||||
mAborted = true;
|
||||
mReason = aReason;
|
||||
|
||||
// Step 3.
|
||||
// When there are multiple followers, the follower removal algorithm
|
||||
|
@ -48,11 +66,31 @@ void AbortSignalImpl::SignalAbort() {
|
|||
mFollowers.Clear();
|
||||
}
|
||||
|
||||
/* static */ void AbortSignalImpl::Traverse(
|
||||
AbortSignalImpl* aSignal, nsCycleCollectionTraversalCallback& cb) {
|
||||
void AbortSignalImpl::Traverse(AbortSignalImpl* aSignal,
|
||||
nsCycleCollectionTraversalCallback& cb) {
|
||||
// To be filled in shortly.
|
||||
}
|
||||
|
||||
void AbortSignalImpl::Unlink(AbortSignalImpl* aSignal) {
|
||||
aSignal->mReason.setUndefined();
|
||||
}
|
||||
|
||||
void AbortSignalImpl::MaybeAssignAbortError(JSContext* aCx) {
|
||||
MOZ_ASSERT(mAborted);
|
||||
if (!mReason.isUndefined()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> exception(aCx);
|
||||
RefPtr<DOMException> dom = DOMException::Create(NS_ERROR_DOM_ABORT_ERR);
|
||||
|
||||
if (NS_WARN_IF(!ToJSValue(aCx, dom, &exception))) {
|
||||
return;
|
||||
}
|
||||
|
||||
mReason.set(exception);
|
||||
}
|
||||
|
||||
// AbortSignal
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -73,27 +111,38 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortSignal)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(AbortSignal,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReason)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(AbortSignal, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(AbortSignal, DOMEventTargetHelper)
|
||||
|
||||
AbortSignal::AbortSignal(nsIGlobalObject* aGlobalObject, bool aAborted)
|
||||
: DOMEventTargetHelper(aGlobalObject), AbortSignalImpl(aAborted) {}
|
||||
AbortSignal::AbortSignal(nsIGlobalObject* aGlobalObject, bool aAborted,
|
||||
JS::Handle<JS::Value> aReason)
|
||||
: DOMEventTargetHelper(aGlobalObject), AbortSignalImpl(aAborted, aReason) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
JSObject* AbortSignal::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return AbortSignal_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<AbortSignal> AbortSignal::Abort(GlobalObject& aGlobal) {
|
||||
already_AddRefed<AbortSignal> AbortSignal::Abort(GlobalObject& aGlobal,
|
||||
JS::Handle<JS::Value> aReason,
|
||||
ErrorResult& aRv) {
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
RefPtr<AbortSignal> abortSignal = new AbortSignal(global, true);
|
||||
|
||||
RefPtr<AbortSignal> abortSignal = new AbortSignal(global, true, aReason);
|
||||
return abortSignal.forget();
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal-signal-abort
|
||||
void AbortSignal::SignalAbort() {
|
||||
void AbortSignal::SignalAbort(JS::Handle<JS::Value> aReason) {
|
||||
// Steps 1-4.
|
||||
AbortSignalImpl::SignalAbort();
|
||||
AbortSignalImpl::SignalAbort(aReason);
|
||||
|
||||
// Step 5.
|
||||
EventInit init;
|
||||
|
@ -106,6 +155,13 @@ void AbortSignal::SignalAbort() {
|
|||
DispatchEvent(*event);
|
||||
}
|
||||
|
||||
void AbortSignal::RunAbortAlgorithm() {
|
||||
JS::Rooted<JS::Value> reason(RootingCx(), Signal()->RawReason());
|
||||
SignalAbort(reason);
|
||||
}
|
||||
|
||||
AbortSignal::~AbortSignal() { mozilla::DropJSObjects(this); }
|
||||
|
||||
// AbortFollower
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -30,25 +30,29 @@ class AbortSignal final : public DOMEventTargetHelper,
|
|||
public AbortFollower {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AbortSignal, DOMEventTargetHelper)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(AbortSignal,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
AbortSignal(nsIGlobalObject* aGlobalObject, bool aAborted);
|
||||
AbortSignal(nsIGlobalObject* aGlobalObject, bool aAborted,
|
||||
JS::Handle<JS::Value> aReason);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
IMPL_EVENT_HANDLER(abort);
|
||||
|
||||
static already_AddRefed<AbortSignal> Abort(GlobalObject& aGlobal);
|
||||
static already_AddRefed<AbortSignal> Abort(GlobalObject& aGlobal,
|
||||
JS::Handle<JS::Value> aReason,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// AbortSignalImpl
|
||||
void SignalAbort() override;
|
||||
void SignalAbort(JS::Handle<JS::Value> aReason) override;
|
||||
|
||||
// AbortFollower
|
||||
void RunAbortAlgorithm() override { SignalAbort(); }
|
||||
void RunAbortAlgorithm() override;
|
||||
|
||||
private:
|
||||
~AbortSignal() = default;
|
||||
~AbortSignal();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "Fetch.h"
|
||||
|
||||
#include "js/Value.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
|
@ -81,12 +82,15 @@ void AbortStream(JSContext* aCx, JS::Handle<JSObject*> aStream,
|
|||
class AbortSignalMainThread final : public AbortSignalImpl {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(AbortSignalMainThread)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbortSignalMainThread)
|
||||
|
||||
explicit AbortSignalMainThread(bool aAborted) : AbortSignalImpl(aAborted) {}
|
||||
explicit AbortSignalMainThread(bool aAborted)
|
||||
: AbortSignalImpl(aAborted, JS::UndefinedHandleValue) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
private:
|
||||
~AbortSignalMainThread() = default;
|
||||
~AbortSignalMainThread() { mozilla::DropJSObjects(this); };
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(AbortSignalMainThread)
|
||||
|
@ -99,6 +103,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbortSignalMainThread)
|
|||
AbortSignalImpl::Traverse(static_cast<AbortSignalImpl*>(tmp), cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbortSignalMainThread)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReason)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortSignalMainThread)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
@ -212,7 +220,7 @@ NS_IMPL_ISUPPORTS0(AbortSignalProxy)
|
|||
NS_IMETHODIMP WorkerSignalFollower::AbortSignalProxyRunnable::Run() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AbortSignalImpl* signalImpl = mProxy->GetOrCreateSignalImplForMainThread();
|
||||
signalImpl->SignalAbort();
|
||||
signalImpl->SignalAbort(JS::UndefinedHandleValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "Request.h"
|
||||
|
||||
#include "js/Value.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
@ -63,7 +64,8 @@ Request::Request(nsIGlobalObject* aOwner, SafeRefPtr<InternalRequest> aRequest,
|
|||
if (aSignal) {
|
||||
// If we don't have a signal as argument, we will create it when required by
|
||||
// content, otherwise the Request's signal must follow what has been passed.
|
||||
mSignal = new AbortSignal(aOwner, aSignal->Aborted());
|
||||
JS::Rooted<JS::Value> reason(RootingCx(), aSignal->RawReason());
|
||||
mSignal = new AbortSignal(aOwner, aSignal->Aborted(), reason);
|
||||
if (!mSignal->Aborted()) {
|
||||
mSignal->Follow(aSignal);
|
||||
}
|
||||
|
@ -651,7 +653,7 @@ Headers* Request::Headers_() {
|
|||
|
||||
AbortSignal* Request::GetOrCreateSignal() {
|
||||
if (!mSignal) {
|
||||
mSignal = new AbortSignal(mOwner, false);
|
||||
mSignal = new AbortSignal(mOwner, false, JS::UndefinedHandleValue);
|
||||
}
|
||||
|
||||
return mSignal;
|
||||
|
|
|
@ -12,7 +12,7 @@ interface AbortController {
|
|||
[Throws]
|
||||
constructor();
|
||||
|
||||
readonly attribute AbortSignal signal;
|
||||
[SameObject] readonly attribute AbortSignal signal;
|
||||
|
||||
void abort();
|
||||
void abort(optional any reason);
|
||||
};
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
|
||||
[Exposed=(Window,Worker)]
|
||||
interface AbortSignal : EventTarget {
|
||||
[NewObject] static AbortSignal abort();
|
||||
[NewObject, Throws] static AbortSignal abort(optional any reason);
|
||||
|
||||
readonly attribute boolean aborted;
|
||||
readonly attribute any reason;
|
||||
|
||||
attribute EventHandler onabort;
|
||||
};
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
[event.any.html]
|
||||
[AbortController abort() should fire event synchronously]
|
||||
expected: FAIL
|
||||
|
||||
[AbortController abort(reason) should set signal.reason]
|
||||
expected: FAIL
|
||||
|
||||
[aborting AbortController without reason creates an "AbortError" DOMException]
|
||||
expected: FAIL
|
||||
|
||||
[AbortController abort(undefined) creates an "AbortError" DOMException]
|
||||
expected: FAIL
|
||||
|
||||
[static aborting signal should have right properties]
|
||||
expected: FAIL
|
||||
|
||||
[static aborting signal with reason should set signal.reason]
|
||||
expected: FAIL
|
||||
|
||||
[AbortController abort(null) should set signal.reason]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[event.any.worker.html]
|
||||
[AbortController abort() should fire event synchronously]
|
||||
expected: FAIL
|
||||
|
||||
[AbortController abort(reason) should set signal.reason]
|
||||
expected: FAIL
|
||||
|
||||
[aborting AbortController without reason creates an "AbortError" DOMException]
|
||||
expected: FAIL
|
||||
|
||||
[AbortController abort(undefined) creates an "AbortError" DOMException]
|
||||
expected: FAIL
|
||||
|
||||
[static aborting signal should have right properties]
|
||||
expected: FAIL
|
||||
|
||||
[static aborting signal with reason should set signal.reason]
|
||||
expected: FAIL
|
||||
|
||||
[AbortController abort(null) should set signal.reason]
|
||||
expected: FAIL
|
|
@ -139,4 +139,33 @@ test(t => {
|
|||
assert_equals(signal.reason, reason, "signal.reason");
|
||||
}, "static aborting signal with reason should set signal.reason");
|
||||
|
||||
test(t => {
|
||||
const signal = AbortSignal.abort();
|
||||
|
||||
assert_true(
|
||||
signal.reason instanceof DOMException,
|
||||
"signal.reason is a DOMException"
|
||||
);
|
||||
assert_equals(
|
||||
signal.reason,
|
||||
signal.reason,
|
||||
"signal.reason returns the same DOMException"
|
||||
);
|
||||
}, "AbortSignal.reason returns the same DOMException");
|
||||
|
||||
test(t => {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
|
||||
assert_true(
|
||||
controller.signal.reason instanceof DOMException,
|
||||
"signal.reason is a DOMException"
|
||||
);
|
||||
assert_equals(
|
||||
controller.signal.reason,
|
||||
controller.signal.reason,
|
||||
"signal.reason returns the same DOMException"
|
||||
);
|
||||
}, "AbortController.signal.reason returns the same DOMException");
|
||||
|
||||
done();
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE HTML>
|
||||
<meta charset=utf-8>
|
||||
<title>AbortSignal.reason constructor</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<iframe id="iframe"></iframe>
|
||||
<script>
|
||||
test(() => {
|
||||
const aborted = iframe.contentWindow.AbortSignal.abort();
|
||||
assert_equals(aborted.reason.constructor, iframe.contentWindow.DOMException, "DOMException is using the correct global");
|
||||
}, "AbortSignal.reason.constructor should be from iframe");
|
||||
</script>
|
Загрузка…
Ссылка в новой задаче