mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 1035060 - Implement AbortablePromise. r=bz
This commit is contained in:
parent
6a9b2b1e98
commit
fcfee0071f
@ -91,6 +91,10 @@ DOMInterfaces = {
|
|||||||
'nativeType': 'mozilla::dom::Activity',
|
'nativeType': 'mozilla::dom::Activity',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'MozAbortablePromise': {
|
||||||
|
'nativeType': 'mozilla::dom::AbortablePromise',
|
||||||
|
},
|
||||||
|
|
||||||
'AbstractWorker': {
|
'AbstractWorker': {
|
||||||
'concrete': False
|
'concrete': False
|
||||||
},
|
},
|
||||||
|
118
dom/promise/AbortablePromise.cpp
Normal file
118
dom/promise/AbortablePromise.cpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/* -*- 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/. */
|
||||||
|
|
||||||
|
#include "mozilla/dom/AbortablePromise.h"
|
||||||
|
|
||||||
|
#include "mozilla/dom/AbortablePromiseBinding.h"
|
||||||
|
#include "mozilla/dom/PromiseNativeAbortCallback.h"
|
||||||
|
#include "mozilla/ErrorResult.h"
|
||||||
|
#include "nsThreadUtils.h"
|
||||||
|
#include "PromiseCallback.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseNativeAbortCallback)
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseNativeAbortCallback)
|
||||||
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseNativeAbortCallback)
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||||
|
NS_INTERFACE_MAP_END
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_0(PromiseNativeAbortCallback)
|
||||||
|
|
||||||
|
NS_IMPL_ADDREF_INHERITED(AbortablePromise, Promise)
|
||||||
|
NS_IMPL_RELEASE_INHERITED(AbortablePromise, Promise)
|
||||||
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AbortablePromise)
|
||||||
|
NS_INTERFACE_MAP_END_INHERITING(Promise)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(AbortablePromise, Promise, mAbortCallback)
|
||||||
|
|
||||||
|
AbortablePromise::AbortablePromise(nsIGlobalObject* aGlobal,
|
||||||
|
PromiseNativeAbortCallback& aAbortCallback)
|
||||||
|
: Promise(aGlobal)
|
||||||
|
, mAbortCallback(&aAbortCallback)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AbortablePromise::AbortablePromise(nsIGlobalObject* aGlobal)
|
||||||
|
: Promise(aGlobal)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AbortablePromise::~AbortablePromise()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ already_AddRefed<AbortablePromise>
|
||||||
|
AbortablePromise::Create(nsIGlobalObject* aGlobal,
|
||||||
|
PromiseNativeAbortCallback& aAbortCallback,
|
||||||
|
ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
nsRefPtr<AbortablePromise> p = new AbortablePromise(aGlobal, aAbortCallback);
|
||||||
|
p->CreateWrapper(aRv);
|
||||||
|
if (aRv.Failed()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return p.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
AbortablePromise::WrapObject(JSContext* aCx)
|
||||||
|
{
|
||||||
|
return MozAbortablePromiseBinding::Wrap(aCx, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ already_AddRefed<AbortablePromise>
|
||||||
|
AbortablePromise::Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||||
|
AbortCallback& aAbortCallback, ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIGlobalObject> global;
|
||||||
|
global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||||
|
if (!global) {
|
||||||
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<AbortablePromise> promise = new AbortablePromise(global);
|
||||||
|
promise->CreateWrapper(aRv);
|
||||||
|
if (aRv.Failed()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise->CallInitFunction(aGlobal, aInit, aRv);
|
||||||
|
if (aRv.Failed()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise->mAbortCallback = &aAbortCallback;
|
||||||
|
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AbortablePromise::Abort()
|
||||||
|
{
|
||||||
|
if (IsPending()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MaybeReject(NS_ERROR_ABORT);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> runnable =
|
||||||
|
NS_NewRunnableMethod(this, &AbortablePromise::DoAbort);
|
||||||
|
Promise::DispatchToMainOrWorkerThread(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AbortablePromise::DoAbort()
|
||||||
|
{
|
||||||
|
if (mAbortCallback.HasWebIDLCallback()) {
|
||||||
|
ErrorResult rv;
|
||||||
|
mAbortCallback.GetWebIDLCallback()->Call(rv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mAbortCallback.GetXPCOMCallback()->Call();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
66
dom/promise/AbortablePromise.h
Normal file
66
dom/promise/AbortablePromise.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* -*- 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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_AbortablePromise_h__
|
||||||
|
#define mozilla_dom_AbortablePromise_h__
|
||||||
|
|
||||||
|
#include "js/TypeDecls.h"
|
||||||
|
#include "mozilla/dom/Promise.h"
|
||||||
|
#include "mozilla/dom/CallbackObject.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class AbortCallback;
|
||||||
|
class PromiseNativeAbortCallback;
|
||||||
|
|
||||||
|
class AbortablePromise
|
||||||
|
: public Promise
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AbortablePromise, Promise)
|
||||||
|
|
||||||
|
public:
|
||||||
|
// It is the same as Promise::Create except that this takes an extra
|
||||||
|
// aAbortCallback parameter to set the abort callback handler.
|
||||||
|
static already_AddRefed<AbortablePromise>
|
||||||
|
Create(nsIGlobalObject* aGlobal, PromiseNativeAbortCallback& aAbortCallback,
|
||||||
|
ErrorResult& aRv);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Constructor used to create native AbortablePromise with C++.
|
||||||
|
AbortablePromise(nsIGlobalObject* aGlobal,
|
||||||
|
PromiseNativeAbortCallback& aAbortCallback);
|
||||||
|
|
||||||
|
// Constructor used to create AbortablePromise for JavaScript. It should be
|
||||||
|
// called by the static AbortablePromise::Constructor.
|
||||||
|
AbortablePromise(nsIGlobalObject* aGlobal);
|
||||||
|
|
||||||
|
virtual ~AbortablePromise();
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual JSObject*
|
||||||
|
WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
static already_AddRefed<AbortablePromise>
|
||||||
|
Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||||
|
AbortCallback& aAbortCallback, ErrorResult& aRv);
|
||||||
|
|
||||||
|
void Abort();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DoAbort();
|
||||||
|
|
||||||
|
// The callback functions to abort the promise.
|
||||||
|
CallbackObjectHolder<AbortCallback,
|
||||||
|
PromiseNativeAbortCallback> mAbortCallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_AbortablePromise_h__
|
@ -325,26 +325,33 @@ Promise::WrapObject(JSContext* aCx)
|
|||||||
already_AddRefed<Promise>
|
already_AddRefed<Promise>
|
||||||
Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
|
Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
AutoJSAPI jsapi;
|
nsRefPtr<Promise> p = new Promise(aGlobal);
|
||||||
if (!jsapi.Init(aGlobal)) {
|
p->CreateWrapper(aRv);
|
||||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
if (aRv.Failed()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
return p.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Promise::CreateWrapper(ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
AutoJSAPI jsapi;
|
||||||
|
if (!jsapi.Init(mGlobal)) {
|
||||||
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
JSContext* cx = jsapi.cx();
|
JSContext* cx = jsapi.cx();
|
||||||
|
|
||||||
nsRefPtr<Promise> p = new Promise(aGlobal);
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> ignored(cx);
|
JS::Rooted<JS::Value> ignored(cx);
|
||||||
if (!WrapNewBindingObject(cx, p, &ignored)) {
|
if (!WrapNewBindingObject(cx, this, &ignored)) {
|
||||||
JS_ClearPendingException(cx);
|
JS_ClearPendingException(cx);
|
||||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
return nullptr;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need the .get() bit here to get template deduction working right
|
// Need the .get() bit here to get template deduction working right
|
||||||
dom::PreserveWrapper(p.get());
|
dom::PreserveWrapper(this);
|
||||||
|
|
||||||
return p.forget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -483,8 +490,6 @@ Promise::CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTas
|
|||||||
Promise::Constructor(const GlobalObject& aGlobal,
|
Promise::Constructor(const GlobalObject& aGlobal,
|
||||||
PromiseInit& aInit, ErrorResult& aRv)
|
PromiseInit& aInit, ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
JSContext* cx = aGlobal.Context();
|
|
||||||
|
|
||||||
nsCOMPtr<nsIGlobalObject> global;
|
nsCOMPtr<nsIGlobalObject> global;
|
||||||
global = do_QueryInterface(aGlobal.GetAsSupports());
|
global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||||
if (!global) {
|
if (!global) {
|
||||||
@ -497,20 +502,34 @@ Promise::Constructor(const GlobalObject& aGlobal,
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS::Rooted<JSObject*> resolveFunc(cx,
|
promise->CallInitFunction(aGlobal, aInit, aRv);
|
||||||
CreateFunction(cx, aGlobal.Get(), promise,
|
if (aRv.Failed()) {
|
||||||
PromiseCallback::Resolve));
|
|
||||||
if (!resolveFunc) {
|
|
||||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Promise::CallInitFunction(const GlobalObject& aGlobal,
|
||||||
|
PromiseInit& aInit, ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
JSContext* cx = aGlobal.Context();
|
||||||
|
|
||||||
|
JS::Rooted<JSObject*> resolveFunc(cx,
|
||||||
|
CreateFunction(cx, aGlobal.Get(), this,
|
||||||
|
PromiseCallback::Resolve));
|
||||||
|
if (!resolveFunc) {
|
||||||
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
JS::Rooted<JSObject*> rejectFunc(cx,
|
JS::Rooted<JSObject*> rejectFunc(cx,
|
||||||
CreateFunction(cx, aGlobal.Get(), promise,
|
CreateFunction(cx, aGlobal.Get(), this,
|
||||||
PromiseCallback::Reject));
|
PromiseCallback::Reject));
|
||||||
if (!rejectFunc) {
|
if (!rejectFunc) {
|
||||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||||
return nullptr;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aInit.Call(resolveFunc, rejectFunc, aRv, CallbackObject::eRethrowExceptions);
|
aInit.Call(resolveFunc, rejectFunc, aRv, CallbackObject::eRethrowExceptions);
|
||||||
@ -524,13 +543,11 @@ Promise::Constructor(const GlobalObject& aGlobal,
|
|||||||
// function Promise(arg) { try { arg(a, b); } catch (e) { this.reject(e); }}
|
// function Promise(arg) { try { arg(a, b); } catch (e) { this.reject(e); }}
|
||||||
if (!JS_WrapValue(cx, &value)) {
|
if (!JS_WrapValue(cx, &value)) {
|
||||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||||
return nullptr;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
promise->MaybeRejectInternal(cx, value);
|
MaybeRejectInternal(cx, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.forget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ already_AddRefed<Promise>
|
/* static */ already_AddRefed<Promise>
|
||||||
|
@ -50,9 +50,9 @@ public:
|
|||||||
Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE;
|
Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Promise MOZ_FINAL : public nsISupports,
|
class Promise : public nsISupports,
|
||||||
public nsWrapperCache,
|
public nsWrapperCache,
|
||||||
public SupportsWeakPtr<Promise>
|
public SupportsWeakPtr<Promise>
|
||||||
{
|
{
|
||||||
friend class NativePromiseCallback;
|
friend class NativePromiseCallback;
|
||||||
friend class PromiseResolverTask;
|
friend class PromiseResolverTask;
|
||||||
@ -65,8 +65,6 @@ class Promise MOZ_FINAL : public nsISupports,
|
|||||||
friend class ThenableResolverTask;
|
friend class ThenableResolverTask;
|
||||||
friend class WrapperPromiseCallback;
|
friend class WrapperPromiseCallback;
|
||||||
|
|
||||||
~Promise();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
|
||||||
@ -159,11 +157,32 @@ public:
|
|||||||
|
|
||||||
void AppendNativeHandler(PromiseNativeHandler* aRunnable);
|
void AppendNativeHandler(PromiseNativeHandler* aRunnable);
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
// Do NOT call this unless you're Promise::Create. I wish we could enforce
|
// Do NOT call this unless you're Promise::Create. I wish we could enforce
|
||||||
// that from inside this class too, somehow.
|
// that from inside this class too, somehow.
|
||||||
explicit Promise(nsIGlobalObject* aGlobal);
|
explicit Promise(nsIGlobalObject* aGlobal);
|
||||||
|
|
||||||
|
virtual ~Promise();
|
||||||
|
|
||||||
|
// Queue an async task to current main or worker thread.
|
||||||
|
static void
|
||||||
|
DispatchToMainOrWorkerThread(nsIRunnable* aRunnable);
|
||||||
|
|
||||||
|
// Do JS-wrapping after Promise creation.
|
||||||
|
void CreateWrapper(ErrorResult& aRv);
|
||||||
|
|
||||||
|
// Create the JS resolving functions of resolve() and reject(). And provide
|
||||||
|
// references to the two functions by calling PromiseInit passed from Promise
|
||||||
|
// constructor.
|
||||||
|
void CallInitFunction(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||||
|
ErrorResult& aRv);
|
||||||
|
|
||||||
|
bool IsPending()
|
||||||
|
{
|
||||||
|
return mResolvePending;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
friend class PromiseDebugging;
|
friend class PromiseDebugging;
|
||||||
|
|
||||||
enum PromiseState {
|
enum PromiseState {
|
||||||
@ -189,10 +208,6 @@ private:
|
|||||||
mResult = aValue;
|
mResult = aValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue an async task to current main or worker thread.
|
|
||||||
static void
|
|
||||||
DispatchToMainOrWorkerThread(nsIRunnable* aRunnable);
|
|
||||||
|
|
||||||
// This method processes promise's resolve/reject callbacks with promise's
|
// This method processes promise's resolve/reject callbacks with promise's
|
||||||
// result. It's executed when the resolver.resolve() or resolver.reject() is
|
// result. It's executed when the resolver.resolve() or resolver.reject() is
|
||||||
// called or when the promise already has a result and new callbacks are
|
// called or when the promise already has a result and new callbacks are
|
||||||
|
@ -20,13 +20,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseCallback)
|
|||||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||||
NS_INTERFACE_MAP_END
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_CLASS(PromiseCallback)
|
NS_IMPL_CYCLE_COLLECTION_0(PromiseCallback)
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PromiseCallback)
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PromiseCallback)
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
||||||
|
|
||||||
PromiseCallback::PromiseCallback()
|
PromiseCallback::PromiseCallback()
|
||||||
{
|
{
|
||||||
|
36
dom/promise/PromiseNativeAbortCallback.h
Normal file
36
dom/promise/PromiseNativeAbortCallback.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* -*- 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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_PromiseNativeAbortCallback_h
|
||||||
|
#define mozilla_dom_PromiseNativeAbortCallback_h
|
||||||
|
|
||||||
|
#include "nsISupports.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PromiseNativeAbortCallback allows C++ to react to an AbortablePromise being
|
||||||
|
* aborted.
|
||||||
|
*/
|
||||||
|
class PromiseNativeAbortCallback : public nsISupports
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual ~PromiseNativeAbortCallback()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Implemented in AbortablePromise.cpp.
|
||||||
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
|
NS_DECL_CYCLE_COLLECTION_CLASS(PromiseNativeAbortCallback)
|
||||||
|
|
||||||
|
virtual void Call() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_PromiseNativeAbortCallback_h
|
@ -5,13 +5,16 @@
|
|||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
EXPORTS.mozilla.dom += [
|
EXPORTS.mozilla.dom += [
|
||||||
|
'AbortablePromise.h',
|
||||||
'Promise.h',
|
'Promise.h',
|
||||||
'PromiseDebugging.h',
|
'PromiseDebugging.h',
|
||||||
|
'PromiseNativeAbortCallback.h',
|
||||||
'PromiseNativeHandler.h',
|
'PromiseNativeHandler.h',
|
||||||
'PromiseWorkerProxy.h',
|
'PromiseWorkerProxy.h',
|
||||||
]
|
]
|
||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
|
'AbortablePromise.cpp',
|
||||||
'Promise.cpp',
|
'Promise.cpp',
|
||||||
'PromiseCallback.cpp',
|
'PromiseCallback.cpp',
|
||||||
'PromiseDebugging.cpp'
|
'PromiseDebugging.cpp'
|
||||||
|
@ -4,3 +4,4 @@
|
|||||||
[test_promise.html]
|
[test_promise.html]
|
||||||
[test_promise_utils.html]
|
[test_promise_utils.html]
|
||||||
[test_resolve.html]
|
[test_resolve.html]
|
||||||
|
[test_abortable_promise.html]
|
||||||
|
115
dom/promise/tests/test_abortable_promise.html
Normal file
115
dom/promise/tests/test_abortable_promise.html
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Promise.resolve(anything) Test</title>
|
||||||
|
<script type="application/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">
|
||||||
|
<script type="application/javascript"><!--
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
SpecialPowers.pushPrefEnv({"set": [["dom.abortablepromise.enabled", true]]},
|
||||||
|
runTest);
|
||||||
|
var gTests = [testPending, testResolved, testRejected];
|
||||||
|
|
||||||
|
function runTest() {
|
||||||
|
if (gTests.length == 0) {
|
||||||
|
SimpleTest.finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new Promise(gTests.shift()).then(runTest, SimpleTest.finish);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aborting pending promise should first reject the promise and then call the
|
||||||
|
// abortable callback.
|
||||||
|
// The test succeeds once both the rejection handler and the abort handler have
|
||||||
|
// been called.
|
||||||
|
function testPending(succeed, fail) {
|
||||||
|
var rejected = false;
|
||||||
|
var aborted = false;
|
||||||
|
|
||||||
|
var p = new MozAbortablePromise(function(resolve, reject) {
|
||||||
|
// Wait for a while so that the promise can be aborted before resolved.
|
||||||
|
SimpleTest.executeSoon(function() {
|
||||||
|
resolve(0);
|
||||||
|
});
|
||||||
|
}, function abortable() {
|
||||||
|
aborted = true;
|
||||||
|
ok(true, "Promise aborted.");
|
||||||
|
if (rejected) {
|
||||||
|
succeed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
p.then(function() {
|
||||||
|
ok(false, "Failed to abort pending promise.");
|
||||||
|
fail();
|
||||||
|
}, function(what) {
|
||||||
|
rejected = true;
|
||||||
|
var isAbortException = (what && what.name) == "NS_ERROR_ABORT";
|
||||||
|
ok(isAbortException, "Should have NS_ERROR_ABORT exception");
|
||||||
|
if (!isAbortException) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
if (aborted) {
|
||||||
|
succeed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Abort the promise on creation.
|
||||||
|
p.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing when aborting resolved promise.
|
||||||
|
function testResolved(succeed, fail) {
|
||||||
|
var p = new MozAbortablePromise(function(resolve, reject) {
|
||||||
|
resolve();
|
||||||
|
}, function abortable() {
|
||||||
|
ok(false, "Should not abort a resolved promise.");
|
||||||
|
fail();
|
||||||
|
});
|
||||||
|
p.then(function() {
|
||||||
|
ok(true, "Promise resolved.");
|
||||||
|
// Wait for a while to ensure abort handle won't be called.
|
||||||
|
setTimeout(succeed, 1000);
|
||||||
|
}, function(what) {
|
||||||
|
ok(false, "Failed to resolve promise: " + what);
|
||||||
|
fail();
|
||||||
|
});
|
||||||
|
p.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing when aborting rejected promise.
|
||||||
|
function testRejected(succeed, fail) {
|
||||||
|
var p = new MozAbortablePromise(function(resolve, reject) {
|
||||||
|
reject(0);
|
||||||
|
}, function abortable() {
|
||||||
|
ok(false, "Should not abort a rejected promise.");
|
||||||
|
fail();
|
||||||
|
});
|
||||||
|
|
||||||
|
p.then(function() {
|
||||||
|
ok(false, "Failed to reject promise.");
|
||||||
|
fail();
|
||||||
|
}, function(what) {
|
||||||
|
is(what, 0, "promise rejected: " + what);
|
||||||
|
// Wait for a while to ensure abort handle won't be called.
|
||||||
|
setTimeout(succeed, 1000);
|
||||||
|
});
|
||||||
|
p.abort();
|
||||||
|
}
|
||||||
|
// -->
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -118,6 +118,8 @@ var legacyMozPrefixedInterfaces =
|
|||||||
// IMPORTANT: Do not change the list below without review from a DOM peer!
|
// IMPORTANT: Do not change the list below without review from a DOM peer!
|
||||||
var interfaceNamesInGlobalScope =
|
var interfaceNamesInGlobalScope =
|
||||||
[
|
[
|
||||||
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
|
{name: "MozAbortablePromise", pref: "dom.abortablepromise.enabled"},
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
{name: "AlarmsManager", pref: "dom.mozAlarms.enabled"},
|
{name: "AlarmsManager", pref: "dom.mozAlarms.enabled"},
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
|
19
dom/webidl/AbortablePromise.webidl
Normal file
19
dom/webidl/AbortablePromise.webidl
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
callback AbortCallback = void ();
|
||||||
|
|
||||||
|
[Constructor(PromiseInit init, AbortCallback abortCallback),
|
||||||
|
Pref="dom.abortablepromise.enabled"]
|
||||||
|
interface MozAbortablePromise : _Promise {
|
||||||
|
/*
|
||||||
|
* Aborts the promise.
|
||||||
|
* If the promise has not been resolved or rejected, it should be rejected
|
||||||
|
* with an Exception of type abort and then AbortCallback is called
|
||||||
|
* asynchronously. Otherwise, nothing should be done.
|
||||||
|
*/
|
||||||
|
void abort();
|
||||||
|
};
|
@ -16,6 +16,7 @@ PREPROCESSED_WEBIDL_FILES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
WEBIDL_FILES = [
|
WEBIDL_FILES = [
|
||||||
|
'AbortablePromise.webidl',
|
||||||
'AbstractWorker.webidl',
|
'AbstractWorker.webidl',
|
||||||
'ActivityRequestHandler.webidl',
|
'ActivityRequestHandler.webidl',
|
||||||
'AlarmsManager.webidl',
|
'AlarmsManager.webidl',
|
||||||
|
@ -107,6 +107,9 @@ pref("offline-apps.quota.warn", 51200);
|
|||||||
// cache compression turned off for now - see bug #715198
|
// cache compression turned off for now - see bug #715198
|
||||||
pref("browser.cache.compression_level", 0);
|
pref("browser.cache.compression_level", 0);
|
||||||
|
|
||||||
|
// Whether or not MozAbortablePromise is enabled.
|
||||||
|
pref("dom.abortablepromise.enabled", false);
|
||||||
|
|
||||||
// Whether or not testing features are enabled.
|
// Whether or not testing features are enabled.
|
||||||
pref("dom.quotaManager.testing", false);
|
pref("dom.quotaManager.testing", false);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user