From fb96b43b5f6188233710c5f8c9c9e46baa9cb369 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Thu, 19 Dec 2019 01:35:24 +0100 Subject: Issue #1322 - Part 1: Remove the DOM Promise guts. This removes all the parts guarded by SPIDERMONKEY_PROMISE --- dom/bindings/BindingUtils.cpp | 37 - dom/bindings/Codegen.py | 40 +- dom/bindings/ToJSValue.cpp | 4 - dom/bindings/ToJSValue.h | 2 - dom/promise/Promise.cpp | 2409 +----------------------------------- dom/promise/Promise.h | 315 ----- dom/promise/PromiseCallback.cpp | 571 --------- dom/promise/PromiseCallback.h | 203 --- dom/promise/PromiseDebugging.cpp | 221 ---- dom/promise/PromiseDebugging.h | 20 - dom/promise/moz.build | 3 +- dom/webidl/Promise.webidl | 48 +- dom/webidl/PromiseDebugging.webidl | 44 - dom/webidl/moz.build | 4 +- 14 files changed, 61 insertions(+), 3860 deletions(-) delete mode 100644 dom/promise/PromiseCallback.cpp delete mode 100644 dom/promise/PromiseCallback.h (limited to 'dom') diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 6e0db29b1..b244d4d2a 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -2976,42 +2976,6 @@ ConvertExceptionToPromise(JSContext* cx, JSObject* promiseScope, JS::MutableHandle rval) { -#ifndef SPIDERMONKEY_PROMISE - GlobalObject global(cx, promiseScope); - if (global.Failed()) { - return false; - } - - JS::Rooted exn(cx); - if (!JS_GetPendingException(cx, &exn)) { - // This is very important: if there is no pending exception here but we're - // ending up in this code, that means the callee threw an uncatchable - // exception. Just propagate that out as-is. - return false; - } - - JS_ClearPendingException(cx); - - nsCOMPtr globalObj = - do_QueryInterface(global.GetAsSupports()); - if (!globalObj) { - ErrorResult rv; - rv.Throw(NS_ERROR_UNEXPECTED); - return !rv.MaybeSetPendingException(cx); - } - - ErrorResult rv; - RefPtr promise = Promise::Reject(globalObj, cx, exn, rv); - if (rv.MaybeSetPendingException(cx)) { - // We just give up. We put the exception from the ErrorResult on - // the JSContext just to make sure to not leak memory on the - // ErrorResult, but now just put the original exception back. - JS_SetPendingException(cx, exn); - return false; - } - - return GetOrCreateDOMReflector(cx, promise, rval); -#else // SPIDERMONKEY_PROMISE { JSAutoCompartment ac(cx, promiseScope); @@ -3037,7 +3001,6 @@ ConvertExceptionToPromise(JSContext* cx, // Now make sure we rewrap promise back into the compartment we want return JS_WrapValue(cx, rval); -#endif // SPIDERMONKEY_PROMISE } /* static */ diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 924241aa0..9cde82df9 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3052,23 +3052,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): else: unforgeableHolderSetup = None - if self.descriptor.name == "Promise": - speciesSetup = CGGeneric(fill( - """ - #ifndef SPIDERMONKEY_PROMISE - JS::Rooted promiseConstructor(aCx, *interfaceCache); - JS::Rooted species(aCx, - SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species))); - if (!JS_DefinePropertyById(aCx, promiseConstructor, species, JS::UndefinedHandleValue, - JSPROP_SHARED, Promise::PromiseSpecies, nullptr)) { - $*{failureCode} - } - #endif // SPIDERMONKEY_PROMISE - """, - failureCode=failureCode)) - else: - speciesSetup = None - if (self.descriptor.interface.isOnGlobalProtoChain() and needInterfacePrototypeObject): makeProtoPrototypeImmutable = CGGeneric(fill( @@ -3094,7 +3077,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): return CGList( [getParentProto, getConstructorProto, initIds, prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup, - speciesSetup, makeProtoPrototypeImmutable], + makeProtoPrototypeImmutable], "\n").define() @@ -5414,7 +5397,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, $*{exceptionCode} } binding_detail::FastErrorResult promiseRv; - #ifdef SPIDERMONKEY_PROMISE nsCOMPtr global = do_QueryInterface(promiseGlobal.GetAsSupports()); if (!global) { @@ -5427,26 +5409,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if (promiseRv.MaybeSetPendingException(cx)) { $*{exceptionCode} } - #else - JS::Handle promiseCtor = - PromiseBinding::GetConstructorObjectHandle(cx); - if (!promiseCtor) { - $*{exceptionCode} - } - JS::Rooted resolveThisv(cx, JS::ObjectValue(*promiseCtor)); - JS::Rooted resolveResult(cx); - Promise::Resolve(promiseGlobal, resolveThisv, valueToResolve, - &resolveResult, promiseRv); - if (promiseRv.MaybeSetPendingException(cx)) { - $*{exceptionCode} - } - nsresult unwrapRv = UNWRAP_OBJECT(Promise, &resolveResult.toObject(), $${declName}); - if (NS_FAILED(unwrapRv)) { // Quite odd - promiseRv.Throw(unwrapRv); - promiseRv.MaybeSetPendingException(cx); - $*{exceptionCode} - } - #endif // SPIDERMONKEY_PROMISE } """, getPromiseGlobal=getPromiseGlobal, diff --git a/dom/bindings/ToJSValue.cpp b/dom/bindings/ToJSValue.cpp index d84428fb3..19219f432 100644 --- a/dom/bindings/ToJSValue.cpp +++ b/dom/bindings/ToJSValue.cpp @@ -7,9 +7,7 @@ #include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/DOMException.h" #include "mozilla/dom/Exceptions.h" -#ifdef SPIDERMONKEY_PROMISE #include "mozilla/dom/Promise.h" -#endif // SPIDERMONKEY_PROMISE #include "nsAString.h" #include "nsContentUtils.h" #include "nsStringBuffer.h" @@ -66,7 +64,6 @@ ToJSValue(JSContext* aCx, return true; } -#ifdef SPIDERMONKEY_PROMISE bool ToJSValue(JSContext* aCx, Promise& aArgument, JS::MutableHandle aValue) @@ -74,7 +71,6 @@ ToJSValue(JSContext* aCx, Promise& aArgument, aValue.setObject(*aArgument.PromiseObj()); return true; } -#endif // SPIDERMONKEY_PROMISE } // namespace dom } // namespace mozilla diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h index 2021c0b4c..76e91c7bc 100644 --- a/dom/bindings/ToJSValue.h +++ b/dom/bindings/ToJSValue.h @@ -306,13 +306,11 @@ ToJSValue(JSContext* aCx, return ToJSValue(aCx, *aArgument, aValue); } -#ifdef SPIDERMONKEY_PROMISE // Accept Promise objects, which need special handling. MOZ_MUST_USE bool ToJSValue(JSContext* aCx, Promise& aArgument, JS::MutableHandle aValue); -#endif // SPIDERMONKEY_PROMISE // Accept arrays of other things we accept template diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index f636a9101..429b68529 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -30,7 +30,6 @@ #include "nsJSPrincipals.h" #include "nsJSUtils.h" #include "nsPIDOMWindow.h" -#include "PromiseCallback.h" #include "PromiseDebugging.h" #include "PromiseNativeHandler.h" #include "PromiseWorkerProxy.h" @@ -49,456 +48,45 @@ Atomic gIDGenerator(0); using namespace workers; -#ifndef SPIDERMONKEY_PROMISE -// This class processes the promise's callbacks with promise's result. -class PromiseReactionJob final : public Runnable -{ -public: - PromiseReactionJob(Promise* aPromise, - PromiseCallback* aCallback, - const JS::Value& aValue) - : mPromise(aPromise) - , mCallback(aCallback) - , mValue(CycleCollectedJSContext::Get()->Context(), aValue) - { - MOZ_ASSERT(aPromise); - MOZ_ASSERT(aCallback); - MOZ_COUNT_CTOR(PromiseReactionJob); - } - - virtual - ~PromiseReactionJob() - { - NS_ASSERT_OWNINGTHREAD(PromiseReactionJob); - MOZ_COUNT_DTOR(PromiseReactionJob); - } - -protected: - NS_IMETHOD - Run() override - { - NS_ASSERT_OWNINGTHREAD(PromiseReactionJob); - - MOZ_ASSERT(mPromise->GetWrapper()); // It was preserved! - - AutoJSAPI jsapi; - if (!jsapi.Init(mPromise->GetWrapper())) { - return NS_ERROR_FAILURE; - } - JSContext* cx = jsapi.cx(); - - JS::Rooted value(cx, mValue); - if (!MaybeWrapValue(cx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - JS_ClearPendingException(cx); - return NS_OK; - } - - JS::Rooted asyncStack(cx, mPromise->mAllocationStack); - - { - Maybe sas; - if (asyncStack) { - sas.emplace(cx, asyncStack, "Promise"); - } - mCallback->Call(cx, value); - } - - return NS_OK; - } - -private: - RefPtr mPromise; - RefPtr mCallback; - JS::PersistentRooted mValue; - NS_DECL_OWNINGTHREAD; -}; - -/* - * Utilities for thenable callbacks. - * - * A thenable is a { then: function(resolve, reject) { } }. - * `then` is called with a resolve and reject callback pair. - * Since only one of these should be called at most once (first call wins), the - * two keep a reference to each other in SLOT_DATA. When either of them is - * called, the references are cleared. Further calls are ignored. - */ -namespace { -void -LinkThenableCallables(JSContext* aCx, JS::Handle aResolveFunc, - JS::Handle aRejectFunc) -{ - js::SetFunctionNativeReserved(aResolveFunc, Promise::SLOT_DATA, - JS::ObjectValue(*aRejectFunc)); - js::SetFunctionNativeReserved(aRejectFunc, Promise::SLOT_DATA, - JS::ObjectValue(*aResolveFunc)); -} - -/* - * Returns false if callback was already called before, otherwise breaks the - * links and returns true. - */ -bool -MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle aFunc) -{ - JS::Value otherFuncVal = - js::GetFunctionNativeReserved(aFunc, Promise::SLOT_DATA); - - if (!otherFuncVal.isObject()) { - return false; - } - - JSObject* otherFuncObj = &otherFuncVal.toObject(); - MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, - Promise::SLOT_DATA).isObject()); - - // Break both references. - js::SetFunctionNativeReserved(aFunc, Promise::SLOT_DATA, - JS::UndefinedValue()); - js::SetFunctionNativeReserved(otherFuncObj, Promise::SLOT_DATA, - JS::UndefinedValue()); - - return true; -} - -Promise* -GetPromise(JSContext* aCx, JS::Handle aFunc) -{ - JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, - Promise::SLOT_PROMISE); - - MOZ_ASSERT(promiseVal.isObject()); - - Promise* promise; - UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise); - return promise; -} -} // namespace - -// Runnable to resolve thenables. -// Equivalent to the specification's ResolvePromiseViaThenableTask. -class PromiseResolveThenableJob final : public Runnable -{ -public: - PromiseResolveThenableJob(Promise* aPromise, - JS::Handle aThenable, - PromiseInit* aThen) - : mPromise(aPromise) - , mThenable(CycleCollectedJSContext::Get()->Context(), aThenable) - , mThen(aThen) - { - MOZ_ASSERT(aPromise); - MOZ_COUNT_CTOR(PromiseResolveThenableJob); - } - - virtual - ~PromiseResolveThenableJob() - { - NS_ASSERT_OWNINGTHREAD(PromiseResolveThenableJob); - MOZ_COUNT_DTOR(PromiseResolveThenableJob); - } - -protected: - NS_IMETHOD - Run() override - { - NS_ASSERT_OWNINGTHREAD(PromiseResolveThenableJob); - - MOZ_ASSERT(mPromise->GetWrapper()); // It was preserved! - - AutoJSAPI jsapi; - // If we ever change which compartment we're working in here, make sure to - // fix the fast-path for resolved-with-a-Promise in ResolveInternal. - if (!jsapi.Init(mPromise->GetWrapper())) { - return NS_ERROR_FAILURE; - } - JSContext* cx = jsapi.cx(); - - JS::Rooted resolveFunc(cx, - mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Resolve)); - - if (!resolveFunc) { - mPromise->HandleException(cx); - return NS_OK; - } - - JS::Rooted rejectFunc(cx, - mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Reject)); - if (!rejectFunc) { - mPromise->HandleException(cx); - return NS_OK; - } - - LinkThenableCallables(cx, resolveFunc, rejectFunc); - - ErrorResult rv; - - JS::Rooted rootedThenable(cx, mThenable); - - mThen->Call(rootedThenable, resolveFunc, rejectFunc, rv, - "promise thenable", CallbackObject::eRethrowExceptions, - mPromise->Compartment()); - - rv.WouldReportJSException(); - if (rv.Failed()) { - JS::Rooted exn(cx); - { // Scope for JSAutoCompartment - - // Convert the ErrorResult to a JS exception object that we can reject - // ourselves with. This will be exactly the exception that would get - // thrown from a binding method whose ErrorResult ended up with - // whatever is on "rv" right now. - JSAutoCompartment ac(cx, mPromise->GlobalJSObject()); - DebugOnly conversionResult = ToJSValue(cx, rv, &exn); - MOZ_ASSERT(conversionResult); - } - - bool couldMarkAsCalled = MarkAsCalledIfNotCalledBefore(cx, resolveFunc); - - // If we could mark as called, neither of the callbacks had been called - // when the exception was thrown. So we can reject the Promise. - if (couldMarkAsCalled) { - bool ok = JS_WrapValue(cx, &exn); - MOZ_ASSERT(ok); - if (!ok) { - NS_WARNING("Failed to wrap value into the right compartment."); - } - - mPromise->RejectInternal(cx, exn); - } - // At least one of resolveFunc or rejectFunc have been called, so ignore - // the exception. FIXME(nsm): This should be reported to the error - // console though, for debugging. - } - - return rv.StealNSResult(); - } - -private: - RefPtr mPromise; - JS::PersistentRooted mThenable; - RefPtr mThen; - NS_DECL_OWNINGTHREAD; -}; - -// A struct implementing -// . -// While the spec holds on to these in some places, in practice those places -// don't actually need everything from this struct, so we explicitly grab -// members from it as needed in those situations. That allows us to make this a -// stack-only struct and keep the rooting simple. -// -// We also add an optimization for the (common) case when we discover that the -// Promise constructor we're supposed to use is in fact the canonical Promise -// constructor. In that case we will just set mNativePromise in our -// PromiseCapability and not set mPromise/mResolve/mReject; the correct -// callbacks will be the standard Promise ones, and we don't really want to -// synthesize JSFunctions for them in that situation. -struct MOZ_STACK_CLASS Promise::PromiseCapability -{ - explicit PromiseCapability(JSContext* aCx) - : mPromise(aCx) - , mResolve(aCx) - , mReject(aCx) - {} - - // Take an exception on aCx and try to convert it into a promise rejection. - // Note that this can result in a new exception being thrown on aCx, or an - // exception getting thrown on aRv. On entry to this method, aRv is assumed - // to not be a failure. This should only be called if NewPromiseCapability - // succeeded on this PromiseCapability. - void RejectWithException(JSContext* aCx, ErrorResult& aRv); - - // Return a JS::Value representing the promise. This should only be called if - // NewPromiseCapability succeeded on this PromiseCapability. It makes no - // guarantees about compartments (e.g. in the mNativePromise case it's in the - // compartment of the reflector, but in the mPromise case it might be in the - // compartment of some cross-compartment wrapper for a reflector). - JS::Value PromiseValue() const; - - // All the JS::Value fields of this struct are actually objects, but for our - // purposes it's simpler to store them as JS::Value. - - // [[Promise]]. - JS::Rooted mPromise; - // [[Resolve]]. Value in the context compartment. - JS::Rooted mResolve; - // [[Reject]]. Value in the context compartment. - JS::Rooted mReject; - // If mNativePromise is non-null, we should use it, not mPromise. - RefPtr mNativePromise; - -private: - // We don't want to allow creation of temporaries of this type, ever. - PromiseCapability(const PromiseCapability&) = delete; - PromiseCapability(PromiseCapability&&) = delete; -}; - -void -Promise::PromiseCapability::RejectWithException(JSContext* aCx, - ErrorResult& aRv) -{ - // This method basically implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-ifabruptrejectpromise - // or at least the parts of it that happen if we have an abrupt completion. - - MOZ_ASSERT(!aRv.Failed()); - MOZ_ASSERT(mNativePromise || mPromise, - "NewPromiseCapability didn't succeed"); - - JS::Rooted exn(aCx); - if (!JS_GetPendingException(aCx, &exn)) { - // This is an uncatchable exception, so can't be converted into a rejection. - // Just rethrow that on aRv. - aRv.ThrowUncatchableException(); - return; - } - - JS_ClearPendingException(aCx); - - // If we have a native promise, just reject it without trying to call out into - // JS. - if (mNativePromise) { - mNativePromise->MaybeRejectInternal(aCx, exn); - return; - } - - JS::Rooted ignored(aCx); - if (!JS::Call(aCx, JS::UndefinedHandleValue, mReject, JS::HandleValueArray(exn), - &ignored)) { - aRv.NoteJSContextException(aCx); - } -} - -JS::Value -Promise::PromiseCapability::PromiseValue() const -{ - MOZ_ASSERT(mNativePromise || mPromise, - "NewPromiseCapability didn't succeed"); - - if (mNativePromise) { - return JS::ObjectValue(*mNativePromise->GetWrapper()); - } - - return JS::ObjectValue(*mPromise); -} - -#endif // SPIDERMONKEY_PROMISE - // Promise NS_IMPL_CYCLE_COLLECTION_CLASS(Promise) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise) -#ifndef SPIDERMONKEY_PROMISE -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - tmp->MaybeReportRejectedOnce(); -#else - tmp->mResult = JS::UndefinedValue(); -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) -#endif // SPIDERMONKEY_PROMISE NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) -#ifndef SPIDERMONKEY_PROMISE - NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks) - NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER -#else // SPIDERMONKEY_PROMISE tmp->mPromiseObj = nullptr; -#endif // SPIDERMONKEY_PROMISE NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) -#ifndef SPIDERMONKEY_PROMISE - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks) -#endif // SPIDERMONKEY_PROMISE NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise) -#ifndef SPIDERMONKEY_PROMISE - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAllocationStack) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRejectionStack) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFullfillmentStack) - NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER -#else // SPIDERMONKEY_PROMISE NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPromiseObj); -#endif // SPIDERMONKEY_PROMISE NS_IMPL_CYCLE_COLLECTION_TRACE_END -#ifndef SPIDERMONKEY_PROMISE -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Promise) - if (tmp->IsBlack()) { - tmp->mResult.exposeToActiveJS(); - tmp->mAllocationStack.exposeToActiveJS(); - tmp->mRejectionStack.exposeToActiveJS(); - tmp->mFullfillmentStack.exposeToActiveJS(); - return true; - } -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END - -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Promise) - return tmp->IsBlackAndDoesNotNeedTracing(tmp); -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END - -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Promise) - return tmp->IsBlack(); -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END -#endif // SPIDERMONKEY_PROMISE - NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise) NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Promise) -#ifndef SPIDERMONKEY_PROMISE - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY -#endif // SPIDERMONKEY_PROMISE NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY(Promise) NS_INTERFACE_MAP_END Promise::Promise(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) -#ifndef SPIDERMONKEY_PROMISE - , mResult(JS::UndefinedValue()) - , mAllocationStack(nullptr) - , mRejectionStack(nullptr) - , mFullfillmentStack(nullptr) - , mState(Pending) -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - , mHadRejectCallback(false) -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - , mTaskPending(false) - , mResolvePending(false) - , mIsLastInChain(true) - , mWasNotifiedAsUncaught(false) - , mID(0) -#else // SPIDERMONKEY_PROMISE , mPromiseObj(nullptr) -#endif // SPIDERMONKEY_PROMISE { MOZ_ASSERT(mGlobal); mozilla::HoldJSObjects(this); - -#ifndef SPIDERMONKEY_PROMISE - mCreationTimestamp = TimeStamp::Now(); -#endif // SPIDERMONKEY_PROMISE } Promise::~Promise() { -#ifndef SPIDERMONKEY_PROMISE -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - MaybeReportRejectedOnce(); -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) -#endif // SPIDERMONKEY_PROMISE mozilla::DropJSObjects(this); } -#ifdef SPIDERMONKEY_PROMISE - bool Promise::WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aWrapper) @@ -884,8 +472,7 @@ Promise::HandleException(JSContext* aCx) JS::Rooted exn(aCx); if (JS_GetPendingException(aCx, &exn)) { JS_ClearPendingException(aCx); - // This is only called from MaybeSomething, so it's OK to MaybeReject here, - // unlike in the version that's used when !SPIDERMONKEY_PROMISE. + // This is only called from MaybeSomething, so it's OK to MaybeReject here. MaybeReject(aCx, exn); } } @@ -902,80 +489,6 @@ Promise::CreateFromExisting(nsIGlobalObject* aGlobal, return p.forget(); } -#else // SPIDERMONKEY_PROMISE - -JSObject* -Promise::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - return PromiseBinding::Wrap(aCx, this, aGivenProto); -} - -already_AddRefed -Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv, - JS::Handle aDesiredProto) -{ - if (!aGlobal) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - RefPtr p = new Promise(aGlobal); - p->CreateWrapper(aDesiredProto, aRv); - if (aRv.Failed()) { - return nullptr; - } - return p.forget(); -} - -void -Promise::CreateWrapper(JS::Handle aDesiredProto, ErrorResult& aRv) -{ - AutoJSAPI jsapi; - if (!jsapi.Init(mGlobal)) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - JSContext* cx = jsapi.cx(); - - JS::Rooted wrapper(cx); - if (!GetOrCreateDOMReflector(cx, this, &wrapper, aDesiredProto)) { - JS_ClearPendingException(cx); - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return; - } - - dom::PreserveWrapper(this); - - // Now grab our allocation stack - if (!CaptureStack(cx, mAllocationStack)) { - JS_ClearPendingException(cx); - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return; - } - - JS::RootedObject obj(cx, &wrapper.toObject()); - JS::dbg::onNewPromise(cx, obj); -} - -void -Promise::MaybeResolve(JSContext* aCx, - JS::Handle aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - MaybeResolveInternal(aCx, aValue); -} - -void -Promise::MaybeReject(JSContext* aCx, - JS::Handle aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - MaybeRejectInternal(aCx, aValue); -} - -#endif // SPIDERMONKEY_PROMISE - void Promise::MaybeResolveWithUndefined() { @@ -999,7 +512,6 @@ Promise::MaybeRejectWithUndefined() MaybeSomething(JS::UndefinedHandleValue, &Promise::MaybeReject); } -#ifdef SPIDERMONKEY_PROMISE void Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise) { @@ -1026,7 +538,6 @@ Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise) // Now post an event to do the real reporting async NS_DispatchToMainThread(new AsyncErrorReporter(xpcReport)); } -#endif // defined(SPIDERMONKEY_PROMISE) bool Promise::PerformMicroTaskCheckpoint() @@ -1132,1870 +643,78 @@ Promise::PerformWorkerDebuggerMicroTaskCheckpoint() } } -#ifndef SPIDERMONKEY_PROMISE - -/* static */ bool -Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp) +JSObject* +Promise::GlobalJSObject() const { - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); + return mGlobal->GetGlobalJSObject(); +} - JS::Rooted v(aCx, - js::GetFunctionNativeReserved(&args.callee(), - SLOT_PROMISE)); - MOZ_ASSERT(v.isObject()); +JSCompartment* +Promise::Compartment() const +{ + return js::GetObjectCompartment(GlobalJSObject()); +} - Promise* promise; - if (NS_FAILED(UNWRAP_OBJECT(Promise, &v.toObject(), promise))) { - return Throw(aCx, NS_ERROR_UNEXPECTED); +// A WorkerRunnable to resolve/reject the Promise on the worker thread. +// Calling thread MUST hold PromiseWorkerProxy's mutex before creating this. +class PromiseWorkerProxyRunnable : public WorkerRunnable +{ +public: + PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy, + PromiseWorkerProxy::RunCallbackFunc aFunc) + : WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(), + WorkerThreadUnchangedBusyCount) + , mPromiseWorkerProxy(aPromiseWorkerProxy) + , mFunc(aFunc) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mPromiseWorkerProxy); } - v = js::GetFunctionNativeReserved(&args.callee(), SLOT_DATA); - PromiseCallback::Task task = static_cast(v.toInt32()); + virtual bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate); + + MOZ_ASSERT(mPromiseWorkerProxy); + RefPtr workerPromise = mPromiseWorkerProxy->WorkerPromise(); - if (task == PromiseCallback::Resolve) { - if (!promise->CaptureStack(aCx, promise->mFullfillmentStack)) { - return false; - } - promise->MaybeResolveInternal(aCx, args.get(0)); - } else { - promise->MaybeRejectInternal(aCx, args.get(0)); - if (!promise->CaptureStack(aCx, promise->mRejectionStack)) { + // Here we convert the buffer to a JS::Value. + JS::Rooted value(aCx); + if (!mPromiseWorkerProxy->Read(aCx, &value)) { + JS_ClearPendingException(aCx); return false; } - } - args.rval().setUndefined(); - return true; -} + (workerPromise->*mFunc)(aCx, value); -/* - * Common bits of (JSCallbackThenableResolver/JSCallbackThenableRejecter). - * Resolves/rejects the Promise if it is ok to do so, based on whether either of - * the callbacks have been called before or not. - */ -/* static */ bool -Promise::ThenableResolverCommon(JSContext* aCx, uint32_t aTask, - unsigned aArgc, JS::Value* aVp) -{ - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); - JS::Rooted thisFunc(aCx, &args.callee()); - if (!MarkAsCalledIfNotCalledBefore(aCx, thisFunc)) { - // A function from this pair has been called before. - args.rval().setUndefined(); + // Release the Promise because it has been resolved/rejected for sure. + mPromiseWorkerProxy->CleanUp(); return true; } - Promise* promise = GetPromise(aCx, thisFunc); - MOZ_ASSERT(promise); - - if (aTask == PromiseCallback::Resolve) { - promise->ResolveInternal(aCx, args.get(0)); - } else { - promise->RejectInternal(aCx, args.get(0)); - } +protected: + ~PromiseWorkerProxyRunnable() {} - args.rval().setUndefined(); - return true; -} +private: + RefPtr mPromiseWorkerProxy; -/* static */ bool -Promise::JSCallbackThenableResolver(JSContext* aCx, - unsigned aArgc, JS::Value* aVp) -{ - return ThenableResolverCommon(aCx, PromiseCallback::Resolve, aArgc, aVp); -} + // Function pointer for calling Promise::{ResolveInternal,RejectInternal}. + PromiseWorkerProxy::RunCallbackFunc mFunc; +}; -/* static */ bool -Promise::JSCallbackThenableRejecter(JSContext* aCx, - unsigned aArgc, JS::Value* aVp) +class PromiseWorkerHolder final : public WorkerHolder { - return ThenableResolverCommon(aCx, PromiseCallback::Reject, aArgc, aVp); -} + // RawPointer because this proxy keeps alive the holder. + PromiseWorkerProxy* mProxy; -/* 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); - if (!func) { - return nullptr; - } - - JS::Rooted obj(aCx, JS_GetFunctionObject(func)); - - JS::Rooted promiseObj(aCx); - if (!dom::GetOrCreateDOMReflector(aCx, aPromise, &promiseObj)) { - return nullptr; - } - - JS::ExposeValueToActiveJS(promiseObj); - js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj); - js::SetFunctionNativeReserved(obj, SLOT_DATA, JS::Int32Value(aTask)); - - return obj; -} - -/* static */ JSObject* -Promise::CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask) -{ - JSNative whichFunc = - aTask == PromiseCallback::Resolve ? JSCallbackThenableResolver : - JSCallbackThenableRejecter ; - - JSFunction* func = js::NewFunctionWithReserved(aCx, whichFunc, - 1 /* nargs */, 0 /* flags */, - nullptr); - if (!func) { - return nullptr; - } - - JS::Rooted obj(aCx, JS_GetFunctionObject(func)); - - JS::Rooted promiseObj(aCx); - if (!dom::GetOrCreateDOMReflector(aCx, aPromise, &promiseObj)) { - return nullptr; - } - - JS::ExposeValueToActiveJS(promiseObj); - js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj); - - return obj; -} - -/* static */ already_AddRefed -Promise::Constructor(const GlobalObject& aGlobal, PromiseInit& aInit, - ErrorResult& aRv, JS::Handle aDesiredProto) -{ - nsCOMPtr global; - global = do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - RefPtr promise = Create(global, aRv, aDesiredProto); - if (aRv.Failed()) { - return nullptr; - } - - promise->CallInitFunction(aGlobal, aInit, aRv); - if (aRv.Failed()) { - return nullptr; - } - - return promise.forget(); -} - -void -Promise::CallInitFunction(const GlobalObject& aGlobal, - PromiseInit& aInit, ErrorResult& aRv) -{ - JSContext* cx = aGlobal.Context(); - - JS::Rooted resolveFunc(cx, - CreateFunction(cx, this, - PromiseCallback::Resolve)); - if (!resolveFunc) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - JS::Rooted rejectFunc(cx, - CreateFunction(cx, this, - PromiseCallback::Reject)); - if (!rejectFunc) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - aInit.Call(resolveFunc, rejectFunc, aRv, "promise initializer", - CallbackObject::eRethrowExceptions, Compartment()); - aRv.WouldReportJSException(); - - if (aRv.Failed()) { - if (aRv.IsUncatchableException()) { - // Just propagate this to the caller. - return; - } - - // There are two possibilities here. Either we've got a rethrown exception, - // or we reported that already and synthesized a generic NS_ERROR_FAILURE on - // the ErrorResult. In the former case, it doesn't much matter how we get - // the exception JS::Value from the ErrorResult to us, since we'll just end - // up wrapping it into the right compartment as needed if we hand it to - // someone. But in the latter case we have to ensure that the new exception - // object we create is created in our reflector compartment, not in our - // current compartment, because in the case when we're a Promise constructor - // called over Xrays creating it in the current compartment would mean - // rejecting with a value that can't be accessed by code that can call - // then() on this Promise. - // - // Luckily, MaybeReject(aRv) does exactly what we want here: it enters our - // reflector compartment before trying to produce a JS::Value from the - // ErrorResult. - MaybeReject(aRv); - } -} - -#define GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT 0 -#define GET_CAPABILITIES_EXECUTOR_REJECT_SLOT 1 - -namespace { -bool -GetCapabilitiesExecutor(JSContext* aCx, unsigned aArgc, JS::Value* aVp) -{ - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-getcapabilitiesexecutor-functions - // except we store the [[Resolve]] and [[Reject]] in our own internal slots, - // not in a PromiseCapability. The PromiseCapability will then read them from - // us. - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); - - // Step 1 is an assert. - - // Step 2 doesn't need to be done, because it's just giving a name to the - // PromiseCapability record which is supposed to be stored in an internal - // slot. But we don't store that at all, per the comment above; we just - // directly store its [[Resolve]] and [[Reject]] members. - - // Steps 3 and 4. - if (!js::GetFunctionNativeReserved(&args.callee(), - GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT).isUndefined() || - !js::GetFunctionNativeReserved(&args.callee(), - GET_CAPABILITIES_EXECUTOR_REJECT_SLOT).isUndefined()) { - ErrorResult rv; - rv.ThrowTypeError(); - return !rv.MaybeSetPendingException(aCx); - } - - // Step 5. - js::SetFunctionNativeReserved(&args.callee(), - GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT, - args.get(0)); - - // Step 6. - js::SetFunctionNativeReserved(&args.callee(), - GET_CAPABILITIES_EXECUTOR_REJECT_SLOT, - args.get(1)); - - // Step 7. - args.rval().setUndefined(); - return true; -} -} // anonymous namespace - -/* static */ void -Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal, - JS::Handle aConstructor, - bool aForceCallbackCreation, - PromiseCapability& aCapability, - ErrorResult& aRv) -{ - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-newpromisecapability - - if (!aConstructor.isObject() || - !JS::IsConstructor(&aConstructor.toObject())) { - aRv.ThrowTypeError(); - return; - } - - // Step 2 is a note. - // Step 3 is already done because we got the PromiseCapability passed in. - - // Optimization: Check whether constructor is in fact the canonical - // Promise constructor for aGlobal. - JS::Rooted global(aCx, aGlobal->GetGlobalJSObject()); - { - // Scope for the JSAutoCompartment, since we need to enter the compartment - // of global to get constructors from it. Save the compartment we used to - // be in, though; we'll need it later. - JS::Rooted callerGlobal(aCx, JS::CurrentGlobalOrNull(aCx)); - JSAutoCompartment ac(aCx, global); - - // Now wrap aConstructor into the compartment of aGlobal, so comparing it to - // the canonical Promise for that compartment actually makes sense. - JS::Rooted constructorValue(aCx, aConstructor); - if (!MaybeWrapObjectValue(aCx, &constructorValue)) { - aRv.NoteJSContextException(aCx); - return; - } - - JSObject* defaultCtor = PromiseBinding::GetConstructorObject(aCx); - if (!defaultCtor) { - aRv.NoteJSContextException(aCx); - return; - } - if (defaultCtor == &constructorValue.toObject()) { - // This is the canonical Promise constructor. - aCapability.mNativePromise = Promise::Create(aGlobal, aRv); - if (aForceCallbackCreation) { - // We have to be a bit careful here. We want to create these functions - // in the compartment in which they would be created if we actually - // invoked the constructor via JS::Construct below. That means our - // callerGlobal compartment if aConstructor is an Xray and the reflector - // compartment of the promise we're creating otherwise. But note that - // our callerGlobal compartment is precisely the reflector compartment - // unless the call was done over Xrays, because the reflector - // compartment comes from xpc::XrayAwareCalleeGlobal. So we really just - // want to create these functions in the callerGlobal compartment. - MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(&aConstructor.toObject()) || - callerGlobal == global); - JSAutoCompartment ac2(aCx, callerGlobal); - - JSObject* resolveFuncObj = - CreateFunction(aCx, aCapability.mNativePromise, - PromiseCallback::Resolve); - if (!resolveFuncObj) { - aRv.NoteJSContextException(aCx); - return; - } - aCapability.mResolve.setObject(*resolveFuncObj); - - JSObject* rejectFuncObj = - CreateFunction(aCx, aCapability.mNativePromise, - PromiseCallback::Reject); - if (!rejectFuncObj) { - aRv.NoteJSContextException(aCx); - return; - } - aCapability.mReject.setObject(*rejectFuncObj); - } - return; - } - } - - // Step 4. - // We can create our get-capabilities function in the calling compartment. It - // will work just as if we did |new promiseConstructor(function(a,b){}). - // Notably, if we're called over Xrays that's all fine, because we will end up - // creating the callbacks in the caller compartment in that case. - JSFunction* getCapabilitiesFunc = - js::NewFunctionWithReserved(aCx, GetCapabilitiesExecutor, - 2 /* nargs */, - 0 /* flags */, - nullptr); - if (!getCapabilitiesFunc) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return; - } - - JS::Rooted getCapabilitiesObj(aCx); - getCapabilitiesObj = JS_GetFunctionObject(getCapabilitiesFunc); - - // Step 5 doesn't need to be done, since we're not actually storing a - // PromiseCapability in the executor; see the comments in - // GetCapabilitiesExecutor above. - - // Step 6 and step 7. - JS::Rooted getCapabilities(aCx, - JS::ObjectValue(*getCapabilitiesObj)); - JS::Rooted promiseObj(aCx); - if (!JS::Construct(aCx, aConstructor, - JS::HandleValueArray(getCapabilities), - &promiseObj)) { - aRv.NoteJSContextException(aCx); - return; - } - - // Step 8 plus copying over the value to the PromiseCapability. - JS::Rooted v(aCx); - v = js::GetFunctionNativeReserved(getCapabilitiesObj, - GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT); - if (!v.isObject() || !JS::IsCallable(&v.toObject())) { - aRv.ThrowTypeError(); - return; - } - aCapability.mResolve = v; - - // Step 9 plus copying over the value to the PromiseCapability. - v = js::GetFunctionNativeReserved(getCapabilitiesObj, - GET_CAPABILITIES_EXECUTOR_REJECT_SLOT); - if (!v.isObject() || !JS::IsCallable(&v.toObject())) { - aRv.ThrowTypeError(); - return; - } - aCapability.mReject = v; - - // Step 10. - aCapability.mPromise = promiseObj; - - // Step 11 doesn't need anything, since the PromiseCapability was passed in. -} - -/* static */ void -Promise::Resolve(const GlobalObject& aGlobal, JS::Handle aThisv, - JS::Handle aValue, - JS::MutableHandle aRetval, ErrorResult& aRv) -{ - // Implementation of - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.resolve - - JSContext* cx = aGlobal.Context(); - - nsCOMPtr global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - // Steps 1 and 2. - if (!aThisv.isObject()) { - aRv.ThrowTypeError(); - return; - } - - // Step 3. If a Promise was passed and matches our constructor, just return it. - if (aValue.isObject()) { - JS::Rooted valueObj(cx, &aValue.toObject()); - Promise* nextPromise; - nsresult rv = UNWRAP_OBJECT(Promise, valueObj, nextPromise); - - if (NS_SUCCEEDED(rv)) { - JS::Rooted constructor(cx); - if (!JS_GetProperty(cx, valueObj, "constructor", &constructor)) { - aRv.NoteJSContextException(cx); - return; - } - - // Cheat instead of calling JS_SameValue, since we know one's an object. - if (aThisv == constructor) { - aRetval.setObject(*valueObj); - return; - } - } - } - - // Step 4. - PromiseCapability capability(cx); - NewPromiseCapability(cx, global, aThisv, false, capability, aRv); - // Step 5. - if (aRv.Failed()) { - return; - } - - // Step 6. - Promise* p = capability.mNativePromise; - if (p) { - p->MaybeResolveInternal(cx, aValue); - p->mFullfillmentStack = p->mAllocationStack; - } else { - JS::Rooted value(cx, aValue); - JS::Rooted ignored(cx); - if (!JS::Call(cx, JS::UndefinedHandleValue /* thisVal */, - capability.mResolve, JS::HandleValueArray(value), - &ignored)) { - // Step 7. - aRv.NoteJSContextException(cx); - return; - } - } - - // Step 8. - aRetval.set(capability.PromiseValue()); -} - -/* static */ already_AddRefed -Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx, - JS::Handle aValue, ErrorResult& aRv) -{ - RefPtr promise = Create(aGlobal, aRv); - if (aRv.Failed()) { - return nullptr; - } - - promise->MaybeResolveInternal(aCx, aValue); - return promise.forget(); -} - -/* static */ void -Promise::Reject(const GlobalObject& aGlobal, JS::Handle aThisv, - JS::Handle aValue, - JS::MutableHandle aRetval, ErrorResult& aRv) -{ - // Implementation of - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.reject - - JSContext* cx = aGlobal.Context(); - - nsCOMPtr global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - // Steps 1 and 2. - if (!aThisv.isObject()) { - aRv.ThrowTypeError(); - return; - } - - // Step 3. - PromiseCapability capability(cx); - NewPromiseCapability(cx, global, aThisv, false, capability, aRv); - // Step 4. - if (aRv.Failed()) { - return; - } - - // Step 5. - Promise* p = capability.mNativePromise; - if (p) { - p->MaybeRejectInternal(cx, aValue); - p->mRejectionStack = p->mAllocationStack; - } else { - JS::Rooted value(cx, aValue); - JS::Rooted ignored(cx); - if (!JS::Call(cx, JS::UndefinedHandleValue /* thisVal */, - capability.mReject, JS::HandleValueArray(value), - &ignored)) { - // Step 6. - aRv.NoteJSContextException(cx); - return; - } - } - - // Step 7. - aRetval.set(capability.PromiseValue()); -} - -/* static */ already_AddRefed -Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx, - JS::Handle aValue, ErrorResult& aRv) -{ - RefPtr promise = Create(aGlobal, aRv); - if (aRv.Failed()) { - return nullptr; - } - - promise->MaybeRejectInternal(aCx, aValue); - return promise.forget(); -} - -namespace { -void -SpeciesConstructor(JSContext* aCx, - JS::Handle promise, - JS::Handle defaultCtor, - JS::MutableHandle ctor, - ErrorResult& aRv) -{ - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-speciesconstructor - - // Step 1. - MOZ_ASSERT(promise); - - // Step 2. - JS::Rooted constructorVal(aCx); - if (!JS_GetProperty(aCx, promise, "constructor", &constructorVal)) { - // Step 3. - aRv.NoteJSContextException(aCx); - return; - } - - // Step 4. - if (constructorVal.isUndefined()) { - ctor.set(defaultCtor); - return; - } - - // Step 5. - if (!constructorVal.isObject()) { - aRv.ThrowTypeError(); - return; - } - - // Step 6. - JS::Rooted species(aCx, - SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species))); - JS::Rooted speciesVal(aCx); - JS::Rooted constructorObj(aCx, &constructorVal.toObject()); - if (!JS_GetPropertyById(aCx, constructorObj, species, &speciesVal)) { - // Step 7. - aRv.NoteJSContextException(aCx); - return; - } - - // Step 8. - if (speciesVal.isNullOrUndefined()) { - ctor.set(defaultCtor); - return; - } - - // Step 9. - if (speciesVal.isObject() && JS::IsConstructor(&speciesVal.toObject())) { - ctor.set(speciesVal); - return; - } - - // Step 10. - aRv.ThrowTypeError(); -} -} // anonymous namespace - -void -Promise::Then(JSContext* aCx, JS::Handle aCalleeGlobal, - AnyCallback* aResolveCallback, AnyCallback* aRejectCallback, - JS::MutableHandle aRetval, ErrorResult& aRv) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.then - - // Step 1. - JS::Rooted promiseVal(aCx, JS::ObjectValue(*GetWrapper())); - if (!MaybeWrapObjectValue(aCx, &promiseVal)) { - aRv.NoteJSContextException(aCx); - return; - } - JS::Rooted promiseObj(aCx, &promiseVal.toObject()); - MOZ_ASSERT(promiseObj); - - // Step 2 was done by the bindings. - - // Step 3. We want to use aCalleeGlobal here because it will do the - // right thing for us via Xrays (where we won't find @@species on - // our promise constructor for now). - JS::Rooted defaultCtorVal(aCx); - { // Scope for JSAutoCompartment - JSAutoCompartment ac(aCx, aCalleeGlobal); - JSObject* defaultCtor = PromiseBinding::GetConstructorObject(aCx); - if (!defaultCtor) { - aRv.NoteJSContextException(aCx); - return; - } - defaultCtorVal.setObject(*defaultCtor); - } - if (!MaybeWrapObjectValue(aCx, &defaultCtorVal)) { - aRv.NoteJSContextException(aCx); - return; - } - - JS::Rooted constructor(aCx); - SpeciesConstructor(aCx, promiseObj, defaultCtorVal, &constructor, aRv); - if (aRv.Failed()) { - // Step 4. - return; - } - - // Step 5. - GlobalObject globalObj(aCx, GetWrapper()); - if (globalObj.Failed()) { - aRv.NoteJSContextException(aCx); - return; - } - nsCOMPtr globalObject = - do_QueryInterface(globalObj.GetAsSupports()); - if (!globalObject) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - PromiseCapability capability(aCx); - NewPromiseCapability(aCx, globalObject, constructor, false, capability, aRv); - if (aRv.Failed()) { - // Step 6. - return; - } - - // Now step 7: start - // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromisethen - - // Step 1 and step 2 are just assertions. - - // Step 3 and step 4 are kinda handled for us already; we use null - // to represent "Identity" and "Thrower". - - // Steps 5 and 6. These branch based on whether we know we have a - // vanilla Promise or not. - JS::Rooted global(aCx, JS::CurrentGlobalOrNull(aCx)); - if (capability.mNativePromise) { - Promise* promise = capability.mNativePromise; - - RefPtr resolveCb = - PromiseCallback::Factory(promise, global, aResolveCallback, - PromiseCallback::Resolve); - - RefPtr rejectCb = - PromiseCallback::Factory(promise, global, aRejectCallback, - PromiseCallback::Reject); - - AppendCallbacks(resolveCb, rejectCb); - } else { - JS::Rooted resolveObj(aCx, &capability.mResolve.toObject()); - RefPtr resolveFunc = - new AnyCallback(aCx, resolveObj, GetIncumbentGlobal()); - - JS::Rooted rejectObj(aCx, &capability.mReject.toObject()); - RefPtr rejectFunc = - new AnyCallback(aCx, rejectObj, GetIncumbentGlobal()); - - if (!capability.mPromise) { - aRv.ThrowTypeError(); - return; - } - JS::Rooted newPromiseObj(aCx, capability.mPromise); - // We want to store the reflector itself. - newPromiseObj = js::CheckedUnwrap(newPromiseObj); - if (!newPromiseObj) { - // Just throw something. - aRv.ThrowTypeError(); - return; - } - - RefPtr resolveCb; - if (aResolveCallback) { - resolveCb = new WrapperPromiseCallback(global, aResolveCallback, - newPromiseObj, - resolveFunc, rejectFunc); - } else { - resolveCb = new InvokePromiseFuncCallback(global, newPromiseObj, - resolveFunc); - } - - RefPtr rejectCb; - if (aRejectCallback) { - rejectCb = new WrapperPromiseCallback(global, aRejectCallback, - newPromiseObj, - resolveFunc, rejectFunc); - } else { - rejectCb = new InvokePromiseFuncCallback(global, newPromiseObj, - rejectFunc); - } - - AppendCallbacks(resolveCb, rejectCb); - } - - aRetval.set(capability.PromiseValue()); -} - -void -Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback, - JS::MutableHandle aRetval, - ErrorResult& aRv) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.catch - - // We can't call Promise::Then directly, because someone might have - // overridden Promise.prototype.then. - JS::Rooted promiseVal(aCx, JS::ObjectValue(*GetWrapper())); - if (!MaybeWrapObjectValue(aCx, &promiseVal)) { - aRv.NoteJSContextException(aCx); - return; - } - JS::Rooted promiseObj(aCx, &promiseVal.toObject()); - MOZ_ASSERT(promiseObj); - JS::AutoValueArray<2> callbacks(aCx); - callbacks[0].setUndefined(); - if (aRejectCallback) { - callbacks[1].setObject(*aRejectCallback->Callable()); - // It could be in any compartment, so put it in ours. - if (!MaybeWrapObjectValue(aCx, callbacks[1])) { - aRv.NoteJSContextException(aCx); - return; - } - } else { - callbacks[1].setNull(); - } - if (!JS_CallFunctionName(aCx, promiseObj, "then", callbacks, aRetval)) { - aRv.NoteJSContextException(aCx); - } -} - -/** - * The CountdownHolder class encapsulates Promise.all countdown functions and - * the countdown holder parts of the Promises spec. It maintains the result - * array and AllResolveElementFunctions use SetValue() to set the array indices. - */ -class CountdownHolder final : public nsISupports -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CountdownHolder) - - CountdownHolder(const GlobalObject& aGlobal, Promise* aPromise, - uint32_t aCountdown) - : mPromise(aPromise), mCountdown(aCountdown) - { - MOZ_ASSERT(aCountdown != 0); - JSContext* cx = aGlobal.Context(); - - // The only time aGlobal.Context() and aGlobal.Get() are not - // same-compartment is when we're called via Xrays, and in that situation we - // in fact want to create the array in the callee compartment - - JSAutoCompartment ac(cx, aGlobal.Get()); - mValues = JS_NewArrayObject(cx, aCountdown); - mozilla::HoldJSObjects(this); - } - -private: - ~CountdownHolder() - { - mozilla::DropJSObjects(this); - } - -public: - void SetValue(uint32_t index, const JS::Handle aValue) - { - MOZ_ASSERT(mCountdown > 0); - - AutoJSAPI jsapi; - if (!jsapi.Init(mValues)) { - return; - } - JSContext* cx = jsapi.cx(); - - JS::Rooted value(cx, aValue); - JS::Rooted values(cx, mValues); - if (!JS_WrapValue(cx, &value) || - !JS_DefineElement(cx, values, index, value, JSPROP_ENUMERATE)) { - MOZ_ASSERT(JS_IsExceptionPending(cx)); - JS::Rooted exn(cx); - if (!jsapi.StealException(&exn)) { - mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); - } else { - mPromise->MaybeReject(cx, exn); - } - } - - --mCountdown; - if (mCountdown == 0) { - JS::Rooted result(cx, JS::ObjectValue(*mValues)); - mPromise->MaybeResolve(cx, result); - } - } - -private: - RefPtr mPromise; - uint32_t mCountdown; - JS::Heap mValues; -}; - -NS_IMPL_CYCLE_COLLECTING_ADDREF(CountdownHolder) -NS_IMPL_CYCLE_COLLECTING_RELEASE(CountdownHolder) -NS_IMPL_CYCLE_COLLECTION_CLASS(CountdownHolder) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CountdownHolder) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CountdownHolder) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mValues) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CountdownHolder) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CountdownHolder) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) - tmp->mValues = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -/** - * An AllResolveElementFunction is the per-promise - * part of the Promise.all() algorithm. - * Every Promise in the handler is handed an instance of this as a resolution - * handler and it sets the relevant index in the CountdownHolder. - */ -class AllResolveElementFunction final : public PromiseNativeHandler -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(AllResolveElementFunction) - - AllResolveElementFunction(CountdownHolder* aHolder, uint32_t aIndex) - : mCountdownHolder(aHolder), mIndex(aIndex) - { - MOZ_ASSERT(aHolder); - } - - void - ResolvedCallback(JSContext* aCx, JS::Handle aValue) override - { - mCountdownHolder->SetValue(mIndex, aValue); - } - - void - RejectedCallback(JSContext* aCx, JS::Handle aValue) override - { - // Should never be attached to Promise as a reject handler. - MOZ_CRASH("AllResolveElementFunction should never be attached to a Promise's reject handler!"); - } - -private: - ~AllResolveElementFunction() - { - } - - RefPtr mCountdownHolder; - uint32_t mIndex; -}; - -NS_IMPL_CYCLE_COLLECTING_ADDREF(AllResolveElementFunction) -NS_IMPL_CYCLE_COLLECTING_RELEASE(AllResolveElementFunction) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AllResolveElementFunction) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION(AllResolveElementFunction, mCountdownHolder) - -static const JSClass PromiseAllDataHolderClass = { - "PromiseAllDataHolder", JSCLASS_HAS_RESERVED_SLOTS(3) -}; - -// Slot indices for objects of class PromiseAllDataHolderClass. -#define DATA_HOLDER_REMAINING_ELEMENTS_SLOT 0 -#define DATA_HOLDER_VALUES_ARRAY_SLOT 1 -#define DATA_HOLDER_RESOLVE_FUNCTION_SLOT 2 - -// Slot indices for PromiseAllResolveElement. -// The RESOLVE_ELEMENT_INDEX_SLOT stores our index unless we've already been -// called. Then it stores INT32_MIN (which is never a valid index value). -#define RESOLVE_ELEMENT_INDEX_SLOT 0 -// The RESOLVE_ELEMENT_DATA_HOLDER_SLOT slot stores an object of class -// PromiseAllDataHolderClass. -#define RESOLVE_ELEMENT_DATA_HOLDER_SLOT 1 - -static bool -PromiseAllResolveElement(JSContext* aCx, unsigned aArgc, JS::Value* aVp) -{ - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.all-resolve-element-functions - // - // See the big comment about compartments in Promise::All "Substep 4" that - // explains what compartments the various stuff here lives in. - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); - - // Step 1. - int32_t index = - js::GetFunctionNativeReserved(&args.callee(), - RESOLVE_ELEMENT_INDEX_SLOT).toInt32(); - // Step 2. - if (index == INT32_MIN) { - args.rval().setUndefined(); - return true; - } - - // Step 3. - js::SetFunctionNativeReserved(&args.callee(), - RESOLVE_ELEMENT_INDEX_SLOT, - JS::Int32Value(INT32_MIN)); - - // Step 4 already done. - - // Step 5. - JS::Rooted dataHolder(aCx, - &js::GetFunctionNativeReserved(&args.callee(), - RESOLVE_ELEMENT_DATA_HOLDER_SLOT).toObject()); - - JS::Rooted values(aCx, - js::GetReservedSlot(dataHolder, DATA_HOLDER_VALUES_ARRAY_SLOT)); - - // Step 6, effectively. - JS::Rooted resolveFunc(aCx, - js::GetReservedSlot(dataHolder, DATA_HOLDER_RESOLVE_FUNCTION_SLOT)); - - // Step 7. - int32_t remainingElements = - js::GetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32(); - - // Step 8. - JS::Rooted valuesObj(aCx, &values.toObject()); - if (!JS_DefineElement(aCx, valuesObj, index, args.get(0), JSPROP_ENUMERATE)) { - return false; - } - - // Step 9. - remainingElements -= 1; - js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT, - JS::Int32Value(remainingElements)); - - // Step 10. - if (remainingElements == 0) { - return JS::Call(aCx, JS::UndefinedHandleValue, resolveFunc, - JS::HandleValueArray(values), args.rval()); - } - - // Step 11. - args.rval().setUndefined(); - return true; -} - - -/* static */ void -Promise::All(const GlobalObject& aGlobal, JS::Handle aThisv, - JS::Handle aIterable, - JS::MutableHandle aRetval, ErrorResult& aRv) -{ - // Implements http://www.ecma-international.org/ecma-262/6.0/#sec-promise.all - nsCOMPtr global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - JSContext* cx = aGlobal.Context(); - - // Steps 1-5: nothing to do. Note that the @@species bits got removed in - // https://github.com/tc39/ecma262/pull/211 - - // Step 6. - PromiseCapability capability(cx); - NewPromiseCapability(cx, global, aThisv, true, capability, aRv); - // Step 7. - if (aRv.Failed()) { - return; - } - - MOZ_ASSERT(aThisv.isObject(), "How did NewPromiseCapability succeed?"); - JS::Rooted constructorObj(cx, &aThisv.toObject()); - - // After this point we have a useful promise value in "capability", so just go - // ahead and put it in our retval now. Every single return path below would - // want to do that anyway. - aRetval.set(capability.PromiseValue()); - if (!MaybeWrapValue(cx, aRetval)) { - aRv.NoteJSContextException(cx); - return; - } - - // The arguments we're going to be passing to "then" on each loop iteration. - // The second one we know already; the first one will be created on each - // iteration of the loop. - JS::AutoValueArray<2> callbackFunctions(cx); - callbackFunctions[1].set(capability.mReject); - - // Steps 8 and 9. - JS::ForOfIterator iter(cx); - if (!iter.init(aIterable, JS::ForOfIterator::AllowNonIterable)) { - capability.RejectWithException(cx, aRv); - return; - } - - if (!iter.valueIsIterable()) { - ThrowErrorMessage(cx, MSG_PROMISE_ARG_NOT_ITERABLE, - "Argument of Promise.all"); - capability.RejectWithException(cx, aRv); - return; - } - - // Step 10 doesn't need to be done, because ForOfIterator handles it - // for us. - - // Now we jump over to - // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromiseall - // and do its steps. - - // Substep 4. Create our data holder that holds all the things shared across - // every step of the iterator. In particular, this holds the - // remainingElementsCount (as an integer reserved slot), the array of values, - // and the resolve function from our PromiseCapability. - // - // We have to be very careful about which compartments we create things in - // here. In particular, we have to maintain the invariant that anything - // stored in a reserved slot is same-compartment with the object whose - // reserved slot it's in. But we want to create the values array in the - // Promise reflector compartment, because that array can get exposed to code - // that has access to the Promise reflector (in particular code from that - // compartment), and that should work, even if the Promise reflector - // compartment is less-privileged than our caller compartment. - // - // So the plan is as follows: Create the values array in the promise reflector - // compartment. Create the PromiseAllResolveElement function and the data - // holder in our current compartment. Store a cross-compartment wrapper to - // the values array in the holder. This should be OK because the only things - // we hand the PromiseAllResolveElement function to are the "then" calls we do - // and in the case when the reflector compartment is not the current - // compartment those are happening over Xrays anyway, which means they get the - // canonical "then" function and content can't see our - // PromiseAllResolveElement. - JS::Rooted dataHolder(cx); - dataHolder = JS_NewObjectWithGivenProto(cx, &PromiseAllDataHolderClass, - nullptr); - if (!dataHolder) { - capability.RejectWithException(cx, aRv); - return; - } - - JS::Rooted reflectorGlobal(cx, global->GetGlobalJSObject()); - JS::Rooted valuesArray(cx); - { // Scope for JSAutoCompartment. - JSAutoCompartment ac(cx, reflectorGlobal); - valuesArray = JS_NewArrayObject(cx, 0); - } - if (!valuesArray) { - // It's important that we've exited the JSAutoCompartment by now, before - // calling RejectWithException and possibly invoking capability.mReject. - capability.RejectWithException(cx, aRv); - return; - } - - // The values array as a value we can pass to a function in our current - // compartment, or store in the holder's reserved slot. - JS::Rooted valuesArrayVal(cx, JS::ObjectValue(*valuesArray)); - if (!MaybeWrapObjectValue(cx, &valuesArrayVal)) { - capability.RejectWithException(cx, aRv); - return; - } - - js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT, - JS::Int32Value(1)); - js::SetReservedSlot(dataHolder, DATA_HOLDER_VALUES_ARRAY_SLOT, - valuesArrayVal); - js::SetReservedSlot(dataHolder, DATA_HOLDER_RESOLVE_FUNCTION_SLOT, - capability.mResolve); - - // Substep 5. - CheckedInt32 index = 0; - - // Substep 6. - JS::Rooted nextValue(cx); - while (true) { - bool done; - // Steps a, b, c, e, f, g. - if (!iter.next(&nextValue, &done)) { - capability.RejectWithException(cx, aRv); - return; - } - - // Step d. - if (done) { - int32_t remainingCount = - js::GetReservedSlot(dataHolder, - DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32(); - remainingCount -= 1; - if (remainingCount == 0) { - JS::Rooted ignored(cx); - if (!JS::Call(cx, JS::UndefinedHandleValue, capability.mResolve, - JS::HandleValueArray(valuesArrayVal), &ignored)) { - capability.RejectWithException(cx, aRv); - } - return; - } - js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT, - JS::Int32Value(remainingCount)); - // We're all set for now! - return; - } - - // Step h. - { // Scope for the JSAutoCompartment we need to work with valuesArray. We - // mostly do this for performance; we could go ahead and do the define via - // a cross-compartment proxy instead... - JSAutoCompartment ac(cx, valuesArray); - if (!JS_DefineElement(cx, valuesArray, index.value(), - JS::UndefinedHandleValue, JSPROP_ENUMERATE)) { - // Have to go back into the caller compartment before we try to touch - // capability.mReject. Luckily, capability.mReject is guaranteed to be - // an object in the right compartment here. - JSAutoCompartment ac2(cx, &capability.mReject.toObject()); - capability.RejectWithException(cx, aRv); - return; - } - } - - // Step i. Sadly, we can't take a shortcut here even if - // capability.mNativePromise exists, because someone could have overridden - // "resolve" on the canonical Promise constructor. - JS::Rooted nextPromise(cx); - if (!JS_CallFunctionName(cx, constructorObj, "resolve", - JS::HandleValueArray(nextValue), - &nextPromise)) { - // Step j. - capability.RejectWithException(cx, aRv); - return; - } - - // Step k. - JS::Rooted resolveElement(cx); - JSFunction* resolveFunc = - js::NewFunctionWithReserved(cx, PromiseAllResolveElement, - 1 /* nargs */, 0 /* flags */, nullptr); - if (!resolveFunc) { - capability.RejectWithException(cx, aRv); - return; - } - - resolveElement = JS_GetFunctionObject(resolveFunc); - // Steps l-p. - js::SetFunctionNativeReserved(resolveElement, - RESOLVE_ELEMENT_INDEX_SLOT, - JS::Int32Value(index.value())); - js::SetFunctionNativeReserved(resolveElement, - RESOLVE_ELEMENT_DATA_HOLDER_SLOT, - JS::ObjectValue(*dataHolder)); - - // Step q. - int32_t remainingElements = - js::GetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32(); - js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT, - JS::Int32Value(remainingElements + 1)); - - // Step r. And now we don't know whether nextPromise has an overridden - // "then" method, so no shortcuts here either. - callbackFunctions[0].setObject(*resolveElement); - JS::Rooted nextPromiseObj(cx); - JS::Rooted ignored(cx); - if (!JS_ValueToObject(cx, nextPromise, &nextPromiseObj) || - !JS_CallFunctionName(cx, nextPromiseObj, "then", callbackFunctions, - &ignored)) { - // Step s. - capability.RejectWithException(cx, aRv); - } - - // Step t. - index += 1; - if (!index.isValid()) { - // Let's just claim OOM. - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - capability.RejectWithException(cx, aRv); - } - } -} - -/* static */ already_AddRefed -Promise::All(const GlobalObject& aGlobal, - const nsTArray>& aPromiseList, ErrorResult& aRv) -{ - nsCOMPtr global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - JSContext* cx = aGlobal.Context(); - - if (aPromiseList.IsEmpty()) { - JS::Rooted empty(cx, JS_NewArrayObject(cx, 0)); - if (!empty) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return nullptr; - } - JS::Rooted value(cx, JS::ObjectValue(*empty)); - // We know "value" is not a promise, so call the Resolve function - // that doesn't have to check for that. - return Promise::Resolve(global, cx, value, aRv); - } - - RefPtr promise = Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } - RefPtr holder = - new CountdownHolder(aGlobal, promise, aPromiseList.Length()); - - JS::Rooted obj(cx, JS::CurrentGlobalOrNull(cx)); - if (!obj) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - RefPtr rejectCb = new RejectPromiseCallback(promise, obj); - - for (uint32_t i = 0; i < aPromiseList.Length(); ++i) { - RefPtr resolveHandler = - new AllResolveElementFunction(holder, i); - - RefPtr resolveCb = - new NativePromiseCallback(resolveHandler, Resolved); - - // Every promise gets its own resolve callback, which will set the right - // index in the array to the resolution value. - aPromiseList[i]->AppendCallbacks(resolveCb, rejectCb); - } - - return promise.forget(); -} - -/* static */ void -Promise::Race(const GlobalObject& aGlobal, JS::Handle aThisv, - JS::Handle aIterable, JS::MutableHandle aRetval, - ErrorResult& aRv) -{ - // Implements http://www.ecma-international.org/ecma-262/6.0/#sec-promise.race - nsCOMPtr global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - JSContext* cx = aGlobal.Context(); - - // Steps 1-5: nothing to do. Note that the @@species bits got removed in - // https://github.com/tc39/ecma262/pull/211 - PromiseCapability capability(cx); - - // Step 6. - NewPromiseCapability(cx, global, aThisv, true, capability, aRv); - // Step 7. - if (aRv.Failed()) { - return; - } - - MOZ_ASSERT(aThisv.isObject(), "How did NewPromiseCapability succeed?"); - JS::Rooted constructorObj(cx, &aThisv.toObject()); - - // After this point we have a useful promise value in "capability", so just go - // ahead and put it in our retval now. Every single return path below would - // want to do that anyway. - aRetval.set(capability.PromiseValue()); - if (!MaybeWrapValue(cx, aRetval)) { - aRv.NoteJSContextException(cx); - return; - } - - // The arguments we're going to be passing to "then" on each loop iteration. - JS::AutoValueArray<2> callbackFunctions(cx); - callbackFunctions[0].set(capability.mResolve); - callbackFunctions[1].set(capability.mReject); - - // Steps 8 and 9. - JS::ForOfIterator iter(cx); - if (!iter.init(aIterable, JS::ForOfIterator::AllowNonIterable)) { - capability.RejectWithException(cx, aRv); - return; - } - - if (!iter.valueIsIterable()) { - ThrowErrorMessage(cx, MSG_PROMISE_ARG_NOT_ITERABLE, - "Argument of Promise.race"); - capability.RejectWithException(cx, aRv); - return; - } - - // Step 10 doesn't need to be done, because ForOfIterator handles it - // for us. - - // Now we jump over to - // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromiserace - // and do its steps. - JS::Rooted nextValue(cx); - while (true) { - bool done; - // Steps a, b, c, e, f, g. - if (!iter.next(&nextValue, &done)) { - capability.RejectWithException(cx, aRv); - return; - } - - // Step d. - if (done) { - // We're all set! - return; - } - - // Step h. Sadly, we can't take a shortcut here even if - // capability.mNativePromise exists, because someone could have overridden - // "resolve" on the canonical Promise constructor. - JS::Rooted nextPromise(cx); - if (!JS_CallFunctionName(cx, constructorObj, "resolve", - JS::HandleValueArray(nextValue), &nextPromise)) { - // Step i. - capability.RejectWithException(cx, aRv); - return; - } - - // Step j. And now we don't know whether nextPromise has an overridden - // "then" method, so no shortcuts here either. - JS::Rooted nextPromiseObj(cx); - JS::Rooted ignored(cx); - if (!JS_ValueToObject(cx, nextPromise, &nextPromiseObj) || - !JS_CallFunctionName(cx, nextPromiseObj, "then", callbackFunctions, - &ignored)) { - // Step k. - capability.RejectWithException(cx, aRv); - } - } -} - -/* static */ -bool -Promise::PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp) -{ - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); - args.rval().set(args.thisv()); - return true; -} - -void -Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - RefPtr resolveCb = - new NativePromiseCallback(aRunnable, Resolved); - - RefPtr rejectCb = - new NativePromiseCallback(aRunnable, Rejected); - - AppendCallbacks(resolveCb, rejectCb); -} - -#endif // SPIDERMONKEY_PROMISE - -JSObject* -Promise::GlobalJSObject() const -{ - return mGlobal->GetGlobalJSObject(); -} - -JSCompartment* -Promise::Compartment() const -{ - return js::GetObjectCompartment(GlobalJSObject()); -} - -#ifndef SPIDERMONKEY_PROMISE -void -Promise::AppendCallbacks(PromiseCallback* aResolveCallback, - PromiseCallback* aRejectCallback) -{ - if (!mGlobal || mGlobal->IsDying()) { - return; - } - - MOZ_ASSERT(aResolveCallback); - MOZ_ASSERT(aRejectCallback); - - if (mIsLastInChain && mState == PromiseState::Rejected) { - // This rejection is now consumed. - PromiseDebugging::AddConsumedRejection(*this); - // Note that we may not have had the opportunity to call - // RunResolveTask() yet, so we may never have called - // `PromiseDebugging:AddUncaughtRejection`. - } - mIsLastInChain = false; - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - // Now that there is a callback, we don't need to report anymore. - mHadRejectCallback = true; - RemoveWorkerHolder(); -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - mResolveCallbacks.AppendElement(aResolveCallback); - mRejectCallbacks.AppendElement(aRejectCallback); - - // If promise's state is fulfilled, queue a task to process our fulfill - // callbacks with promise's result. If promise's state is rejected, queue a - // task to process our reject callbacks with promise's result. - if (mState != Pending) { - TriggerPromiseReactions(); - } -} -#endif // SPIDERMONKEY_PROMISE - -#ifndef SPIDERMONKEY_PROMISE -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) -void -Promise::MaybeReportRejected() -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - if (mState != Rejected || mHadRejectCallback || mResult.isUndefined()) { - return; - } - - AutoJSAPI jsapi; - // We may not have a usable global by now (if it got unlinked - // already), so don't init with it. - jsapi.Init(); - JSContext* cx = jsapi.cx(); - JS::Rooted obj(cx, GetWrapper()); - MOZ_ASSERT(obj); // We preserve our wrapper, so should always have one here. - JS::Rooted val(cx, mResult); - - JSAutoCompartment ac(cx, obj); - if (!JS_WrapValue(cx, &val)) { - JS_ClearPendingException(cx); - return; - } - - js::ErrorReport report(cx); - RefPtr exp; - bool isObject = val.isObject(); - if (!isObject || NS_FAILED(UNWRAP_OBJECT(Exception, &val.toObject(), exp))) { - if (!isObject || - NS_FAILED(UNWRAP_OBJECT(DOMException, &val.toObject(), exp))) { - if (!report.init(cx, val, js::ErrorReport::NoSideEffects)) { - NS_WARNING("Couldn't convert the unhandled rejected value to an exception."); - JS_ClearPendingException(cx); - return; - } - } - } - - RefPtr xpcReport = new xpc::ErrorReport(); - bool isMainThread = MOZ_LIKELY(NS_IsMainThread()); - bool isChrome = isMainThread ? nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj)) - : GetCurrentThreadWorkerPrivate()->IsChromeWorker(); - nsGlobalWindow* win = isMainThread ? xpc::WindowGlobalOrNull(obj) : nullptr; - uint64_t windowID = win ? win->AsInner()->WindowID() : 0; - if (exp) { - xpcReport->Init(cx, exp, isChrome, windowID); - } else { - xpcReport->Init(report.report(), report.toStringResult(), - isChrome, windowID); - } - - // Now post an event to do the real reporting async - // Since Promises preserve their wrapper, it is essential to RefPtr<> the - // AsyncErrorReporter, otherwise if the call to DispatchToMainThread fails, it - // will leak. See Bug 958684. So... don't use DispatchToMainThread() - nsCOMPtr mainThread = do_GetMainThread(); - if (NS_WARN_IF(!mainThread)) { - // Would prefer NS_ASSERTION, but that causes failure in xpcshell tests - NS_WARNING("!!! Trying to report rejected Promise after MainThread shutdown"); - } - if (mainThread) { - RefPtr r = new AsyncErrorReporter(xpcReport); - mainThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); - } -} -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - -void -Promise::MaybeResolveInternal(JSContext* aCx, - JS::Handle aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - if (mResolvePending) { - return; - } - - ResolveInternal(aCx, aValue); -} - -void -Promise::MaybeRejectInternal(JSContext* aCx, - JS::Handle aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - if (mResolvePending) { - return; - } - - RejectInternal(aCx, aValue); -} - -void -Promise::HandleException(JSContext* aCx) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - JS::Rooted exn(aCx); - if (JS_GetPendingException(aCx, &exn)) { - JS_ClearPendingException(aCx); - RejectInternal(aCx, exn); - } -} - -void -Promise::ResolveInternal(JSContext* aCx, - JS::Handle aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - CycleCollectedJSContext* context = CycleCollectedJSContext::Get(); - - mResolvePending = true; - - if (aValue.isObject()) { - JS::Rooted valueObj(aCx, &aValue.toObject()); - - // Thenables. - JS::Rooted then(aCx); - if (!JS_GetProperty(aCx, valueObj, "then", &then)) { - HandleException(aCx); - return; - } - - if (then.isObject() && JS::IsCallable(&then.toObject())) { - // This is the then() function of the thenable aValueObj. - JS::Rooted thenObj(aCx, &then.toObject()); - - // We used to have a fast path here for the case when the following - // requirements held: - // - // 1) valueObj is a Promise. - // 2) thenObj is a JSFunction backed by our actual Promise::Then - // implementation. - // - // But now that we're doing subclassing in Promise.prototype.then we would - // also need the following requirements: - // - // 3) Getting valueObj.constructor has no side-effects. - // 4) Getting valueObj.constructor[@@species] has no side-effects. - // 5) valueObj.constructor[@@species] is a function and calling it has no - // side-effects (e.g. it's the canonical Promise constructor) and it - // provides some callback functions to call as arguments to its - // argument. - // - // Ensuring that stuff while not inside SpiderMonkey is painful, so let's - // drop the fast path for now. - - RefPtr thenCallback = - new PromiseInit(nullptr, thenObj, mozilla::dom::GetIncumbentGlobal()); - RefPtr task = - new PromiseResolveThenableJob(this, valueObj, thenCallback); - context->DispatchToMicroTask(task.forget()); - return; - } - } - - MaybeSettle(aValue, Resolved); -} - -void -Promise::RejectInternal(JSContext* aCx, - JS::Handle aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - mResolvePending = true; - - MaybeSettle(aValue, Rejected); -} - -void -Promise::Settle(JS::Handle aValue, PromiseState aState) -{ - MOZ_ASSERT(mGlobal, - "We really should have a global here. Except we sometimes don't " - "in the wild for some odd reason"); - NS_ASSERT_OWNINGTHREAD(Promise); - - if (!mGlobal || mGlobal->IsDying()) { - return; - } - - mSettlementTimestamp = TimeStamp::Now(); - - AutoJSAPI jsapi; - jsapi.Init(); - JSContext* cx = jsapi.cx(); - JS::RootedObject wrapper(cx, GetWrapper()); - MOZ_ASSERT(wrapper); // We preserved it - JSAutoCompartment ac(cx, wrapper); - - JS::Rooted value(cx, aValue); - - if (!JS_WrapValue(cx, &value)) { - JS_ClearPendingException(cx); - value = JS::UndefinedValue(); - } - SetResult(value); - SetState(aState); - - JS::dbg::onPromiseSettled(cx, wrapper); - - if (aState == PromiseState::Rejected && - mIsLastInChain) { - // The Promise has just been rejected, and it is last in chain. - // We need to inform PromiseDebugging. - // If the Promise is eventually not the last in chain anymore, - // we will need to inform PromiseDebugging again. - PromiseDebugging::AddUncaughtRejection(*this); - } - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - // If the Promise was rejected, and there is no reject handler already setup, - // watch for thread shutdown. - if (aState == PromiseState::Rejected && - !mHadRejectCallback && - !NS_IsMainThread()) { - WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - worker->AssertIsOnWorkerThread(); - - mWorkerHolder = new PromiseReportRejectWorkerHolder(this); - if (NS_WARN_IF(!mWorkerHolder->HoldWorker(worker, Closing))) { - mWorkerHolder = nullptr; - // Worker is shutting down, report rejection immediately since it is - // unlikely that reject callbacks will be added after this point. - MaybeReportRejectedOnce(); - } - } -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - TriggerPromiseReactions(); -} - -void -Promise::MaybeSettle(JS::Handle aValue, - PromiseState aState) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // Promise.all() or Promise.race() implementations will repeatedly call - // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState - // from asserting. - if (mState != Pending) { - return; - } - - Settle(aValue, aState); -} - -void -Promise::TriggerPromiseReactions() -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - CycleCollectedJSContext* runtime = CycleCollectedJSContext::Get(); - - nsTArray> callbacks; - callbacks.SwapElements(mState == Resolved ? mResolveCallbacks - : mRejectCallbacks); - mResolveCallbacks.Clear(); - mRejectCallbacks.Clear(); - - for (uint32_t i = 0; i < callbacks.Length(); ++i) { - RefPtr task = - new PromiseReactionJob(this, callbacks[i], mResult); - runtime->DispatchToMicroTask(task.forget()); - } -} - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) -void -Promise::RemoveWorkerHolder() -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // The DTOR of this WorkerHolder will release the worker for us. - mWorkerHolder = nullptr; -} - -bool -PromiseReportRejectWorkerHolder::Notify(Status aStatus) -{ - MOZ_ASSERT(aStatus > Running); - mPromise->MaybeReportRejectedOnce(); - // After this point, `this` has been deleted by RemoveWorkerHolder! - return true; -} -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - -bool -Promise::CaptureStack(JSContext* aCx, JS::Heap& aTarget) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - JS::Rooted stack(aCx); - if (!JS::CaptureCurrentStack(aCx, &stack)) { - return false; - } - aTarget = stack; - return true; -} - -void -Promise::GetDependentPromises(nsTArray>& aPromises) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // We want to return promises that correspond to then() calls, Promise.all() - // calls, and Promise.race() calls. - // - // For the then() case, we have both resolve and reject callbacks that know - // what the next promise is. - // - // For the race() case, likewise. - // - // For the all() case, our reject callback knows what the next promise is, but - // our resolve callback just knows it needs to notify some - // PromiseNativeHandler, which itself only has an indirect relationship to the - // next promise. - // - // So we walk over our _reject_ callbacks and ask each of them what promise - // its dependent promise is. - for (size_t i = 0; i < mRejectCallbacks.Length(); ++i) { - Promise* p = mRejectCallbacks[i]->GetDependentPromise(); - if (p) { - aPromises.AppendElement(p); - } - } -} - -#endif // SPIDERMONKEY_PROMISE - -// A WorkerRunnable to resolve/reject the Promise on the worker thread. -// Calling thread MUST hold PromiseWorkerProxy's mutex before creating this. -class PromiseWorkerProxyRunnable : public WorkerRunnable -{ -public: - PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy, - PromiseWorkerProxy::RunCallbackFunc aFunc) - : WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(), - WorkerThreadUnchangedBusyCount) - , mPromiseWorkerProxy(aPromiseWorkerProxy) - , mFunc(aFunc) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPromiseWorkerProxy); - } - - virtual bool - WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) - { - MOZ_ASSERT(aWorkerPrivate); - aWorkerPrivate->AssertIsOnWorkerThread(); - MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate); - - MOZ_ASSERT(mPromiseWorkerProxy); - RefPtr workerPromise = mPromiseWorkerProxy->WorkerPromise(); - - // Here we convert the buffer to a JS::Value. - JS::Rooted value(aCx); - if (!mPromiseWorkerProxy->Read(aCx, &value)) { - JS_ClearPendingException(aCx); - return false; - } - - (workerPromise->*mFunc)(aCx, value); - - // Release the Promise because it has been resolved/rejected for sure. - mPromiseWorkerProxy->CleanUp(); - return true; - } - -protected: - ~PromiseWorkerProxyRunnable() {} - -private: - RefPtr mPromiseWorkerProxy; - - // Function pointer for calling Promise::{ResolveInternal,RejectInternal}. - PromiseWorkerProxy::RunCallbackFunc mFunc; -}; - -class PromiseWorkerHolder final : public WorkerHolder -{ - // RawPointer because this proxy keeps alive the holder. - PromiseWorkerProxy* mProxy; - -public: - explicit PromiseWorkerHolder(PromiseWorkerProxy* aProxy) - : mProxy(aProxy) - { - MOZ_ASSERT(aProxy); +public: + explicit PromiseWorkerHolder(PromiseWorkerProxy* aProxy) + : mProxy(aProxy) + { + MOZ_ASSERT(aProxy); } bool @@ -3223,23 +942,6 @@ void Promise::MaybeRejectBrokenly(const nsAString& aArg) { MaybeSomething(aArg, &Promise::MaybeReject); } -#ifndef SPIDERMONKEY_PROMISE -uint64_t -Promise::GetID() { - if (mID != 0) { - return mID; - } - return mID = ++gIDGenerator; -} -#endif // SPIDERMONKEY_PROMISE - -#ifndef SPIDERMONKEY_PROMISE -Promise::PromiseState -Promise::State() const -{ - return mState; -} -#else // SPIDERMONKEY_PROMISE Promise::PromiseState Promise::State() const { @@ -3256,7 +958,6 @@ Promise::State() const return PromiseState::Pending; } -#endif // SPIDERMONKEY_PROMISE } // namespace dom } // namespace mozilla diff --git a/dom/promise/Promise.h b/dom/promise/Promise.h index 642603a11..77914079a 100644 --- a/dom/promise/Promise.h +++ b/dom/promise/Promise.h @@ -21,17 +21,6 @@ #include "js/TypeDecls.h" #include "jspubtd.h" -// Bug 1083361 introduces a new mechanism for tracking uncaught -// rejections. This #define serves to track down the parts of code -// that need to be removed once clients have been put together -// to take advantage of the new mechanism. New code should not -// depend on code #ifdefed to this #define. -#define DOM_PROMISE_DEPRECATED_REPORTING !SPIDERMONKEY_PROMISE - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) -#include "mozilla/dom/workers/bindings/WorkerHolder.h" -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - class nsIGlobalObject; namespace mozilla { @@ -40,88 +29,36 @@ namespace dom { class AnyCallback; class DOMError; class MediaStreamError; -class PromiseCallback; class PromiseInit; class PromiseNativeHandler; class PromiseDebugging; -class Promise; - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) -class PromiseReportRejectWorkerHolder : public workers::WorkerHolder -{ - // PromiseReportRejectWorkerHolder is held by an nsAutoPtr on the Promise - // which means that this object will be destroyed before the Promise is - // destroyed. - Promise* MOZ_NON_OWNING_REF mPromise; - -public: - explicit PromiseReportRejectWorkerHolder(Promise* aPromise) - : mPromise(aPromise) - { - MOZ_ASSERT(mPromise); - } - - virtual bool - Notify(workers::Status aStatus) override; -}; -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - #define NS_PROMISE_IID \ { 0x1b8d6215, 0x3e67, 0x43ba, \ { 0x8a, 0xf9, 0x31, 0x5e, 0x8f, 0xce, 0x75, 0x65 } } class Promise : public nsISupports, -#ifndef SPIDERMONKEY_PROMISE - // Only wrappercached when we're not using SpiderMonkey - // promises, because those don't have a useful object moved - // hook, which wrappercache needs. - public nsWrapperCache, -#endif // SPIDERMONKEY_PROMISE public SupportsWeakPtr { - friend class NativePromiseCallback; - friend class PromiseReactionJob; - friend class PromiseResolverTask; friend class PromiseTask; -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - friend class PromiseReportRejectWorkerHolder; -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) friend class PromiseWorkerProxy; friend class PromiseWorkerProxyRunnable; - friend class RejectPromiseCallback; - friend class ResolvePromiseCallback; - friend class PromiseResolveThenableJob; - friend class FastPromiseResolveThenableJob; - friend class WrapperPromiseCallback; public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROMISE_IID) NS_DECL_CYCLE_COLLECTING_ISUPPORTS -#ifdef SPIDERMONKEY_PROMISE - // We're not skippable, since we're not owned from JS to start with. NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise) -#else // SPIDERMONKEY_PROMISE - NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(Promise) -#endif // SPIDERMONKEY_PROMISE MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Promise) // Promise creation tries to create a JS reflector for the Promise, so is // fallible. Furthermore, we don't want to do JS-wrapping on a 0-refcount // object, so we addref before doing that and return the addrefed pointer // here. -#ifdef SPIDERMONKEY_PROMISE static already_AddRefed Create(nsIGlobalObject* aGlobal, ErrorResult& aRv); // Reports a rejected Promise by sending an error report. static void ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise); -#else - static already_AddRefed - Create(nsIGlobalObject* aGlobal, ErrorResult& aRv, - // Passing null for aDesiredProto will use Promise.prototype. - JS::Handle aDesiredProto = nullptr); -#endif // SPIDERMONKEY_PROMISE typedef void (Promise::*MaybeFunc)(JSContext* aCx, JS::Handle aValue); @@ -183,7 +120,6 @@ public: return mGlobal; } -#ifdef SPIDERMONKEY_PROMISE bool WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aWrapper); @@ -223,95 +159,17 @@ public: return mPromiseObj; } -#else // SPIDERMONKEY_PROMISE - JSObject* PromiseObj() - { - return GetWrapper(); - } - - virtual JSObject* - WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - - static already_AddRefed - Constructor(const GlobalObject& aGlobal, PromiseInit& aInit, - ErrorResult& aRv, JS::Handle aDesiredProto); - - static void - Resolve(const GlobalObject& aGlobal, JS::Handle aThisv, - JS::Handle aValue, - JS::MutableHandle aRetval, ErrorResult& aRv); - - static already_AddRefed - Resolve(nsIGlobalObject* aGlobal, JSContext* aCx, - JS::Handle aValue, ErrorResult& aRv); - - static void - Reject(const GlobalObject& aGlobal, JS::Handle aThisv, - JS::Handle aValue, - JS::MutableHandle aRetval, ErrorResult& aRv); - - static already_AddRefed - Reject(nsIGlobalObject* aGlobal, JSContext* aCx, - JS::Handle aValue, ErrorResult& aRv); - - void - Then(JSContext* aCx, - // aCalleeGlobal may not be in the compartment of aCx, when called over - // Xrays. - JS::Handle aCalleeGlobal, - AnyCallback* aResolveCallback, AnyCallback* aRejectCallback, - JS::MutableHandle aRetval, - ErrorResult& aRv); - - void - Catch(JSContext* aCx, - AnyCallback* aRejectCallback, - JS::MutableHandle aRetval, - ErrorResult& aRv); - - static void - All(const GlobalObject& aGlobal, JS::Handle aThisv, - JS::Handle aIterable, JS::MutableHandle aRetval, - ErrorResult& aRv); - - static already_AddRefed - All(const GlobalObject& aGlobal, - const nsTArray>& aPromiseList, ErrorResult& aRv); - - static void - Race(const GlobalObject& aGlobal, JS::Handle aThisv, - JS::Handle aIterable, JS::MutableHandle aRetval, - ErrorResult& aRv); - - static bool - PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp); -#endif // SPIDERMONKEY_PROMISE - void AppendNativeHandler(PromiseNativeHandler* aRunnable); JSObject* GlobalJSObject() const; JSCompartment* Compartment() const; -#ifndef SPIDERMONKEY_PROMISE - // Return a unique-to-the-process identifier for this Promise. - uint64_t GetID(); -#endif // SPIDERMONKEY_PROMISE - -#ifndef SPIDERMONKEY_PROMISE - enum JSCallbackSlots { - SLOT_PROMISE = 0, - SLOT_DATA - }; -#endif // SPIDERMONKEY_PROMISE - -#ifdef SPIDERMONKEY_PROMISE // Create a dom::Promise from a given SpiderMonkey Promise object. // aPromiseObj MUST be in the compartment of aGlobal's global JS object. static already_AddRefed CreateFromExisting(nsIGlobalObject* aGlobal, JS::Handle aPromiseObj); -#endif // SPIDERMONKEY_PROMISE enum class PromiseState { Pending, @@ -335,99 +193,7 @@ protected: // use the default prototype for the sort of Promise we have. void CreateWrapper(JS::Handle aDesiredProto, ErrorResult& aRv); -#ifndef SPIDERMONKEY_PROMISE - // Create the JS resolving functions of resolve() and reject(). And provide - // references to the two functions by calling PromiseInit passed from Promise - // constructor. - void CallInitFunction(const GlobalObject& aGlobal, PromiseInit& aInit, - ErrorResult& aRv); - - // The NewPromiseCapability function from - // . - // Errors are communicated via aRv. If aForceCallbackCreation is - // true, then this function will ensure that aCapability has a - // useful mResolve/mReject even if mNativePromise is non-null. - static void NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal, - JS::Handle aConstructor, - bool aForceCallbackCreation, - PromiseCapability& aCapability, - ErrorResult& aRv); - - bool IsPending() - { - return mResolvePending; - } - - void GetDependentPromises(nsTArray>& aPromises); - - bool IsLastInChain() const - { - return mIsLastInChain; - } - - void SetNotifiedAsUncaught() - { - mWasNotifiedAsUncaught = true; - } - - bool WasNotifiedAsUncaught() const - { - return mWasNotifiedAsUncaught; - } -#endif // SPIDERMONKEY_PROMISE - private: -#ifndef SPIDERMONKEY_PROMISE - friend class PromiseDebugging; - - void SetState(PromiseState aState) - { - MOZ_ASSERT(mState == Pending); - MOZ_ASSERT(aState != Pending); - mState = aState; - } - - void SetResult(JS::Handle aValue) - { - mResult = aValue; - } - - // This method enqueues promise's resolve/reject callbacks with promise's - // result. It's executed when the resolver.resolve() or resolver.reject() is - // called or when the promise already has a result and new callbacks are - // appended by then() or catch(). - void TriggerPromiseReactions(); - - void Settle(JS::Handle aValue, Promise::PromiseState aState); - void MaybeSettle(JS::Handle aValue, Promise::PromiseState aState); - - void AppendCallbacks(PromiseCallback* aResolveCallback, - PromiseCallback* aRejectCallback); - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - // If we have been rejected and our mResult is a JS exception, - // report it to the error console. - // Use MaybeReportRejectedOnce() for actual calls. - void MaybeReportRejected(); - - void MaybeReportRejectedOnce() { - MaybeReportRejected(); - RemoveWorkerHolder(); - mResult.setUndefined(); - } -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - void MaybeResolveInternal(JSContext* aCx, - JS::Handle aValue); - void MaybeRejectInternal(JSContext* aCx, - JS::Handle aValue); - - void ResolveInternal(JSContext* aCx, - JS::Handle aValue); - void RejectInternal(JSContext* aCx, - JS::Handle aValue); -#endif // SPIDERMONKEY_PROMISE - template void MaybeSomething(T& aArgument, MaybeFunc aFunc) { MOZ_ASSERT(PromiseObj()); // It was preserved! @@ -444,92 +210,11 @@ private: (this->*aFunc)(cx, val); } -#ifndef SPIDERMONKEY_PROMISE - // Static methods for the PromiseInit functions. - static bool - JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp); - - static bool - ThenableResolverCommon(JSContext* aCx, uint32_t /* PromiseCallback::Task */ aTask, - unsigned aArgc, JS::Value* aVp); - static bool - JSCallbackThenableResolver(JSContext *aCx, unsigned aArgc, JS::Value *aVp); - static bool - JSCallbackThenableRejecter(JSContext *aCx, unsigned aArgc, JS::Value *aVp); - - static JSObject* - CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask); - - static JSObject* - CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask); - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - void RemoveWorkerHolder(); -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - // Capture the current stack and store it in aTarget. If false is - // returned, an exception is presumably pending on aCx. - bool CaptureStack(JSContext* aCx, JS::Heap& aTarget); -#endif // SPIDERMONKEY_PROMISE - void HandleException(JSContext* aCx); RefPtr mGlobal; -#ifndef SPIDERMONKEY_PROMISE - nsTArray > mResolveCallbacks; - nsTArray > mRejectCallbacks; - - JS::Heap mResult; - // A stack that shows where this promise was allocated, if there was - // JS running at the time. Otherwise null. - JS::Heap mAllocationStack; - // mRejectionStack is only set when the promise is rejected directly from - // script, by calling Promise.reject() or the rejection callback we pass to - // the PromiseInit function. Promises that are rejected internally do not - // have a rejection stack. - JS::Heap mRejectionStack; - // mFullfillmentStack is only set when the promise is fulfilled directly from - // script, by calling Promise.resolve() or the fulfillment callback we pass to - // the PromiseInit function. Promises that are fulfilled internally do not - // have a fulfillment stack. - JS::Heap mFullfillmentStack; - PromiseState mState; - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - bool mHadRejectCallback; - - // If a rejected promise on a worker has no reject callbacks attached, it - // needs to know when the worker is shutting down, to report the error on the - // console before the worker's context is deleted. This feature is used for - // that purpose. - nsAutoPtr mWorkerHolder; -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - bool mTaskPending; - bool mResolvePending; - - // `true` if this Promise is the last in the chain, or `false` if - // another Promise has been created from this one by a call to - // `then`, `all`, `race`, etc. - bool mIsLastInChain; - - // `true` if PromiseDebugging has already notified at least one observer that - // this promise was left uncaught, `false` otherwise. - bool mWasNotifiedAsUncaught; - - // The time when this promise was created. - TimeStamp mCreationTimestamp; - - // The time when this promise transitioned out of the pending state. - TimeStamp mSettlementTimestamp; - - // Once `GetID()` has been called, a unique-to-the-process identifier for this - // promise. Until then, `0`. - uint64_t mID; -#else // SPIDERMONKEY_PROMISE JS::Heap mPromiseObj; -#endif // SPIDERMONKEY_PROMISE }; NS_DEFINE_STATIC_IID_ACCESSOR(Promise, NS_PROMISE_IID) diff --git a/dom/promise/PromiseCallback.cpp b/dom/promise/PromiseCallback.cpp deleted file mode 100644 index 6ecf983b7..000000000 --- a/dom/promise/PromiseCallback.cpp +++ /dev/null @@ -1,571 +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 "PromiseCallback.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/dom/PromiseNativeHandler.h" - -#include "jsapi.h" -#include "jsfriendapi.h" -#include "jswrapper.h" - -namespace mozilla { -namespace dom { - -#ifndef SPIDERMONKEY_PROMISE - -NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseCallback) -NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseCallback) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseCallback) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION_0(PromiseCallback) - -PromiseCallback::PromiseCallback() -{ -} - -PromiseCallback::~PromiseCallback() -{ -} - -// ResolvePromiseCallback - -NS_IMPL_CYCLE_COLLECTION_CLASS(ResolvePromiseCallback) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ResolvePromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) - tmp->mGlobal = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ResolvePromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ResolvePromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ResolvePromiseCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_ADDREF_INHERITED(ResolvePromiseCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(ResolvePromiseCallback, PromiseCallback) - -ResolvePromiseCallback::ResolvePromiseCallback(Promise* aPromise, - JS::Handle aGlobal) - : mPromise(aPromise) - , mGlobal(aGlobal) -{ - MOZ_ASSERT(aPromise); - MOZ_ASSERT(aGlobal); - HoldJSObjects(this); -} - -ResolvePromiseCallback::~ResolvePromiseCallback() -{ - DropJSObjects(this); -} - -nsresult -ResolvePromiseCallback::Call(JSContext* aCx, - JS::Handle aValue) -{ - // Run resolver's algorithm with value and the synchronous flag set. - - JS::ExposeValueToActiveJS(aValue); - - JSAutoCompartment ac(aCx, mGlobal); - JS::Rooted value(aCx, aValue); - if (!JS_WrapValue(aCx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - mPromise->ResolveInternal(aCx, value); - return NS_OK; -} - -// RejectPromiseCallback - -NS_IMPL_CYCLE_COLLECTION_CLASS(RejectPromiseCallback) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RejectPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) - tmp->mGlobal = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RejectPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(RejectPromiseCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(RejectPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_ADDREF_INHERITED(RejectPromiseCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(RejectPromiseCallback, PromiseCallback) - -RejectPromiseCallback::RejectPromiseCallback(Promise* aPromise, - JS::Handle aGlobal) - : mPromise(aPromise) - , mGlobal(aGlobal) -{ - MOZ_ASSERT(aPromise); - MOZ_ASSERT(mGlobal); - HoldJSObjects(this); -} - -RejectPromiseCallback::~RejectPromiseCallback() -{ - DropJSObjects(this); -} - -nsresult -RejectPromiseCallback::Call(JSContext* aCx, - JS::Handle aValue) -{ - // Run resolver's algorithm with value and the synchronous flag set. - - JS::ExposeValueToActiveJS(aValue); - - JSAutoCompartment ac(aCx, mGlobal); - JS::Rooted value(aCx, aValue); - if (!JS_WrapValue(aCx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - - mPromise->RejectInternal(aCx, value); - return NS_OK; -} - -// InvokePromiseFuncCallback - -NS_IMPL_CYCLE_COLLECTION_CLASS(InvokePromiseFuncCallback) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(InvokePromiseFuncCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromiseFunc) - tmp->mGlobal = nullptr; - tmp->mNextPromiseObj = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(InvokePromiseFuncCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromiseFunc) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(InvokePromiseFuncCallback) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNextPromiseObj) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(InvokePromiseFuncCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_ADDREF_INHERITED(InvokePromiseFuncCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(InvokePromiseFuncCallback, PromiseCallback) - -InvokePromiseFuncCallback::InvokePromiseFuncCallback(JS::Handle aGlobal, - JS::Handle aNextPromiseObj, - AnyCallback* aPromiseFunc) - : mGlobal(aGlobal) - , mNextPromiseObj(aNextPromiseObj) - , mPromiseFunc(aPromiseFunc) -{ - MOZ_ASSERT(aGlobal); - MOZ_ASSERT(aNextPromiseObj); - MOZ_ASSERT(aPromiseFunc); - HoldJSObjects(this); -} - -InvokePromiseFuncCallback::~InvokePromiseFuncCallback() -{ - DropJSObjects(this); -} - -nsresult -InvokePromiseFuncCallback::Call(JSContext* aCx, - JS::Handle aValue) -{ - // Run resolver's algorithm with value and the synchronous flag set. - - JS::ExposeValueToActiveJS(aValue); - - JSAutoCompartment ac(aCx, mGlobal); - JS::Rooted value(aCx, aValue); - if (!JS_WrapValue(aCx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - ErrorResult rv; - JS::Rooted ignored(aCx); - mPromiseFunc->Call(value, &ignored, rv); - // Useful exceptions already got reported. - rv.SuppressException(); - return NS_OK; -} - -Promise* -InvokePromiseFuncCallback::GetDependentPromise() -{ - Promise* promise; - if (NS_SUCCEEDED(UNWRAP_OBJECT(Promise, mNextPromiseObj, promise))) { - return promise; - } - - // Oh, well. - return nullptr; -} - -// WrapperPromiseCallback -NS_IMPL_CYCLE_COLLECTION_CLASS(WrapperPromiseCallback) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WrapperPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextPromise) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveFunc) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectFunc) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) - tmp->mGlobal = nullptr; - tmp->mNextPromiseObj = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WrapperPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextPromise) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveFunc) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectFunc) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WrapperPromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNextPromiseObj) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WrapperPromiseCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_ADDREF_INHERITED(WrapperPromiseCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(WrapperPromiseCallback, PromiseCallback) - -WrapperPromiseCallback::WrapperPromiseCallback(Promise* aNextPromise, - JS::Handle aGlobal, - AnyCallback* aCallback) - : mNextPromise(aNextPromise) - , mGlobal(aGlobal) - , mCallback(aCallback) -{ - MOZ_ASSERT(aNextPromise); - MOZ_ASSERT(aGlobal); - HoldJSObjects(this); -} - -WrapperPromiseCallback::WrapperPromiseCallback(JS::Handle aGlobal, - AnyCallback* aCallback, - JS::Handle aNextPromiseObj, - AnyCallback* aResolveFunc, - AnyCallback* aRejectFunc) - : mNextPromiseObj(aNextPromiseObj) - , mResolveFunc(aResolveFunc) - , mRejectFunc(aRejectFunc) - , mGlobal(aGlobal) - , mCallback(aCallback) -{ - MOZ_ASSERT(mNextPromiseObj); - MOZ_ASSERT(aResolveFunc); - MOZ_ASSERT(aRejectFunc); - MOZ_ASSERT(aGlobal); - HoldJSObjects(this); -} - -WrapperPromiseCallback::~WrapperPromiseCallback() -{ - DropJSObjects(this); -} - -nsresult -WrapperPromiseCallback::Call(JSContext* aCx, - JS::Handle aValue) -{ - JS::ExposeValueToActiveJS(aValue); - - JSAutoCompartment ac(aCx, mGlobal); - JS::Rooted value(aCx, aValue); - if (!JS_WrapValue(aCx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - ErrorResult rv; - - // PromiseReactionTask step 6 - JS::Rooted retValue(aCx); - JSCompartment* compartment; - if (mNextPromise) { - compartment = mNextPromise->Compartment(); - } else { - MOZ_ASSERT(mNextPromiseObj); - compartment = js::GetObjectCompartment(mNextPromiseObj); - } - mCallback->Call(value, &retValue, rv, "promise callback", - CallbackObject::eRethrowExceptions, - compartment); - - rv.WouldReportJSException(); - - // PromiseReactionTask step 7 - if (rv.Failed()) { - if (rv.IsUncatchableException()) { - // We have nothing to resolve/reject the promise with. - return rv.StealNSResult(); - } - - JS::Rooted value(aCx); - { // Scope for JSAutoCompartment - // Convert the ErrorResult to a JS exception object that we can reject - // ourselves with. This will be exactly the exception that would get - // thrown from a binding method whose ErrorResult ended up with whatever - // is on "rv" right now. Do this in the promise reflector compartment. - Maybe ac; - if (mNextPromise) { - ac.emplace(aCx, mNextPromise->GlobalJSObject()); - } else { - ac.emplace(aCx, mNextPromiseObj); - } - DebugOnly conversionResult = ToJSValue(aCx, rv, &value); - MOZ_ASSERT(conversionResult); - } - - if (mNextPromise) { - mNextPromise->RejectInternal(aCx, value); - } else { - JS::Rooted ignored(aCx); - ErrorResult rejectRv; - mRejectFunc->Call(value, &ignored, rejectRv); - // This reported any JS exceptions; we just have a pointless exception on - // there now. - rejectRv.SuppressException(); - } - return NS_OK; - } - - // If the return value is the same as the promise itself, throw TypeError. - if (retValue.isObject()) { - JS::Rooted valueObj(aCx, &retValue.toObject()); - valueObj = js::CheckedUnwrap(valueObj); - JS::Rooted nextPromiseObj(aCx); - if (mNextPromise) { - nextPromiseObj = mNextPromise->GetWrapper(); - } else { - MOZ_ASSERT(mNextPromiseObj); - nextPromiseObj = mNextPromiseObj; - } - // XXXbz shouldn't this check be over in ResolveInternal anyway? - if (valueObj == nextPromiseObj) { - const char* fileName = nullptr; - uint32_t lineNumber = 0; - - // Try to get some information about the callback to report a sane error, - // but don't try too hard (only deals with scripted functions). - JS::Rooted unwrapped(aCx, - js::CheckedUnwrap(mCallback->Callback())); - - if (unwrapped) { - JSAutoCompartment ac(aCx, unwrapped); - if (JS_ObjectIsFunction(aCx, unwrapped)) { - JS::Rooted asValue(aCx, JS::ObjectValue(*unwrapped)); - JS::Rooted func(aCx, JS_ValueToFunction(aCx, asValue)); - - MOZ_ASSERT(func); - JSScript* script = JS_GetFunctionScript(aCx, func); - if (script) { - fileName = JS_GetScriptFilename(script); - lineNumber = JS_GetScriptBaseLineNumber(aCx, script); - } - } - } - - // We're back in aValue's compartment here. - JS::Rooted fn(aCx, JS_NewStringCopyZ(aCx, fileName)); - if (!fn) { - // Out of memory. Promise will stay unresolved. - JS_ClearPendingException(aCx); - return NS_ERROR_OUT_OF_MEMORY; - } - - JS::Rooted message(aCx, - JS_NewStringCopyZ(aCx, - "then() cannot return same Promise that it resolves.")); - if (!message) { - // Out of memory. Promise will stay unresolved. - JS_ClearPendingException(aCx); - return NS_ERROR_OUT_OF_MEMORY; - } - - JS::Rooted typeError(aCx); - if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, fn, lineNumber, 0, - nullptr, message, &typeError)) { - // Out of memory. Promise will stay unresolved. - JS_ClearPendingException(aCx); - return NS_ERROR_OUT_OF_MEMORY; - } - - if (mNextPromise) { - mNextPromise->RejectInternal(aCx, typeError); - } else { - JS::Rooted ignored(aCx); - ErrorResult rejectRv; - mRejectFunc->Call(typeError, &ignored, rejectRv); - // This reported any JS exceptions; we just have a pointless exception - // on there now. - rejectRv.SuppressException(); - } - return NS_OK; - } - } - - // Otherwise, run resolver's resolve with value. - if (!JS_WrapValue(aCx, &retValue)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - if (mNextPromise) { - mNextPromise->ResolveInternal(aCx, retValue); - } else { - JS::Rooted ignored(aCx); - ErrorResult resolveRv; - mResolveFunc->Call(retValue, &ignored, resolveRv); - // This reported any JS exceptions; we just have a pointless exception - // on there now. - resolveRv.SuppressException(); - } - - 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; - } - - if (mNextPromise) { - return mNextPromise; - } - - Promise* promise; - if (NS_SUCCEEDED(UNWRAP_OBJECT(Promise, mNextPromiseObj, promise))) { - return promise; - } - - // Oh, well. - return nullptr; -} - -// NativePromiseCallback - -NS_IMPL_CYCLE_COLLECTION_INHERITED(NativePromiseCallback, - PromiseCallback, mHandler) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(NativePromiseCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_ADDREF_INHERITED(NativePromiseCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(NativePromiseCallback, PromiseCallback) - -NativePromiseCallback::NativePromiseCallback(PromiseNativeHandler* aHandler, - Promise::PromiseState aState) - : mHandler(aHandler) - , mState(aState) -{ - MOZ_ASSERT(aHandler); -} - -NativePromiseCallback::~NativePromiseCallback() -{ -} - -nsresult -NativePromiseCallback::Call(JSContext* aCx, - JS::Handle aValue) -{ - JS::ExposeValueToActiveJS(aValue); - - if (mState == Promise::Resolved) { - mHandler->ResolvedCallback(aCx, aValue); - return NS_OK; - } - - if (mState == Promise::Rejected) { - mHandler->RejectedCallback(aCx, aValue); - return NS_OK; - } - - NS_NOTREACHED("huh?"); - return NS_ERROR_FAILURE; -} - -/* static */ PromiseCallback* -PromiseCallback::Factory(Promise* aNextPromise, JS::Handle aGlobal, - AnyCallback* aCallback, Task aTask) -{ - MOZ_ASSERT(aNextPromise); - - // If we have a callback and a next resolver, we have to exec the callback and - // then propagate the return value to the next resolver->resolve(). - if (aCallback) { - return new WrapperPromiseCallback(aNextPromise, aGlobal, aCallback); - } - - if (aTask == Resolve) { - return new ResolvePromiseCallback(aNextPromise, aGlobal); - } - - if (aTask == Reject) { - return new RejectPromiseCallback(aNextPromise, aGlobal); - } - - MOZ_ASSERT(false, "This should not happen"); - return nullptr; -} - -#endif // SPIDERMONKEY_PROMISE - -} // namespace dom -} // namespace mozilla diff --git a/dom/promise/PromiseCallback.h b/dom/promise/PromiseCallback.h deleted file mode 100644 index 9f55e03d0..000000000 --- a/dom/promise/PromiseCallback.h +++ /dev/null @@ -1,203 +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_PromiseCallback_h -#define mozilla_dom_PromiseCallback_h - -#include "mozilla/dom/Promise.h" -#include "nsCycleCollectionParticipant.h" - -namespace mozilla { -namespace dom { - -#ifndef SPIDERMONKEY_PROMISE -// This is the base class for any PromiseCallback. -// It's a logical step in the promise chain of callbacks. -class PromiseCallback : public nsISupports -{ -protected: - virtual ~PromiseCallback(); - -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(PromiseCallback) - - PromiseCallback(); - - virtual nsresult Call(JSContext* aCx, - JS::Handle aValue) = 0; - - // Return the Promise that this callback will end up resolving or - // rejecting, if any. - virtual Promise* GetDependentPromise() = 0; - - enum Task { - Resolve, - Reject - }; - - // This factory returns a PromiseCallback object with refcount of 0. - static PromiseCallback* - Factory(Promise* aNextPromise, JS::Handle aObject, - AnyCallback* aCallback, Task aTask); -}; - -// WrapperPromiseCallback execs a JS Callback with a value, and then the return -// value is sent to either: -// a) If aNextPromise is non-null, the aNextPromise->ResolveFunction() or to -// aNextPromise->RejectFunction() if the JS Callback throws. -// or -// b) If aNextPromise is null, in which case aResolveFunc and aRejectFunc must -// be non-null, then to aResolveFunc, unless aCallback threw, in which case -// aRejectFunc. -class WrapperPromiseCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WrapperPromiseCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle aValue) override; - - Promise* GetDependentPromise() override; - - // Constructor for when we know we have a vanilla Promise. - WrapperPromiseCallback(Promise* aNextPromise, JS::Handle aGlobal, - AnyCallback* aCallback); - - // Constructor for when all we have to work with are resolve/reject functions. - WrapperPromiseCallback(JS::Handle aGlobal, - AnyCallback* aCallback, - JS::Handle mNextPromiseObj, - AnyCallback* aResolveFunc, - AnyCallback* aRejectFunc); - -private: - ~WrapperPromiseCallback(); - - // Either mNextPromise is non-null or all three of mNextPromiseObj, - // mResolveFund and mRejectFunc must are non-null. - RefPtr mNextPromise; - // mNextPromiseObj is the reflector itself; it may not be in the - // same compartment as anything else we have. - JS::Heap mNextPromiseObj; - RefPtr mResolveFunc; - RefPtr mRejectFunc; - JS::Heap mGlobal; - RefPtr mCallback; -}; - -// ResolvePromiseCallback calls aPromise->ResolveFunction() with the value -// received by Call(). -class ResolvePromiseCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ResolvePromiseCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle aValue) override; - - Promise* GetDependentPromise() override - { - return mPromise; - } - - ResolvePromiseCallback(Promise* aPromise, JS::Handle aGlobal); - -private: - ~ResolvePromiseCallback(); - - RefPtr mPromise; - JS::Heap mGlobal; -}; - -// RejectPromiseCallback calls aPromise->RejectFunction() with the value -// received by Call(). -class RejectPromiseCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(RejectPromiseCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle aValue) override; - - Promise* GetDependentPromise() override - { - return mPromise; - } - - RejectPromiseCallback(Promise* aPromise, JS::Handle aGlobal); - -private: - ~RejectPromiseCallback(); - - RefPtr mPromise; - JS::Heap mGlobal; -}; - -// InvokePromiseFuncCallback calls the given function with the value -// received by Call(). -class InvokePromiseFuncCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(InvokePromiseFuncCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle aValue) override; - - Promise* GetDependentPromise() override; - - InvokePromiseFuncCallback(JS::Handle aGlobal, - JS::Handle aNextPromiseObj, - AnyCallback* aPromiseFunc); - -private: - ~InvokePromiseFuncCallback(); - - JS::Heap mGlobal; - JS::Heap mNextPromiseObj; - RefPtr mPromiseFunc; -}; - -// NativePromiseCallback wraps a PromiseNativeHandler. -class NativePromiseCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NativePromiseCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle aValue) override; - - Promise* GetDependentPromise() override - { - return nullptr; - } - - NativePromiseCallback(PromiseNativeHandler* aHandler, - Promise::PromiseState aState); - -private: - ~NativePromiseCallback(); - - RefPtr mHandler; - Promise::PromiseState mState; -}; - -#endif // SPIDERMONKEY_PROMISE - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PromiseCallback_h diff --git a/dom/promise/PromiseDebugging.cpp b/dom/promise/PromiseDebugging.cpp index fc0942ee4..f3ec33e8b 100644 --- a/dom/promise/PromiseDebugging.cpp +++ b/dom/promise/PromiseDebugging.cpp @@ -66,20 +66,6 @@ private: /* static */ MOZ_THREAD_LOCAL(bool) FlushRejections::sDispatched; -#ifndef SPIDERMONKEY_PROMISE -static Promise* -UnwrapPromise(JS::Handle aPromise, ErrorResult& aRv) -{ - Promise* promise; - if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Promise, aPromise, promise)))) { - aRv.ThrowTypeError(NS_LITERAL_STRING("Argument")); - return nullptr; - } - return promise; -} -#endif // SPIDERMONKEY_PROMISE - -#ifdef SPIDERMONKEY_PROMISE /* static */ void PromiseDebugging::GetState(GlobalObject& aGlobal, JS::Handle aPromise, PromiseDebuggingStateHolder& aState, @@ -173,34 +159,6 @@ PromiseDebugging::GetFullfillmentStack(GlobalObject& aGlobal, aStack.set(JS::GetPromiseResolutionSite(obj)); } -#else - -/* static */ void -PromiseDebugging::GetState(GlobalObject&, JS::Handle aPromise, - PromiseDebuggingStateHolder& aState, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - switch (promise->mState) { - case Promise::Pending: - aState.mState = PromiseDebuggingState::Pending; - break; - case Promise::Resolved: - aState.mState = PromiseDebuggingState::Fulfilled; - aState.mValue = promise->mResult; - break; - case Promise::Rejected: - aState.mState = PromiseDebuggingState::Rejected; - aState.mReason = promise->mResult; - break; - } -} - -#endif // SPIDERMONKEY_PROMISE - /*static */ nsString PromiseDebugging::sIDPrefix; @@ -232,86 +190,6 @@ PromiseDebugging::FlushUncaughtRejections() FlushRejections::FlushSync(); } -#ifndef SPIDERMONKEY_PROMISE - -/* static */ void -PromiseDebugging::GetAllocationStack(GlobalObject&, JS::Handle aPromise, - JS::MutableHandle aStack, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - aStack.set(promise->mAllocationStack); -} - -/* static */ void -PromiseDebugging::GetRejectionStack(GlobalObject&, JS::Handle aPromise, - JS::MutableHandle aStack, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - aStack.set(promise->mRejectionStack); -} - -/* static */ void -PromiseDebugging::GetFullfillmentStack(GlobalObject&, JS::Handle aPromise, - JS::MutableHandle aStack, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - aStack.set(promise->mFullfillmentStack); -} - -/* static */ void -PromiseDebugging::GetDependentPromises(GlobalObject&, JS::Handle aPromise, - nsTArray>& aPromises, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - promise->GetDependentPromises(aPromises); -} - -/* static */ double -PromiseDebugging::GetPromiseLifetime(GlobalObject&, - JS::Handle aPromise, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return 0; - } - return (TimeStamp::Now() - promise->mCreationTimestamp).ToMilliseconds(); -} - -/* static */ double -PromiseDebugging::GetTimeToSettle(GlobalObject&, JS::Handle aPromise, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return 0; - } - if (promise->mState == Promise::Pending) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return 0; - } - return (promise->mSettlementTimestamp - - promise->mCreationTimestamp).ToMilliseconds(); -} - -#endif // SPIDERMONKEY_PROMISE - /* static */ void PromiseDebugging::AddUncaughtRejectionObserver(GlobalObject&, UncaughtRejectionObserver& aObserver) @@ -337,8 +215,6 @@ PromiseDebugging::RemoveUncaughtRejectionObserver(GlobalObject&, return false; } -#ifdef SPIDERMONKEY_PROMISE - /* static */ void PromiseDebugging::AddUncaughtRejection(JS::HandleObject aPromise) { @@ -420,102 +296,5 @@ PromiseDebugging::FlushUncaughtRejectionsInternal() storage->mConsumedRejections.clear(); } -#else - -/* static */ void -PromiseDebugging::AddUncaughtRejection(Promise& aPromise) -{ - CycleCollectedJSContext::Get()->mUncaughtRejections.AppendElement(&aPromise); - FlushRejections::DispatchNeeded(); -} - -/* void */ void -PromiseDebugging::AddConsumedRejection(Promise& aPromise) -{ - CycleCollectedJSContext::Get()->mConsumedRejections.AppendElement(&aPromise); - FlushRejections::DispatchNeeded(); -} - -/* static */ void -PromiseDebugging::GetPromiseID(GlobalObject&, - JS::Handle aPromise, - nsString& aID, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - uint64_t promiseID = promise->GetID(); - aID = sIDPrefix; - aID.AppendInt(promiseID); -} - -/* static */ void -PromiseDebugging::FlushUncaughtRejectionsInternal() -{ - CycleCollectedJSContext* storage = CycleCollectedJSContext::Get(); - - // The Promise that have been left uncaught (rejected and last in - // their chain) since the last call to this function. - nsTArray> uncaught; - storage->mUncaughtRejections.SwapElements(uncaught); - - // The Promise that have been left uncaught at some point, but that - // have eventually had their `then` method called. - nsTArray> consumed; - storage->mConsumedRejections.SwapElements(consumed); - - nsTArray>& observers = storage->mUncaughtRejectionObservers; - - nsresult rv; - // Notify observers of uncaught Promise. - - for (size_t i = 0; i < uncaught.Length(); ++i) { - nsCOMPtr promise = do_QueryInterface(uncaught[i], &rv); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - if (!promise->IsLastInChain()) { - // This promise is not the last in the chain anymore, - // so the error has been caught at some point. - continue; - } - - // For the moment, the Promise is still at the end of the - // chain. Let's inform observers, so that they may decide whether - // to report it. - for (size_t j = 0; j < observers.Length(); ++j) { - ErrorResult err; - RefPtr obs = - static_cast(observers[j].get()); - - obs->OnLeftUncaught(*promise, err); // Ignore errors - } - - promise->SetNotifiedAsUncaught(); - } - - // Notify observers of consumed Promise. - - for (size_t i = 0; i < consumed.Length(); ++i) { - nsCOMPtr promise = do_QueryInterface(consumed[i], &rv); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - if (!promise->WasNotifiedAsUncaught()) { - continue; - } - - MOZ_ASSERT(!promise->IsLastInChain()); - for (size_t j = 0; j < observers.Length(); ++j) { - ErrorResult err; - RefPtr obs = - static_cast(observers[j].get()); - - obs->OnConsumed(*promise, err); // Ignore errors - } - } -} -#endif // SPIDERMONKEY_PROMISE - } // namespace dom } // namespace mozilla diff --git a/dom/promise/PromiseDebugging.h b/dom/promise/PromiseDebugging.h index 218a64c2e..77397b841 100644 --- a/dom/promise/PromiseDebugging.h +++ b/dom/promise/PromiseDebugging.h @@ -52,37 +52,17 @@ public: JS::MutableHandle aStack, ErrorResult& aRv); -#ifndef SPIDERMONKEY_PROMISE - static void GetDependentPromises(GlobalObject&, - JS::Handle aPromise, - nsTArray>& aPromises, - ErrorResult& aRv); - static double GetPromiseLifetime(GlobalObject&, - JS::Handle aPromise, - ErrorResult& aRv); - static double GetTimeToSettle(GlobalObject&, JS::Handle aPromise, - ErrorResult& aRv); -#endif // SPIDERMONKEY_PROMISE - // Mechanism for watching uncaught instances of Promise. static void AddUncaughtRejectionObserver(GlobalObject&, UncaughtRejectionObserver& aObserver); static bool RemoveUncaughtRejectionObserver(GlobalObject&, UncaughtRejectionObserver& aObserver); -#ifdef SPIDERMONKEY_PROMISE // Mark a Promise as having been left uncaught at script completion. static void AddUncaughtRejection(JS::HandleObject); // Mark a Promise previously added with `AddUncaughtRejection` as // eventually consumed. static void AddConsumedRejection(JS::HandleObject); -#else - // Mark a Promise as having been left uncaught at script completion. - static void AddUncaughtRejection(Promise&); - // Mark a Promise previously added with `AddUncaughtRejection` as - // eventually consumed. - static void AddConsumedRejection(Promise&); -#endif // SPIDERMONKEY_PROMISE // Propagate the informations from AddUncaughtRejection // and AddConsumedRejection to observers. static void FlushUncaughtRejections(); diff --git a/dom/promise/moz.build b/dom/promise/moz.build index 11d2a7496..c0e3d79a7 100644 --- a/dom/promise/moz.build +++ b/dom/promise/moz.build @@ -11,9 +11,8 @@ EXPORTS.mozilla.dom += [ 'PromiseWorkerProxy.h', ] -UNIFIED_SOURCES += [ +SOURCES += [ 'Promise.cpp', - 'PromiseCallback.cpp', 'PromiseDebugging.cpp', ] diff --git a/dom/webidl/Promise.webidl b/dom/webidl/Promise.webidl index 4dcb7d43e..65b406b6f 100644 --- a/dom/webidl/Promise.webidl +++ b/dom/webidl/Promise.webidl @@ -17,51 +17,8 @@ callback PromiseJobCallback = void(); [TreatNonCallableAsNull] callback AnyCallback = any (any value); -// When using SpiderMonkey promises, we don't want to define all this stuff; -// just define a tiny interface to make codegen of Promise arguments and return -// values work. -#ifndef SPIDERMONKEY_PROMISE -[Constructor(PromiseInit init), - Exposed=(Window,Worker,WorkerDebugger,System)] -// Need to escape "Promise" so it's treated as an identifier. -interface _Promise { - // Have to use "any" (or "object", but "any" is simpler) as the type to - // support the subclassing behavior, since nothing actually requires the - // return value of PromiseSubclass.resolve/reject to be a Promise object. - [NewObject, Throws] - static any resolve(optional any value); - [NewObject, Throws] - static any reject(optional any value); - - // The [TreatNonCallableAsNull] annotation is required since then() should do - // nothing instead of throwing errors when non-callable arguments are passed. - // Have to use "any" (or "object", but "any" is simpler) as the type to - // support the subclassing behavior, since nothing actually requires the - // return value of PromiseSubclass.then/catch to be a Promise object. - [NewObject, Throws] - any then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null, - [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null); - - [NewObject, Throws] - any catch([TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null); - - // Have to use "any" (or "object", but "any" is simpler) as the type to - // support the subclassing behavior, since nothing actually requires the - // return value of PromiseSubclass.all to be a Promise object. As a result, - // we also have to do our argument conversion manually, because we want to - // convert its exceptions into rejections. - [NewObject, Throws] - static any all(optional any iterable); - - // Have to use "any" (or "object", but "any" is simpler) as the type to - // support the subclassing behavior, since nothing actually requires the - // return value of PromiseSubclass.race to be a Promise object. As a result, - // we also have to do our argument conversion manually, because we want to - // convert its exceptions into rejections. - [NewObject, Throws] - static any race(optional any iterable); -}; -#else // SPIDERMONKEY_PROMISE +// Promises are implemented in SpiderMonkey; just define a tiny interface to make +// codegen of Promise arguments and return values work. [NoInterfaceObject, Exposed=(Window,Worker,WorkerDebugger,System)] // Need to escape "Promise" so it's treated as an identifier. @@ -74,4 +31,3 @@ interface _Promise { Exposed=(Window,Worker,System)] interface PromiseNativeHandler { }; -#endif // SPIDERMONKEY_PROMISE diff --git a/dom/webidl/PromiseDebugging.webidl b/dom/webidl/PromiseDebugging.webidl index 107b8bc65..1a5c1aa32 100644 --- a/dom/webidl/PromiseDebugging.webidl +++ b/dom/webidl/PromiseDebugging.webidl @@ -38,11 +38,7 @@ callback interface UncaughtRejectionObserver { * caught, i.e. if its `then` callback is called, `onConsumed` will * be called. */ -#ifdef SPIDERMONKEY_PROMISE void onLeftUncaught(object p); -#else - void onLeftUncaught(Promise p); -#endif SPIDERMONKEY_PROMISE /** * A Promise previously left uncaught is not the last in its @@ -51,11 +47,7 @@ callback interface UncaughtRejectionObserver { * @param p A Promise that was previously left in uncaught state is * now caught, i.e. it is not the last in its chain anymore. */ -#ifdef SPIDERMONKEY_PROMISE void onConsumed(object p); -#else - void onConsumed(Promise p); -#endif SPIDERMONKEY_PROMISE }; [ChromeOnly, Exposed=(Window,System)] @@ -105,42 +97,6 @@ interface PromiseDebugging { [Throws] static object? getFullfillmentStack(object p); -#ifndef SPIDERMONKEY_PROMISE - /** - * Get the promises directly depending on a given promise. These are: - * - * 1) Return values of then() calls on the promise - * 2) Return values of Promise.all() if the given promise was passed in as one - * of the arguments. - * 3) Return values of Promise.race() if the given promise was passed in as - * one of the arguments. - * - * Once a promise is settled, it will generally notify its dependent promises - * and forget about them, so this is most useful on unsettled promises. - * - * Note that this function only returns the promises that directly depend on - * p. It does not recursively return promises that depend on promises that - * depend on p. - */ - [Throws] - static sequence> getDependentPromises(object p); - - /** - * Get the number of milliseconds elapsed since the given promise was created. - */ - [Throws] - static DOMHighResTimeStamp getPromiseLifetime(object p); - - /* - * Get the number of milliseconds elapsed between the promise being created - * and being settled. Throws NS_ERROR_UNEXPECTED if the promise has not - * settled. - */ - [Throws] - static DOMHighResTimeStamp getTimeToSettle(object p); - -#endif // SPIDERMONKEY_PROMISE - /** * Watching uncaught rejections on the current thread. * diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 0fe10eff9..172895f97 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -12,8 +12,6 @@ PREPROCESSED_WEBIDL_FILES = [ 'HTMLMediaElement.webidl', 'Navigator.webidl', 'Node.webidl', - 'Promise.webidl', - 'PromiseDebugging.webidl', 'Window.webidl', ] @@ -371,6 +369,8 @@ WEBIDL_FILES = [ 'PresentationRequest.webidl', 'ProcessingInstruction.webidl', 'ProfileTimelineMarker.webidl', + 'Promise.webidl', + 'PromiseDebugging.webidl', 'PushEvent.webidl', 'PushManager.webidl', 'PushManager.webidl', -- cgit v1.2.3