From 3e34b8d21b72bdaa77d31b732f6eb750bb06c93c Mon Sep 17 00:00:00 2001 From: roytam1 Date: Wed, 8 Nov 2023 17:17:49 +0800 Subject: [PATCH] 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) --- dom/base/DOMRequest.cpp | 2 +- dom/base/WebSocket.cpp | 8 +- dom/base/nsDOMWindowUtils.cpp | 17 +- dom/base/nsDocument.cpp | 116 +++-- dom/base/nsDocument.h | 16 +- dom/bindings/BindingUtils.cpp | 28 ++ dom/bindings/BindingUtils.h | 16 +- dom/bindings/Bindings.conf | 8 +- dom/bindings/Codegen.py | 44 +- dom/bindings/Configuration.py | 7 + dom/bindings/ToJSValue.cpp | 13 + dom/bindings/ToJSValue.h | 10 + dom/html/test/browser.ini | 3 + .../browser_fullscreen-contextmenu-esc.js | 105 +++++ dom/html/test/dummy_page.html | 10 + .../file_fullscreen-esc-context-menu.html | 100 ----- dom/html/test/head.js | 54 +++ dom/html/test/mochitest.ini | 1 - dom/html/test/test_fullscreen-api.html | 1 - dom/promise/AbortablePromise.cpp | 118 ----- dom/promise/AbortablePromise.h | 67 --- dom/promise/Promise.cpp | 424 ++++++++++++++++-- dom/promise/Promise.h | 99 +++- dom/promise/PromiseCallback.cpp | 4 + dom/promise/PromiseCallback.h | 3 + dom/promise/PromiseDebugging.cpp | 16 + dom/promise/PromiseDebugging.h | 6 + dom/promise/PromiseNativeAbortCallback.h | 36 -- dom/promise/PromiseNativeHandler.cpp | 26 ++ dom/promise/PromiseNativeHandler.h | 6 + dom/promise/moz.build | 6 +- dom/promise/tests/mochitest.ini | 1 - dom/promise/tests/test_abortable_promise.html | 116 ----- .../mochitest/general/test_interfaces.html | 2 - dom/webidl/AbortablePromise.webidl | 19 - dom/webidl/DummyBinding.webidl | 1 + dom/webidl/Promise.webidl | 21 +- dom/webidl/PromiseDebugging.webidl | 3 + dom/webidl/moz.build | 5 +- js/src/gc/Zone.h | 2 +- js/src/vm/Debugger.cpp | 3 +- js/xpconnect/src/Sandbox.cpp | 7 +- js/xpconnect/src/XPCConvert.cpp | 30 ++ modules/libpref/init/all.js | 3 - .../document.title-09.html.ini | 11 - .../dom-tree-accessors/document.title-09.html | 97 ++++ xpcom/base/CycleCollectedJSRuntime.cpp | 44 ++ xpcom/base/CycleCollectedJSRuntime.h | 3 + 48 files changed, 1118 insertions(+), 620 deletions(-) create mode 100644 dom/html/test/browser_fullscreen-contextmenu-esc.js create mode 100644 dom/html/test/dummy_page.html delete mode 100644 dom/html/test/file_fullscreen-esc-context-menu.html create mode 100644 dom/html/test/head.js delete mode 100644 dom/promise/AbortablePromise.cpp delete mode 100644 dom/promise/AbortablePromise.h delete mode 100644 dom/promise/PromiseNativeAbortCallback.h create mode 100644 dom/promise/PromiseNativeHandler.cpp delete mode 100644 dom/promise/tests/test_abortable_promise.html delete mode 100644 dom/webidl/AbortablePromise.webidl delete mode 100644 testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-09.html.ini create mode 100644 testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html diff --git a/dom/base/DOMRequest.cpp b/dom/base/DOMRequest.cpp index dfc4732a53..8f23c059e1 100644 --- a/dom/base/DOMRequest.cpp +++ b/dom/base/DOMRequest.cpp @@ -232,7 +232,7 @@ DOMRequest::Then(JSContext* aCx, AnyCallback* aResolveCallback, } // Just use the global of the Promise itself as the callee global. - JS::Rooted global(aCx, mPromise->GetWrapper()); + JS::Rooted global(aCx, mPromise->PromiseObj()); global = js::GetGlobalForObjectCrossCompartment(global); mPromise->Then(aCx, global, aResolveCallback, aRejectCallback, aRetval, aRv); } diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index b548a90b45..3b5b4a4819 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -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)) { diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 83da1c102c..28a066616c 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2285,7 +2285,7 @@ nsDOMWindowUtils::GetSupportsHardwareH264Decoding(JS::MutableHandle a RefPtr promise = MP4Decoder::IsVideoAccelerated(mgr->GetCompositorBackendType(), parentObject); NS_ENSURE_STATE(promise); - aPromise.setObject(*promise->GetWrapper()); + aPromise.setObject(*promise->PromiseObj()); #else ErrorResult rv; RefPtr promise = Promise::Create(parentObject, rv); @@ -2293,7 +2293,7 @@ nsDOMWindowUtils::GetSupportsHardwareH264Decoding(JS::MutableHandle 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 transaction = GetLayerTransaction(); - if (transaction && transaction->IPCOpen()) { - transaction->SendSetTestSampleTime(driver->MostRecentRefresh()); + RefPtr transaction = GetLayerTransaction(); + if (transaction && transaction->IPCOpen()) { + transaction->SendSetTestSampleTime(driver->MostRecentRefresh()); + } } return NS_OK; diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index f8baf1c067..6b5f746ed6 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -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 // 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); diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 1ddf12757e..85b1caeb2f 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -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; diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 4b6eac00c6..ca7eba41f2 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -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 */ diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 128ef05800..0a04b1a377 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -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 diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 8e82ca7961..d55d5f0170 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -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', }, diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 91e446972b..0fb885253a 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -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") diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index ee1be46a50..22a3599360 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -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 diff --git a/dom/bindings/ToJSValue.cpp b/dom/bindings/ToJSValue.cpp index 8776f55e6d..fb2be1b879 100644 --- a/dom/bindings/ToJSValue.cpp +++ b/dom/bindings/ToJSValue.cpp @@ -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 diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h index f47fba3124..b5125208af 100644 --- a/dom/bindings/ToJSValue.h +++ b/dom/bindings/ToJSValue.h @@ -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 diff --git a/dom/html/test/browser.ini b/dom/html/test/browser.ini index 7755e4be58..3e2cbf83b2 100644 --- a/dom/html/test/browser.ini +++ b/dom/html/test/browser.ini @@ -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] diff --git a/dom/html/test/browser_fullscreen-contextmenu-esc.js b/dom/html/test/browser_fullscreen-contextmenu-esc.js new file mode 100644 index 0000000000..a4975145b0 --- /dev/null +++ b/dom/html/test/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"); +}); diff --git a/dom/html/test/dummy_page.html b/dom/html/test/dummy_page.html new file mode 100644 index 0000000000..fd238954c6 --- /dev/null +++ b/dom/html/test/dummy_page.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<title>Dummy test page + + + +

