diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index 0c91bb7ed3a9..7bf0e75b80ae 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -115,11 +115,6 @@ private: NS_DECL_OWNINGTHREAD; }; -enum { - SLOT_PROMISE = 0, - SLOT_DATA -}; - /* * Utilities for thenable callbacks. * @@ -134,9 +129,9 @@ void LinkThenableCallables(JSContext* aCx, JS::Handle aResolveFunc, JS::Handle aRejectFunc) { - js::SetFunctionNativeReserved(aResolveFunc, SLOT_DATA, + js::SetFunctionNativeReserved(aResolveFunc, Promise::SLOT_DATA, JS::ObjectValue(*aRejectFunc)); - js::SetFunctionNativeReserved(aRejectFunc, SLOT_DATA, + js::SetFunctionNativeReserved(aRejectFunc, Promise::SLOT_DATA, JS::ObjectValue(*aResolveFunc)); } @@ -147,18 +142,22 @@ LinkThenableCallables(JSContext* aCx, JS::Handle aResolveFunc, bool MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle aFunc) { - JS::Value otherFuncVal = js::GetFunctionNativeReserved(aFunc, SLOT_DATA); + JS::Value otherFuncVal = + js::GetFunctionNativeReserved(aFunc, Promise::SLOT_DATA); if (!otherFuncVal.isObject()) { return false; } JSObject* otherFuncObj = &otherFuncVal.toObject(); - MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, SLOT_DATA).isObject()); + MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, + Promise::SLOT_DATA).isObject()); // Break both references. - js::SetFunctionNativeReserved(aFunc, SLOT_DATA, JS::UndefinedValue()); - js::SetFunctionNativeReserved(otherFuncObj, SLOT_DATA, JS::UndefinedValue()); + js::SetFunctionNativeReserved(aFunc, Promise::SLOT_DATA, + JS::UndefinedValue()); + js::SetFunctionNativeReserved(otherFuncObj, Promise::SLOT_DATA, + JS::UndefinedValue()); return true; } @@ -166,7 +165,8 @@ MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle aFunc) Promise* GetPromise(JSContext* aCx, JS::Handle aFunc) { - JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, SLOT_PROMISE); + JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, + Promise::SLOT_PROMISE); MOZ_ASSERT(promiseVal.isObject()); @@ -700,6 +700,8 @@ Promise::JSCallbackThenableRejecter(JSContext* aCx, /* static */ JSObject* Promise::CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask) { + // If this function ever changes, make sure to update + // WrapperPromiseCallback::GetDependentPromise. JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback, 1 /* nargs */, 0 /* flags */, nullptr); diff --git a/dom/promise/Promise.h b/dom/promise/Promise.h index ab4b71f6b99a..a47fe406835a 100644 --- a/dom/promise/Promise.h +++ b/dom/promise/Promise.h @@ -214,6 +214,11 @@ public: static void DispatchToMicroTask(nsIRunnable* aRunnable); + enum JSCallbackSlots { + SLOT_PROMISE = 0, + SLOT_DATA + }; + protected: struct PromiseCapability; diff --git a/dom/promise/PromiseCallback.cpp b/dom/promise/PromiseCallback.cpp index 07dbcb6498e1..e8506cac0a75 100644 --- a/dom/promise/PromiseCallback.cpp +++ b/dom/promise/PromiseCallback.cpp @@ -9,6 +9,8 @@ #include "mozilla/dom/PromiseNativeHandler.h" #include "jsapi.h" +#include "jsfriendapi.h" +#include "jswrapper.h" namespace mozilla { namespace dom { @@ -309,6 +311,32 @@ WrapperPromiseCallback::Call(JSContext* aCx, return NS_OK; } +Promise* +WrapperPromiseCallback::GetDependentPromise() +{ + // Per spec, various algorithms like all() and race() are actually implemented + // in terms of calling then() but passing it the resolve/reject functions that + // are passed as arguments to function passed to the Promise constructor. + // That will cause the promise in question to hold on to a + // WrapperPromiseCallback, but the dependent promise should really be the one + // whose constructor those functions came from, not the about-to-be-ignored + // return value of "then". So try to determine whether we're in that case and + // if so go ahead and dig the dependent promise out of the function we have. + JSObject* callable = mCallback->Callable(); + // Unwrap it, in case it's a cross-compartment wrapper. Our caller here is + // system, so it's really ok to just go and unwrap. + callable = js::UncheckedUnwrap(callable); + if (JS_IsNativeFunction(callable, Promise::JSCallback)) { + JS::Value promiseVal = + js::GetFunctionNativeReserved(callable, Promise::SLOT_PROMISE); + Promise* promise; + UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise); + return promise; + } + + return mNextPromise; +} + // NativePromiseCallback NS_IMPL_CYCLE_COLLECTION_INHERITED(NativePromiseCallback, diff --git a/dom/promise/PromiseCallback.h b/dom/promise/PromiseCallback.h index 767c6da7bb35..1f1f6700a783 100644 --- a/dom/promise/PromiseCallback.h +++ b/dom/promise/PromiseCallback.h @@ -57,10 +57,7 @@ public: nsresult Call(JSContext* aCx, JS::Handle aValue) override; - Promise* GetDependentPromise() override - { - return mNextPromise; - } + Promise* GetDependentPromise() override; WrapperPromiseCallback(Promise* aNextPromise, JS::Handle aGlobal, AnyCallback* aCallback);