mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:43:44 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1213818 - Align document.title for SVG documents with HTML spec; r=bz (fb60e8c048) - Bug 1234170 - WebSocket should check if the channel has been opened before send the 'close' notification to the WebSocketEventService, r=jduell (4bfd6f3f3f) - Bug 1245261 - Use an atomic to safely access gcTriggerBytes; r=jonco (f9c80d47e1) - Bug 1243001 part 1. Remove the dead WrappedWorkerRunnable class from Promise code. r=peterv (9f8c758723) - Bug 1243001 part 2. Make Promise an empty [NoInterfaceObject] interface when SPIDERMONKEY_PROMISE is defined. r=peterv (6be034ee59) - Bug 1243001 part 3. Turn off the IDL bits of PromiseDebugging when SPIDERMONKEY_PROMISE is defined. r=peterv (114241ddd6) - Bug 1243001 part 4. Switch to using MaybeResolve/MaybeReject instead of ResolveInternal/RejectInternal for PromiseWorkerProxy. r=peterv (ca8faf02f8) - Bug 1243114 - Convert PromiseCapability::mPromise to a rooted JSObject* instead of a rooted JS::Value. r=bz (e4c907e5c2) - Bug 1243001 part 5. Get rid of most of the dom::Promise methods when SPIDERMONKEY_PROMISE is defined, and reimplement the rest in terms of SpiderMonkey Promise. r=peterv (693a61e5a2) - Bug 1242054. Get rid of AbortablePromise, so we can move Promise into SpiderMonkey more easily. r=khuey (6a0200e625) - Bug 1243001 part 6. Implement Promise::AppendNativeHandler in the SPIDERMONKEY_PROMISE world. r=peterv (7c3a6390f9) - Bug 1243001 part 7. Stop wrappercaching dom::Promise when SPIDERMONKEY_PROMISE is defined. r=peterv (be1bd9b33f) - Bug 1243001 part 8. Tell SpiderMonkey to put its promise jobs into the CycleCollectedJSRuntime job queue. r=peterv (192e6a551c) - Bug 1156880 - Null check the prescontext in nsDOMWindowUtils::AdvanceTimeAndRefresh; r=mstange (11f1a39f22) - Bug 1191597 part 1 - Add head.js and dummy page for browser chrome test. r=smaug (5257870dd3) - Bug 1191597 part 2 - Convert fullscreen-esc-context-menu to a browser chrome test. r=smaug (e1c0fe84a4)
This commit is contained in:
@@ -232,7 +232,7 @@ DOMRequest::Then(JSContext* aCx, AnyCallback* aResolveCallback,
|
||||
}
|
||||
|
||||
// Just use the global of the Promise itself as the callee global.
|
||||
JS::Rooted<JSObject*> global(aCx, mPromise->GetWrapper());
|
||||
JS::Rooted<JSObject*> global(aCx, mPromise->PromiseObj());
|
||||
global = js::GetGlobalForObjectCrossCompartment(global);
|
||||
mPromise->Then(aCx, global, aResolveCallback, aRejectCallback, aRetval, aRv);
|
||||
}
|
||||
|
||||
@@ -1912,9 +1912,11 @@ WebSocket::CreateAndDispatchCloseEvent(bool aWasClean,
|
||||
MOZ_ASSERT(mImpl);
|
||||
AssertIsOnTargetThread();
|
||||
|
||||
mImpl->mService->WebSocketClosed(mImpl->mChannel->Serial(),
|
||||
mImpl->mInnerWindowID,
|
||||
aWasClean, aCode, aReason);
|
||||
if (mImpl->mChannel) {
|
||||
mImpl->mService->WebSocketClosed(mImpl->mChannel->Serial(),
|
||||
mImpl->mInnerWindowID,
|
||||
aWasClean, aCode, aReason);
|
||||
}
|
||||
|
||||
nsresult rv = CheckInnerWindowCorrectness();
|
||||
if (NS_FAILED(rv)) {
|
||||
|
||||
@@ -2285,7 +2285,7 @@ nsDOMWindowUtils::GetSupportsHardwareH264Decoding(JS::MutableHandle<JS::Value> a
|
||||
RefPtr<Promise> promise =
|
||||
MP4Decoder::IsVideoAccelerated(mgr->GetCompositorBackendType(), parentObject);
|
||||
NS_ENSURE_STATE(promise);
|
||||
aPromise.setObject(*promise->GetWrapper());
|
||||
aPromise.setObject(*promise->PromiseObj());
|
||||
#else
|
||||
ErrorResult rv;
|
||||
RefPtr<Promise> promise = Promise::Create(parentObject, rv);
|
||||
@@ -2293,7 +2293,7 @@ nsDOMWindowUtils::GetSupportsHardwareH264Decoding(JS::MutableHandle<JS::Value> a
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
promise.MaybeResolve(NS_LITERAL_STRING("No; Compiled without MP4 support."));
|
||||
aPromise.setObject(*promise->GetWrapper());
|
||||
aPromise.setObject(*promise->PromiseObj());
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -2416,12 +2416,15 @@ nsDOMWindowUtils::AdvanceTimeAndRefresh(int64_t aMilliseconds)
|
||||
}
|
||||
}
|
||||
|
||||
nsRefreshDriver* driver = GetPresContext()->RefreshDriver();
|
||||
driver->AdvanceTimeAndRefresh(aMilliseconds);
|
||||
nsPresContext* presContext = GetPresContext();
|
||||
if (presContext) {
|
||||
nsRefreshDriver* driver = presContext->RefreshDriver();
|
||||
driver->AdvanceTimeAndRefresh(aMilliseconds);
|
||||
|
||||
RefPtr<LayerTransactionChild> transaction = GetLayerTransaction();
|
||||
if (transaction && transaction->IPCOpen()) {
|
||||
transaction->SendSetTestSampleTime(driver->MostRecentRefresh());
|
||||
RefPtr<LayerTransactionChild> transaction = GetLayerTransaction();
|
||||
if (transaction && transaction->IPCOpen()) {
|
||||
transaction->SendSetTestSampleTime(driver->MostRecentRefresh());
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
||||
+68
-48
@@ -7010,8 +7010,8 @@ nsIDocument::GetHtmlChildElement(nsIAtom* aTag)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsDocument::GetTitleContent(uint32_t aNamespace)
|
||||
Element*
|
||||
nsDocument::GetTitleElement()
|
||||
{
|
||||
// mMayHaveTitleElement will have been set to true if any HTML or SVG
|
||||
// <title> element has been bound to this document. So if it's false,
|
||||
@@ -7021,19 +7021,26 @@ nsDocument::GetTitleContent(uint32_t aNamespace)
|
||||
if (!mMayHaveTitleElement)
|
||||
return nullptr;
|
||||
|
||||
Element* root = GetRootElement();
|
||||
if (root && root->IsSVGElement(nsGkAtoms::svg)) {
|
||||
// In SVG, the document's title must be a child
|
||||
for (nsIContent* child = root->GetFirstChild();
|
||||
child; child = child->GetNextSibling()) {
|
||||
if (child->IsSVGElement(nsGkAtoms::title)) {
|
||||
return child->AsElement();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We check the HTML namespace even for non-HTML documents, except SVG. This
|
||||
// matches the spec and the behavior of all tested browsers.
|
||||
RefPtr<nsContentList> list =
|
||||
NS_GetContentList(this, aNamespace, NS_LITERAL_STRING("title"));
|
||||
NS_GetContentList(this, kNameSpaceID_XHTML, NS_LITERAL_STRING("title"));
|
||||
|
||||
return list->Item(0, false);
|
||||
}
|
||||
nsIContent* first = list->Item(0, false);
|
||||
|
||||
void
|
||||
nsDocument::GetTitleFromElement(uint32_t aNamespace, nsAString& aTitle)
|
||||
{
|
||||
nsIContent* title = GetTitleContent(aNamespace);
|
||||
if (!title)
|
||||
return;
|
||||
nsContentUtils::GetNodeTextContent(title, false, aTitle);
|
||||
return first ? first->AsElement() : nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -7050,26 +7057,24 @@ nsDocument::GetTitle(nsString& aTitle)
|
||||
{
|
||||
aTitle.Truncate();
|
||||
|
||||
nsIContent *rootElement = GetRootElement();
|
||||
if (!rootElement)
|
||||
Element* rootElement = GetRootElement();
|
||||
if (!rootElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString tmp;
|
||||
|
||||
switch (rootElement->GetNameSpaceID()) {
|
||||
#ifdef MOZ_XUL
|
||||
case kNameSpaceID_XUL:
|
||||
rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
|
||||
break;
|
||||
if (rootElement->IsXULElement()) {
|
||||
rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
|
||||
} else
|
||||
#endif
|
||||
case kNameSpaceID_SVG:
|
||||
if (rootElement->IsSVGElement(nsGkAtoms::svg)) {
|
||||
GetTitleFromElement(kNameSpaceID_SVG, tmp);
|
||||
break;
|
||||
} // else fall through
|
||||
default:
|
||||
GetTitleFromElement(kNameSpaceID_XHTML, tmp);
|
||||
break;
|
||||
{
|
||||
Element* title = GetTitleElement();
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
nsContentUtils::GetNodeTextContent(title, false, tmp);
|
||||
}
|
||||
|
||||
tmp.CompressWhitespace();
|
||||
@@ -7079,41 +7084,56 @@ nsDocument::GetTitle(nsString& aTitle)
|
||||
NS_IMETHODIMP
|
||||
nsDocument::SetTitle(const nsAString& aTitle)
|
||||
{
|
||||
Element *rootElement = GetRootElement();
|
||||
if (!rootElement)
|
||||
Element* rootElement = GetRootElement();
|
||||
if (!rootElement) {
|
||||
return NS_OK;
|
||||
|
||||
switch (rootElement->GetNameSpaceID()) {
|
||||
case kNameSpaceID_SVG:
|
||||
return NS_OK; // SVG doesn't support setting a title
|
||||
#ifdef MOZ_XUL
|
||||
case kNameSpaceID_XUL:
|
||||
return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
|
||||
aTitle, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
if (rootElement->IsXULElement()) {
|
||||
return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
|
||||
aTitle, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Batch updates so that mutation events don't change "the title
|
||||
// element" under us
|
||||
mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, true);
|
||||
|
||||
nsIContent* title = GetTitleContent(kNameSpaceID_XHTML);
|
||||
if (!title) {
|
||||
Element *head = GetHeadElement();
|
||||
if (!head)
|
||||
return NS_OK;
|
||||
nsCOMPtr<Element> title = GetTitleElement();
|
||||
if (rootElement->IsSVGElement(nsGkAtoms::svg)) {
|
||||
if (!title) {
|
||||
RefPtr<mozilla::dom::NodeInfo> titleInfo =
|
||||
mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
|
||||
kNameSpaceID_SVG,
|
||||
nsIDOMNode::ELEMENT_NODE);
|
||||
NS_NewSVGElement(getter_AddRefs(title), titleInfo.forget(),
|
||||
NOT_FROM_PARSER);
|
||||
if (!title) {
|
||||
return NS_OK;
|
||||
}
|
||||
rootElement->InsertChildAt(title, 0, true);
|
||||
}
|
||||
} else if (rootElement->IsHTMLElement()) {
|
||||
if (!title) {
|
||||
Element* head = GetHeadElement();
|
||||
if (!head) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
{
|
||||
RefPtr<mozilla::dom::NodeInfo> titleInfo;
|
||||
titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
|
||||
kNameSpaceID_XHTML,
|
||||
nsIDOMNode::ELEMENT_NODE);
|
||||
kNameSpaceID_XHTML,
|
||||
nsIDOMNode::ELEMENT_NODE);
|
||||
title = NS_NewHTMLTitleElement(titleInfo.forget());
|
||||
if (!title)
|
||||
if (!title) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
head->AppendChildTo(title, true);
|
||||
head->AppendChildTo(title, true);
|
||||
}
|
||||
} else {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return nsContentUtils::SetNodeTextContent(title, aTitle, false);
|
||||
|
||||
@@ -1447,13 +1447,15 @@ protected:
|
||||
nsIContent* GetFirstBaseNodeWithHref();
|
||||
nsresult SetFirstBaseNodeWithHref(nsIContent *node);
|
||||
|
||||
// Get the first <title> element with the given IsNodeOfType type, or
|
||||
// return null if there isn't one
|
||||
nsIContent* GetTitleContent(uint32_t aNodeType);
|
||||
// Find the first "title" element in the given IsNodeOfType type and
|
||||
// append the concatenation of its text node children to aTitle. Do
|
||||
// nothing if there is no such element.
|
||||
void GetTitleFromElement(uint32_t aNodeType, nsAString& aTitle);
|
||||
/**
|
||||
* Returns the title element of the document as defined by the HTML
|
||||
* specification, or null if there isn't one. For documents whose root
|
||||
* element is an <svg:svg>, this is the first <svg:title> element that's a
|
||||
* child of the root. For other documents, it's the first HTML title element
|
||||
* in the document.
|
||||
*/
|
||||
Element* GetTitleElement();
|
||||
|
||||
public:
|
||||
// Get our title
|
||||
virtual void GetTitle(nsString& aTitle) override;
|
||||
|
||||
@@ -2795,6 +2795,7 @@ ConvertExceptionToPromise(JSContext* cx,
|
||||
JSObject* promiseScope,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
{
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
GlobalObject global(cx, promiseScope);
|
||||
if (global.Failed()) {
|
||||
return false;
|
||||
@@ -2829,6 +2830,33 @@ ConvertExceptionToPromise(JSContext* cx,
|
||||
}
|
||||
|
||||
return GetOrCreateDOMReflector(cx, promise, rval);
|
||||
#else // SPIDERMONKEY_PROMISE
|
||||
{
|
||||
JSAutoCompartment ac(cx, promiseScope);
|
||||
|
||||
JS::Rooted<JS::Value> exn(cx);
|
||||
if (!JS_GetPendingException(cx, &exn)) {
|
||||
// This is very important: if there is no pending exception here but we're
|
||||
// ending up in this code, that means the callee threw an uncatchable
|
||||
// exception. Just propagate that out as-is.
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_ClearPendingException(cx);
|
||||
|
||||
JSObject* promise = JS::CallOriginalPromiseReject(cx, exn);
|
||||
if (!promise) {
|
||||
// We just give up. Put the exception back.
|
||||
JS_SetPendingException(cx, exn);
|
||||
return false;
|
||||
}
|
||||
|
||||
rval.setObject(*promise);
|
||||
}
|
||||
|
||||
// Now make sure we rewrap promise back into the compartment we want
|
||||
return JS_WrapValue(cx, rval);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
||||
@@ -1131,7 +1131,8 @@ WrapNewBindingNonWrapperCachedObject(JSContext* cx,
|
||||
|
||||
// Helper for smart pointers (nsRefPtr/nsCOMPtr).
|
||||
template <template <typename> class SmartPtr, typename T,
|
||||
typename U=typename EnableIf<IsRefcounted<T>::value, T>::Type>
|
||||
typename U=typename EnableIf<IsRefcounted<T>::value, T>::Type,
|
||||
typename V=typename EnableIf<IsSmartPtr<SmartPtr<T>>::value, T>::Type>
|
||||
inline bool
|
||||
WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
|
||||
const SmartPtr<T>& value,
|
||||
@@ -1142,6 +1143,19 @@ WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
|
||||
givenProto);
|
||||
}
|
||||
|
||||
// Helper for object references (as opposed to pointers).
|
||||
template <typename T,
|
||||
typename U=typename EnableIf<!IsSmartPtr<T>::value, T>::Type>
|
||||
inline bool
|
||||
WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
|
||||
T& value,
|
||||
JS::MutableHandle<JS::Value> rval,
|
||||
JS::Handle<JSObject*> givenProto = nullptr)
|
||||
{
|
||||
return WrapNewBindingNonWrapperCachedObject(cx, scope, &value, rval,
|
||||
givenProto);
|
||||
}
|
||||
|
||||
// Only set allowNativeWrapper to false if you really know you need it, if in
|
||||
// doubt use true. Setting it to false disables security wrappers.
|
||||
bool
|
||||
|
||||
@@ -80,10 +80,6 @@ DOMInterfaces = {
|
||||
'nativeType': 'mozilla::dom::Activity',
|
||||
},
|
||||
|
||||
'MozAbortablePromise': {
|
||||
'nativeType': 'mozilla::dom::AbortablePromise',
|
||||
},
|
||||
|
||||
'AbstractWorker': {
|
||||
'concrete': False
|
||||
},
|
||||
@@ -977,6 +973,10 @@ DOMInterfaces = {
|
||||
'concrete': False,
|
||||
},
|
||||
|
||||
'PromiseNativeHandler': {
|
||||
'wrapperCache': False,
|
||||
},
|
||||
|
||||
'PropertyNodeList': {
|
||||
'headerFile': 'HTMLPropertiesCollection.h',
|
||||
},
|
||||
|
||||
+31
-13
@@ -2982,6 +2982,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
||||
if self.descriptor.name == "Promise":
|
||||
speciesSetup = CGGeneric(fill(
|
||||
"""
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
JS::Rooted<JSObject*> promiseConstructor(aCx, *interfaceCache);
|
||||
JS::Rooted<jsid> species(aCx,
|
||||
SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species)));
|
||||
@@ -2989,6 +2990,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
||||
JSPROP_SHARED, Promise::PromiseSpecies, nullptr)) {
|
||||
$*{failureCode}
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
""",
|
||||
failureCode=failureCode))
|
||||
else:
|
||||
@@ -5037,11 +5039,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
return JSToNativeConversionInfo(template, declType=declType,
|
||||
dealWithOptional=isOptional)
|
||||
|
||||
if descriptor.interface.identifier.name == "AbortablePromise":
|
||||
raise TypeError("Need to figure out what argument conversion "
|
||||
"should look like for AbortablePromise: %s" %
|
||||
sourceDescription)
|
||||
|
||||
# This is an interface that we implement as a concrete class
|
||||
# or an XPCOM interface.
|
||||
|
||||
@@ -5185,7 +5182,26 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
if (promiseGlobal.Failed()) {
|
||||
$*{exceptionCode}
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> valueToResolve(cx, $${val});
|
||||
if (!JS_WrapValue(cx, &valueToResolve)) {
|
||||
$*{exceptionCode}
|
||||
}
|
||||
ErrorResult promiseRv;
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
nsCOMPtr<nsIGlobalObject> global =
|
||||
do_QueryInterface(promiseGlobal.GetAsSupports());
|
||||
if (!global) {
|
||||
promiseRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
promiseRv.MaybeSetPendingException(cx);
|
||||
$*{exceptionCode}
|
||||
}
|
||||
$${declName} = Promise::Resolve(global, cx, valueToResolve,
|
||||
promiseRv);
|
||||
if (promiseRv.MaybeSetPendingException(cx)) {
|
||||
$*{exceptionCode}
|
||||
}
|
||||
#else
|
||||
JS::Handle<JSObject*> promiseCtor =
|
||||
PromiseBinding::GetConstructorObjectHandle(cx, globalObj);
|
||||
if (!promiseCtor) {
|
||||
@@ -5193,10 +5209,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
}
|
||||
JS::Rooted<JS::Value> resolveThisv(cx, JS::ObjectValue(*promiseCtor));
|
||||
JS::Rooted<JS::Value> resolveResult(cx);
|
||||
JS::Rooted<JS::Value> valueToResolve(cx, $${val});
|
||||
if (!JS_WrapValue(cx, &valueToResolve)) {
|
||||
$*{exceptionCode}
|
||||
}
|
||||
Promise::Resolve(promiseGlobal, resolveThisv, valueToResolve,
|
||||
&resolveResult, promiseRv);
|
||||
if (promiseRv.MaybeSetPendingException(cx)) {
|
||||
@@ -5208,6 +5220,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
promiseRv.MaybeSetPendingException(cx);
|
||||
$*{exceptionCode}
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
}
|
||||
""",
|
||||
getPromiseGlobal=getPromiseGlobal,
|
||||
@@ -6328,7 +6341,13 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
||||
wrapMethod = "GetOrCreateDOMReflector"
|
||||
wrapArgs = "cx, %s, ${jsvalHandle}" % result
|
||||
else:
|
||||
if not returnsNewObject:
|
||||
# Hack: the "Promise" interface is OK to return from
|
||||
# non-newobject things even when it's not wrappercached; that
|
||||
# happens when using SpiderMonkey promises, and the WrapObject()
|
||||
# method will just return the existing reflector, which is just
|
||||
# not stored in a wrappercache.
|
||||
if (not returnsNewObject and
|
||||
descriptor.interface.identifier.name != "Promise"):
|
||||
raise MethodNotNewObjectError(descriptor.interface.identifier.name)
|
||||
wrapMethod = "WrapNewBindingNonWrapperCachedObject"
|
||||
wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result
|
||||
@@ -7149,8 +7168,7 @@ class CGPerSignatureCall(CGThing):
|
||||
|
||||
# Hack for making Promise.prototype.then work well over Xrays.
|
||||
if (not static and
|
||||
(descriptor.name == "Promise" or
|
||||
descriptor.name == "MozAbortablePromise") and
|
||||
descriptor.name == "Promise" and
|
||||
idlNode.isMethod() and
|
||||
idlNode.identifier.name == "then"):
|
||||
cgThings.append(CGGeneric(dedent(
|
||||
@@ -7162,7 +7180,7 @@ class CGPerSignatureCall(CGThing):
|
||||
needsUnwrap = False
|
||||
argsPost = []
|
||||
if isConstructor:
|
||||
if descriptor.name == "Promise" or descriptor.name == "MozAbortablePromise":
|
||||
if descriptor.name == "Promise":
|
||||
# Hack for Promise for now: pass in our desired proto so the
|
||||
# implementation can create the reflector with the right proto.
|
||||
argsPost.append("desiredProto")
|
||||
|
||||
@@ -532,6 +532,13 @@ class Descriptor(DescriptorProvider):
|
||||
self.wrapperCache = (not self.interface.isCallback() and
|
||||
not self.interface.isIteratorInterface() and
|
||||
desc.get('wrapperCache', True))
|
||||
# Nasty temporary hack for supporting both DOM and SpiderMonkey promises
|
||||
# without too much pain
|
||||
if self.interface.identifier.name == "Promise":
|
||||
assert self.wrapperCache
|
||||
# But really, we're only wrappercached if we have an interface
|
||||
# object (that is, when we're not using SpiderMonkey promises).
|
||||
self.wrapperCache = self.interface.hasInterfaceObject()
|
||||
|
||||
def make_name(name):
|
||||
return name + "_workers" if self.workers else name
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
#include "nsAString.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsStringBuffer.h"
|
||||
@@ -64,5 +67,15 @@ ToJSValue(JSContext* aCx,
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
bool
|
||||
ToJSValue(JSContext* aCx, Promise& aArgument,
|
||||
JS::MutableHandle<JS::Value> aValue)
|
||||
{
|
||||
aValue.setObject(*aArgument.PromiseObj());
|
||||
return true;
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
|
||||
// If ToJSValue returns false, it must set an exception on the
|
||||
// JSContext.
|
||||
|
||||
@@ -304,6 +306,14 @@ ToJSValue(JSContext* aCx,
|
||||
return ToJSValue(aCx, *aArgument, aValue);
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
// Accept Promise objects, which need special handling.
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
ToJSValue(JSContext* aCx,
|
||||
Promise& aArgument,
|
||||
JS::MutableHandle<JS::Value> aValue);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
// Accept arrays of other things we accept
|
||||
template <typename T>
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
bug592641_img.jpg
|
||||
dummy_page.html
|
||||
file_bug649778.html
|
||||
file_bug649778.html^headers^
|
||||
head.js
|
||||
|
||||
[browser_bug592641.js]
|
||||
[browser_bug649778.js]
|
||||
@@ -14,3 +16,4 @@ support-files =
|
||||
file_bug1108547-2.html
|
||||
file_bug1108547-3.html
|
||||
[browser_DOMDocElementInserted.js]
|
||||
[browser_fullscreen-contextmenu-esc.js]
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
"use strict";
|
||||
|
||||
function frameScript() {
|
||||
addMessageListener("Test:RequestFullscreen", () => {
|
||||
content.document.body.mozRequestFullScreen();
|
||||
});
|
||||
content.document.addEventListener("mozfullscreenchange", () => {
|
||||
sendAsyncMessage("Test:FullscreenChanged", content.document.mozFullScreen);
|
||||
});
|
||||
addMessageListener("Test:QueryFullscreenState", () => {
|
||||
sendAsyncMessage("Test:FullscreenState", content.document.mozFullScreen);
|
||||
});
|
||||
function waitUntilActive() {
|
||||
let doc = content.document;
|
||||
if (doc.docShell.isActive && doc.hasFocus()) {
|
||||
sendAsyncMessage("Test:Activated");
|
||||
} else {
|
||||
setTimeout(waitUntilActive, 10);
|
||||
}
|
||||
}
|
||||
waitUntilActive();
|
||||
}
|
||||
|
||||
var gMessageManager;
|
||||
|
||||
function listenOneMessage(aMsg, aListener) {
|
||||
function listener({ data }) {
|
||||
gMessageManager.removeMessageListener(aMsg, listener);
|
||||
aListener(data);
|
||||
}
|
||||
gMessageManager.addMessageListener(aMsg, listener);
|
||||
}
|
||||
|
||||
function promiseOneMessage(aMsg) {
|
||||
return new Promise(resolve => listenOneMessage(aMsg, resolve));
|
||||
}
|
||||
|
||||
function captureUnexpectedFullscreenChange() {
|
||||
ok(false, "Caught an unexpected fullscreen change");
|
||||
}
|
||||
|
||||
const kPage = "http://example.org/browser/dom/html/test/dummy_page.html";
|
||||
|
||||
add_task(function* () {
|
||||
yield pushPrefs(
|
||||
["full-screen-api.transition-duration.enter", "0 0"],
|
||||
["full-screen-api.transition-duration.leave", "0 0"]);
|
||||
|
||||
let tab = gBrowser.addTab(kPage);
|
||||
registerCleanupFunction(() => gBrowser.removeTab(tab));
|
||||
let browser = tab.linkedBrowser;
|
||||
gBrowser.selectedTab = tab;
|
||||
yield waitForDocLoadComplete();
|
||||
|
||||
gMessageManager = browser.messageManager;
|
||||
gMessageManager.loadFrameScript(
|
||||
"data:,(" + frameScript.toString() + ")();", false);
|
||||
|
||||
// Wait for the document being activated, so that
|
||||
// fullscreen request won't be denied.
|
||||
yield promiseOneMessage("Test:Activated");
|
||||
|
||||
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
ok(contextMenu, "Got context menu");
|
||||
|
||||
let state;
|
||||
info("Enter DOM fullscreen");
|
||||
gMessageManager.sendAsyncMessage("Test:RequestFullscreen");
|
||||
state = yield promiseOneMessage("Test:FullscreenChanged");
|
||||
ok(state, "The content should have entered fullscreen");
|
||||
ok(document.mozFullScreen, "The chrome should also be in fullscreen");
|
||||
gMessageManager.addMessageListener(
|
||||
"Test:FullscreenChanged", captureUnexpectedFullscreenChange);
|
||||
|
||||
info("Open context menu");
|
||||
is(contextMenu.state, "closed", "Should not have opened context menu");
|
||||
let popupShownPromise = promiseWaitForEvent(window, "popupshown");
|
||||
EventUtils.synthesizeMouse(browser, screen.width / 2, screen.height / 2,
|
||||
{type: "contextmenu", button: 2}, window);
|
||||
yield popupShownPromise;
|
||||
is(contextMenu.state, "open", "Should have opened context menu");
|
||||
|
||||
info("Send the first escape");
|
||||
let popupHidePromise = promiseWaitForEvent(window, "popuphidden");
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield popupHidePromise;
|
||||
is(contextMenu.state, "closed", "Should have closed context menu");
|
||||
|
||||
// Wait a small time to confirm that the first ESC key
|
||||
// does not exit fullscreen.
|
||||
yield new Promise(resolve => setTimeout(resolve, 1000));
|
||||
gMessageManager.sendAsyncMessage("Test:QueryFullscreenState");
|
||||
state = yield promiseOneMessage("Test:FullscreenState");
|
||||
ok(state, "The content should still be in fullscreen");
|
||||
ok(document.mozFullScreen, "The chrome should still be in fullscreen");
|
||||
|
||||
info("Send the second escape");
|
||||
gMessageManager.removeMessageListener(
|
||||
"Test:FullscreenChanged", captureUnexpectedFullscreenChange);
|
||||
let fullscreenExitPromise = promiseOneMessage("Test:FullscreenChanged");
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
state = yield fullscreenExitPromise;
|
||||
ok(!state, "The content should have exited fullscreen");
|
||||
ok(!document.mozFullScreen, "The chrome should have exited fullscreen");
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Dummy test page</title>
|
||||
<meta charset="utf-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<p>Dummy test page</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,100 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=910532
|
||||
|
||||
Verify that an ESC key press canceling the context menu
|
||||
won't exit DOM fullscreen.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Text for bug 910532</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="file_fullscreen-utils.js"></script>
|
||||
<style>
|
||||
body:-moz-full-screen {
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript">
|
||||
|
||||
const Ci = SpecialPowers.Ci;
|
||||
|
||||
SimpleTest.requestFlakyTimeout("We need to wait a small time to confirm " +
|
||||
"that the first ESC key does not exit fullscreen.");
|
||||
|
||||
function ok(condition, msg) {
|
||||
opener.ok(condition, "[esc-context-menu] " + msg);
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
opener.is(a, b, "[esc-context-menu] " + msg);
|
||||
}
|
||||
|
||||
var contextMenu;
|
||||
var escapeSent = 0;
|
||||
|
||||
function sendEscape() {
|
||||
escapeSent++;
|
||||
synthesizeKey("VK_ESCAPE", {});
|
||||
}
|
||||
|
||||
function begin() {
|
||||
// Copy from browser/base/content/test/general/test_contextmenu.html
|
||||
var chromeWin = SpecialPowers.wrap(window)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
contextMenu = chromeWin.document.getElementById("contentAreaContextMenu");
|
||||
ok(contextMenu, "Got context menu XUL");
|
||||
|
||||
addFullscreenChangeContinuation("enter", fullscreenEntered);
|
||||
SpecialPowers.pushPrefEnv({"set":[["full-screen-api.approval-required", false]]}, function() {
|
||||
document.body.mozRequestFullScreen();
|
||||
});
|
||||
}
|
||||
|
||||
function finish() {
|
||||
opener.nextTest();
|
||||
}
|
||||
|
||||
function fullscreenEntered(event) {
|
||||
ok(document.mozFullScreen, "Should have entered fullscreen mode");
|
||||
is(document.mozFullScreenElement, document.body, "FSE should be doc");
|
||||
contextMenu.addEventListener("popupshown", contextMenuOpened, false);
|
||||
is(contextMenu.state, "closed", "Should not have opened context menu");
|
||||
synthesizeMouseAtCenter(document.body, {type: 'contextmenu', button: 2});
|
||||
}
|
||||
|
||||
function contextMenuOpened(event) {
|
||||
contextMenu.removeEventListener("popupshown", contextMenuOpened);
|
||||
is(contextMenu.state, "open", "Should have opened context menu");
|
||||
addFullscreenChangeContinuation("exit", fullscreenExited);
|
||||
contextMenu.addEventListener("popuphidden", contextMenuClosed, false);
|
||||
sendEscape();
|
||||
}
|
||||
|
||||
function contextMenuClosed(event) {
|
||||
is(contextMenu.state, "closed", "Should have closed context menu");
|
||||
setTimeout(function () {
|
||||
ok(document.mozFullScreen, "Should still be in fullscreen mode");
|
||||
sendEscape();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function fullscreenExited(event) {
|
||||
is(escapeSent, 2, "Only the second escape should exit fullscreen");
|
||||
ok(!document.mozFullScreen, "Should have left fullscreen mode");
|
||||
finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,54 @@
|
||||
function pushPrefs(...aPrefs) {
|
||||
return new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseWaitForEvent(object, eventName, capturing = false, chrome = false) {
|
||||
return new Promise((resolve) => {
|
||||
function listener(event) {
|
||||
info("Saw " + eventName);
|
||||
object.removeEventListener(eventName, listener, capturing, chrome);
|
||||
resolve(event);
|
||||
}
|
||||
|
||||
info("Waiting for " + eventName);
|
||||
object.addEventListener(eventName, listener, capturing, chrome);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the next load to complete in any browser or the given browser.
|
||||
* If a <tabbrowser> is given it waits for a load in any of its browsers.
|
||||
*
|
||||
* @return promise
|
||||
*/
|
||||
function waitForDocLoadComplete(aBrowser=gBrowser) {
|
||||
return new Promise(resolve => {
|
||||
let listener = {
|
||||
onStateChange: function (webProgress, req, flags, status) {
|
||||
let docStop = Ci.nsIWebProgressListener.STATE_IS_NETWORK |
|
||||
Ci.nsIWebProgressListener.STATE_STOP;
|
||||
info("Saw state " + flags.toString(16) + " and status " + status.toString(16));
|
||||
// When a load needs to be retargetted to a new process it is cancelled
|
||||
// with NS_BINDING_ABORTED so ignore that case
|
||||
if ((flags & docStop) == docStop && status != Cr.NS_BINDING_ABORTED) {
|
||||
aBrowser.removeProgressListener(this);
|
||||
waitForDocLoadComplete.listeners.delete(this);
|
||||
let chan = req.QueryInterface(Ci.nsIChannel);
|
||||
info("Browser loaded " + chan.originalURI.spec);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference])
|
||||
};
|
||||
aBrowser.addProgressListener(listener);
|
||||
waitForDocLoadComplete.listeners.add(listener);
|
||||
info("Waiting for browser load");
|
||||
});
|
||||
}
|
||||
// Keep a set of progress listeners for waitForDocLoadComplete() to make sure
|
||||
// they're not GC'ed before we saw the page load.
|
||||
waitForDocLoadComplete.listeners = new Set();
|
||||
registerCleanupFunction(() => waitForDocLoadComplete.listeners.clear());
|
||||
@@ -51,7 +51,6 @@ support-files =
|
||||
file_fullscreen-backdrop.html
|
||||
file_fullscreen-denied-inner.html
|
||||
file_fullscreen-denied.html
|
||||
file_fullscreen-esc-context-menu.html
|
||||
file_fullscreen-esc-exit-inner.html
|
||||
file_fullscreen-esc-exit.html
|
||||
file_fullscreen-hidden.html
|
||||
|
||||
@@ -30,7 +30,6 @@ SimpleTest.requestFlakyTimeout("untriaged");
|
||||
var gTestWindows = [
|
||||
"file_fullscreen-multiple.html",
|
||||
"file_fullscreen-rollback.html",
|
||||
"file_fullscreen-esc-context-menu.html",
|
||||
"file_fullscreen-esc-exit.html",
|
||||
"file_fullscreen-denied.html",
|
||||
"file_fullscreen-api.html",
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "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)
|
||||
{
|
||||
RefPtr<AbortablePromise> p = new AbortablePromise(aGlobal, aAbortCallback);
|
||||
p->CreateWrapper(nullptr, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
AbortablePromise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return MozAbortablePromiseBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<AbortablePromise>
|
||||
AbortablePromise::Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||
AbortCallback& aAbortCallback, ErrorResult& aRv,
|
||||
JS::Handle<JSObject*> aDesiredProto)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global;
|
||||
global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<AbortablePromise> promise = new AbortablePromise(global);
|
||||
promise->CreateWrapper(aDesiredProto, 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::DispatchToMicroTask(runnable);
|
||||
}
|
||||
|
||||
void
|
||||
AbortablePromise::DoAbort()
|
||||
{
|
||||
if (mAbortCallback.HasWebIDLCallback()) {
|
||||
mAbortCallback.GetWebIDLCallback()->Call();
|
||||
return;
|
||||
}
|
||||
mAbortCallback.GetXPCOMCallback()->Call();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -1,67 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_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.
|
||||
explicit AbortablePromise(nsIGlobalObject* aGlobal);
|
||||
|
||||
virtual ~AbortablePromise();
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<AbortablePromise>
|
||||
Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||
AbortCallback& aAbortCallback, ErrorResult& aRv,
|
||||
JS::Handle<JSObject*> aDesiredProto);
|
||||
|
||||
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__
|
||||
+384
-40
@@ -50,6 +50,7 @@ Atomic<uintptr_t> gIDGenerator(0);
|
||||
|
||||
using namespace workers;
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
// This class processes the promise's callbacks with promise's result.
|
||||
class PromiseReactionJob final : public nsRunnable
|
||||
{
|
||||
@@ -317,7 +318,7 @@ struct MOZ_STACK_CLASS Promise::PromiseCapability
|
||||
// purposes it's simpler to store them as JS::Value.
|
||||
|
||||
// [[Promise]].
|
||||
JS::Rooted<JS::Value> mPromise;
|
||||
JS::Rooted<JSObject*> mPromise;
|
||||
// [[Resolve]]. Value in the context compartment.
|
||||
JS::Rooted<JS::Value> mResolve;
|
||||
// [[Reject]]. Value in the context compartment.
|
||||
@@ -340,7 +341,7 @@ Promise::PromiseCapability::RejectWithException(JSContext* aCx,
|
||||
// or at least the parts of it that happen if we have an abrupt completion.
|
||||
|
||||
MOZ_ASSERT(!aRv.Failed());
|
||||
MOZ_ASSERT(mNativePromise || !mPromise.isUndefined(),
|
||||
MOZ_ASSERT(mNativePromise || mPromise,
|
||||
"NewPromiseCapability didn't succeed");
|
||||
|
||||
JS::Rooted<JS::Value> exn(aCx);
|
||||
@@ -370,47 +371,62 @@ Promise::PromiseCapability::RejectWithException(JSContext* aCx,
|
||||
JS::Value
|
||||
Promise::PromiseCapability::PromiseValue() const
|
||||
{
|
||||
MOZ_ASSERT(mNativePromise || !mPromise.isUndefined(),
|
||||
MOZ_ASSERT(mNativePromise || mPromise,
|
||||
"NewPromiseCapability didn't succeed");
|
||||
|
||||
if (mNativePromise) {
|
||||
return JS::ObjectValue(*mNativePromise->GetWrapper());
|
||||
}
|
||||
|
||||
return mPromise;
|
||||
return JS::ObjectValue(*mPromise);
|
||||
}
|
||||
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
// Promise
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
||||
tmp->MaybeReportRejectedOnce();
|
||||
#else
|
||||
tmp->mResult = JS::UndefinedValue();
|
||||
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
#else // SPIDERMONKEY_PROMISE
|
||||
tmp->mPromiseObj = nullptr;
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks)
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAllocationStack)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRejectionStack)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFullfillmentStack)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
#else // SPIDERMONKEY_PROMISE
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPromiseObj);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Promise)
|
||||
if (tmp->IsBlack()) {
|
||||
JS::ExposeValueToActiveJS(tmp->mResult);
|
||||
@@ -434,18 +450,22 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Promise)
|
||||
return tmp->IsBlack();
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Promise)
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(Promise)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
Promise::Promise(nsIGlobalObject* aGlobal)
|
||||
: mGlobal(aGlobal)
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
, mResult(JS::UndefinedValue())
|
||||
, mAllocationStack(nullptr)
|
||||
, mRejectionStack(nullptr)
|
||||
@@ -459,22 +479,364 @@ Promise::Promise(nsIGlobalObject* aGlobal)
|
||||
, mIsLastInChain(true)
|
||||
, mWasNotifiedAsUncaught(false)
|
||||
, mID(0)
|
||||
#else // SPIDERMONKEY_PROMISE
|
||||
, mPromiseObj(nullptr)
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
{
|
||||
MOZ_ASSERT(mGlobal);
|
||||
|
||||
mozilla::HoldJSObjects(this);
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
mCreationTimestamp = TimeStamp::Now();
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
}
|
||||
|
||||
Promise::~Promise()
|
||||
{
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
||||
MaybeReportRejectedOnce();
|
||||
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
|
||||
bool
|
||||
Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
|
||||
JS::MutableHandle<JSObject*> aWrapper)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
binding_detail::AssertReflectorHasGivenProto(aCx, mPromiseObj, aGivenProto);
|
||||
#endif // DEBUG
|
||||
JS::ExposeObjectToActiveJS(mPromiseObj);
|
||||
aWrapper.set(mPromiseObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Promise>
|
||||
Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Promise> p = new Promise(aGlobal);
|
||||
p->CreateWrapper(nullptr, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Promise>
|
||||
Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
||||
{
|
||||
JSAutoCompartment ac(aCx, aGlobal->GetGlobalJSObject());
|
||||
JS::Rooted<JSObject*> p(aCx,
|
||||
JS::CallOriginalPromiseResolve(aCx, aValue));
|
||||
if (!p) {
|
||||
aRv.NoteJSContextException();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateFromExisting(aGlobal, p);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Promise>
|
||||
Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
||||
{
|
||||
JSAutoCompartment ac(aCx, aGlobal->GetGlobalJSObject());
|
||||
JS::Rooted<JSObject*> p(aCx,
|
||||
JS::CallOriginalPromiseReject(aCx, aValue));
|
||||
if (!p) {
|
||||
aRv.NoteJSContextException();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateFromExisting(aGlobal, p);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Promise>
|
||||
Promise::All(const GlobalObject& aGlobal,
|
||||
const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global;
|
||||
global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSContext* cx = aGlobal.Context();
|
||||
|
||||
JS::AutoObjectVector promises(cx);
|
||||
if (!promises.reserve(aPromiseList.Length())) {
|
||||
aRv.NoteJSContextException();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto& promise : aPromiseList) {
|
||||
JS::Rooted<JSObject*> promiseObj(cx, promise->PromiseObj());
|
||||
// Just in case, make sure these are all in the context compartment.
|
||||
if (!JS_WrapObject(cx, &promiseObj)) {
|
||||
aRv.NoteJSContextException();
|
||||
return nullptr;
|
||||
}
|
||||
promises.infallibleAppend(promiseObj);
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> result(cx, JS::GetWaitForAllPromise(cx, promises));
|
||||
if (!result) {
|
||||
aRv.NoteJSContextException();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateFromExisting(global, result);
|
||||
}
|
||||
|
||||
void
|
||||
Promise::Then(JSContext* aCx,
|
||||
// aCalleeGlobal may not be in the compartment of aCx, when called over
|
||||
// Xrays.
|
||||
JS::Handle<JSObject*> aCalleeGlobal,
|
||||
AnyCallback* aResolveCallback, AnyCallback* aRejectCallback,
|
||||
JS::MutableHandle<JS::Value> aRetval,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// Let's hope this does the right thing with Xrays... Ensure everything is
|
||||
// just in the caller compartment; that ought to do the trick. In theory we
|
||||
// should consider aCalleeGlobal, but in practice our only caller is
|
||||
// DOMRequest::Then, which is not working with a Promise subclass, so things
|
||||
// should be OK.
|
||||
JS::Rooted<JSObject*> promise(aCx, PromiseObj());
|
||||
if (!JS_WrapObject(aCx, &promise)) {
|
||||
aRv.NoteJSContextException();
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> resolveCallback(aCx);
|
||||
if (aResolveCallback) {
|
||||
resolveCallback = aResolveCallback->Callback();
|
||||
if (!JS_WrapObject(aCx, &resolveCallback)) {
|
||||
aRv.NoteJSContextException();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> rejectCallback(aCx);
|
||||
if (aRejectCallback) {
|
||||
rejectCallback = aRejectCallback->Callback();
|
||||
if (!JS_WrapObject(aCx, &rejectCallback)) {
|
||||
aRv.NoteJSContextException();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> retval(aCx);
|
||||
retval = JS::CallOriginalPromiseThen(aCx, promise, resolveCallback,
|
||||
rejectCallback);
|
||||
if (!retval) {
|
||||
aRv.NoteJSContextException();
|
||||
return;
|
||||
}
|
||||
|
||||
aRetval.setObject(*retval);
|
||||
}
|
||||
|
||||
// We need a dummy function to pass to JS::NewPromiseObject.
|
||||
static bool
|
||||
DoNothingPromiseExecutor(JSContext*, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv)
|
||||
{
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(mGlobal)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JSFunction* doNothingFunc =
|
||||
JS_NewFunction(cx, DoNothingPromiseExecutor, /* nargs = */ 2,
|
||||
/* flags = */ 0, nullptr);
|
||||
if (!doNothingFunc) {
|
||||
JS_ClearPendingException(cx);
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> doNothingObj(cx, JS_GetFunctionObject(doNothingFunc));
|
||||
mPromiseObj = JS::NewPromiseObject(cx, doNothingObj, aDesiredProto);
|
||||
if (!mPromiseObj) {
|
||||
JS_ClearPendingException(cx);
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Promise::MaybeResolve(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
JS::Rooted<JSObject*> p(aCx, PromiseObj());
|
||||
if (!JS::ResolvePromise(aCx, p, aValue)) {
|
||||
// Now what? There's nothing sane to do here.
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Promise::MaybeReject(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
JS::Rooted<JSObject*> p(aCx, PromiseObj());
|
||||
if (!JS::RejectPromise(aCx, p, aValue)) {
|
||||
// Now what? There's nothing sane to do here.
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
#define SLOT_NATIVEHANDLER 0
|
||||
#define SLOT_NATIVEHANDLER_TASK 1
|
||||
|
||||
enum class NativeHandlerTask : int32_t {
|
||||
Resolve,
|
||||
Reject
|
||||
};
|
||||
|
||||
static bool
|
||||
NativeHandlerCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
|
||||
|
||||
JS::Rooted<JS::Value> v(aCx,
|
||||
js::GetFunctionNativeReserved(&args.callee(),
|
||||
SLOT_NATIVEHANDLER));
|
||||
MOZ_ASSERT(v.isObject());
|
||||
|
||||
PromiseNativeHandler* handler;
|
||||
if (NS_FAILED(UNWRAP_OBJECT(PromiseNativeHandler, &v.toObject(),
|
||||
handler))) {
|
||||
return Throw(aCx, NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
v = js::GetFunctionNativeReserved(&args.callee(), SLOT_NATIVEHANDLER_TASK);
|
||||
NativeHandlerTask task = static_cast<NativeHandlerTask>(v.toInt32());
|
||||
|
||||
if (task == NativeHandlerTask::Resolve) {
|
||||
handler->ResolvedCallback(aCx, args.get(0));
|
||||
} else {
|
||||
MOZ_ASSERT(task == NativeHandlerTask::Reject);
|
||||
handler->RejectedCallback(aCx, args.get(0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
CreateNativeHandlerFunction(JSContext* aCx, JS::Handle<JSObject*> aHolder,
|
||||
NativeHandlerTask aTask)
|
||||
{
|
||||
JSFunction* func = js::NewFunctionWithReserved(aCx, NativeHandlerCallback,
|
||||
/* nargs = */ 1,
|
||||
/* flags = */ 0, nullptr);
|
||||
if (!func) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
|
||||
|
||||
JS::ExposeObjectToActiveJS(aHolder);
|
||||
js::SetFunctionNativeReserved(obj, SLOT_NATIVEHANDLER,
|
||||
JS::ObjectValue(*aHolder));
|
||||
js::SetFunctionNativeReserved(obj, SLOT_NATIVEHANDLER_TASK,
|
||||
JS::Int32Value(static_cast<int32_t>(aTask)));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void
|
||||
Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
|
||||
{
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
|
||||
// Our API doesn't allow us to return a useful error. Not like this should
|
||||
// happen anyway.
|
||||
return;
|
||||
}
|
||||
jsapi.TakeOwnershipOfErrorReporting();
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> handlerWrapper(cx);
|
||||
// Note: PromiseNativeHandler is NOT wrappercached. So we can't use
|
||||
// ToJSValue here, because it will try to do XPConnect wrapping on it, sadly.
|
||||
if (NS_WARN_IF(!aRunnable->WrapObject(cx, nullptr, &handlerWrapper))) {
|
||||
// Again, no way to report errors.
|
||||
jsapi.ClearException();
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> resolveFunc(cx);
|
||||
resolveFunc =
|
||||
CreateNativeHandlerFunction(cx, handlerWrapper, NativeHandlerTask::Resolve);
|
||||
if (NS_WARN_IF(!resolveFunc)) {
|
||||
jsapi.ClearException();
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> rejectFunc(cx);
|
||||
rejectFunc =
|
||||
CreateNativeHandlerFunction(cx, handlerWrapper, NativeHandlerTask::Reject);
|
||||
if (NS_WARN_IF(!rejectFunc)) {
|
||||
jsapi.ClearException();
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> promiseObj(cx, PromiseObj());
|
||||
if (NS_WARN_IF(!JS::AddPromiseReactions(cx, promiseObj, resolveFunc,
|
||||
rejectFunc))) {
|
||||
jsapi.ClearException();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Promise::HandleException(JSContext* aCx)
|
||||
{
|
||||
JS::Rooted<JS::Value> exn(aCx);
|
||||
if (JS_GetPendingException(aCx, &exn)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
// This is only called from MaybeSomething, so it's OK to MaybeReject here,
|
||||
// unlike in the version that's used when !SPIDERMONKEY_PROMISE.
|
||||
MaybeReject(aCx, exn);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Promise>
|
||||
Promise::CreateFromExisting(nsIGlobalObject* aGlobal,
|
||||
JS::Handle<JSObject*> aPromiseObj)
|
||||
{
|
||||
MOZ_ASSERT(js::GetObjectCompartment(aGlobal->GetGlobalJSObject()) ==
|
||||
js::GetObjectCompartment(aPromiseObj));
|
||||
RefPtr<Promise> p = new Promise(aGlobal);
|
||||
p->mPromiseObj = aPromiseObj;
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
#else // SPIDERMONKEY_PROMISE
|
||||
|
||||
JSObject*
|
||||
Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
@@ -537,6 +899,8 @@ Promise::MaybeReject(JSContext* aCx,
|
||||
MaybeRejectInternal(aCx, aValue);
|
||||
}
|
||||
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
void
|
||||
Promise::MaybeReject(const RefPtr<MediaStreamError>& aArg) {
|
||||
MaybeSomething(aArg, &Promise::MaybeReject);
|
||||
@@ -577,6 +941,8 @@ Promise::PerformMicroTaskCheckpoint()
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
|
||||
/* static */ bool
|
||||
Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
@@ -959,7 +1325,7 @@ Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
aCapability.mReject = v;
|
||||
|
||||
// Step 10.
|
||||
aCapability.mPromise.setObject(*promiseObj);
|
||||
aCapability.mPromise = promiseObj;
|
||||
|
||||
// Step 11 doesn't need anything, since the PromiseCapability was passed in.
|
||||
}
|
||||
@@ -1274,11 +1640,11 @@ Promise::Then(JSContext* aCx, JS::Handle<JSObject*> aCalleeGlobal,
|
||||
RefPtr<AnyCallback> rejectFunc =
|
||||
new AnyCallback(aCx, rejectObj, GetIncumbentGlobal());
|
||||
|
||||
if (!capability.mPromise.isObject()) {
|
||||
if (!capability.mPromise) {
|
||||
aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>();
|
||||
return;
|
||||
}
|
||||
JS::Rooted<JSObject*> newPromiseObj(aCx, &capability.mPromise.toObject());
|
||||
JS::Rooted<JSObject*> newPromiseObj(aCx, capability.mPromise);
|
||||
// We want to store the reflector itself.
|
||||
newPromiseObj = js::CheckedUnwrap(newPromiseObj);
|
||||
if (!newPromiseObj) {
|
||||
@@ -1979,6 +2345,8 @@ Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
|
||||
AppendCallbacks(resolveCb, rejectCb);
|
||||
}
|
||||
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
JSObject*
|
||||
Promise::GlobalJSObject() const
|
||||
{
|
||||
@@ -1991,6 +2359,7 @@ Promise::Compartment() const
|
||||
return js::GetObjectCompartment(GlobalJSObject());
|
||||
}
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
void
|
||||
Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
|
||||
PromiseCallback* aRejectCallback)
|
||||
@@ -2027,37 +2396,7 @@ Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
|
||||
TriggerPromiseReactions();
|
||||
}
|
||||
}
|
||||
|
||||
class WrappedWorkerRunnable final : public WorkerSameThreadRunnable
|
||||
{
|
||||
public:
|
||||
WrappedWorkerRunnable(workers::WorkerPrivate* aWorkerPrivate, nsIRunnable* aRunnable)
|
||||
: WorkerSameThreadRunnable(aWorkerPrivate)
|
||||
, mRunnable(aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
MOZ_COUNT_CTOR(WrappedWorkerRunnable);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
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
|
||||
};
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
/* static */ void
|
||||
Promise::DispatchToMicroTask(nsIRunnable* aRunnable)
|
||||
@@ -2071,6 +2410,7 @@ Promise::DispatchToMicroTask(nsIRunnable* aRunnable)
|
||||
microtaskQueue.push(aRunnable);
|
||||
}
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
||||
void
|
||||
Promise::MaybeReportRejected()
|
||||
@@ -2363,6 +2703,8 @@ Promise::GetDependentPromises(nsTArray<RefPtr<Promise>>& aPromises)
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
// A WorkerRunnable to resolve/reject the Promise on the worker thread.
|
||||
// Calling thread MUST hold PromiseWorkerProxy's mutex before creating this.
|
||||
class PromiseWorkerProxyRunnable : public workers::WorkerRunnable
|
||||
@@ -2564,14 +2906,14 @@ void
|
||||
PromiseWorkerProxy::ResolvedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
RunCallback(aCx, aValue, &Promise::ResolveInternal);
|
||||
RunCallback(aCx, aValue, &Promise::MaybeResolve);
|
||||
}
|
||||
|
||||
void
|
||||
PromiseWorkerProxy::RejectedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
RunCallback(aCx, aValue, &Promise::RejectInternal);
|
||||
RunCallback(aCx, aValue, &Promise::MaybeReject);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -2647,6 +2989,7 @@ void Promise::MaybeRejectBrokenly(const nsAString& aArg) {
|
||||
MaybeSomething(aArg, &Promise::MaybeReject);
|
||||
}
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
uint64_t
|
||||
Promise::GetID() {
|
||||
if (mID != 0) {
|
||||
@@ -2654,6 +2997,7 @@ Promise::GetID() {
|
||||
}
|
||||
return mID = ++gIDGenerator;
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
+92
-7
@@ -71,7 +71,12 @@ public:
|
||||
{ 0x8a, 0xf9, 0x31, 0x5e, 0x8f, 0xce, 0x75, 0x65 } }
|
||||
|
||||
class Promise : public nsISupports,
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
// Only wrappercached when we're not using SpiderMonkey
|
||||
// promises, because those don't have a useful object moved
|
||||
// hook, which wrappercache needs.
|
||||
public nsWrapperCache,
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
public SupportsWeakPtr<Promise>
|
||||
{
|
||||
friend class NativePromiseCallback;
|
||||
@@ -92,17 +97,27 @@ class Promise : public nsISupports,
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROMISE_IID)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
// We're not skippable, since we're not owned from JS to start with.
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
|
||||
#else // SPIDERMONKEY_PROMISE
|
||||
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(Promise)
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Promise)
|
||||
|
||||
// Promise creation tries to create a JS reflector for the Promise, so is
|
||||
// fallible. Furthermore, we don't want to do JS-wrapping on a 0-refcount
|
||||
// object, so we addref before doing that and return the addrefed pointer
|
||||
// here.
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
static already_AddRefed<Promise>
|
||||
Create(nsIGlobalObject* aGlobal, ErrorResult& aRv);
|
||||
#else
|
||||
static already_AddRefed<Promise>
|
||||
Create(nsIGlobalObject* aGlobal, ErrorResult& aRv,
|
||||
// Passing null for aDesiredProto will use Promise.prototype.
|
||||
JS::Handle<JSObject*> aDesiredProto = nullptr);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
typedef void (Promise::*MaybeFunc)(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue);
|
||||
@@ -156,6 +171,52 @@ public:
|
||||
return mGlobal;
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
bool
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
|
||||
JS::MutableHandle<JSObject*> aWrapper);
|
||||
|
||||
// Do the equivalent of Promise.resolve in the current compartment of aCx.
|
||||
// Errorrs are reported on the ErrorResult; if aRv comes back !Failed(), this
|
||||
// function MUST return a non-null value.
|
||||
static already_AddRefed<Promise>
|
||||
Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv);
|
||||
|
||||
// Do the equivalent of Promise.reject in the current compartment of aCx.
|
||||
// Errorrs are reported on the ErrorResult; if aRv comes back !Failed(), this
|
||||
// function MUST return a non-null value.
|
||||
static already_AddRefed<Promise>
|
||||
Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Promise>
|
||||
All(const GlobalObject& aGlobal,
|
||||
const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
Then(JSContext* aCx,
|
||||
// aCalleeGlobal may not be in the compartment of aCx, when called over
|
||||
// Xrays.
|
||||
JS::Handle<JSObject*> aCalleeGlobal,
|
||||
AnyCallback* aResolveCallback, AnyCallback* aRejectCallback,
|
||||
JS::MutableHandle<JS::Value> aRetval,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* PromiseObj() const
|
||||
{
|
||||
if (mPromiseObj) {
|
||||
JS::ExposeObjectToActiveJS(mPromiseObj);
|
||||
}
|
||||
return mPromiseObj;
|
||||
}
|
||||
|
||||
#else // SPIDERMONKEY_PROMISE
|
||||
JSObject* PromiseObj()
|
||||
{
|
||||
return GetWrapper();
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
@@ -212,6 +273,7 @@ public:
|
||||
|
||||
static bool
|
||||
PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
void AppendNativeHandler(PromiseNativeHandler* aRunnable);
|
||||
|
||||
@@ -219,23 +281,36 @@ public:
|
||||
|
||||
JSCompartment* Compartment() const;
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
// Return a unique-to-the-process identifier for this Promise.
|
||||
uint64_t GetID();
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
// Queue an async microtask to current main or worker thread.
|
||||
static void
|
||||
DispatchToMicroTask(nsIRunnable* aRunnable);
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
enum JSCallbackSlots {
|
||||
SLOT_PROMISE = 0,
|
||||
SLOT_DATA
|
||||
};
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
// Create a dom::Promise from a given SpiderMonkey Promise object.
|
||||
// aPromiseObj MUST be in the compartment of aGlobal's global JS object.
|
||||
static already_AddRefed<Promise>
|
||||
CreateFromExisting(nsIGlobalObject* aGlobal,
|
||||
JS::Handle<JSObject*> aPromiseObj);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
protected:
|
||||
struct PromiseCapability;
|
||||
|
||||
// Do NOT call this unless you're Promise::Create. I wish we could enforce
|
||||
// that from inside this class too, somehow.
|
||||
// Do NOT call this unless you're Promise::Create or
|
||||
// Promise::CreateFromExisting. I wish we could enforce that from inside this
|
||||
// class too, somehow.
|
||||
explicit Promise(nsIGlobalObject* aGlobal);
|
||||
|
||||
virtual ~Promise();
|
||||
@@ -244,6 +319,7 @@ protected:
|
||||
// use the default prototype for the sort of Promise we have.
|
||||
void CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv);
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
// Create the JS resolving functions of resolve() and reject(). And provide
|
||||
// references to the two functions by calling PromiseInit passed from Promise
|
||||
// constructor.
|
||||
@@ -282,16 +358,18 @@ protected:
|
||||
{
|
||||
return mWasNotifiedAsUncaught;
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
private:
|
||||
friend class PromiseDebugging;
|
||||
|
||||
enum PromiseState {
|
||||
Pending,
|
||||
Resolved,
|
||||
Rejected
|
||||
};
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
friend class PromiseDebugging;
|
||||
|
||||
void SetState(PromiseState aState)
|
||||
{
|
||||
MOZ_ASSERT(mState == Pending);
|
||||
@@ -338,11 +416,12 @@ private:
|
||||
JS::Handle<JS::Value> aValue);
|
||||
void RejectInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
template <typename T>
|
||||
void MaybeSomething(T& aArgument, MaybeFunc aFunc) {
|
||||
ThreadsafeAutoJSContext cx;
|
||||
JSObject* wrapper = GetWrapper();
|
||||
JSObject* wrapper = PromiseObj();
|
||||
MOZ_ASSERT(wrapper); // We preserved it!
|
||||
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
@@ -355,6 +434,7 @@ private:
|
||||
(this->*aFunc)(cx, val);
|
||||
}
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
// Static methods for the PromiseInit functions.
|
||||
static bool
|
||||
JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp);
|
||||
@@ -373,8 +453,6 @@ private:
|
||||
static JSObject*
|
||||
CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask);
|
||||
|
||||
void HandleException(JSContext* aCx);
|
||||
|
||||
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
||||
void RemoveFeature();
|
||||
#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
||||
@@ -382,9 +460,13 @@ private:
|
||||
// Capture the current stack and store it in aTarget. If false is
|
||||
// returned, an exception is presumably pending on aCx.
|
||||
bool CaptureStack(JSContext* aCx, JS::Heap<JSObject*>& aTarget);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
void HandleException(JSContext* aCx);
|
||||
|
||||
RefPtr<nsIGlobalObject> mGlobal;
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
nsTArray<RefPtr<PromiseCallback> > mResolveCallbacks;
|
||||
nsTArray<RefPtr<PromiseCallback> > mRejectCallbacks;
|
||||
|
||||
@@ -435,6 +517,9 @@ private:
|
||||
// Once `GetID()` has been called, a unique-to-the-process identifier for this
|
||||
// promise. Until then, `0`.
|
||||
uint64_t mID;
|
||||
#else // SPIDERMONKEY_PROMISE
|
||||
JS::Heap<JSObject*> mPromiseObj;
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(Promise, NS_PROMISE_IID)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseCallback)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseCallback)
|
||||
|
||||
@@ -566,5 +568,7 @@ PromiseCallback::Factory(Promise* aNextPromise, JS::Handle<JSObject*> aGlobal,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
// This is the base class for any PromiseCallback.
|
||||
// It's a logical step in the promise chain of callbacks.
|
||||
class PromiseCallback : public nsISupports
|
||||
@@ -194,6 +195,8 @@ private:
|
||||
Promise::PromiseState mState;
|
||||
};
|
||||
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ private:
|
||||
/* static */ MOZ_THREAD_LOCAL(bool)
|
||||
FlushRejections::sDispatched;
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
static Promise*
|
||||
UnwrapPromise(JS::Handle<JSObject*> aPromise, ErrorResult& aRv)
|
||||
{
|
||||
@@ -103,6 +104,8 @@ PromiseDebugging::GetState(GlobalObject&, JS::Handle<JSObject*> aPromise,
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
/*static */ nsString
|
||||
PromiseDebugging::sIDPrefix;
|
||||
|
||||
@@ -130,10 +133,15 @@ PromiseDebugging::Shutdown()
|
||||
/* static */ void
|
||||
PromiseDebugging::FlushUncaughtRejections()
|
||||
{
|
||||
// XXXbz figure out the plan
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
FlushRejections::FlushSync();
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
}
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
|
||||
/* static */ void
|
||||
PromiseDebugging::GetAllocationStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
|
||||
JS::MutableHandle<JSObject*> aStack,
|
||||
@@ -210,6 +218,8 @@ PromiseDebugging::GetTimeToSettle(GlobalObject&, JS::Handle<JSObject*> aPromise,
|
||||
promise->mCreationTimestamp).ToMilliseconds();
|
||||
}
|
||||
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
/* static */ void
|
||||
PromiseDebugging::AddUncaughtRejectionObserver(GlobalObject&,
|
||||
UncaughtRejectionObserver& aObserver)
|
||||
@@ -235,6 +245,8 @@ PromiseDebugging::RemoveUncaughtRejectionObserver(GlobalObject&,
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
|
||||
/* static */ void
|
||||
PromiseDebugging::AddUncaughtRejection(Promise& aPromise)
|
||||
{
|
||||
@@ -263,10 +275,13 @@ PromiseDebugging::GetPromiseID(GlobalObject&,
|
||||
aID = sIDPrefix;
|
||||
aID.AppendInt(promiseID);
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
/* static */ void
|
||||
PromiseDebugging::FlushUncaughtRejectionsInternal()
|
||||
{
|
||||
// XXXbz talk to till about replacement for this stuff.
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
CycleCollectedJSRuntime* storage = CycleCollectedJSRuntime::Get();
|
||||
|
||||
// The Promise that have been left uncaught (rejected and last in
|
||||
@@ -327,6 +342,7 @@ PromiseDebugging::FlushUncaughtRejectionsInternal()
|
||||
obs->OnConsumed(*promise, err); // Ignore errors
|
||||
}
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
static void GetState(GlobalObject&, JS::Handle<JSObject*> aPromise,
|
||||
PromiseDebuggingStateHolder& aState,
|
||||
ErrorResult& aRv);
|
||||
@@ -58,20 +59,25 @@ public:
|
||||
|
||||
static void GetPromiseID(GlobalObject&, JS::Handle<JSObject*>, nsString&,
|
||||
ErrorResult&);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
// Mechanism for watching uncaught instances of Promise.
|
||||
// XXXbz figure out the plan
|
||||
static void AddUncaughtRejectionObserver(GlobalObject&,
|
||||
UncaughtRejectionObserver& aObserver);
|
||||
static bool RemoveUncaughtRejectionObserver(GlobalObject&,
|
||||
UncaughtRejectionObserver& aObserver);
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
// Mark a Promise as having been left uncaught at script completion.
|
||||
static void AddUncaughtRejection(Promise&);
|
||||
// Mark a Promise previously added with `AddUncaughtRejection` as
|
||||
// eventually consumed.
|
||||
static void AddConsumedRejection(Promise&);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
// Propagate the informations from AddUncaughtRejection
|
||||
// and AddConsumedRejection to observers.
|
||||
// XXXbz figure out the plan.
|
||||
static void FlushUncaughtRejections();
|
||||
|
||||
protected:
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_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
|
||||
@@ -0,0 +1,26 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/PromiseBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
bool
|
||||
PromiseNativeHandler::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto,
|
||||
JS::MutableHandle<JSObject*> aWrapper)
|
||||
{
|
||||
return PromiseNativeHandlerBinding::Wrap(aCx, this, aGivenProto, aWrapper);
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
@@ -30,6 +30,12 @@ public:
|
||||
|
||||
virtual void
|
||||
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) = 0;
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
|
||||
JS::MutableHandle<JSObject*> aWrapper);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -5,19 +5,17 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'AbortablePromise.h',
|
||||
'Promise.h',
|
||||
'PromiseDebugging.h',
|
||||
'PromiseNativeAbortCallback.h',
|
||||
'PromiseNativeHandler.h',
|
||||
'PromiseWorkerProxy.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AbortablePromise.cpp',
|
||||
'Promise.cpp',
|
||||
'PromiseCallback.cpp',
|
||||
'PromiseDebugging.cpp'
|
||||
'PromiseDebugging.cpp',
|
||||
'PromiseNativeHandler.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
# to go in here, not chrome.ini, apparently.
|
||||
support-files = file_promise_xrays.html
|
||||
|
||||
[test_abortable_promise.html]
|
||||
[test_bug883683.html]
|
||||
[test_promise.html]
|
||||
[test_promise_utils.html]
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
<!--
|
||||
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();
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
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,8 +118,6 @@ var legacyMozPrefixedInterfaces =
|
||||
// IMPORTANT: Do not change the list below without review from a DOM peer!
|
||||
var interfaceNamesInGlobalScope =
|
||||
[
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "MozAbortablePromise", disabled: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "AlarmsManager", b2g: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/* -*- 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();
|
||||
};
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
interface DummyInterface {
|
||||
void lifecycleCallbacks(optional LifecycleCallbacks arg);
|
||||
void promiseJobCallback(PromiseJobCallback arg);
|
||||
};
|
||||
|
||||
interface DummyInterfaceWorkers {
|
||||
|
||||
@@ -12,10 +12,15 @@
|
||||
// function"; for now, we just use "object".
|
||||
callback PromiseInit = void (object resolve, object reject);
|
||||
|
||||
callback PromiseJobCallback = void();
|
||||
|
||||
[TreatNonCallableAsNull]
|
||||
callback AnyCallback = any (any value);
|
||||
|
||||
// REMOVE THE RELEVANT ENTRY FROM test_interfaces.html WHEN THIS IS IMPLEMENTED IN JS.
|
||||
// When using SpiderMonkey promises, we don't want to define all this stuff;
|
||||
// just define a tiny interface to make codegen of Promise arguments and return
|
||||
// values work.
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
[Constructor(PromiseInit init),
|
||||
Exposed=(Window,Worker,System)]
|
||||
// Need to escape "Promise" so it's treated as an identifier.
|
||||
@@ -56,3 +61,17 @@ interface _Promise {
|
||||
[NewObject, Throws]
|
||||
static any race(optional any iterable);
|
||||
};
|
||||
#else // SPIDERMONKEY_PROMISE
|
||||
[NoInterfaceObject,
|
||||
Exposed=(Window,Worker,System)]
|
||||
// Need to escape "Promise" so it's treated as an identifier.
|
||||
interface _Promise {
|
||||
};
|
||||
|
||||
// Hack to allow us to have JS owning and properly tracing/CCing/etc a
|
||||
// PromiseNativeHandler.
|
||||
[NoInterfaceObject,
|
||||
Exposed=(Window,Worker,System)]
|
||||
interface PromiseNativeHandler {
|
||||
};
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
@@ -52,6 +52,7 @@ callback interface UncaughtRejectionObserver {
|
||||
|
||||
[ChromeOnly, Exposed=(Window,System)]
|
||||
interface PromiseDebugging {
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
/**
|
||||
* The various functions on this interface all expect to take promises but
|
||||
* don't want the WebIDL behavior of assimilating random passed-in objects
|
||||
@@ -130,6 +131,8 @@ interface PromiseDebugging {
|
||||
[Throws]
|
||||
static DOMHighResTimeStamp getTimeToSettle(object p);
|
||||
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
/**
|
||||
* Watching uncaught rejections on the current thread.
|
||||
*
|
||||
|
||||
@@ -11,12 +11,13 @@ GENERATED_WEBIDL_FILES = [
|
||||
PREPROCESSED_WEBIDL_FILES = [
|
||||
'HTMLMediaElement.webidl',
|
||||
'Navigator.webidl',
|
||||
'Promise.webidl',
|
||||
'PromiseDebugging.webidl',
|
||||
'ServiceWorkerRegistration.webidl',
|
||||
'Window.webidl',
|
||||
]
|
||||
|
||||
WEBIDL_FILES = [
|
||||
'AbortablePromise.webidl',
|
||||
'AbstractWorker.webidl',
|
||||
'ActivityRequestHandler.webidl',
|
||||
'AlarmsManager.webidl',
|
||||
@@ -389,8 +390,6 @@ WEBIDL_FILES = [
|
||||
'PresentationSession.webidl',
|
||||
'ProcessingInstruction.webidl',
|
||||
'ProfileTimelineMarker.webidl',
|
||||
'Promise.webidl',
|
||||
'PromiseDebugging.webidl',
|
||||
'PropertyIndexedKeyframes.webidl',
|
||||
'RadioNodeList.webidl',
|
||||
'Range.webidl',
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ class ZoneHeapThreshold
|
||||
double gcHeapGrowthFactor_;
|
||||
|
||||
// GC trigger threshold for allocations on the GC heap.
|
||||
size_t gcTriggerBytes_;
|
||||
mozilla::Atomic<size_t, mozilla::Relaxed> gcTriggerBytes_;
|
||||
|
||||
public:
|
||||
ZoneHeapThreshold()
|
||||
|
||||
@@ -8661,8 +8661,7 @@ AssertIsPromise(JSContext* cx, HandleObject promise)
|
||||
{
|
||||
MOZ_ASSERT(promise);
|
||||
assertSameCompartment(cx, promise);
|
||||
MOZ_ASSERT(strcmp(promise->getClass()->name, "Promise") == 0 ||
|
||||
strcmp(promise->getClass()->name, "MozAbortablePromise") == 0);
|
||||
MOZ_ASSERT(strcmp(promise->getClass()->name, "Promise") == 0);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
||||
@@ -293,9 +293,8 @@ SandboxFetch(JSContext* cx, JS::HandleObject scope, const CallArgs& args)
|
||||
if (rv.MaybeSetPendingException(cx)) {
|
||||
return false;
|
||||
}
|
||||
if (!GetOrCreateDOMReflector(cx, response, args.rval())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*response->PromiseObj());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1161,10 +1160,12 @@ xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prin
|
||||
if (!options.globalProperties.Define(cx, sandbox))
|
||||
return NS_ERROR_XPC_UNEXPECTED;
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
// Promise is supposed to be part of ES, and therefore should appear on
|
||||
// every global.
|
||||
if (!dom::PromiseBinding::GetConstructorObject(cx, sandbox))
|
||||
return NS_ERROR_XPC_UNEXPECTED;
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
}
|
||||
|
||||
// We handle the case where the context isn't in a compartment for the
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "nsIAtom.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "WrapperFactory.h"
|
||||
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
@@ -25,6 +26,7 @@
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/PrimitiveConversions.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
|
||||
using namespace xpc;
|
||||
@@ -792,6 +794,21 @@ XPCConvert::NativeInterface2JSObject(MutableHandleValue d,
|
||||
return CreateHolderIfNeeded(flat, d, dest);
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
if (iid->Equals(NS_GET_IID(nsISupports))) {
|
||||
// Check for a Promise being returned via nsISupports. In that
|
||||
// situation, we want to dig out its underlying JS object and return
|
||||
// that.
|
||||
RefPtr<Promise> promise = do_QueryObject(aHelper.Object());
|
||||
if (promise) {
|
||||
flat = promise->PromiseObj();
|
||||
if (!JS_WrapObject(cx, &flat))
|
||||
return false;
|
||||
return CreateHolderIfNeeded(flat, d, dest);
|
||||
}
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
// Don't double wrap CPOWs. This is a temporary measure for compatibility
|
||||
// with objects that don't provide necessary QIs (such as objects under
|
||||
// the new DOM bindings). We expect the other side of the CPOW to have
|
||||
@@ -925,6 +942,19 @@ XPCConvert::JSObject2NativeInterface(void** dest, HandleObject src,
|
||||
if (GetISupportsFromJSObject(inner ? inner : src, &iface)) {
|
||||
return iface && NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
// Deal with Promises being passed as nsISupports. In that situation we
|
||||
// want to create a dom::Promise and use that.
|
||||
if (iid->Equals(NS_GET_IID(nsISupports))) {
|
||||
RootedObject innerObj(cx, inner);
|
||||
if (IsPromiseObject(innerObj)) {
|
||||
nsIGlobalObject* glob = NativeGlobal(innerObj);
|
||||
RefPtr<Promise> p = Promise::CreateFromExisting(glob, innerObj);
|
||||
return p && NS_SUCCEEDED(p->QueryInterface(*iid, dest));
|
||||
}
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
}
|
||||
|
||||
RefPtr<nsXPCWrappedJS> wrapper;
|
||||
|
||||
@@ -113,9 +113,6 @@ pref("browser.cache.compression_level", 3);
|
||||
pref("browser.download.saveZoneInformation", 2);
|
||||
#endif
|
||||
|
||||
// Whether or not MozAbortablePromise is enabled.
|
||||
pref("dom.abortablepromise.enabled", false);
|
||||
|
||||
// Whether or not testing features are enabled.
|
||||
pref("dom.quotaManager.testing", false);
|
||||
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
[document.title-09.html]
|
||||
type: testharness
|
||||
[No title element in SVG document]
|
||||
expected: FAIL
|
||||
|
||||
[Title element in SVG document]
|
||||
expected: FAIL
|
||||
|
||||
[Title element not child of SVG root]
|
||||
expected: FAIL
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<link rel="help" href="https://html.spec.whatwg.org/multipage/#document.title">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
||||
var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
function newSVGDocument() {
|
||||
return document.implementation.createDocument(SVG_NAMESPACE, "svg", null);
|
||||
}
|
||||
|
||||
function assertIsSVGTitle(element, expectedText) {
|
||||
assert_equals(element.namespaceURI, SVG_NAMESPACE);
|
||||
assert_equals(element.localName, "title");
|
||||
assert_equals(element.textContent, expectedText);
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var doc = newSVGDocument();
|
||||
assert_equals(doc.title, "");
|
||||
var child = doc.createElementNS(SVG_NAMESPACE, "x-child");
|
||||
doc.documentElement.appendChild(child);
|
||||
doc.title = "foo";
|
||||
assertIsSVGTitle(doc.documentElement.firstChild, "foo");
|
||||
assert_equals(doc.title, "foo");
|
||||
}, "No title element in SVG document");
|
||||
|
||||
test(function() {
|
||||
var doc = newSVGDocument();
|
||||
var title = doc.createElementNS(SVG_NAMESPACE, "title");
|
||||
title.textContent = "foo";
|
||||
doc.documentElement.appendChild(title)
|
||||
assert_equals(doc.title, "foo");
|
||||
doc.title += "bar";
|
||||
assert_equals(title.textContent, "foobar");
|
||||
assert_equals(title.childNodes.length, 1);
|
||||
assert_true(title.childNodes[0] instanceof Text);
|
||||
assert_equals(doc.title, "foobar");
|
||||
doc.title = "";
|
||||
assert_equals(title.textContent, "");
|
||||
assert_equals(doc.title, "");
|
||||
assert_equals(title.childNodes.length, 0);
|
||||
}, "Title element in SVG document");
|
||||
|
||||
test(function() {
|
||||
var doc = newSVGDocument();
|
||||
var title = doc.createElementNS(SVG_NAMESPACE, "title");
|
||||
title.textContent = "foo";
|
||||
var child = doc.createElementNS(SVG_NAMESPACE, "x-child");
|
||||
child.appendChild(title);
|
||||
doc.documentElement.appendChild(child);
|
||||
assert_equals(doc.title, "");
|
||||
|
||||
// Now test that on setting, we create a new element and don't change the
|
||||
// existing one
|
||||
doc.title = "bar";
|
||||
assert_equals(title.textContent, "foo");
|
||||
assertIsSVGTitle(doc.documentElement.firstChild, "bar");
|
||||
assert_equals(doc.title, "bar");
|
||||
}, "Title element not child of SVG root");
|
||||
|
||||
test(function() {
|
||||
var doc = newSVGDocument();
|
||||
var title = doc.createElementNS(HTML_NAMESPACE, "title");
|
||||
title.textContent = "foo";
|
||||
doc.documentElement.appendChild(title);
|
||||
assert_equals(doc.title, "");
|
||||
}, "Title element not in SVG namespace");
|
||||
|
||||
test(function() {
|
||||
// "SVG" != "svg"
|
||||
var doc = document.implementation.createDocument(SVG_NAMESPACE, "SVG", null);
|
||||
|
||||
// Per spec, this does nothing
|
||||
doc.title = "foo";
|
||||
assert_equals(doc.documentElement.childNodes.length, 0);
|
||||
assert_equals(doc.title, "");
|
||||
|
||||
// An SVG title is ignored by .title
|
||||
doc.documentElement.appendChild(doc.createElementNS(SVG_NAMESPACE, "title"));
|
||||
doc.documentElement.lastChild.textContent = "foo";
|
||||
assert_equals(doc.title, "");
|
||||
|
||||
// But an HTML title is respected
|
||||
doc.documentElement.appendChild(doc.createElementNS(HTML_NAMESPACE, "title"));
|
||||
doc.documentElement.lastChild.textContent = "bar";
|
||||
assert_equals(doc.title, "bar");
|
||||
|
||||
// Even if it's not a child of the root
|
||||
var div = doc.createElementNS(HTML_NAMESPACE, "div");
|
||||
div.appendChild(doc.documentElement.lastChild);
|
||||
doc.documentElement.appendChild(div);
|
||||
assert_equals(doc.title, "bar");
|
||||
}, 'Root element not named "svg"');
|
||||
</script>
|
||||
@@ -68,6 +68,7 @@
|
||||
#include "mozilla/dom/DOMJSClass.h"
|
||||
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseBinding.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "jsprf.h"
|
||||
#include "js/Debug.h"
|
||||
@@ -468,6 +469,10 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
|
||||
};
|
||||
SetDOMCallbacks(mJSRuntime, &DOMcallbacks);
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
JS::SetEnqueuePromiseJobCallback(mJSRuntime, EnqueuePromiseJobCallback, this);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
JS::dbg::SetDebuggerMallocSizeOf(mJSRuntime, moz_malloc_size_of);
|
||||
|
||||
nsCycleCollector_registerJSRuntime(this);
|
||||
@@ -864,6 +869,45 @@ CycleCollectedJSRuntime::ContextCallback(JSContext* aContext,
|
||||
return self->CustomContextCallback(aContext, aOperation);
|
||||
}
|
||||
|
||||
class PromiseJobRunnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
PromiseJobRunnable(JSContext* aCx, JS::HandleObject aCallback)
|
||||
: mCallback(new PromiseJobCallback(aCx, aCallback, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~PromiseJobRunnable()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
mCallback->Call();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<PromiseJobCallback> mCallback;
|
||||
};
|
||||
|
||||
/* static */
|
||||
bool
|
||||
CycleCollectedJSRuntime::EnqueuePromiseJobCallback(JSContext* aCx,
|
||||
JS::HandleObject aJob,
|
||||
void* aData)
|
||||
{
|
||||
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
|
||||
MOZ_ASSERT(JS_GetRuntime(aCx) == self->Runtime());
|
||||
MOZ_ASSERT(Get() == self);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aCx, aJob);
|
||||
self->GetPromiseMicroTaskQueue().push(runnable);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct JsGcTracer : public TraceCallbacks
|
||||
{
|
||||
virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
|
||||
|
||||
@@ -210,6 +210,9 @@ private:
|
||||
static void LargeAllocationFailureCallback(void* aData);
|
||||
static bool ContextCallback(JSContext* aCx, unsigned aOperation,
|
||||
void* aData);
|
||||
static bool EnqueuePromiseJobCallback(JSContext* aCx,
|
||||
JS::HandleObject aJob,
|
||||
void* aData);
|
||||
|
||||
virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
|
||||
void TraceNativeGrayRoots(JSTracer* aTracer);
|
||||
|
||||
Reference in New Issue
Block a user