Dummy test page

+ + diff --git a/dom/html/test/file_fullscreen-esc-context-menu.html b/dom/html/test/file_fullscreen-esc-context-menu.html deleted file mode 100644 index c7c62bdc30..0000000000 --- a/dom/html/test/file_fullscreen-esc-context-menu.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - Text for bug 910532 - - - - - - - - - diff --git a/dom/html/test/head.js b/dom/html/test/head.js new file mode 100644 index 0000000000..7f5db315e0 --- /dev/null +++ b/dom/html/test/head.js @@ -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 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()); diff --git a/dom/html/test/mochitest.ini b/dom/html/test/mochitest.ini index 1a06aa84e1..e4a79ed736 100644 --- a/dom/html/test/mochitest.ini +++ b/dom/html/test/mochitest.ini @@ -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 diff --git a/dom/html/test/test_fullscreen-api.html b/dom/html/test/test_fullscreen-api.html index 79e62ecee5..3e5480ae91 100644 --- a/dom/html/test/test_fullscreen-api.html +++ b/dom/html/test/test_fullscreen-api.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", diff --git a/dom/promise/AbortablePromise.cpp b/dom/promise/AbortablePromise.cpp deleted file mode 100644 index 55d255bc3a..0000000000 --- a/dom/promise/AbortablePromise.cpp +++ /dev/null @@ -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::Create(nsIGlobalObject* aGlobal, - PromiseNativeAbortCallback& aAbortCallback, - ErrorResult& aRv) -{ - RefPtr p = new AbortablePromise(aGlobal, aAbortCallback); - p->CreateWrapper(nullptr, aRv); - if (aRv.Failed()) { - return nullptr; - } - return p.forget(); -} - -JSObject* -AbortablePromise::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - return MozAbortablePromiseBinding::Wrap(aCx, this, aGivenProto); -} - -/* static */ already_AddRefed -AbortablePromise::Constructor(const GlobalObject& aGlobal, PromiseInit& aInit, - AbortCallback& aAbortCallback, ErrorResult& aRv, - JS::Handle aDesiredProto) -{ - nsCOMPtr global; - global = do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - RefPtr 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 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 diff --git a/dom/promise/AbortablePromise.h b/dom/promise/AbortablePromise.h deleted file mode 100644 index c6577fb4cd..0000000000 --- a/dom/promise/AbortablePromise.h +++ /dev/null @@ -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 - 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 aGivenProto) override; - - static already_AddRefed - Constructor(const GlobalObject& aGlobal, PromiseInit& aInit, - AbortCallback& aAbortCallback, ErrorResult& aRv, - JS::Handle aDesiredProto); - - void Abort(); - -private: - void DoAbort(); - - // The callback functions to abort the promise. - CallbackObjectHolder mAbortCallback; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_AbortablePromise_h__ diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index 2298d99784..b6a8c66000 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -50,6 +50,7 @@ Atomic 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 mPromise; + JS::Rooted mPromise; // [[Resolve]]. Value in the context compartment. JS::Rooted 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 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 aGivenProto, + JS::MutableHandle aWrapper) +{ +#ifdef DEBUG + binding_detail::AssertReflectorHasGivenProto(aCx, mPromiseObj, aGivenProto); +#endif // DEBUG + JS::ExposeObjectToActiveJS(mPromiseObj); + aWrapper.set(mPromiseObj); + return true; +} + +// static +already_AddRefed +Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv) +{ + RefPtr p = new Promise(aGlobal); + p->CreateWrapper(nullptr, aRv); + if (aRv.Failed()) { + return nullptr; + } + return p.forget(); +} + +// static +already_AddRefed +Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx, + JS::Handle aValue, ErrorResult& aRv) +{ + JSAutoCompartment ac(aCx, aGlobal->GetGlobalJSObject()); + JS::Rooted p(aCx, + JS::CallOriginalPromiseResolve(aCx, aValue)); + if (!p) { + aRv.NoteJSContextException(); + return nullptr; + } + + return CreateFromExisting(aGlobal, p); +} + +// static +already_AddRefed +Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx, + JS::Handle aValue, ErrorResult& aRv) +{ + JSAutoCompartment ac(aCx, aGlobal->GetGlobalJSObject()); + JS::Rooted p(aCx, + JS::CallOriginalPromiseReject(aCx, aValue)); + if (!p) { + aRv.NoteJSContextException(); + return nullptr; + } + + return CreateFromExisting(aGlobal, p); +} + +// static +already_AddRefed +Promise::All(const GlobalObject& aGlobal, + const nsTArray>& aPromiseList, ErrorResult& aRv) +{ + nsCOMPtr 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 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 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 aCalleeGlobal, + AnyCallback* aResolveCallback, AnyCallback* aRejectCallback, + JS::MutableHandle 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 promise(aCx, PromiseObj()); + if (!JS_WrapObject(aCx, &promise)) { + aRv.NoteJSContextException(); + return; + } + + JS::Rooted resolveCallback(aCx); + if (aResolveCallback) { + resolveCallback = aResolveCallback->Callback(); + if (!JS_WrapObject(aCx, &resolveCallback)) { + aRv.NoteJSContextException(); + return; + } + } + + JS::Rooted rejectCallback(aCx); + if (aRejectCallback) { + rejectCallback = aRejectCallback->Callback(); + if (!JS_WrapObject(aCx, &rejectCallback)) { + aRv.NoteJSContextException(); + return; + } + } + + JS::Rooted 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 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 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 aValue) +{ + JS::Rooted 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 aValue) +{ + JS::Rooted 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 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(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 aHolder, + NativeHandlerTask aTask) +{ + JSFunction* func = js::NewFunctionWithReserved(aCx, NativeHandlerCallback, + /* nargs = */ 1, + /* flags = */ 0, nullptr); + if (!func) { + return nullptr; + } + + JS::Rooted 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(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 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 resolveFunc(cx); + resolveFunc = + CreateNativeHandlerFunction(cx, handlerWrapper, NativeHandlerTask::Resolve); + if (NS_WARN_IF(!resolveFunc)) { + jsapi.ClearException(); + return; + } + + JS::Rooted rejectFunc(cx); + rejectFunc = + CreateNativeHandlerFunction(cx, handlerWrapper, NativeHandlerTask::Reject); + if (NS_WARN_IF(!rejectFunc)) { + jsapi.ClearException(); + return; + } + + JS::Rooted promiseObj(cx, PromiseObj()); + if (NS_WARN_IF(!JS::AddPromiseReactions(cx, promiseObj, resolveFunc, + rejectFunc))) { + jsapi.ClearException(); + return; + } +} + +void +Promise::HandleException(JSContext* aCx) +{ + JS::Rooted exn(aCx); + if (JS_GetPendingException(aCx, &exn)) { + JS_ClearPendingException(aCx); + // This is only called from MaybeSomething, so it's OK to MaybeReject here, + // unlike in the version that's used when !SPIDERMONKEY_PROMISE. + MaybeReject(aCx, exn); + } +} + +// static +already_AddRefed +Promise::CreateFromExisting(nsIGlobalObject* aGlobal, + JS::Handle aPromiseObj) +{ + MOZ_ASSERT(js::GetObjectCompartment(aGlobal->GetGlobalJSObject()) == + js::GetObjectCompartment(aPromiseObj)); + RefPtr p = new Promise(aGlobal); + p->mPromiseObj = aPromiseObj; + return p.forget(); +} + +#else // SPIDERMONKEY_PROMISE + JSObject* Promise::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { @@ -537,6 +899,8 @@ Promise::MaybeReject(JSContext* aCx, MaybeRejectInternal(aCx, aValue); } +#endif // SPIDERMONKEY_PROMISE + void Promise::MaybeReject(const RefPtr& 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 aCalleeGlobal, RefPtr rejectFunc = new AnyCallback(aCx, rejectObj, GetIncumbentGlobal()); - if (!capability.mPromise.isObject()) { + if (!capability.mPromise) { aRv.ThrowTypeError(); return; } - JS::Rooted newPromiseObj(aCx, &capability.mPromise.toObject()); + JS::Rooted 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 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>& 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 aValue) { - RunCallback(aCx, aValue, &Promise::ResolveInternal); + RunCallback(aCx, aValue, &Promise::MaybeResolve); } void PromiseWorkerProxy::RejectedCallback(JSContext* aCx, JS::Handle 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 diff --git a/dom/promise/Promise.h b/dom/promise/Promise.h index 0090674355..00b6b2494e 100644 --- a/dom/promise/Promise.h +++ b/dom/promise/Promise.h @@ -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 { 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 + Create(nsIGlobalObject* aGlobal, ErrorResult& aRv); +#else static already_AddRefed Create(nsIGlobalObject* aGlobal, ErrorResult& aRv, // Passing null for aDesiredProto will use Promise.prototype. JS::Handle aDesiredProto = nullptr); +#endif // SPIDERMONKEY_PROMISE typedef void (Promise::*MaybeFunc)(JSContext* aCx, JS::Handle aValue); @@ -156,6 +171,52 @@ public: return mGlobal; } +#ifdef SPIDERMONKEY_PROMISE + bool + WrapObject(JSContext* aCx, JS::Handle aGivenProto, + JS::MutableHandle 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 + Resolve(nsIGlobalObject* aGlobal, JSContext* aCx, + JS::Handle 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 + Reject(nsIGlobalObject* aGlobal, JSContext* aCx, + JS::Handle aValue, ErrorResult& aRv); + + static already_AddRefed + All(const GlobalObject& aGlobal, + const nsTArray>& aPromiseList, ErrorResult& aRv); + + void + Then(JSContext* aCx, + // aCalleeGlobal may not be in the compartment of aCx, when called over + // Xrays. + JS::Handle aCalleeGlobal, + AnyCallback* aResolveCallback, AnyCallback* aRejectCallback, + JS::MutableHandle aRetval, + ErrorResult& aRv); + + JSObject* PromiseObj() const + { + if (mPromiseObj) { + JS::ExposeObjectToActiveJS(mPromiseObj); + } + return mPromiseObj; + } + +#else // SPIDERMONKEY_PROMISE + JSObject* PromiseObj() + { + return GetWrapper(); + } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle 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 + CreateFromExisting(nsIGlobalObject* aGlobal, + JS::Handle 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 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 aValue); void RejectInternal(JSContext* aCx, JS::Handle aValue); +#endif // SPIDERMONKEY_PROMISE template 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& aTarget); +#endif // SPIDERMONKEY_PROMISE + + void HandleException(JSContext* aCx); RefPtr mGlobal; +#ifndef SPIDERMONKEY_PROMISE nsTArray > mResolveCallbacks; nsTArray > 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 mPromiseObj; +#endif // SPIDERMONKEY_PROMISE }; NS_DEFINE_STATIC_IID_ACCESSOR(Promise, NS_PROMISE_IID) diff --git a/dom/promise/PromiseCallback.cpp b/dom/promise/PromiseCallback.cpp index 8081234b15..13ed1c53fd 100644 --- a/dom/promise/PromiseCallback.cpp +++ b/dom/promise/PromiseCallback.cpp @@ -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 aGlobal, return nullptr; } +#endif // SPIDERMONKEY_PROMISE + } // namespace dom } // namespace mozilla diff --git a/dom/promise/PromiseCallback.h b/dom/promise/PromiseCallback.h index 3cb98e1543..9f55e03d01 100644 --- a/dom/promise/PromiseCallback.h +++ b/dom/promise/PromiseCallback.h @@ -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 diff --git a/dom/promise/PromiseDebugging.cpp b/dom/promise/PromiseDebugging.cpp index 6706cd1fd7..92d181f649 100644 --- a/dom/promise/PromiseDebugging.cpp +++ b/dom/promise/PromiseDebugging.cpp @@ -66,6 +66,7 @@ private: /* static */ MOZ_THREAD_LOCAL(bool) FlushRejections::sDispatched; +#ifndef SPIDERMONKEY_PROMISE static Promise* UnwrapPromise(JS::Handle aPromise, ErrorResult& aRv) { @@ -103,6 +104,8 @@ PromiseDebugging::GetState(GlobalObject&, JS::Handle 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 aPromise, JS::MutableHandle aStack, @@ -210,6 +218,8 @@ PromiseDebugging::GetTimeToSettle(GlobalObject&, JS::Handle 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 diff --git a/dom/promise/PromiseDebugging.h b/dom/promise/PromiseDebugging.h index c76076620f..5eeb65e6d3 100644 --- a/dom/promise/PromiseDebugging.h +++ b/dom/promise/PromiseDebugging.h @@ -32,6 +32,7 @@ public: static void Init(); static void Shutdown(); +#ifndef SPIDERMONKEY_PROMISE static void GetState(GlobalObject&, JS::Handle aPromise, PromiseDebuggingStateHolder& aState, ErrorResult& aRv); @@ -58,20 +59,25 @@ public: static void GetPromiseID(GlobalObject&, JS::Handle, 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: diff --git a/dom/promise/PromiseNativeAbortCallback.h b/dom/promise/PromiseNativeAbortCallback.h deleted file mode 100644 index 28d1925f5b..0000000000 --- a/dom/promise/PromiseNativeAbortCallback.h +++ /dev/null @@ -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 diff --git a/dom/promise/PromiseNativeHandler.cpp b/dom/promise/PromiseNativeHandler.cpp new file mode 100644 index 0000000000..50d0e1b96c --- /dev/null +++ b/dom/promise/PromiseNativeHandler.cpp @@ -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 aGivenProto, + JS::MutableHandle aWrapper) +{ + return PromiseNativeHandlerBinding::Wrap(aCx, this, aGivenProto, aWrapper); +} +#endif // SPIDERMONKEY_PROMISE + +} // namespace dom +} // namespace mozilla + + diff --git a/dom/promise/PromiseNativeHandler.h b/dom/promise/PromiseNativeHandler.h index 6ba7142aa1..61657361da 100644 --- a/dom/promise/PromiseNativeHandler.h +++ b/dom/promise/PromiseNativeHandler.h @@ -30,6 +30,12 @@ public: virtual void RejectedCallback(JSContext* aCx, JS::Handle aValue) = 0; + +#ifdef SPIDERMONKEY_PROMISE + bool WrapObject(JSContext* aCx, JS::Handle aGivenProto, + JS::MutableHandle aWrapper); +#endif // SPIDERMONKEY_PROMISE + }; } // namespace dom diff --git a/dom/promise/moz.build b/dom/promise/moz.build index 30337a6c9a..8edbb07c7b 100644 --- a/dom/promise/moz.build +++ b/dom/promise/moz.build @@ -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 += [ diff --git a/dom/promise/tests/mochitest.ini b/dom/promise/tests/mochitest.ini index 888bed0a24..1f2c0d5e08 100644 --- a/dom/promise/tests/mochitest.ini +++ b/dom/promise/tests/mochitest.ini @@ -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] diff --git a/dom/promise/tests/test_abortable_promise.html b/dom/promise/tests/test_abortable_promise.html deleted file mode 100644 index d02b521490..0000000000 --- a/dom/promise/tests/test_abortable_promise.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - Promise.resolve(anything) Test - - - - -

