2013-06-11 18:41:21 -07:00
|
|
|
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
|
|
|
/* vim: set ts=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/. */
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
#include "mozilla/dom/Promise.h"
|
2013-08-28 21:30:06 -07:00
|
|
|
|
|
|
|
#include "jsfriendapi.h"
|
2014-11-17 01:42:00 -08:00
|
|
|
#include "js/Debug.h"
|
2014-07-18 18:31:11 -07:00
|
|
|
#include "mozilla/dom/BindingUtils.h"
|
2014-06-03 08:38:38 -07:00
|
|
|
#include "mozilla/dom/DOMError.h"
|
2013-09-10 00:03:37 -07:00
|
|
|
#include "mozilla/dom/OwningNonNull.h"
|
2013-07-11 13:40:36 -07:00
|
|
|
#include "mozilla/dom/PromiseBinding.h"
|
2014-07-18 18:31:11 -07:00
|
|
|
#include "mozilla/dom/ScriptSettings.h"
|
2014-09-19 23:20:41 -07:00
|
|
|
#include "mozilla/dom/MediaStreamError.h"
|
2015-04-10 08:27:57 -07:00
|
|
|
#include "mozilla/Atomics.h"
|
2014-03-04 10:05:08 -08:00
|
|
|
#include "mozilla/CycleCollectedJSRuntime.h"
|
2013-06-12 06:44:56 -07:00
|
|
|
#include "mozilla/Preferences.h"
|
2013-07-11 13:40:36 -07:00
|
|
|
#include "PromiseCallback.h"
|
2015-04-10 08:27:57 -07:00
|
|
|
#include "PromiseDebugging.h"
|
2013-11-19 10:43:51 -08:00
|
|
|
#include "PromiseNativeHandler.h"
|
2014-02-24 05:56:54 -08:00
|
|
|
#include "PromiseWorkerProxy.h"
|
2013-06-11 18:41:21 -07:00
|
|
|
#include "nsContentUtils.h"
|
2013-08-07 14:40:20 -07:00
|
|
|
#include "WorkerPrivate.h"
|
2013-10-23 06:16:49 -07:00
|
|
|
#include "WorkerRunnable.h"
|
2013-08-07 14:40:20 -07:00
|
|
|
#include "nsJSPrincipals.h"
|
2013-08-28 21:30:06 -07:00
|
|
|
#include "nsJSUtils.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
|
|
|
#include "nsJSEnvironment.h"
|
2014-07-03 22:24:59 -07:00
|
|
|
#include "nsIScriptObjectPrincipal.h"
|
|
|
|
#include "xpcpublic.h"
|
|
|
|
#include "nsGlobalWindow.h"
|
2013-06-11 18:41:21 -07:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
namespace {
|
|
|
|
// Generator used by Promise::GetID.
|
|
|
|
Atomic<uintptr_t> gIDGenerator(0);
|
|
|
|
}
|
|
|
|
|
2013-11-24 11:26:07 -08:00
|
|
|
using namespace workers;
|
|
|
|
|
2013-11-19 10:43:51 -08:00
|
|
|
NS_IMPL_ISUPPORTS0(PromiseNativeHandler)
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
// This class processes the promise's callbacks with promise's result.
|
2015-03-21 09:28:04 -07:00
|
|
|
class PromiseCallbackTask final : public nsRunnable
|
2013-06-11 18:41:21 -07:00
|
|
|
{
|
|
|
|
public:
|
2014-10-28 05:08:19 -07:00
|
|
|
PromiseCallbackTask(Promise* aPromise,
|
|
|
|
PromiseCallback* aCallback,
|
|
|
|
const JS::Value& aValue)
|
2013-07-11 13:40:36 -07:00
|
|
|
: mPromise(aPromise)
|
2014-10-28 05:08:19 -07:00
|
|
|
, mCallback(aCallback)
|
|
|
|
, mValue(CycleCollectedJSRuntime::Get()->Runtime(), aValue)
|
2013-06-11 18:41:21 -07:00
|
|
|
{
|
2013-07-11 13:40:36 -07:00
|
|
|
MOZ_ASSERT(aPromise);
|
2014-10-28 05:08:19 -07:00
|
|
|
MOZ_ASSERT(aCallback);
|
|
|
|
MOZ_COUNT_CTOR(PromiseCallbackTask);
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
virtual
|
|
|
|
~PromiseCallbackTask()
|
2013-06-11 18:41:21 -07:00
|
|
|
{
|
2014-10-28 05:08:19 -07:00
|
|
|
NS_ASSERT_OWNINGTHREAD(PromiseCallbackTask);
|
|
|
|
MOZ_COUNT_DTOR(PromiseCallbackTask);
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
protected:
|
2014-08-19 03:39:56 -07:00
|
|
|
NS_IMETHOD
|
2015-03-21 09:28:04 -07:00
|
|
|
Run() override
|
2013-06-11 18:41:21 -07:00
|
|
|
{
|
2014-10-28 05:08:19 -07:00
|
|
|
NS_ASSERT_OWNINGTHREAD(PromiseCallbackTask);
|
|
|
|
ThreadsafeAutoJSContext cx;
|
|
|
|
JS::Rooted<JSObject*> wrapper(cx, mPromise->GetWrapper());
|
|
|
|
MOZ_ASSERT(wrapper); // It was preserved!
|
|
|
|
JSAutoCompartment ac(cx, wrapper);
|
2013-06-11 18:41:21 -07:00
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
JS::Rooted<JS::Value> value(cx, mValue);
|
|
|
|
if (!MaybeWrapValue(cx, &value)) {
|
|
|
|
NS_WARNING("Failed to wrap value into the right compartment.");
|
|
|
|
JS_ClearPendingException(cx);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2013-09-11 09:03:04 -07:00
|
|
|
|
2015-03-09 05:36:29 -07:00
|
|
|
JS::Rooted<JSObject*> asyncStack(cx, mPromise->mAllocationStack);
|
|
|
|
JS::Rooted<JSString*> asyncCause(cx, JS_NewStringCopyZ(cx, "Promise"));
|
|
|
|
if (!asyncCause) {
|
|
|
|
JS_ClearPendingException(cx);
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
Maybe<JS::AutoSetAsyncStackForNewCalls> sas;
|
|
|
|
if (asyncStack) {
|
|
|
|
sas.emplace(cx, asyncStack, asyncCause);
|
|
|
|
}
|
|
|
|
mCallback->Call(cx, value);
|
|
|
|
}
|
2013-09-11 09:03:04 -07:00
|
|
|
|
2014-08-19 03:39:56 -07:00
|
|
|
return NS_OK;
|
2013-09-11 09:03:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsRefPtr<Promise> mPromise;
|
2014-10-28 05:08:19 -07:00
|
|
|
nsRefPtr<PromiseCallback> mCallback;
|
2014-04-16 01:47:53 -07:00
|
|
|
JS::PersistentRooted<JS::Value> mValue;
|
2013-11-24 11:26:07 -08:00
|
|
|
NS_DECL_OWNINGTHREAD;
|
|
|
|
};
|
|
|
|
|
2014-05-20 14:21:13 -07:00
|
|
|
enum {
|
|
|
|
SLOT_PROMISE = 0,
|
|
|
|
SLOT_DATA
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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<JSObject*> aResolveFunc,
|
|
|
|
JS::Handle<JSObject*> aRejectFunc)
|
|
|
|
{
|
|
|
|
js::SetFunctionNativeReserved(aResolveFunc, SLOT_DATA,
|
|
|
|
JS::ObjectValue(*aRejectFunc));
|
|
|
|
js::SetFunctionNativeReserved(aRejectFunc, 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<JSObject*> aFunc)
|
|
|
|
{
|
|
|
|
JS::Value otherFuncVal = js::GetFunctionNativeReserved(aFunc, SLOT_DATA);
|
|
|
|
|
|
|
|
if (!otherFuncVal.isObject()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject* otherFuncObj = &otherFuncVal.toObject();
|
|
|
|
MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, SLOT_DATA).isObject());
|
|
|
|
|
|
|
|
// Break both references.
|
|
|
|
js::SetFunctionNativeReserved(aFunc, SLOT_DATA, JS::UndefinedValue());
|
|
|
|
js::SetFunctionNativeReserved(otherFuncObj, SLOT_DATA, JS::UndefinedValue());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Promise*
|
|
|
|
GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
|
|
|
|
{
|
|
|
|
JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, SLOT_PROMISE);
|
|
|
|
|
|
|
|
MOZ_ASSERT(promiseVal.isObject());
|
|
|
|
|
|
|
|
Promise* promise;
|
|
|
|
UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
|
|
|
|
return promise;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-04-17 19:01:02 -07:00
|
|
|
// Runnable to resolve thenables.
|
2014-05-20 14:21:13 -07:00
|
|
|
// Equivalent to the specification's ResolvePromiseViaThenableTask.
|
2015-03-21 09:28:04 -07:00
|
|
|
class ThenableResolverTask final : public nsRunnable
|
2014-05-20 14:21:13 -07:00
|
|
|
{
|
|
|
|
public:
|
2014-08-19 03:39:56 -07:00
|
|
|
ThenableResolverTask(Promise* aPromise,
|
2014-05-20 14:21:13 -07:00
|
|
|
JS::Handle<JSObject*> aThenable,
|
|
|
|
PromiseInit* aThen)
|
|
|
|
: mPromise(aPromise)
|
|
|
|
, mThenable(CycleCollectedJSRuntime::Get()->Runtime(), aThenable)
|
|
|
|
, mThen(aThen)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aPromise);
|
2014-08-19 03:39:56 -07:00
|
|
|
MOZ_COUNT_CTOR(ThenableResolverTask);
|
2014-05-20 14:21:13 -07:00
|
|
|
}
|
|
|
|
|
2014-08-19 03:39:56 -07:00
|
|
|
virtual
|
|
|
|
~ThenableResolverTask()
|
2014-05-20 14:21:13 -07:00
|
|
|
{
|
2014-08-19 03:39:56 -07:00
|
|
|
NS_ASSERT_OWNINGTHREAD(ThenableResolverTask);
|
|
|
|
MOZ_COUNT_DTOR(ThenableResolverTask);
|
2014-05-20 14:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2014-08-19 03:39:56 -07:00
|
|
|
NS_IMETHOD
|
2015-03-21 09:28:04 -07:00
|
|
|
Run() override
|
2014-05-20 14:21:13 -07:00
|
|
|
{
|
2014-08-19 03:39:56 -07:00
|
|
|
NS_ASSERT_OWNINGTHREAD(ThenableResolverTask);
|
2014-05-20 14:21:13 -07:00
|
|
|
ThreadsafeAutoJSContext cx;
|
2014-07-18 18:31:11 -07:00
|
|
|
JS::Rooted<JSObject*> wrapper(cx, mPromise->GetWrapper());
|
|
|
|
MOZ_ASSERT(wrapper); // It was preserved!
|
2015-04-17 19:01:02 -07:00
|
|
|
// 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.
|
2014-05-20 14:21:13 -07:00
|
|
|
JSAutoCompartment ac(cx, wrapper);
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> resolveFunc(cx,
|
|
|
|
mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Resolve));
|
|
|
|
|
|
|
|
if (!resolveFunc) {
|
|
|
|
mPromise->HandleException(cx);
|
2014-08-19 03:39:56 -07:00
|
|
|
return NS_OK;
|
2014-05-20 14:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> rejectFunc(cx,
|
|
|
|
mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Reject));
|
|
|
|
if (!rejectFunc) {
|
|
|
|
mPromise->HandleException(cx);
|
2014-08-19 03:39:56 -07:00
|
|
|
return NS_OK;
|
2014-05-20 14:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
LinkThenableCallables(cx, resolveFunc, rejectFunc);
|
|
|
|
|
|
|
|
ErrorResult rv;
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> rootedThenable(cx, mThenable);
|
|
|
|
|
|
|
|
mThen->Call(rootedThenable, resolveFunc, rejectFunc, rv,
|
2015-04-08 18:23:48 -07:00
|
|
|
"promise thenable", CallbackObject::eRethrowExceptions,
|
2015-01-15 14:39:02 -08:00
|
|
|
mPromise->Compartment());
|
2014-05-20 14:21:13 -07:00
|
|
|
|
|
|
|
rv.WouldReportJSException();
|
2015-01-15 14:39:02 -08:00
|
|
|
if (rv.Failed()) {
|
2014-05-20 14:21:13 -07:00
|
|
|
JS::Rooted<JS::Value> exn(cx);
|
2015-01-15 14:39:02 -08:00
|
|
|
if (rv.IsJSException()) {
|
|
|
|
rv.StealJSException(cx, &exn);
|
|
|
|
} else {
|
|
|
|
// 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<bool> conversionResult = ToJSValue(cx, rv, &exn);
|
|
|
|
MOZ_ASSERT(conversionResult);
|
|
|
|
}
|
2014-05-20 14:21:13 -07:00
|
|
|
|
|
|
|
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.");
|
|
|
|
}
|
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
mPromise->RejectInternal(cx, exn);
|
2014-05-20 14:21:13 -07:00
|
|
|
}
|
|
|
|
// 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.
|
|
|
|
}
|
2014-08-19 03:39:56 -07:00
|
|
|
|
2015-04-27 12:00:41 -07:00
|
|
|
return rv.StealNSResult();
|
2014-05-20 14:21:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsRefPtr<Promise> mPromise;
|
|
|
|
JS::PersistentRooted<JSObject*> mThenable;
|
|
|
|
nsRefPtr<PromiseInit> mThen;
|
|
|
|
NS_DECL_OWNINGTHREAD;
|
|
|
|
};
|
|
|
|
|
2015-04-17 19:01:02 -07:00
|
|
|
// Fast version of ThenableResolverTask for use in the cases when we know we're
|
|
|
|
// calling the canonical Promise.prototype.then on an actual DOM Promise. In
|
|
|
|
// that case we can just bypass the jumping into and out of JS and call
|
|
|
|
// AppendCallbacks on that promise directly.
|
|
|
|
class FastThenableResolverTask final : public nsRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
FastThenableResolverTask(PromiseCallback* aResolveCallback,
|
|
|
|
PromiseCallback* aRejectCallback,
|
|
|
|
Promise* aNextPromise)
|
|
|
|
: mResolveCallback(aResolveCallback)
|
|
|
|
, mRejectCallback(aRejectCallback)
|
|
|
|
, mNextPromise(aNextPromise)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aResolveCallback);
|
|
|
|
MOZ_ASSERT(aRejectCallback);
|
|
|
|
MOZ_ASSERT(aNextPromise);
|
|
|
|
MOZ_COUNT_CTOR(FastThenableResolverTask);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual
|
|
|
|
~FastThenableResolverTask()
|
|
|
|
{
|
|
|
|
NS_ASSERT_OWNINGTHREAD(FastThenableResolverTask);
|
|
|
|
MOZ_COUNT_DTOR(FastThenableResolverTask);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
NS_IMETHOD
|
|
|
|
Run() override
|
|
|
|
{
|
|
|
|
NS_ASSERT_OWNINGTHREAD(FastThenableResolverTask);
|
|
|
|
mNextPromise->AppendCallbacks(mResolveCallback, mRejectCallback);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsRefPtr<PromiseCallback> mResolveCallback;
|
|
|
|
nsRefPtr<PromiseCallback> mRejectCallback;
|
|
|
|
nsRefPtr<Promise> mNextPromise;
|
|
|
|
};
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
// Promise
|
2013-06-11 18:41:21 -07:00
|
|
|
|
2013-08-01 18:29:05 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
|
2015-04-10 08:27:57 -07:00
|
|
|
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2014-03-12 07:31:03 -07:00
|
|
|
tmp->MaybeReportRejectedOnce();
|
2015-04-10 08:27:57 -07:00
|
|
|
#else
|
|
|
|
tmp->mResult = JS::UndefinedValue();
|
|
|
|
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2014-02-25 13:34:55 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
|
2014-04-09 01:30:24 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks)
|
2013-06-11 18:41:21 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
|
2014-02-25 13:34:55 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
|
2014-04-09 01:30:24 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks)
|
2013-06-11 18:41:21 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
|
2013-06-11 18:41:21 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
|
2014-10-19 19:27:12 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAllocationStack)
|
2014-10-19 19:27:12 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRejectionStack)
|
2014-10-19 19:27:12 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFullfillmentStack)
|
2013-06-11 18:41:21 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
|
2015-02-24 14:24:45 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Promise)
|
|
|
|
if (tmp->IsBlack()) {
|
|
|
|
if (tmp->mResult.isObject()) {
|
|
|
|
JS::ExposeObjectToActiveJS(&(tmp->mResult.toObject()));
|
|
|
|
}
|
|
|
|
if (tmp->mAllocationStack) {
|
|
|
|
JS::ExposeObjectToActiveJS(tmp->mAllocationStack);
|
|
|
|
}
|
|
|
|
if (tmp->mRejectionStack) {
|
|
|
|
JS::ExposeObjectToActiveJS(tmp->mRejectionStack);
|
|
|
|
}
|
|
|
|
if (tmp->mFullfillmentStack) {
|
|
|
|
JS::ExposeObjectToActiveJS(tmp->mFullfillmentStack);
|
|
|
|
}
|
|
|
|
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
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise)
|
2013-06-11 18:41:21 -07:00
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Promise)
|
2013-06-11 18:41:21 -07:00
|
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
2014-12-25 18:11:20 -08:00
|
|
|
NS_INTERFACE_MAP_ENTRY(Promise)
|
2013-06-11 18:41:21 -07:00
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
2014-02-25 13:34:55 -08:00
|
|
|
Promise::Promise(nsIGlobalObject* aGlobal)
|
|
|
|
: mGlobal(aGlobal)
|
2013-06-11 18:41:21 -07:00
|
|
|
, mResult(JS::UndefinedValue())
|
2014-10-19 19:27:12 -07:00
|
|
|
, mAllocationStack(nullptr)
|
2014-10-19 19:27:12 -07:00
|
|
|
, mRejectionStack(nullptr)
|
2014-10-19 19:27:12 -07:00
|
|
|
, mFullfillmentStack(nullptr)
|
2013-06-11 18:41:21 -07:00
|
|
|
, mState(Pending)
|
2015-04-10 08:27:57 -07:00
|
|
|
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2013-08-28 21:30:06 -07:00
|
|
|
, mHadRejectCallback(false)
|
2015-04-10 08:27:57 -07:00
|
|
|
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
|
|
|
, mTaskPending(false)
|
2013-09-11 09:03:04 -07:00
|
|
|
, mResolvePending(false)
|
2015-04-10 08:27:57 -07:00
|
|
|
, mIsLastInChain(true)
|
|
|
|
, mWasNotifiedAsUncaught(false)
|
|
|
|
, mID(0)
|
2013-06-11 18:41:21 -07:00
|
|
|
{
|
2014-02-25 13:34:55 -08:00
|
|
|
MOZ_ASSERT(mGlobal);
|
|
|
|
|
2013-08-16 13:10:17 -07:00
|
|
|
mozilla::HoldJSObjects(this);
|
2014-10-20 10:02:21 -07:00
|
|
|
|
|
|
|
mCreationTimestamp = TimeStamp::Now();
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
Promise::~Promise()
|
2013-06-11 18:41:21 -07:00
|
|
|
{
|
2015-04-10 08:27:57 -07:00
|
|
|
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2014-03-12 07:31:03 -07:00
|
|
|
MaybeReportRejectedOnce();
|
2015-04-10 08:27:57 -07:00
|
|
|
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2013-08-16 13:10:17 -07:00
|
|
|
mozilla::DropJSObjects(this);
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
JSObject*
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 07:13:33 -07:00
|
|
|
Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
2013-06-11 18:41:21 -07:00
|
|
|
{
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 07:13:33 -07:00
|
|
|
return PromiseBinding::Wrap(aCx, this, aGivenProto);
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
|
|
|
|
2014-07-18 18:31:11 -07:00
|
|
|
already_AddRefed<Promise>
|
|
|
|
Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
|
2014-09-11 19:18:49 -07:00
|
|
|
{
|
|
|
|
nsRefPtr<Promise> p = new Promise(aGlobal);
|
|
|
|
p->CreateWrapper(aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Promise::CreateWrapper(ErrorResult& aRv)
|
2014-02-25 13:34:55 -08:00
|
|
|
{
|
2014-07-18 18:31:11 -07:00
|
|
|
AutoJSAPI jsapi;
|
2014-09-11 19:18:49 -07:00
|
|
|
if (!jsapi.Init(mGlobal)) {
|
2014-07-18 18:31:11 -07:00
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
2014-09-11 19:18:49 -07:00
|
|
|
return;
|
2014-02-25 13:34:55 -08:00
|
|
|
}
|
2014-07-18 18:31:11 -07:00
|
|
|
JSContext* cx = jsapi.cx();
|
2014-02-25 13:34:55 -08:00
|
|
|
|
2014-11-17 01:42:00 -08:00
|
|
|
JS::Rooted<JS::Value> wrapper(cx);
|
2014-11-26 11:25:20 -08:00
|
|
|
if (!GetOrCreateDOMReflector(cx, this, &wrapper)) {
|
2014-07-18 18:31:11 -07:00
|
|
|
JS_ClearPendingException(cx);
|
|
|
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
2014-09-11 19:18:49 -07:00
|
|
|
return;
|
2014-02-25 13:34:55 -08:00
|
|
|
}
|
|
|
|
|
2014-09-11 19:18:49 -07:00
|
|
|
dom::PreserveWrapper(this);
|
2014-10-19 19:27:12 -07:00
|
|
|
|
|
|
|
// Now grab our allocation stack
|
|
|
|
if (!CaptureStack(cx, mAllocationStack)) {
|
|
|
|
JS_ClearPendingException(cx);
|
|
|
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
return;
|
|
|
|
}
|
2014-11-17 01:42:00 -08:00
|
|
|
|
|
|
|
JS::RootedObject obj(cx, &wrapper.toObject());
|
|
|
|
JS::dbg::onNewPromise(cx, obj);
|
2014-02-25 13:34:55 -08:00
|
|
|
}
|
|
|
|
|
2013-09-26 11:09:16 -07:00
|
|
|
void
|
|
|
|
Promise::MaybeResolve(JSContext* aCx,
|
2013-11-19 10:39:51 -08:00
|
|
|
JS::Handle<JS::Value> aValue)
|
2013-09-26 11:09:16 -07:00
|
|
|
{
|
|
|
|
MaybeResolveInternal(aCx, aValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Promise::MaybeReject(JSContext* aCx,
|
2013-11-19 10:39:51 -08:00
|
|
|
JS::Handle<JS::Value> aValue)
|
2013-09-26 11:09:16 -07:00
|
|
|
{
|
|
|
|
MaybeRejectInternal(aCx, aValue);
|
|
|
|
}
|
|
|
|
|
2014-09-19 23:20:41 -07:00
|
|
|
void
|
|
|
|
Promise::MaybeReject(const nsRefPtr<MediaStreamError>& aArg) {
|
|
|
|
MaybeSomething(aArg, &Promise::MaybeReject);
|
|
|
|
}
|
|
|
|
|
2014-11-11 05:47:28 -08:00
|
|
|
bool
|
2014-10-28 05:08:19 -07:00
|
|
|
Promise::PerformMicroTaskCheckpoint()
|
|
|
|
{
|
|
|
|
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
2015-05-01 19:33:01 -07:00
|
|
|
std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
|
2014-10-28 05:08:19 -07:00
|
|
|
runtime->GetPromiseMicroTaskQueue();
|
|
|
|
|
2015-05-01 19:33:01 -07:00
|
|
|
if (microtaskQueue.empty()) {
|
2014-11-11 05:47:28 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-04-27 12:00:41 -07:00
|
|
|
Maybe<AutoSafeJSContext> cx;
|
|
|
|
if (NS_IsMainThread()) {
|
|
|
|
cx.emplace();
|
|
|
|
}
|
|
|
|
|
2014-11-11 05:47:28 -08:00
|
|
|
do {
|
2015-05-01 19:33:01 -07:00
|
|
|
nsCOMPtr<nsIRunnable> runnable = microtaskQueue.front();
|
2014-10-28 05:08:19 -07:00
|
|
|
MOZ_ASSERT(runnable);
|
|
|
|
|
|
|
|
// This function can re-enter, so we remove the element before calling.
|
2015-05-01 19:33:01 -07:00
|
|
|
microtaskQueue.pop();
|
2015-04-27 12:00:41 -07:00
|
|
|
nsresult rv = runnable->Run();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (cx.isSome()) {
|
|
|
|
JS_CheckForInterrupt(cx.ref());
|
|
|
|
}
|
2015-05-01 19:33:01 -07:00
|
|
|
} while (!microtaskQueue.empty());
|
2014-11-11 05:47:28 -08:00
|
|
|
|
|
|
|
return true;
|
2014-10-28 05:08:19 -07:00
|
|
|
}
|
|
|
|
|
2013-09-11 09:03:04 -07:00
|
|
|
/* static */ bool
|
2014-01-23 10:47:29 -08:00
|
|
|
Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
2013-09-11 09:03:04 -07:00
|
|
|
{
|
|
|
|
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
|
|
|
|
|
|
|
|
JS::Rooted<JS::Value> v(aCx,
|
|
|
|
js::GetFunctionNativeReserved(&args.callee(),
|
|
|
|
SLOT_PROMISE));
|
|
|
|
MOZ_ASSERT(v.isObject());
|
|
|
|
|
|
|
|
Promise* promise;
|
2013-11-21 04:51:16 -08:00
|
|
|
if (NS_FAILED(UNWRAP_OBJECT(Promise, &v.toObject(), promise))) {
|
2013-09-11 09:03:04 -07:00
|
|
|
return Throw(aCx, NS_ERROR_UNEXPECTED);
|
|
|
|
}
|
|
|
|
|
2014-01-23 10:47:29 -08:00
|
|
|
v = js::GetFunctionNativeReserved(&args.callee(), SLOT_DATA);
|
2013-09-11 09:03:04 -07:00
|
|
|
PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32());
|
|
|
|
|
|
|
|
if (task == PromiseCallback::Resolve) {
|
2013-11-19 10:39:51 -08:00
|
|
|
promise->MaybeResolveInternal(aCx, args.get(0));
|
2014-10-19 19:27:12 -07:00
|
|
|
if (!promise->CaptureStack(aCx, promise->mFullfillmentStack)) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-09-11 09:03:04 -07:00
|
|
|
} else {
|
2013-11-19 10:39:51 -08:00
|
|
|
promise->MaybeRejectInternal(aCx, args.get(0));
|
2014-10-19 19:27:12 -07:00
|
|
|
if (!promise->CaptureStack(aCx, promise->mRejectionStack)) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-09-11 09:03:04 -07:00
|
|
|
}
|
|
|
|
|
2015-01-12 19:35:33 -08:00
|
|
|
args.rval().setUndefined();
|
2013-09-11 09:03:04 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-01-23 10:47:29 -08:00
|
|
|
/*
|
|
|
|
* 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<JSObject*> thisFunc(aCx, &args.callee());
|
|
|
|
if (!MarkAsCalledIfNotCalledBefore(aCx, thisFunc)) {
|
|
|
|
// A function from this pair has been called before.
|
2015-01-12 19:35:33 -08:00
|
|
|
args.rval().setUndefined();
|
2014-01-23 10:47:29 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Promise* promise = GetPromise(aCx, thisFunc);
|
|
|
|
MOZ_ASSERT(promise);
|
|
|
|
|
|
|
|
if (aTask == PromiseCallback::Resolve) {
|
2014-05-20 14:55:36 -07:00
|
|
|
promise->ResolveInternal(aCx, args.get(0));
|
2014-01-23 10:47:29 -08:00
|
|
|
} else {
|
2014-05-20 14:55:36 -07:00
|
|
|
promise->RejectInternal(aCx, args.get(0));
|
2014-01-23 10:47:29 -08:00
|
|
|
}
|
2015-01-12 19:35:33 -08:00
|
|
|
|
|
|
|
args.rval().setUndefined();
|
2014-01-23 10:47:29 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ bool
|
|
|
|
Promise::JSCallbackThenableResolver(JSContext* aCx,
|
|
|
|
unsigned aArgc, JS::Value* aVp)
|
|
|
|
{
|
|
|
|
return ThenableResolverCommon(aCx, PromiseCallback::Resolve, aArgc, aVp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ bool
|
|
|
|
Promise::JSCallbackThenableRejecter(JSContext* aCx,
|
|
|
|
unsigned aArgc, JS::Value* aVp)
|
|
|
|
{
|
|
|
|
return ThenableResolverCommon(aCx, PromiseCallback::Reject, aArgc, aVp);
|
|
|
|
}
|
|
|
|
|
2013-09-11 09:03:04 -07:00
|
|
|
/* static */ JSObject*
|
2015-03-09 09:50:07 -07:00
|
|
|
Promise::CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask)
|
2013-09-11 09:03:04 -07:00
|
|
|
{
|
|
|
|
JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
|
|
|
|
1 /* nargs */, 0 /* flags */,
|
2015-03-09 09:50:07 -07:00
|
|
|
nullptr);
|
2013-09-11 09:03:04 -07:00
|
|
|
if (!func) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
|
|
|
|
|
|
|
|
JS::Rooted<JS::Value> promiseObj(aCx);
|
2014-11-26 11:25:20 -08:00
|
|
|
if (!dom::GetOrCreateDOMReflector(aCx, aPromise, &promiseObj)) {
|
2013-09-11 09:03:04 -07:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
|
2014-01-23 10:47:29 -08:00
|
|
|
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 */,
|
2015-03-09 09:50:07 -07:00
|
|
|
nullptr);
|
2014-01-23 10:47:29 -08:00
|
|
|
if (!func) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
|
|
|
|
|
|
|
|
JS::Rooted<JS::Value> promiseObj(aCx);
|
2014-11-26 11:25:20 -08:00
|
|
|
if (!dom::GetOrCreateDOMReflector(aCx, aPromise, &promiseObj)) {
|
2014-01-23 10:47:29 -08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
|
2013-09-11 09:03:04 -07:00
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
/* static */ already_AddRefed<Promise>
|
2013-08-22 22:17:08 -07:00
|
|
|
Promise::Constructor(const GlobalObject& aGlobal,
|
2013-07-11 13:40:36 -07:00
|
|
|
PromiseInit& aInit, ErrorResult& aRv)
|
2013-06-11 18:41:21 -07:00
|
|
|
{
|
2014-02-25 13:34:55 -08:00
|
|
|
nsCOMPtr<nsIGlobalObject> global;
|
|
|
|
global = do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
if (!global) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
|
|
|
|
2014-07-18 18:31:11 -07:00
|
|
|
nsRefPtr<Promise> promise = Create(global, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-06-11 18:41:21 -07:00
|
|
|
|
2014-09-11 19:18:49 -07:00
|
|
|
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();
|
|
|
|
|
2013-09-11 09:03:04 -07:00
|
|
|
JS::Rooted<JSObject*> resolveFunc(cx,
|
2015-03-09 09:50:07 -07:00
|
|
|
CreateFunction(cx, this,
|
2013-09-11 09:03:04 -07:00
|
|
|
PromiseCallback::Resolve));
|
|
|
|
if (!resolveFunc) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
2014-09-11 19:18:49 -07:00
|
|
|
return;
|
2013-09-11 09:03:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> rejectFunc(cx,
|
2015-03-09 09:50:07 -07:00
|
|
|
CreateFunction(cx, this,
|
2013-09-11 09:03:04 -07:00
|
|
|
PromiseCallback::Reject));
|
|
|
|
if (!rejectFunc) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
2014-09-11 19:18:49 -07:00
|
|
|
return;
|
2013-09-11 09:03:04 -07:00
|
|
|
}
|
|
|
|
|
2015-04-08 18:23:48 -07:00
|
|
|
aInit.Call(resolveFunc, rejectFunc, aRv, "promise initializer",
|
|
|
|
CallbackObject::eRethrowExceptions, Compartment());
|
2013-06-11 18:41:21 -07:00
|
|
|
aRv.WouldReportJSException();
|
|
|
|
|
|
|
|
if (aRv.IsJSException()) {
|
2013-11-19 10:39:51 -08:00
|
|
|
JS::Rooted<JS::Value> value(cx);
|
2015-03-05 22:08:06 -08:00
|
|
|
aRv.StealJSException(cx, &value);
|
2013-06-26 06:44:21 -07:00
|
|
|
|
2014-04-09 01:30:24 -07:00
|
|
|
// we want the same behavior as this JS implementation:
|
|
|
|
// function Promise(arg) { try { arg(a, b); } catch (e) { this.reject(e); }}
|
|
|
|
if (!JS_WrapValue(cx, &value)) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
2014-09-11 19:18:49 -07:00
|
|
|
return;
|
2014-04-09 01:30:24 -07:00
|
|
|
}
|
|
|
|
|
2014-09-11 19:18:49 -07:00
|
|
|
MaybeRejectInternal(cx, value);
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
2015-01-15 14:39:02 -08:00
|
|
|
|
|
|
|
// Else aRv is an error. We _could_ reject ourselves with that error, but
|
|
|
|
// we're just going to propagate aRv out to the binding code, which will then
|
|
|
|
// throw us away and create a new promise rejected with the error on aRv. So
|
|
|
|
// there's no need to worry about rejecting ourselves here; the bindings
|
|
|
|
// will do the right thing.
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
/* static */ already_AddRefed<Promise>
|
2014-06-16 09:52:00 -07:00
|
|
|
Promise::Resolve(const GlobalObject& aGlobal,
|
2014-02-19 07:13:38 -08:00
|
|
|
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
2013-06-11 18:41:22 -07:00
|
|
|
{
|
2014-02-10 09:27:02 -08:00
|
|
|
// If a Promise was passed, just return it.
|
2014-02-19 07:13:38 -08:00
|
|
|
if (aValue.isObject()) {
|
2014-06-16 09:52:00 -07:00
|
|
|
JS::Rooted<JSObject*> valueObj(aGlobal.Context(), &aValue.toObject());
|
2014-02-10 09:27:02 -08:00
|
|
|
Promise* nextPromise;
|
|
|
|
nsresult rv = UNWRAP_OBJECT(Promise, valueObj, nextPromise);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
nsRefPtr<Promise> addRefed = nextPromise;
|
|
|
|
return addRefed.forget();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-25 13:34:55 -08:00
|
|
|
nsCOMPtr<nsIGlobalObject> global =
|
|
|
|
do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
if (!global) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
2013-06-11 18:41:22 -07:00
|
|
|
}
|
|
|
|
|
2014-10-19 19:27:12 -07:00
|
|
|
nsRefPtr<Promise> p = Resolve(global, aGlobal.Context(), aValue, aRv);
|
|
|
|
if (p) {
|
|
|
|
p->mFullfillmentStack = p->mAllocationStack;
|
|
|
|
}
|
|
|
|
return p.forget();
|
2014-01-13 14:36:03 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ already_AddRefed<Promise>
|
2014-02-25 13:34:55 -08:00
|
|
|
Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
|
2014-02-10 09:27:02 -08:00
|
|
|
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
2014-01-13 14:36:03 -08:00
|
|
|
{
|
2014-07-18 18:31:11 -07:00
|
|
|
nsRefPtr<Promise> promise = Create(aGlobal, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-06-11 18:41:22 -07:00
|
|
|
|
2014-01-13 14:36:03 -08:00
|
|
|
promise->MaybeResolveInternal(aCx, aValue);
|
2013-07-11 13:40:36 -07:00
|
|
|
return promise.forget();
|
2013-06-11 18:41:22 -07:00
|
|
|
}
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
/* static */ already_AddRefed<Promise>
|
2014-06-16 09:52:00 -07:00
|
|
|
Promise::Reject(const GlobalObject& aGlobal,
|
2014-02-19 07:13:38 -08:00
|
|
|
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
2013-06-11 18:41:22 -07:00
|
|
|
{
|
2014-02-25 13:34:55 -08:00
|
|
|
nsCOMPtr<nsIGlobalObject> global =
|
|
|
|
do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
if (!global) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
2013-06-11 18:41:22 -07:00
|
|
|
}
|
|
|
|
|
2014-10-19 19:27:12 -07:00
|
|
|
nsRefPtr<Promise> p = Reject(global, aGlobal.Context(), aValue, aRv);
|
|
|
|
if (p) {
|
|
|
|
p->mRejectionStack = p->mAllocationStack;
|
|
|
|
}
|
|
|
|
return p.forget();
|
2014-01-13 14:36:03 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ already_AddRefed<Promise>
|
2014-02-25 13:34:55 -08:00
|
|
|
Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
|
2014-01-13 14:36:03 -08:00
|
|
|
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
|
|
|
{
|
2014-07-18 18:31:11 -07:00
|
|
|
nsRefPtr<Promise> promise = Create(aGlobal, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-06-11 18:41:22 -07:00
|
|
|
|
2014-01-13 14:36:03 -08:00
|
|
|
promise->MaybeRejectInternal(aCx, aValue);
|
2013-07-11 13:40:36 -07:00
|
|
|
return promise.forget();
|
2013-06-11 18:41:22 -07:00
|
|
|
}
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
already_AddRefed<Promise>
|
2014-04-09 01:30:24 -07:00
|
|
|
Promise::Then(JSContext* aCx, AnyCallback* aResolveCallback,
|
2014-07-18 18:31:11 -07:00
|
|
|
AnyCallback* aRejectCallback, ErrorResult& aRv)
|
2013-06-11 18:41:22 -07:00
|
|
|
{
|
2014-07-18 18:31:11 -07:00
|
|
|
nsRefPtr<Promise> promise = Create(GetParentObject(), aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-06-11 18:41:22 -07:00
|
|
|
|
2014-04-09 01:30:24 -07:00
|
|
|
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
nsRefPtr<PromiseCallback> resolveCb =
|
2014-04-09 01:30:24 -07:00
|
|
|
PromiseCallback::Factory(promise, global, aResolveCallback,
|
|
|
|
PromiseCallback::Resolve);
|
2013-06-11 18:41:22 -07:00
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
nsRefPtr<PromiseCallback> rejectCb =
|
2014-04-09 01:30:24 -07:00
|
|
|
PromiseCallback::Factory(promise, global, aRejectCallback,
|
|
|
|
PromiseCallback::Reject);
|
2013-06-11 18:41:22 -07:00
|
|
|
|
|
|
|
AppendCallbacks(resolveCb, rejectCb);
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
return promise.forget();
|
2013-06-11 18:41:22 -07:00
|
|
|
}
|
|
|
|
|
2013-07-11 13:40:36 -07:00
|
|
|
already_AddRefed<Promise>
|
2014-07-18 18:31:11 -07:00
|
|
|
Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback, ErrorResult& aRv)
|
2013-06-11 18:41:22 -07:00
|
|
|
{
|
2014-01-28 10:14:57 -08:00
|
|
|
nsRefPtr<AnyCallback> resolveCb;
|
2014-07-18 18:31:11 -07:00
|
|
|
return Then(aCx, resolveCb, aRejectCallback, aRv);
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
|
|
|
|
2013-11-19 13:53:00 -08:00
|
|
|
/**
|
|
|
|
* The CountdownHolder class encapsulates Promise.all countdown functions and
|
|
|
|
* the countdown holder parts of the Promises spec. It maintains the result
|
|
|
|
* array and AllResolveHandlers use SetValue() to set the array indices.
|
|
|
|
*/
|
2015-03-21 09:28:04 -07:00
|
|
|
class CountdownHolder final : public nsISupports
|
2013-11-19 13:53:00 -08:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
|
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CountdownHolder)
|
|
|
|
|
2014-04-09 01:30:24 -07:00
|
|
|
CountdownHolder(const GlobalObject& aGlobal, Promise* aPromise,
|
|
|
|
uint32_t aCountdown)
|
2013-11-19 13:53:00 -08:00
|
|
|
: mPromise(aPromise), mCountdown(aCountdown)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aCountdown != 0);
|
2014-06-16 11:08:00 -07:00
|
|
|
JSContext* cx = aGlobal.Context();
|
2014-04-09 01:30:24 -07:00
|
|
|
|
2014-06-16 11:08:00 -07:00
|
|
|
// The only time aGlobal.Context() and aGlobal.Get() are not
|
2014-04-09 01:30:24 -07:00
|
|
|
// 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
|
|
|
|
|
2013-11-19 13:53:00 -08:00
|
|
|
JSAutoCompartment ac(cx, aGlobal.Get());
|
2014-02-12 02:50:46 -08:00
|
|
|
mValues = JS_NewArrayObject(cx, aCountdown);
|
2013-11-19 13:53:00 -08:00
|
|
|
mozilla::HoldJSObjects(this);
|
|
|
|
}
|
|
|
|
|
2014-06-23 12:56:07 -07:00
|
|
|
private:
|
2013-11-19 13:53:00 -08:00
|
|
|
~CountdownHolder()
|
|
|
|
{
|
|
|
|
mozilla::DropJSObjects(this);
|
|
|
|
}
|
|
|
|
|
2014-06-23 12:56:07 -07:00
|
|
|
public:
|
2013-11-19 13:53:00 -08:00
|
|
|
void SetValue(uint32_t index, const JS::Handle<JS::Value> aValue)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mCountdown > 0);
|
|
|
|
|
2014-03-04 10:05:08 -08:00
|
|
|
ThreadsafeAutoSafeJSContext cx;
|
2013-11-19 13:53:00 -08:00
|
|
|
JSAutoCompartment ac(cx, mValues);
|
2014-04-09 01:08:39 -07:00
|
|
|
{
|
2014-04-09 01:30:24 -07:00
|
|
|
|
2013-11-19 13:53:00 -08:00
|
|
|
AutoDontReportUncaught silenceReporting(cx);
|
2014-04-09 01:30:24 -07:00
|
|
|
JS::Rooted<JS::Value> value(cx, aValue);
|
2014-04-30 02:10:33 -07:00
|
|
|
JS::Rooted<JSObject*> values(cx, mValues);
|
2014-04-09 01:30:24 -07:00
|
|
|
if (!JS_WrapValue(cx, &value) ||
|
2014-04-30 02:10:33 -07:00
|
|
|
!JS_DefineElement(cx, values, index, value, JSPROP_ENUMERATE)) {
|
2013-11-19 13:53:00 -08:00
|
|
|
MOZ_ASSERT(JS_IsExceptionPending(cx));
|
|
|
|
JS::Rooted<JS::Value> exn(cx);
|
|
|
|
JS_GetPendingException(cx, &exn);
|
|
|
|
|
|
|
|
mPromise->MaybeReject(cx, exn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
--mCountdown;
|
|
|
|
if (mCountdown == 0) {
|
|
|
|
JS::Rooted<JS::Value> result(cx, JS::ObjectValue(*mValues));
|
|
|
|
mPromise->MaybeResolve(cx, result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsRefPtr<Promise> mPromise;
|
|
|
|
uint32_t mCountdown;
|
|
|
|
JS::Heap<JSObject*> 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_SCRIPT_OBJECTS
|
|
|
|
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 AllResolveHandler 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.
|
|
|
|
*/
|
2015-03-21 09:28:04 -07:00
|
|
|
class AllResolveHandler final : public PromiseNativeHandler
|
2013-11-19 13:53:00 -08:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
|
|
NS_DECL_CYCLE_COLLECTION_CLASS(AllResolveHandler)
|
|
|
|
|
|
|
|
AllResolveHandler(CountdownHolder* aHolder, uint32_t aIndex)
|
|
|
|
: mCountdownHolder(aHolder), mIndex(aIndex)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aHolder);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-03-21 09:28:04 -07:00
|
|
|
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
2013-11-19 13:53:00 -08:00
|
|
|
{
|
|
|
|
mCountdownHolder->SetValue(mIndex, aValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-03-21 09:28:04 -07:00
|
|
|
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
2013-11-19 13:53:00 -08:00
|
|
|
{
|
|
|
|
// Should never be attached to Promise as a reject handler.
|
|
|
|
MOZ_ASSERT(false, "AllResolveHandler should never be attached to a Promise's reject handler!");
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2014-06-23 12:56:07 -07:00
|
|
|
~AllResolveHandler()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-11-19 13:53:00 -08:00
|
|
|
nsRefPtr<CountdownHolder> mCountdownHolder;
|
|
|
|
uint32_t mIndex;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(AllResolveHandler)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(AllResolveHandler)
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AllResolveHandler)
|
|
|
|
NS_INTERFACE_MAP_END_INHERITING(PromiseNativeHandler)
|
|
|
|
|
2014-04-25 09:49:00 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION(AllResolveHandler, mCountdownHolder)
|
2013-11-19 13:53:00 -08:00
|
|
|
|
|
|
|
/* static */ already_AddRefed<Promise>
|
2014-06-16 09:52:00 -07:00
|
|
|
Promise::All(const GlobalObject& aGlobal,
|
2013-11-19 13:53:00 -08:00
|
|
|
const Sequence<JS::Value>& aIterable, ErrorResult& aRv)
|
2015-04-29 08:59:43 -07:00
|
|
|
{
|
|
|
|
JSContext* cx = aGlobal.Context();
|
|
|
|
|
|
|
|
nsTArray<nsRefPtr<Promise>> promiseList;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < aIterable.Length(); ++i) {
|
|
|
|
JS::Rooted<JS::Value> value(cx, aIterable.ElementAt(i));
|
|
|
|
nsRefPtr<Promise> nextPromise = Promise::Resolve(aGlobal, value, aRv);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!aRv.Failed());
|
|
|
|
|
|
|
|
promiseList.AppendElement(Move(nextPromise));
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise::All(aGlobal, promiseList, aRv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ already_AddRefed<Promise>
|
|
|
|
Promise::All(const GlobalObject& aGlobal,
|
|
|
|
const nsTArray<nsRefPtr<Promise>>& aPromiseList, ErrorResult& aRv)
|
2013-11-19 13:53:00 -08:00
|
|
|
{
|
2014-02-25 13:34:55 -08:00
|
|
|
nsCOMPtr<nsIGlobalObject> global =
|
|
|
|
do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
if (!global) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
2013-11-19 13:53:00 -08:00
|
|
|
}
|
|
|
|
|
2014-06-16 09:52:00 -07:00
|
|
|
JSContext* cx = aGlobal.Context();
|
|
|
|
|
2015-04-29 08:59:43 -07:00
|
|
|
if (aPromiseList.IsEmpty()) {
|
2014-06-16 09:52:00 -07:00
|
|
|
JS::Rooted<JSObject*> empty(cx, JS_NewArrayObject(cx, 0));
|
2013-11-19 13:53:00 -08:00
|
|
|
if (!empty) {
|
|
|
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2014-06-16 09:52:00 -07:00
|
|
|
JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*empty));
|
2014-10-19 19:27:12 -07:00
|
|
|
// 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);
|
2013-11-19 13:53:00 -08:00
|
|
|
}
|
|
|
|
|
2014-07-18 18:31:11 -07:00
|
|
|
nsRefPtr<Promise> promise = Create(global, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-11-19 13:53:00 -08:00
|
|
|
nsRefPtr<CountdownHolder> holder =
|
2015-04-29 08:59:43 -07:00
|
|
|
new CountdownHolder(aGlobal, promise, aPromiseList.Length());
|
2013-11-19 13:53:00 -08:00
|
|
|
|
2014-06-16 09:52:00 -07:00
|
|
|
JS::Rooted<JSObject*> obj(cx, JS::CurrentGlobalOrNull(cx));
|
2014-04-09 01:30:24 -07:00
|
|
|
if (!obj) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(promise, obj);
|
2013-11-19 13:53:00 -08:00
|
|
|
|
2015-04-29 08:59:43 -07:00
|
|
|
for (uint32_t i = 0; i < aPromiseList.Length(); ++i) {
|
2013-11-19 13:53:00 -08:00
|
|
|
nsRefPtr<PromiseNativeHandler> resolveHandler =
|
|
|
|
new AllResolveHandler(holder, i);
|
|
|
|
|
|
|
|
nsRefPtr<PromiseCallback> resolveCb =
|
|
|
|
new NativePromiseCallback(resolveHandler, Resolved);
|
2015-04-29 08:59:43 -07:00
|
|
|
|
2013-11-19 13:53:00 -08:00
|
|
|
// Every promise gets its own resolve callback, which will set the right
|
|
|
|
// index in the array to the resolution value.
|
2015-04-29 08:59:43 -07:00
|
|
|
aPromiseList[i]->AppendCallbacks(resolveCb, rejectCb);
|
2013-11-19 13:53:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ already_AddRefed<Promise>
|
2014-06-16 09:52:00 -07:00
|
|
|
Promise::Race(const GlobalObject& aGlobal,
|
2013-11-19 13:53:00 -08:00
|
|
|
const Sequence<JS::Value>& aIterable, ErrorResult& aRv)
|
|
|
|
{
|
2014-02-25 13:34:55 -08:00
|
|
|
nsCOMPtr<nsIGlobalObject> global =
|
|
|
|
do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
if (!global) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
2013-11-19 13:53:00 -08:00
|
|
|
}
|
|
|
|
|
2014-06-16 09:52:00 -07:00
|
|
|
JSContext* cx = aGlobal.Context();
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> obj(cx, JS::CurrentGlobalOrNull(cx));
|
2014-04-09 01:30:24 -07:00
|
|
|
if (!obj) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-07-18 18:31:11 -07:00
|
|
|
nsRefPtr<Promise> promise = Create(global, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2014-04-09 01:30:24 -07:00
|
|
|
|
|
|
|
nsRefPtr<PromiseCallback> resolveCb =
|
|
|
|
new ResolvePromiseCallback(promise, obj);
|
|
|
|
|
|
|
|
nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(promise, obj);
|
2013-11-19 13:53:00 -08:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < aIterable.Length(); ++i) {
|
2014-06-16 09:52:00 -07:00
|
|
|
JS::Rooted<JS::Value> value(cx, aIterable.ElementAt(i));
|
|
|
|
nsRefPtr<Promise> nextPromise = Promise::Resolve(aGlobal, value, aRv);
|
2014-02-10 09:27:02 -08:00
|
|
|
// According to spec, Resolve can throw, but our implementation never does.
|
|
|
|
// Well it does when window isn't passed on the main thread, but that is an
|
|
|
|
// implementation detail which should never be reached since we are checking
|
|
|
|
// for window above. Remove this when subclassing is supported.
|
2013-11-19 13:53:00 -08:00
|
|
|
MOZ_ASSERT(!aRv.Failed());
|
|
|
|
nextPromise->AppendCallbacks(resolveCb, rejectCb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
2013-11-19 10:43:51 -08:00
|
|
|
void
|
|
|
|
Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
|
|
|
|
{
|
|
|
|
nsRefPtr<PromiseCallback> resolveCb =
|
2013-11-19 13:53:00 -08:00
|
|
|
new NativePromiseCallback(aRunnable, Resolved);
|
2013-11-19 10:43:51 -08:00
|
|
|
|
|
|
|
nsRefPtr<PromiseCallback> rejectCb =
|
2013-11-19 13:53:00 -08:00
|
|
|
new NativePromiseCallback(aRunnable, Rejected);
|
2013-11-19 10:43:51 -08:00
|
|
|
|
|
|
|
AppendCallbacks(resolveCb, rejectCb);
|
|
|
|
}
|
|
|
|
|
2015-01-15 14:39:02 -08:00
|
|
|
JSObject*
|
|
|
|
Promise::GlobalJSObject() const
|
|
|
|
{
|
|
|
|
return mGlobal->GetGlobalJSObject();
|
|
|
|
}
|
|
|
|
|
|
|
|
JSCompartment*
|
|
|
|
Promise::Compartment() const
|
|
|
|
{
|
|
|
|
return js::GetObjectCompartment(GlobalJSObject());
|
|
|
|
}
|
|
|
|
|
2013-06-11 18:41:21 -07:00
|
|
|
void
|
2013-07-11 13:40:36 -07:00
|
|
|
Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
|
|
|
|
PromiseCallback* aRejectCallback)
|
2013-06-11 18:41:21 -07:00
|
|
|
{
|
2015-04-27 12:00:41 -07:00
|
|
|
if (mGlobal->IsDying()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
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`.
|
2013-06-11 18:41:22 -07:00
|
|
|
}
|
2015-04-10 08:27:57 -07:00
|
|
|
mIsLastInChain = false;
|
2013-06-11 18:41:22 -07:00
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
|
|
|
// Now that there is a callback, we don't need to report anymore.
|
|
|
|
mHadRejectCallback = true;
|
|
|
|
RemoveFeature();
|
|
|
|
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2014-03-12 07:31:03 -07:00
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
mResolveCallbacks.AppendElement(aResolveCallback);
|
|
|
|
mRejectCallbacks.AppendElement(aRejectCallback);
|
2013-06-11 18:41:21 -07:00
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
// If promise's state is fulfilled, queue a task to process our fulfill
|
2013-09-11 09:03:04 -07:00
|
|
|
// callbacks with promise's result. If promise's state is rejected, queue a
|
|
|
|
// task to process our reject callbacks with promise's result.
|
2014-10-28 05:08:19 -07:00
|
|
|
if (mState != Pending) {
|
|
|
|
EnqueueCallbackTasks();
|
2013-06-11 18:41:21 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-21 09:28:04 -07:00
|
|
|
class WrappedWorkerRunnable final : public WorkerSameThreadRunnable
|
2014-08-19 03:39:56 -07:00
|
|
|
{
|
|
|
|
public:
|
2015-04-10 08:27:57 -07:00
|
|
|
WrappedWorkerRunnable(workers::WorkerPrivate* aWorkerPrivate, nsIRunnable* aRunnable)
|
2014-08-19 03:39:56 -07:00
|
|
|
: WorkerSameThreadRunnable(aWorkerPrivate)
|
|
|
|
, mRunnable(aRunnable)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aRunnable);
|
|
|
|
MOZ_COUNT_CTOR(WrappedWorkerRunnable);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2015-04-10 08:27:57 -07:00
|
|
|
WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
|
2014-08-19 03:39:56 -07:00
|
|
|
{
|
|
|
|
NS_ASSERT_OWNINGTHREAD(WrappedWorkerRunnable);
|
|
|
|
mRunnable->Run();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
virtual
|
|
|
|
~WrappedWorkerRunnable()
|
|
|
|
{
|
|
|
|
MOZ_COUNT_DTOR(WrappedWorkerRunnable);
|
|
|
|
NS_ASSERT_OWNINGTHREAD(WrappedWorkerRunnable);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIRunnable> mRunnable;
|
|
|
|
NS_DECL_OWNINGTHREAD
|
|
|
|
};
|
|
|
|
|
|
|
|
/* static */ void
|
2014-10-28 05:08:19 -07:00
|
|
|
Promise::DispatchToMicroTask(nsIRunnable* aRunnable)
|
2014-08-19 03:39:56 -07:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(aRunnable);
|
2014-10-28 05:08:19 -07:00
|
|
|
|
|
|
|
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
2015-05-01 19:33:01 -07:00
|
|
|
std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
|
2014-10-28 05:08:19 -07:00
|
|
|
runtime->GetPromiseMicroTaskQueue();
|
|
|
|
|
2015-05-01 19:33:01 -07:00
|
|
|
microtaskQueue.push(aRunnable);
|
2014-08-19 03:39:56 -07:00
|
|
|
}
|
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2013-08-28 21:30:06 -07:00
|
|
|
void
|
|
|
|
Promise::MaybeReportRejected()
|
|
|
|
{
|
|
|
|
if (mState != Rejected || mHadRejectCallback || mResult.isUndefined()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-03 22:24:59 -07:00
|
|
|
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<JSObject*> obj(cx, GetWrapper());
|
|
|
|
MOZ_ASSERT(obj); // We preserve our wrapper, so should always have one here.
|
|
|
|
JS::Rooted<JS::Value> val(cx, mResult);
|
|
|
|
JS::ExposeValueToActiveJS(val);
|
|
|
|
|
|
|
|
JSAutoCompartment ac(cx, obj);
|
|
|
|
if (!JS_WrapValue(cx, &val)) {
|
|
|
|
JS_ClearPendingException(cx);
|
2014-01-30 09:30:29 -08:00
|
|
|
return;
|
|
|
|
}
|
2014-07-03 22:24:59 -07:00
|
|
|
|
|
|
|
js::ErrorReport report(cx);
|
|
|
|
if (!report.init(cx, val)) {
|
|
|
|
JS_ClearPendingException(cx);
|
2013-08-28 21:30:06 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-08 16:30:10 -07:00
|
|
|
nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
|
2014-09-29 06:34:20 -07:00
|
|
|
bool isMainThread = MOZ_LIKELY(NS_IsMainThread());
|
|
|
|
bool isChrome = isMainThread ? nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj))
|
|
|
|
: GetCurrentThreadWorkerPrivate()->IsChromeWorker();
|
|
|
|
nsPIDOMWindow* win = isMainThread ? xpc::WindowGlobalOrNull(obj) : nullptr;
|
|
|
|
xpcReport->Init(report.report(), report.message(), isChrome, win ? win->WindowID() : 0);
|
2013-08-28 21:30:06 -07:00
|
|
|
|
|
|
|
// Now post an event to do the real reporting async
|
2014-01-10 14:07:46 -08:00
|
|
|
// Since Promises preserve their wrapper, it is essential to nsRefPtr<> the
|
|
|
|
// AsyncErrorReporter, otherwise if the call to DispatchToMainThread fails, it
|
|
|
|
// will leak. See Bug 958684.
|
|
|
|
nsRefPtr<AsyncErrorReporter> r =
|
2014-09-08 16:30:10 -07:00
|
|
|
new AsyncErrorReporter(CycleCollectedJSRuntime::Get()->Runtime(), xpcReport);
|
2014-01-10 14:07:46 -08:00
|
|
|
NS_DispatchToMainThread(r);
|
2013-08-28 21:30:06 -07:00
|
|
|
}
|
2015-04-10 08:27:57 -07:00
|
|
|
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2013-08-28 21:30:06 -07:00
|
|
|
|
2013-09-11 09:03:04 -07:00
|
|
|
void
|
2013-09-26 11:09:16 -07:00
|
|
|
Promise::MaybeResolveInternal(JSContext* aCx,
|
2014-10-28 05:08:19 -07:00
|
|
|
JS::Handle<JS::Value> aValue)
|
2013-09-11 09:03:04 -07:00
|
|
|
{
|
|
|
|
if (mResolvePending) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
ResolveInternal(aCx, aValue);
|
2013-09-11 09:03:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-09-26 11:09:16 -07:00
|
|
|
Promise::MaybeRejectInternal(JSContext* aCx,
|
2014-10-28 05:08:19 -07:00
|
|
|
JS::Handle<JS::Value> aValue)
|
2013-09-11 09:03:04 -07:00
|
|
|
{
|
|
|
|
if (mResolvePending) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
RejectInternal(aCx, aValue);
|
2013-09-11 09:03:04 -07:00
|
|
|
}
|
|
|
|
|
2014-01-23 10:47:29 -08:00
|
|
|
void
|
|
|
|
Promise::HandleException(JSContext* aCx)
|
|
|
|
{
|
|
|
|
JS::Rooted<JS::Value> exn(aCx);
|
|
|
|
if (JS_GetPendingException(aCx, &exn)) {
|
|
|
|
JS_ClearPendingException(aCx);
|
2014-10-28 05:08:19 -07:00
|
|
|
RejectInternal(aCx, exn);
|
2014-01-23 10:47:29 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-11 09:03:04 -07:00
|
|
|
void
|
|
|
|
Promise::ResolveInternal(JSContext* aCx,
|
2014-10-28 05:08:19 -07:00
|
|
|
JS::Handle<JS::Value> aValue)
|
2013-09-11 09:03:04 -07:00
|
|
|
{
|
|
|
|
mResolvePending = true;
|
|
|
|
|
2013-11-19 10:39:51 -08:00
|
|
|
if (aValue.isObject()) {
|
2013-11-19 13:53:00 -08:00
|
|
|
AutoDontReportUncaught silenceReporting(aCx);
|
2013-11-19 10:39:51 -08:00
|
|
|
JS::Rooted<JSObject*> valueObj(aCx, &aValue.toObject());
|
2013-09-11 09:03:04 -07:00
|
|
|
|
2014-01-23 10:47:29 -08:00
|
|
|
// Thenables.
|
|
|
|
JS::Rooted<JS::Value> then(aCx);
|
|
|
|
if (!JS_GetProperty(aCx, valueObj, "then", &then)) {
|
|
|
|
HandleException(aCx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-25 04:13:28 -07:00
|
|
|
if (then.isObject() && JS::IsCallable(&then.toObject())) {
|
2014-05-20 14:21:13 -07:00
|
|
|
// This is the then() function of the thenable aValueObj.
|
2014-01-23 10:47:29 -08:00
|
|
|
JS::Rooted<JSObject*> thenObj(aCx, &then.toObject());
|
2015-04-17 19:01:02 -07:00
|
|
|
|
|
|
|
// Add a fast path for the case when we're resolved with an actual
|
|
|
|
// Promise. This has two requirements:
|
|
|
|
//
|
|
|
|
// 1) valueObj is a Promise.
|
|
|
|
// 2) thenObj is a JSFunction backed by our actual Promise::Then
|
|
|
|
// implementation.
|
|
|
|
//
|
|
|
|
// If those requirements are satisfied, then we know exactly what
|
|
|
|
// thenObj.call(valueObj) will do, so we can optimize a bit and avoid ever
|
|
|
|
// entering JS for this stuff.
|
|
|
|
Promise* nextPromise;
|
|
|
|
if (PromiseBinding::IsThenMethod(thenObj) &&
|
|
|
|
NS_SUCCEEDED(UNWRAP_OBJECT(Promise, valueObj, nextPromise))) {
|
|
|
|
// If we were taking the codepath that involves ThenableResolverTask and
|
|
|
|
// PromiseInit below, then eventually, in ThenableResolverTask::Run, we
|
|
|
|
// would create some JSFunctions in the compartment of
|
|
|
|
// this->GetWrapper() and pass them to the PromiseInit. So by the time
|
|
|
|
// we'd see the resolution value it would be wrapped into the
|
|
|
|
// compartment of this->GetWrapper(). The global of that compartment is
|
|
|
|
// this->GetGlobalJSObject(), so use that as the global for
|
|
|
|
// ResolvePromiseCallback/RejectPromiseCallback.
|
|
|
|
JS::Rooted<JSObject*> glob(aCx, GlobalJSObject());
|
|
|
|
nsRefPtr<PromiseCallback> resolveCb = new ResolvePromiseCallback(this, glob);
|
|
|
|
nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(this, glob);
|
|
|
|
nsRefPtr<FastThenableResolverTask> task =
|
|
|
|
new FastThenableResolverTask(resolveCb, rejectCb, nextPromise);
|
|
|
|
DispatchToMicroTask(task);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-23 10:47:29 -08:00
|
|
|
nsRefPtr<PromiseInit> thenCallback =
|
|
|
|
new PromiseInit(thenObj, mozilla::dom::GetIncumbentGlobal());
|
2014-08-19 03:39:56 -07:00
|
|
|
nsRefPtr<ThenableResolverTask> task =
|
|
|
|
new ThenableResolverTask(this, valueObj, thenCallback);
|
2014-10-28 05:08:19 -07:00
|
|
|
DispatchToMicroTask(task);
|
2013-09-11 09:03:04 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
MaybeSettle(aValue, Resolved);
|
2013-09-11 09:03:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Promise::RejectInternal(JSContext* aCx,
|
2014-10-28 05:08:19 -07:00
|
|
|
JS::Handle<JS::Value> aValue)
|
2013-09-11 09:03:04 -07:00
|
|
|
{
|
|
|
|
mResolvePending = true;
|
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
MaybeSettle(aValue, Rejected);
|
2013-09-11 09:03:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-11-17 01:44:00 -08:00
|
|
|
Promise::Settle(JS::Handle<JS::Value> aValue, PromiseState aState)
|
2013-09-11 09:03:04 -07:00
|
|
|
{
|
2015-04-27 12:00:41 -07:00
|
|
|
if (mGlobal->IsDying()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-17 01:44:00 -08:00
|
|
|
mSettlementTimestamp = TimeStamp::Now();
|
2015-04-27 12:00:41 -07:00
|
|
|
|
2013-09-11 09:03:04 -07:00
|
|
|
SetResult(aValue);
|
|
|
|
SetState(aState);
|
2014-11-17 01:44:00 -08:00
|
|
|
|
|
|
|
AutoJSAPI jsapi;
|
|
|
|
jsapi.Init();
|
|
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
JS::RootedObject wrapper(cx, GetWrapper());
|
|
|
|
MOZ_ASSERT(wrapper); // We preserved it
|
|
|
|
JSAutoCompartment ac(cx, wrapper);
|
|
|
|
JS::dbg::onPromiseSettled(cx, wrapper);
|
2014-03-12 07:31:03 -07:00
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
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)
|
2014-03-12 07:31:03 -07:00
|
|
|
// If the Promise was rejected, and there is no reject handler already setup,
|
|
|
|
// watch for thread shutdown.
|
|
|
|
if (aState == PromiseState::Rejected &&
|
|
|
|
!mHadRejectCallback &&
|
|
|
|
!NS_IsMainThread()) {
|
2015-04-10 08:27:57 -07:00
|
|
|
workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
2014-03-12 07:31:03 -07:00
|
|
|
MOZ_ASSERT(worker);
|
|
|
|
worker->AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
mFeature = new PromiseReportRejectFeature(this);
|
|
|
|
if (NS_WARN_IF(!worker->AddFeature(worker->GetJSContext(), mFeature))) {
|
2014-06-05 12:21:56 -07:00
|
|
|
// To avoid a false RemoveFeature().
|
|
|
|
mFeature = nullptr;
|
2014-03-12 07:31:03 -07:00
|
|
|
// Worker is shutting down, report rejection immediately since it is
|
|
|
|
// unlikely that reject callbacks will be added after this point.
|
2014-06-05 12:21:56 -07:00
|
|
|
MaybeReportRejectedOnce();
|
2014-03-12 07:31:03 -07:00
|
|
|
}
|
|
|
|
}
|
2015-04-10 08:27:57 -07:00
|
|
|
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2014-03-12 07:31:03 -07:00
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
EnqueueCallbackTasks();
|
|
|
|
}
|
|
|
|
|
2014-11-17 01:44:00 -08:00
|
|
|
void
|
|
|
|
Promise::MaybeSettle(JS::Handle<JS::Value> aValue,
|
|
|
|
PromiseState aState)
|
|
|
|
{
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2014-10-28 05:08:19 -07:00
|
|
|
void
|
|
|
|
Promise::EnqueueCallbackTasks()
|
|
|
|
{
|
|
|
|
nsTArray<nsRefPtr<PromiseCallback>> callbacks;
|
|
|
|
callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
|
|
|
|
: mRejectCallbacks);
|
|
|
|
mResolveCallbacks.Clear();
|
|
|
|
mRejectCallbacks.Clear();
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < callbacks.Length(); ++i) {
|
|
|
|
nsRefPtr<PromiseCallbackTask> task =
|
|
|
|
new PromiseCallbackTask(this, callbacks[i], mResult);
|
2014-10-28 05:08:19 -07:00
|
|
|
DispatchToMicroTask(task);
|
2014-10-28 05:08:19 -07:00
|
|
|
}
|
2013-09-11 09:03:04 -07:00
|
|
|
}
|
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2014-03-12 07:31:03 -07:00
|
|
|
void
|
|
|
|
Promise::RemoveFeature()
|
|
|
|
{
|
|
|
|
if (mFeature) {
|
2015-04-10 08:27:57 -07:00
|
|
|
workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
2014-03-12 07:31:03 -07:00
|
|
|
MOZ_ASSERT(worker);
|
|
|
|
worker->RemoveFeature(worker->GetJSContext(), mFeature);
|
|
|
|
mFeature = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
PromiseReportRejectFeature::Notify(JSContext* aCx, workers::Status aStatus)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aStatus > workers::Running);
|
|
|
|
mPromise->MaybeReportRejectedOnce();
|
|
|
|
// After this point, `this` has been deleted by RemoveFeature!
|
|
|
|
return true;
|
|
|
|
}
|
2015-04-10 08:27:57 -07:00
|
|
|
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
2014-03-12 07:31:03 -07:00
|
|
|
|
2014-10-19 19:27:12 -07:00
|
|
|
bool
|
|
|
|
Promise::CaptureStack(JSContext* aCx, JS::Heap<JSObject*>& aTarget)
|
|
|
|
{
|
|
|
|
JS::Rooted<JSObject*> stack(aCx);
|
|
|
|
if (!JS::CaptureCurrentStack(aCx, &stack)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
aTarget = stack;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-10-19 19:27:36 -07:00
|
|
|
void
|
|
|
|
Promise::GetDependentPromises(nsTArray<nsRefPtr<Promise>>& aPromises)
|
|
|
|
{
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-24 05:56:54 -08:00
|
|
|
// A WorkerRunnable to resolve/reject the Promise on the worker thread.
|
|
|
|
|
|
|
|
class PromiseWorkerProxyRunnable : public workers::WorkerRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy,
|
2014-12-16 16:30:39 -08:00
|
|
|
const JSStructuredCloneCallbacks* aCallbacks,
|
2014-02-24 05:56:54 -08:00
|
|
|
JSAutoStructuredCloneBuffer&& aBuffer,
|
|
|
|
PromiseWorkerProxy::RunCallbackFunc aFunc)
|
|
|
|
: WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(),
|
|
|
|
WorkerThreadUnchangedBusyCount)
|
|
|
|
, mPromiseWorkerProxy(aPromiseWorkerProxy)
|
|
|
|
, mCallbacks(aCallbacks)
|
|
|
|
, mBuffer(Move(aBuffer))
|
|
|
|
, mFunc(aFunc)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(mPromiseWorkerProxy);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
|
|
|
WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aWorkerPrivate);
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
|
|
|
|
|
|
|
|
MOZ_ASSERT(mPromiseWorkerProxy);
|
|
|
|
nsRefPtr<Promise> workerPromise = mPromiseWorkerProxy->GetWorkerPromise();
|
|
|
|
MOZ_ASSERT(workerPromise);
|
|
|
|
|
|
|
|
// Here we convert the buffer to a JS::Value.
|
|
|
|
JS::Rooted<JS::Value> value(aCx);
|
|
|
|
if (!mBuffer.read(aCx, &value, mCallbacks, mPromiseWorkerProxy)) {
|
|
|
|
JS_ClearPendingException(aCx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO Bug 975246 - nsRefPtr should support operator |nsRefPtr->*funcType|.
|
|
|
|
(workerPromise.get()->*mFunc)(aCx,
|
2014-10-28 05:08:19 -07:00
|
|
|
value);
|
2014-02-24 05:56:54 -08:00
|
|
|
|
|
|
|
// Release the Promise because it has been resolved/rejected for sure.
|
|
|
|
mPromiseWorkerProxy->CleanUp(aCx);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
~PromiseWorkerProxyRunnable() {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
|
2014-12-16 16:30:39 -08:00
|
|
|
const JSStructuredCloneCallbacks* mCallbacks;
|
2014-02-24 05:56:54 -08:00
|
|
|
JSAutoStructuredCloneBuffer mBuffer;
|
|
|
|
|
|
|
|
// Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
|
|
|
|
PromiseWorkerProxy::RunCallbackFunc mFunc;
|
|
|
|
};
|
|
|
|
|
2014-12-17 06:49:36 -08:00
|
|
|
/* static */
|
|
|
|
already_AddRefed<PromiseWorkerProxy>
|
2015-04-10 08:27:57 -07:00
|
|
|
PromiseWorkerProxy::Create(workers::WorkerPrivate* aWorkerPrivate,
|
2014-12-17 06:49:36 -08:00
|
|
|
Promise* aWorkerPromise,
|
|
|
|
const JSStructuredCloneCallbacks* aCb)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aWorkerPrivate);
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
MOZ_ASSERT(aWorkerPromise);
|
|
|
|
|
|
|
|
nsRefPtr<PromiseWorkerProxy> proxy =
|
|
|
|
new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise, aCb);
|
|
|
|
|
|
|
|
// We do this to make sure the worker thread won't shut down before the
|
|
|
|
// promise is resolved/rejected on the worker thread.
|
|
|
|
if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), proxy)) {
|
|
|
|
// Probably the worker is terminating. We cannot complete the operation
|
|
|
|
// and we have to release all the resources.
|
|
|
|
proxy->mCleanedUp = true;
|
|
|
|
proxy->mWorkerPromise = nullptr;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return proxy.forget();
|
|
|
|
}
|
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
PromiseWorkerProxy::PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
|
2014-02-24 05:56:54 -08:00
|
|
|
Promise* aWorkerPromise,
|
2014-12-16 16:30:39 -08:00
|
|
|
const JSStructuredCloneCallbacks* aCallbacks)
|
2014-02-24 05:56:54 -08:00
|
|
|
: mWorkerPrivate(aWorkerPrivate)
|
|
|
|
, mWorkerPromise(aWorkerPromise)
|
|
|
|
, mCleanedUp(false)
|
|
|
|
, mCallbacks(aCallbacks)
|
|
|
|
, mCleanUpLock("cleanUpLock")
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
PromiseWorkerProxy::~PromiseWorkerProxy()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mCleanedUp);
|
|
|
|
MOZ_ASSERT(!mWorkerPromise);
|
|
|
|
}
|
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
workers::WorkerPrivate*
|
2014-02-24 05:56:54 -08:00
|
|
|
PromiseWorkerProxy::GetWorkerPrivate() const
|
|
|
|
{
|
|
|
|
// It's ok to race on |mCleanedUp|, because it will never cause us to fire
|
|
|
|
// the assertion when we should not.
|
|
|
|
MOZ_ASSERT(!mCleanedUp);
|
|
|
|
|
|
|
|
return mWorkerPrivate;
|
|
|
|
}
|
|
|
|
|
|
|
|
Promise*
|
|
|
|
PromiseWorkerProxy::GetWorkerPromise() const
|
|
|
|
{
|
|
|
|
return mWorkerPromise;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PromiseWorkerProxy::StoreISupports(nsISupports* aSupports)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
2014-07-29 17:43:56 -07:00
|
|
|
nsMainThreadPtrHandle<nsISupports> supports(
|
|
|
|
new nsMainThreadPtrHolder<nsISupports>(aSupports));
|
2014-02-24 05:56:54 -08:00
|
|
|
mSupportsArray.AppendElement(supports);
|
|
|
|
}
|
|
|
|
|
2015-04-07 06:25:08 -07:00
|
|
|
bool
|
|
|
|
PromiseWorkerProxyControlRunnable::WorkerRun(JSContext* aCx,
|
2015-04-10 08:27:57 -07:00
|
|
|
workers::WorkerPrivate* aWorkerPrivate)
|
2014-12-17 06:49:36 -08:00
|
|
|
{
|
2015-04-07 06:25:08 -07:00
|
|
|
mProxy->CleanUp(aCx);
|
|
|
|
return true;
|
|
|
|
}
|
2014-12-17 06:49:36 -08:00
|
|
|
|
2014-02-24 05:56:54 -08:00
|
|
|
void
|
|
|
|
PromiseWorkerProxy::RunCallback(JSContext* aCx,
|
|
|
|
JS::Handle<JS::Value> aValue,
|
|
|
|
RunCallbackFunc aFunc)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
MutexAutoLock lock(mCleanUpLock);
|
|
|
|
// If the worker thread's been cancelled we don't need to resolve the Promise.
|
|
|
|
if (mCleanedUp) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The |aValue| is written into the buffer. Note that we also pass |this|
|
|
|
|
// into the structured-clone write in order to set its |mSupportsArray| to
|
|
|
|
// keep objects alive until the structured-clone read/write is done.
|
|
|
|
JSAutoStructuredCloneBuffer buffer;
|
|
|
|
if (!buffer.write(aCx, aValue, mCallbacks, this)) {
|
|
|
|
JS_ClearPendingException(aCx);
|
|
|
|
MOZ_ASSERT(false, "cannot write the JSAutoStructuredCloneBuffer!");
|
|
|
|
}
|
|
|
|
|
|
|
|
nsRefPtr<PromiseWorkerProxyRunnable> runnable =
|
|
|
|
new PromiseWorkerProxyRunnable(this,
|
|
|
|
mCallbacks,
|
|
|
|
Move(buffer),
|
|
|
|
aFunc);
|
|
|
|
|
2014-12-17 06:49:36 -08:00
|
|
|
if (!runnable->Dispatch(aCx)) {
|
|
|
|
nsRefPtr<WorkerControlRunnable> runnable =
|
|
|
|
new PromiseWorkerProxyControlRunnable(mWorkerPrivate, this);
|
|
|
|
mWorkerPrivate->DispatchControlRunnable(runnable);
|
|
|
|
}
|
2014-02-24 05:56:54 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PromiseWorkerProxy::ResolvedCallback(JSContext* aCx,
|
|
|
|
JS::Handle<JS::Value> aValue)
|
|
|
|
{
|
|
|
|
RunCallback(aCx, aValue, &Promise::ResolveInternal);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PromiseWorkerProxy::RejectedCallback(JSContext* aCx,
|
|
|
|
JS::Handle<JS::Value> aValue)
|
|
|
|
{
|
|
|
|
RunCallback(aCx, aValue, &Promise::RejectInternal);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
PromiseWorkerProxy::Notify(JSContext* aCx, Status aStatus)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mWorkerPrivate);
|
|
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
|
|
|
|
|
|
|
|
if (aStatus >= Canceling) {
|
|
|
|
CleanUp(aCx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PromiseWorkerProxy::CleanUp(JSContext* aCx)
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mCleanUpLock);
|
|
|
|
|
|
|
|
// |mWorkerPrivate| might not be safe to use anymore if we have already
|
|
|
|
// cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first.
|
|
|
|
if (mCleanedUp) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(mWorkerPrivate);
|
|
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
|
|
|
|
|
|
|
|
// Release the Promise and remove the PromiseWorkerProxy from the features of
|
|
|
|
// the worker thread since the Promise has been resolved/rejected or the
|
|
|
|
// worker thread has been cancelled.
|
|
|
|
mWorkerPromise = nullptr;
|
|
|
|
mWorkerPrivate->RemoveFeature(aCx, this);
|
|
|
|
mCleanedUp = true;
|
|
|
|
}
|
|
|
|
|
2014-06-03 08:38:38 -07:00
|
|
|
// Specializations of MaybeRejectBrokenly we actually support.
|
|
|
|
template<>
|
|
|
|
void Promise::MaybeRejectBrokenly(const nsRefPtr<DOMError>& aArg) {
|
|
|
|
MaybeSomething(aArg, &Promise::MaybeReject);
|
|
|
|
}
|
|
|
|
template<>
|
|
|
|
void Promise::MaybeRejectBrokenly(const nsAString& aArg) {
|
|
|
|
MaybeSomething(aArg, &Promise::MaybeReject);
|
|
|
|
}
|
|
|
|
|
2015-04-10 08:27:57 -07:00
|
|
|
uint64_t
|
|
|
|
Promise::GetID() {
|
|
|
|
if (mID != 0) {
|
|
|
|
return mID;
|
|
|
|
}
|
|
|
|
return mID = ++gIDGenerator;
|
|
|
|
}
|
|
|
|
|
2013-06-11 18:41:21 -07:00
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|