зеркало из https://github.com/mozilla/gecko-dev.git
Bug 915233 - DOM Promises on Workers. r=baku,smaug,bz sr=sicking
--HG-- extra : rebase_source : 0923cb54bb13291df69c894866b4054310a4627c
This commit is contained in:
Родитель
587777116a
Коммит
d1aa0a285c
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/dom/OwningNonNull.h"
|
||||
#include "mozilla/dom/PromiseBinding.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "PromiseCallback.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
@ -22,6 +23,8 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
using namespace workers;
|
||||
|
||||
// PromiseTask
|
||||
|
||||
// This class processes the promise's callbacks with promise's result.
|
||||
|
@ -51,36 +54,65 @@ private:
|
|||
nsRefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
// This class processes the promise's callbacks with promise's result.
|
||||
class PromiseResolverTask MOZ_FINAL : public nsRunnable
|
||||
class WorkerPromiseTask MOZ_FINAL : public WorkerRunnable
|
||||
{
|
||||
public:
|
||||
PromiseResolverTask(Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
WorkerPromiseTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThread,
|
||||
UnchangedBusyCount, SkipWhenClearing)
|
||||
, mPromise(aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_COUNT_CTOR(WorkerPromiseTask);
|
||||
}
|
||||
|
||||
~WorkerPromiseTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(WorkerPromiseTask);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
mPromise->mTaskPending = false;
|
||||
mPromise->RunTask();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
class PromiseResolverMixin
|
||||
{
|
||||
public:
|
||||
PromiseResolverMixin(Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
: mPromise(aPromise)
|
||||
, mValue(aValue)
|
||||
, mState(aState)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(mState != Promise::Pending);
|
||||
MOZ_COUNT_CTOR(PromiseResolverTask);
|
||||
MOZ_COUNT_CTOR(PromiseResolverMixin);
|
||||
|
||||
JSContext* cx = nsContentUtils::GetSafeJSContext();
|
||||
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
|
||||
/* It's safe to use unsafeGet() here: the unsafeness comes from the
|
||||
* possibility of updating the value of mJSObject without triggering the
|
||||
* barriers. However if the value will always be marked, post barriers
|
||||
* unnecessary. */
|
||||
JS_AddNamedValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet(),
|
||||
"PromiseResolverTask.mValue");
|
||||
"PromiseResolverMixin.mValue");
|
||||
}
|
||||
|
||||
~PromiseResolverTask()
|
||||
virtual ~PromiseResolverMixin()
|
||||
{
|
||||
MOZ_COUNT_DTOR(PromiseResolverTask);
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
|
||||
MOZ_COUNT_DTOR(PromiseResolverMixin);
|
||||
|
||||
JSContext* cx = nsContentUtils::GetSafeJSContext();
|
||||
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
|
||||
/* It's safe to use unsafeGet() here: the unsafeness comes from the
|
||||
* possibility of updating the value of mJSObject without triggering the
|
||||
|
@ -89,18 +121,66 @@ public:
|
|||
JS_RemoveValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
protected:
|
||||
void
|
||||
RunInternal()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
|
||||
mPromise->RunResolveTask(
|
||||
JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
|
||||
mState, Promise::SyncTask);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
JS::Heap<JS::Value> mValue;
|
||||
Promise::PromiseState mState;
|
||||
NS_DECL_OWNINGTHREAD;
|
||||
};
|
||||
|
||||
// This class processes the promise's callbacks with promise's result.
|
||||
class PromiseResolverTask MOZ_FINAL : public nsRunnable,
|
||||
public PromiseResolverMixin
|
||||
{
|
||||
public:
|
||||
PromiseResolverTask(Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
: PromiseResolverMixin(aPromise, aValue, aState)
|
||||
{}
|
||||
|
||||
~PromiseResolverTask()
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
RunInternal();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class WorkerPromiseResolverTask MOZ_FINAL : public WorkerRunnable,
|
||||
public PromiseResolverMixin
|
||||
{
|
||||
public:
|
||||
WorkerPromiseResolverTask(WorkerPrivate* aWorkerPrivate,
|
||||
Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThread,
|
||||
UnchangedBusyCount, SkipWhenClearing),
|
||||
PromiseResolverMixin(aPromise, aValue, aState)
|
||||
{}
|
||||
|
||||
~WorkerPromiseResolverTask()
|
||||
{}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
RunInternal();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Promise
|
||||
|
@ -163,25 +243,55 @@ Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
|||
return PromiseBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Promise::PrefEnabled()
|
||||
class PromisePrefEnabledRunnable : public nsRunnable
|
||||
{
|
||||
return Preferences::GetBool("dom.promise.enabled", false);
|
||||
}
|
||||
public:
|
||||
PromisePrefEnabledRunnable() : mEnabled(false) {}
|
||||
|
||||
bool
|
||||
Enabled()
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mEnabled = Preferences::GetBool("dom.promise.enabled", false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mEnabled;
|
||||
};
|
||||
|
||||
/* static */ bool
|
||||
Promise::EnabledForScope(JSContext* aCx, JSObject* /* unused */)
|
||||
{
|
||||
// Enable if the pref is enabled or if we're chrome or if we're a
|
||||
// certified app.
|
||||
if (PrefEnabled()) {
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
nsRefPtr<PromisePrefEnabledRunnable> r = new PromisePrefEnabledRunnable();
|
||||
|
||||
// When used from the main thread, SyncRunnable will internally directly call
|
||||
// the function rather than dispatch a Runnable. So this is usable on any
|
||||
// thread.
|
||||
// Although this pause is expensive, it is performed only once per worker when
|
||||
// the worker in initialized.
|
||||
SyncRunnable::DispatchToThread(mainThread, r);
|
||||
if (r->Enabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME(nsm): Remove these checks once promises are enabled by default.
|
||||
// Note that we have no concept of a certified app in workers.
|
||||
// XXXbz well, why not?
|
||||
if (!NS_IsMainThread()) {
|
||||
return workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
|
||||
return GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
|
||||
}
|
||||
|
||||
nsIPrincipal* prin = nsContentUtils::GetSubjectPrincipal();
|
||||
|
@ -280,10 +390,15 @@ Promise::Constructor(const GlobalObject& aGlobal,
|
|||
PromiseInit& aInit, ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.GetContext();
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
nsCOMPtr<nsPIDOMWindow> window;
|
||||
|
||||
// On workers, let the window be null.
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = new Promise(window);
|
||||
|
@ -324,10 +439,13 @@ Promise::Constructor(const GlobalObject& aGlobal,
|
|||
Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
nsCOMPtr<nsPIDOMWindow> window;
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = new Promise(window);
|
||||
|
@ -341,10 +459,13 @@ Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
|
|||
Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
nsCOMPtr<nsPIDOMWindow> window;
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = new Promise(window);
|
||||
|
@ -403,8 +524,15 @@ Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
|
|||
// 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 && !mTaskPending) {
|
||||
nsRefPtr<PromiseTask> task = new PromiseTask(this);
|
||||
NS_DispatchToCurrentThread(task);
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
nsRefPtr<PromiseTask> task = new PromiseTask(this);
|
||||
NS_DispatchToCurrentThread(task);
|
||||
} else {
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
nsRefPtr<WorkerPromiseTask> task = new WorkerPromiseTask(worker, this);
|
||||
worker->Dispatch(task);
|
||||
}
|
||||
mTaskPending = true;
|
||||
}
|
||||
}
|
||||
|
@ -420,8 +548,9 @@ Promise::RunTask()
|
|||
mResolveCallbacks.Clear();
|
||||
mRejectCallbacks.Clear();
|
||||
|
||||
JSAutoRequest ar(nsContentUtils::GetSafeJSContext());
|
||||
Optional<JS::Handle<JS::Value> > value(nsContentUtils::GetSafeJSContext(), mResult);
|
||||
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
JSAutoRequest ar(cx);
|
||||
Optional<JS::Handle<JS::Value> > value(cx, mResult);
|
||||
|
||||
for (uint32_t i = 0; i < callbacks.Length(); ++i) {
|
||||
callbacks[i]->Call(value);
|
||||
|
@ -442,16 +571,27 @@ Promise::MaybeReportRejected()
|
|||
|
||||
MOZ_ASSERT(mResult.isObject(), "How did we get a JSErrorReport?");
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win =
|
||||
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(&mResult.toObject()));
|
||||
// Remains null in case of worker.
|
||||
nsCOMPtr<nsPIDOMWindow> win;
|
||||
bool isChromeError = false;
|
||||
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
win =
|
||||
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(&mResult.toObject()));
|
||||
nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(&mResult.toObject());
|
||||
isChromeError = nsContentUtils::IsSystemPrincipal(principal);
|
||||
} else {
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
isChromeError = worker->IsChromeWorker();
|
||||
}
|
||||
|
||||
nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(&mResult.toObject());
|
||||
// Now post an event to do the real reporting async
|
||||
NS_DispatchToCurrentThread(
|
||||
NS_DispatchToMainThread(
|
||||
new AsyncErrorReporter(JS_GetObjectRuntime(&mResult.toObject()),
|
||||
report,
|
||||
nullptr,
|
||||
nsContentUtils::IsSystemPrincipal(principal),
|
||||
isChromeError,
|
||||
win));
|
||||
}
|
||||
|
||||
|
@ -530,9 +670,17 @@ Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
|
|||
// If the synchronous flag is unset, queue a task to process our
|
||||
// accept callbacks with value.
|
||||
if (aAsynchronous == AsyncTask) {
|
||||
nsRefPtr<PromiseResolverTask> task =
|
||||
new PromiseResolverTask(this, aValue, aState);
|
||||
NS_DispatchToCurrentThread(task);
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
nsRefPtr<PromiseResolverTask> task =
|
||||
new PromiseResolverTask(this, aValue, aState);
|
||||
NS_DispatchToCurrentThread(task);
|
||||
} else {
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
nsRefPtr<WorkerPromiseResolverTask> task =
|
||||
new WorkerPromiseResolverTask(worker, this, aValue, aState);
|
||||
worker->Dispatch(task);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,10 +27,13 @@ class AnyCallback;
|
|||
class Promise MOZ_FINAL : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
friend class PromiseTask;
|
||||
friend class PromiseResolverMixin;
|
||||
friend class PromiseResolverTask;
|
||||
friend class ResolvePromiseCallback;
|
||||
friend class PromiseTask;
|
||||
friend class RejectPromiseCallback;
|
||||
friend class ResolvePromiseCallback;
|
||||
friend class WorkerPromiseResolverTask;
|
||||
friend class WorkerPromiseTask;
|
||||
friend class WrapperPromiseCallback;
|
||||
|
||||
public:
|
||||
|
@ -40,7 +43,6 @@ public:
|
|||
Promise(nsPIDOMWindow* aWindow);
|
||||
~Promise();
|
||||
|
||||
static bool PrefEnabled();
|
||||
static bool EnabledForScope(JSContext* aCx, JSObject* /* unused */);
|
||||
|
||||
void MaybeResolve(JSContext* aCx,
|
||||
|
|
|
@ -74,7 +74,13 @@ void
|
|||
ResolvePromiseCallback::Call(const Optional<JS::Handle<JS::Value> >& aValue)
|
||||
{
|
||||
// Run resolver's algorithm with value and the synchronous flag set.
|
||||
AutoJSContext cx;
|
||||
JSContext *cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
|
||||
Maybe<AutoCxPusher> pusher;
|
||||
if (NS_IsMainThread()) {
|
||||
pusher.construct(cx);
|
||||
}
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
EnterCompartment(ac, cx, aValue);
|
||||
|
||||
|
@ -109,7 +115,13 @@ void
|
|||
RejectPromiseCallback::Call(const Optional<JS::Handle<JS::Value> >& aValue)
|
||||
{
|
||||
// Run resolver's algorithm with value and the synchronous flag set.
|
||||
AutoJSContext cx;
|
||||
JSContext *cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
|
||||
Maybe<AutoCxPusher> pusher;
|
||||
if (NS_IsMainThread()) {
|
||||
pusher.construct(cx);
|
||||
}
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
EnterCompartment(ac, cx, aValue);
|
||||
|
||||
|
@ -145,7 +157,17 @@ WrapperPromiseCallback::~WrapperPromiseCallback()
|
|||
void
|
||||
WrapperPromiseCallback::Call(const Optional<JS::Handle<JS::Value> >& aValue)
|
||||
{
|
||||
AutoJSContext cx;
|
||||
// AutoCxPusher and co. interact with xpconnect, which crashes on
|
||||
// workers. On workers we'll get the right context from
|
||||
// GetDefaultJSContextForThread(), and since there is only one context, we
|
||||
// don't need to push or pop it from the stack. Is that correct?
|
||||
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
|
||||
Maybe<AutoCxPusher> pusher;
|
||||
if (NS_IsMainThread()) {
|
||||
pusher.construct(cx);
|
||||
}
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
EnterCompartment(ac, cx, aValue);
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
};
|
||||
|
||||
// WrapperPromiseCallback execs a JS Callback with a value, and then the return
|
||||
// value is sent to the aNextPromise->resolveFunction() or to
|
||||
// value is sent to the aNextPromise->ResolveFunction() or to
|
||||
// aNextPromise->RejectFunction() if the JS Callback throws.
|
||||
class WrapperPromiseCallback MOZ_FINAL : public PromiseCallback
|
||||
{
|
||||
|
|
|
@ -368,6 +368,15 @@ function promiseResolveNestedPromise() {
|
|||
});
|
||||
}
|
||||
|
||||
function promiseRejectNoHandler() {
|
||||
// This test only checks that the code that reports unhandled errors in the
|
||||
// Promises implementation does not crash or leak.
|
||||
var promise = new Promise(function(res, rej) {
|
||||
noSuchMethod();
|
||||
});
|
||||
runTest();
|
||||
}
|
||||
|
||||
var tests = [ promiseResolve, promiseReject,
|
||||
promiseException, promiseGC, promiseAsync,
|
||||
promiseDoubleThen, promiseThenException,
|
||||
|
@ -380,6 +389,7 @@ var tests = [ promiseResolve, promiseReject,
|
|||
promiseWrongNestedPromise, promiseLoop,
|
||||
promiseStaticReject, promiseStaticResolve,
|
||||
promiseResolveNestedPromise,
|
||||
promiseRejectNoHandler,
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "mozilla/dom/ImageDataBinding.h"
|
||||
#include "mozilla/dom/MessageEventBinding.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/PromiseBinding.h"
|
||||
#include "mozilla/dom/TextDecoderBinding.h"
|
||||
#include "mozilla/dom/TextEncoderBinding.h"
|
||||
#include "mozilla/dom/XMLHttpRequestBinding.h"
|
||||
|
@ -61,6 +62,8 @@ WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
|
|||
!ImageDataBinding::GetConstructorObject(aCx, aGlobal) ||
|
||||
!MessageEventBinding::GetConstructorObject(aCx, aGlobal) ||
|
||||
!MessagePortBinding::GetConstructorObject(aCx, aGlobal) ||
|
||||
(PromiseBinding::ConstructorEnabled(aCx, aGlobal) &&
|
||||
!PromiseBinding::GetConstructorObject(aCx, aGlobal)) ||
|
||||
!TextDecoderBinding::GetConstructorObject(aCx, aGlobal) ||
|
||||
!TextEncoderBinding::GetConstructorObject(aCx, aGlobal) ||
|
||||
!XMLHttpRequestBinding_workers::GetConstructorObject(aCx, aGlobal) ||
|
||||
|
@ -77,4 +80,4 @@ WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ support-files =
|
|||
multi_sharedWorker_sharedWorker.js
|
||||
navigator_worker.js
|
||||
newError_worker.js
|
||||
promise_worker.js
|
||||
recursion_worker.js
|
||||
recursiveOnerror_worker.js
|
||||
relativeLoad_import.js
|
||||
|
@ -83,6 +84,7 @@ support-files =
|
|||
[test_multi_sharedWorker_lifetimes.html]
|
||||
[test_navigator.html]
|
||||
[test_newError.html]
|
||||
[test_promise.html]
|
||||
[test_recursion.html]
|
||||
[test_recursiveOnerror.html]
|
||||
[test_relativeLoad.html]
|
||||
|
|
|
@ -0,0 +1,395 @@
|
|||
function ok(a, msg) {
|
||||
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
|
||||
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
|
||||
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
|
||||
}
|
||||
|
||||
function isnot(a, b, msg) {
|
||||
dump("ISNOT: " + (a!==b) + " => " + a + " | " + b + " " + msg + "\n");
|
||||
postMessage({type: 'status', status: a !== b, msg: a + " !== " + b + ": " + msg });
|
||||
}
|
||||
|
||||
function promiseResolve() {
|
||||
ok(Promise, "Promise object should exist");
|
||||
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
ok(resolve, "Promise.resolve exists");
|
||||
ok(reject, "Promise.reject exists");
|
||||
|
||||
resolve(42);
|
||||
}).then(function(what) {
|
||||
ok(true, "Then - resolveCb has been called");
|
||||
is(what, 42, "ResolveCb received 42");
|
||||
runTest();
|
||||
}, function() {
|
||||
ok(false, "Then - rejectCb has been called");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function promiseReject() {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
reject(42);
|
||||
}).then(function(what) {
|
||||
ok(false, "Then - resolveCb has been called");
|
||||
runTest();
|
||||
}, function(what) {
|
||||
ok(true, "Then - rejectCb has been called");
|
||||
is(what, 42, "RejectCb received 42");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseException() {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
throw 42;
|
||||
}).then(function(what) {
|
||||
ok(false, "Then - resolveCb has been called");
|
||||
runTest();
|
||||
}, function(what) {
|
||||
ok(true, "Then - rejectCb has been called");
|
||||
is(what, 42, "RejectCb received 42");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseAsync() {
|
||||
var global = "foo";
|
||||
var f = new Promise(function(r1, r2) {
|
||||
is(global, "foo", "Global should be foo");
|
||||
r1(42);
|
||||
is(global, "foo", "Global should still be foo");
|
||||
setTimeout(function() {
|
||||
is(global, "bar", "Global should still be bar!");
|
||||
runTest();
|
||||
}, 0);
|
||||
}).then(function() {
|
||||
global = "bar";
|
||||
});
|
||||
is(global, "foo", "Global should still be foo (2)");
|
||||
}
|
||||
|
||||
function promiseDoubleThen() {
|
||||
var steps = 0;
|
||||
var promise = new Promise(function(r1, r2) {
|
||||
r1(42);
|
||||
});
|
||||
|
||||
promise.then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(what, 42, "Value == 42");
|
||||
steps++;
|
||||
}, function(what) {
|
||||
ok(false, "Then.reject has been called");
|
||||
});
|
||||
|
||||
promise.then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(steps, 1, "Then.resolve - step == 1");
|
||||
is(what, 42, "Value == 42");
|
||||
runTest();
|
||||
}, function(what) {
|
||||
ok(false, "Then.reject has been called");
|
||||
});
|
||||
}
|
||||
|
||||
function promiseThenException() {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
resolve(42);
|
||||
});
|
||||
|
||||
promise.then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
throw "booh";
|
||||
}).catch(function(e) {
|
||||
ok(true, "Catch has been called!");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseThenCatchThen() {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
resolve(42);
|
||||
});
|
||||
|
||||
var promise2 = promise.then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(what, 42, "Value == 42");
|
||||
return what + 1;
|
||||
}, function(what) {
|
||||
ok(false, "Then.reject has been called");
|
||||
});
|
||||
|
||||
isnot(promise, promise2, "These 2 promise objs are different");
|
||||
|
||||
promise2.then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(what, 43, "Value == 43");
|
||||
return what + 1;
|
||||
}, function(what) {
|
||||
ok(false, "Then.reject has been called");
|
||||
}).catch(function() {
|
||||
ok(false, "Catch has been called");
|
||||
}).then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(what, 44, "Value == 44");
|
||||
runTest();
|
||||
}, function(what) {
|
||||
ok(false, "Then.reject has been called");
|
||||
});
|
||||
}
|
||||
|
||||
function promiseRejectThenCatchThen() {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
reject(42);
|
||||
});
|
||||
|
||||
var promise2 = promise.then(function(what) {
|
||||
ok(false, "Then.resolve has been called");
|
||||
}, function(what) {
|
||||
ok(true, "Then.reject has been called");
|
||||
is(what, 42, "Value == 42");
|
||||
return what + 1;
|
||||
});
|
||||
|
||||
isnot(promise, promise2, "These 2 promise objs are different");
|
||||
|
||||
promise2.then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(what, 43, "Value == 43");
|
||||
return what+1;
|
||||
}).catch(function(what) {
|
||||
ok(false, "Catch has been called");
|
||||
}).then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(what, 44, "Value == 44");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseRejectThenCatchThen2() {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
reject(42);
|
||||
});
|
||||
|
||||
promise.then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(what, 42, "Value == 42");
|
||||
return what+1;
|
||||
}).catch(function(what) {
|
||||
is(what, 42, "Value == 42");
|
||||
ok(true, "Catch has been called");
|
||||
return what+1;
|
||||
}).then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(what, 43, "Value == 43");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseRejectThenCatchExceptionThen() {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
reject(42);
|
||||
});
|
||||
|
||||
promise.then(function(what) {
|
||||
ok(false, "Then.resolve has been called");
|
||||
}, function(what) {
|
||||
ok(true, "Then.reject has been called");
|
||||
is(what, 42, "Value == 42");
|
||||
throw(what + 1);
|
||||
}).catch(function(what) {
|
||||
ok(true, "Catch has been called");
|
||||
is(what, 43, "Value == 43");
|
||||
return what + 1;
|
||||
}).then(function(what) {
|
||||
ok(true, "Then.resolve has been called");
|
||||
is(what, 44, "Value == 44");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseThenCatchOrderingResolve() {
|
||||
var global = 0;
|
||||
var f = new Promise(function(r1, r2) {
|
||||
r1(42);
|
||||
});
|
||||
|
||||
f.then(function() {
|
||||
f.then(function() {
|
||||
global++;
|
||||
});
|
||||
f.catch(function() {
|
||||
global++;
|
||||
});
|
||||
f.then(function() {
|
||||
global++;
|
||||
});
|
||||
setTimeout(function() {
|
||||
is(global, 2, "Many steps... should return 2");
|
||||
runTest();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseThenCatchOrderingReject() {
|
||||
var global = 0;
|
||||
var f = new Promise(function(r1, r2) {
|
||||
r2(42);
|
||||
})
|
||||
|
||||
f.then(function() {}, function() {
|
||||
f.then(function() {
|
||||
global++;
|
||||
});
|
||||
f.catch(function() {
|
||||
global++;
|
||||
});
|
||||
f.then(function() {}, function() {
|
||||
global++;
|
||||
});
|
||||
setTimeout(function() {
|
||||
is(global, 2, "Many steps... should return 2");
|
||||
runTest();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseNestedPromise() {
|
||||
new Promise(function(resolve, reject) {
|
||||
resolve(new Promise(function(resolve, reject) {
|
||||
ok(true, "Nested promise is executed");
|
||||
resolve(42);
|
||||
}));
|
||||
}).then(function(value) {
|
||||
is(value, 42, "Nested promise is executed and then == 42");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseNestedNestedPromise() {
|
||||
new Promise(function(resolve, reject) {
|
||||
resolve(new Promise(function(resolve, reject) {
|
||||
ok(true, "Nested promise is executed");
|
||||
resolve(42);
|
||||
}).then(function(what) { return what+1; }));
|
||||
}).then(function(value) {
|
||||
is(value, 43, "Nested promise is executed and then == 43");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseWrongNestedPromise() {
|
||||
new Promise(function(resolve, reject) {
|
||||
resolve(new Promise(function(r, r2) {
|
||||
ok(true, "Nested promise is executed");
|
||||
r(42);
|
||||
}));
|
||||
reject(42);
|
||||
}).then(function(value) {
|
||||
is(value, 42, "Nested promise is executed and then == 42");
|
||||
runTest();
|
||||
}, function(value) {
|
||||
ok(false, "This is wrong");
|
||||
});
|
||||
}
|
||||
|
||||
function promiseLoop() {
|
||||
new Promise(function(resolve, reject) {
|
||||
resolve(new Promise(function(r1, r2) {
|
||||
ok(true, "Nested promise is executed");
|
||||
r1(new Promise(function(r1, r2) {
|
||||
ok(true, "Nested nested promise is executed");
|
||||
r1(42);
|
||||
}));
|
||||
}));
|
||||
}).then(function(value) {
|
||||
is(value, 42, "Nested nested promise is executed and then == 42");
|
||||
runTest();
|
||||
}, function(value) {
|
||||
ok(false, "This is wrong");
|
||||
});
|
||||
}
|
||||
|
||||
function promiseStaticReject() {
|
||||
var promise = Promise.reject(42).then(function(what) {
|
||||
ok(false, "This should not be called");
|
||||
}, function(what) {
|
||||
is(what, 42, "Value == 42");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseStaticResolve() {
|
||||
var promise = Promise.resolve(42).then(function(what) {
|
||||
is(what, 42, "Value == 42");
|
||||
runTest();
|
||||
}, function() {
|
||||
ok(false, "This should not be called");
|
||||
});
|
||||
}
|
||||
|
||||
function promiseResolveNestedPromise() {
|
||||
var promise = Promise.resolve(new Promise(function(r, r2) {
|
||||
ok(true, "Nested promise is executed");
|
||||
r(42);
|
||||
}, function() {
|
||||
ok(false, "This should not be called");
|
||||
})).then(function(what) {
|
||||
is(what, 42, "Value == 42");
|
||||
runTest();
|
||||
}, function() {
|
||||
ok(false, "This should not be called");
|
||||
});
|
||||
}
|
||||
|
||||
function promiseRejectNoHandler() {
|
||||
// This test only checks that the code that reports unhandled errors in the
|
||||
// Promises implementation does not crash or leak.
|
||||
var promise = new Promise(function(res, rej) {
|
||||
noSuchMethod();
|
||||
});
|
||||
runTest();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
promiseResolve,
|
||||
promiseReject,
|
||||
promiseException,
|
||||
promiseAsync,
|
||||
promiseDoubleThen,
|
||||
promiseThenException,
|
||||
promiseThenCatchThen,
|
||||
promiseRejectThenCatchThen,
|
||||
promiseRejectThenCatchThen2,
|
||||
promiseRejectThenCatchExceptionThen,
|
||||
promiseThenCatchOrderingResolve,
|
||||
promiseThenCatchOrderingReject,
|
||||
promiseNestedPromise,
|
||||
promiseNestedNestedPromise,
|
||||
promiseWrongNestedPromise,
|
||||
promiseLoop,
|
||||
promiseStaticReject,
|
||||
promiseStaticResolve,
|
||||
promiseResolveNestedPromise,
|
||||
promiseRejectNoHandler,
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
if (!tests.length) {
|
||||
postMessage({ type: 'finish' });
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
onmessage = function() {
|
||||
runTest();
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Promise object in workers</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function runTest() {
|
||||
var worker = new Worker("promise_worker.js");
|
||||
|
||||
worker.onmessage = function(event) {
|
||||
|
||||
if (event.data.type == 'finish') {
|
||||
SimpleTest.finish();
|
||||
} else if (event.data.type == 'status') {
|
||||
ok(event.data.status, event.data.msg);
|
||||
}
|
||||
}
|
||||
|
||||
worker.onerror = function(event) {
|
||||
ok(false, "Worker had an error: " + event.data);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
worker.postMessage(true);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, runTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
Загрузка…
Ссылка в новой задаче