- -
-
-
- - - diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 9d3e5d754c..0e7ebb9f7e 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.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! diff --git a/dom/webidl/AbortablePromise.webidl b/dom/webidl/AbortablePromise.webidl deleted file mode 100644 index bc06891a9c..0000000000 --- a/dom/webidl/AbortablePromise.webidl +++ /dev/null @@ -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(); -}; diff --git a/dom/webidl/DummyBinding.webidl b/dom/webidl/DummyBinding.webidl index 931c9db99e..01b0a54b21 100644 --- a/dom/webidl/DummyBinding.webidl +++ b/dom/webidl/DummyBinding.webidl @@ -9,6 +9,7 @@ interface DummyInterface { void lifecycleCallbacks(optional LifecycleCallbacks arg); + void promiseJobCallback(PromiseJobCallback arg); }; interface DummyInterfaceWorkers { diff --git a/dom/webidl/Promise.webidl b/dom/webidl/Promise.webidl index 328affcd3b..d449368af8 100644 --- a/dom/webidl/Promise.webidl +++ b/dom/webidl/Promise.webidl @@ -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 diff --git a/dom/webidl/PromiseDebugging.webidl b/dom/webidl/PromiseDebugging.webidl index 20eed3deb6..a679b3f198 100644 --- a/dom/webidl/PromiseDebugging.webidl +++ b/dom/webidl/PromiseDebugging.webidl @@ -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. * diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index b38f13c7bf..e2f8155a5b 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -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', diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index eda0933904..8b7b1873cd 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -36,7 +36,7 @@ class ZoneHeapThreshold double gcHeapGrowthFactor_; // GC trigger threshold for allocations on the GC heap. - size_t gcTriggerBytes_; + mozilla::Atomic gcTriggerBytes_; public: ZoneHeapThreshold() diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 3d3f294a9b..7acf84f58d 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -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) diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 209240ee4d..edbfb6e9e0 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -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 diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index ca0e82490f..a33a11d38b 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -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 = 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 p = Promise::CreateFromExisting(glob, innerObj); + return p && NS_SUCCEEDED(p->QueryInterface(*iid, dest)); + } + } +#endif // SPIDERMONKEY_PROMISE } RefPtr wrapper; diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index c7bb97f2be..8fa85b554c 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -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); diff --git a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-09.html.ini b/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-09.html.ini deleted file mode 100644 index 617f8d14ba..0000000000 --- a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-09.html.ini +++ /dev/null @@ -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 - diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html new file mode 100644 index 0000000000..a3273f626c --- /dev/null +++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html @@ -0,0 +1,97 @@ + + + + +
+ diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 780f36f88c..cecfb20df7 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -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 mCallback; +}; + +/* static */ +bool +CycleCollectedJSRuntime::EnqueuePromiseJobCallback(JSContext* aCx, + JS::HandleObject aJob, + void* aData) +{ + CycleCollectedJSRuntime* self = static_cast(aData); + MOZ_ASSERT(JS_GetRuntime(aCx) == self->Runtime()); + MOZ_ASSERT(Get() == self); + + nsCOMPtr runnable = new PromiseJobRunnable(aCx, aJob); + self->GetPromiseMicroTaskQueue().push(runnable); + return true; +} + struct JsGcTracer : public TraceCallbacks { virtual void Trace(JS::Heap* aPtr, const char* aName, diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index 3085dfa5ab..8cf8137c25 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -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);