Skip to content

Commit

Permalink
Bug 1737771 - Implement AbortSignal.reason r=mgaudet,smaug
Browse files Browse the repository at this point in the history
  • Loading branch information
saschanaz committed Dec 13, 2021
1 parent aaa7b14 commit 712c0d5
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 81 deletions.
32 changes: 27 additions & 5 deletions dom/abort/AbortController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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) {
Expand All @@ -46,22 +63,27 @@ 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;
}

mAborted = true;

if (mSignal) {
mSignal->SignalAbort();
mSignal->SignalAbort(aReason);
} else {
mReason = aReason;
}
}

AbortController::~AbortController() { mozilla::DropJSObjects(this); }

} // namespace mozilla::dom
5 changes: 3 additions & 2 deletions dom/abort/AbortController.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 13 additions & 5 deletions dom/abort/AbortFollower.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#ifndef mozilla_dom_AbortFollower_h
#define mozilla_dom_AbortFollower_h

#include "jsapi.h"
#include "nsISupportsImpl.h"
#include "nsTObserverArray.h"

Expand Down Expand Up @@ -47,27 +48,34 @@ 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
// during corresponding cycle collection operations.
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
Expand Down
76 changes: 66 additions & 10 deletions dom/abort/AbortSignal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,47 @@

#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;
}

// Step 2.
mAborted = true;
mReason = aReason;

// Step 3.
// When there are multiple followers, the follower removal algorithm
Expand All @@ -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
// ----------------------------------------------------------------------------

Expand All @@ -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;
Expand All @@ -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
// ----------------------------------------------------------------------------

Expand Down
16 changes: 10 additions & 6 deletions dom/abort/AbortSignal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 12 additions & 4 deletions dom/fetch/Fetch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "Fetch.h"

#include "js/Value.h"
#include "mozilla/dom/Document.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsIGlobalObject.h"
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down
6 changes: 4 additions & 2 deletions dom/fetch/Request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "Request.h"

#include "js/Value.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsPIDOMWindow.h"
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 712c0d5

Please sign in to comment.