diff options
Diffstat (limited to 'dom')
139 files changed, 1262 insertions, 6095 deletions
diff --git a/dom/base/nsContentPolicy.cpp b/dom/base/nsContentPolicy.cpp index 5511b9086..534466103 100644 --- a/dom/base/nsContentPolicy.cpp +++ b/dom/base/nsContentPolicy.cpp @@ -22,6 +22,7 @@ #include "nsIDOMWindow.h" #include "nsITabChild.h" #include "nsIContent.h" +#include "nsIImageLoadingContent.h" #include "nsILoadContext.h" #include "nsCOMArray.h" #include "nsContentUtils.h" @@ -145,6 +146,16 @@ nsContentPolicy::CheckPolicy(CPMethod policyMethod, decision); if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) { + // If we are blocking an image, we have to let the + // ImageLoadingContent know that we blocked the load. + if (externalType == nsIContentPolicy::TYPE_IMAGE || + externalType == nsIContentPolicy::TYPE_IMAGESET) { + nsCOMPtr<nsIImageLoadingContent> img = + do_QueryInterface(requestingContext); + if (img) { + img->SetBlockedRequest(*decision); + } + } /* policy says no, no point continuing to check */ return NS_OK; } @@ -193,6 +204,16 @@ nsContentPolicy::CheckPolicy(CPMethod policyMethod, decision); if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) { + // If we are blocking an image, we have to let the + // ImageLoadingContent know that we blocked the load. + if (externalType == nsIContentPolicy::TYPE_IMAGE || + externalType == nsIContentPolicy::TYPE_IMAGESET) { + nsCOMPtr<nsIImageLoadingContent> img = + do_QueryInterface(requestingContext); + if (img) { + img->SetBlockedRequest(*decision); + } + } /* policy says no, no point continuing to check */ return NS_OK; } diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 1f9c17947..800f40fa1 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -8478,12 +8478,9 @@ nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(nsContentPolicyType bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) { - if (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD || - aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD || - aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD) { - return true; - } - return false; + return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD || + aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD || + aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD); } nsresult diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 299a8e859..606d67de9 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -9,10 +9,14 @@ #ifndef nsContentUtils_h___ #define nsContentUtils_h___ -#if defined(XP_WIN) +#ifdef XP_WIN #include <float.h> #endif +#ifdef XP_SOLARIS +#include <ieeefp.h> +#endif + #include "js/TypeDecls.h" #include "js/Value.h" #include "js/RootingAPI.h" diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index d8abc174f..e2be6b664 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -395,6 +395,21 @@ nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement, } } +void +nsIdentifierMapEntry::ClearAndNotify() +{ + Element* currentElement = mIdContentList.SafeElementAt(0); + mIdContentList.Clear(); + if (currentElement) { + FireChangeCallbacks(currentElement, nullptr); + } + mNameContentList = nullptr; + if (mImageElement) { + SetImageElement(nullptr); + } + mChangeCallbacks = nullptr; +} + namespace { struct PositionComparator @@ -1422,12 +1437,12 @@ nsDocument::~nsDocument() delete mSubDocuments; mSubDocuments = nullptr; + nsAutoScriptBlocker scriptBlocker; + // Destroy link map now so we don't waste time removing // links one by one DestroyElementMaps(); - nsAutoScriptBlocker scriptBlocker; - for (uint32_t indx = mChildren.ChildCount(); indx-- != 0; ) { mChildren.ChildAt(indx)->UnbindFromTree(); mChildren.RemoveChildAt(indx); @@ -1972,15 +1987,16 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup, delete mSubDocuments; mSubDocuments = nullptr; - // Destroy link map now so we don't waste time removing - // links one by one - DestroyElementMaps(); - bool oldVal = mInUnlinkOrDeletion; mInUnlinkOrDeletion = true; uint32_t count = mChildren.ChildCount(); { // Scope for update MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true); + + // Destroy link map now so we don't waste time removing + // links one by one + DestroyElementMaps(); + for (int32_t i = int32_t(count) - 1; i >= 0; i--) { nsCOMPtr<nsIContent> content = mChildren.ChildAt(i); @@ -8955,7 +8971,14 @@ nsDocument::DestroyElementMaps() mStyledLinksCleared = true; #endif mStyledLinks.Clear(); + + // Notify ID change listeners before clearing the identifier map. + for (auto iter = mIdentifierMap.Iter(); !iter.Done(); iter.Next()) { + iter.Get()->ClearAndNotify(); + } + mIdentifierMap.Clear(); + ++mExpandoAndGeneration.generation; } @@ -9222,19 +9245,23 @@ already_AddRefed<nsIURI> nsDocument::ResolvePreloadImage(nsIURI *aBaseURI, const nsAString& aSrcAttr, const nsAString& aSrcsetAttr, - const nsAString& aSizesAttr) + const nsAString& aSizesAttr, + bool *aIsImgSet) { nsString sourceURL; + bool isImgSet; if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) { // We're in a <picture> element and found a URI from a source previous to // this image, use it. sourceURL = mPreloadPictureFoundSource; + isImgSet = true; } else { // Otherwise try to use this <img> as a source HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr, aSrcsetAttr, aSizesAttr, NullString(), NullString(), sourceURL); + isImgSet = !aSrcsetAttr.IsEmpty(); } // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI) @@ -9252,6 +9279,8 @@ nsDocument::ResolvePreloadImage(nsIURI *aBaseURI, return nullptr; } + *aIsImgSet = isImgSet; + // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in // this this <picture> share the same <sources> (though this is not valid per // spec) @@ -9260,16 +9289,12 @@ nsDocument::ResolvePreloadImage(nsIURI *aBaseURI, void nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr, - ReferrerPolicy aReferrerPolicy) + ReferrerPolicy aReferrerPolicy, bool aIsImgSet) { // Early exit if the img is already present in the img-cache // which indicates that the "real" load has already started and // that we shouldn't preload it. - int16_t blockingStatus; - if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) || - !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this), - this, NodePrincipal(), &blockingStatus, - nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD)) { + if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this))) { return; } @@ -9288,6 +9313,10 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr, MOZ_CRASH("Unknown CORS mode!"); } + nsContentPolicyType policyType = + aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET : + nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD; + // Image not in cache - trigger preload RefPtr<imgRequestProxy> request; nsresult rv = @@ -9301,7 +9330,7 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr, loadFlags, NS_LITERAL_STRING("img"), getter_AddRefs(request), - nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD); + policyType); // Pin image-reference to avoid evicting it from the img-cache before // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 95fd57545..ac600eb43 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -216,6 +216,11 @@ public: void RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback, void* aData, bool aForImage); + /** + * Remove all elements and notify change listeners. + */ + void ClearAndNotify(); + void Traverse(nsCycleCollectionTraversalCallback* aCallback); struct ChangeCallback { @@ -948,11 +953,13 @@ public: ResolvePreloadImage(nsIURI *aBaseURI, const nsAString& aSrcAttr, const nsAString& aSrcsetAttr, - const nsAString& aSizesAttr) override; + const nsAString& aSizesAttr, + bool *aIsImgSet) override; virtual void MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr, - ReferrerPolicy aReferrerPolicy) override; + ReferrerPolicy aReferrerPolicy, + bool aIsImgSet) override; virtual void ForgetImagePreload(nsIURI* aURI) override; virtual void MaybePreconnect(nsIURI* uri, diff --git a/dom/base/nsDocumentEncoder.cpp b/dom/base/nsDocumentEncoder.cpp index 84b128b15..34eb6ed38 100644 --- a/dom/base/nsDocumentEncoder.cpp +++ b/dom/base/nsDocumentEncoder.cpp @@ -82,7 +82,9 @@ protected: nsAString& aStr, bool aDontSerializeRoot, uint32_t aMaxLength = 0); - nsresult SerializeNodeEnd(nsINode* aNode, nsAString& aStr); + nsresult SerializeNodeEnd(nsINode* aOriginalNode, + nsAString& aStr, + nsINode* aFixupNode = nullptr); // This serializes the content of aNode. nsresult SerializeToStringIterative(nsINode* aNode, nsAString& aStr); @@ -405,14 +407,37 @@ nsDocumentEncoder::SerializeNodeStart(nsINode* aNode, } nsresult -nsDocumentEncoder::SerializeNodeEnd(nsINode* aNode, - nsAString& aStr) +nsDocumentEncoder::SerializeNodeEnd(nsINode* aOriginalNode, + nsAString& aStr, + nsINode* aFixupNode) { - if (!IsVisibleNode(aNode)) + if (!IsVisibleNode(aOriginalNode)) return NS_OK; - if (aNode->IsElement()) { - mSerializer->AppendElementEnd(aNode->AsElement(), aStr); + nsINode* node = nullptr; + nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip; + + // Caller didn't do fixup, so we'll do it ourselves + if (!aFixupNode) { + aFixupNode = aOriginalNode; + if (mNodeFixup) { + bool dummy; + nsCOMPtr<nsIDOMNode> domNodeIn = do_QueryInterface(aOriginalNode); + nsCOMPtr<nsIDOMNode> domNodeOut; + mNodeFixup->FixupNode(domNodeIn, &dummy, getter_AddRefs(domNodeOut)); + fixedNodeKungfuDeathGrip = do_QueryInterface(domNodeOut); + node = fixedNodeKungfuDeathGrip; + } + } + + // Fall back to original node if needed. + if (!node) + node = aOriginalNode; + + if (node->IsElement()) { + mSerializer->AppendElementEnd(node->AsElement(), + aOriginalNode->AsElement(), + aStr); } return NS_OK; } @@ -481,7 +506,7 @@ nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode, } if (!aDontSerializeRoot) { - rv = SerializeNodeEnd(maybeFixedNode, aStr); + rv = SerializeNodeEnd(aNode, aStr, maybeFixedNode); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 8fefa0e02..73a3a02b1 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -665,6 +665,7 @@ GK_ATOM(noembed, "noembed") GK_ATOM(noframes, "noframes") GK_ATOM(nohref, "nohref") GK_ATOM(noisolation, "noisolation") +GK_ATOM(nomodule, "nomodule") GK_ATOM(nonce, "nonce") GK_ATOM(none, "none") GK_ATOM(noresize, "noresize") diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index afaa24f09..47b46dda0 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1026,11 +1026,6 @@ public: return false; } - virtual bool watch(JSContext *cx, JS::Handle<JSObject*> proxy, - JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const override; - virtual bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy, - JS::Handle<jsid> id) const override; - static void ObjectMoved(JSObject *obj, const JSObject *old); static const nsOuterWindowProxy singleton; @@ -1398,20 +1393,6 @@ nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy, return true; } -bool -nsOuterWindowProxy::watch(JSContext *cx, JS::Handle<JSObject*> proxy, - JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const -{ - return js::WatchGuts(cx, proxy, id, callable); -} - -bool -nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy, - JS::Handle<jsid> id) const -{ - return js::UnwatchGuts(cx, proxy, id); -} - void nsOuterWindowProxy::ObjectMoved(JSObject *obj, const JSObject *old) { @@ -3224,6 +3205,12 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, newInnerWindow->mLocalStorage = nullptr; newInnerWindow->mSessionStorage = nullptr; + newInnerWindow->mPerformance = nullptr; + + // This must be called after nulling the internal objects because + // we might recreate them here by calling the getter methods, and + // store them into the JS slots. If we null them after, the slot + // values and the objects will be out of sync. newInnerWindow->ClearDocumentDependentSlots(cx); } } else { @@ -3364,10 +3351,16 @@ nsGlobalWindow::InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument) } mDoc = aDocument; - ClearDocumentDependentSlots(aCx); mFocusedNode = nullptr; mLocalStorage = nullptr; mSessionStorage = nullptr; + mPerformance = nullptr; + + // This must be called after nulling the internal objects because we might + // recreate them here by calling the getter methods, and store them into the JS + // slots. If we null them after, the slot values and the objects will be + // out of sync. + ClearDocumentDependentSlots(aCx); #ifdef DEBUG mLastOpenedURI = aDocument->GetDocumentURI(); @@ -10957,35 +10950,12 @@ nsGlobalWindow::GetComputedStyleHelperOuter(Element& aElt, { MOZ_RELEASE_ASSERT(IsOuterWindow()); - if (!mDocShell) { + if (!mDoc) { return nullptr; } - nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); - - if (!presShell) { - // Try flushing frames on our parent in case there's a pending - // style change that will create the presshell. - auto* parent = nsGlobalWindow::Cast(GetPrivateParent()); - if (!parent) { - return nullptr; - } - - parent->FlushPendingNotifications(Flush_Frames); - - // Might have killed mDocShell - if (!mDocShell) { - return nullptr; - } - - presShell = mDocShell->GetPresShell(); - if (!presShell) { - return nullptr; - } - } - RefPtr<nsComputedDOMStyle> compStyle = - NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell, + NS_NewComputedDOMStyle(&aElt, aPseudoElt, mDoc, aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly : nsComputedDOMStyle::eAll); diff --git a/dom/base/nsHTMLContentSerializer.cpp b/dom/base/nsHTMLContentSerializer.cpp index ab8b4f2b2..c135c4cf8 100644 --- a/dom/base/nsHTMLContentSerializer.cpp +++ b/dom/base/nsHTMLContentSerializer.cpp @@ -301,6 +301,7 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement, NS_IMETHODIMP nsHTMLContentSerializer::AppendElementEnd(Element* aElement, + Element* aOriginalElement /* unused */, nsAString& aStr) { NS_ENSURE_ARG(aElement); diff --git a/dom/base/nsHTMLContentSerializer.h b/dom/base/nsHTMLContentSerializer.h index 6f3500e01..8e23d54ca 100644 --- a/dom/base/nsHTMLContentSerializer.h +++ b/dom/base/nsHTMLContentSerializer.h @@ -31,6 +31,7 @@ class nsHTMLContentSerializer final : public nsXHTMLContentSerializer { nsAString& aStr) override; NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement, + mozilla::dom::Element* aOriginalElement, nsAString& aStr) override; NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument, diff --git a/dom/base/nsIContentSerializer.h b/dom/base/nsIContentSerializer.h index f023cbc90..35014bd2c 100644 --- a/dom/base/nsIContentSerializer.h +++ b/dom/base/nsIContentSerializer.h @@ -55,6 +55,7 @@ class nsIContentSerializer : public nsISupports { nsAString& aStr) = 0; NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement, + mozilla::dom::Element* aOriginalElement, nsAString& aStr) = 0; NS_IMETHOD Flush(nsAString& aStr) = 0; diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index e5d12ab8f..d76a12d71 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2260,21 +2260,27 @@ public: * nesting and possible sources, which are used to inform URL selection * responsive <picture> or <img srcset> images. Unset attributes are expected * to be marked void. + * If this image is for <picture> or <img srcset>, aIsImgSet will be set to + * true, false otherwise. */ virtual already_AddRefed<nsIURI> ResolvePreloadImage(nsIURI *aBaseURI, const nsAString& aSrcAttr, const nsAString& aSrcsetAttr, - const nsAString& aSizesAttr) = 0; + const nsAString& aSizesAttr, + bool *aIsImgSet) = 0; /** * Called by nsParser to preload images. Can be removed and code moved * to nsPreloadURIs::PreloadURIs() in file nsParser.cpp whenever the * parser-module is linked with gklayout-module. aCrossOriginAttr should * be a void string if the attr is not present. + * aIsImgSet is the value got from calling ResolvePreloadImage, it is true + * when this image is for loading <picture> or <img srcset> images. */ virtual void MaybePreLoadImage(nsIURI* uri, const nsAString& aCrossOriginAttr, - ReferrerPolicyEnum aReferrerPolicy) = 0; + ReferrerPolicyEnum aReferrerPolicy, + bool aIsImgSet) = 0; /** * Called by images to forget an image preload when they start doing diff --git a/dom/base/nsIImageLoadingContent.idl b/dom/base/nsIImageLoadingContent.idl index fea261a34..eacc4ac3a 100644 --- a/dom/base/nsIImageLoadingContent.idl +++ b/dom/base/nsIImageLoadingContent.idl @@ -104,6 +104,15 @@ interface nsIImageLoadingContent : imgINotificationObserver imgIRequest getRequest(in long aRequestType); /** + * Call this function when the request was blocked by any of the + * security policies enforced. + * + * @param aContentDecision the decision returned from nsIContentPolicy + * (any of the types REJECT_*) + */ + void setBlockedRequest(in int16_t aContentDecision); + + /** * @return true if the current request's size is available. */ [noscript, notxpcom] boolean currentRequestHasSize(); diff --git a/dom/base/nsImageLoadingContent.cpp b/dom/base/nsImageLoadingContent.cpp index 0c6c37b44..4aad55941 100644 --- a/dom/base/nsImageLoadingContent.cpp +++ b/dom/base/nsImageLoadingContent.cpp @@ -44,6 +44,7 @@ #include "mozAutoDocUpdate.h" #include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/AutoRestore.h" #include "mozilla/EventStates.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/ImageTracker.h" @@ -94,7 +95,8 @@ nsImageLoadingContent::nsImageLoadingContent() mNewRequestsWillNeedAnimationReset(false), mStateChangerDepth(0), mCurrentRequestRegistered(false), - mPendingRequestRegistered(false) + mPendingRequestRegistered(false), + mIsStartingImageLoad(false) { if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) { mLoadingEnabled = false; @@ -785,6 +787,11 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, nsIDocument* aDocument, nsLoadFlags aLoadFlags) { + MOZ_ASSERT(!mIsStartingImageLoad, "some evil code is reentering LoadImage."); + if (mIsStartingImageLoad) { + return NS_OK; + } + // Pending load/error events need to be canceled in some situations. This // is not documented in the spec, but can cause site compat problems if not // done. See bug 1309461 and https://github.com/whatwg/html/issues/1872. @@ -814,6 +821,21 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, } } + AutoRestore<bool> guard(mIsStartingImageLoad); + mIsStartingImageLoad = true; + + // Data documents, or documents from DOMParser shouldn't perform image loading. + if (aDocument->IsLoadedAsData()) { + // This is the only codepath on which we can reach SetBlockedRequest while + // our pending request exists. Just clear it out here if we do have one. + ClearPendingRequest(NS_BINDING_ABORTED, + Some(OnNonvisible::DISCARD_IMAGES)); + SetBlockedRequest(nsIContentPolicy::REJECT_REQUEST); + FireEvent(NS_LITERAL_STRING("error")); + FireEvent(NS_LITERAL_STRING("loadend")); + return NS_OK; + } + // URI equality check. // // We skip the equality check if our current image was blocked, since in that @@ -844,23 +866,8 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, "Principal mismatch?"); #endif - // Are we blocked? - int16_t cpDecision = nsIContentPolicy::REJECT_REQUEST; nsContentPolicyType policyType = PolicyTypeForLoad(aImageLoadType); - nsContentUtils::CanLoadImage(aNewURI, - static_cast<nsIImageLoadingContent*>(this), - aDocument, - aDocument->NodePrincipal(), - &cpDecision, - policyType); - if (!NS_CP_ACCEPTED(cpDecision)) { - FireEvent(NS_LITERAL_STRING("error")); - FireEvent(NS_LITERAL_STRING("loadend")); - SetBlockedRequest(aNewURI, cpDecision); - return NS_OK; - } - nsLoadFlags loadFlags = aLoadFlags; int32_t corsmode = GetCORSMode(); if (corsmode == CORS_ANONYMOUS) { @@ -878,7 +885,6 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, referrerPolicy = imgReferrerPolicy; } - // Not blocked. Do the load. RefPtr<imgRequestProxy>& req = PrepareNextRequest(aImageLoadType); nsCOMPtr<nsIContent> content = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); @@ -932,7 +938,6 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, FireEvent(NS_LITERAL_STRING("error")); FireEvent(NS_LITERAL_STRING("loadend")); - return NS_OK; } return NS_OK; @@ -1212,46 +1217,42 @@ nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType) mMostRecentRequestChange = now; } - // If we don't have a usable current request, get rid of any half-baked - // request that might be sitting there and make this one current. - if (!HaveSize(mCurrentRequest)) - return PrepareCurrentRequest(aImageLoadType); - // Otherwise, make it pending. - return PreparePendingRequest(aImageLoadType); + // We only want to cancel the existing current request if size is not + // available. bz says the web depends on this behavior. + // Otherwise, we get rid of any half-baked request that might be sitting there + // and make this one current. + // TODO: Bug 583491 + // Investigate/Cleanup NS_ERROR_IMAGE_SRC_CHANGED use in nsImageFrame.cpp + return HaveSize(mCurrentRequest) ? + PreparePendingRequest(aImageLoadType) : + PrepareCurrentRequest(aImageLoadType); } -void -nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision) +nsresult +nsImageLoadingContent::SetBlockedRequest(int16_t aContentDecision) { + // If this is not calling from LoadImage, for example, from ServiceWorker, + // bail out. + if (!mIsStartingImageLoad) { + return NS_OK; + } + // Sanity MOZ_ASSERT(!NS_CP_ACCEPTED(aContentDecision), "Blocked but not?"); - // We do some slightly illogical stuff here to maintain consistency with - // old behavior that people probably depend on. Even in the case where the - // new image is blocked, the old one should really be canceled with the - // reason "image source changed". However, apparently there's some abuse - // over in nsImageFrame where the displaying of the "broken" icon for the - // next image depends on the cancel reason of the previous image. ugh. - // XXX(seth): So shouldn't we fix nsImageFrame?! - ClearPendingRequest(NS_ERROR_IMAGE_BLOCKED, - Some(OnNonvisible::DISCARD_IMAGES)); - - // For the blocked case, we only want to cancel the existing current request - // if size is not available. bz says the web depends on this behavior. - if (!HaveSize(mCurrentRequest)) { + // We should never have a pending request after we got blocked. + MOZ_ASSERT(!mPendingRequest, "mPendingRequest should be null."); + if (HaveSize(mCurrentRequest)) { + // PreparePendingRequest set mPendingRequestFlags, now since we've decided + // to block it, we reset it back to 0. + mPendingRequestFlags = 0; + } else { mImageBlockingStatus = aContentDecision; - uint32_t keepFlags = mCurrentRequestFlags & REQUEST_IS_IMAGESET; - ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED, - Some(OnNonvisible::DISCARD_IMAGES)); - - // We still want to remember what URI we were and if it was an imageset, - // despite not having an actual request. These are both cleared as part of - // ClearCurrentRequest() before a new request is started. - mCurrentURI = aURI; - mCurrentRequestFlags = keepFlags; } + + return NS_OK; } RefPtr<imgRequestProxy>& @@ -1262,7 +1263,7 @@ nsImageLoadingContent::PrepareCurrentRequest(ImageLoadType aImageLoadType) mImageBlockingStatus = nsIContentPolicy::ACCEPT; // Get rid of anything that was there previously. - ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED, + ClearCurrentRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES)); if (mNewRequestsWillNeedAnimationReset) { @@ -1281,7 +1282,7 @@ RefPtr<imgRequestProxy>& nsImageLoadingContent::PreparePendingRequest(ImageLoadType aImageLoadType) { // Get rid of anything that was there previously. - ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED, + ClearPendingRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES)); if (mNewRequestsWillNeedAnimationReset) { diff --git a/dom/base/nsImageLoadingContent.h b/dom/base/nsImageLoadingContent.h index 5f7daff72..cfb2a6207 100644 --- a/dom/base/nsImageLoadingContent.h +++ b/dom/base/nsImageLoadingContent.h @@ -303,17 +303,10 @@ protected: RefPtr<imgRequestProxy>& PrepareNextRequest(ImageLoadType aImageLoadType); /** - * Called when we would normally call PrepareNextRequest(), but the request was - * blocked. - */ - void SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision); - - /** * Returns a COMPtr reference to the current/pending image requests, cleaning * up and canceling anything that was there before. Note that if you just want * to get rid of one of the requests, you should call - * Clear*Request(NS_BINDING_ABORTED) instead, since it passes a more appropriate - * aReason than Prepare*Request() does (NS_ERROR_IMAGE_SRC_CHANGED). + * Clear*Request(NS_BINDING_ABORTED) instead. * * @param aImageLoadType The ImageLoadType for this request */ @@ -459,6 +452,14 @@ private: // registered with the refresh driver. bool mCurrentRequestRegistered; bool mPendingRequestRegistered; + + // This member is used in SetBlockedRequest, if it's true, then this call is + // triggered from LoadImage. + // If this is false, it means this call is from other places like + // ServiceWorker, then we will ignore call to SetBlockedRequest for now. + // + // Also we use this variable to check if some evil code is reentering LoadImage. + bool mIsStartingImageLoad; }; #endif // nsImageLoadingContent_h__ diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 3c850c4cd..4978744e8 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -718,9 +718,9 @@ nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent) if (mType == eType_Plugin) { nsIDocument* doc = thisContent->GetComposedDoc(); if (doc && doc->IsActive()) { - nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(doc, - NS_LITERAL_STRING("PluginRemoved")); - NS_DispatchToCurrentThread(ev); + nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(doc, + NS_LITERAL_STRING("PluginRemoved")); + NS_DispatchToCurrentThread(ev); } } } @@ -3628,6 +3628,14 @@ nsObjectLoadingContent::HasGoodFallback() { } } + // RULE "nosrc": + // Use fallback content if the object has not specified a src URI. + if (rulesList[i].EqualsLiteral("nosrc")) { + if (!mOriginalURI) { + return true; + } + } + // RULE "adobelink": // Don't use fallback content when it has a link to adobe's website. if (rulesList[i].EqualsLiteral("adobelink")) { diff --git a/dom/base/nsPlainTextSerializer.cpp b/dom/base/nsPlainTextSerializer.cpp index ef6bdcac7..98c9cfe32 100644 --- a/dom/base/nsPlainTextSerializer.cpp +++ b/dom/base/nsPlainTextSerializer.cpp @@ -53,7 +53,6 @@ static int32_t GetUnicharStringWidth(const char16_t* pwcs, int32_t n); // Someday may want to make this non-const: static const uint32_t TagStackSize = 500; -static const uint32_t OLStackSize = 100; nsresult NS_NewPlainTextSerializer(nsIContentSerializer** aSerializer) @@ -100,10 +99,6 @@ nsPlainTextSerializer::nsPlainTextSerializer() mTagStackIndex = 0; mIgnoreAboveIndex = (uint32_t)kNotFound; - // initialize the OL stack, where numbers for ordered lists are kept - mOLStack = new int32_t[OLStackSize]; - mOLStackIndex = 0; - mULCount = 0; mIgnoredChildNodeLevel = 0; @@ -112,7 +107,6 @@ nsPlainTextSerializer::nsPlainTextSerializer() nsPlainTextSerializer::~nsPlainTextSerializer() { delete[] mTagStack; - delete[] mOLStack; NS_WARNING_ASSERTION(mHeadLevel == 0, "Wrong head level!"); } @@ -189,6 +183,8 @@ nsPlainTextSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn, // XXX We should let the caller decide whether to do this or not mFlags &= ~nsIDocumentEncoder::OutputNoFramesContent; + MOZ_ASSERT(mOLStack.IsEmpty()); + return NS_OK; } @@ -390,6 +386,7 @@ nsPlainTextSerializer::AppendElementStart(Element* aElement, NS_IMETHODIMP nsPlainTextSerializer::AppendElementEnd(Element* aElement, + Element* aOriginalElement /* unused */, nsAString& aStr) { NS_ENSURE_ARG(aElement); @@ -437,6 +434,8 @@ nsPlainTextSerializer::AppendDocumentStart(nsIDocument *aDocument, return NS_OK; } +int32_t kOlStackDummyValue = 0; + nsresult nsPlainTextSerializer::DoOpenContainer(nsIAtom* aTag) { @@ -615,44 +614,45 @@ nsPlainTextSerializer::DoOpenContainer(nsIAtom* aTag) } else if (aTag == nsGkAtoms::ul) { // Indent here to support nested lists, which aren't included in li :-( - EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0); - // Must end the current line before we change indention + EnsureVerticalSpace(IsInOLOrUL() ? 0 : 1); + // Must end the current line before we change indention mIndent += kIndentSizeList; mULCount++; } else if (aTag == nsGkAtoms::ol) { - EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0); + EnsureVerticalSpace(IsInOLOrUL() ? 0 : 1); if (mFlags & nsIDocumentEncoder::OutputFormatted) { // Must end the current line before we change indention - if (mOLStackIndex < OLStackSize) { - nsAutoString startAttr; - int32_t startVal = 1; - if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::start, startAttr))) { - nsresult rv = NS_OK; - startVal = startAttr.ToInteger(&rv); - if (NS_FAILED(rv)) - startVal = 1; + nsAutoString startAttr; + int32_t startVal = 1; + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::start, startAttr))) { + nsresult rv = NS_OK; + startVal = startAttr.ToInteger(&rv); + if (NS_FAILED(rv)) { + startVal = 1; } - mOLStack[mOLStackIndex++] = startVal; } + mOLStack.AppendElement(startVal); } else { - mOLStackIndex++; + mOLStack.AppendElement(kOlStackDummyValue); } mIndent += kIndentSizeList; // see ul } else if (aTag == nsGkAtoms::li && (mFlags & nsIDocumentEncoder::OutputFormatted)) { if (mTagStackIndex > 1 && IsInOL()) { - if (mOLStackIndex > 0) { + if (!mOLStack.IsEmpty()) { nsAutoString valueAttr; if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::value, valueAttr))) { nsresult rv = NS_OK; int32_t valueAttrVal = valueAttr.ToInteger(&rv); - if (NS_SUCCEEDED(rv)) - mOLStack[mOLStackIndex-1] = valueAttrVal; + if (NS_SUCCEEDED(rv)) { + mOLStack.LastElement() = valueAttrVal; + } } // This is what nsBulletFrame does for OLs: - mInIndentString.AppendInt(mOLStack[mOLStackIndex-1]++, 10); + mInIndentString.AppendInt(mOLStack.LastElement(), 10); + mOLStack.LastElement()++; } else { mInIndentString.Append(char16_t('#')); @@ -877,7 +877,8 @@ nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag) else if (aTag == nsGkAtoms::ul) { FlushLine(); mIndent -= kIndentSizeList; - if (--mULCount + mOLStackIndex == 0) { + --mULCount; + if (!IsInOLOrUL()) { mFloatingLines = 1; mLineBreakDue = true; } @@ -885,9 +886,9 @@ nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag) else if (aTag == nsGkAtoms::ol) { FlushLine(); // Doing this after decreasing OLStackIndex would be wrong. mIndent -= kIndentSizeList; - NS_ASSERTION(mOLStackIndex, "Wrong OLStack level!"); - mOLStackIndex--; - if (mULCount + mOLStackIndex == 0) { + NS_ASSERTION(!mOLStack.IsEmpty(), "Wrong OLStack level!"); + mOLStack.RemoveElementAt(mOLStack.Length() - 1); + if (!IsInOLOrUL()) { mFloatingLines = 1; mLineBreakDue = true; } @@ -1860,6 +1861,10 @@ nsPlainTextSerializer::IsInOL() return false; } +bool nsPlainTextSerializer::IsInOLOrUL() const { + return (mULCount > 0) || !mOLStack.IsEmpty(); +} + /* @return 0 = no header, 1 = h1, ..., 6 = h6 */ diff --git a/dom/base/nsPlainTextSerializer.h b/dom/base/nsPlainTextSerializer.h index 95cf5590c..650a8e3e7 100644 --- a/dom/base/nsPlainTextSerializer.h +++ b/dom/base/nsPlainTextSerializer.h @@ -61,6 +61,7 @@ public: mozilla::dom::Element* aOriginalElement, nsAString& aStr) override; NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement, + mozilla::dom::Element* aOriginalElement, nsAString& aStr) override; NS_IMETHOD Flush(nsAString& aStr) override; @@ -80,6 +81,7 @@ private: void Write(const nsAString& aString); bool IsInPre(); bool IsInOL(); + bool IsInOLOrUL() const; bool IsCurrentNodeConverted(); bool MustSuppressLeaf(); @@ -217,8 +219,7 @@ private: uint32_t mIgnoreAboveIndex; // The stack for ordered lists - int32_t *mOLStack; - uint32_t mOLStackIndex; + AutoTArray<int32_t, 100> mOLStack; uint32_t mULCount; diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 1e23d6c5f..3ac00142d 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -654,6 +654,19 @@ nsScriptLoader::CheckContentPolicy(nsIDocument* aDocument, } bool +nsScriptLoader::ModuleScriptsEnabled() +{ + static bool sEnabledForContent = false; + static bool sCachedPref = false; + if (!sCachedPref) { + sCachedPref = true; + Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false); + } + + return nsContentUtils::IsChromeDoc(mDocument) || sEnabledForContent; +} + +bool nsScriptLoader::ModuleMapContainsModule(nsModuleLoadRequest *aRequest) const { // Returns whether we have fetched, or are currently fetching, a module script @@ -1230,15 +1243,27 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell)); nsSecurityFlags securityFlags; - // TODO: the spec currently gives module scripts different CORS behaviour to - // classic scripts. - securityFlags = aRequest->mCORSMode == CORS_NONE - ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL - : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; - if (aRequest->mCORSMode == CORS_ANONYMOUS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; - } else if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) { - securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; + if (aRequest->IsModuleRequest()) { + // According to the spec, module scripts have different behaviour to classic + // scripts and always use CORS. + securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; + if (aRequest->mCORSMode == CORS_NONE) { + securityFlags |= nsILoadInfo::SEC_COOKIES_OMIT; + } else if (aRequest->mCORSMode == CORS_ANONYMOUS) { + securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; + } else { + MOZ_ASSERT(aRequest->mCORSMode == CORS_USE_CREDENTIALS); + securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; + } + } else { + securityFlags = aRequest->mCORSMode == CORS_NONE + ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL + : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; + if (aRequest->mCORSMode == CORS_ANONYMOUS) { + securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; + } else if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) { + securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; + } } securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; @@ -1434,7 +1459,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement); - // Step 12. Check that the script is not an eventhandler + // Step 13. Check that the script is not an eventhandler if (IsScriptEventHandler(scriptContent)) { return false; } @@ -1448,8 +1473,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) nsScriptKind scriptKind = nsScriptKind::Classic; if (!type.IsEmpty()) { - // Support type="module" only for chrome documents. - if (nsContentUtils::IsChromeDoc(mDocument) && type.LowerCaseEqualsASCII("module")) { + if (ModuleScriptsEnabled() && type.LowerCaseEqualsASCII("module")) { scriptKind = nsScriptKind::Module; } else { NS_ENSURE_TRUE(ParseTypeAttribute(type, &version), false); @@ -1469,7 +1493,18 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) } } - // Step 14. in the HTML5 spec + // "In modern user agents that support module scripts, the script element with + // the nomodule attribute will be ignored". + // "The nomodule attribute must not be specified on module scripts (and will + // be ignored if it is)." + if (ModuleScriptsEnabled() && + scriptKind == nsScriptKind::Classic && + scriptContent->IsHTMLElement() && + scriptContent->HasAttr(kNameSpaceID_None, nsGkAtoms::nomodule)) { + return false; + } + + // Step 15. and later in the HTML5 spec nsresult rv = NS_OK; RefPtr<nsScriptLoadRequest> request; if (aElement->GetScriptExternal()) { @@ -1577,7 +1612,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) } return false; } - if (!aElement->GetParserCreated() && !request->IsModuleRequest()) { + if (!aElement->GetParserCreated()) { // Violate the HTML5 spec in order to make LABjs and the "order" plug-in // for RequireJS work with their Gecko-sniffed code path. See // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html @@ -2768,7 +2803,7 @@ nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset, } // TODO: Preload module scripts. - if (nsContentUtils::IsChromeDoc(mDocument) && aType.LowerCaseEqualsASCII("module")) { + if (ModuleScriptsEnabled() && aType.LowerCaseEqualsASCII("module")) { return; } diff --git a/dom/base/nsScriptLoader.h b/dom/base/nsScriptLoader.h index d30a58441..a00239be5 100644 --- a/dom/base/nsScriptLoader.h +++ b/dom/base/nsScriptLoader.h @@ -568,6 +568,8 @@ private: JS::SourceBufferHolder GetScriptSource(nsScriptLoadRequest* aRequest, nsAutoString& inlineData); + bool ModuleScriptsEnabled(); + void SetModuleFetchStarted(nsModuleLoadRequest *aRequest); void SetModuleFetchFinishedAndResumeWaitingRequests(nsModuleLoadRequest *aRequest, nsresult aResult); diff --git a/dom/base/nsXHTMLContentSerializer.cpp b/dom/base/nsXHTMLContentSerializer.cpp index 111ed46c7..0a39ef663 100755 --- a/dom/base/nsXHTMLContentSerializer.cpp +++ b/dom/base/nsXHTMLContentSerializer.cpp @@ -514,6 +514,7 @@ nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent, bool nsXHTMLContentSerializer::CheckElementEnd(mozilla::dom::Element* aElement, + mozilla::dom::Element* aOriginalElement, bool& aForceFormat, nsAString& aStr) { @@ -532,7 +533,7 @@ nsXHTMLContentSerializer::CheckElementEnd(mozilla::dom::Element* aElement, } bool dummyFormat; - return nsXMLContentSerializer::CheckElementEnd(aElement, dummyFormat, aStr); + return nsXMLContentSerializer::CheckElementEnd(aElement, aOriginalElement, dummyFormat, aStr); } bool diff --git a/dom/base/nsXHTMLContentSerializer.h b/dom/base/nsXHTMLContentSerializer.h index 7473ba074..79ecf28f1 100644 --- a/dom/base/nsXHTMLContentSerializer.h +++ b/dom/base/nsXHTMLContentSerializer.h @@ -53,6 +53,7 @@ class nsXHTMLContentSerializer : public nsXMLContentSerializer { nsAString& aStr) override; virtual bool CheckElementEnd(mozilla::dom::Element* aContent, + mozilla::dom::Element* aOriginalElement, bool& aForceFormat, nsAString& aStr) override; diff --git a/dom/base/nsXMLContentSerializer.cpp b/dom/base/nsXMLContentSerializer.cpp index 54fadaa94..f12bb8fdc 100644 --- a/dom/base/nsXMLContentSerializer.cpp +++ b/dom/base/nsXMLContentSerializer.cpp @@ -1028,6 +1028,7 @@ nsXMLContentSerializer::AppendEndOfElementStart(Element* aElement, NS_IMETHODIMP nsXMLContentSerializer::AppendElementEnd(Element* aElement, + Element* aOriginalElement, nsAString& aStr) { NS_ENSURE_ARG(aElement); @@ -1035,7 +1036,7 @@ nsXMLContentSerializer::AppendElementEnd(Element* aElement, nsIContent* content = aElement; bool forceFormat = false, outputElementEnd; - outputElementEnd = CheckElementEnd(aElement, forceFormat, aStr); + outputElementEnd = CheckElementEnd(aElement, aOriginalElement, forceFormat, aStr); nsIAtom *name = content->NodeInfo()->NameAtom(); @@ -1161,16 +1162,14 @@ nsXMLContentSerializer::CheckElementStart(nsIContent * aContent, bool nsXMLContentSerializer::CheckElementEnd(Element* aElement, + Element* aOriginalElement, bool& aForceFormat, nsAString& aStr) { // We don't output a separate end tag for empty element aForceFormat = false; - // XXXbz this is a bit messed up, but by now we don't have our fixed-up - // version of aElement anymore. Let's hope fixup never changes the localName - // or namespace... - return ElementNeedsSeparateEndTag(aElement, aElement); + return ElementNeedsSeparateEndTag(aElement, aOriginalElement); } bool diff --git a/dom/base/nsXMLContentSerializer.h b/dom/base/nsXMLContentSerializer.h index 941acb179..2f76b0892 100644 --- a/dom/base/nsXMLContentSerializer.h +++ b/dom/base/nsXMLContentSerializer.h @@ -59,6 +59,7 @@ class nsXMLContentSerializer : public nsIContentSerializer { nsAString& aStr) override; NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement, + mozilla::dom::Element* aOriginalElement, nsAString& aStr) override; NS_IMETHOD Flush(nsAString& aStr) override { return NS_OK; } @@ -263,6 +264,7 @@ class nsXMLContentSerializer : public nsIContentSerializer { * @return boolean true if the element can be output */ virtual bool CheckElementEnd(mozilla::dom::Element* aElement, + mozilla::dom::Element* aOriginalElement, bool& aForceFormat, nsAString& aStr); diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index a26fc4422..b244d4d2a 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1968,8 +1968,6 @@ const js::ObjectOps sInterfaceObjectClassObjectOps = { nullptr, /* setProperty */ nullptr, /* getOwnPropertyDescriptor */ nullptr, /* deleteProperty */ - nullptr, /* watch */ - nullptr, /* unwatch */ nullptr, /* getElements */ nullptr, /* enumerate */ InterfaceObjectToString, /* funToString */ @@ -2978,42 +2976,6 @@ ConvertExceptionToPromise(JSContext* cx, JSObject* promiseScope, JS::MutableHandle<JS::Value> rval) { -#ifndef SPIDERMONKEY_PROMISE - GlobalObject global(cx, promiseScope); - if (global.Failed()) { - return false; - } - - 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); - - nsCOMPtr<nsIGlobalObject> globalObj = - do_QueryInterface(global.GetAsSupports()); - if (!globalObj) { - ErrorResult rv; - rv.Throw(NS_ERROR_UNEXPECTED); - return !rv.MaybeSetPendingException(cx); - } - - ErrorResult rv; - RefPtr<Promise> promise = Promise::Reject(globalObj, cx, exn, rv); - if (rv.MaybeSetPendingException(cx)) { - // We just give up. We put the exception from the ErrorResult on - // the JSContext just to make sure to not leak memory on the - // ErrorResult, but now just put the original exception back. - JS_SetPendingException(cx, exn); - return false; - } - - return GetOrCreateDOMReflector(cx, promise, rval); -#else // SPIDERMONKEY_PROMISE { JSAutoCompartment ac(cx, promiseScope); @@ -3039,7 +3001,6 @@ ConvertExceptionToPromise(JSContext* cx, // 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/Bindings.conf b/dom/bindings/Bindings.conf index 6f9733c5f..b00af2085 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -694,10 +694,6 @@ DOMInterfaces = { 'headerFile': 'nsGeolocation.h' }, -'Promise': { - 'implicitJSContext': [ 'then', 'catch' ], -}, - 'PromiseDebugging': { 'concrete': False, }, diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index d7d700a96..6b23e8225 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1135,6 +1135,12 @@ class CGHeaders(CGWrapper): declareIncludes.add("mozilla/dom/Date.h") else: bindingHeaders.add("mozilla/dom/Date.h") + elif unrolled.isPromise(): + # See comment in the isInterface() case for why we add + # Promise.h to headerSet, not bindingHeaders. + headerSet.add("mozilla/dom/Promise.h") + # We need ToJSValue to do the Promise to JS conversion. + bindingHeaders.add("mozilla/dom/ToJSValue.h") elif unrolled.isInterface(): if unrolled.isSpiderMonkeyInterface(): bindingHeaders.add("jsfriendapi.h") @@ -1352,7 +1358,11 @@ def UnionTypes(unionTypes, config): headers.add("mozilla/dom/Nullable.h") isSequence = f.isSequence() f = f.unroll() - if f.isInterface(): + if f.isPromise(): + headers.add("mozilla/dom/Promise.h") + # We need ToJSValue to do the Promise to JS conversion. + headers.add("mozilla/dom/ToJSValue.h") + elif f.isInterface(): if f.isSpiderMonkeyInterface(): headers.add("jsfriendapi.h") headers.add("mozilla/dom/TypedArray.h") @@ -1434,7 +1444,11 @@ def UnionConversions(unionTypes, config): def addHeadersForType(f): f = f.unroll() - if f.isInterface(): + if f.isPromise(): + headers.add("mozilla/dom/Promise.h") + # We need ToJSValue to do the Promise to JS conversion. + headers.add("mozilla/dom/ToJSValue.h") + elif f.isInterface(): if f.isSpiderMonkeyInterface(): headers.add("jsfriendapi.h") headers.add("mozilla/dom/TypedArray.h") @@ -3052,23 +3066,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): else: unforgeableHolderSetup = None - 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))); - if (!JS_DefinePropertyById(aCx, promiseConstructor, species, JS::UndefinedHandleValue, - JSPROP_SHARED, Promise::PromiseSpecies, nullptr)) { - $*{failureCode} - } - #endif // SPIDERMONKEY_PROMISE - """, - failureCode=failureCode)) - else: - speciesSetup = None - if (self.descriptor.interface.isOnGlobalProtoChain() and needInterfacePrototypeObject): makeProtoPrototypeImmutable = CGGeneric(fill( @@ -3094,7 +3091,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): return CGList( [getParentProto, getConstructorProto, initIds, prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup, - speciesSetup, makeProtoPrototypeImmutable], + makeProtoPrototypeImmutable], "\n").define() @@ -5244,6 +5241,139 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, holderArgs=holderArgs, dealWithOptional=isOptional and (not nullable or isOwningUnion)) + if type.isPromise(): + assert not type.nullable() + assert defaultValue is None + + # We always have to hold a strong ref to Promise here, because + # Promise::resolve returns an addrefed thing. + argIsPointer = isCallbackReturnValue + if argIsPointer: + declType = CGGeneric("RefPtr<Promise>") + else: + declType = CGGeneric("OwningNonNull<Promise>") + + # Per spec, what we're supposed to do is take the original + # Promise.resolve and call it with the original Promise as this + # value to make a Promise out of whatever value we actually have + # here. The question is which global we should use. There are + # several cases to consider: + # + # 1) Normal call to API with a Promise argument. This is a case the + # spec covers, and we should be using the current Realm's + # Promise. That means the current compartment. + # 2) Call to API with a Promise argument over Xrays. In practice, + # this sort of thing seems to be used for giving an API + # implementation a way to wait for conclusion of an asyc + # operation, _not_ to expose the Promise to content code. So we + # probably want to allow callers to use such an API in a + # "natural" way, by passing chrome-side promises; indeed, that + # may be all that the caller has to represent their async + # operation. That means we really need to do the + # Promise.resolve() in the caller (chrome) compartment: if we do + # it in the content compartment, we will try to call .then() on + # the chrome promise while in the content compartment, which will + # throw and we'll just get a rejected Promise. Note that this is + # also the reason why a caller who has a chrome Promise + # representing an async operation can't itself convert it to a + # content-side Promise (at least not without some serious + # gyrations). + # 3) Promise return value from a callback or callback interface. + # Per spec, this should use the Realm of the callback object. In + # our case, that's the compartment of the underlying callback, + # not the current compartment (which may be the compartment of + # some cross-compartment wrapper around said callback). + # 4) Return value from a JS-implemented interface. In this case we + # have a problem. Our current compartment is the compartment of + # the JS implementation. But if the JS implementation returned + # a page-side Promise (which is a totally sane thing to do, and + # in fact the right thing to do given that this return value is + # going right to content script) then we don't want to + # Promise.resolve with our current compartment Promise, because + # that will wrap it up in a chrome-side Promise, which is + # decidedly _not_ what's desired here. So in that case we + # should really unwrap the return value and use the global of + # the result. CheckedUnwrap should be good enough for that; if + # it fails, then we're failing unwrap while in a + # system-privileged compartment, so presumably we have a dead + # object wrapper. Just error out. Do NOT fall back to using + # the current compartment instead: that will return a + # system-privileged rejected (because getting .then inside + # resolve() failed) Promise to the caller, which they won't be + # able to touch. That's not helpful. If we error out, on the + # other hand, they will get a content-side rejected promise. + # Same thing if the value returned is not even an object. + if isCallbackReturnValue == "JSImpl": + # Case 4 above. Note that globalObj defaults to the current + # compartment global. Note that we don't use $*{exceptionCode} + # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED) + # which we don't really want here. + assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n" + getPromiseGlobal = fill( + """ + if (!$${val}.isObject()) { + aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); + return nullptr; + } + JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject()); + if (!unwrappedVal) { + // A slight lie, but not much of one, for a dead object wrapper. + aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); + return nullptr; + } + globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal); + """, + sourceDescription=sourceDescription) + elif isCallbackReturnValue == "Callback": + getPromiseGlobal = dedent( + """ + // We basically want our entry global here. Play it safe + // and use GetEntryGlobal() to get it, with whatever + // principal-clamping it ends up doing. + globalObj = GetEntryGlobal()->GetGlobalJSObject(); + """) + else: + getPromiseGlobal = "" + + templateBody = fill( + """ + { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment, + // etc. + + JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx)); + $*{getPromiseGlobal} + JSAutoCompartment ac(cx, globalObj); + GlobalObject promiseGlobal(cx, globalObj); + if (promiseGlobal.Failed()) { + $*{exceptionCode} + } + + JS::Rooted<JS::Value> valueToResolve(cx, $${val}); + if (!JS_WrapValue(cx, &valueToResolve)) { + $*{exceptionCode} + } + binding_detail::FastErrorResult promiseRv; + 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} + } + } + """, + getPromiseGlobal=getPromiseGlobal, + exceptionCode=exceptionCode) + + return JSToNativeConversionInfo(templateBody, + declType=declType, + dealWithOptional=isOptional) + if type.isGeckoInterface(): assert not isEnforceRange and not isClamp @@ -5282,12 +5412,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, # Also, callback return values always end up addrefing anyway, so there # is no point trying to avoid it here and it makes other things simpler # since we can assume the return value is a strong ref. - # - # Finally, promises need to hold a strong ref because that's what - # Promise.resolve returns. assert not descriptor.interface.isCallback() - isPromise = descriptor.interface.identifier.name == "Promise" - forceOwningType = isMember or isCallbackReturnValue or isPromise + forceOwningType = isMember or isCallbackReturnValue typeName = descriptor.nativeType typePtr = typeName + "*" @@ -5315,143 +5441,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if forceOwningType: templateBody += 'static_assert(IsRefcounted<%s>::value, "We can only store refcounted classes.");' % typeName - if isPromise: - # Per spec, what we're supposed to do is take the original - # Promise.resolve and call it with the original Promise as this - # value to make a Promise out of whatever value we actually have - # here. The question is which global we should use. There are - # several cases to consider: - # - # 1) Normal call to API with a Promise argument. This is a case the - # spec covers, and we should be using the current Realm's - # Promise. That means the current compartment. - # 2) Call to API with a Promise argument over Xrays. In practice, - # this sort of thing seems to be used for giving an API - # implementation a way to wait for conclusion of an asyc - # operation, _not_ to expose the Promise to content code. So we - # probably want to allow callers to use such an API in a - # "natural" way, by passing chrome-side promises; indeed, that - # may be all that the caller has to represent their async - # operation. That means we really need to do the - # Promise.resolve() in the caller (chrome) compartment: if we do - # it in the content compartment, we will try to call .then() on - # the chrome promise while in the content compartment, which will - # throw and we'll just get a rejected Promise. Note that this is - # also the reason why a caller who has a chrome Promise - # representing an async operation can't itself convert it to a - # content-side Promise (at least not without some serious - # gyrations). - # 3) Promise return value from a callback or callback interface. - # This is in theory a case the spec covers but in practice it - # really doesn't define behavior here because it doesn't define - # what Realm we're in after the callback returns, which is when - # the argument conversion happens. We will use the current - # compartment, which is the compartment of the callable (which - # may itself be a cross-compartment wrapper itself), which makes - # as much sense as anything else. In practice, such an API would - # once again be providing a Promise to signal completion of an - # operation, which would then not be exposed to anyone other than - # our own implementation code. - # 4) Return value from a JS-implemented interface. In this case we - # have a problem. Our current compartment is the compartment of - # the JS implementation. But if the JS implementation returned - # a page-side Promise (which is a totally sane thing to do, and - # in fact the right thing to do given that this return value is - # going right to content script) then we don't want to - # Promise.resolve with our current compartment Promise, because - # that will wrap it up in a chrome-side Promise, which is - # decidedly _not_ what's desired here. So in that case we - # should really unwrap the return value and use the global of - # the result. CheckedUnwrap should be good enough for that; if - # it fails, then we're failing unwrap while in a - # system-privileged compartment, so presumably we have a dead - # object wrapper. Just error out. Do NOT fall back to using - # the current compartment instead: that will return a - # system-privileged rejected (because getting .then inside - # resolve() failed) Promise to the caller, which they won't be - # able to touch. That's not helpful. If we error out, on the - # other hand, they will get a content-side rejected promise. - # Same thing if the value returned is not even an object. - if isCallbackReturnValue == "JSImpl": - # Case 4 above. Note that globalObj defaults to the current - # compartment global. Note that we don't use $*{exceptionCode} - # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED) - # which we don't really want here. - assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n" - getPromiseGlobal = fill( - """ - if (!$${val}.isObject()) { - aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); - return nullptr; - } - JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject()); - if (!unwrappedVal) { - // A slight lie, but not much of one, for a dead object wrapper. - aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); - return nullptr; - } - globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal); - """, - sourceDescription=sourceDescription) - else: - getPromiseGlobal = "" - - templateBody = fill( - """ - { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment, - // etc. - - JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx)); - $*{getPromiseGlobal} - JSAutoCompartment ac(cx, globalObj); - GlobalObject promiseGlobal(cx, globalObj); - if (promiseGlobal.Failed()) { - $*{exceptionCode} - } - - JS::Rooted<JS::Value> valueToResolve(cx, $${val}); - if (!JS_WrapValue(cx, &valueToResolve)) { - $*{exceptionCode} - } - binding_detail::FastErrorResult 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); - if (!promiseCtor) { - $*{exceptionCode} - } - JS::Rooted<JS::Value> resolveThisv(cx, JS::ObjectValue(*promiseCtor)); - JS::Rooted<JS::Value> resolveResult(cx); - Promise::Resolve(promiseGlobal, resolveThisv, valueToResolve, - &resolveResult, promiseRv); - if (promiseRv.MaybeSetPendingException(cx)) { - $*{exceptionCode} - } - nsresult unwrapRv = UNWRAP_OBJECT(Promise, &resolveResult.toObject(), $${declName}); - if (NS_FAILED(unwrapRv)) { // Quite odd - promiseRv.Throw(unwrapRv); - promiseRv.MaybeSetPendingException(cx); - $*{exceptionCode} - } - #endif // SPIDERMONKEY_PROMISE - } - """, - getPromiseGlobal=getPromiseGlobal, - exceptionCode=exceptionCode) - elif not descriptor.interface.isConsequential() and not descriptor.interface.isExternal(): + if (not descriptor.interface.isConsequential() and + not descriptor.interface.isExternal()): if failureCode is not None: templateBody += str(CastableObjectUnwrapper( descriptor, @@ -5491,24 +5482,12 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, # And store our value in ${declName} templateBody += "${declName} = ${holderName};\n" - if isPromise: - if type.nullable(): - codeToSetNull = "${declName} = nullptr;\n" - templateBody = CGIfElseWrapper( - "${val}.isNullOrUndefined()", - CGGeneric(codeToSetNull), - CGGeneric(templateBody)).define() - if isinstance(defaultValue, IDLNullValue): - templateBody = handleDefault(templateBody, codeToSetNull) - else: - assert defaultValue is None - else: - # Just pass failureCode, not onFailureBadType, here, so we'll report - # the thing as not an object as opposed to not implementing whatever - # our interface is. - templateBody = wrapObjectTemplate(templateBody, type, - "${declName} = nullptr;\n", - failureCode) + # Just pass failureCode, not onFailureBadType, here, so we'll report + # the thing as not an object as opposed to not implementing whatever + # our interface is. + templateBody = wrapObjectTemplate(templateBody, type, + "${declName} = nullptr;\n", + failureCode) declType = CGGeneric(declType) if holderType is not None: @@ -6575,6 +6554,19 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode, return (code, False) + if type.isPromise(): + assert not type.nullable() + # The use of ToJSValue here is a bit annoying because the Promise + # version is not inlined, but we can't put an inline version in either + # ToJSValue.h or BindingUtils.h, because Promise.h includes ToJSValue.h + # and that includes BindingUtils.h, so we'd get an include loop if + # either of those headers included Promise.h. Trying to write the + # conversion by hand here is annoying because we'd have to handle + # the various RefPtr, rawptr, NonNull, etc. cases, which ToJSValue will + # already handle for us, so we just use the function call. + return (wrapAndSetPtr("ToJSValue(cx, %s, ${jsvalHandle})" % result), + False) + if type.isGeckoInterface() and not type.isCallbackInterface(): descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name) if type.nullable(): @@ -6589,14 +6581,6 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode, wrapMethod = "GetOrCreateDOMReflector" wrapArgs = "cx, %s, ${jsvalHandle}" % result else: - # 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 if isConstructorRetval: @@ -6912,14 +6896,17 @@ def getRetvalDeclarationForType(returnType, descriptorProvider, if returnType.nullable(): result = CGTemplatedType("Nullable", result) return result, None, None, None, None - if returnType.isGeckoInterface(): - result = CGGeneric(descriptorProvider.getDescriptor( - returnType.unroll().inner.identifier.name).nativeType) - conversion = None + if returnType.isGeckoInterface() or returnType.isPromise(): + if returnType.isGeckoInterface(): + typeName = descriptorProvider.getDescriptor( + returnType.unroll().inner.identifier.name).nativeType + else: + typeName = "Promise" if isMember: - result = CGGeneric("StrongPtrForMember<%s>::Type" % result.define()) + conversion = None + result = CGGeneric("StrongPtrForMember<%s>::Type" % typeName) else: - conversion = CGGeneric("StrongOrRawPtr<%s>" % result.define()) + conversion = CGGeneric("StrongOrRawPtr<%s>" % typeName) result = CGGeneric("auto") return result, None, None, None, conversion if returnType.isCallback(): @@ -7084,7 +7071,9 @@ class CGCallGenerator(CGThing): if needsConst(a): arg = CGWrapper(arg, pre="Constify(", post=")") # And convert NonNull<T> to T& - if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or + if (((a.type.isGeckoInterface() or a.type.isCallback() or + a.type.isPromise()) and + not a.type.nullable()) or a.type.isDOMString()): arg = CGWrapper(arg, pre="NonNullHelper(", post=")") args.append(arg) @@ -7208,6 +7197,9 @@ class CGCallGenerator(CGThing): def getUnionMemberName(type): + # Promises can't be in unions, because they're not distinguishable + # from anything else. + assert not type.isPromise() if type.isGeckoInterface(): return type.inner.identifier.name if type.isEnum(): @@ -7337,8 +7329,9 @@ def wrapTypeIntoCurrentCompartment(type, value, isMember=True): return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None if (type.isString() or type.isPrimitive() or type.isEnum() or - type.isGeckoInterface() or type.isCallback() or type.isDate()): - # All of these don't need wrapping + type.isGeckoInterface() or type.isCallback() or type.isDate() or + type.isPromise()): + # All of these don't need wrapping. return None raise TypeError("Unknown type; we don't know how to wrap it in constructor " @@ -7476,58 +7469,12 @@ class CGPerSignatureCall(CGThing): if needsCx: argsPre.append("cx") - # Hack for making Promise.prototype.then work well over Xrays. - if (not idlNode.isStatic() and - descriptor.name == "Promise" and - idlNode.isMethod() and - idlNode.identifier.name == "then"): - cgThings.append(CGGeneric(dedent( - """ - JS::Rooted<JSObject*> calleeGlobal(cx, xpc::XrayAwareCalleeGlobal(&args.callee())); - """))) - argsPre.append("calleeGlobal") - needsUnwrap = False argsPost = [] if isConstructor: - 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") - # Also, we do not want to enter the content compartment when the - # Promise constructor is called via Xrays, because we want to - # create our callback functions that we will hand to our caller - # in the Xray compartment. The reason we want to do that is the - # following situation, over Xrays: - # - # contentWindow.Promise.race([Promise.resolve(5)]) - # - # Ideally this would work. Internally, race() does a - # contentWindow.Promise.resolve() on everything in the array. - # Per spec, to support subclassing, - # contentWindow.Promise.resolve has to do: - # - # var resolve, reject; - # var p = new contentWindow.Promise(function(a, b) { - # resolve = a; - # reject = b; - # }); - # resolve(arg); - # return p; - # - # where "arg" is, in this case, the chrome-side return value of - # Promise.resolve(5). But if the "resolve" function in that - # case were created in the content compartment, then calling it - # would wrap "arg" in an opaque wrapper, and that function tries - # to get .then off the argument, which would throw. So we need - # to create the "resolve" function in the chrome compartment, - # and hence want to be running the entire Promise constructor - # (which creates that function) in the chrome compartment in - # this case. So don't set needsUnwrap here. - else: - needsUnwrap = True - needsUnwrappedVar = False - unwrappedVar = "obj" + needsUnwrap = True + needsUnwrappedVar = False + unwrappedVar = "obj" elif descriptor.interface.isJSImplemented(): if not idlNode.isStatic(): needsUnwrap = True @@ -7540,11 +7487,6 @@ class CGPerSignatureCall(CGThing): needsUnwrappedVar = True argsPre.append("unwrappedObj ? *unwrappedObj : obj") - if idlNode.isStatic() and not isConstructor and descriptor.name == "Promise": - # Hack for Promise for now: pass in the "this" value to - # Promise static methods. - argsPre.append("args.thisv()") - if needsUnwrap and needsUnwrappedVar: # We cannot assign into obj because it's a Handle, not a # MutableHandle, so we need a separate Rooted. @@ -7687,7 +7629,8 @@ class CGPerSignatureCall(CGThing): returnsNewObject = memberReturnsNewObject(self.idlNode) if (returnsNewObject and - self.returnType.isGeckoInterface()): + (self.returnType.isGeckoInterface() or + self.returnType.isPromise())): wrapCode += dedent( """ static_assert(!IsPointer<decltype(result)>::value, @@ -9493,6 +9436,8 @@ class CGMemberJITInfo(CGThing): return "JSVAL_TYPE_OBJECT" if t.isRecord(): return "JSVAL_TYPE_OBJECT" + if t.isPromise(): + return "JSVAL_TYPE_OBJECT" if t.isGeckoInterface(): return "JSVAL_TYPE_OBJECT" if t.isString(): @@ -9566,6 +9511,8 @@ class CGMemberJITInfo(CGThing): return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner) if t.isSequence(): return "JSJitInfo::Object" + if t.isPromise(): + return "JSJitInfo::Object" if t.isGeckoInterface(): return "JSJitInfo::Object" if t.isString(): @@ -9752,6 +9699,10 @@ def getUnionAccessorSignatureType(type, descriptorProvider): # Flat member types have already unwrapped nullables. assert not type.nullable() + # Promise types can never appear in unions, because Promise is not + # distinguishable from anything. + assert not type.isPromise() + if type.isSequence() or type.isRecord(): if type.isSequence(): wrapperType = "Sequence" @@ -13169,9 +13120,9 @@ class CGDictionary(CGThing): # continues to match the list in test_Object.prototype_props.html if (member.identifier.name in ["constructor", "toSource", "toString", "toLocaleString", "valueOf", - "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", - "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", - "__lookupGetter__", "__lookupSetter__", "__proto__"]): + "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", + "__defineGetter__", "__defineSetter__", "__lookupGetter__", + "__lookupSetter__", "__proto__"]): raise TypeError("'%s' member of %s dictionary shadows " "a property of Object.prototype, and Xrays to " "Object can't handle that.\n" @@ -13660,6 +13611,8 @@ class ForwardDeclarationBuilder: # Note: Spidermonkey interfaces are typedefs, so can't be # forward-declared + elif t.isPromise(): + self.addInMozillaDom("Promise") elif t.isCallback(): self.addInMozillaDom(t.callback.identifier.name) elif t.isDictionary(): @@ -14114,10 +14067,13 @@ class CGNativeMember(ClassMethod): else: defaultValue = "%s(0)" % enumName return enumName, defaultValue, "return ${declName};\n" - if type.isGeckoInterface(): - iface = type.unroll().inner - result = CGGeneric(self.descriptorProvider.getDescriptor( - iface.identifier.name).prettyNativeType) + if type.isGeckoInterface() or type.isPromise(): + if type.isGeckoInterface(): + iface = type.unroll().inner + result = CGGeneric(self.descriptorProvider.getDescriptor( + iface.identifier.name).prettyNativeType) + else: + result = CGGeneric("Promise") if self.resultAlreadyAddRefed: if isMember: holder = "RefPtr" @@ -14320,11 +14276,18 @@ class CGNativeMember(ClassMethod): # auto-wrapping in Nullable return CGUnionStruct.unionTypeDecl(type, isMember), True, False + if type.isPromise(): + assert not type.nullable() + if optional or isMember: + typeDecl = "OwningNonNull<Promise>" + else: + typeDecl = "Promise&" + return (typeDecl, False, False) + if type.isGeckoInterface() and not type.isCallbackInterface(): iface = type.unroll().inner argIsPointer = type.nullable() or iface.isExternal() - forceOwningType = (iface.isCallback() or isMember or - iface.identifier.name == "Promise") + forceOwningType = (iface.isCallback() or isMember) if argIsPointer: if (optional or isMember) and forceOwningType: typeDecl = "RefPtr<%s>" @@ -16958,7 +16921,10 @@ class CGEventGetter(CGNativeMember): def getMethodBody(self): type = self.member.type memberName = CGDictionary.makeMemberName(self.member.identifier.name) - if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface(): + if ((type.isPrimitive() and type.tag() in builtinNames) or + type.isEnum() or + type.isPromise() or + type.isGeckoInterface()): return "return " + memberName + ";\n" if type.isDOMString() or type.isByteString() or type.isUSVString(): return "aRetVal = " + memberName + ";\n" @@ -17369,6 +17335,8 @@ class CGEventClass(CGBindingImplClass): nativeType = CGGeneric("nsString") elif type.isByteString(): nativeType = CGGeneric("nsCString") + elif type.isPromise(): + nativeType = CGGeneric("RefPtr<Promise>") elif type.isGeckoInterface(): iface = type.unroll().inner nativeType = self.descriptor.getDescriptor( @@ -17393,7 +17361,7 @@ class CGEventClass(CGBindingImplClass): innerType = type.inner if (not innerType.isPrimitive() and not innerType.isEnum() and not innerType.isDOMString() and not innerType.isByteString() and - not innerType.isGeckoInterface()): + not innerType.isPromise() and not innerType.isGeckoInterface()): raise TypeError("Don't know how to properly manage GC/CC for " "event member of type %s" % type) diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index f80c19c33..a56f2f2fd 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -468,13 +468,6 @@ 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() self.name = interface.identifier.name diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index 23f0abd88..49281c1c2 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -275,19 +275,6 @@ DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy, } bool -BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, - JS::Handle<JSObject*> callable) const -{ - return js::WatchGuts(cx, proxy, id, callable); -} - -bool -BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const -{ - return js::UnwatchGuts(cx, proxy, id); -} - -bool BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy, JS::AutoIdVector& props) const diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index 1781649cc..e3e151b7a 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -72,11 +72,6 @@ public: virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy, JS::AutoIdVector &props) const override; - bool watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, - JS::Handle<JSObject*> callable) const override; - bool unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, - JS::Handle<jsid> id) const override; - protected: // Hook for subclasses to implement shared ownPropertyKeys()/keys() // functionality. The "flags" argument is either JSITER_OWNONLY (for keys()) diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg index 142ccfdd6..c47f75875 100644 --- a/dom/bindings/Errors.msg +++ b/dom/bindings/Errors.msg @@ -84,11 +84,6 @@ MSG_DEF(MSG_NOTIFICATION_PERMISSION_DENIED, 0, JSEXN_TYPEERR, "Permission to sho MSG_DEF(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER, 0, JSEXN_TYPEERR, "Notification constructor cannot be used in ServiceWorkerGlobalScope. Use registration.showNotification() instead.") MSG_DEF(MSG_INVALID_SCOPE, 2, JSEXN_TYPEERR, "Invalid scope trying to resolve {0} with base URL {1}.") MSG_DEF(MSG_INVALID_KEYFRAME_OFFSETS, 0, JSEXN_TYPEERR, "Keyframes with specified offsets must be in order and all be in the range [0, 1].") -MSG_DEF(MSG_ILLEGAL_PROMISE_CONSTRUCTOR, 0, JSEXN_TYPEERR, "Non-constructor value passed to NewPromiseCapability.") -MSG_DEF(MSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCapabilitiesExecutor function already invoked with non-undefined values.") -MSG_DEF(MSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.") -MSG_DEF(MSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.") -MSG_DEF(MSG_PROMISE_ARG_NOT_ITERABLE, 1, JSEXN_TYPEERR, "{0} is not iterable") MSG_DEF(MSG_IS_NOT_PROMISE, 1, JSEXN_TYPEERR, "{0} is not a Promise") MSG_DEF(MSG_SW_INSTALL_ERROR, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} encountered an error during installation.") MSG_DEF(MSG_SW_SCRIPT_THREW, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} threw an exception during script evaluation.") diff --git a/dom/bindings/ToJSValue.cpp b/dom/bindings/ToJSValue.cpp index d84428fb3..7afff41e2 100644 --- a/dom/bindings/ToJSValue.cpp +++ b/dom/bindings/ToJSValue.cpp @@ -7,9 +7,7 @@ #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" @@ -66,15 +64,13 @@ 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; + return MaybeWrapObjectValue(aCx, aValue); } -#endif // SPIDERMONKEY_PROMISE } // namespace dom } // namespace mozilla diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h index 2021c0b4c..76e91c7bc 100644 --- a/dom/bindings/ToJSValue.h +++ b/dom/bindings/ToJSValue.h @@ -306,13 +306,11 @@ ToJSValue(JSContext* aCx, return ToJSValue(aCx, *aArgument, aValue); } -#ifdef SPIDERMONKEY_PROMISE // Accept Promise objects, which need special handling. MOZ_MUST_USE bool ToJSValue(JSContext* aCx, Promise& aArgument, JS::MutableHandle<JS::Value> aValue); -#endif // SPIDERMONKEY_PROMISE // Accept arrays of other things we accept template <typename T> diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h index a86abcd9d..75313e255 100644 --- a/dom/bindings/TypedArray.h +++ b/dom/bindings/TypedArray.h @@ -344,7 +344,7 @@ typedef TypedArray<uint8_t, js::UnwrapSharedArrayBuffer, JS_GetSharedArrayBuffer // A class for converting an nsTArray to a TypedArray // Note: A TypedArrayCreator must not outlive the nsTArray it was created from. // So this is best used to pass from things that understand nsTArray to -// things that understand TypedArray, as with Promise::ArgumentToJSValue. +// things that understand TypedArray, as with ToJSValue. template<typename TypedArrayType> class TypedArrayCreator { diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 8c32a8738..4f602365b 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -1597,11 +1597,7 @@ class IDLInterface(IDLInterfaceOrNamespace): args = attr.args() if attr.hasArgs() else [] - if self.identifier.name == "Promise": - promiseType = BuiltinTypes[IDLBuiltinType.Types.any] - else: - promiseType = None - retType = IDLWrapperType(self.location, self, promiseType) + retType = IDLWrapperType(self.location, self) if identifier == "Constructor" or identifier == "ChromeConstructor": name = "constructor" @@ -1988,7 +1984,8 @@ class IDLType(IDLObject): 'callback', 'union', 'sequence', - 'record' + 'record', + 'promise' ) def __init__(self, location, name): @@ -2142,9 +2139,8 @@ class IDLUnresolvedType(IDLType): Unresolved types are interface types """ - def __init__(self, location, name, promiseInnerType=None): + def __init__(self, location, name): IDLType.__init__(self, location, name) - self._promiseInnerType = promiseInnerType def isComplete(self): return False @@ -2171,11 +2167,8 @@ class IDLUnresolvedType(IDLType): assert self.name.name == obj.identifier.name return IDLCallbackType(self.location, obj) - if self._promiseInnerType and not self._promiseInnerType.isComplete(): - self._promiseInnerType = self._promiseInnerType.complete(scope) - name = self.name.resolve(scope, None) - return IDLWrapperType(self.location, obj, self._promiseInnerType) + return IDLWrapperType(self.location, obj) def isDistinguishableFrom(self, other): raise TypeError("Can't tell whether an unresolved type is or is not " @@ -2285,7 +2278,9 @@ class IDLNullableType(IDLParameterizedType): return self.inner.isInterface() def isPromise(self): - return self.inner.isPromise() + # There is no such thing as a nullable Promise. + assert not self.inner.isPromise() + return False def isCallbackInterface(self): return self.inner.isCallbackInterface() @@ -2698,13 +2693,11 @@ class IDLTypedef(IDLObjectWithIdentifier): class IDLWrapperType(IDLType): - def __init__(self, location, inner, promiseInnerType=None): + def __init__(self, location, inner): IDLType.__init__(self, location, inner.identifier.name) self.inner = inner self._identifier = inner.identifier self.builtin = False - assert not promiseInnerType or inner.identifier.name == "Promise" - self._promiseInnerType = promiseInnerType def __eq__(self, other): return (isinstance(other, IDLWrapperType) and @@ -2754,14 +2747,6 @@ class IDLWrapperType(IDLType): def isEnum(self): return isinstance(self.inner, IDLEnum) - def isPromise(self): - return (isinstance(self.inner, IDLInterface) and - self.inner.identifier.name == "Promise") - - def promiseInnerType(self): - assert self.isPromise() - return self._promiseInnerType - def isSerializable(self): if self.isInterface(): if self.inner.isExternal(): @@ -2793,8 +2778,6 @@ class IDLWrapperType(IDLType): assert False def isDistinguishableFrom(self, other): - if self.isPromise(): - return False if other.isPromise(): return False if other.isUnion(): @@ -2841,10 +2824,6 @@ class IDLWrapperType(IDLType): # Let's say true, though ideally we'd only do this when # exposureSet contains the primary global's name. return True - if (self.isPromise() and - # Check the internal type - not self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)): - return False return iface.exposureSet.issuperset(exposureSet) def _getDependentObjects(self): @@ -2872,6 +2851,45 @@ class IDLWrapperType(IDLType): return set() +class IDLPromiseType(IDLParameterizedType): + def __init__(self, location, innerType): + IDLParameterizedType.__init__(self, location, "Promise", innerType) + + def __eq__(self, other): + return (isinstance(other, IDLPromiseType) and + self.promiseInnerType() == other.promiseInnerType()) + + def __str__(self): + return self.inner.__str__() + "Promise" + + def isPromise(self): + return True + + def promiseInnerType(self): + return self.inner + + def tag(self): + return IDLType.Tags.promise + + def complete(self, scope): + self.inner = self.promiseInnerType().complete(scope) + return self + + def unroll(self): + # We do not unroll our inner. Just stop at ourselves. That + # lets us add headers for both ourselves and our inner as + # needed. + return self + + def isDistinguishableFrom(self, other): + # Promises are not distinguishable from anything. + return False + + def isExposedInAllOf(self, exposureSet): + # Check the internal type + return self.promiseInnerType().unroll().isExposedInAllOf(exposureSet) + + class IDLBuiltinType(IDLType): Types = enum( @@ -3990,7 +4008,9 @@ class IDLAttribute(IDLInterfaceMember): raise WebIDLError("An attribute with [PutForwards] must have an " "interface type as its type", [self.location]) - if not self.type.isInterface() and self.getExtendedAttribute("SameObject"): + if (not self.type.isInterface() and + not self.type.isPromise() and + self.getExtendedAttribute("SameObject")): raise WebIDLError("An attribute with [SameObject] must have an " "interface type as its type", [self.location]) @@ -6394,17 +6414,13 @@ class Parser(Tokenizer): type = IDLSequenceType(self.getLocation(p, 1), innerType) p[0] = self.handleNullable(type, p[5]) - # Note: Promise<void> is allowed, so we want to parametrize on - # ReturnType, not Type. Also, we want this to end up picking up - # the Promise interface for now, hence the games with IDLUnresolvedType. + # Note: Promise<void> is allowed, so we want to parameterize on + # ReturnType, not Type. Promise types can't be null, hence no "Null" in there. def p_NonAnyTypePromiseType(self, p): """ - NonAnyType : PROMISE LT ReturnType GT Null + NonAnyType : PROMISE LT ReturnType GT """ - innerType = p[3] - promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise") - type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3]) - p[0] = self.handleNullable(type, p[5]) + p[0] = IDLPromiseType(self.getLocation(p, 1), p[3]) def p_NonAnyTypeRecordType(self, p): """ @@ -6423,7 +6439,7 @@ class Parser(Tokenizer): if p[1].name == "Promise": raise WebIDLError("Promise used without saying what it's " - "parametrized over", + "parameterized over", [self.getLocation(p, 1)]) type = None diff --git a/dom/bindings/parser/tests/test_distinguishability.py b/dom/bindings/parser/tests/test_distinguishability.py index d7780c1ff..ac515a01d 100644 --- a/dom/bindings/parser/tests/test_distinguishability.py +++ b/dom/bindings/parser/tests/test_distinguishability.py @@ -263,7 +263,6 @@ def WebIDLTest(parser, harness): callback Callback2 = long(short arg); dictionary Dict {}; dictionary Dict2 {}; - interface _Promise {}; interface TestInterface {%s }; """ diff --git a/dom/bindings/parser/tests/test_promise.py b/dom/bindings/parser/tests/test_promise.py index 55bc07680..091381fab 100644 --- a/dom/bindings/parser/tests/test_promise.py +++ b/dom/bindings/parser/tests/test_promise.py @@ -2,7 +2,6 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - interface _Promise {}; interface A { legacycaller Promise<any> foo(); }; @@ -18,7 +17,6 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - interface _Promise {}; interface A { Promise<any> foo(); long foo(long arg); @@ -35,7 +33,6 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - interface _Promise {}; interface A { long foo(long arg); Promise<any> foo(); @@ -50,7 +47,6 @@ def WebIDLTest(parser, harness): parser = parser.reset() parser.parse(""" - interface _Promise {}; interface A { Promise<any> foo(); Promise<any> foo(long arg); diff --git a/dom/bindings/test/test_Object.prototype_props.html b/dom/bindings/test/test_Object.prototype_props.html index 03147eb03..3ab27c5e4 100644 --- a/dom/bindings/test/test_Object.prototype_props.html +++ b/dom/bindings/test/test_Object.prototype_props.html @@ -11,9 +11,9 @@ test(function() { // Codegen.py's CGDictionary.getMemberDefinition method. var expected = [ "constructor", "toSource", "toString", "toLocaleString", "valueOf", - "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", - "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", - "__lookupGetter__", "__lookupSetter__", "__proto__" + "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", + "__defineGetter__", "__defineSetter__", "__lookupGetter__", + "__lookupSetter__", "__proto__" ]; assert_array_equals(props.sort(), expected.sort()); }, "Own properties of Object.prototype"); diff --git a/dom/canvas/WebGLContextBuffers.cpp b/dom/canvas/WebGLContextBuffers.cpp index af506c01c..f53f9d7d7 100644 --- a/dom/canvas/WebGLContextBuffers.cpp +++ b/dom/canvas/WebGLContextBuffers.cpp @@ -9,6 +9,8 @@ #include "WebGLBuffer.h" #include "WebGLVertexArray.h" +#include "mozilla/CheckedInt.h" + namespace mozilla { WebGLRefPtr<WebGLBuffer>* @@ -345,6 +347,16 @@ WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage) //// + const auto checkedSize = CheckedInt<size_t>(size); + if (!checkedSize.isValid()) + return ErrorOutOfMemory("%s: Size too large for platform.", funcName); + +#if defined(XP_MACOSX) + if (gl->WorkAroundDriverBugs() && size > 1200000000) { + return ErrorOutOfMemory("Allocations larger than 1200000000 fail on MacOS."); + } +#endif + const UniqueBuffer zeroBuffer(calloc(size, 1)); if (!zeroBuffer) return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName); diff --git a/dom/canvas/WebGLShader.cpp b/dom/canvas/WebGLShader.cpp index 37380f1e0..69ca03fc4 100644 --- a/dom/canvas/WebGLShader.cpp +++ b/dom/canvas/WebGLShader.cpp @@ -168,16 +168,6 @@ WebGLShader::ShaderSource(const nsAString& source) // 7-bit ASCII range, so we can skip the NS_IsAscii() check. const NS_LossyConvertUTF16toASCII sourceCString(cleanSource); - if (mContext->gl->WorkAroundDriverBugs()) { - const size_t maxSourceLength = 0x3ffff; - if (sourceCString.Length() > maxSourceLength) { - mContext->ErrorInvalidValue("shaderSource: Source has more than %d" - " characters. (Driver workaround)", - maxSourceLength); - return; - } - } - if (PR_GetEnv("MOZ_WEBGL_DUMP_SHADERS")) { printf_stderr("////////////////////////////////////////\n"); printf_stderr("// MOZ_WEBGL_DUMP_SHADERS:\n"); diff --git a/dom/canvas/test/reftest/filters/liveness-document-open.html b/dom/canvas/test/reftest/filters/liveness-document-open.html new file mode 100644 index 000000000..b3d76e550 --- /dev/null +++ b/dom/canvas/test/reftest/filters/liveness-document-open.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html lang="en"> + +<title>canvas filters: remove referenced filter element through document.open()</title> + +<body onload="loaded()"> + +<canvas id="canvas" width="10" height="10"></canvas> + +<svg height="0"> + <filter id="filter"> + <feFlood flood-color="red"/> + </filter> +</svg> + +<script> + +function loaded() { + var ctx = document.getElementById('canvas').getContext('2d'); + + ctx.filter = 'url(#filter)'; + ctx.fillRect(0, 0, 10, 10); // do a draw first to work around bug 1287316 + + document.open(); + + // The document.open() call removed #filter from the document. So the filter + // reference should now be invalid, and the rect should be drawn without a + // filter applied, resulting in black. + ctx.fillRect(0, 0, 10, 10); + + try { + var data = ctx.getImageData(0, 0, 1, 1).data; + if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 255) { + // Successfully painted black. + document.write('PASS'); + } else { + // Painted something else, like red. + document.write('FAIL'); + } + } catch (e) { + document.write('getImageData failed'); + } + document.close(); +} + +</script> diff --git a/dom/canvas/test/reftest/filters/reftest.list b/dom/canvas/test/reftest/filters/reftest.list index 983030715..f5d671e4d 100644 --- a/dom/canvas/test/reftest/filters/reftest.list +++ b/dom/canvas/test/reftest/filters/reftest.list @@ -6,6 +6,7 @@ default-preferences pref(canvas.filters.enabled,true) fuzzy-if(azureSkia,1,1500) == global-alpha.html global-alpha-ref.html == global-composite-operation.html global-composite-operation-ref.html == liveness.html ref.html +== liveness-document-open.html data:text/html,PASS == multiple-drop-shadows.html shadow-ref.html == shadow.html shadow-ref.html == subregion-fill-paint.html subregion-ref.html diff --git a/dom/events/test/test_continuous_wheel_events.html b/dom/events/test/test_continuous_wheel_events.html index fc8c69390..e1910afa9 100644 --- a/dom/events/test/test_continuous_wheel_events.html +++ b/dom/events/test/test_continuous_wheel_events.html @@ -9,7 +9,7 @@ </head> <body> <p id="display"></p> -<div id="scrollable" style="font-family:monospace; font-size: 18px; line-height: 1; overflow: auto; width: 200px; height: 200px;"> +<div id="scrollable" style="font-family:'Courier New'; font-size: 18px; line-height: 1; overflow: auto; width: 200px; height: 200px;"> <div id="scrolled" style="font-size: 64px; width: 5000px; height: 5000px;"> Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br> Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br> diff --git a/dom/grid/GridLines.cpp b/dom/grid/GridLines.cpp index fac645c64..898885346 100644 --- a/dom/grid/GridLines.cpp +++ b/dom/grid/GridLines.cpp @@ -90,7 +90,9 @@ GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, for (uint32_t i = aTrackInfo->mStartFragmentTrack; i < aTrackInfo->mEndFragmentTrack + 1; i++) { - uint32_t line1Index = i + 1; + // Since line indexes are 1-based, calculate a 1-based value + // for this track to simplify some calculations. + const uint32_t line1Index = i + 1; startOfNextTrack = (i < aTrackInfo->mEndFragmentTrack) ? aTrackInfo->mPositions[i] : @@ -127,7 +129,8 @@ GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, } } - if (i >= aTrackInfo->mRepeatFirstTrack && + if (i >= (aTrackInfo->mRepeatFirstTrack + + aTrackInfo->mNumLeadingImplicitTracks) && repeatIndex < numRepeatTracks) { numAddedLines += AppendRemovedAutoFits(aTrackInfo, aLineInfo, @@ -139,23 +142,30 @@ GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, RefPtr<GridLine> line = new GridLine(this); mLines.AppendElement(line); + MOZ_ASSERT(line1Index > 0, "line1Index must be positive."); + bool isBeforeFirstExplicit = + (line1Index <= aTrackInfo->mNumLeadingImplicitTracks); + // Calculate an actionable line number for this line, that could be used + // in a css grid property to align a grid item or area at that line. + // For implicit lines that appear before line 1, report a number of 0. + // We can't report negative indexes, because those have a different + // meaning in the css grid spec (negative indexes are negative-1-based + // from the end of the grid decreasing towards the front). + uint32_t lineNumber = isBeforeFirstExplicit ? 0 : + (line1Index - aTrackInfo->mNumLeadingImplicitTracks + numAddedLines); + GridDeclaration lineType = + (isBeforeFirstExplicit || + line1Index > (aTrackInfo->mNumLeadingImplicitTracks + + aTrackInfo->mNumExplicitTracks + 1)) + ? GridDeclaration::Implicit + : GridDeclaration::Explicit; line->SetLineValues( lineNames, nsPresContext::AppUnitsToDoubleCSSPixels(lastTrackEdge), nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack - lastTrackEdge), - line1Index + numAddedLines, - ( - // Implicit if there are no explicit tracks, or if the index - // is before the first explicit track, or after - // a track beyond the last explicit track. - (aTrackInfo->mNumExplicitTracks == 0) || - (i < aTrackInfo->mNumLeadingImplicitTracks) || - (i > aTrackInfo->mNumLeadingImplicitTracks + - aTrackInfo->mNumExplicitTracks) ? - GridDeclaration::Implicit : - GridDeclaration::Explicit - ) + lineNumber, + lineType ); if (i < aTrackInfo->mEndFragmentTrack) { @@ -215,11 +225,13 @@ GridLines::AppendRemovedAutoFits(const ComputedGridTrackInfo* aTrackInfo, RefPtr<GridLine> line = new GridLine(this); mLines.AppendElement(line); + uint32_t lineNumber = aTrackInfo->mRepeatFirstTrack + + aRepeatIndex + 1; line->SetLineValues( aLineNames, nsPresContext::AppUnitsToDoubleCSSPixels(aLastTrackEdge), nsPresContext::AppUnitsToDoubleCSSPixels(0), - aTrackInfo->mRepeatFirstTrack + aRepeatIndex + 1, + lineNumber, GridDeclaration::Explicit ); diff --git a/dom/grid/test/chrome.ini b/dom/grid/test/chrome.ini index 2241cf9eb..169fa9b89 100644 --- a/dom/grid/test/chrome.ini +++ b/dom/grid/test/chrome.ini @@ -2,6 +2,7 @@ [chrome/test_grid_fragmentation.html] [chrome/test_grid_implicit.html] [chrome/test_grid_lines.html] +[chrome/test_grid_line_numbers.html] [chrome/test_grid_object.html] [chrome/test_grid_repeats.html] [chrome/test_grid_tracks.html] diff --git a/dom/grid/test/chrome/test_grid_implicit.html b/dom/grid/test/chrome/test_grid_implicit.html index c7782e0e5..1f7142658 100644 --- a/dom/grid/test/chrome/test_grid_implicit.html +++ b/dom/grid/test/chrome/test_grid_implicit.html @@ -33,6 +33,11 @@ body { grid-template-rows: [areaA-end areaB-start areaC-end] 50px [areaA-start areaB-end areaC-start]; } +.template4 { + grid-template-columns: 100px 50px 100px; + grid-template-rows: 50px; +} + .box { background-color: #444; color: #fff; @@ -50,6 +55,9 @@ body { .d { grid-area: areaD; } +.e { + grid-column: -7 / 5; +} </style> <script> @@ -78,9 +86,12 @@ function runTests() { is(grid.cols.lines[4].type, "implicit", "Grid column line 5 is implicit."); is(grid.cols.lines[5].type, "implicit", "Grid column line 6 is implicit."); - is(grid.rows.lines[0].type, "implicit", "Grid row line 1 is implicit."); - is(grid.rows.lines[1].type, "explicit", "Grid row line 2 is explicit."); - is(grid.rows.lines[3].type, "explicit", "Grid row line 4 is explicit."); + is(grid.rows.lines[0].type, "implicit", "Grid row line 0 is implicit."); + is(grid.rows.lines[0].number, 0, "Grid row line 0 has correct number."); + is(grid.rows.lines[1].type, "explicit", "Grid row line 1 is explicit."); + is(grid.rows.lines[1].number, 1, "Grid row line 1 has correct number."); + is(grid.rows.lines[3].type, "explicit", "Grid row line 3 is explicit."); + is(grid.rows.lines[3].number, 3, "Grid row line 3 has correct number."); // test that row line 1 gets the name forced on it by placement of item B todo_isnot(grid.rows.lines[0].names.indexOf("got-this-name-implicitly"), -1, @@ -221,6 +232,48 @@ function runTests() { } } + // test the fourth grid wrapper + wrapper = document.getElementById("wrapper4"); + grid = wrapper.getGridFragments()[0]; + + // test column and row line counts + is(grid.cols.lines.length, 8, + "Grid.cols.lines property expands properly with implicit columns on both sides." + ); + is(grid.rows.lines.length, 2, + "Grid.rows.lines property is as expected" + ); + + if (grid.cols.lines.length == 8) { + // check that all the lines get correct implict/explicit type and number + let expectedType = [ + "implicit", + "implicit", + "implicit", + "explicit", + "explicit", + "explicit", + "explicit", + "implicit", + ]; + let expectedNumber = [ + 0, + 0, + 0, + 1, + 2, + 3, + 4, + 5, + ]; + + for (let i = 0; i < grid.cols.lines.length; i++) { + let line = grid.cols.lines[i]; + is(line.type, expectedType[i], "Line index " + i + " has expected type."); + is(line.number, expectedNumber[i], "Line index " + i + " has expected number."); + } + } + SimpleTest.finish(); } </script> @@ -246,5 +299,9 @@ function runTests() { <div id="boxC" class="box c">C</div> </div> + <div id="wrapper4" class="wrapper template4"> + <div id="boxE" class="box e">E</div> + </div> + </body> </html> diff --git a/dom/grid/test/chrome/test_grid_line_numbers.html b/dom/grid/test/chrome/test_grid_line_numbers.html new file mode 100644 index 000000000..c8e5226b6 --- /dev/null +++ b/dom/grid/test/chrome/test_grid_line_numbers.html @@ -0,0 +1,101 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> +body { + margin: 40px; +} +.wrapper { + display: grid; + grid-gap: 0px; + background-color: #f00; +} +.wrapper > div { + background-color: #444; + color: #fff; +} +.repeatColumns { + width: 600px; + grid-auto-columns: 50px; + grid-template-columns: repeat(auto-fit, 100px); +} +.repeatRows { + height: 600px; + grid-auto-rows: 50px; + grid-template-rows: repeat(auto-fit, 100px); +} +</style> + +<script> +'use strict'; + +SimpleTest.waitForExplicitFinish(); + +function testLines(elementName, lines, expectedValues) { + is(lines.count, expectedValues.count, elementName + " has expected number of lines."); + for (let i = 0; i < lines.length; i++) { + is(lines[i].number, expectedValues[i].number, elementName + " line index " + i + " has expected number."); + } +} + +function runTests() { + let grid; + let lines; + let expectedValues; + + grid = document.getElementById("gridWithColumns").getGridFragments()[0]; + lines = grid.cols.lines; + expectedValues = [ + { "number": 0 }, + { "number": 0 }, + { "number": 1 }, + { "number": 2 }, + { "number": 3 }, + { "number": 4 }, + { "number": 5 }, + { "number": 6 }, + { "number": 7 }, + { "number": 8 }, + ]; + testLines("gridWithColumns", lines, expectedValues); + + grid = document.getElementById("gridWithRows").getGridFragments()[0]; + lines = grid.rows.lines; + expectedValues = [ + { "number": 0 }, + { "number": 0 }, + { "number": 1 }, + { "number": 2 }, + { "number": 3 }, + { "number": 4 }, + { "number": 5 }, + { "number": 6 }, + { "number": 7 }, + { "number": 8 }, + ]; + testLines("gridWithRows", lines, expectedValues); + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + +<div id="gridWithColumns" class="wrapper repeatColumns"> +<div style="grid-column: -9">A</div> +<div style="grid-column: 4">B</div> +<div style="grid-column: 7">C</div> +</div> + +<div id="gridWithRows" class="wrapper repeatRows"> +<div style="grid-row: span 3 / 2">A</div> +<div style="grid-row: 4">B</div> +<div style="grid-row: 5">C</div> +<div style="grid-row: span 2 / 8">D</div> +</div> + +</body> +</html> diff --git a/dom/html/HTMLScriptElement.cpp b/dom/html/HTMLScriptElement.cpp index 94d09c12c..095b9b77d 100644 --- a/dom/html/HTMLScriptElement.cpp +++ b/dom/html/HTMLScriptElement.cpp @@ -218,6 +218,18 @@ HTMLScriptElement::SetAsync(bool aValue, ErrorResult& rv) SetHTMLBoolAttr(nsGkAtoms::async, aValue, rv); } +bool +HTMLScriptElement::NoModule() +{ + return GetBoolAttr(nsGkAtoms::nomodule); +} + +void +HTMLScriptElement::SetNoModule(bool aValue, ErrorResult& aRv) +{ + SetHTMLBoolAttr(nsGkAtoms::nomodule, aValue, aRv); +} + nsresult HTMLScriptElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, const nsAttrValue* aValue, bool aNotify) diff --git a/dom/html/HTMLScriptElement.h b/dom/html/HTMLScriptElement.h index 00628bd6d..19ceb414f 100644 --- a/dom/html/HTMLScriptElement.h +++ b/dom/html/HTMLScriptElement.h @@ -89,6 +89,8 @@ public: } bool Async(); void SetAsync(bool aValue, ErrorResult& rv); + bool NoModule(); + void SetNoModule(bool aValue, ErrorResult& rv); protected: virtual ~HTMLScriptElement(); diff --git a/dom/html/ImageDocument.cpp b/dom/html/ImageDocument.cpp index f83a804be..451d989c3 100644 --- a/dom/html/ImageDocument.cpp +++ b/dom/html/ImageDocument.cpp @@ -659,7 +659,7 @@ ImageDocument::CreateSyntheticDocument() NS_ENSURE_SUCCESS(rv, rv); // Add the image element - Element* body = GetBodyElement(); + RefPtr<Element> body = GetBodyElement(); if (!body) { NS_WARNING("no body on image document!"); return NS_ERROR_FAILURE; diff --git a/dom/html/PluginDocument.cpp b/dom/html/PluginDocument.cpp index 1c923ecc6..f6be8a915 100644 --- a/dom/html/PluginDocument.cpp +++ b/dom/html/PluginDocument.cpp @@ -206,7 +206,7 @@ PluginDocument::CreateSyntheticPluginDocument() NS_ENSURE_SUCCESS(rv, rv); // then attach our plugin - Element* body = GetBodyElement(); + RefPtr<Element> body = GetBodyElement(); if (!body) { NS_WARNING("no body on plugin document!"); return NS_ERROR_FAILURE; diff --git a/dom/html/VideoDocument.cpp b/dom/html/VideoDocument.cpp index 1bd898564..76b2e326f 100644 --- a/dom/html/VideoDocument.cpp +++ b/dom/html/VideoDocument.cpp @@ -90,7 +90,7 @@ VideoDocument::CreateSyntheticVideoDocument(nsIChannel* aChannel, nsresult rv = MediaDocument::CreateSyntheticDocument(); NS_ENSURE_SUCCESS(rv, rv); - Element* body = GetBodyElement(); + RefPtr<Element> body = GetBodyElement(); if (!body) { NS_WARNING("no body on video document!"); return NS_ERROR_FAILURE; diff --git a/dom/html/test/file_script_module.html b/dom/html/test/file_script_module.html new file mode 100644 index 000000000..78c499265 --- /dev/null +++ b/dom/html/test/file_script_module.html @@ -0,0 +1,42 @@ +<html> +<body> + <script> +// Helper methods. +function ok(a, msg) { + parent.postMessage({ check: !!a, msg }, "*") +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function finish() { + parent.postMessage({ done: true }, "*"); +} + </script> + + <script id="a" nomodule>42</script> + <script id="b">42</script> + <script> +// Let's test the behavior of nomodule attribute and noModule getter/setter. +var a = document.getElementById("a"); +is(a.noModule, true, "HTMLScriptElement with nomodule attribute has noModule set to true"); +a.removeAttribute("nomodule"); +is(a.noModule, false, "HTMLScriptElement without nomodule attribute has noModule set to false"); +a.noModule = true; +ok(a.hasAttribute('nomodule'), "HTMLScriptElement.noModule = true add the nomodule attribute"); + +var b = document.getElementById("b"); +is(b.noModule, false, "HTMLScriptElement without nomodule attribute has noModule set to false"); +b.noModule = true; +ok(b.hasAttribute('nomodule'), "HTMLScriptElement.noModule = true add the nomodule attribute"); + </script> + + <script>var foo = 42;</script> + <script nomodule>foo = 43;</script> + <script> +is(foo, 42, "nomodule HTMLScriptElements should not be executed in modern browsers"); +finish(); + </script> +</body> +</html> diff --git a/dom/html/test/file_script_nomodule.html b/dom/html/test/file_script_nomodule.html new file mode 100644 index 000000000..303edb90b --- /dev/null +++ b/dom/html/test/file_script_nomodule.html @@ -0,0 +1,32 @@ +<html> +<body> + <script> +// Helper methods. +function ok(a, msg) { + parent.postMessage({ check: !!a, msg }, "*") +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function finish() { + parent.postMessage({ done: true }, "*"); +} + </script> + + <script id="a" nomodule>42</script> + <script> +// Let's test the behavior of nomodule attribute and noModule getter/setter. +var a = document.getElementById("a"); +ok(!("noModule" in a), "When modules are disabled HTMLScriptElement.noModule is not defined"); + </script> + + <script>var foo = 42;</script> + <script nomodule>foo = 43;</script> + <script> +is(foo, 43, "nomodule attribute is ignored when modules are disabled"); +finish(); + </script> +</body> +</html> diff --git a/dom/html/test/mochitest.ini b/dom/html/test/mochitest.ini index 99b425df8..b9da7def8 100644 --- a/dom/html/test/mochitest.ini +++ b/dom/html/test/mochitest.ini @@ -553,7 +553,6 @@ skip-if = true # Disabled for timeouts. [test_viewport.html] [test_documentAll.html] [test_document-element-inserted.html] -[test_document.watch.html] [test_bug445004.html] skip-if = true || toolkit == 'android' # Disabled permanently (bug 559932). [test_bug446483.html] @@ -606,3 +605,7 @@ skip-if = os == "android" # up/down arrow keys not supported on android [test_bug1295719_event_sequence_for_number_keys.html] [test_bug1310865.html] [test_bug1315146.html] +[test_script_module.html] +support-files = + file_script_module.html + file_script_nomodule.html
\ No newline at end of file diff --git a/dom/html/test/test_document.watch.html b/dom/html/test/test_document.watch.html deleted file mode 100644 index 54509823b..000000000 --- a/dom/html/test/test_document.watch.html +++ /dev/null @@ -1,129 +0,0 @@ -<!DOCTYPE html> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=903332 ---> -<head> - <meta charset="utf-8"> - <title>Test for Bug 903332</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript"> - - /** Test for Bug 903332 **/ - - var watch1Called; - function watch1(prop, oldValue, newValue) - { - is(watch1Called, false, "watch1Called not reset properly?"); - watch1Called = true; - - is(prop, "cookie", "wrong property name passed to watch1"); - return newValue; - } - - var watch2Called; - function watch2(prop, oldValue, newValue) - { - is(watch2Called, false, "watch2Called not reset properly?"); - watch2Called = true; - - is(prop, "cookie", "wrong property name passed to watch2"); - return newValue; - } - - // Just in case subsequent tests depend on a particular value... - var originalValue = document.cookie; - ok(true, "originalValue: " + originalValue); - - var originalPrefix = originalValue.length > 0 ? originalValue + "; " : ""; - - try - { - // trial set (no watch) to verify things work - document.cookie = "first=set"; - is(document.cookie, originalPrefix + "first=set", - "first value correct"); - - // add a watch - document.watch("cookie", watch1); - - // set, check for watch invoked - watch1Called = false; - document.cookie = "second=set"; - is(watch1Called, true, "watch1 function should be called"); - is(document.cookie, originalPrefix + "first=set; second=set", - "second value correct"); - - // and a second time, just in case - watch1Called = false; - document.cookie = "third=set"; - is(watch1Called, true, "watch1 function should be called"); - is(document.cookie, originalPrefix + "first=set; second=set; third=set", - "third value correct"); - - // overwrite the current watch with a new one - document.watch("cookie", watch2); - - // set, check for watch invoked - watch1Called = false; - watch2Called = false; - document.cookie = "fourth=set"; - is(watch1Called, false, "watch1 invoked erroneously"); - is(watch2Called, true, "watch2 function should be called"); - is(document.cookie, originalPrefix + "first=set; second=set; third=set; fourth=set", - "fourth value correct"); - - // and a second time, just in case - watch1Called = false; - watch2Called = false; - document.cookie = "fifth=set"; - is(watch1Called, false, "watch1 invoked erroneously"); - is(watch2Called, true, "watch2 function should be called"); - is(document.cookie, originalPrefix + "first=set; second=set; third=set; fourth=set; fifth=set", - "fifth value correct"); - - // remove the watch - document.unwatch("cookie"); - - // check for non-invocation now - watch1Called = false; - watch2Called = false; - document.cookie = "sixth=set"; - is(watch1Called, false, "watch1 shouldn't be called"); - is(watch2Called, false, "watch2 shouldn't be called"); - is(document.cookie, originalPrefix + "first=set; second=set; third=set; fourth=set; fifth=set; sixth=set", - "sixth value correct"); - } - finally - { - // reset - document.unwatch("cookie"); // harmless, should be no-op except if bugs - - var d = new Date(); - d.setTime(0); - var suffix = "=; expires=" + d.toGMTString(); - - document.cookie = "first" + suffix; - document.cookie = "second" + suffix; - document.cookie = "third" + suffix; - document.cookie = "fourth" + suffix; - document.cookie = "fifth" + suffix; - document.cookie = "sixth" + suffix; - } - - is(document.cookie, originalValue, - "document.cookie isn't what it was initially! expect bustage further " + - "down the line"); - </script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=903332">Mozilla Bug 903332</a> -<p id="display"></p> -<div id="content" style="display: none"> - -</div> -<pre id="test"> -</pre> -</body> -</html> diff --git a/dom/html/test/test_script_module.html b/dom/html/test/test_script_module.html new file mode 100644 index 000000000..4878bb379 --- /dev/null +++ b/dom/html/test/test_script_module.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for HTMLScriptElement with nomodule attribute</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> + +<body> + <script> +onmessage = (e) => { + if ("done" in e.data) { + next(); + } else if ("check" in e.data) { + ok(e.data.check, e.data.msg); + } else { + ok(false, "Unknown message"); + } +} + +var tests = [ + function() { + SpecialPowers.pushPrefEnv({"set":[["dom.moduleScripts.enabled", true]]}) + .then(() => { + var ifr = document.createElement('iframe'); + ifr.src = "file_script_module.html"; + document.body.appendChild(ifr); + }); + }, + + function() { + SpecialPowers.pushPrefEnv({"set":[["dom.moduleScripts.enabled", false]]}) + .then(() => { + var ifr = document.createElement('iframe'); + ifr.src = "file_script_nomodule.html"; + document.body.appendChild(ifr); + }); + }, +]; + +SimpleTest.waitForExplicitFinish(); +next(); + +function next() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + </script> + +</body> +</html> diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index cd998c31c..74afef452 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -4427,18 +4427,6 @@ CreateStorageConnection(nsIFile* aDBFile, nsresult rv; bool exists; - if (IndexedDatabaseManager::InLowDiskSpaceMode()) { - rv = aDBFile->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!exists) { - NS_WARNING("Refusing to create database because disk space is low!"); - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - } - nsCOMPtr<nsIFileURL> dbFileUrl; rv = GetDatabaseFileURL(aDBFile, aPersistenceType, @@ -19103,23 +19091,6 @@ DatabaseMaintenance::DetermineMaintenanceAction( return NS_OK; } - bool lowDiskSpace = IndexedDatabaseManager::InLowDiskSpaceMode(); - - if (QuotaManager::IsRunningXPCShellTests()) { - // If we're running XPCShell then we want to test both the low disk space - // and normal disk space code paths so pick semi-randomly based on the - // current time. - lowDiskSpace = ((PR_Now() / PR_USEC_PER_MSEC) % 2) == 0; - } - - // If we're low on disk space then the best we can hope for is that an - // incremental vacuum might free some space. That is a journaled operation so - // it may not be possible even then. - if (lowDiskSpace) { - *aMaintenanceAction = MaintenanceAction::IncrementalVacuum; - return NS_OK; - } - // This method shouldn't make any permanent changes to the database, so make // sure everything gets rolled back when we leave. mozStorageTransaction transaction(aConnection, @@ -24233,11 +24204,6 @@ CreateFileOp::DoDatabaseWork() "CreateFileOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { - NS_WARNING("Refusing to create file because disk space is low!"); - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - if (NS_WARN_IF(QuotaManager::IsShuttingDown()) || !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -24378,10 +24344,6 @@ CreateObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) "CreateObjectStoreOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - #ifdef DEBUG { // Make sure that we're not creating an object store with the same name as @@ -24705,10 +24667,6 @@ RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) "RenameObjectStoreOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - #ifdef DEBUG { // Make sure that we're not renaming an object store with the same name as @@ -24798,7 +24756,6 @@ CreateIndexOp::InsertDataFromObjectStore(DatabaseConnection* aConnection) { MOZ_ASSERT(aConnection); aConnection->AssertIsOnConnectionThread(); - MOZ_ASSERT(!IndexedDatabaseManager::InLowDiskSpaceMode()); MOZ_ASSERT(mMaybeUniqueIndexTable); PROFILER_LABEL("IndexedDB", @@ -24849,7 +24806,6 @@ CreateIndexOp::InsertDataFromObjectStoreInternal( { MOZ_ASSERT(aConnection); aConnection->AssertIsOnConnectionThread(); - MOZ_ASSERT(!IndexedDatabaseManager::InLowDiskSpaceMode()); MOZ_ASSERT(mMaybeUniqueIndexTable); DebugOnly<void*> storageConnection = aConnection->GetStorageConnection(); @@ -24926,10 +24882,6 @@ CreateIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) "CreateIndexOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - #ifdef DEBUG { // Make sure that we're not creating an index with the same name and object @@ -25806,10 +25758,6 @@ RenameIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) "RenameIndexOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - #ifdef DEBUG { // Make sure that we're not renaming an index with the same name as another @@ -26294,10 +26242,6 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) "ObjectStoreAddOrPutRequestOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - DatabaseConnection::AutoSavepoint autoSave; nsresult rv = autoSave.Start(Transaction()); if (NS_WARN_IF(NS_FAILED(rv))) { diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp index af88742f0..be0295dc7 100644 --- a/dom/indexedDB/IDBCursor.cpp +++ b/dom/indexedDB/IDBCursor.cpp @@ -430,7 +430,7 @@ IDBCursor::Continue(JSContext* aCx, } Key key; - aRv = key.SetFromJSVal(aCx, aKey); + aRv = key.SetFromJSVal(aCx, aKey, /* aCallGetters */ true); if (aRv.Failed()) { return; } @@ -536,7 +536,7 @@ IDBCursor::ContinuePrimaryKey(JSContext* aCx, } Key key; - aRv = key.SetFromJSVal(aCx, aKey); + aRv = key.SetFromJSVal(aCx, aKey, /* aCallGetters */ true); if (aRv.Failed()) { return; } @@ -558,7 +558,7 @@ IDBCursor::ContinuePrimaryKey(JSContext* aCx, } Key primaryKey; - aRv = primaryKey.SetFromJSVal(aCx, aPrimaryKey); + aRv = primaryKey.SetFromJSVal(aCx, aPrimaryKey, /* aCallGetters */ true); if (aRv.Failed()) { return; } @@ -718,7 +718,7 @@ IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue, const KeyPath& keyPath = objectStore->GetKeyPath(); Key key; - aRv = keyPath.ExtractKey(aCx, aValue, key); + aRv = keyPath.ExtractKey(aCx, aValue, key, /* aCallGetters */ false); if (aRv.Failed()) { return nullptr; } diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 5592e7f93..6ef352801 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -1257,6 +1257,9 @@ IDBDatabase::LastRelease() AssertIsOnOwningThread(); CloseInternal(); + + // Make sure that file actors created after the database was closed are expired. + ExpireFileActors(/* aExpireAll */ true); if (mBackgroundActor) { mBackgroundActor->SendDeleteMeInternal(); diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index c1ef6353d..66471fe24 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -482,13 +482,13 @@ IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst, JS::Handle<JS::Value> aSecond, ErrorResult& aRv) { Key first, second; - nsresult rv = first.SetFromJSVal(aCx, aFirst); + nsresult rv = first.SetFromJSVal(aCx, aFirst, /* aCallGetters */ true); if (NS_FAILED(rv)) { aRv.Throw(rv); return 0; } - rv = second.SetFromJSVal(aCx, aSecond); + rv = second.SetFromJSVal(aCx, aSecond, /* aCallGetters */ true); if (NS_FAILED(rv)) { aRv.Throw(rv); return 0; diff --git a/dom/indexedDB/IDBKeyRange.cpp b/dom/indexedDB/IDBKeyRange.cpp index e61c80617..168fb4a5a 100644 --- a/dom/indexedDB/IDBKeyRange.cpp +++ b/dom/indexedDB/IDBKeyRange.cpp @@ -24,7 +24,7 @@ GetKeyFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, Key& aKey) { - nsresult rv = aKey.SetFromJSVal(aCx, aVal); + nsresult rv = aKey.SetFromJSVal(aCx, aVal, /* aCallGetters */ true); if (NS_FAILED(rv)) { MOZ_ASSERT(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB); return rv; diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 756792741..cbac30894 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -1084,7 +1084,7 @@ IDBObjectStore::AppendIndexUpdateInfo( if (!aMultiEntry) { Key key; - rv = aKeyPath.ExtractKey(aCx, aVal, key); + rv = aKeyPath.ExtractKey(aCx, aVal, key, /* aCallGetters */ false); // If an index's keyPath doesn't match an object, we ignore that object. if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) { @@ -1114,7 +1114,7 @@ IDBObjectStore::AppendIndexUpdateInfo( } bool isArray; - if (!JS_IsArrayObject(aCx, val, &isArray)) { + if (NS_WARN_IF(!JS_IsArrayObject(aCx, val, &isArray))) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -1127,14 +1127,31 @@ IDBObjectStore::AppendIndexUpdateInfo( } for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { - JS::Rooted<JS::Value> arrayItem(aCx); - if (NS_WARN_IF(!JS_GetElement(aCx, array, arrayIndex, &arrayItem))) { + JS::RootedId indexId(aCx);
+ if (NS_WARN_IF(!JS_IndexToId(aCx, arrayIndex, &indexId))) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ bool hasOwnProperty;
+ if (NS_WARN_IF(
+ !JS_HasOwnPropertyById(aCx, array, indexId, &hasOwnProperty))) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ if (!hasOwnProperty) {
+ continue;
+ }
+
+ JS::RootedValue arrayItem(aCx);
+ if (NS_WARN_IF(!JS_GetPropertyById(aCx, array, indexId, &arrayItem))) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } Key value; - if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) || + if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem, /* aCallGetters */ false)) || value.IsUnset()) { // Not a value we can do anything with, ignore it. continue; @@ -1153,7 +1170,7 @@ IDBObjectStore::AppendIndexUpdateInfo( } else { Key value; - if (NS_FAILED(value.SetFromJSVal(aCx, val)) || + if (NS_FAILED(value.SetFromJSVal(aCx, val, /* aCallGetters */ false)) || value.IsUnset()) { // Not a value we can do anything with, ignore it. return NS_OK; @@ -1324,12 +1341,12 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, if (!HasValidKeyPath()) { // Out-of-line keys must be passed in. - rv = aKey.SetFromJSVal(aCx, aKeyVal); + rv = aKey.SetFromJSVal(aCx, aKeyVal, /* aCallGetters */ true); if (NS_FAILED(rv)) { return rv; } } else if (!isAutoIncrement) { - rv = GetKeyPath().ExtractKey(aCx, aValue, aKey); + rv = GetKeyPath().ExtractKey(aCx, aValue, aKey, /* aCallGetters */ false); if (NS_FAILED(rv)) { return rv; } @@ -1368,7 +1385,8 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, aValue, aKey, &GetAddInfoCallback, - &data); + &data, + /* aCallGetters */ false); } else { rv = GetAddInfoCallback(aCx, &data); } diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index 62ba51c08..213de5cc9 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -8,11 +8,9 @@ #include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize #include "nsIConsoleService.h" -#include "nsIDiskSpaceWatcher.h" #include "nsIDOMWindow.h" #include "nsIEventTarget.h" #include "nsIFile.h" -#include "nsIObserverService.h" #include "nsIScriptError.h" #include "nsIScriptGlobalObject.h" @@ -64,11 +62,6 @@ #define IDB_STR "indexedDB" -// The two possible values for the data argument when receiving the disk space -// observer notification. -#define LOW_DISK_SPACE_DATA_FULL "full" -#define LOW_DISK_SPACE_DATA_FREE "free" - namespace mozilla { namespace dom { namespace indexedDB { @@ -313,8 +306,6 @@ Atomic<IndexedDatabaseManager::LoggingMode> IndexedDatabaseManager::sLoggingMode( IndexedDatabaseManager::Logging_Disabled); -mozilla::Atomic<bool> IndexedDatabaseManager::sLowDiskSpaceMode(false); - // static IndexedDatabaseManager* IndexedDatabaseManager::GetOrCreate() @@ -329,24 +320,6 @@ IndexedDatabaseManager::GetOrCreate() if (!gDBManager) { sIsMainProcess = XRE_IsParentProcess(); - if (sIsMainProcess && Preferences::GetBool("disk_space_watcher.enabled", false)) { - // See if we're starting up in low disk space conditions. - nsCOMPtr<nsIDiskSpaceWatcher> watcher = - do_GetService(DISKSPACEWATCHER_CONTRACTID); - if (watcher) { - bool isDiskFull; - if (NS_SUCCEEDED(watcher->GetIsDiskFull(&isDiskFull))) { - sLowDiskSpaceMode = isDiskFull; - } - else { - NS_WARNING("GetIsDiskFull failed!"); - } - } - else { - NS_WARNING("No disk space watcher component available!"); - } - } - RefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager()); nsresult rv = instance->Init(); @@ -380,13 +353,6 @@ IndexedDatabaseManager::Init() // During Init() we can't yet call IsMainProcess(), just check sIsMainProcess // directly. if (sIsMainProcess) { - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - NS_ENSURE_STATE(obs); - - nsresult rv = - obs->AddObserver(this, DISKSPACEWATCHER_OBSERVER_TOPIC, false); - NS_ENSURE_SUCCESS(rv, rv); - mDeleteTimer = do_CreateInstance(NS_TIMER_CONTRACTID); NS_ENSURE_STATE(mDeleteTimer); @@ -680,16 +646,6 @@ IndexedDatabaseManager::IsMainProcess() return sIsMainProcess; } -//static -bool -IndexedDatabaseManager::InLowDiskSpaceMode() -{ - NS_ASSERTION(gDBManager, - "InLowDiskSpaceMode() called before indexedDB has been " - "initialized!"); - return sLowDiskSpaceMode; -} - // static IndexedDatabaseManager::LoggingMode IndexedDatabaseManager::GetLoggingMode() @@ -1087,36 +1043,7 @@ IndexedDatabaseManager::GetLocale() NS_IMPL_ADDREF(IndexedDatabaseManager) NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy()) -NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIObserver, nsITimerCallback) - -NS_IMETHODIMP -IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic, - const char16_t* aData) -{ - NS_ASSERTION(IsMainProcess(), "Wrong process!"); - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!strcmp(aTopic, DISKSPACEWATCHER_OBSERVER_TOPIC)) { - NS_ASSERTION(aData, "No data?!"); - - const nsDependentString data(aData); - - if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FULL)) { - sLowDiskSpaceMode = true; - } - else if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FREE)) { - sLowDiskSpaceMode = false; - } - else { - NS_NOTREACHED("Unknown data value!"); - } - - return NS_OK; - } - - NS_NOTREACHED("Unknown topic!"); - return NS_ERROR_UNEXPECTED; -} +NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsITimerCallback) NS_IMETHODIMP IndexedDatabaseManager::Notify(nsITimer* aTimer) diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index d63c548ec..fb4376426 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -7,8 +7,6 @@ #ifndef mozilla_dom_indexeddatabasemanager_h__ #define mozilla_dom_indexeddatabasemanager_h__ -#include "nsIObserver.h" - #include "js/TypeDecls.h" #include "mozilla/Atomics.h" #include "mozilla/dom/quota/PersistenceType.h" @@ -43,8 +41,7 @@ class FileManagerInfo; } // namespace indexedDB class IndexedDatabaseManager final - : public nsIObserver - , public nsITimerCallback + : public nsITimerCallback { typedef mozilla::dom::quota::PersistenceType PersistenceType; typedef mozilla::dom::quota::QuotaManager QuotaManager; @@ -62,7 +59,6 @@ public: }; NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER NS_DECL_NSITIMERCALLBACK // Returns a non-owning reference. @@ -87,16 +83,6 @@ public: #endif static bool - InLowDiskSpaceMode() -#ifdef DEBUG - ; -#else - { - return !!sLowDiskSpaceMode; - } -#endif - - static bool InTestingMode(); static bool @@ -244,7 +230,6 @@ private: static bool sFullSynchronousMode; static LazyLogModule sLoggingModule; static Atomic<LoggingMode> sLoggingMode; - static mozilla::Atomic<bool> sLowDiskSpaceMode; }; } // namespace dom diff --git a/dom/indexedDB/Key.cpp b/dom/indexedDB/Key.cpp index 0f693b2c6..575734af2 100644 --- a/dom/indexedDB/Key.cpp +++ b/dom/indexedDB/Key.cpp @@ -201,8 +201,11 @@ Key::ToLocaleBasedKey(Key& aTarget, const nsCString& aLocale) const } nsresult -Key::EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal, - uint8_t aTypeOffset, uint16_t aRecursionDepth) +Key::EncodeJSValInternal(JSContext* aCx, + JS::Handle<JS::Value> aVal, + uint8_t aTypeOffset, + uint16_t aRecursionDepth, + bool aCallGetters) { static_assert(eMaxType * kMaxArrayCollapse < 256, "Unable to encode jsvals."); @@ -257,13 +260,18 @@ Key::EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal, for (uint32_t index = 0; index < length; index++) { JS::Rooted<JS::Value> val(aCx); - if (!JS_GetElement(aCx, obj, index, &val)) { + bool ok = aCallGetters ? JS_GetElement(aCx, obj, index, &val) + : JS_GetOwnElement(aCx, obj, index, &val); + if (!ok) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - nsresult rv = EncodeJSValInternal(aCx, val, aTypeOffset, - aRecursionDepth + 1); + nsresult rv = EncodeJSValInternal(aCx, + val, + aTypeOffset, + aRecursionDepth + 1, + aCallGetters); if (NS_FAILED(rv)) { return rv; } @@ -406,9 +414,10 @@ Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd, nsresult Key::EncodeJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, - uint8_t aTypeOffset) + uint8_t aTypeOffset, + bool aCallGetters) { - return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); + return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0, aCallGetters); } void @@ -741,7 +750,8 @@ Key::SetFromValueArray(mozIStorageValueArray* aValues, nsresult Key::SetFromJSVal(JSContext* aCx, - JS::Handle<JS::Value> aVal) + JS::Handle<JS::Value> aVal, + bool aCallGetters) { mBuffer.Truncate(); @@ -750,7 +760,7 @@ Key::SetFromJSVal(JSContext* aCx, return NS_OK; } - nsresult rv = EncodeJSVal(aCx, aVal, 0); + nsresult rv = EncodeJSVal(aCx, aVal, 0, aCallGetters); if (NS_FAILED(rv)) { Unset(); return rv; @@ -793,9 +803,15 @@ Key::ToJSVal(JSContext* aCx, } nsresult -Key::AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle<JS::Value> aVal) +Key::AppendItem(JSContext* aCx, + bool aFirstOfArray, + JS::Handle<JS::Value> aVal, + bool aCallGetters) { - nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); + nsresult rv = EncodeJSVal(aCx, + aVal, + aFirstOfArray ? eMaxType : 0, + aCallGetters); if (NS_FAILED(rv)) { Unset(); return rv; diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h index 9d70ce6ad..a4fb65b48 100644 --- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -203,7 +203,7 @@ public: } nsresult - SetFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal); + SetFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, bool aCallGetters); nsresult ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) const; @@ -212,7 +212,10 @@ public: ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aVal) const; nsresult - AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle<JS::Value> aVal); + AppendItem(JSContext* aCx, + bool aFirstOfArray, + JS::Handle<JS::Value> aVal, + bool aCallGetters); nsresult ToLocaleBasedKey(Key& aTarget, const nsCString& aLocale) const; @@ -283,7 +286,10 @@ private: // Encoding functions. These append the encoded value to the end of mBuffer nsresult - EncodeJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, uint8_t aTypeOffset); + EncodeJSVal(JSContext* aCx, + JS::Handle<JS::Value> aVal, + uint8_t aTypeOffset, + bool aCallGetters); void EncodeString(const nsAString& aString, uint8_t aTypeOffset); @@ -331,7 +337,8 @@ private: EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal, uint8_t aTypeOffset, - uint16_t aRecursionDepth); + uint16_t aRecursionDepth, + bool aCallGetters); static nsresult DecodeJSValInternal(const unsigned char*& aPos, diff --git a/dom/indexedDB/KeyPath.cpp b/dom/indexedDB/KeyPath.cpp index 30edd8cd7..0221c9450 100644 --- a/dom/indexedDB/KeyPath.cpp +++ b/dom/indexedDB/KeyPath.cpp @@ -372,11 +372,13 @@ KeyPath::AppendStringWithValidation(const nsAString& aString) } nsresult -KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const +KeyPath::ExtractKey(JSContext* aCx, + const JS::Value& aValue, + Key& aKey, + bool aCallGetters) const { uint32_t len = mStrings.Length(); JS::Rooted<JS::Value> value(aCx); - aKey.Unset(); for (uint32_t i = 0; i < len; ++i) { @@ -388,7 +390,10 @@ KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const return rv; } - if (NS_FAILED(aKey.AppendItem(aCx, IsArray() && i == 0, value))) { + if (NS_FAILED(aKey.AppendItem(aCx, + IsArray() && i == 0, + value, + aCallGetters))) { NS_ASSERTION(aKey.IsUnset(), "Encoding error should unset"); return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } @@ -437,9 +442,12 @@ KeyPath::ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue, } nsresult -KeyPath::ExtractOrCreateKey(JSContext* aCx, const JS::Value& aValue, - Key& aKey, ExtractOrCreateKeyCallback aCallback, - void* aClosure) const +KeyPath::ExtractOrCreateKey(JSContext* aCx, + const JS::Value& aValue, + Key& aKey, + ExtractOrCreateKeyCallback aCallback, + void* aClosure, + bool aCallGetters) const { NS_ASSERTION(IsString(), "This doesn't make sense!"); @@ -455,7 +463,7 @@ KeyPath::ExtractOrCreateKey(JSContext* aCx, const JS::Value& aValue, return rv; } - if (NS_FAILED(aKey.AppendItem(aCx, false, value))) { + if (NS_FAILED(aKey.AppendItem(aCx, false, value, aCallGetters))) { NS_ASSERTION(aKey.IsUnset(), "Should be unset"); return value.isUndefined() ? NS_OK : NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } diff --git a/dom/indexedDB/KeyPath.h b/dom/indexedDB/KeyPath.h index c133cdc4a..e6e5f57d4 100644 --- a/dom/indexedDB/KeyPath.h +++ b/dom/indexedDB/KeyPath.h @@ -72,7 +72,10 @@ public: Parse(const Nullable<OwningStringOrStringSequence>& aValue, KeyPath* aKeyPath); nsresult - ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const; + ExtractKey(JSContext* aCx, + const JS::Value& aValue, + Key& aKey, + bool aCallGetters) const; nsresult ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue, @@ -82,9 +85,12 @@ public: (*ExtractOrCreateKeyCallback)(JSContext* aCx, void* aClosure); nsresult - ExtractOrCreateKey(JSContext* aCx, const JS::Value& aValue, Key& aKey, + ExtractOrCreateKey(JSContext* aCx, + const JS::Value& aValue, + Key& aKey, ExtractOrCreateKeyCallback aCallback, - void* aClosure) const; + void* aClosure, + bool aCallGetters) const; inline bool IsValid() const { return mType != NONEXISTENT; diff --git a/dom/indexedDB/crashtests/1558522-1.html b/dom/indexedDB/crashtests/1558522-1.html new file mode 100644 index 000000000..47dd2f843 --- /dev/null +++ b/dom/indexedDB/crashtests/1558522-1.html @@ -0,0 +1,40 @@ +<html>
+<head>
+ <script id='worker' type='javascript/worker'>
+ onmessage = function (e) {
+ const file = e.data[0]
+ const db = indexedDB.open('', {})
+ db.onupgradeneeded = function (event) {
+ const store = event.target.result.createObjectStore('IDBStore_0', {})
+ store.add({}, '')
+ }
+ db.onsuccess = function (event) {
+ const transaction = event.target.result.transaction('IDBStore_0', 'readwrite')
+ const store = transaction.objectStore('IDBStore_0')
+ const cursor = store.openCursor()
+ cursor.onsuccess = function (event) {
+ event.target.result.update({
+ data: file
+ })
+ event.target.result.advance(1)
+ }
+
+ event.target.result.close()
+ }
+ }
+
+ </script>
+ <script>
+ let worker;
+
+ function start () {
+ const file = new File([], 'x')
+ const blob = new Blob([document.getElementById('worker').textContent], { type: 'text/javascript' })
+ worker = new Worker(window.URL.createObjectURL(blob))
+ worker.postMessage([file], [])
+ }
+
+ document.addEventListener('DOMContentLoaded', start)
+ </script>
+</head>
+</html>
\ No newline at end of file diff --git a/dom/indexedDB/crashtests/crashtests.list b/dom/indexedDB/crashtests/crashtests.list index 69f5dab0b..ead6024dc 100644 --- a/dom/indexedDB/crashtests/crashtests.list +++ b/dom/indexedDB/crashtests/crashtests.list @@ -1 +1,2 @@ load 726376-1.html +load 1558522-1.html diff --git a/dom/indexedDB/test/helpers.js b/dom/indexedDB/test/helpers.js index e6e27f3f3..ffe66ebcd 100644 --- a/dom/indexedDB/test/helpers.js +++ b/dom/indexedDB/test/helpers.js @@ -217,10 +217,6 @@ if (!window.runTest) { function finishTest() { - SpecialPowers.notifyObserversInParentProcess(null, - "disk-space-watcher", - "free"); - SimpleTest.executeSoon(function() { testGenerator.close(); testHarnessGenerator.close(); diff --git a/dom/indexedDB/test/mochitest.ini b/dom/indexedDB/test/mochitest.ini index 4ab55a9dc..ca65ea8b6 100644 --- a/dom/indexedDB/test/mochitest.ini +++ b/dom/indexedDB/test/mochitest.ini @@ -66,7 +66,6 @@ support-files = unit/test_locale_aware_indexes.js unit/test_locale_aware_index_getAll.js unit/test_locale_aware_index_getAllObjects.js - unit/test_lowDiskSpace.js unit/test_maximal_serialized_object_size.js unit/test_multientry.js unit/test_names_sorted.js @@ -214,7 +213,6 @@ skip-if = true [test_key_requirements.html] [test_keys.html] [test_leaving_page.html] -[test_lowDiskSpace.html] [test_maximal_serialized_object_size.html] [test_message_manager_ipc.html] # This test is only supposed to run in the main process. diff --git a/dom/indexedDB/test/test_lowDiskSpace.html b/dom/indexedDB/test/test_lowDiskSpace.html deleted file mode 100644 index cffd46549..000000000 --- a/dom/indexedDB/test/test_lowDiskSpace.html +++ /dev/null @@ -1,19 +0,0 @@ -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<html> -<head> - <title>Indexed Database Low Disk Space Test</title> - - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - - <script type="text/javascript;version=1.7" src="unit/test_lowDiskSpace.js"></script> - <script type="text/javascript;version=1.7" src="helpers.js"></script> - -</head> - -<body onload="runTest();"></body> - -</html> diff --git a/dom/indexedDB/test/unit/test_lowDiskSpace.js b/dom/indexedDB/test/unit/test_lowDiskSpace.js deleted file mode 100644 index eaea5797d..000000000 --- a/dom/indexedDB/test/unit/test_lowDiskSpace.js +++ /dev/null @@ -1,754 +0,0 @@ -/** - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ -"use strict"; - -var disableWorkerTest = "This test uses SpecialPowers"; - -var self = this; - -var testGenerator = testSteps(); - -function testSteps() -{ - const dbName = self.window ? window.location.pathname : "test_lowDiskSpace"; - const dbVersion = 1; - - const objectStoreName = "foo"; - const objectStoreOptions = { keyPath: "foo" }; - - const indexName = "bar"; - const indexOptions = { unique: true }; - - const dbData = [ - { foo: 0, bar: 0 }, - { foo: 1, bar: 10 }, - { foo: 2, bar: 20 }, - { foo: 3, bar: 30 }, - { foo: 4, bar: 40 }, - { foo: 5, bar: 50 }, - { foo: 6, bar: 60 }, - { foo: 7, bar: 70 }, - { foo: 8, bar: 80 }, - { foo: 9, bar: 90 } - ]; - - let lowDiskMode = false; - function setLowDiskMode(val) { - let data = val ? "full" : "free"; - - if (val == lowDiskMode) { - info("Low disk mode is: " + data); - } - else { - info("Changing low disk mode to: " + data); - SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", - data); - lowDiskMode = val; - } - } - - { // Make sure opening works from the beginning. - info("Test 1"); - - setLowDiskMode(false); - - let request = indexedDB.open(dbName, dbVersion); - request.onerror = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - let event = yield undefined; - - is(event.type, "success", "Opened database without setting low disk mode"); - - let db = event.target.result; - db.close(); - } - - { // Make sure delete works in low disk mode. - info("Test 2"); - - setLowDiskMode(true); - - let request = indexedDB.deleteDatabase(dbName); - request.onerror = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - let event = yield undefined; - - is(event.type, "success", "Deleted database after setting low disk mode"); - } - - { // Make sure creating a db in low disk mode fails. - info("Test 3"); - - setLowDiskMode(true); - - let request = indexedDB.open(dbName, dbVersion); - request.onerror = expectedErrorHandler("QuotaExceededError"); - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = unexpectedSuccessHandler; - let event = yield undefined; - - is(event.type, "error", "Didn't create new database in low disk mode"); - } - - { // Make sure opening an already-existing db in low disk mode succeeds. - info("Test 4"); - - setLowDiskMode(false); - - let request = indexedDB.open(dbName, dbVersion); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - let event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database"); - - let db = event.target.result; - db.onerror = errorHandler; - - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "success", "Created database"); - ok(event.target.result === db, "Got the same database"); - - db.close(); - - setLowDiskMode(true); - - request = indexedDB.open(dbName); - request.onerror = errorHandler; - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "success", "Opened existing database in low disk mode"); - - db = event.target.result; - db.close(); - } - - { // Make sure upgrading an already-existing db in low disk mode succeeds. - info("Test 5"); - - setLowDiskMode(true); - - let request = indexedDB.open(dbName, dbVersion + 1); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - - let event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database"); - - let db = event.target.result; - db.onerror = errorHandler; - - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "success", "Created database"); - ok(event.target.result === db, "Got the same database"); - - db.close(); - } - - { // Make sure creating objectStores in low disk mode fails. - info("Test 6"); - - setLowDiskMode(true); - - let request = indexedDB.open(dbName, dbVersion + 2); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - - let event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database"); - - let db = event.target.result; - db.onerror = errorHandler; - - let txn = event.target.transaction; - txn.onerror = expectedErrorHandler("AbortError"); - txn.onabort = grabEventAndContinueHandler; - - let objectStore = db.createObjectStore(objectStoreName, objectStoreOptions); - - request.onupgradeneeded = unexpectedSuccessHandler; - event = yield undefined; - - is(event.type, "abort", "Got correct event type"); - is(event.target.error.name, "QuotaExceededError", "Got correct error type"); - - request.onerror = expectedErrorHandler("AbortError"); - event = yield undefined; - } - - { // Make sure creating indexes in low disk mode fails. - info("Test 7"); - - setLowDiskMode(false); - - let request = indexedDB.open(dbName, dbVersion + 2); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - - let event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database"); - - let db = event.target.result; - db.onerror = errorHandler; - - let objectStore = db.createObjectStore(objectStoreName, objectStoreOptions); - - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "success", "Upgraded database"); - ok(event.target.result === db, "Got the same database"); - - db.close(); - - setLowDiskMode(true); - - request = indexedDB.open(dbName, dbVersion + 3); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database"); - - db = event.target.result; - db.onerror = errorHandler; - let txn = event.target.transaction; - txn.onerror = expectedErrorHandler("AbortError"); - txn.onabort = grabEventAndContinueHandler; - - objectStore = event.target.transaction.objectStore(objectStoreName); - let index = objectStore.createIndex(indexName, indexName, indexOptions); - - request.onupgradeneeded = unexpectedSuccessHandler; - event = yield undefined; - - is(event.type, "abort", "Got correct event type"); - is(event.target.error.name, "QuotaExceededError", "Got correct error type"); - - request.onerror = expectedErrorHandler("AbortError"); - event = yield undefined; - } - - { // Make sure deleting indexes in low disk mode succeeds. - info("Test 8"); - - setLowDiskMode(false); - - let request = indexedDB.open(dbName, dbVersion + 3); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - - let event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database"); - - let db = event.target.result; - db.onerror = errorHandler; - - let objectStore = event.target.transaction.objectStore(objectStoreName); - let index = objectStore.createIndex(indexName, indexName, indexOptions); - - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "success", "Upgraded database"); - ok(event.target.result === db, "Got the same database"); - - db.close(); - - setLowDiskMode(true); - - request = indexedDB.open(dbName, dbVersion + 4); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database"); - - db = event.target.result; - db.onerror = errorHandler; - - objectStore = event.target.transaction.objectStore(objectStoreName); - objectStore.deleteIndex(indexName); - - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "success", "Upgraded database"); - ok(event.target.result === db, "Got the same database"); - - db.close(); - } - - { // Make sure deleting objectStores in low disk mode succeeds. - info("Test 9"); - - setLowDiskMode(true); - - let request = indexedDB.open(dbName, dbVersion + 5); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - - let event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database"); - - let db = event.target.result; - db.onerror = errorHandler; - - db.deleteObjectStore(objectStoreName); - - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "success", "Upgraded database"); - ok(event.target.result === db, "Got the same database"); - - db.close(); - - // Reset everything. - indexedDB.deleteDatabase(dbName); - } - - - { // Add data that the rest of the tests will use. - info("Adding test data"); - - setLowDiskMode(false); - - let request = indexedDB.open(dbName, dbVersion); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - let event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database"); - - let db = event.target.result; - db.onerror = errorHandler; - - let objectStore = db.createObjectStore(objectStoreName, objectStoreOptions); - let index = objectStore.createIndex(indexName, indexName, indexOptions); - - for (let data of dbData) { - objectStore.add(data); - } - - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "success", "Upgraded database"); - ok(event.target.result === db, "Got the same database"); - - db.close(); - } - - { // Make sure read operations in readonly transactions succeed in low disk - // mode. - info("Test 10"); - - setLowDiskMode(true); - - let request = indexedDB.open(dbName, dbVersion); - request.onerror = errorHandler; - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - let event = yield undefined; - - let db = event.target.result; - db.onerror = errorHandler; - - let transaction = db.transaction(objectStoreName); - let objectStore = transaction.objectStore(objectStoreName); - let index = objectStore.index(indexName); - - let data = dbData[0]; - - let requestCounter = new RequestCounter(); - - objectStore.get(data.foo).onsuccess = requestCounter.handler(); - objectStore.mozGetAll().onsuccess = requestCounter.handler(); - objectStore.count().onsuccess = requestCounter.handler(); - index.get(data.bar).onsuccess = requestCounter.handler(); - index.mozGetAll().onsuccess = requestCounter.handler(); - index.getKey(data.bar).onsuccess = requestCounter.handler(); - index.mozGetAllKeys().onsuccess = requestCounter.handler(); - index.count().onsuccess = requestCounter.handler(); - - let objectStoreDataCount = 0; - - request = objectStore.openCursor(); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - objectStoreDataCount++; - objectStoreDataCount % 2 ? cursor.continue() : cursor.advance(1); - } - else { - is(objectStoreDataCount, dbData.length, "Saw all data"); - requestCounter.decr(); - } - }; - requestCounter.incr(); - - let indexDataCount = 0; - - request = index.openCursor(); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - indexDataCount++; - indexDataCount % 2 ? cursor.continue() : cursor.advance(1); - } - else { - is(indexDataCount, dbData.length, "Saw all data"); - requestCounter.decr(); - } - }; - requestCounter.incr(); - - let indexKeyDataCount = 0; - - request = index.openCursor(); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - indexKeyDataCount++; - indexKeyDataCount % 2 ? cursor.continue() : cursor.advance(1); - } - else { - is(indexKeyDataCount, dbData.length, "Saw all data"); - requestCounter.decr(); - } - }; - requestCounter.incr(); - - // Wait for all requests. - yield undefined; - - transaction.oncomplete = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "complete", "Transaction succeeded"); - - db.close(); - } - - { // Make sure read operations in readwrite transactions succeed in low disk - // mode. - info("Test 11"); - - setLowDiskMode(true); - - let request = indexedDB.open(dbName, dbVersion); - request.onerror = errorHandler; - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - let event = yield undefined; - - let db = event.target.result; - db.onerror = errorHandler; - - let transaction = db.transaction(objectStoreName, "readwrite"); - let objectStore = transaction.objectStore(objectStoreName); - let index = objectStore.index(indexName); - - let data = dbData[0]; - - let requestCounter = new RequestCounter(); - - objectStore.get(data.foo).onsuccess = requestCounter.handler(); - objectStore.mozGetAll().onsuccess = requestCounter.handler(); - objectStore.count().onsuccess = requestCounter.handler(); - index.get(data.bar).onsuccess = requestCounter.handler(); - index.mozGetAll().onsuccess = requestCounter.handler(); - index.getKey(data.bar).onsuccess = requestCounter.handler(); - index.mozGetAllKeys().onsuccess = requestCounter.handler(); - index.count().onsuccess = requestCounter.handler(); - - let objectStoreDataCount = 0; - - request = objectStore.openCursor(); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - objectStoreDataCount++; - objectStoreDataCount % 2 ? cursor.continue() : cursor.advance(1); - } - else { - is(objectStoreDataCount, dbData.length, "Saw all data"); - requestCounter.decr(); - } - }; - requestCounter.incr(); - - let indexDataCount = 0; - - request = index.openCursor(); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - indexDataCount++; - indexDataCount % 2 ? cursor.continue() : cursor.advance(1); - } - else { - is(indexDataCount, dbData.length, "Saw all data"); - requestCounter.decr(); - } - }; - requestCounter.incr(); - - let indexKeyDataCount = 0; - - request = index.openCursor(); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - indexKeyDataCount++; - indexKeyDataCount % 2 ? cursor.continue() : cursor.advance(1); - } - else { - is(indexKeyDataCount, dbData.length, "Saw all data"); - requestCounter.decr(); - } - }; - requestCounter.incr(); - - // Wait for all requests. - yield undefined; - - transaction.oncomplete = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "complete", "Transaction succeeded"); - - db.close(); - } - - { // Make sure write operations in readwrite transactions fail in low disk - // mode. - info("Test 12"); - - setLowDiskMode(true); - - let request = indexedDB.open(dbName, dbVersion); - request.onerror = errorHandler; - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - let event = yield undefined; - - let db = event.target.result; - db.onerror = errorHandler; - - let transaction = db.transaction(objectStoreName, "readwrite"); - let objectStore = transaction.objectStore(objectStoreName); - let index = objectStore.index(indexName); - - let data = dbData[0]; - let newData = { foo: 999, bar: 999 }; - - let requestCounter = new RequestCounter(); - - objectStore.add(newData).onerror = requestCounter.errorHandler(); - objectStore.put(newData).onerror = requestCounter.errorHandler(); - - objectStore.get(data.foo).onsuccess = requestCounter.handler(); - objectStore.mozGetAll().onsuccess = requestCounter.handler(); - objectStore.count().onsuccess = requestCounter.handler(); - index.get(data.bar).onsuccess = requestCounter.handler(); - index.mozGetAll().onsuccess = requestCounter.handler(); - index.getKey(data.bar).onsuccess = requestCounter.handler(); - index.mozGetAllKeys().onsuccess = requestCounter.handler(); - index.count().onsuccess = requestCounter.handler(); - - let objectStoreDataCount = 0; - - request = objectStore.openCursor(); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - objectStoreDataCount++; - cursor.update(cursor.value).onerror = requestCounter.errorHandler(); - objectStoreDataCount % 2 ? cursor.continue() : cursor.advance(1); - } - else { - is(objectStoreDataCount, dbData.length, "Saw all data"); - requestCounter.decr(); - } - }; - requestCounter.incr(); - - let indexDataCount = 0; - - request = index.openCursor(); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - indexDataCount++; - cursor.update(cursor.value).onerror = requestCounter.errorHandler(); - indexDataCount % 2 ? cursor.continue() : cursor.advance(1); - } - else { - is(indexDataCount, dbData.length, "Saw all data"); - requestCounter.decr(); - } - }; - requestCounter.incr(); - - let indexKeyDataCount = 0; - - request = index.openCursor(); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - indexKeyDataCount++; - cursor.update(cursor.value).onerror = requestCounter.errorHandler(); - indexKeyDataCount % 2 ? cursor.continue() : cursor.advance(1); - } - else { - is(indexKeyDataCount, dbData.length, "Saw all data"); - requestCounter.decr(); - } - }; - requestCounter.incr(); - - // Wait for all requests. - yield undefined; - - transaction.oncomplete = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "complete", "Transaction succeeded"); - - db.close(); - } - - { // Make sure deleting operations in readwrite transactions succeed in low - // disk mode. - info("Test 13"); - - setLowDiskMode(true); - - let request = indexedDB.open(dbName, dbVersion); - request.onerror = errorHandler; - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = grabEventAndContinueHandler; - let event = yield undefined; - - let db = event.target.result; - db.onerror = errorHandler; - - let transaction = db.transaction(objectStoreName, "readwrite"); - let objectStore = transaction.objectStore(objectStoreName); - let index = objectStore.index(indexName); - - let dataIndex = 0; - let data = dbData[dataIndex++]; - - let requestCounter = new RequestCounter(); - - objectStore.delete(data.foo).onsuccess = requestCounter.handler(); - - objectStore.openCursor().onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - cursor.delete().onsuccess = requestCounter.handler(); - } - requestCounter.decr(); - }; - requestCounter.incr(); - - index.openCursor(null, "prev").onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - cursor.delete().onsuccess = requestCounter.handler(); - } - requestCounter.decr(); - }; - requestCounter.incr(); - - yield undefined; - - objectStore.count().onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.target.result, dbData.length - 3, "Actually deleted something"); - - objectStore.clear(); - objectStore.count().onsuccess = grabEventAndContinueHandler; - event = yield undefined; - - is(event.target.result, 0, "Actually cleared"); - - transaction.oncomplete = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "complete", "Transaction succeeded"); - - db.close(); - } - - finishTest(); - yield undefined; -} - -function RequestCounter(expectedType) { - this._counter = 0; -} -RequestCounter.prototype = { - incr: function() { - this._counter++; - }, - - decr: function() { - if (!--this._counter) { - continueToNextStepSync(); - } - }, - - handler: function(type, preventDefault) { - this.incr(); - return function(event) { - is(event.type, type || "success", "Correct type"); - this.decr(); - }.bind(this); - }, - - errorHandler: function(eventType, errorName) { - this.incr(); - return function(event) { - is(event.type, eventType || "error", "Correct type"); - is(event.target.error.name, errorName || "QuotaExceededError", - "Correct error name"); - event.preventDefault(); - event.stopPropagation(); - this.decr(); - }.bind(this); - } -}; diff --git a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js index def791f52..fe69b1f7b 100644 --- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js +++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js @@ -66,9 +66,6 @@ function finishTest() resetWasm(); resetExperimental(); resetTesting(); - - SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", - "free"); } SpecialPowers.removeFiles(); diff --git a/dom/indexedDB/test/unit/xpcshell-parent-process.ini b/dom/indexedDB/test/unit/xpcshell-parent-process.ini index 04df5f552..2def60c34 100644 --- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini +++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini @@ -46,7 +46,6 @@ skip-if = toolkit == 'android' [test_invalidate.js] # disabled for the moment. skip-if = true -[test_lowDiskSpace.js] [test_maximal_serialized_object_size.js] [test_metadata2Restore.js] [test_metadataRestore.js] diff --git a/dom/interfaces/css/nsIDOMCSSGroupingRule.idl b/dom/interfaces/css/nsIDOMCSSGroupingRule.idl index 80f072d16..f3580aeec 100644 --- a/dom/interfaces/css/nsIDOMCSSGroupingRule.idl +++ b/dom/interfaces/css/nsIDOMCSSGroupingRule.idl @@ -14,7 +14,7 @@ interface nsIDOMCSSGroupingRule : nsIDOMCSSRule readonly attribute nsIDOMCSSRuleList cssRules; unsigned long insertRule(in DOMString rule, - in unsigned long index) + [optional] in unsigned long index) raises(DOMException); void deleteRule(in unsigned long index) raises(DOMException); diff --git a/dom/interfaces/css/nsIDOMCSSStyleSheet.idl b/dom/interfaces/css/nsIDOMCSSStyleSheet.idl index b9e13134e..cefe4f704 100644 --- a/dom/interfaces/css/nsIDOMCSSStyleSheet.idl +++ b/dom/interfaces/css/nsIDOMCSSStyleSheet.idl @@ -20,7 +20,7 @@ interface nsIDOMCSSStyleSheet : nsIDOMStyleSheet readonly attribute nsIDOMCSSRuleList cssRules; unsigned long insertRule(in DOMString rule, - in unsigned long index) + [optional] in unsigned long index) raises(DOMException); void deleteRule(in unsigned long index) raises(DOMException); diff --git a/dom/locales/en-US/chrome/plugins.properties b/dom/locales/en-US/chrome/plugins.properties index fe03be59e..d5e65fea7 100644 --- a/dom/locales/en-US/chrome/plugins.properties +++ b/dom/locales/en-US/chrome/plugins.properties @@ -28,7 +28,5 @@ gmp_privacy_info=Privacy Information openH264_name=OpenH264 Video Codec provided by Cisco Systems, Inc. openH264_description2=This plugin is automatically installed by Mozilla to comply with the WebRTC specification and to enable WebRTC calls with devices that require the H.264 video codec. Visit http://www.openh264.org/ to view the codec source code and learn more about the implementation. -eme-adobe_name=Primetime Content Decryption Module provided by Adobe Systems, Incorporated -eme-adobe_description=Play back protected web video. - -widevine_description=Widevine Content Decryption Module provided by Google Inc. +widevine_name=Widevine Content Decryption Module provided by Google Inc. +widevine_description2=Play back protected web video. diff --git a/dom/media/AudioStream.h b/dom/media/AudioStream.h index acc38b93d..199314d4b 100644 --- a/dom/media/AudioStream.h +++ b/dom/media/AudioStream.h @@ -17,6 +17,10 @@ #include "mozilla/UniquePtr.h" #include "CubebUtils.h" #include "soundtouch/SoundTouchFactory.h" +#ifdef XP_SOLARIS +#include "soundtouch/SoundTouch.h" +#endif + namespace mozilla { diff --git a/dom/media/VideoUtils.cpp b/dom/media/VideoUtils.cpp index c06ba9070..56033c2fa 100644 --- a/dom/media/VideoUtils.cpp +++ b/dom/media/VideoUtils.cpp @@ -31,7 +31,6 @@ namespace mozilla { NS_NAMED_LITERAL_CSTRING(kEMEKeySystemClearkey, "org.w3.clearkey"); NS_NAMED_LITERAL_CSTRING(kEMEKeySystemWidevine, "com.widevine.alpha"); -NS_NAMED_LITERAL_CSTRING(kEMEKeySystemPrimetime, "com.adobe.primetime"); using layers::PlanarYCbCrImage; diff --git a/dom/media/VideoUtils.h b/dom/media/VideoUtils.h index aaf0e9903..eee6561fd 100644 --- a/dom/media/VideoUtils.h +++ b/dom/media/VideoUtils.h @@ -47,7 +47,6 @@ class MediaContentType; // EME Key System String. extern const nsLiteralCString kEMEKeySystemClearkey; extern const nsLiteralCString kEMEKeySystemWidevine; -extern const nsLiteralCString kEMEKeySystemPrimetime; /** * ReentrantMonitorConditionallyEnter diff --git a/dom/media/eme/EMEUtils.cpp b/dom/media/eme/EMEUtils.cpp index c248b3a24..11eb0026e 100644 --- a/dom/media/eme/EMEUtils.cpp +++ b/dom/media/eme/EMEUtils.cpp @@ -54,12 +54,6 @@ IsClearkeyKeySystem(const nsAString& aKeySystem) } bool -IsPrimetimeKeySystem(const nsAString& aKeySystem) -{ - return !CompareUTF8toUTF16(kEMEKeySystemPrimetime, aKeySystem); -} - -bool IsWidevineKeySystem(const nsAString& aKeySystem) { return !CompareUTF8toUTF16(kEMEKeySystemWidevine, aKeySystem); @@ -68,9 +62,6 @@ IsWidevineKeySystem(const nsAString& aKeySystem) nsString KeySystemToGMPName(const nsAString& aKeySystem) { - if (IsPrimetimeKeySystem(aKeySystem)) { - return NS_LITERAL_STRING("gmp-eme-adobe"); - } if (IsClearkeyKeySystem(aKeySystem)) { return NS_LITERAL_STRING("gmp-clearkey"); } @@ -88,8 +79,6 @@ ToCDMTypeTelemetryEnum(const nsString& aKeySystem) return CDMType::eWidevine; } else if (IsClearkeyKeySystem(aKeySystem)) { return CDMType::eClearKey; - } else if (IsPrimetimeKeySystem(aKeySystem)) { - return CDMType::ePrimetime; } return CDMType::eUnknown; } diff --git a/dom/media/eme/EMEUtils.h b/dom/media/eme/EMEUtils.h index 1794f8462..4a2e5da18 100644 --- a/dom/media/eme/EMEUtils.h +++ b/dom/media/eme/EMEUtils.h @@ -87,14 +87,10 @@ bool IsClearkeyKeySystem(const nsAString& aKeySystem); bool -IsPrimetimeKeySystem(const nsAString& aKeySystem); - -bool IsWidevineKeySystem(const nsAString& aKeySystem); enum CDMType { eClearKey = 0, - ePrimetime = 1, eWidevine = 2, eUnknown = 3 }; diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp index 4cff464e7..4a5a7a30c 100644 --- a/dom/media/eme/MediaKeySystemAccess.cpp +++ b/dom/media/eme/MediaKeySystemAccess.cpp @@ -134,16 +134,6 @@ MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem, return EnsureCDMInstalled(aKeySystem, aOutMessage); } - if (Preferences::GetBool("media.gmp-eme-adobe.visible", false)) { - if (IsPrimetimeKeySystem(aKeySystem)) { - if (!Preferences::GetBool("media.gmp-eme-adobe.enabled", false)) { - aOutMessage = NS_LITERAL_CSTRING("Adobe EME disabled"); - return MediaKeySystemStatus::Cdm_disabled; - } - return EnsureCDMInstalled(aKeySystem, aOutMessage); - } - } - if (IsWidevineKeySystem(aKeySystem)) { if (Preferences::GetBool("media.gmp-widevinecdm.visible", false)) { if (!Preferences::GetBool("media.gmp-widevinecdm.enabled", false)) { @@ -376,19 +366,6 @@ GetSupportedKeySystems() keySystemConfigs.AppendElement(Move(widevine)); } } - { - if (HavePluginForKeySystem(kEMEKeySystemPrimetime)) { - KeySystemConfig primetime; - primetime.mKeySystem = NS_ConvertUTF8toUTF16(kEMEKeySystemPrimetime); - primetime.mInitDataTypes.AppendElement(NS_LITERAL_STRING("cenc")); - primetime.mPersistentState = KeySystemFeatureSupport::Required; - primetime.mDistinctiveIdentifier = KeySystemFeatureSupport::Required; - primetime.mSessionTypes.AppendElement(MediaKeySessionType::Temporary); - primetime.mMP4.SetCanDecryptAndDecode(EME_CODEC_AAC); - primetime.mMP4.SetCanDecryptAndDecode(EME_CODEC_H264); - keySystemConfigs.AppendElement(Move(primetime)); - } - } return keySystemConfigs; } diff --git a/dom/media/eme/MediaKeySystemAccessManager.cpp b/dom/media/eme/MediaKeySystemAccessManager.cpp index 8fefc62ec..ed31059e2 100644 --- a/dom/media/eme/MediaKeySystemAccessManager.cpp +++ b/dom/media/eme/MediaKeySystemAccessManager.cpp @@ -95,8 +95,7 @@ MediaKeySystemAccessManager::Request(DetailedPromise* aPromise, // Ensure keysystem is supported. if (!IsWidevineKeySystem(aKeySystem) && - !IsClearkeyKeySystem(aKeySystem) && - !IsPrimetimeKeySystem(aKeySystem)) { + !IsClearkeyKeySystem(aKeySystem)) { // Not to inform user, because nothing to do if the keySystem is not // supported. aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR, @@ -132,7 +131,7 @@ MediaKeySystemAccessManager::Request(DetailedPromise* aPromise, LogToBrowserConsole(NS_ConvertUTF8toUTF16(msg)); if (status == MediaKeySystemStatus::Cdm_not_installed && - (IsPrimetimeKeySystem(aKeySystem) || IsWidevineKeySystem(aKeySystem))) { + IsWidevineKeySystem(aKeySystem)) { // These are cases which could be resolved by downloading a new(er) CDM. // When we send the status to chrome, chrome's GMPProvider will attempt to // download or update the CDM. In AwaitInstall() we add listeners to wait diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp index 418f14736..234ed5c05 100644 --- a/dom/media/gmp/GMPParent.cpp +++ b/dom/media/gmp/GMPParent.cpp @@ -726,16 +726,6 @@ GMPParent::ReadGMPInfoFile(nsIFile* aFile) if (cap.mAPIName.EqualsLiteral(GMP_API_DECRYPTOR)) { mCanDecrypt = true; - -#ifdef XP_WIN - // Adobe GMP doesn't work without SSE2. Check the tags to see if - // the decryptor is for the Adobe GMP, and refuse to load it if - // SSE2 isn't supported. - if (cap.mAPITags.Contains(kEMEKeySystemPrimetime) && - !mozilla::supports_sse2()) { - return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); - } -#endif // XP_WIN } mCapabilities.AppendElement(Move(cap)); diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp index 2b4831cd6..fcf9fa920 100644 --- a/dom/media/gmp/GMPServiceParent.cpp +++ b/dom/media/gmp/GMPServiceParent.cpp @@ -203,29 +203,6 @@ MoveAndOverwrite(nsIFile* aOldParentDir, } } -static void -MigratePreGecko42StorageDir(nsIFile* aOldStorageDir, - nsIFile* aNewStorageDir) -{ - MoveAndOverwrite(aOldStorageDir, aNewStorageDir, NS_LITERAL_STRING("id")); - MoveAndOverwrite(aOldStorageDir, aNewStorageDir, NS_LITERAL_STRING("storage")); -} - -static void -MigratePreGecko45StorageDir(nsIFile* aStorageDirBase) -{ - nsCOMPtr<nsIFile> adobeStorageDir(CloneAndAppend(aStorageDirBase, NS_LITERAL_STRING("gmp-eme-adobe"))); - if (NS_WARN_IF(!adobeStorageDir)) { - return; - } - - // The base storage dir in pre-45 contained "id" and "storage" subdirs. - // We assume all storage in the base storage dir that aren't known to GMP - // storage are records for the Adobe GMP. - MoveAndOverwrite(aStorageDirBase, adobeStorageDir, NS_LITERAL_STRING("id")); - MoveAndOverwrite(aStorageDirBase, adobeStorageDir, NS_LITERAL_STRING("storage")); -} - static nsresult GMPPlatformString(nsAString& aOutPlatform) { @@ -308,19 +285,6 @@ GeckoMediaPluginServiceParent::InitStorage() return rv; } - // Prior to 42, GMP storage was stored in $profileDir/gmp/. After 42, it's - // stored in $profileDir/gmp/$platform/. So we must migrate any old records - // from the old location to the new location, for forwards compatibility. - MigratePreGecko42StorageDir(gmpDirWithoutPlatform, mStorageBaseDir); - - // Prior to 45, GMP storage was not separated by plugin. In 45 and after, - // it's stored in $profile/gmp/$platform/$gmpName. So we must migrate old - // records from the old location to the new location, for forwards - // compatibility. We assume all directories in the base storage dir that - // aren't known to GMP storage are records for the Adobe GMP, since it - // was first. - MigratePreGecko45StorageDir(mStorageBaseDir); - return GeckoMediaPluginService::Init(); } diff --git a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp index cc53d2c93..50a5097ac 100644 --- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp +++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp @@ -109,7 +109,6 @@ GMPDecoderModule::PreferredGMP(const nsACString& aMimeType) if (aMimeType.EqualsLiteral("audio/mp4a-latm")) { switch (MediaPrefs::GMPAACPreferred()) { case 1: rv.emplace(kEMEKeySystemClearkey); break; - case 2: rv.emplace(kEMEKeySystemPrimetime); break; default: break; } } @@ -117,7 +116,6 @@ GMPDecoderModule::PreferredGMP(const nsACString& aMimeType) if (MP4Decoder::IsH264(aMimeType)) { switch (MediaPrefs::GMPH264Preferred()) { case 1: rv.emplace(kEMEKeySystemClearkey); break; - case 2: rv.emplace(kEMEKeySystemPrimetime); break; default: break; } } diff --git a/dom/media/test/external/external_media_harness/testcase.py b/dom/media/test/external/external_media_harness/testcase.py index 56350ccd9..35a944484 100644 --- a/dom/media/test/external/external_media_harness/testcase.py +++ b/dom/media/test/external/external_media_harness/testcase.py @@ -200,19 +200,6 @@ class NetworkBandwidthTestsMixin(object): self.run_videos(timeout=120) -reset_adobe_gmp_script = """ -navigator.requestMediaKeySystemAccess('com.adobe.primetime', -[{initDataTypes: ['cenc']}]).then( - function(access) { - marionetteScriptFinished('success'); - }, - function(ex) { - marionetteScriptFinished(ex); - } -); -""" - - reset_widevine_gmp_script = """ navigator.requestMediaKeySystemAccess('com.widevine.alpha', [{initDataTypes: ['cenc']}]).then( @@ -256,21 +243,12 @@ class EMESetupMixin(object): def reset_GMP_version(self): if EMESetupMixin.version_needs_reset: with self.marionette.using_context(Marionette.CONTEXT_CHROME): - if self.marionette.get_pref('media.gmp-eme-adobe.version'): - self.marionette.reset_pref('media.gmp-eme-adobe.version') if self.marionette.get_pref('media.gmp-widevinecdm.version'): self.marionette.reset_pref('media.gmp-widevinecdm.version') with self.marionette.using_context(Marionette.CONTEXT_CONTENT): - adobe_result = self.marionette.execute_async_script( - reset_adobe_gmp_script, - script_timeout=60000) widevine_result = self.marionette.execute_async_script( reset_widevine_gmp_script, script_timeout=60000) - if not adobe_result == 'success': - raise VideoException( - 'ERROR: Resetting Adobe GMP failed {}' - .format(adobe_result)) if not widevine_result == 'success': raise VideoException( 'ERROR: Resetting Widevine GMP failed {}' @@ -352,10 +330,6 @@ class EMESetupMixin(object): self.check_and_log_boolean_pref( 'media.mediasource.mp4.enabled', True), self.check_and_log_boolean_pref( - 'media.gmp-eme-adobe.enabled', True), - self.check_and_log_integer_pref( - 'media.gmp-eme-adobe.version', 1), - self.check_and_log_boolean_pref( 'media.gmp-widevinecdm.enabled', True), self.chceck_and_log_version_string_pref( 'media.gmp-widevinecdm.version', '1.0.0.0') diff --git a/dom/plugins/base/npapi.h b/dom/plugins/base/npapi.h index 12ac635c7..e554aaabc 100644 --- a/dom/plugins/base/npapi.h +++ b/dom/plugins/base/npapi.h @@ -327,9 +327,12 @@ typedef enum { #define NP_ABI_GCC3_MASK 0x10000000 /* * gcc 3.x generated vtables on UNIX and OSX are incompatible with - * previous compilers. + * previous compilers. Flash plugin binaries for Solaris were compiled + * with Sun Studio, so this has to be false to make things work. This may + * become a problem in the future when/if new plugins are compiled with + * GCC, however. */ -#if (defined(XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3)) +#if (defined(XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3) && !defined(XP_SOLARIS)) #define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK #else #define _NP_ABI_MIXIN_FOR_GCC3 0 diff --git a/dom/plugins/base/nptypes.h b/dom/plugins/base/nptypes.h index c36532472..d0cef6540 100644 --- a/dom/plugins/base/nptypes.h +++ b/dom/plugins/base/nptypes.h @@ -22,6 +22,19 @@ typedef unsigned int uint32_t; typedef long long int64_t; typedef unsigned long long uint64_t; +#elif defined(__sun) + /* + * SunOS ships an inttypes.h header that defines [u]int32_t, + * but not bool for C. + */ + #include <inttypes.h> + + + #ifndef __cplusplus + typedef int bool; + #define true 1 + #define false 0 + #endif #elif defined(bsdi) || defined(FREEBSD) || defined(OPENBSD) /* * BSD/OS, FreeBSD, and OpenBSD ship sys/types.h that define int32_t and diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index 05e0ec4ba..1d42c18d6 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -248,7 +248,6 @@ const static js::ObjectOps sNPObjectJSWrapperObjectOps = { nullptr, // setProperty nullptr, // getOwnPropertyDescriptor nullptr, // deleteProperty - nullptr, nullptr, // watch/unwatch nullptr, // getElements NPObjWrapper_Enumerate, nullptr, diff --git a/dom/plugins/base/nsPluginsDirUnix.cpp b/dom/plugins/base/nsPluginsDirUnix.cpp index e6956c34c..de3b7a2d1 100644 --- a/dom/plugins/base/nsPluginsDirUnix.cpp +++ b/dom/plugins/base/nsPluginsDirUnix.cpp @@ -19,7 +19,9 @@ #include "nsIPrefService.h" #define LOCAL_PLUGIN_DLL_SUFFIX ".so" -#if defined(LINUX) +#ifdef XP_SOLARIS +#define DEFAULT_X11_PATH "/usr/openwin/lib" +#elif defined(LINUX) #define DEFAULT_X11_PATH "/usr/X11R6/lib/" #elif defined(__APPLE__) #define DEFAULT_X11_PATH "/usr/X11R6/lib" @@ -92,7 +94,11 @@ static bool LoadExtraSharedLib(const char *name, char **soname, bool tryToGetSon #define PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS 32 #define PREF_PLUGINS_SONAME "plugin.soname.list" +#ifdef XP_SOLARIS +#define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX ":libXm" LOCAL_PLUGIN_DLL_SUFFIX +#else #define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX +#endif /* this function looks for user_pref("plugin.soname.list", "/usr/X11R6/lib/libXt.so.6:libXext.so"); @@ -264,11 +270,15 @@ nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary) // at runtime. Explicitly opening Xt/Xext into the global // namespace before attempting to load the plug-in seems to // work fine. - - +#if defined(XP_SOLARIS) + // Acrobat/libXm: Lazy resolving might cause crash later (bug 211587) + *outLibrary = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW); + pLibrary = *outLibrary; +#else // Some dlopen() doesn't recover from a failed PR_LD_NOW (bug 223744) *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0); pLibrary = *outLibrary; +#endif if (!pLibrary) { LoadExtraSharedLibs(); // try reload plugin once more diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index af9db9103..3f2cdbc13 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -310,9 +310,10 @@ PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue, switch (aValue) { case NPNVWindowNPObject: if (!(actor = mCachedWindowActor)) { + result = NPERR_GENERIC_ERROR; PPluginScriptableObjectChild* actorProtocol; - CallNPN_GetValue_NPNVWindowNPObject(&actorProtocol, &result); - if (result == NPERR_NO_ERROR) { + if (CallNPN_GetValue_NPNVWindowNPObject(&actorProtocol, &result) && + result == NPERR_NO_ERROR) { actor = mCachedWindowActor = static_cast<PluginScriptableObjectChild*>(actorProtocol); NS_ASSERTION(actor, "Null actor!"); @@ -324,10 +325,10 @@ PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue, case NPNVPluginElementNPObject: if (!(actor = mCachedElementActor)) { + result = NPERR_GENERIC_ERROR; PPluginScriptableObjectChild* actorProtocol; - CallNPN_GetValue_NPNVPluginElementNPObject(&actorProtocol, - &result); - if (result == NPERR_NO_ERROR) { + if (CallNPN_GetValue_NPNVPluginElementNPObject(&actorProtocol, &result) && + result == NPERR_NO_ERROR) { actor = mCachedElementActor = static_cast<PluginScriptableObjectChild*>(actorProtocol); NS_ASSERTION(actor, "Null actor!"); @@ -338,6 +339,7 @@ PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue, break; default: + result = NPERR_GENERIC_ERROR; NS_NOTREACHED("Don't know what to do with this value type!"); } @@ -434,6 +436,7 @@ PluginInstanceChild::NPN_GetValue(NPNVariable aVar, case NPNVWindowNPObject: // Intentional fall-through case NPNVPluginElementNPObject: { NPObject* object; + *((NPObject**)aValue) = nullptr; NPError result = InternalGetNPObjectForValue(aVar, &object); if (result == NPERR_NO_ERROR) { *((NPObject**)aValue) = object; diff --git a/dom/plugins/ipc/PluginMessageUtils.cpp b/dom/plugins/ipc/PluginMessageUtils.cpp index 47653fe6e..5b1d1667f 100644 --- a/dom/plugins/ipc/PluginMessageUtils.cpp +++ b/dom/plugins/ipc/PluginMessageUtils.cpp @@ -82,7 +82,7 @@ MediateRace(const MessageChannel::MessageInfo& parent, } } -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_SOLARIS) static string ReplaceAll(const string& haystack, const string& needle, const string& with) { @@ -101,7 +101,7 @@ ReplaceAll(const string& haystack, const string& needle, const string& with) string MungePluginDsoPath(const string& path) { -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_SOLARIS) // https://bugzilla.mozilla.org/show_bug.cgi?id=519601 return ReplaceAll(path, "netscape", "netsc@pe"); #else @@ -112,7 +112,7 @@ MungePluginDsoPath(const string& path) string UnmungePluginDsoPath(const string& munged) { -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_SOLARIS) return ReplaceAll(munged, "netsc@pe", "netscape"); #else return munged; diff --git a/dom/plugins/ipc/PluginModuleChild.cpp b/dom/plugins/ipc/PluginModuleChild.cpp index cbf6e509f..f943dfc42 100644 --- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -286,7 +286,7 @@ PluginModuleChild::InitForChrome(const std::string& aPluginFilename, // TODO: use PluginPRLibrary here -#if defined(OS_LINUX) || defined(OS_BSD) +#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS) mShutdownFunc = (NP_PLUGINSHUTDOWN) PR_FindFunctionSymbol(mLibrary, "NP_Shutdown"); @@ -1821,7 +1821,7 @@ PluginModuleChild::AnswerNP_GetEntryPoints(NPError* _retval) AssertPluginThread(); MOZ_ASSERT(mIsChrome); -#if defined(OS_LINUX) || defined(OS_BSD) +#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS) return true; #elif defined(OS_WIN) || defined(OS_MACOSX) *_retval = mGetEntryPointsFunc(&mFunctions); @@ -1866,7 +1866,7 @@ PluginModuleChild::DoNP_Initialize(const PluginSettings& aSettings) #endif NPError result; -#if defined(OS_LINUX) || defined(OS_BSD) +#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS) result = mInitializeFunc(&sBrowserFuncs, &mFunctions); #elif defined(OS_WIN) || defined(OS_MACOSX) result = mInitializeFunc(&sBrowserFuncs); diff --git a/dom/plugins/ipc/PluginModuleChild.h b/dom/plugins/ipc/PluginModuleChild.h index 681743582..5e4fa7d20 100644 --- a/dom/plugins/ipc/PluginModuleChild.h +++ b/dom/plugins/ipc/PluginModuleChild.h @@ -258,7 +258,7 @@ private: // we get this from the plugin NP_PLUGINSHUTDOWN mShutdownFunc; -#if defined(OS_LINUX) || defined(OS_BSD) +#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS) NP_PLUGINUNIXINIT mInitializeFunc; #elif defined(OS_WIN) || defined(OS_MACOSX) NP_PLUGININIT mInitializeFunc; diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index f636a9101..e5279345d 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -30,7 +30,6 @@ #include "nsJSPrincipals.h" #include "nsJSUtils.h" #include "nsPIDOMWindow.h" -#include "PromiseCallback.h" #include "PromiseDebugging.h" #include "PromiseNativeHandler.h" #include "PromiseWorkerProxy.h" @@ -49,467 +48,45 @@ 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 Runnable -{ -public: - PromiseReactionJob(Promise* aPromise, - PromiseCallback* aCallback, - const JS::Value& aValue) - : mPromise(aPromise) - , mCallback(aCallback) - , mValue(CycleCollectedJSContext::Get()->Context(), aValue) - { - MOZ_ASSERT(aPromise); - MOZ_ASSERT(aCallback); - MOZ_COUNT_CTOR(PromiseReactionJob); - } - - virtual - ~PromiseReactionJob() - { - NS_ASSERT_OWNINGTHREAD(PromiseReactionJob); - MOZ_COUNT_DTOR(PromiseReactionJob); - } - -protected: - NS_IMETHOD - Run() override - { - NS_ASSERT_OWNINGTHREAD(PromiseReactionJob); - - MOZ_ASSERT(mPromise->GetWrapper()); // It was preserved! - - AutoJSAPI jsapi; - if (!jsapi.Init(mPromise->GetWrapper())) { - return NS_ERROR_FAILURE; - } - JSContext* cx = jsapi.cx(); - - JS::Rooted<JS::Value> value(cx, mValue); - if (!MaybeWrapValue(cx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - JS_ClearPendingException(cx); - return NS_OK; - } - - JS::Rooted<JSObject*> asyncStack(cx, mPromise->mAllocationStack); - - { - Maybe<JS::AutoSetAsyncStackForNewCalls> sas; - if (asyncStack) { - sas.emplace(cx, asyncStack, "Promise"); - } - mCallback->Call(cx, value); - } - - return NS_OK; - } - -private: - RefPtr<Promise> mPromise; - RefPtr<PromiseCallback> mCallback; - JS::PersistentRooted<JS::Value> mValue; - NS_DECL_OWNINGTHREAD; -}; - -/* - * Utilities for thenable callbacks. - * - * A thenable is a { then: function(resolve, reject) { } }. - * `then` is called with a resolve and reject callback pair. - * Since only one of these should be called at most once (first call wins), the - * two keep a reference to each other in SLOT_DATA. When either of them is - * called, the references are cleared. Further calls are ignored. - */ -namespace { -void -LinkThenableCallables(JSContext* aCx, JS::Handle<JSObject*> aResolveFunc, - JS::Handle<JSObject*> aRejectFunc) -{ - js::SetFunctionNativeReserved(aResolveFunc, Promise::SLOT_DATA, - JS::ObjectValue(*aRejectFunc)); - js::SetFunctionNativeReserved(aRejectFunc, Promise::SLOT_DATA, - JS::ObjectValue(*aResolveFunc)); -} - -/* - * Returns false if callback was already called before, otherwise breaks the - * links and returns true. - */ -bool -MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle<JSObject*> aFunc) -{ - JS::Value otherFuncVal = - js::GetFunctionNativeReserved(aFunc, Promise::SLOT_DATA); - - if (!otherFuncVal.isObject()) { - return false; - } - - JSObject* otherFuncObj = &otherFuncVal.toObject(); - MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, - Promise::SLOT_DATA).isObject()); - - // Break both references. - js::SetFunctionNativeReserved(aFunc, Promise::SLOT_DATA, - JS::UndefinedValue()); - js::SetFunctionNativeReserved(otherFuncObj, Promise::SLOT_DATA, - JS::UndefinedValue()); - - return true; -} - -Promise* -GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc) -{ - JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, - Promise::SLOT_PROMISE); - - MOZ_ASSERT(promiseVal.isObject()); - - Promise* promise; - UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise); - return promise; -} -} // namespace - -// Runnable to resolve thenables. -// Equivalent to the specification's ResolvePromiseViaThenableTask. -class PromiseResolveThenableJob final : public Runnable -{ -public: - PromiseResolveThenableJob(Promise* aPromise, - JS::Handle<JSObject*> aThenable, - PromiseInit* aThen) - : mPromise(aPromise) - , mThenable(CycleCollectedJSContext::Get()->Context(), aThenable) - , mThen(aThen) - { - MOZ_ASSERT(aPromise); - MOZ_COUNT_CTOR(PromiseResolveThenableJob); - } - - virtual - ~PromiseResolveThenableJob() - { - NS_ASSERT_OWNINGTHREAD(PromiseResolveThenableJob); - MOZ_COUNT_DTOR(PromiseResolveThenableJob); - } - -protected: - NS_IMETHOD - Run() override - { - NS_ASSERT_OWNINGTHREAD(PromiseResolveThenableJob); - - MOZ_ASSERT(mPromise->GetWrapper()); // It was preserved! - - AutoJSAPI jsapi; - // If we ever change which compartment we're working in here, make sure to - // fix the fast-path for resolved-with-a-Promise in ResolveInternal. - if (!jsapi.Init(mPromise->GetWrapper())) { - return NS_ERROR_FAILURE; - } - JSContext* cx = jsapi.cx(); - - JS::Rooted<JSObject*> resolveFunc(cx, - mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Resolve)); - - if (!resolveFunc) { - mPromise->HandleException(cx); - return NS_OK; - } - - JS::Rooted<JSObject*> rejectFunc(cx, - mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Reject)); - if (!rejectFunc) { - mPromise->HandleException(cx); - return NS_OK; - } - - LinkThenableCallables(cx, resolveFunc, rejectFunc); - - ErrorResult rv; - - JS::Rooted<JSObject*> rootedThenable(cx, mThenable); - - mThen->Call(rootedThenable, resolveFunc, rejectFunc, rv, - "promise thenable", CallbackObject::eRethrowExceptions, - mPromise->Compartment()); - - rv.WouldReportJSException(); - if (rv.Failed()) { - JS::Rooted<JS::Value> exn(cx); - { // Scope for JSAutoCompartment - - // Convert the ErrorResult to a JS exception object that we can reject - // ourselves with. This will be exactly the exception that would get - // thrown from a binding method whose ErrorResult ended up with - // whatever is on "rv" right now. - JSAutoCompartment ac(cx, mPromise->GlobalJSObject()); - DebugOnly<bool> conversionResult = ToJSValue(cx, rv, &exn); - MOZ_ASSERT(conversionResult); - } - - bool couldMarkAsCalled = MarkAsCalledIfNotCalledBefore(cx, resolveFunc); - - // If we could mark as called, neither of the callbacks had been called - // when the exception was thrown. So we can reject the Promise. - if (couldMarkAsCalled) { - bool ok = JS_WrapValue(cx, &exn); - MOZ_ASSERT(ok); - if (!ok) { - NS_WARNING("Failed to wrap value into the right compartment."); - } - - mPromise->RejectInternal(cx, exn); - } - // At least one of resolveFunc or rejectFunc have been called, so ignore - // the exception. FIXME(nsm): This should be reported to the error - // console though, for debugging. - } - - return rv.StealNSResult(); - } - -private: - RefPtr<Promise> mPromise; - JS::PersistentRooted<JSObject*> mThenable; - RefPtr<PromiseInit> mThen; - NS_DECL_OWNINGTHREAD; -}; - -// A struct implementing -// <http://www.ecma-international.org/ecma-262/6.0/#sec-promisecapability-records>. -// While the spec holds on to these in some places, in practice those places -// don't actually need everything from this struct, so we explicitly grab -// members from it as needed in those situations. That allows us to make this a -// stack-only struct and keep the rooting simple. -// -// We also add an optimization for the (common) case when we discover that the -// Promise constructor we're supposed to use is in fact the canonical Promise -// constructor. In that case we will just set mNativePromise in our -// PromiseCapability and not set mPromise/mResolve/mReject; the correct -// callbacks will be the standard Promise ones, and we don't really want to -// synthesize JSFunctions for them in that situation. -struct MOZ_STACK_CLASS Promise::PromiseCapability -{ - explicit PromiseCapability(JSContext* aCx) - : mPromise(aCx) - , mResolve(aCx) - , mReject(aCx) - {} - - // Take an exception on aCx and try to convert it into a promise rejection. - // Note that this can result in a new exception being thrown on aCx, or an - // exception getting thrown on aRv. On entry to this method, aRv is assumed - // to not be a failure. This should only be called if NewPromiseCapability - // succeeded on this PromiseCapability. - void RejectWithException(JSContext* aCx, ErrorResult& aRv); - - // Return a JS::Value representing the promise. This should only be called if - // NewPromiseCapability succeeded on this PromiseCapability. It makes no - // guarantees about compartments (e.g. in the mNativePromise case it's in the - // compartment of the reflector, but in the mPromise case it might be in the - // compartment of some cross-compartment wrapper for a reflector). - JS::Value PromiseValue() const; - - // All the JS::Value fields of this struct are actually objects, but for our - // purposes it's simpler to store them as JS::Value. - - // [[Promise]]. - JS::Rooted<JSObject*> mPromise; - // [[Resolve]]. Value in the context compartment. - JS::Rooted<JS::Value> mResolve; - // [[Reject]]. Value in the context compartment. - JS::Rooted<JS::Value> mReject; - // If mNativePromise is non-null, we should use it, not mPromise. - RefPtr<Promise> mNativePromise; - -private: - // We don't want to allow creation of temporaries of this type, ever. - PromiseCapability(const PromiseCapability&) = delete; - PromiseCapability(PromiseCapability&&) = delete; -}; - -void -Promise::PromiseCapability::RejectWithException(JSContext* aCx, - ErrorResult& aRv) -{ - // This method basically implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-ifabruptrejectpromise - // or at least the parts of it that happen if we have an abrupt completion. - - MOZ_ASSERT(!aRv.Failed()); - MOZ_ASSERT(mNativePromise || mPromise, - "NewPromiseCapability didn't succeed"); - - JS::Rooted<JS::Value> exn(aCx); - if (!JS_GetPendingException(aCx, &exn)) { - // This is an uncatchable exception, so can't be converted into a rejection. - // Just rethrow that on aRv. - aRv.ThrowUncatchableException(); - return; - } - - JS_ClearPendingException(aCx); - - // If we have a native promise, just reject it without trying to call out into - // JS. - if (mNativePromise) { - mNativePromise->MaybeRejectInternal(aCx, exn); - return; - } - - JS::Rooted<JS::Value> ignored(aCx); - if (!JS::Call(aCx, JS::UndefinedHandleValue, mReject, JS::HandleValueArray(exn), - &ignored)) { - aRv.NoteJSContextException(aCx); - } -} - -JS::Value -Promise::PromiseCapability::PromiseValue() const -{ - MOZ_ASSERT(mNativePromise || mPromise, - "NewPromiseCapability didn't succeed"); - - if (mNativePromise) { - return JS::ObjectValue(*mNativePromise->GetWrapper()); - } - - 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_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise) -#ifndef SPIDERMONKEY_PROMISE - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_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()) { - tmp->mResult.exposeToActiveJS(); - tmp->mAllocationStack.exposeToActiveJS(); - tmp->mRejectionStack.exposeToActiveJS(); - tmp->mFullfillmentStack.exposeToActiveJS(); - return true; - } -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END - -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Promise) - return tmp->IsBlackAndDoesNotNeedTracing(tmp); -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END - -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Promise) - return tmp->IsBlack(); -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END -#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) - , mFullfillmentStack(nullptr) - , mState(Pending) -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - , mHadRejectCallback(false) -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - , mTaskPending(false) - , mResolvePending(false) - , 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 - aWrapper.set(mPromiseObj); - return true; -} - // static already_AddRefed<Promise> Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv) @@ -884,8 +461,7 @@ 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. + // This is only called from MaybeSomething, so it's OK to MaybeReject here. MaybeReject(aCx, exn); } } @@ -902,80 +478,6 @@ Promise::CreateFromExisting(nsIGlobalObject* aGlobal, return p.forget(); } -#else // SPIDERMONKEY_PROMISE - -JSObject* -Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) -{ - return PromiseBinding::Wrap(aCx, this, aGivenProto); -} - -already_AddRefed<Promise> -Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv, - JS::Handle<JSObject*> aDesiredProto) -{ - if (!aGlobal) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - RefPtr<Promise> p = new Promise(aGlobal); - p->CreateWrapper(aDesiredProto, aRv); - if (aRv.Failed()) { - return nullptr; - } - return p.forget(); -} - -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(); - - JS::Rooted<JS::Value> wrapper(cx); - if (!GetOrCreateDOMReflector(cx, this, &wrapper, aDesiredProto)) { - JS_ClearPendingException(cx); - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return; - } - - dom::PreserveWrapper(this); - - // Now grab our allocation stack - if (!CaptureStack(cx, mAllocationStack)) { - JS_ClearPendingException(cx); - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return; - } - - JS::RootedObject obj(cx, &wrapper.toObject()); - JS::dbg::onNewPromise(cx, obj); -} - -void -Promise::MaybeResolve(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - MaybeResolveInternal(aCx, aValue); -} - -void -Promise::MaybeReject(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - MaybeRejectInternal(aCx, aValue); -} - -#endif // SPIDERMONKEY_PROMISE - void Promise::MaybeResolveWithUndefined() { @@ -999,7 +501,6 @@ Promise::MaybeRejectWithUndefined() MaybeSomething(JS::UndefinedHandleValue, &Promise::MaybeReject); } -#ifdef SPIDERMONKEY_PROMISE void Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise) { @@ -1026,7 +527,6 @@ Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise) // Now post an event to do the real reporting async NS_DispatchToMainThread(new AsyncErrorReporter(xpcReport)); } -#endif // defined(SPIDERMONKEY_PROMISE) bool Promise::PerformMicroTaskCheckpoint() @@ -1132,1422 +632,6 @@ Promise::PerformWorkerDebuggerMicroTaskCheckpoint() } } -#ifndef SPIDERMONKEY_PROMISE - -/* static */ bool -Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp) -{ - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); - - JS::Rooted<JS::Value> v(aCx, - js::GetFunctionNativeReserved(&args.callee(), - SLOT_PROMISE)); - MOZ_ASSERT(v.isObject()); - - Promise* promise; - if (NS_FAILED(UNWRAP_OBJECT(Promise, &v.toObject(), promise))) { - return Throw(aCx, NS_ERROR_UNEXPECTED); - } - - v = js::GetFunctionNativeReserved(&args.callee(), SLOT_DATA); - PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32()); - - if (task == PromiseCallback::Resolve) { - if (!promise->CaptureStack(aCx, promise->mFullfillmentStack)) { - return false; - } - promise->MaybeResolveInternal(aCx, args.get(0)); - } else { - promise->MaybeRejectInternal(aCx, args.get(0)); - if (!promise->CaptureStack(aCx, promise->mRejectionStack)) { - return false; - } - } - - args.rval().setUndefined(); - return true; -} - -/* - * Common bits of (JSCallbackThenableResolver/JSCallbackThenableRejecter). - * Resolves/rejects the Promise if it is ok to do so, based on whether either of - * the callbacks have been called before or not. - */ -/* static */ bool -Promise::ThenableResolverCommon(JSContext* aCx, uint32_t aTask, - unsigned aArgc, JS::Value* aVp) -{ - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); - JS::Rooted<JSObject*> thisFunc(aCx, &args.callee()); - if (!MarkAsCalledIfNotCalledBefore(aCx, thisFunc)) { - // A function from this pair has been called before. - args.rval().setUndefined(); - return true; - } - - Promise* promise = GetPromise(aCx, thisFunc); - MOZ_ASSERT(promise); - - if (aTask == PromiseCallback::Resolve) { - promise->ResolveInternal(aCx, args.get(0)); - } else { - promise->RejectInternal(aCx, args.get(0)); - } - - args.rval().setUndefined(); - return true; -} - -/* static */ bool -Promise::JSCallbackThenableResolver(JSContext* aCx, - unsigned aArgc, JS::Value* aVp) -{ - return ThenableResolverCommon(aCx, PromiseCallback::Resolve, aArgc, aVp); -} - -/* static */ bool -Promise::JSCallbackThenableRejecter(JSContext* aCx, - unsigned aArgc, JS::Value* aVp) -{ - return ThenableResolverCommon(aCx, PromiseCallback::Reject, aArgc, aVp); -} - -/* static */ JSObject* -Promise::CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask) -{ - // If this function ever changes, make sure to update - // WrapperPromiseCallback::GetDependentPromise. - JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback, - 1 /* nargs */, 0 /* flags */, - nullptr); - if (!func) { - return nullptr; - } - - JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func)); - - JS::Rooted<JS::Value> promiseObj(aCx); - if (!dom::GetOrCreateDOMReflector(aCx, aPromise, &promiseObj)) { - return nullptr; - } - - JS::ExposeValueToActiveJS(promiseObj); - js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj); - js::SetFunctionNativeReserved(obj, SLOT_DATA, JS::Int32Value(aTask)); - - return obj; -} - -/* static */ JSObject* -Promise::CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask) -{ - JSNative whichFunc = - aTask == PromiseCallback::Resolve ? JSCallbackThenableResolver : - JSCallbackThenableRejecter ; - - JSFunction* func = js::NewFunctionWithReserved(aCx, whichFunc, - 1 /* nargs */, 0 /* flags */, - nullptr); - if (!func) { - return nullptr; - } - - JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func)); - - JS::Rooted<JS::Value> promiseObj(aCx); - if (!dom::GetOrCreateDOMReflector(aCx, aPromise, &promiseObj)) { - return nullptr; - } - - JS::ExposeValueToActiveJS(promiseObj); - js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj); - - return obj; -} - -/* static */ already_AddRefed<Promise> -Promise::Constructor(const GlobalObject& aGlobal, PromiseInit& aInit, - ErrorResult& aRv, JS::Handle<JSObject*> aDesiredProto) -{ - nsCOMPtr<nsIGlobalObject> global; - global = do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - RefPtr<Promise> promise = Create(global, aRv, aDesiredProto); - if (aRv.Failed()) { - return nullptr; - } - - promise->CallInitFunction(aGlobal, aInit, aRv); - if (aRv.Failed()) { - return nullptr; - } - - return promise.forget(); -} - -void -Promise::CallInitFunction(const GlobalObject& aGlobal, - PromiseInit& aInit, ErrorResult& aRv) -{ - JSContext* cx = aGlobal.Context(); - - JS::Rooted<JSObject*> resolveFunc(cx, - CreateFunction(cx, this, - PromiseCallback::Resolve)); - if (!resolveFunc) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - JS::Rooted<JSObject*> rejectFunc(cx, - CreateFunction(cx, this, - PromiseCallback::Reject)); - if (!rejectFunc) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - aInit.Call(resolveFunc, rejectFunc, aRv, "promise initializer", - CallbackObject::eRethrowExceptions, Compartment()); - aRv.WouldReportJSException(); - - if (aRv.Failed()) { - if (aRv.IsUncatchableException()) { - // Just propagate this to the caller. - return; - } - - // There are two possibilities here. Either we've got a rethrown exception, - // or we reported that already and synthesized a generic NS_ERROR_FAILURE on - // the ErrorResult. In the former case, it doesn't much matter how we get - // the exception JS::Value from the ErrorResult to us, since we'll just end - // up wrapping it into the right compartment as needed if we hand it to - // someone. But in the latter case we have to ensure that the new exception - // object we create is created in our reflector compartment, not in our - // current compartment, because in the case when we're a Promise constructor - // called over Xrays creating it in the current compartment would mean - // rejecting with a value that can't be accessed by code that can call - // then() on this Promise. - // - // Luckily, MaybeReject(aRv) does exactly what we want here: it enters our - // reflector compartment before trying to produce a JS::Value from the - // ErrorResult. - MaybeReject(aRv); - } -} - -#define GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT 0 -#define GET_CAPABILITIES_EXECUTOR_REJECT_SLOT 1 - -namespace { -bool -GetCapabilitiesExecutor(JSContext* aCx, unsigned aArgc, JS::Value* aVp) -{ - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-getcapabilitiesexecutor-functions - // except we store the [[Resolve]] and [[Reject]] in our own internal slots, - // not in a PromiseCapability. The PromiseCapability will then read them from - // us. - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); - - // Step 1 is an assert. - - // Step 2 doesn't need to be done, because it's just giving a name to the - // PromiseCapability record which is supposed to be stored in an internal - // slot. But we don't store that at all, per the comment above; we just - // directly store its [[Resolve]] and [[Reject]] members. - - // Steps 3 and 4. - if (!js::GetFunctionNativeReserved(&args.callee(), - GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT).isUndefined() || - !js::GetFunctionNativeReserved(&args.callee(), - GET_CAPABILITIES_EXECUTOR_REJECT_SLOT).isUndefined()) { - ErrorResult rv; - rv.ThrowTypeError<MSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY>(); - return !rv.MaybeSetPendingException(aCx); - } - - // Step 5. - js::SetFunctionNativeReserved(&args.callee(), - GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT, - args.get(0)); - - // Step 6. - js::SetFunctionNativeReserved(&args.callee(), - GET_CAPABILITIES_EXECUTOR_REJECT_SLOT, - args.get(1)); - - // Step 7. - args.rval().setUndefined(); - return true; -} -} // anonymous namespace - -/* static */ void -Promise::NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal, - JS::Handle<JS::Value> aConstructor, - bool aForceCallbackCreation, - PromiseCapability& aCapability, - ErrorResult& aRv) -{ - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-newpromisecapability - - if (!aConstructor.isObject() || - !JS::IsConstructor(&aConstructor.toObject())) { - aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>(); - return; - } - - // Step 2 is a note. - // Step 3 is already done because we got the PromiseCapability passed in. - - // Optimization: Check whether constructor is in fact the canonical - // Promise constructor for aGlobal. - JS::Rooted<JSObject*> global(aCx, aGlobal->GetGlobalJSObject()); - { - // Scope for the JSAutoCompartment, since we need to enter the compartment - // of global to get constructors from it. Save the compartment we used to - // be in, though; we'll need it later. - JS::Rooted<JSObject*> callerGlobal(aCx, JS::CurrentGlobalOrNull(aCx)); - JSAutoCompartment ac(aCx, global); - - // Now wrap aConstructor into the compartment of aGlobal, so comparing it to - // the canonical Promise for that compartment actually makes sense. - JS::Rooted<JS::Value> constructorValue(aCx, aConstructor); - if (!MaybeWrapObjectValue(aCx, &constructorValue)) { - aRv.NoteJSContextException(aCx); - return; - } - - JSObject* defaultCtor = PromiseBinding::GetConstructorObject(aCx); - if (!defaultCtor) { - aRv.NoteJSContextException(aCx); - return; - } - if (defaultCtor == &constructorValue.toObject()) { - // This is the canonical Promise constructor. - aCapability.mNativePromise = Promise::Create(aGlobal, aRv); - if (aForceCallbackCreation) { - // We have to be a bit careful here. We want to create these functions - // in the compartment in which they would be created if we actually - // invoked the constructor via JS::Construct below. That means our - // callerGlobal compartment if aConstructor is an Xray and the reflector - // compartment of the promise we're creating otherwise. But note that - // our callerGlobal compartment is precisely the reflector compartment - // unless the call was done over Xrays, because the reflector - // compartment comes from xpc::XrayAwareCalleeGlobal. So we really just - // want to create these functions in the callerGlobal compartment. - MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(&aConstructor.toObject()) || - callerGlobal == global); - JSAutoCompartment ac2(aCx, callerGlobal); - - JSObject* resolveFuncObj = - CreateFunction(aCx, aCapability.mNativePromise, - PromiseCallback::Resolve); - if (!resolveFuncObj) { - aRv.NoteJSContextException(aCx); - return; - } - aCapability.mResolve.setObject(*resolveFuncObj); - - JSObject* rejectFuncObj = - CreateFunction(aCx, aCapability.mNativePromise, - PromiseCallback::Reject); - if (!rejectFuncObj) { - aRv.NoteJSContextException(aCx); - return; - } - aCapability.mReject.setObject(*rejectFuncObj); - } - return; - } - } - - // Step 4. - // We can create our get-capabilities function in the calling compartment. It - // will work just as if we did |new promiseConstructor(function(a,b){}). - // Notably, if we're called over Xrays that's all fine, because we will end up - // creating the callbacks in the caller compartment in that case. - JSFunction* getCapabilitiesFunc = - js::NewFunctionWithReserved(aCx, GetCapabilitiesExecutor, - 2 /* nargs */, - 0 /* flags */, - nullptr); - if (!getCapabilitiesFunc) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return; - } - - JS::Rooted<JSObject*> getCapabilitiesObj(aCx); - getCapabilitiesObj = JS_GetFunctionObject(getCapabilitiesFunc); - - // Step 5 doesn't need to be done, since we're not actually storing a - // PromiseCapability in the executor; see the comments in - // GetCapabilitiesExecutor above. - - // Step 6 and step 7. - JS::Rooted<JS::Value> getCapabilities(aCx, - JS::ObjectValue(*getCapabilitiesObj)); - JS::Rooted<JSObject*> promiseObj(aCx); - if (!JS::Construct(aCx, aConstructor, - JS::HandleValueArray(getCapabilities), - &promiseObj)) { - aRv.NoteJSContextException(aCx); - return; - } - - // Step 8 plus copying over the value to the PromiseCapability. - JS::Rooted<JS::Value> v(aCx); - v = js::GetFunctionNativeReserved(getCapabilitiesObj, - GET_CAPABILITIES_EXECUTOR_RESOLVE_SLOT); - if (!v.isObject() || !JS::IsCallable(&v.toObject())) { - aRv.ThrowTypeError<MSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE>(); - return; - } - aCapability.mResolve = v; - - // Step 9 plus copying over the value to the PromiseCapability. - v = js::GetFunctionNativeReserved(getCapabilitiesObj, - GET_CAPABILITIES_EXECUTOR_REJECT_SLOT); - if (!v.isObject() || !JS::IsCallable(&v.toObject())) { - aRv.ThrowTypeError<MSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE>(); - return; - } - aCapability.mReject = v; - - // Step 10. - aCapability.mPromise = promiseObj; - - // Step 11 doesn't need anything, since the PromiseCapability was passed in. -} - -/* static */ void -Promise::Resolve(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv, - JS::Handle<JS::Value> aValue, - JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) -{ - // Implementation of - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.resolve - - JSContext* cx = aGlobal.Context(); - - nsCOMPtr<nsIGlobalObject> global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - // Steps 1 and 2. - if (!aThisv.isObject()) { - aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>(); - return; - } - - // Step 3. If a Promise was passed and matches our constructor, just return it. - if (aValue.isObject()) { - JS::Rooted<JSObject*> valueObj(cx, &aValue.toObject()); - Promise* nextPromise; - nsresult rv = UNWRAP_OBJECT(Promise, valueObj, nextPromise); - - if (NS_SUCCEEDED(rv)) { - JS::Rooted<JS::Value> constructor(cx); - if (!JS_GetProperty(cx, valueObj, "constructor", &constructor)) { - aRv.NoteJSContextException(cx); - return; - } - - // Cheat instead of calling JS_SameValue, since we know one's an object. - if (aThisv == constructor) { - aRetval.setObject(*valueObj); - return; - } - } - } - - // Step 4. - PromiseCapability capability(cx); - NewPromiseCapability(cx, global, aThisv, false, capability, aRv); - // Step 5. - if (aRv.Failed()) { - return; - } - - // Step 6. - Promise* p = capability.mNativePromise; - if (p) { - p->MaybeResolveInternal(cx, aValue); - p->mFullfillmentStack = p->mAllocationStack; - } else { - JS::Rooted<JS::Value> value(cx, aValue); - JS::Rooted<JS::Value> ignored(cx); - if (!JS::Call(cx, JS::UndefinedHandleValue /* thisVal */, - capability.mResolve, JS::HandleValueArray(value), - &ignored)) { - // Step 7. - aRv.NoteJSContextException(cx); - return; - } - } - - // Step 8. - aRetval.set(capability.PromiseValue()); -} - -/* static */ already_AddRefed<Promise> -Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx, - JS::Handle<JS::Value> aValue, ErrorResult& aRv) -{ - RefPtr<Promise> promise = Create(aGlobal, aRv); - if (aRv.Failed()) { - return nullptr; - } - - promise->MaybeResolveInternal(aCx, aValue); - return promise.forget(); -} - -/* static */ void -Promise::Reject(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv, - JS::Handle<JS::Value> aValue, - JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) -{ - // Implementation of - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.reject - - JSContext* cx = aGlobal.Context(); - - nsCOMPtr<nsIGlobalObject> global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - // Steps 1 and 2. - if (!aThisv.isObject()) { - aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>(); - return; - } - - // Step 3. - PromiseCapability capability(cx); - NewPromiseCapability(cx, global, aThisv, false, capability, aRv); - // Step 4. - if (aRv.Failed()) { - return; - } - - // Step 5. - Promise* p = capability.mNativePromise; - if (p) { - p->MaybeRejectInternal(cx, aValue); - p->mRejectionStack = p->mAllocationStack; - } else { - JS::Rooted<JS::Value> value(cx, aValue); - JS::Rooted<JS::Value> ignored(cx); - if (!JS::Call(cx, JS::UndefinedHandleValue /* thisVal */, - capability.mReject, JS::HandleValueArray(value), - &ignored)) { - // Step 6. - aRv.NoteJSContextException(cx); - return; - } - } - - // Step 7. - aRetval.set(capability.PromiseValue()); -} - -/* static */ already_AddRefed<Promise> -Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx, - JS::Handle<JS::Value> aValue, ErrorResult& aRv) -{ - RefPtr<Promise> promise = Create(aGlobal, aRv); - if (aRv.Failed()) { - return nullptr; - } - - promise->MaybeRejectInternal(aCx, aValue); - return promise.forget(); -} - -namespace { -void -SpeciesConstructor(JSContext* aCx, - JS::Handle<JSObject*> promise, - JS::Handle<JS::Value> defaultCtor, - JS::MutableHandle<JS::Value> ctor, - ErrorResult& aRv) -{ - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-speciesconstructor - - // Step 1. - MOZ_ASSERT(promise); - - // Step 2. - JS::Rooted<JS::Value> constructorVal(aCx); - if (!JS_GetProperty(aCx, promise, "constructor", &constructorVal)) { - // Step 3. - aRv.NoteJSContextException(aCx); - return; - } - - // Step 4. - if (constructorVal.isUndefined()) { - ctor.set(defaultCtor); - return; - } - - // Step 5. - if (!constructorVal.isObject()) { - aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>(); - return; - } - - // Step 6. - JS::Rooted<jsid> species(aCx, - SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species))); - JS::Rooted<JS::Value> speciesVal(aCx); - JS::Rooted<JSObject*> constructorObj(aCx, &constructorVal.toObject()); - if (!JS_GetPropertyById(aCx, constructorObj, species, &speciesVal)) { - // Step 7. - aRv.NoteJSContextException(aCx); - return; - } - - // Step 8. - if (speciesVal.isNullOrUndefined()) { - ctor.set(defaultCtor); - return; - } - - // Step 9. - if (speciesVal.isObject() && JS::IsConstructor(&speciesVal.toObject())) { - ctor.set(speciesVal); - return; - } - - // Step 10. - aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>(); -} -} // anonymous namespace - -void -Promise::Then(JSContext* aCx, JS::Handle<JSObject*> aCalleeGlobal, - AnyCallback* aResolveCallback, AnyCallback* aRejectCallback, - JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.then - - // Step 1. - JS::Rooted<JS::Value> promiseVal(aCx, JS::ObjectValue(*GetWrapper())); - if (!MaybeWrapObjectValue(aCx, &promiseVal)) { - aRv.NoteJSContextException(aCx); - return; - } - JS::Rooted<JSObject*> promiseObj(aCx, &promiseVal.toObject()); - MOZ_ASSERT(promiseObj); - - // Step 2 was done by the bindings. - - // Step 3. We want to use aCalleeGlobal here because it will do the - // right thing for us via Xrays (where we won't find @@species on - // our promise constructor for now). - JS::Rooted<JS::Value> defaultCtorVal(aCx); - { // Scope for JSAutoCompartment - JSAutoCompartment ac(aCx, aCalleeGlobal); - JSObject* defaultCtor = PromiseBinding::GetConstructorObject(aCx); - if (!defaultCtor) { - aRv.NoteJSContextException(aCx); - return; - } - defaultCtorVal.setObject(*defaultCtor); - } - if (!MaybeWrapObjectValue(aCx, &defaultCtorVal)) { - aRv.NoteJSContextException(aCx); - return; - } - - JS::Rooted<JS::Value> constructor(aCx); - SpeciesConstructor(aCx, promiseObj, defaultCtorVal, &constructor, aRv); - if (aRv.Failed()) { - // Step 4. - return; - } - - // Step 5. - GlobalObject globalObj(aCx, GetWrapper()); - if (globalObj.Failed()) { - aRv.NoteJSContextException(aCx); - return; - } - nsCOMPtr<nsIGlobalObject> globalObject = - do_QueryInterface(globalObj.GetAsSupports()); - if (!globalObject) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - PromiseCapability capability(aCx); - NewPromiseCapability(aCx, globalObject, constructor, false, capability, aRv); - if (aRv.Failed()) { - // Step 6. - return; - } - - // Now step 7: start - // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromisethen - - // Step 1 and step 2 are just assertions. - - // Step 3 and step 4 are kinda handled for us already; we use null - // to represent "Identity" and "Thrower". - - // Steps 5 and 6. These branch based on whether we know we have a - // vanilla Promise or not. - JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx)); - if (capability.mNativePromise) { - Promise* promise = capability.mNativePromise; - - RefPtr<PromiseCallback> resolveCb = - PromiseCallback::Factory(promise, global, aResolveCallback, - PromiseCallback::Resolve); - - RefPtr<PromiseCallback> rejectCb = - PromiseCallback::Factory(promise, global, aRejectCallback, - PromiseCallback::Reject); - - AppendCallbacks(resolveCb, rejectCb); - } else { - JS::Rooted<JSObject*> resolveObj(aCx, &capability.mResolve.toObject()); - RefPtr<AnyCallback> resolveFunc = - new AnyCallback(aCx, resolveObj, GetIncumbentGlobal()); - - JS::Rooted<JSObject*> rejectObj(aCx, &capability.mReject.toObject()); - RefPtr<AnyCallback> rejectFunc = - new AnyCallback(aCx, rejectObj, GetIncumbentGlobal()); - - if (!capability.mPromise) { - aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>(); - return; - } - JS::Rooted<JSObject*> newPromiseObj(aCx, capability.mPromise); - // We want to store the reflector itself. - newPromiseObj = js::CheckedUnwrap(newPromiseObj); - if (!newPromiseObj) { - // Just throw something. - aRv.ThrowTypeError<MSG_ILLEGAL_PROMISE_CONSTRUCTOR>(); - return; - } - - RefPtr<PromiseCallback> resolveCb; - if (aResolveCallback) { - resolveCb = new WrapperPromiseCallback(global, aResolveCallback, - newPromiseObj, - resolveFunc, rejectFunc); - } else { - resolveCb = new InvokePromiseFuncCallback(global, newPromiseObj, - resolveFunc); - } - - RefPtr<PromiseCallback> rejectCb; - if (aRejectCallback) { - rejectCb = new WrapperPromiseCallback(global, aRejectCallback, - newPromiseObj, - resolveFunc, rejectFunc); - } else { - rejectCb = new InvokePromiseFuncCallback(global, newPromiseObj, - rejectFunc); - } - - AppendCallbacks(resolveCb, rejectCb); - } - - aRetval.set(capability.PromiseValue()); -} - -void -Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback, - JS::MutableHandle<JS::Value> aRetval, - ErrorResult& aRv) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.catch - - // We can't call Promise::Then directly, because someone might have - // overridden Promise.prototype.then. - JS::Rooted<JS::Value> promiseVal(aCx, JS::ObjectValue(*GetWrapper())); - if (!MaybeWrapObjectValue(aCx, &promiseVal)) { - aRv.NoteJSContextException(aCx); - return; - } - JS::Rooted<JSObject*> promiseObj(aCx, &promiseVal.toObject()); - MOZ_ASSERT(promiseObj); - JS::AutoValueArray<2> callbacks(aCx); - callbacks[0].setUndefined(); - if (aRejectCallback) { - callbacks[1].setObject(*aRejectCallback->Callable()); - // It could be in any compartment, so put it in ours. - if (!MaybeWrapObjectValue(aCx, callbacks[1])) { - aRv.NoteJSContextException(aCx); - return; - } - } else { - callbacks[1].setNull(); - } - if (!JS_CallFunctionName(aCx, promiseObj, "then", callbacks, aRetval)) { - aRv.NoteJSContextException(aCx); - } -} - -/** - * The CountdownHolder class encapsulates Promise.all countdown functions and - * the countdown holder parts of the Promises spec. It maintains the result - * array and AllResolveElementFunctions use SetValue() to set the array indices. - */ -class CountdownHolder final : public nsISupports -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CountdownHolder) - - CountdownHolder(const GlobalObject& aGlobal, Promise* aPromise, - uint32_t aCountdown) - : mPromise(aPromise), mCountdown(aCountdown) - { - MOZ_ASSERT(aCountdown != 0); - JSContext* cx = aGlobal.Context(); - - // The only time aGlobal.Context() and aGlobal.Get() are not - // same-compartment is when we're called via Xrays, and in that situation we - // in fact want to create the array in the callee compartment - - JSAutoCompartment ac(cx, aGlobal.Get()); - mValues = JS_NewArrayObject(cx, aCountdown); - mozilla::HoldJSObjects(this); - } - -private: - ~CountdownHolder() - { - mozilla::DropJSObjects(this); - } - -public: - void SetValue(uint32_t index, const JS::Handle<JS::Value> aValue) - { - MOZ_ASSERT(mCountdown > 0); - - AutoJSAPI jsapi; - if (!jsapi.Init(mValues)) { - return; - } - JSContext* cx = jsapi.cx(); - - JS::Rooted<JS::Value> value(cx, aValue); - JS::Rooted<JSObject*> values(cx, mValues); - if (!JS_WrapValue(cx, &value) || - !JS_DefineElement(cx, values, index, value, JSPROP_ENUMERATE)) { - MOZ_ASSERT(JS_IsExceptionPending(cx)); - JS::Rooted<JS::Value> exn(cx); - if (!jsapi.StealException(&exn)) { - mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); - } else { - mPromise->MaybeReject(cx, exn); - } - } - - --mCountdown; - if (mCountdown == 0) { - JS::Rooted<JS::Value> result(cx, JS::ObjectValue(*mValues)); - mPromise->MaybeResolve(cx, result); - } - } - -private: - RefPtr<Promise> mPromise; - uint32_t mCountdown; - JS::Heap<JSObject*> mValues; -}; - -NS_IMPL_CYCLE_COLLECTING_ADDREF(CountdownHolder) -NS_IMPL_CYCLE_COLLECTING_RELEASE(CountdownHolder) -NS_IMPL_CYCLE_COLLECTION_CLASS(CountdownHolder) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CountdownHolder) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CountdownHolder) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mValues) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CountdownHolder) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CountdownHolder) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) - tmp->mValues = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -/** - * An AllResolveElementFunction is the per-promise - * part of the Promise.all() algorithm. - * Every Promise in the handler is handed an instance of this as a resolution - * handler and it sets the relevant index in the CountdownHolder. - */ -class AllResolveElementFunction final : public PromiseNativeHandler -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(AllResolveElementFunction) - - AllResolveElementFunction(CountdownHolder* aHolder, uint32_t aIndex) - : mCountdownHolder(aHolder), mIndex(aIndex) - { - MOZ_ASSERT(aHolder); - } - - void - ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override - { - mCountdownHolder->SetValue(mIndex, aValue); - } - - void - RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override - { - // Should never be attached to Promise as a reject handler. - MOZ_CRASH("AllResolveElementFunction should never be attached to a Promise's reject handler!"); - } - -private: - ~AllResolveElementFunction() - { - } - - RefPtr<CountdownHolder> mCountdownHolder; - uint32_t mIndex; -}; - -NS_IMPL_CYCLE_COLLECTING_ADDREF(AllResolveElementFunction) -NS_IMPL_CYCLE_COLLECTING_RELEASE(AllResolveElementFunction) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AllResolveElementFunction) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION(AllResolveElementFunction, mCountdownHolder) - -static const JSClass PromiseAllDataHolderClass = { - "PromiseAllDataHolder", JSCLASS_HAS_RESERVED_SLOTS(3) -}; - -// Slot indices for objects of class PromiseAllDataHolderClass. -#define DATA_HOLDER_REMAINING_ELEMENTS_SLOT 0 -#define DATA_HOLDER_VALUES_ARRAY_SLOT 1 -#define DATA_HOLDER_RESOLVE_FUNCTION_SLOT 2 - -// Slot indices for PromiseAllResolveElement. -// The RESOLVE_ELEMENT_INDEX_SLOT stores our index unless we've already been -// called. Then it stores INT32_MIN (which is never a valid index value). -#define RESOLVE_ELEMENT_INDEX_SLOT 0 -// The RESOLVE_ELEMENT_DATA_HOLDER_SLOT slot stores an object of class -// PromiseAllDataHolderClass. -#define RESOLVE_ELEMENT_DATA_HOLDER_SLOT 1 - -static bool -PromiseAllResolveElement(JSContext* aCx, unsigned aArgc, JS::Value* aVp) -{ - // Implements - // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.all-resolve-element-functions - // - // See the big comment about compartments in Promise::All "Substep 4" that - // explains what compartments the various stuff here lives in. - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); - - // Step 1. - int32_t index = - js::GetFunctionNativeReserved(&args.callee(), - RESOLVE_ELEMENT_INDEX_SLOT).toInt32(); - // Step 2. - if (index == INT32_MIN) { - args.rval().setUndefined(); - return true; - } - - // Step 3. - js::SetFunctionNativeReserved(&args.callee(), - RESOLVE_ELEMENT_INDEX_SLOT, - JS::Int32Value(INT32_MIN)); - - // Step 4 already done. - - // Step 5. - JS::Rooted<JSObject*> dataHolder(aCx, - &js::GetFunctionNativeReserved(&args.callee(), - RESOLVE_ELEMENT_DATA_HOLDER_SLOT).toObject()); - - JS::Rooted<JS::Value> values(aCx, - js::GetReservedSlot(dataHolder, DATA_HOLDER_VALUES_ARRAY_SLOT)); - - // Step 6, effectively. - JS::Rooted<JS::Value> resolveFunc(aCx, - js::GetReservedSlot(dataHolder, DATA_HOLDER_RESOLVE_FUNCTION_SLOT)); - - // Step 7. - int32_t remainingElements = - js::GetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32(); - - // Step 8. - JS::Rooted<JSObject*> valuesObj(aCx, &values.toObject()); - if (!JS_DefineElement(aCx, valuesObj, index, args.get(0), JSPROP_ENUMERATE)) { - return false; - } - - // Step 9. - remainingElements -= 1; - js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT, - JS::Int32Value(remainingElements)); - - // Step 10. - if (remainingElements == 0) { - return JS::Call(aCx, JS::UndefinedHandleValue, resolveFunc, - JS::HandleValueArray(values), args.rval()); - } - - // Step 11. - args.rval().setUndefined(); - return true; -} - - -/* static */ void -Promise::All(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv, - JS::Handle<JS::Value> aIterable, - JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) -{ - // Implements http://www.ecma-international.org/ecma-262/6.0/#sec-promise.all - nsCOMPtr<nsIGlobalObject> global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - JSContext* cx = aGlobal.Context(); - - // Steps 1-5: nothing to do. Note that the @@species bits got removed in - // https://github.com/tc39/ecma262/pull/211 - - // Step 6. - PromiseCapability capability(cx); - NewPromiseCapability(cx, global, aThisv, true, capability, aRv); - // Step 7. - if (aRv.Failed()) { - return; - } - - MOZ_ASSERT(aThisv.isObject(), "How did NewPromiseCapability succeed?"); - JS::Rooted<JSObject*> constructorObj(cx, &aThisv.toObject()); - - // After this point we have a useful promise value in "capability", so just go - // ahead and put it in our retval now. Every single return path below would - // want to do that anyway. - aRetval.set(capability.PromiseValue()); - if (!MaybeWrapValue(cx, aRetval)) { - aRv.NoteJSContextException(cx); - return; - } - - // The arguments we're going to be passing to "then" on each loop iteration. - // The second one we know already; the first one will be created on each - // iteration of the loop. - JS::AutoValueArray<2> callbackFunctions(cx); - callbackFunctions[1].set(capability.mReject); - - // Steps 8 and 9. - JS::ForOfIterator iter(cx); - if (!iter.init(aIterable, JS::ForOfIterator::AllowNonIterable)) { - capability.RejectWithException(cx, aRv); - return; - } - - if (!iter.valueIsIterable()) { - ThrowErrorMessage(cx, MSG_PROMISE_ARG_NOT_ITERABLE, - "Argument of Promise.all"); - capability.RejectWithException(cx, aRv); - return; - } - - // Step 10 doesn't need to be done, because ForOfIterator handles it - // for us. - - // Now we jump over to - // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromiseall - // and do its steps. - - // Substep 4. Create our data holder that holds all the things shared across - // every step of the iterator. In particular, this holds the - // remainingElementsCount (as an integer reserved slot), the array of values, - // and the resolve function from our PromiseCapability. - // - // We have to be very careful about which compartments we create things in - // here. In particular, we have to maintain the invariant that anything - // stored in a reserved slot is same-compartment with the object whose - // reserved slot it's in. But we want to create the values array in the - // Promise reflector compartment, because that array can get exposed to code - // that has access to the Promise reflector (in particular code from that - // compartment), and that should work, even if the Promise reflector - // compartment is less-privileged than our caller compartment. - // - // So the plan is as follows: Create the values array in the promise reflector - // compartment. Create the PromiseAllResolveElement function and the data - // holder in our current compartment. Store a cross-compartment wrapper to - // the values array in the holder. This should be OK because the only things - // we hand the PromiseAllResolveElement function to are the "then" calls we do - // and in the case when the reflector compartment is not the current - // compartment those are happening over Xrays anyway, which means they get the - // canonical "then" function and content can't see our - // PromiseAllResolveElement. - JS::Rooted<JSObject*> dataHolder(cx); - dataHolder = JS_NewObjectWithGivenProto(cx, &PromiseAllDataHolderClass, - nullptr); - if (!dataHolder) { - capability.RejectWithException(cx, aRv); - return; - } - - JS::Rooted<JSObject*> reflectorGlobal(cx, global->GetGlobalJSObject()); - JS::Rooted<JSObject*> valuesArray(cx); - { // Scope for JSAutoCompartment. - JSAutoCompartment ac(cx, reflectorGlobal); - valuesArray = JS_NewArrayObject(cx, 0); - } - if (!valuesArray) { - // It's important that we've exited the JSAutoCompartment by now, before - // calling RejectWithException and possibly invoking capability.mReject. - capability.RejectWithException(cx, aRv); - return; - } - - // The values array as a value we can pass to a function in our current - // compartment, or store in the holder's reserved slot. - JS::Rooted<JS::Value> valuesArrayVal(cx, JS::ObjectValue(*valuesArray)); - if (!MaybeWrapObjectValue(cx, &valuesArrayVal)) { - capability.RejectWithException(cx, aRv); - return; - } - - js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT, - JS::Int32Value(1)); - js::SetReservedSlot(dataHolder, DATA_HOLDER_VALUES_ARRAY_SLOT, - valuesArrayVal); - js::SetReservedSlot(dataHolder, DATA_HOLDER_RESOLVE_FUNCTION_SLOT, - capability.mResolve); - - // Substep 5. - CheckedInt32 index = 0; - - // Substep 6. - JS::Rooted<JS::Value> nextValue(cx); - while (true) { - bool done; - // Steps a, b, c, e, f, g. - if (!iter.next(&nextValue, &done)) { - capability.RejectWithException(cx, aRv); - return; - } - - // Step d. - if (done) { - int32_t remainingCount = - js::GetReservedSlot(dataHolder, - DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32(); - remainingCount -= 1; - if (remainingCount == 0) { - JS::Rooted<JS::Value> ignored(cx); - if (!JS::Call(cx, JS::UndefinedHandleValue, capability.mResolve, - JS::HandleValueArray(valuesArrayVal), &ignored)) { - capability.RejectWithException(cx, aRv); - } - return; - } - js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT, - JS::Int32Value(remainingCount)); - // We're all set for now! - return; - } - - // Step h. - { // Scope for the JSAutoCompartment we need to work with valuesArray. We - // mostly do this for performance; we could go ahead and do the define via - // a cross-compartment proxy instead... - JSAutoCompartment ac(cx, valuesArray); - if (!JS_DefineElement(cx, valuesArray, index.value(), - JS::UndefinedHandleValue, JSPROP_ENUMERATE)) { - // Have to go back into the caller compartment before we try to touch - // capability.mReject. Luckily, capability.mReject is guaranteed to be - // an object in the right compartment here. - JSAutoCompartment ac2(cx, &capability.mReject.toObject()); - capability.RejectWithException(cx, aRv); - return; - } - } - - // Step i. Sadly, we can't take a shortcut here even if - // capability.mNativePromise exists, because someone could have overridden - // "resolve" on the canonical Promise constructor. - JS::Rooted<JS::Value> nextPromise(cx); - if (!JS_CallFunctionName(cx, constructorObj, "resolve", - JS::HandleValueArray(nextValue), - &nextPromise)) { - // Step j. - capability.RejectWithException(cx, aRv); - return; - } - - // Step k. - JS::Rooted<JSObject*> resolveElement(cx); - JSFunction* resolveFunc = - js::NewFunctionWithReserved(cx, PromiseAllResolveElement, - 1 /* nargs */, 0 /* flags */, nullptr); - if (!resolveFunc) { - capability.RejectWithException(cx, aRv); - return; - } - - resolveElement = JS_GetFunctionObject(resolveFunc); - // Steps l-p. - js::SetFunctionNativeReserved(resolveElement, - RESOLVE_ELEMENT_INDEX_SLOT, - JS::Int32Value(index.value())); - js::SetFunctionNativeReserved(resolveElement, - RESOLVE_ELEMENT_DATA_HOLDER_SLOT, - JS::ObjectValue(*dataHolder)); - - // Step q. - int32_t remainingElements = - js::GetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT).toInt32(); - js::SetReservedSlot(dataHolder, DATA_HOLDER_REMAINING_ELEMENTS_SLOT, - JS::Int32Value(remainingElements + 1)); - - // Step r. And now we don't know whether nextPromise has an overridden - // "then" method, so no shortcuts here either. - callbackFunctions[0].setObject(*resolveElement); - JS::Rooted<JSObject*> nextPromiseObj(cx); - JS::Rooted<JS::Value> ignored(cx); - if (!JS_ValueToObject(cx, nextPromise, &nextPromiseObj) || - !JS_CallFunctionName(cx, nextPromiseObj, "then", callbackFunctions, - &ignored)) { - // Step s. - capability.RejectWithException(cx, aRv); - } - - // Step t. - index += 1; - if (!index.isValid()) { - // Let's just claim OOM. - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - capability.RejectWithException(cx, aRv); - } - } -} - -/* static */ already_AddRefed<Promise> -Promise::All(const GlobalObject& aGlobal, - const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv) -{ - nsCOMPtr<nsIGlobalObject> global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - JSContext* cx = aGlobal.Context(); - - if (aPromiseList.IsEmpty()) { - JS::Rooted<JSObject*> empty(cx, JS_NewArrayObject(cx, 0)); - if (!empty) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return nullptr; - } - JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*empty)); - // We know "value" is not a promise, so call the Resolve function - // that doesn't have to check for that. - return Promise::Resolve(global, cx, value, aRv); - } - - RefPtr<Promise> promise = Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } - RefPtr<CountdownHolder> holder = - new CountdownHolder(aGlobal, promise, aPromiseList.Length()); - - JS::Rooted<JSObject*> obj(cx, JS::CurrentGlobalOrNull(cx)); - if (!obj) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - RefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(promise, obj); - - for (uint32_t i = 0; i < aPromiseList.Length(); ++i) { - RefPtr<PromiseNativeHandler> resolveHandler = - new AllResolveElementFunction(holder, i); - - RefPtr<PromiseCallback> resolveCb = - new NativePromiseCallback(resolveHandler, Resolved); - - // Every promise gets its own resolve callback, which will set the right - // index in the array to the resolution value. - aPromiseList[i]->AppendCallbacks(resolveCb, rejectCb); - } - - return promise.forget(); -} - -/* static */ void -Promise::Race(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv, - JS::Handle<JS::Value> aIterable, JS::MutableHandle<JS::Value> aRetval, - ErrorResult& aRv) -{ - // Implements http://www.ecma-international.org/ecma-262/6.0/#sec-promise.race - nsCOMPtr<nsIGlobalObject> global = - do_QueryInterface(aGlobal.GetAsSupports()); - if (!global) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - JSContext* cx = aGlobal.Context(); - - // Steps 1-5: nothing to do. Note that the @@species bits got removed in - // https://github.com/tc39/ecma262/pull/211 - PromiseCapability capability(cx); - - // Step 6. - NewPromiseCapability(cx, global, aThisv, true, capability, aRv); - // Step 7. - if (aRv.Failed()) { - return; - } - - MOZ_ASSERT(aThisv.isObject(), "How did NewPromiseCapability succeed?"); - JS::Rooted<JSObject*> constructorObj(cx, &aThisv.toObject()); - - // After this point we have a useful promise value in "capability", so just go - // ahead and put it in our retval now. Every single return path below would - // want to do that anyway. - aRetval.set(capability.PromiseValue()); - if (!MaybeWrapValue(cx, aRetval)) { - aRv.NoteJSContextException(cx); - return; - } - - // The arguments we're going to be passing to "then" on each loop iteration. - JS::AutoValueArray<2> callbackFunctions(cx); - callbackFunctions[0].set(capability.mResolve); - callbackFunctions[1].set(capability.mReject); - - // Steps 8 and 9. - JS::ForOfIterator iter(cx); - if (!iter.init(aIterable, JS::ForOfIterator::AllowNonIterable)) { - capability.RejectWithException(cx, aRv); - return; - } - - if (!iter.valueIsIterable()) { - ThrowErrorMessage(cx, MSG_PROMISE_ARG_NOT_ITERABLE, - "Argument of Promise.race"); - capability.RejectWithException(cx, aRv); - return; - } - - // Step 10 doesn't need to be done, because ForOfIterator handles it - // for us. - - // Now we jump over to - // http://www.ecma-international.org/ecma-262/6.0/#sec-performpromiserace - // and do its steps. - JS::Rooted<JS::Value> nextValue(cx); - while (true) { - bool done; - // Steps a, b, c, e, f, g. - if (!iter.next(&nextValue, &done)) { - capability.RejectWithException(cx, aRv); - return; - } - - // Step d. - if (done) { - // We're all set! - return; - } - - // Step h. Sadly, we can't take a shortcut here even if - // capability.mNativePromise exists, because someone could have overridden - // "resolve" on the canonical Promise constructor. - JS::Rooted<JS::Value> nextPromise(cx); - if (!JS_CallFunctionName(cx, constructorObj, "resolve", - JS::HandleValueArray(nextValue), &nextPromise)) { - // Step i. - capability.RejectWithException(cx, aRv); - return; - } - - // Step j. And now we don't know whether nextPromise has an overridden - // "then" method, so no shortcuts here either. - JS::Rooted<JSObject*> nextPromiseObj(cx); - JS::Rooted<JS::Value> ignored(cx); - if (!JS_ValueToObject(cx, nextPromise, &nextPromiseObj) || - !JS_CallFunctionName(cx, nextPromiseObj, "then", callbackFunctions, - &ignored)) { - // Step k. - capability.RejectWithException(cx, aRv); - } - } -} - -/* static */ -bool -Promise::PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp) -{ - JS::CallArgs args = CallArgsFromVp(aArgc, aVp); - args.rval().set(args.thisv()); - return true; -} - -void -Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - RefPtr<PromiseCallback> resolveCb = - new NativePromiseCallback(aRunnable, Resolved); - - RefPtr<PromiseCallback> rejectCb = - new NativePromiseCallback(aRunnable, Rejected); - - AppendCallbacks(resolveCb, rejectCb); -} - -#endif // SPIDERMONKEY_PROMISE - JSObject* Promise::GlobalJSObject() const { @@ -2560,382 +644,6 @@ Promise::Compartment() const return js::GetObjectCompartment(GlobalJSObject()); } -#ifndef SPIDERMONKEY_PROMISE -void -Promise::AppendCallbacks(PromiseCallback* aResolveCallback, - PromiseCallback* aRejectCallback) -{ - if (!mGlobal || mGlobal->IsDying()) { - return; - } - - MOZ_ASSERT(aResolveCallback); - MOZ_ASSERT(aRejectCallback); - - if (mIsLastInChain && mState == PromiseState::Rejected) { - // This rejection is now consumed. - PromiseDebugging::AddConsumedRejection(*this); - // Note that we may not have had the opportunity to call - // RunResolveTask() yet, so we may never have called - // `PromiseDebugging:AddUncaughtRejection`. - } - mIsLastInChain = false; - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - // Now that there is a callback, we don't need to report anymore. - mHadRejectCallback = true; - RemoveWorkerHolder(); -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - mResolveCallbacks.AppendElement(aResolveCallback); - mRejectCallbacks.AppendElement(aRejectCallback); - - // If promise's state is fulfilled, queue a task to process our fulfill - // callbacks with promise's result. If promise's state is rejected, queue a - // task to process our reject callbacks with promise's result. - if (mState != Pending) { - TriggerPromiseReactions(); - } -} -#endif // SPIDERMONKEY_PROMISE - -#ifndef SPIDERMONKEY_PROMISE -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) -void -Promise::MaybeReportRejected() -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - if (mState != Rejected || mHadRejectCallback || mResult.isUndefined()) { - return; - } - - AutoJSAPI jsapi; - // We may not have a usable global by now (if it got unlinked - // already), so don't init with it. - jsapi.Init(); - JSContext* cx = jsapi.cx(); - JS::Rooted<JSObject*> obj(cx, GetWrapper()); - MOZ_ASSERT(obj); // We preserve our wrapper, so should always have one here. - JS::Rooted<JS::Value> val(cx, mResult); - - JSAutoCompartment ac(cx, obj); - if (!JS_WrapValue(cx, &val)) { - JS_ClearPendingException(cx); - return; - } - - js::ErrorReport report(cx); - RefPtr<Exception> exp; - bool isObject = val.isObject(); - if (!isObject || NS_FAILED(UNWRAP_OBJECT(Exception, &val.toObject(), exp))) { - if (!isObject || - NS_FAILED(UNWRAP_OBJECT(DOMException, &val.toObject(), exp))) { - if (!report.init(cx, val, js::ErrorReport::NoSideEffects)) { - NS_WARNING("Couldn't convert the unhandled rejected value to an exception."); - JS_ClearPendingException(cx); - return; - } - } - } - - RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); - bool isMainThread = MOZ_LIKELY(NS_IsMainThread()); - bool isChrome = isMainThread ? nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj)) - : GetCurrentThreadWorkerPrivate()->IsChromeWorker(); - nsGlobalWindow* win = isMainThread ? xpc::WindowGlobalOrNull(obj) : nullptr; - uint64_t windowID = win ? win->AsInner()->WindowID() : 0; - if (exp) { - xpcReport->Init(cx, exp, isChrome, windowID); - } else { - xpcReport->Init(report.report(), report.toStringResult(), - isChrome, windowID); - } - - // Now post an event to do the real reporting async - // Since Promises preserve their wrapper, it is essential to RefPtr<> the - // AsyncErrorReporter, otherwise if the call to DispatchToMainThread fails, it - // will leak. See Bug 958684. So... don't use DispatchToMainThread() - nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); - if (NS_WARN_IF(!mainThread)) { - // Would prefer NS_ASSERTION, but that causes failure in xpcshell tests - NS_WARNING("!!! Trying to report rejected Promise after MainThread shutdown"); - } - if (mainThread) { - RefPtr<AsyncErrorReporter> r = new AsyncErrorReporter(xpcReport); - mainThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); - } -} -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - -void -Promise::MaybeResolveInternal(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - if (mResolvePending) { - return; - } - - ResolveInternal(aCx, aValue); -} - -void -Promise::MaybeRejectInternal(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - if (mResolvePending) { - return; - } - - RejectInternal(aCx, aValue); -} - -void -Promise::HandleException(JSContext* aCx) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - JS::Rooted<JS::Value> exn(aCx); - if (JS_GetPendingException(aCx, &exn)) { - JS_ClearPendingException(aCx); - RejectInternal(aCx, exn); - } -} - -void -Promise::ResolveInternal(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - CycleCollectedJSContext* context = CycleCollectedJSContext::Get(); - - mResolvePending = true; - - if (aValue.isObject()) { - JS::Rooted<JSObject*> valueObj(aCx, &aValue.toObject()); - - // Thenables. - JS::Rooted<JS::Value> then(aCx); - if (!JS_GetProperty(aCx, valueObj, "then", &then)) { - HandleException(aCx); - return; - } - - if (then.isObject() && JS::IsCallable(&then.toObject())) { - // This is the then() function of the thenable aValueObj. - JS::Rooted<JSObject*> thenObj(aCx, &then.toObject()); - - // We used to have a fast path here for the case when the following - // requirements held: - // - // 1) valueObj is a Promise. - // 2) thenObj is a JSFunction backed by our actual Promise::Then - // implementation. - // - // But now that we're doing subclassing in Promise.prototype.then we would - // also need the following requirements: - // - // 3) Getting valueObj.constructor has no side-effects. - // 4) Getting valueObj.constructor[@@species] has no side-effects. - // 5) valueObj.constructor[@@species] is a function and calling it has no - // side-effects (e.g. it's the canonical Promise constructor) and it - // provides some callback functions to call as arguments to its - // argument. - // - // Ensuring that stuff while not inside SpiderMonkey is painful, so let's - // drop the fast path for now. - - RefPtr<PromiseInit> thenCallback = - new PromiseInit(nullptr, thenObj, mozilla::dom::GetIncumbentGlobal()); - RefPtr<PromiseResolveThenableJob> task = - new PromiseResolveThenableJob(this, valueObj, thenCallback); - context->DispatchToMicroTask(task.forget()); - return; - } - } - - MaybeSettle(aValue, Resolved); -} - -void -Promise::RejectInternal(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - mResolvePending = true; - - MaybeSettle(aValue, Rejected); -} - -void -Promise::Settle(JS::Handle<JS::Value> aValue, PromiseState aState) -{ - MOZ_ASSERT(mGlobal, - "We really should have a global here. Except we sometimes don't " - "in the wild for some odd reason"); - NS_ASSERT_OWNINGTHREAD(Promise); - - if (!mGlobal || mGlobal->IsDying()) { - return; - } - - mSettlementTimestamp = TimeStamp::Now(); - - AutoJSAPI jsapi; - jsapi.Init(); - JSContext* cx = jsapi.cx(); - JS::RootedObject wrapper(cx, GetWrapper()); - MOZ_ASSERT(wrapper); // We preserved it - JSAutoCompartment ac(cx, wrapper); - - JS::Rooted<JS::Value> value(cx, aValue); - - if (!JS_WrapValue(cx, &value)) { - JS_ClearPendingException(cx); - value = JS::UndefinedValue(); - } - SetResult(value); - SetState(aState); - - JS::dbg::onPromiseSettled(cx, wrapper); - - if (aState == PromiseState::Rejected && - mIsLastInChain) { - // The Promise has just been rejected, and it is last in chain. - // We need to inform PromiseDebugging. - // If the Promise is eventually not the last in chain anymore, - // we will need to inform PromiseDebugging again. - PromiseDebugging::AddUncaughtRejection(*this); - } - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - // If the Promise was rejected, and there is no reject handler already setup, - // watch for thread shutdown. - if (aState == PromiseState::Rejected && - !mHadRejectCallback && - !NS_IsMainThread()) { - WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - worker->AssertIsOnWorkerThread(); - - mWorkerHolder = new PromiseReportRejectWorkerHolder(this); - if (NS_WARN_IF(!mWorkerHolder->HoldWorker(worker, Closing))) { - mWorkerHolder = nullptr; - // Worker is shutting down, report rejection immediately since it is - // unlikely that reject callbacks will be added after this point. - MaybeReportRejectedOnce(); - } - } -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - TriggerPromiseReactions(); -} - -void -Promise::MaybeSettle(JS::Handle<JS::Value> aValue, - PromiseState aState) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // Promise.all() or Promise.race() implementations will repeatedly call - // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState - // from asserting. - if (mState != Pending) { - return; - } - - Settle(aValue, aState); -} - -void -Promise::TriggerPromiseReactions() -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - CycleCollectedJSContext* runtime = CycleCollectedJSContext::Get(); - - nsTArray<RefPtr<PromiseCallback>> callbacks; - callbacks.SwapElements(mState == Resolved ? mResolveCallbacks - : mRejectCallbacks); - mResolveCallbacks.Clear(); - mRejectCallbacks.Clear(); - - for (uint32_t i = 0; i < callbacks.Length(); ++i) { - RefPtr<PromiseReactionJob> task = - new PromiseReactionJob(this, callbacks[i], mResult); - runtime->DispatchToMicroTask(task.forget()); - } -} - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) -void -Promise::RemoveWorkerHolder() -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // The DTOR of this WorkerHolder will release the worker for us. - mWorkerHolder = nullptr; -} - -bool -PromiseReportRejectWorkerHolder::Notify(Status aStatus) -{ - MOZ_ASSERT(aStatus > Running); - mPromise->MaybeReportRejectedOnce(); - // After this point, `this` has been deleted by RemoveWorkerHolder! - return true; -} -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - -bool -Promise::CaptureStack(JSContext* aCx, JS::Heap<JSObject*>& aTarget) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - JS::Rooted<JSObject*> stack(aCx); - if (!JS::CaptureCurrentStack(aCx, &stack)) { - return false; - } - aTarget = stack; - return true; -} - -void -Promise::GetDependentPromises(nsTArray<RefPtr<Promise>>& aPromises) -{ - NS_ASSERT_OWNINGTHREAD(Promise); - - // We want to return promises that correspond to then() calls, Promise.all() - // calls, and Promise.race() calls. - // - // For the then() case, we have both resolve and reject callbacks that know - // what the next promise is. - // - // For the race() case, likewise. - // - // For the all() case, our reject callback knows what the next promise is, but - // our resolve callback just knows it needs to notify some - // PromiseNativeHandler, which itself only has an indirect relationship to the - // next promise. - // - // So we walk over our _reject_ callbacks and ask each of them what promise - // its dependent promise is. - for (size_t i = 0; i < mRejectCallbacks.Length(); ++i) { - Promise* p = mRejectCallbacks[i]->GetDependentPromise(); - if (p) { - aPromises.AppendElement(p); - } - } -} - -#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 WorkerRunnable @@ -3223,23 +931,6 @@ void Promise::MaybeRejectBrokenly(const nsAString& aArg) { MaybeSomething(aArg, &Promise::MaybeReject); } -#ifndef SPIDERMONKEY_PROMISE -uint64_t -Promise::GetID() { - if (mID != 0) { - return mID; - } - return mID = ++gIDGenerator; -} -#endif // SPIDERMONKEY_PROMISE - -#ifndef SPIDERMONKEY_PROMISE -Promise::PromiseState -Promise::State() const -{ - return mState; -} -#else // SPIDERMONKEY_PROMISE Promise::PromiseState Promise::State() const { @@ -3256,7 +947,6 @@ Promise::State() const return PromiseState::Pending; } -#endif // SPIDERMONKEY_PROMISE } // namespace dom } // namespace mozilla diff --git a/dom/promise/Promise.h b/dom/promise/Promise.h index 642603a11..2fe365c46 100644 --- a/dom/promise/Promise.h +++ b/dom/promise/Promise.h @@ -21,17 +21,6 @@ #include "js/TypeDecls.h" #include "jspubtd.h" -// Bug 1083361 introduces a new mechanism for tracking uncaught -// rejections. This #define serves to track down the parts of code -// that need to be removed once clients have been put together -// to take advantage of the new mechanism. New code should not -// depend on code #ifdefed to this #define. -#define DOM_PROMISE_DEPRECATED_REPORTING !SPIDERMONKEY_PROMISE - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) -#include "mozilla/dom/workers/bindings/WorkerHolder.h" -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - class nsIGlobalObject; namespace mozilla { @@ -40,88 +29,36 @@ namespace dom { class AnyCallback; class DOMError; class MediaStreamError; -class PromiseCallback; class PromiseInit; class PromiseNativeHandler; class PromiseDebugging; -class Promise; - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) -class PromiseReportRejectWorkerHolder : public workers::WorkerHolder -{ - // PromiseReportRejectWorkerHolder is held by an nsAutoPtr on the Promise - // which means that this object will be destroyed before the Promise is - // destroyed. - Promise* MOZ_NON_OWNING_REF mPromise; - -public: - explicit PromiseReportRejectWorkerHolder(Promise* aPromise) - : mPromise(aPromise) - { - MOZ_ASSERT(mPromise); - } - - virtual bool - Notify(workers::Status aStatus) override; -}; -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - #define NS_PROMISE_IID \ { 0x1b8d6215, 0x3e67, 0x43ba, \ { 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; - friend class PromiseReactionJob; - friend class PromiseResolverTask; friend class PromiseTask; -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - friend class PromiseReportRejectWorkerHolder; -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) friend class PromiseWorkerProxy; friend class PromiseWorkerProxyRunnable; - friend class RejectPromiseCallback; - friend class ResolvePromiseCallback; - friend class PromiseResolveThenableJob; - friend class FastPromiseResolveThenableJob; - friend class WrapperPromiseCallback; 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); // Reports a rejected Promise by sending an error report. static void ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise); -#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); @@ -183,11 +120,6 @@ 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 compartment of aGlobal. The // compartment of aCx is ignored. Errors are reported on the ErrorResult; if // aRv comes back !Failed(), this function MUST return a non-null value. @@ -223,95 +155,17 @@ public: return mPromiseObj; } -#else // SPIDERMONKEY_PROMISE - JSObject* PromiseObj() - { - return GetWrapper(); - } - - virtual JSObject* - WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; - - static already_AddRefed<Promise> - Constructor(const GlobalObject& aGlobal, PromiseInit& aInit, - ErrorResult& aRv, JS::Handle<JSObject*> aDesiredProto); - - static void - Resolve(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv, - JS::Handle<JS::Value> aValue, - JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv); - - static already_AddRefed<Promise> - Resolve(nsIGlobalObject* aGlobal, JSContext* aCx, - JS::Handle<JS::Value> aValue, ErrorResult& aRv); - - static void - Reject(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv, - JS::Handle<JS::Value> aValue, - JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv); - - static already_AddRefed<Promise> - Reject(nsIGlobalObject* aGlobal, JSContext* aCx, - JS::Handle<JS::Value> aValue, 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); - - void - Catch(JSContext* aCx, - AnyCallback* aRejectCallback, - JS::MutableHandle<JS::Value> aRetval, - ErrorResult& aRv); - - static void - All(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv, - JS::Handle<JS::Value> aIterable, JS::MutableHandle<JS::Value> aRetval, - ErrorResult& aRv); - - static already_AddRefed<Promise> - All(const GlobalObject& aGlobal, - const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv); - - static void - Race(const GlobalObject& aGlobal, JS::Handle<JS::Value> aThisv, - JS::Handle<JS::Value> aIterable, JS::MutableHandle<JS::Value> aRetval, - ErrorResult& aRv); - - static bool - PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp); -#endif // SPIDERMONKEY_PROMISE - void AppendNativeHandler(PromiseNativeHandler* aRunnable); JSObject* GlobalJSObject() const; JSCompartment* Compartment() const; -#ifndef SPIDERMONKEY_PROMISE - // Return a unique-to-the-process identifier for this Promise. - uint64_t GetID(); -#endif // SPIDERMONKEY_PROMISE - -#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 enum class PromiseState { Pending, @@ -335,99 +189,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. - void CallInitFunction(const GlobalObject& aGlobal, PromiseInit& aInit, - ErrorResult& aRv); - - // The NewPromiseCapability function from - // <http://www.ecma-international.org/ecma-262/6.0/#sec-newpromisecapability>. - // Errors are communicated via aRv. If aForceCallbackCreation is - // true, then this function will ensure that aCapability has a - // useful mResolve/mReject even if mNativePromise is non-null. - static void NewPromiseCapability(JSContext* aCx, nsIGlobalObject* aGlobal, - JS::Handle<JS::Value> aConstructor, - bool aForceCallbackCreation, - PromiseCapability& aCapability, - ErrorResult& aRv); - - bool IsPending() - { - return mResolvePending; - } - - void GetDependentPromises(nsTArray<RefPtr<Promise>>& aPromises); - - bool IsLastInChain() const - { - return mIsLastInChain; - } - - void SetNotifiedAsUncaught() - { - mWasNotifiedAsUncaught = true; - } - - bool WasNotifiedAsUncaught() const - { - return mWasNotifiedAsUncaught; - } -#endif // SPIDERMONKEY_PROMISE - private: -#ifndef SPIDERMONKEY_PROMISE - friend class PromiseDebugging; - - void SetState(PromiseState aState) - { - MOZ_ASSERT(mState == Pending); - MOZ_ASSERT(aState != Pending); - mState = aState; - } - - void SetResult(JS::Handle<JS::Value> aValue) - { - mResult = aValue; - } - - // This method enqueues promise's resolve/reject callbacks with promise's - // result. It's executed when the resolver.resolve() or resolver.reject() is - // called or when the promise already has a result and new callbacks are - // appended by then() or catch(). - void TriggerPromiseReactions(); - - void Settle(JS::Handle<JS::Value> aValue, Promise::PromiseState aState); - void MaybeSettle(JS::Handle<JS::Value> aValue, Promise::PromiseState aState); - - void AppendCallbacks(PromiseCallback* aResolveCallback, - PromiseCallback* aRejectCallback); - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - // If we have been rejected and our mResult is a JS exception, - // report it to the error console. - // Use MaybeReportRejectedOnce() for actual calls. - void MaybeReportRejected(); - - void MaybeReportRejectedOnce() { - MaybeReportRejected(); - RemoveWorkerHolder(); - mResult.setUndefined(); - } -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - void MaybeResolveInternal(JSContext* aCx, - JS::Handle<JS::Value> aValue); - void MaybeRejectInternal(JSContext* aCx, - JS::Handle<JS::Value> aValue); - - void ResolveInternal(JSContext* aCx, - 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) { MOZ_ASSERT(PromiseObj()); // It was preserved! @@ -444,92 +206,11 @@ private: (this->*aFunc)(cx, val); } -#ifndef SPIDERMONKEY_PROMISE - // Static methods for the PromiseInit functions. - static bool - JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp); - - static bool - ThenableResolverCommon(JSContext* aCx, uint32_t /* PromiseCallback::Task */ aTask, - unsigned aArgc, JS::Value* aVp); - static bool - JSCallbackThenableResolver(JSContext *aCx, unsigned aArgc, JS::Value *aVp); - static bool - JSCallbackThenableRejecter(JSContext *aCx, unsigned aArgc, JS::Value *aVp); - - static JSObject* - CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask); - - static JSObject* - CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask); - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - void RemoveWorkerHolder(); -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - // 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; - - JS::Heap<JS::Value> mResult; - // A stack that shows where this promise was allocated, if there was - // JS running at the time. Otherwise null. - JS::Heap<JSObject*> mAllocationStack; - // mRejectionStack is only set when the promise is rejected directly from - // script, by calling Promise.reject() or the rejection callback we pass to - // the PromiseInit function. Promises that are rejected internally do not - // have a rejection stack. - JS::Heap<JSObject*> mRejectionStack; - // mFullfillmentStack is only set when the promise is fulfilled directly from - // script, by calling Promise.resolve() or the fulfillment callback we pass to - // the PromiseInit function. Promises that are fulfilled internally do not - // have a fulfillment stack. - JS::Heap<JSObject*> mFullfillmentStack; - PromiseState mState; - -#if defined(DOM_PROMISE_DEPRECATED_REPORTING) - bool mHadRejectCallback; - - // If a rejected promise on a worker has no reject callbacks attached, it - // needs to know when the worker is shutting down, to report the error on the - // console before the worker's context is deleted. This feature is used for - // that purpose. - nsAutoPtr<PromiseReportRejectWorkerHolder> mWorkerHolder; -#endif // defined(DOM_PROMISE_DEPRECATED_REPORTING) - - bool mTaskPending; - bool mResolvePending; - - // `true` if this Promise is the last in the chain, or `false` if - // another Promise has been created from this one by a call to - // `then`, `all`, `race`, etc. - bool mIsLastInChain; - - // `true` if PromiseDebugging has already notified at least one observer that - // this promise was left uncaught, `false` otherwise. - bool mWasNotifiedAsUncaught; - - // The time when this promise was created. - TimeStamp mCreationTimestamp; - - // The time when this promise transitioned out of the pending state. - TimeStamp mSettlementTimestamp; - - // 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) diff --git a/dom/promise/PromiseCallback.cpp b/dom/promise/PromiseCallback.cpp deleted file mode 100644 index 6ecf983b7..000000000 --- a/dom/promise/PromiseCallback.cpp +++ /dev/null @@ -1,571 +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 "PromiseCallback.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/dom/PromiseNativeHandler.h" - -#include "jsapi.h" -#include "jsfriendapi.h" -#include "jswrapper.h" - -namespace mozilla { -namespace dom { - -#ifndef SPIDERMONKEY_PROMISE - -NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseCallback) -NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseCallback) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseCallback) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION_0(PromiseCallback) - -PromiseCallback::PromiseCallback() -{ -} - -PromiseCallback::~PromiseCallback() -{ -} - -// ResolvePromiseCallback - -NS_IMPL_CYCLE_COLLECTION_CLASS(ResolvePromiseCallback) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ResolvePromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) - tmp->mGlobal = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ResolvePromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ResolvePromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ResolvePromiseCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_ADDREF_INHERITED(ResolvePromiseCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(ResolvePromiseCallback, PromiseCallback) - -ResolvePromiseCallback::ResolvePromiseCallback(Promise* aPromise, - JS::Handle<JSObject*> aGlobal) - : mPromise(aPromise) - , mGlobal(aGlobal) -{ - MOZ_ASSERT(aPromise); - MOZ_ASSERT(aGlobal); - HoldJSObjects(this); -} - -ResolvePromiseCallback::~ResolvePromiseCallback() -{ - DropJSObjects(this); -} - -nsresult -ResolvePromiseCallback::Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - // Run resolver's algorithm with value and the synchronous flag set. - - JS::ExposeValueToActiveJS(aValue); - - JSAutoCompartment ac(aCx, mGlobal); - JS::Rooted<JS::Value> value(aCx, aValue); - if (!JS_WrapValue(aCx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - mPromise->ResolveInternal(aCx, value); - return NS_OK; -} - -// RejectPromiseCallback - -NS_IMPL_CYCLE_COLLECTION_CLASS(RejectPromiseCallback) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RejectPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise) - tmp->mGlobal = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RejectPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(RejectPromiseCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(RejectPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_ADDREF_INHERITED(RejectPromiseCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(RejectPromiseCallback, PromiseCallback) - -RejectPromiseCallback::RejectPromiseCallback(Promise* aPromise, - JS::Handle<JSObject*> aGlobal) - : mPromise(aPromise) - , mGlobal(aGlobal) -{ - MOZ_ASSERT(aPromise); - MOZ_ASSERT(mGlobal); - HoldJSObjects(this); -} - -RejectPromiseCallback::~RejectPromiseCallback() -{ - DropJSObjects(this); -} - -nsresult -RejectPromiseCallback::Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - // Run resolver's algorithm with value and the synchronous flag set. - - JS::ExposeValueToActiveJS(aValue); - - JSAutoCompartment ac(aCx, mGlobal); - JS::Rooted<JS::Value> value(aCx, aValue); - if (!JS_WrapValue(aCx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - - mPromise->RejectInternal(aCx, value); - return NS_OK; -} - -// InvokePromiseFuncCallback - -NS_IMPL_CYCLE_COLLECTION_CLASS(InvokePromiseFuncCallback) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(InvokePromiseFuncCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromiseFunc) - tmp->mGlobal = nullptr; - tmp->mNextPromiseObj = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(InvokePromiseFuncCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromiseFunc) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(InvokePromiseFuncCallback) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNextPromiseObj) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(InvokePromiseFuncCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_ADDREF_INHERITED(InvokePromiseFuncCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(InvokePromiseFuncCallback, PromiseCallback) - -InvokePromiseFuncCallback::InvokePromiseFuncCallback(JS::Handle<JSObject*> aGlobal, - JS::Handle<JSObject*> aNextPromiseObj, - AnyCallback* aPromiseFunc) - : mGlobal(aGlobal) - , mNextPromiseObj(aNextPromiseObj) - , mPromiseFunc(aPromiseFunc) -{ - MOZ_ASSERT(aGlobal); - MOZ_ASSERT(aNextPromiseObj); - MOZ_ASSERT(aPromiseFunc); - HoldJSObjects(this); -} - -InvokePromiseFuncCallback::~InvokePromiseFuncCallback() -{ - DropJSObjects(this); -} - -nsresult -InvokePromiseFuncCallback::Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - // Run resolver's algorithm with value and the synchronous flag set. - - JS::ExposeValueToActiveJS(aValue); - - JSAutoCompartment ac(aCx, mGlobal); - JS::Rooted<JS::Value> value(aCx, aValue); - if (!JS_WrapValue(aCx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - ErrorResult rv; - JS::Rooted<JS::Value> ignored(aCx); - mPromiseFunc->Call(value, &ignored, rv); - // Useful exceptions already got reported. - rv.SuppressException(); - return NS_OK; -} - -Promise* -InvokePromiseFuncCallback::GetDependentPromise() -{ - Promise* promise; - if (NS_SUCCEEDED(UNWRAP_OBJECT(Promise, mNextPromiseObj, promise))) { - return promise; - } - - // Oh, well. - return nullptr; -} - -// WrapperPromiseCallback -NS_IMPL_CYCLE_COLLECTION_CLASS(WrapperPromiseCallback) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WrapperPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextPromise) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveFunc) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectFunc) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) - tmp->mGlobal = nullptr; - tmp->mNextPromiseObj = nullptr; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WrapperPromiseCallback, - PromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextPromise) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveFunc) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectFunc) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WrapperPromiseCallback) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNextPromiseObj) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WrapperPromiseCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_ADDREF_INHERITED(WrapperPromiseCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(WrapperPromiseCallback, PromiseCallback) - -WrapperPromiseCallback::WrapperPromiseCallback(Promise* aNextPromise, - JS::Handle<JSObject*> aGlobal, - AnyCallback* aCallback) - : mNextPromise(aNextPromise) - , mGlobal(aGlobal) - , mCallback(aCallback) -{ - MOZ_ASSERT(aNextPromise); - MOZ_ASSERT(aGlobal); - HoldJSObjects(this); -} - -WrapperPromiseCallback::WrapperPromiseCallback(JS::Handle<JSObject*> aGlobal, - AnyCallback* aCallback, - JS::Handle<JSObject*> aNextPromiseObj, - AnyCallback* aResolveFunc, - AnyCallback* aRejectFunc) - : mNextPromiseObj(aNextPromiseObj) - , mResolveFunc(aResolveFunc) - , mRejectFunc(aRejectFunc) - , mGlobal(aGlobal) - , mCallback(aCallback) -{ - MOZ_ASSERT(mNextPromiseObj); - MOZ_ASSERT(aResolveFunc); - MOZ_ASSERT(aRejectFunc); - MOZ_ASSERT(aGlobal); - HoldJSObjects(this); -} - -WrapperPromiseCallback::~WrapperPromiseCallback() -{ - DropJSObjects(this); -} - -nsresult -WrapperPromiseCallback::Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - JS::ExposeValueToActiveJS(aValue); - - JSAutoCompartment ac(aCx, mGlobal); - JS::Rooted<JS::Value> value(aCx, aValue); - if (!JS_WrapValue(aCx, &value)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - ErrorResult rv; - - // PromiseReactionTask step 6 - JS::Rooted<JS::Value> retValue(aCx); - JSCompartment* compartment; - if (mNextPromise) { - compartment = mNextPromise->Compartment(); - } else { - MOZ_ASSERT(mNextPromiseObj); - compartment = js::GetObjectCompartment(mNextPromiseObj); - } - mCallback->Call(value, &retValue, rv, "promise callback", - CallbackObject::eRethrowExceptions, - compartment); - - rv.WouldReportJSException(); - - // PromiseReactionTask step 7 - if (rv.Failed()) { - if (rv.IsUncatchableException()) { - // We have nothing to resolve/reject the promise with. - return rv.StealNSResult(); - } - - JS::Rooted<JS::Value> value(aCx); - { // Scope for JSAutoCompartment - // Convert the ErrorResult to a JS exception object that we can reject - // ourselves with. This will be exactly the exception that would get - // thrown from a binding method whose ErrorResult ended up with whatever - // is on "rv" right now. Do this in the promise reflector compartment. - Maybe<JSAutoCompartment> ac; - if (mNextPromise) { - ac.emplace(aCx, mNextPromise->GlobalJSObject()); - } else { - ac.emplace(aCx, mNextPromiseObj); - } - DebugOnly<bool> conversionResult = ToJSValue(aCx, rv, &value); - MOZ_ASSERT(conversionResult); - } - - if (mNextPromise) { - mNextPromise->RejectInternal(aCx, value); - } else { - JS::Rooted<JS::Value> ignored(aCx); - ErrorResult rejectRv; - mRejectFunc->Call(value, &ignored, rejectRv); - // This reported any JS exceptions; we just have a pointless exception on - // there now. - rejectRv.SuppressException(); - } - return NS_OK; - } - - // If the return value is the same as the promise itself, throw TypeError. - if (retValue.isObject()) { - JS::Rooted<JSObject*> valueObj(aCx, &retValue.toObject()); - valueObj = js::CheckedUnwrap(valueObj); - JS::Rooted<JSObject*> nextPromiseObj(aCx); - if (mNextPromise) { - nextPromiseObj = mNextPromise->GetWrapper(); - } else { - MOZ_ASSERT(mNextPromiseObj); - nextPromiseObj = mNextPromiseObj; - } - // XXXbz shouldn't this check be over in ResolveInternal anyway? - if (valueObj == nextPromiseObj) { - const char* fileName = nullptr; - uint32_t lineNumber = 0; - - // Try to get some information about the callback to report a sane error, - // but don't try too hard (only deals with scripted functions). - JS::Rooted<JSObject*> unwrapped(aCx, - js::CheckedUnwrap(mCallback->Callback())); - - if (unwrapped) { - JSAutoCompartment ac(aCx, unwrapped); - if (JS_ObjectIsFunction(aCx, unwrapped)) { - JS::Rooted<JS::Value> asValue(aCx, JS::ObjectValue(*unwrapped)); - JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, asValue)); - - MOZ_ASSERT(func); - JSScript* script = JS_GetFunctionScript(aCx, func); - if (script) { - fileName = JS_GetScriptFilename(script); - lineNumber = JS_GetScriptBaseLineNumber(aCx, script); - } - } - } - - // We're back in aValue's compartment here. - JS::Rooted<JSString*> fn(aCx, JS_NewStringCopyZ(aCx, fileName)); - if (!fn) { - // Out of memory. Promise will stay unresolved. - JS_ClearPendingException(aCx); - return NS_ERROR_OUT_OF_MEMORY; - } - - JS::Rooted<JSString*> message(aCx, - JS_NewStringCopyZ(aCx, - "then() cannot return same Promise that it resolves.")); - if (!message) { - // Out of memory. Promise will stay unresolved. - JS_ClearPendingException(aCx); - return NS_ERROR_OUT_OF_MEMORY; - } - - JS::Rooted<JS::Value> typeError(aCx); - if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, fn, lineNumber, 0, - nullptr, message, &typeError)) { - // Out of memory. Promise will stay unresolved. - JS_ClearPendingException(aCx); - return NS_ERROR_OUT_OF_MEMORY; - } - - if (mNextPromise) { - mNextPromise->RejectInternal(aCx, typeError); - } else { - JS::Rooted<JS::Value> ignored(aCx); - ErrorResult rejectRv; - mRejectFunc->Call(typeError, &ignored, rejectRv); - // This reported any JS exceptions; we just have a pointless exception - // on there now. - rejectRv.SuppressException(); - } - return NS_OK; - } - } - - // Otherwise, run resolver's resolve with value. - if (!JS_WrapValue(aCx, &retValue)) { - NS_WARNING("Failed to wrap value into the right compartment."); - return NS_ERROR_FAILURE; - } - - if (mNextPromise) { - mNextPromise->ResolveInternal(aCx, retValue); - } else { - JS::Rooted<JS::Value> ignored(aCx); - ErrorResult resolveRv; - mResolveFunc->Call(retValue, &ignored, resolveRv); - // This reported any JS exceptions; we just have a pointless exception - // on there now. - resolveRv.SuppressException(); - } - - return NS_OK; -} - -Promise* -WrapperPromiseCallback::GetDependentPromise() -{ - // Per spec, various algorithms like all() and race() are actually implemented - // in terms of calling then() but passing it the resolve/reject functions that - // are passed as arguments to function passed to the Promise constructor. - // That will cause the promise in question to hold on to a - // WrapperPromiseCallback, but the dependent promise should really be the one - // whose constructor those functions came from, not the about-to-be-ignored - // return value of "then". So try to determine whether we're in that case and - // if so go ahead and dig the dependent promise out of the function we have. - JSObject* callable = mCallback->Callable(); - // Unwrap it, in case it's a cross-compartment wrapper. Our caller here is - // system, so it's really ok to just go and unwrap. - callable = js::UncheckedUnwrap(callable); - if (JS_IsNativeFunction(callable, Promise::JSCallback)) { - JS::Value promiseVal = - js::GetFunctionNativeReserved(callable, Promise::SLOT_PROMISE); - Promise* promise; - UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise); - return promise; - } - - if (mNextPromise) { - return mNextPromise; - } - - Promise* promise; - if (NS_SUCCEEDED(UNWRAP_OBJECT(Promise, mNextPromiseObj, promise))) { - return promise; - } - - // Oh, well. - return nullptr; -} - -// NativePromiseCallback - -NS_IMPL_CYCLE_COLLECTION_INHERITED(NativePromiseCallback, - PromiseCallback, mHandler) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(NativePromiseCallback) -NS_INTERFACE_MAP_END_INHERITING(PromiseCallback) - -NS_IMPL_ADDREF_INHERITED(NativePromiseCallback, PromiseCallback) -NS_IMPL_RELEASE_INHERITED(NativePromiseCallback, PromiseCallback) - -NativePromiseCallback::NativePromiseCallback(PromiseNativeHandler* aHandler, - Promise::PromiseState aState) - : mHandler(aHandler) - , mState(aState) -{ - MOZ_ASSERT(aHandler); -} - -NativePromiseCallback::~NativePromiseCallback() -{ -} - -nsresult -NativePromiseCallback::Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - JS::ExposeValueToActiveJS(aValue); - - if (mState == Promise::Resolved) { - mHandler->ResolvedCallback(aCx, aValue); - return NS_OK; - } - - if (mState == Promise::Rejected) { - mHandler->RejectedCallback(aCx, aValue); - return NS_OK; - } - - NS_NOTREACHED("huh?"); - return NS_ERROR_FAILURE; -} - -/* static */ PromiseCallback* -PromiseCallback::Factory(Promise* aNextPromise, JS::Handle<JSObject*> aGlobal, - AnyCallback* aCallback, Task aTask) -{ - MOZ_ASSERT(aNextPromise); - - // If we have a callback and a next resolver, we have to exec the callback and - // then propagate the return value to the next resolver->resolve(). - if (aCallback) { - return new WrapperPromiseCallback(aNextPromise, aGlobal, aCallback); - } - - if (aTask == Resolve) { - return new ResolvePromiseCallback(aNextPromise, aGlobal); - } - - if (aTask == Reject) { - return new RejectPromiseCallback(aNextPromise, aGlobal); - } - - MOZ_ASSERT(false, "This should not happen"); - return nullptr; -} - -#endif // SPIDERMONKEY_PROMISE - -} // namespace dom -} // namespace mozilla diff --git a/dom/promise/PromiseCallback.h b/dom/promise/PromiseCallback.h deleted file mode 100644 index 9f55e03d0..000000000 --- a/dom/promise/PromiseCallback.h +++ /dev/null @@ -1,203 +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_PromiseCallback_h -#define mozilla_dom_PromiseCallback_h - -#include "mozilla/dom/Promise.h" -#include "nsCycleCollectionParticipant.h" - -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 -{ -protected: - virtual ~PromiseCallback(); - -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(PromiseCallback) - - PromiseCallback(); - - virtual nsresult Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) = 0; - - // Return the Promise that this callback will end up resolving or - // rejecting, if any. - virtual Promise* GetDependentPromise() = 0; - - enum Task { - Resolve, - Reject - }; - - // This factory returns a PromiseCallback object with refcount of 0. - static PromiseCallback* - Factory(Promise* aNextPromise, JS::Handle<JSObject*> aObject, - AnyCallback* aCallback, Task aTask); -}; - -// WrapperPromiseCallback execs a JS Callback with a value, and then the return -// value is sent to either: -// a) If aNextPromise is non-null, the aNextPromise->ResolveFunction() or to -// aNextPromise->RejectFunction() if the JS Callback throws. -// or -// b) If aNextPromise is null, in which case aResolveFunc and aRejectFunc must -// be non-null, then to aResolveFunc, unless aCallback threw, in which case -// aRejectFunc. -class WrapperPromiseCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WrapperPromiseCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) override; - - Promise* GetDependentPromise() override; - - // Constructor for when we know we have a vanilla Promise. - WrapperPromiseCallback(Promise* aNextPromise, JS::Handle<JSObject*> aGlobal, - AnyCallback* aCallback); - - // Constructor for when all we have to work with are resolve/reject functions. - WrapperPromiseCallback(JS::Handle<JSObject*> aGlobal, - AnyCallback* aCallback, - JS::Handle<JSObject*> mNextPromiseObj, - AnyCallback* aResolveFunc, - AnyCallback* aRejectFunc); - -private: - ~WrapperPromiseCallback(); - - // Either mNextPromise is non-null or all three of mNextPromiseObj, - // mResolveFund and mRejectFunc must are non-null. - RefPtr<Promise> mNextPromise; - // mNextPromiseObj is the reflector itself; it may not be in the - // same compartment as anything else we have. - JS::Heap<JSObject*> mNextPromiseObj; - RefPtr<AnyCallback> mResolveFunc; - RefPtr<AnyCallback> mRejectFunc; - JS::Heap<JSObject*> mGlobal; - RefPtr<AnyCallback> mCallback; -}; - -// ResolvePromiseCallback calls aPromise->ResolveFunction() with the value -// received by Call(). -class ResolvePromiseCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ResolvePromiseCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) override; - - Promise* GetDependentPromise() override - { - return mPromise; - } - - ResolvePromiseCallback(Promise* aPromise, JS::Handle<JSObject*> aGlobal); - -private: - ~ResolvePromiseCallback(); - - RefPtr<Promise> mPromise; - JS::Heap<JSObject*> mGlobal; -}; - -// RejectPromiseCallback calls aPromise->RejectFunction() with the value -// received by Call(). -class RejectPromiseCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(RejectPromiseCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) override; - - Promise* GetDependentPromise() override - { - return mPromise; - } - - RejectPromiseCallback(Promise* aPromise, JS::Handle<JSObject*> aGlobal); - -private: - ~RejectPromiseCallback(); - - RefPtr<Promise> mPromise; - JS::Heap<JSObject*> mGlobal; -}; - -// InvokePromiseFuncCallback calls the given function with the value -// received by Call(). -class InvokePromiseFuncCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(InvokePromiseFuncCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) override; - - Promise* GetDependentPromise() override; - - InvokePromiseFuncCallback(JS::Handle<JSObject*> aGlobal, - JS::Handle<JSObject*> aNextPromiseObj, - AnyCallback* aPromiseFunc); - -private: - ~InvokePromiseFuncCallback(); - - JS::Heap<JSObject*> mGlobal; - JS::Heap<JSObject*> mNextPromiseObj; - RefPtr<AnyCallback> mPromiseFunc; -}; - -// NativePromiseCallback wraps a PromiseNativeHandler. -class NativePromiseCallback final : public PromiseCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NativePromiseCallback, - PromiseCallback) - - nsresult Call(JSContext* aCx, - JS::Handle<JS::Value> aValue) override; - - Promise* GetDependentPromise() override - { - return nullptr; - } - - NativePromiseCallback(PromiseNativeHandler* aHandler, - Promise::PromiseState aState); - -private: - ~NativePromiseCallback(); - - RefPtr<PromiseNativeHandler> mHandler; - Promise::PromiseState mState; -}; - -#endif // SPIDERMONKEY_PROMISE - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PromiseCallback_h diff --git a/dom/promise/PromiseDebugging.cpp b/dom/promise/PromiseDebugging.cpp index fc0942ee4..f3ec33e8b 100644 --- a/dom/promise/PromiseDebugging.cpp +++ b/dom/promise/PromiseDebugging.cpp @@ -66,20 +66,6 @@ private: /* static */ MOZ_THREAD_LOCAL(bool) FlushRejections::sDispatched; -#ifndef SPIDERMONKEY_PROMISE -static Promise* -UnwrapPromise(JS::Handle<JSObject*> aPromise, ErrorResult& aRv) -{ - Promise* promise; - if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Promise, aPromise, promise)))) { - aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING("Argument")); - return nullptr; - } - return promise; -} -#endif // SPIDERMONKEY_PROMISE - -#ifdef SPIDERMONKEY_PROMISE /* static */ void PromiseDebugging::GetState(GlobalObject& aGlobal, JS::Handle<JSObject*> aPromise, PromiseDebuggingStateHolder& aState, @@ -173,34 +159,6 @@ PromiseDebugging::GetFullfillmentStack(GlobalObject& aGlobal, aStack.set(JS::GetPromiseResolutionSite(obj)); } -#else - -/* static */ void -PromiseDebugging::GetState(GlobalObject&, JS::Handle<JSObject*> aPromise, - PromiseDebuggingStateHolder& aState, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - switch (promise->mState) { - case Promise::Pending: - aState.mState = PromiseDebuggingState::Pending; - break; - case Promise::Resolved: - aState.mState = PromiseDebuggingState::Fulfilled; - aState.mValue = promise->mResult; - break; - case Promise::Rejected: - aState.mState = PromiseDebuggingState::Rejected; - aState.mReason = promise->mResult; - break; - } -} - -#endif // SPIDERMONKEY_PROMISE - /*static */ nsString PromiseDebugging::sIDPrefix; @@ -232,86 +190,6 @@ PromiseDebugging::FlushUncaughtRejections() FlushRejections::FlushSync(); } -#ifndef SPIDERMONKEY_PROMISE - -/* static */ void -PromiseDebugging::GetAllocationStack(GlobalObject&, JS::Handle<JSObject*> aPromise, - JS::MutableHandle<JSObject*> aStack, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - aStack.set(promise->mAllocationStack); -} - -/* static */ void -PromiseDebugging::GetRejectionStack(GlobalObject&, JS::Handle<JSObject*> aPromise, - JS::MutableHandle<JSObject*> aStack, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - aStack.set(promise->mRejectionStack); -} - -/* static */ void -PromiseDebugging::GetFullfillmentStack(GlobalObject&, JS::Handle<JSObject*> aPromise, - JS::MutableHandle<JSObject*> aStack, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - aStack.set(promise->mFullfillmentStack); -} - -/* static */ void -PromiseDebugging::GetDependentPromises(GlobalObject&, JS::Handle<JSObject*> aPromise, - nsTArray<RefPtr<Promise>>& aPromises, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - promise->GetDependentPromises(aPromises); -} - -/* static */ double -PromiseDebugging::GetPromiseLifetime(GlobalObject&, - JS::Handle<JSObject*> aPromise, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return 0; - } - return (TimeStamp::Now() - promise->mCreationTimestamp).ToMilliseconds(); -} - -/* static */ double -PromiseDebugging::GetTimeToSettle(GlobalObject&, JS::Handle<JSObject*> aPromise, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return 0; - } - if (promise->mState == Promise::Pending) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return 0; - } - return (promise->mSettlementTimestamp - - promise->mCreationTimestamp).ToMilliseconds(); -} - -#endif // SPIDERMONKEY_PROMISE - /* static */ void PromiseDebugging::AddUncaughtRejectionObserver(GlobalObject&, UncaughtRejectionObserver& aObserver) @@ -337,8 +215,6 @@ PromiseDebugging::RemoveUncaughtRejectionObserver(GlobalObject&, return false; } -#ifdef SPIDERMONKEY_PROMISE - /* static */ void PromiseDebugging::AddUncaughtRejection(JS::HandleObject aPromise) { @@ -420,102 +296,5 @@ PromiseDebugging::FlushUncaughtRejectionsInternal() storage->mConsumedRejections.clear(); } -#else - -/* static */ void -PromiseDebugging::AddUncaughtRejection(Promise& aPromise) -{ - CycleCollectedJSContext::Get()->mUncaughtRejections.AppendElement(&aPromise); - FlushRejections::DispatchNeeded(); -} - -/* void */ void -PromiseDebugging::AddConsumedRejection(Promise& aPromise) -{ - CycleCollectedJSContext::Get()->mConsumedRejections.AppendElement(&aPromise); - FlushRejections::DispatchNeeded(); -} - -/* static */ void -PromiseDebugging::GetPromiseID(GlobalObject&, - JS::Handle<JSObject*> aPromise, - nsString& aID, - ErrorResult& aRv) -{ - Promise* promise = UnwrapPromise(aPromise, aRv); - if (aRv.Failed()) { - return; - } - uint64_t promiseID = promise->GetID(); - aID = sIDPrefix; - aID.AppendInt(promiseID); -} - -/* static */ void -PromiseDebugging::FlushUncaughtRejectionsInternal() -{ - CycleCollectedJSContext* storage = CycleCollectedJSContext::Get(); - - // The Promise that have been left uncaught (rejected and last in - // their chain) since the last call to this function. - nsTArray<nsCOMPtr<nsISupports>> uncaught; - storage->mUncaughtRejections.SwapElements(uncaught); - - // The Promise that have been left uncaught at some point, but that - // have eventually had their `then` method called. - nsTArray<nsCOMPtr<nsISupports>> consumed; - storage->mConsumedRejections.SwapElements(consumed); - - nsTArray<nsCOMPtr<nsISupports>>& observers = storage->mUncaughtRejectionObservers; - - nsresult rv; - // Notify observers of uncaught Promise. - - for (size_t i = 0; i < uncaught.Length(); ++i) { - nsCOMPtr<Promise> promise = do_QueryInterface(uncaught[i], &rv); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - if (!promise->IsLastInChain()) { - // This promise is not the last in the chain anymore, - // so the error has been caught at some point. - continue; - } - - // For the moment, the Promise is still at the end of the - // chain. Let's inform observers, so that they may decide whether - // to report it. - for (size_t j = 0; j < observers.Length(); ++j) { - ErrorResult err; - RefPtr<UncaughtRejectionObserver> obs = - static_cast<UncaughtRejectionObserver*>(observers[j].get()); - - obs->OnLeftUncaught(*promise, err); // Ignore errors - } - - promise->SetNotifiedAsUncaught(); - } - - // Notify observers of consumed Promise. - - for (size_t i = 0; i < consumed.Length(); ++i) { - nsCOMPtr<Promise> promise = do_QueryInterface(consumed[i], &rv); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - if (!promise->WasNotifiedAsUncaught()) { - continue; - } - - MOZ_ASSERT(!promise->IsLastInChain()); - for (size_t j = 0; j < observers.Length(); ++j) { - ErrorResult err; - RefPtr<UncaughtRejectionObserver> obs = - static_cast<UncaughtRejectionObserver*>(observers[j].get()); - - obs->OnConsumed(*promise, err); // Ignore errors - } - } -} -#endif // SPIDERMONKEY_PROMISE - } // namespace dom } // namespace mozilla diff --git a/dom/promise/PromiseDebugging.h b/dom/promise/PromiseDebugging.h index 218a64c2e..77397b841 100644 --- a/dom/promise/PromiseDebugging.h +++ b/dom/promise/PromiseDebugging.h @@ -52,37 +52,17 @@ public: JS::MutableHandle<JSObject*> aStack, ErrorResult& aRv); -#ifndef SPIDERMONKEY_PROMISE - static void GetDependentPromises(GlobalObject&, - JS::Handle<JSObject*> aPromise, - nsTArray<RefPtr<Promise>>& aPromises, - ErrorResult& aRv); - static double GetPromiseLifetime(GlobalObject&, - JS::Handle<JSObject*> aPromise, - ErrorResult& aRv); - static double GetTimeToSettle(GlobalObject&, JS::Handle<JSObject*> aPromise, - ErrorResult& aRv); -#endif // SPIDERMONKEY_PROMISE - // Mechanism for watching uncaught instances of Promise. static void AddUncaughtRejectionObserver(GlobalObject&, UncaughtRejectionObserver& aObserver); static bool RemoveUncaughtRejectionObserver(GlobalObject&, UncaughtRejectionObserver& aObserver); -#ifdef SPIDERMONKEY_PROMISE // Mark a Promise as having been left uncaught at script completion. static void AddUncaughtRejection(JS::HandleObject); // Mark a Promise previously added with `AddUncaughtRejection` as // eventually consumed. static void AddConsumedRejection(JS::HandleObject); -#else - // 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. static void FlushUncaughtRejections(); diff --git a/dom/promise/moz.build b/dom/promise/moz.build index 11d2a7496..c0e3d79a7 100644 --- a/dom/promise/moz.build +++ b/dom/promise/moz.build @@ -11,9 +11,8 @@ EXPORTS.mozilla.dom += [ 'PromiseWorkerProxy.h', ] -UNIFIED_SOURCES += [ +SOURCES += [ 'Promise.cpp', - 'PromiseCallback.cpp', 'PromiseDebugging.cpp', ] diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index 65be02809..56a119e1a 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -513,8 +513,19 @@ nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType, for (uint32_t i = 0; i < mPolicies.Length(); i++) { bool allowed = mPolicies[i]->allows(aContentType, CSP_UNSAFE_INLINE, EmptyString(), aParserCreated) || - mPolicies[i]->allows(aContentType, CSP_NONCE, aNonce, aParserCreated) || - mPolicies[i]->allows(aContentType, CSP_HASH, aContent, aParserCreated); + mPolicies[i]->allows(aContentType, CSP_NONCE, aNonce, aParserCreated); + + // If the inlined script or style is allowed by either unsafe-inline or the + // nonce, go ahead and shortcut this loop. + if (allowed) { + continue; + } + + // Check if the csp-hash matches against the hash of the script. + // If we don't have any content to check, block the script. + if (!aContent.IsEmpty()) { + allowed = mPolicies[i]->allows(aContentType, CSP_HASH, aContent, aParserCreated); + } if (!allowed) { // policy is violoated: deny the load unless policy is report only and diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp index 71c8e3433..d07ad7945 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -641,13 +641,22 @@ nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected // just a specific scheme, the parser should generate a nsCSPSchemeSource. NS_ASSERTION((!mHost.IsEmpty()), "host can not be the empty string"); + // Before we can check if the host matches, we have to + // extract the host part from aUri. + nsAutoCString uriHost; + nsresult rv = aUri->GetAsciiHost(uriHost); + NS_ENSURE_SUCCESS(rv, false); + + nsString decodedUriHost; + CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost); + // 2) host matching: Enforce a single * if (mHost.EqualsASCII("*")) { // The single ASTERISK character (*) does not match a URI's scheme of a type // designating a globally unique identifier (such as blob:, data:, or filesystem:) - // At the moment firefox does not support filesystem; but for future compatibility + // At the moment UXP does not support "filesystem:" but for future compatibility // we support it in CSP according to the spec, see: 4.2.2 Matching Source Expressions - // Note, that whitelisting any of these schemes would call nsCSPSchemeSrc::permits(). + // Note: whitelisting any of these schemes would call nsCSPSchemeSrc::permits(). bool isBlobScheme = (NS_SUCCEEDED(aUri->SchemeIs("blob", &isBlobScheme)) && isBlobScheme); bool isDataScheme = @@ -658,20 +667,15 @@ nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected if (isBlobScheme || isDataScheme || isFileScheme) { return false; } - return true; - } - - // Before we can check if the host matches, we have to - // extract the host part from aUri. - nsAutoCString uriHost; - nsresult rv = aUri->GetAsciiHost(uriHost); - NS_ENSURE_SUCCESS(rv, false); - - nsString decodedUriHost; - CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost); + // If no scheme is present there also won't be a port and folder to check + // which means we can return early. + if (mScheme.IsEmpty()) { + return true; + } + } // 4.5) host matching: Check if the allowed host starts with a wilcard. - if (mHost.First() == '*') { + else if (mHost.First() == '*') { NS_ASSERTION(mHost[1] == '.', "Second character needs to be '.' whenever host starts with '*'"); // Eliminate leading "*", but keeping the FULL STOP (.) thereafter before checking diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp index 08fd9afd9..5c6701992 100644 --- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -10,6 +10,7 @@ #include "nsIStreamListener.h" #include "nsCDefaultURIFixup.h" #include "nsIURIFixup.h" +#include "nsIImageLoadingContent.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/TabChild.h" @@ -123,7 +124,7 @@ nsContentSecurityManager::CheckFTPSubresourceLoad(nsIChannel* aChannel) nsCOMPtr<nsIURI> uri; nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(rv, rv); if (!uri) { return NS_OK; } @@ -801,6 +802,8 @@ nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) // within nsCorsListenerProxy rv = DoCheckLoadURIChecks(uri, loadInfo); NS_ENSURE_SUCCESS(rv, rv); + // TODO: Bug 1371237 + // consider calling SetBlockedRequest in nsContentSecurityManager::CheckChannel } return NS_OK; diff --git a/dom/smil/nsSMILCSSProperty.cpp b/dom/smil/nsSMILCSSProperty.cpp index 53f3e0fbf..e74512443 100644 --- a/dom/smil/nsSMILCSSProperty.cpp +++ b/dom/smil/nsSMILCSSProperty.cpp @@ -38,14 +38,8 @@ GetCSSComputedValue(Element* aElem, return false; } - nsIPresShell* shell = doc->GetShell(); - if (!shell) { - NS_WARNING("Unable to look up computed style -- no pres shell"); - return false; - } - RefPtr<nsComputedDOMStyle> computedStyle = - NS_NewComputedDOMStyle(aElem, EmptyString(), shell); + NS_NewComputedDOMStyle(aElem, EmptyString(), doc); computedStyle->GetPropertyValue(aPropID, aResult); return true; diff --git a/dom/storage/DOMStorageCache.cpp b/dom/storage/DOMStorageCache.cpp index a2b5a6f73..ee9a22e96 100644 --- a/dom/storage/DOMStorageCache.cpp +++ b/dom/storage/DOMStorageCache.cpp @@ -205,11 +205,6 @@ DOMStorageCache::ProcessUsageDelta(const DOMStorage* aStorage, int64_t aDelta) bool DOMStorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta) { - // Check if we are in a low disk space situation - if (aDelta > 0 && mManager && mManager->IsLowDiskSpace()) { - return false; - } - // Check limit per this origin Data& data = mData[aGetDataSetIndex]; uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta; diff --git a/dom/storage/DOMStorageIPC.cpp b/dom/storage/DOMStorageIPC.cpp index a8cd745f1..9d87a5788 100644 --- a/dom/storage/DOMStorageIPC.cpp +++ b/dom/storage/DOMStorageIPC.cpp @@ -11,7 +11,6 @@ #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/Unused.h" -#include "nsIDiskSpaceWatcher.h" #include "nsThreadUtils.h" namespace mozilla { @@ -321,22 +320,6 @@ private: mozilla::Unused << mParent->SendOriginsHavingData(scopes); } - // We need to check if the device is in a low disk space situation, so - // we can forbid in that case any write in localStorage. - nsCOMPtr<nsIDiskSpaceWatcher> diskSpaceWatcher = - do_GetService("@mozilla.org/toolkit/disk-space-watcher;1"); - if (!diskSpaceWatcher) { - return NS_OK; - } - - bool lowDiskSpace = false; - diskSpaceWatcher->GetIsDiskFull(&lowDiskSpace); - - if (lowDiskSpace) { - mozilla::Unused << mParent->SendObserve( - nsDependentCString("low-disk-space"), EmptyString(), EmptyCString()); - } - return NS_OK; } diff --git a/dom/storage/DOMStorageManager.cpp b/dom/storage/DOMStorageManager.cpp index 156e846ba..8f50fcfb4 100644 --- a/dom/storage/DOMStorageManager.cpp +++ b/dom/storage/DOMStorageManager.cpp @@ -103,7 +103,6 @@ NS_IMPL_ISUPPORTS(DOMStorageManager, DOMStorageManager::DOMStorageManager(DOMStorage::StorageType aType) : mCaches(8) , mType(aType) - , mLowDiskSpace(false) { DOMStorageObserver* observer = DOMStorageObserver::Self(); NS_ASSERTION(observer, "No DOMStorageObserver, cannot observe private data delete notifications!"); @@ -566,22 +565,6 @@ DOMStorageManager::Observe(const char* aTopic, return NS_OK; } - if (!strcmp(aTopic, "low-disk-space")) { - if (mType == LocalStorage) { - mLowDiskSpace = true; - } - - return NS_OK; - } - - if (!strcmp(aTopic, "no-low-disk-space")) { - if (mType == LocalStorage) { - mLowDiskSpace = false; - } - - return NS_OK; - } - #ifdef DOM_STORAGE_TESTS if (!strcmp(aTopic, "test-reload")) { if (mType != LocalStorage) { diff --git a/dom/storage/DOMStorageManager.h b/dom/storage/DOMStorageManager.h index 666e16a6f..0bfd21975 100644 --- a/dom/storage/DOMStorageManager.h +++ b/dom/storage/DOMStorageManager.h @@ -102,12 +102,6 @@ private: const DOMStorage::StorageType mType; - // If mLowDiskSpace is true it indicates a low device storage situation and - // so no localStorage writes are allowed. sessionStorage writes are still - // allowed. - bool mLowDiskSpace; - bool IsLowDiskSpace() const { return mLowDiskSpace; }; - void ClearCaches(uint32_t aUnloadFlags, const OriginAttributesPattern& aPattern, const nsACString& aKeyPrefix); diff --git a/dom/storage/DOMStorageObserver.cpp b/dom/storage/DOMStorageObserver.cpp index a2b3f1da8..fbbab8e54 100644 --- a/dom/storage/DOMStorageObserver.cpp +++ b/dom/storage/DOMStorageObserver.cpp @@ -70,9 +70,6 @@ DOMStorageObserver::Init() obs->AddObserver(sSelf, "profile-before-change", true); obs->AddObserver(sSelf, "xpcom-shutdown", true); - // Observe low device storage notifications. - obs->AddObserver(sSelf, "disk-space-watcher", true); - #ifdef DOM_STORAGE_TESTS // Testing obs->AddObserver(sSelf, "domstorage-test-flush-force", true); @@ -313,16 +310,6 @@ DOMStorageObserver::Observe(nsISupports* aSubject, return NS_OK; } - if (!strcmp(aTopic, "disk-space-watcher")) { - if (NS_LITERAL_STRING("full").Equals(aData)) { - Notify("low-disk-space"); - } else if (NS_LITERAL_STRING("free").Equals(aData)) { - Notify("no-low-disk-space"); - } - - return NS_OK; - } - #ifdef DOM_STORAGE_TESTS if (!strcmp(aTopic, "domstorage-test-flush-force")) { DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); diff --git a/dom/svg/crashtests/880544-1.svg b/dom/svg/crashtests/880544-1.svg deleted file mode 100644 index 9052d2396..000000000 --- a/dom/svg/crashtests/880544-1.svg +++ /dev/null @@ -1,15 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg">
- <script>//<![CDATA[
-
-function add_watch()
-{
- document.getElementById("p").transform.baseVal.watch("0", function(){});
-}
-
-window.addEventListener("load", add_watch, false);
-
- //]]></script>
-
- <path id="p" transform="scale(1)" />
-
-</svg>
diff --git a/dom/svg/crashtests/880544-2.svg b/dom/svg/crashtests/880544-2.svg deleted file mode 100644 index 7570c7cbf..000000000 --- a/dom/svg/crashtests/880544-2.svg +++ /dev/null @@ -1,15 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg">
- <script>//<![CDATA[
-
-function add_watch()
-{
- document.getElementById("e").x.baseVal.watch("0", function(){});
-}
-
-window.addEventListener("load", add_watch, false);
-
- //]]></script>
-
- <text id="e" x="10">foo</text>
-
-</svg>
diff --git a/dom/svg/crashtests/880544-3.svg b/dom/svg/crashtests/880544-3.svg deleted file mode 100644 index 5791b8ec6..000000000 --- a/dom/svg/crashtests/880544-3.svg +++ /dev/null @@ -1,15 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg">
- <script>//<![CDATA[
-
-function add_watch()
-{
- document.getElementById("e").rotate.baseVal.watch("0", function(){});
-}
-
-window.addEventListener("load", add_watch, false);
-
- //]]></script>
-
- <text id="e" rotate="10">foo</text>
-
-</svg>
diff --git a/dom/svg/crashtests/880544-4.svg b/dom/svg/crashtests/880544-4.svg deleted file mode 100644 index 7bdb80f47..000000000 --- a/dom/svg/crashtests/880544-4.svg +++ /dev/null @@ -1,15 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg">
- <script>//<![CDATA[
-
-function add_watch()
-{
- document.getElementById("e").pathSegList.watch("0", function(){});
-}
-
-window.addEventListener("load", add_watch, false);
-
- //]]></script>
-
- <path id="e" d="M0,0"/>
-
-</svg>
diff --git a/dom/svg/crashtests/880544-5.svg b/dom/svg/crashtests/880544-5.svg deleted file mode 100644 index ef7f468f8..000000000 --- a/dom/svg/crashtests/880544-5.svg +++ /dev/null @@ -1,15 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg">
- <script>//<![CDATA[
-
-function add_watch()
-{
- document.getElementById("e").points.watch("0", function(){});
-}
-
-window.addEventListener("load", add_watch, false);
-
- //]]></script>
-
- <polygon id="e" points="0,0"/>
-
-</svg>
diff --git a/dom/svg/crashtests/crashtests.list b/dom/svg/crashtests/crashtests.list index 147838bbe..b2e920152 100644 --- a/dom/svg/crashtests/crashtests.list +++ b/dom/svg/crashtests/crashtests.list @@ -66,11 +66,6 @@ load 837450-1.svg load 842463-1.html load 847138-1.svg load 864509.svg -load 880544-1.svg -load 880544-2.svg -load 880544-3.svg -load 880544-4.svg -load 880544-5.svg load 898915-1.svg load 1035248-1.svg load 1035248-2.svg diff --git a/dom/tests/mochitest/ajax/offline/mochitest.ini b/dom/tests/mochitest/ajax/offline/mochitest.ini index 961b143b6..45909e94e 100644 --- a/dom/tests/mochitest/ajax/offline/mochitest.ini +++ b/dom/tests/mochitest/ajax/offline/mochitest.ini @@ -79,8 +79,6 @@ support-files = [test_fallback.html] [test_foreign.html] [test_identicalManifest.html] -[test_lowDeviceStorage.html] -[test_lowDeviceStorageDuringUpdate.html] [test_missingFile.html] [test_missingManifest.html] [test_noManifest.html] diff --git a/dom/tests/mochitest/ajax/offline/test_lowDeviceStorage.html b/dom/tests/mochitest/ajax/offline/test_lowDeviceStorage.html deleted file mode 100644 index d03ef5a12..000000000 --- a/dom/tests/mochitest/ajax/offline/test_lowDeviceStorage.html +++ /dev/null @@ -1,91 +0,0 @@ -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<title>Low device storage</title> - -<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script> -<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> - -<script type="text/javascript"> - -/** - * This test checks that an offline cache update scheduled *after* a low device - * storage situation appears is canceled. It basically does: - * - * 1. Notifies to the offline cache update service about a fake - * low device storage situation. - * 2. Schedules an update and observes for its notifications. - * 3. We are supposed to receive an error event notifying about the cancelation - * of the update because of the low storage situation. - * 4. Notifies to the offline cache update service that we've recovered from - * the low storage situation. - */ - -var updateService = SpecialPowers.Cc['@mozilla.org/offlinecacheupdate-service;1'] - .getService(Ci.nsIOfflineCacheUpdateService); - -var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - -var errorReceived = false; - -var systemPrincipal = SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(); - -function finish() { - obs.notifyObservers(updateService, "disk-space-watcher", "free"); - - OfflineTest.teardownAndFinish(); -} - -if (OfflineTest.setup()) { - obs.notifyObservers(updateService, "disk-space-watcher", "full"); - - var updateObserver = { - updateStateChanged: function (aUpdate, aState) { - switch(aState) { - case Ci.nsIOfflineCacheUpdateObserver.STATE_ERROR: - errorReceived = true; - OfflineTest.ok(true, "Expected error. Update canceled"); - break; - case Ci.nsIOfflineCacheUpdateObserver.STATE_FINISHED: - aUpdate.removeObserver(this); - OfflineTest.ok(errorReceived, - "Finished after receiving the expected error"); - finish(); - break; - case Ci.nsIOfflineCacheUpdateObserver.STATE_NOUPDATE: - aUpdate.removeObserver(this); - OfflineTest.ok(false, "No update"); - finish(); - break; - case Ci.nsIOfflineCacheUpdateObserver.STATE_DOWNLOADING: - case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMSTARTED: - case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMPROGRESS: - aUpdate.removeObserver(this); - OfflineTest.ok(false, "The update was supposed to be canceled"); - finish(); - break; - } - }, - applicationCacheAvailable: function() {} - }; - - var manifest = "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest"; - var ioService = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - var manifestURI = ioService.newURI(manifest, null, null); - var documentURI = ioService.newURI(document.documentURI, null, null); - var update = updateService.scheduleUpdate(manifestURI, documentURI, systemPrincipal, window); - update.addObserver(updateObserver, false); -} - -SimpleTest.waitForExplicitFinish(); - -</script> - -</head> - -<body> - -</body> -</html> diff --git a/dom/tests/mochitest/ajax/offline/test_lowDeviceStorageDuringUpdate.html b/dom/tests/mochitest/ajax/offline/test_lowDeviceStorageDuringUpdate.html deleted file mode 100644 index 88a0b4eae..000000000 --- a/dom/tests/mochitest/ajax/offline/test_lowDeviceStorageDuringUpdate.html +++ /dev/null @@ -1,58 +0,0 @@ -<html xmlns="http://www.w3.org/1999/xhtml" manifest="http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest"> -<head> -<title>Low device storage during update</title> - -<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script> -<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> - -<script type="text/javascript"> - -/** - * This test checks that an offline cache update is canceled when a low device - * storage condition is detected during the update. - */ - -var updateService = Cc['@mozilla.org/offlinecacheupdate-service;1'] - .getService(Ci.nsIOfflineCacheUpdateService); - -var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - -function finish() { - obs.notifyObservers(updateService, "disk-space-watcher", "free"); - - OfflineTest.teardownAndFinish(); -} - -function onError() { - OfflineTest.ok(true, "Expected error: Update canceled"); - finish(); -} - -function onUnexpectedEvent() { - OfflineTest.ok(false, "The update was supposed to be canceled"); - finish(); -} - -function onChecking() { - obs.notifyObservers(updateService, "disk-space-watcher", "full"); -} - -if (OfflineTest.setup()) { - applicationCache.onerror = OfflineTest.priv(onError); - applicationCache.onprogress = OfflineTest.priv(onUnexpectedEvent); - applicationCache.oncached = OfflineTest.priv(onUnexpectedEvent); - applicationCache.onchecking = OfflineTest.priv(onChecking); -} - -SimpleTest.waitForExplicitFinish(); - -</script> - -</head> - -<body> - -</body> -</html> diff --git a/dom/tests/mochitest/bugs/iframe_bug38959-1.html b/dom/tests/mochitest/bugs/iframe_bug38959-1.html deleted file mode 100644 index d4c16c47a..000000000 --- a/dom/tests/mochitest/bugs/iframe_bug38959-1.html +++ /dev/null @@ -1,14 +0,0 @@ -<html> -<head> - <title>Iframe test for bug 38959</title> -</head> -<body"> -<script> - -x = false; -window.opener.postMessage(1, "http://mochi.test:8888"); -window.close(); - -</script> -</body> -</html> diff --git a/dom/tests/mochitest/bugs/iframe_bug38959-2.html b/dom/tests/mochitest/bugs/iframe_bug38959-2.html deleted file mode 100644 index 36cd0c156..000000000 --- a/dom/tests/mochitest/bugs/iframe_bug38959-2.html +++ /dev/null @@ -1,14 +0,0 @@ -<html> -<head> - <title>Iframe test for bug 38959</title> -</head> -<body"> -<script> - -x = true; -window.opener.postMessage(2, "http://mochi.test:8888"); -window.close(); - -</script> -</body> -</html> diff --git a/dom/tests/mochitest/bugs/mochitest.ini b/dom/tests/mochitest/bugs/mochitest.ini index e0c71f857..309aab6e0 100644 --- a/dom/tests/mochitest/bugs/mochitest.ini +++ b/dom/tests/mochitest/bugs/mochitest.ini @@ -23,8 +23,6 @@ support-files = grandchild_bug260264.html iframe_bug304459-1.html iframe_bug304459-2.html - iframe_bug38959-1.html - iframe_bug38959-2.html iframe_bug430276-2.html iframe_bug430276.html iframe_bug440572.html @@ -64,7 +62,6 @@ skip-if = toolkit == 'android' #TIMED_OUT [test_bug377539.html] [test_bug384122.html] [test_bug389366.html] -[test_bug38959.html] [test_bug393974.html] [test_bug394769.html] [test_bug396843.html] diff --git a/dom/tests/mochitest/bugs/test_bug38959.html b/dom/tests/mochitest/bugs/test_bug38959.html deleted file mode 100644 index a8d07d1a6..000000000 --- a/dom/tests/mochitest/bugs/test_bug38959.html +++ /dev/null @@ -1,57 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=38959 ---> -<head> - <title>Test for Bug 38959</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=38959">Mozilla Bug 38959</a> -<p id="display"></p> -<div id="content" style="display: none"> - <iframe id="frame"></iframe> -</div> -<pre id="test"> -<script type="application/javascript"> - -/** Test for Bug 38959 **/ - -var newValue; - -function watcher(id, ol, ne) -{ - newValue = ne; - return ne; -} - -function openWindow(url, crossOrigin) -{ - newValue = true; - var w = window.open(url); - w.watch("x", watcher); -} - -function receiveMessage(evt) -{ - ok(newValue, "Watchpoints only allowed same-origin."); - if (evt.data == 1) { - openWindow("/tests/dom/tests/mochitest/bugs/iframe_bug38959-2.html"); - } - else { - SimpleTest.finish(); - } -} - -SimpleTest.waitForExplicitFinish(); - -window.addEventListener("message", receiveMessage, false); - -openWindow("http://example.org/tests/dom/tests/mochitest/bugs/iframe_bug38959-1.html"); - -</script> -</pre> -</body> -</html> diff --git a/dom/tests/mochitest/chrome/queryCaretRectWin.html b/dom/tests/mochitest/chrome/queryCaretRectWin.html index cd5a8ec64..cb2fe78a1 100644 --- a/dom/tests/mochitest/chrome/queryCaretRectWin.html +++ b/dom/tests/mochitest/chrome/queryCaretRectWin.html @@ -20,7 +20,7 @@ left: 0em;
top: 0em;
font-size: 10pt;
- font-family: monospace;
+ font-family: 'Courier New';
line-height: 20px;
letter-spacing: 0px;
margin-top:-1px; /* nix the text area border */
diff --git a/dom/tests/mochitest/chrome/selectAtPoint.html b/dom/tests/mochitest/chrome/selectAtPoint.html index 8c625e6f7..6326b500f 100644 --- a/dom/tests/mochitest/chrome/selectAtPoint.html +++ b/dom/tests/mochitest/chrome/selectAtPoint.html @@ -127,13 +127,7 @@ targetPoint = { xPos: rect.left + ((charDims.width * 4) + (charDims.width / 2)), yPos: rect.top + (charDims.height / 2) }; setEnd(dwu, targetPoint.xPos, targetPoint.yPos, Ci.nsIDOMWindowUtils.SELECT_CHARACTER); - if (isLinux || isMac) { - // XXX I think this is a bug, the right hand selection is 4.5 characters over with a - // monspaced font. what we want: t(te)s(ts)election1 what we get: t(te)st(se)lection1 - checkSelection(document, "split selection", "tese"); - } else if (isWindows) { - checkSelection(document, "split selection", "tets"); - } + checkSelection(document, "split selection", "tets"); // Trying to select where there's no text, should fail but not throw let result = dwu.selectAtPoint(rect.left - 20, rect.top - 20, Ci.nsIDOMWindowUtils.SELECT_CHARACTER, false); @@ -228,7 +222,7 @@ <style type="text/css"> body { - font-family: monospace; + font-family: 'Courier New'; margin-left: 40px; margin-top: 40px; padding: 0; @@ -267,7 +261,7 @@ body { <br /> -<iframe id="frame1" src="data:text/html,<html><body style='margin: 0; padding: 0; font-family: monospace;' onload='window.parent.onFrameLoad();'><div id='sel2'>ttestselection2 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut.</div><br/><br/></body></html>"></iframe> +<iframe id="frame1" src="data:text/html,<html><body style='margin: 0; padding: 0; font-family: \'Courier New\';' onload='window.parent.onFrameLoad();'><div id='sel2'>ttestselection2 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut.</div><br/><br/></body></html>"></iframe> <br/> diff --git a/dom/tests/mochitest/localstorage/mochitest.ini b/dom/tests/mochitest/localstorage/mochitest.ini index 5242bf9b1..30b90664a 100644 --- a/dom/tests/mochitest/localstorage/mochitest.ini +++ b/dom/tests/mochitest/localstorage/mochitest.ini @@ -47,6 +47,5 @@ skip-if = toolkit == 'android' #TIMED_OUT skip-if = toolkit == 'android' #TIMED_OUT [test_localStorageReplace.html] skip-if = toolkit == 'android' -[test_lowDeviceStorage.html] [test_storageConstructor.html] [test_localStorageSessionPrefOverride.html] diff --git a/dom/tests/mochitest/localstorage/test_lowDeviceStorage.html b/dom/tests/mochitest/localstorage/test_lowDeviceStorage.html deleted file mode 100644 index 046587150..000000000 --- a/dom/tests/mochitest/localstorage/test_lowDeviceStorage.html +++ /dev/null @@ -1,76 +0,0 @@ -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<title>Test localStorage usage while in a low device storage situation</title> - -<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -<script type="text/javascript" src="localStorageCommon.js"></script> -<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> - -<script type="text/javascript"> - -/* -This test does the following: -- Stores an item in localStorage. -- Checks the stored value. -- Emulates a low device storage situation. -- Gets the stored item again. -- Removes the stored item. -- Fails storing a new value. -- Emulates recovering from a low device storage situation. -- Stores a new value. -- Checks the stored value. -*/ - -function lowDeviceStorage(lowStorage) { - var data = lowStorage ? "full" : "free"; - os().notifyObservers(null, "disk-space-watcher", data); -} - -function startTest() { - // Add a test item. - localStorage.setItem("item", "value"); - is(localStorage.getItem("item"), "value", "getItem()"); - - // Emulates a low device storage situation. - lowDeviceStorage(true); - - // Checks that we can still access to the stored item. - is(localStorage.getItem("item"), "value", - "getItem() during a device storage situation"); - - // Removes the stored item. - localStorage.removeItem("item"); - is(localStorage.getItem("item"), null, - "getItem() after removing the item"); - - // Fails storing a new item. - try { - localStorage.setItem("newItem", "value"); - ok(false, "Storing a new item is expected to fail"); - } catch(e) { - ok(true, "Got an expected exception " + e); - } finally { - is(localStorage.getItem("newItem"), null, - "setItem while device storage is low"); - } - - // Emulates recovering from a low device storage situation. - lowDeviceStorage(false); - - // Add a test item after recovering from the low device storage situation. - localStorage.setItem("newItem", "value"); - is(localStorage.getItem("newItem"), "value", - "getItem() with available storage"); - - SimpleTest.finish(); -} - -SimpleTest.waitForExplicitFinish(); - -</script> - -</head> - -<body onload="startTest();"> -</body> -</html> diff --git a/dom/webidl/CSSStyleSheet.webidl b/dom/webidl/CSSStyleSheet.webidl index 48fb89db1..15c110b8b 100644 --- a/dom/webidl/CSSStyleSheet.webidl +++ b/dom/webidl/CSSStyleSheet.webidl @@ -23,7 +23,7 @@ interface CSSStyleSheet : StyleSheet { [ChromeOnly, BinaryName="parsingModeDOM"] readonly attribute CSSStyleSheetParsingMode parsingMode; [Throws, NeedsSubjectPrincipal] - unsigned long insertRule(DOMString rule, unsigned long index); + unsigned long insertRule(DOMString rule, optional unsigned long index = 0); [Throws, NeedsSubjectPrincipal] void deleteRule(unsigned long index); }; diff --git a/dom/webidl/HTMLScriptElement.webidl b/dom/webidl/HTMLScriptElement.webidl index 377056366..5b64c42d7 100644 --- a/dom/webidl/HTMLScriptElement.webidl +++ b/dom/webidl/HTMLScriptElement.webidl @@ -13,6 +13,8 @@ interface HTMLScriptElement : HTMLElement { attribute DOMString src; [SetterThrows] attribute DOMString type; + [SetterThrows, Pref="dom.moduleScripts.enabled"] + attribute boolean noModule; [SetterThrows] attribute DOMString charset; [SetterThrows] diff --git a/dom/webidl/Promise.webidl b/dom/webidl/Promise.webidl index 4dcb7d43e..a296553df 100644 --- a/dom/webidl/Promise.webidl +++ b/dom/webidl/Promise.webidl @@ -3,75 +3,18 @@ * 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/. * - * The origin of this IDL file is - * http://dom.spec.whatwg.org/#promises + * This IDL file contains utilities to help connect JS promises to our + * Web IDL infrastructure. */ -// TODO We use object instead Function. There is an open issue on WebIDL to -// have different types for "platform-provided function" and "user-provided -// function"; for now, we just use "object". -callback PromiseInit = void (object resolve, object reject); - callback PromiseJobCallback = void(); [TreatNonCallableAsNull] callback AnyCallback = any (any value); -// 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,WorkerDebugger,System)] -// Need to escape "Promise" so it's treated as an identifier. -interface _Promise { - // Have to use "any" (or "object", but "any" is simpler) as the type to - // support the subclassing behavior, since nothing actually requires the - // return value of PromiseSubclass.resolve/reject to be a Promise object. - [NewObject, Throws] - static any resolve(optional any value); - [NewObject, Throws] - static any reject(optional any value); - - // The [TreatNonCallableAsNull] annotation is required since then() should do - // nothing instead of throwing errors when non-callable arguments are passed. - // Have to use "any" (or "object", but "any" is simpler) as the type to - // support the subclassing behavior, since nothing actually requires the - // return value of PromiseSubclass.then/catch to be a Promise object. - [NewObject, Throws] - any then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null, - [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null); - - [NewObject, Throws] - any catch([TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null); - - // Have to use "any" (or "object", but "any" is simpler) as the type to - // support the subclassing behavior, since nothing actually requires the - // return value of PromiseSubclass.all to be a Promise object. As a result, - // we also have to do our argument conversion manually, because we want to - // convert its exceptions into rejections. - [NewObject, Throws] - static any all(optional any iterable); - - // Have to use "any" (or "object", but "any" is simpler) as the type to - // support the subclassing behavior, since nothing actually requires the - // return value of PromiseSubclass.race to be a Promise object. As a result, - // we also have to do our argument conversion manually, because we want to - // convert its exceptions into rejections. - [NewObject, Throws] - static any race(optional any iterable); -}; -#else // SPIDERMONKEY_PROMISE -[NoInterfaceObject, - Exposed=(Window,Worker,WorkerDebugger,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 107b8bc65..1a5c1aa32 100644 --- a/dom/webidl/PromiseDebugging.webidl +++ b/dom/webidl/PromiseDebugging.webidl @@ -38,11 +38,7 @@ callback interface UncaughtRejectionObserver { * caught, i.e. if its `then` callback is called, `onConsumed` will * be called. */ -#ifdef SPIDERMONKEY_PROMISE void onLeftUncaught(object p); -#else - void onLeftUncaught(Promise<any> p); -#endif SPIDERMONKEY_PROMISE /** * A Promise previously left uncaught is not the last in its @@ -51,11 +47,7 @@ callback interface UncaughtRejectionObserver { * @param p A Promise that was previously left in uncaught state is * now caught, i.e. it is not the last in its chain anymore. */ -#ifdef SPIDERMONKEY_PROMISE void onConsumed(object p); -#else - void onConsumed(Promise<any> p); -#endif SPIDERMONKEY_PROMISE }; [ChromeOnly, Exposed=(Window,System)] @@ -105,42 +97,6 @@ interface PromiseDebugging { [Throws] static object? getFullfillmentStack(object p); -#ifndef SPIDERMONKEY_PROMISE - /** - * Get the promises directly depending on a given promise. These are: - * - * 1) Return values of then() calls on the promise - * 2) Return values of Promise.all() if the given promise was passed in as one - * of the arguments. - * 3) Return values of Promise.race() if the given promise was passed in as - * one of the arguments. - * - * Once a promise is settled, it will generally notify its dependent promises - * and forget about them, so this is most useful on unsettled promises. - * - * Note that this function only returns the promises that directly depend on - * p. It does not recursively return promises that depend on promises that - * depend on p. - */ - [Throws] - static sequence<Promise<any>> getDependentPromises(object p); - - /** - * Get the number of milliseconds elapsed since the given promise was created. - */ - [Throws] - static DOMHighResTimeStamp getPromiseLifetime(object p); - - /* - * Get the number of milliseconds elapsed between the promise being created - * and being settled. Throws NS_ERROR_UNEXPECTED if the promise has not - * settled. - */ - [Throws] - static DOMHighResTimeStamp getTimeToSettle(object p); - -#endif // SPIDERMONKEY_PROMISE - /** * Watching uncaught rejections on the current thread. * diff --git a/dom/webidl/TestInterfaceJS.webidl b/dom/webidl/TestInterfaceJS.webidl index 2cf8d701a..2757745f0 100644 --- a/dom/webidl/TestInterfaceJS.webidl +++ b/dom/webidl/TestInterfaceJS.webidl @@ -70,7 +70,7 @@ interface TestInterfaceJS : EventTarget { // Tests for promise-rejection behavior Promise<void> testPromiseWithThrowingChromePromiseInit(); - Promise<void> testPromiseWithThrowingContentPromiseInit(PromiseInit func); + Promise<void> testPromiseWithThrowingContentPromiseInit(Function func); Promise<void> testPromiseWithDOMExceptionThrowingPromiseInit(); Promise<void> testPromiseWithThrowingChromeThenFunction(); Promise<void> testPromiseWithThrowingContentThenFunction(AnyCallback func); diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 0fe10eff9..172895f97 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -12,8 +12,6 @@ PREPROCESSED_WEBIDL_FILES = [ 'HTMLMediaElement.webidl', 'Navigator.webidl', 'Node.webidl', - 'Promise.webidl', - 'PromiseDebugging.webidl', 'Window.webidl', ] @@ -371,6 +369,8 @@ WEBIDL_FILES = [ 'PresentationRequest.webidl', 'ProcessingInstruction.webidl', 'ProfileTimelineMarker.webidl', + 'Promise.webidl', + 'PromiseDebugging.webidl', 'PushEvent.webidl', 'PushManager.webidl', 'PushManager.webidl', diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 27eb570e9..c6ef21f2c 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2543,6 +2543,12 @@ WorkerPrivateParent<Derived>::DisableDebugger() WorkerPrivate* self = ParentAsWorkerPrivate(); + // RegisterDebugger might have been dispatched but not completed. + // Wait for its execution to complete before unregistering. + if (!NS_IsMainThread()) { + self->WaitForIsDebuggerRegistered(true); + } + if (NS_FAILED(UnregisterWorkerDebugger(self))) { NS_WARNING("Failed to unregister worker debugger!"); } |