diff options
Diffstat (limited to 'dom')
51 files changed, 1295 insertions, 304 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/nsDocument.cpp b/dom/base/nsDocument.cpp index a6ed419df..6b8e11db0 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -9222,19 +9222,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 +9256,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 +9266,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 +9290,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 +9307,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 @@ -11943,7 +11949,8 @@ nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const &aWindowSizes->mLayoutPresShellSize, &aWindowSizes->mLayoutStyleSetsSize, &aWindowSizes->mLayoutTextRunsSize, - &aWindowSizes->mLayoutPresContextSize); + &aWindowSizes->mLayoutPresContextSize, + &aWindowSizes->mLayoutFramePropertiesSize); } aWindowSizes->mPropertyTablesSize += diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 95fd57545..2b29b98fa 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -948,11 +948,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/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index f9126f4da..afaa24f09 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2504,8 +2504,7 @@ nsGlobalWindow::WouldReuseInnerWindow(nsIDocument* aNewDocument) } bool equal; - if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(), - &equal)) && + if (NS_SUCCEEDED(mDoc->NodePrincipal()->EqualsConsideringDomain(aNewDocument->NodePrincipal(), &equal)) && equal) { // The origin is the same. return true; @@ -9335,7 +9334,7 @@ nsGlobalWindow::EnterModalState() topWin->mSuspendedDoc = topDoc; if (topDoc) { - topDoc->SuppressEventHandling(nsIDocument::eEvents); + topDoc->SuppressEventHandling(nsIDocument::eAnimationsOnly); } nsGlobalWindow* inner = topWin->GetCurrentInnerWindowInternal(); @@ -9372,7 +9371,7 @@ nsGlobalWindow::LeaveModalState() if (topWin->mSuspendedDoc) { nsCOMPtr<nsIDocument> currentDoc = topWin->GetExtantDoc(); - topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, + topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eAnimationsOnly, currentDoc == topWin->mSuspendedDoc); topWin->mSuspendedDoc = nullptr; } 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 c1b732258..3c850c4cd 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -715,11 +715,13 @@ nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent) /// would keep the docshell around, but trash the frameloader UnloadObject(); } - nsIDocument* doc = thisContent->GetComposedDoc(); - if (doc && doc->IsActive()) { + 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); + } } } diff --git a/dom/base/nsWindowMemoryReporter.cpp b/dom/base/nsWindowMemoryReporter.cpp index acec4acfb..8f4bf6b11 100644 --- a/dom/base/nsWindowMemoryReporter.cpp +++ b/dom/base/nsWindowMemoryReporter.cpp @@ -400,6 +400,12 @@ CollectWindowReports(nsGlobalWindow *aWindow, aWindowTotalSizes->mLayoutPresContextSize += windowSizes.mLayoutPresContextSize; + REPORT_SIZE("/layout/frame-properties", windowSizes.mLayoutFramePropertiesSize, + "Memory used for frame properties attached to frames " + "within a window."); + aWindowTotalSizes->mLayoutFramePropertiesSize += + windowSizes.mLayoutFramePropertiesSize; + // There are many different kinds of frames, but it is very likely // that only a few matter. Implement a cutoff so we don't bloat // about:memory with many uninteresting entries. @@ -563,6 +569,9 @@ nsWindowMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport, REPORT("window-objects/layout/pres-contexts", windowTotalSizes.mLayoutPresContextSize, "This is the sum of all windows' 'layout/pres-contexts' numbers."); + REPORT("window-objects/layout/frame-properties", windowTotalSizes.mLayoutFramePropertiesSize, + "This is the sum of all windows' 'layout/frame-properties' numbers."); + size_t frameTotal = 0; #define FRAME_ID(classname) \ frameTotal += windowTotalSizes.mArenaStats.FRAME_ID_STAT_FIELD(classname); diff --git a/dom/base/nsWindowMemoryReporter.h b/dom/base/nsWindowMemoryReporter.h index b9e986959..5d40dc9f5 100644 --- a/dom/base/nsWindowMemoryReporter.h +++ b/dom/base/nsWindowMemoryReporter.h @@ -33,6 +33,7 @@ class nsWindowSizes { macro(Style, mLayoutStyleSetsSize) \ macro(Other, mLayoutTextRunsSize) \ macro(Other, mLayoutPresContextSize) \ + macro(Other, mLayoutFramePropertiesSize) \ macro(Other, mPropertyTablesSize) \ public: diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 8d2bdaac6..a26fc4422 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1184,14 +1184,6 @@ GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor, } bool -UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp) -{ - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - args.rval().set(args.thisv()); - return true; -} - -bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp) { return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR); diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index a3ec70f47..5cab835b3 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -2045,9 +2045,6 @@ GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID, } bool -UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp); - -bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp); bool diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 74acb5918..d7d700a96 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2390,11 +2390,10 @@ class MethodDefiner(PropertyDefiner): # Synthesize our valueOf method self.regular.append({ "name": 'valueOf', - "nativeName": "UnforgeableValueOf", + "selfHostedName": "Object_valueOf", "methodInfo": False, "length": 0, - "flags": "JSPROP_ENUMERATE", # readonly/permanent added - # automatically. + "flags": "0", # readonly/permanent added automatically. "condition": MemberCondition() }) @@ -3456,19 +3455,15 @@ def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode, "nsContentUtils::ThreadsafeIsCallerChrome()")) if descriptor.interface.getExtendedAttribute("Unforgeable"): - # We do our undefined toJSON and toPrimitive here, not as a regular - # property because we don't have a concept of value props anywhere in - # IDL. + # We do our undefined toPrimitive here, not as a regular property + # because we don't have a concept of value props anywhere in IDL. unforgeables.append(CGGeneric(fill( """ JS::RootedId toPrimitive(aCx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::toPrimitive))); if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive, JS::UndefinedHandleValue, - JSPROP_READONLY | JSPROP_PERMANENT) || - !JS_DefineProperty(aCx, ${holderName}, "toJSON", - JS::UndefinedHandleValue, - JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) { + JSPROP_READONLY | JSPROP_PERMANENT)) { $*{failureCode} } """, diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index 043b3c494..ed8a4d37e 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -6,6 +6,12 @@ TEST_DIRS += ['test'] +XPIDL_SOURCES += [ + 'nsIScriptError.idl' +] + +XPIDL_MODULE = 'dom_bindings' + EXPORTS.ipc += [ 'ErrorIPCUtils.h', ] @@ -91,6 +97,8 @@ UNIFIED_SOURCES += [ 'DOMJSProxyHandler.cpp', 'Exceptions.cpp', 'IterableIterator.cpp', + 'nsScriptError.cpp', + 'nsScriptErrorWithStack.cpp', 'SimpleGlobalObject.cpp', 'ToJSValue.cpp', 'WebIDLGlobalNameHash.cpp', diff --git a/dom/bindings/nsIScriptError.idl b/dom/bindings/nsIScriptError.idl new file mode 100644 index 000000000..8436361a8 --- /dev/null +++ b/dom/bindings/nsIScriptError.idl @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* + * nsIConsoleMessage subclass for representing JavaScript errors and warnings. + */ + + +#include "nsISupports.idl" +#include "nsIArray.idl" +#include "nsIConsoleMessage.idl" + +%{C++ +#include "nsStringGlue.h" // for nsDependentCString +%} + +[scriptable, uuid(e8933fc9-c302-4e12-a55b-4f88611d9c6c)] +interface nsIScriptErrorNote : nsISupports +{ + readonly attribute AString errorMessage; + readonly attribute AString sourceName; + readonly attribute uint32_t lineNumber; + readonly attribute uint32_t columnNumber; + + AUTF8String toString(); +}; + +[scriptable, uuid(63eb4d3e-7d99-4150-b4f3-11314f9d82a9)] +interface nsIScriptError : nsIConsoleMessage +{ + /** pseudo-flag for default case */ + const unsigned long errorFlag = 0x0; + + /** message is warning */ + const unsigned long warningFlag = 0x1; + + /** exception was thrown for this case - exception-aware hosts can ignore */ + const unsigned long exceptionFlag = 0x2; + + // XXX check how strict is implemented these days. + /** error or warning is due to strict option */ + const unsigned long strictFlag = 0x4; + + /** just a log message */ + const unsigned long infoFlag = 0x8; + + /** + * The error message without any context/line number information. + * + * @note nsIConsoleMessage.message will return the error formatted + * with file/line information. + */ + readonly attribute AString errorMessage; + + readonly attribute AString sourceName; + readonly attribute AString sourceLine; + readonly attribute uint32_t lineNumber; + readonly attribute uint32_t columnNumber; + readonly attribute uint32_t flags; + + /** + * Categories I know about - + * XUL javascript + * content javascript (both of these from nsDocShell, currently) + * system javascript (errors in JS components and other system JS) + */ + readonly attribute string category; + + /* Get the window id this was initialized with. Zero will be + returned if init() was used instead of initWithWindowID(). */ + readonly attribute unsigned long long outerWindowID; + + /* Get the inner window id this was initialized with. Zero will be + returned if init() was used instead of initWithWindowID(). */ + readonly attribute unsigned long long innerWindowID; + + readonly attribute boolean isFromPrivateWindow; + + attribute jsval stack; + + /** + * The name of a template string, as found in js.msg, associated with the + * error message. + */ + attribute AString errorMessageName; + + readonly attribute nsIArray notes; + + void init(in AString message, + in AString sourceName, + in AString sourceLine, + in uint32_t lineNumber, + in uint32_t columnNumber, + in uint32_t flags, + in string category); + + /* This should be called instead of nsIScriptError.init to + initialize with a window id. The window id should be for the + inner window associated with this error. */ + void initWithWindowID(in AString message, + in AString sourceName, + in AString sourceLine, + in uint32_t lineNumber, + in uint32_t columnNumber, + in uint32_t flags, + in ACString category, + in unsigned long long innerWindowID); +%{C++ + // This overload allows passing a literal string for category. + template<uint32_t N> + nsresult InitWithWindowID(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const char (&c)[N], + uint64_t aInnerWindowID) + { + nsDependentCString category(c, N - 1); + return InitWithWindowID(message, sourceName, sourceLine, lineNumber, + columnNumber, flags, category, aInnerWindowID); + } +%} + +}; + +%{ C++ +#define NS_SCRIPTERROR_CID \ +{ 0x1950539a, 0x90f0, 0x4d22, { 0xb5, 0xaf, 0x71, 0x32, 0x9c, 0x68, 0xfa, 0x35 }} + +#define NS_SCRIPTERROR_CONTRACTID "@mozilla.org/scripterror;1" +%} diff --git a/dom/bindings/nsScriptError.cpp b/dom/bindings/nsScriptError.cpp new file mode 100644 index 000000000..9248d1a31 --- /dev/null +++ b/dom/bindings/nsScriptError.cpp @@ -0,0 +1,438 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* 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/. */ + +/* + * nsIScriptError implementation. + */ + +#include "nsScriptError.h" +#include "jsprf.h" +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" +#include "nsGlobalWindow.h" +#include "nsNetUtil.h" +#include "nsPIDOMWindow.h" +#include "nsILoadContext.h" +#include "nsIDocShell.h" +#include "nsIMutableArray.h" +#include "nsIScriptError.h" +#include "nsISensitiveInfoHiddenURI.h" + +static_assert(nsIScriptError::errorFlag == JSREPORT_ERROR && + nsIScriptError::warningFlag == JSREPORT_WARNING && + nsIScriptError::exceptionFlag == JSREPORT_EXCEPTION && + nsIScriptError::strictFlag == JSREPORT_STRICT && + nsIScriptError::infoFlag == JSREPORT_USER_1, + "flags should be consistent"); + +nsScriptErrorBase::nsScriptErrorBase() + : mMessage(), + mMessageName(), + mSourceName(), + mLineNumber(0), + mSourceLine(), + mColumnNumber(0), + mFlags(0), + mCategory(), + mOuterWindowID(0), + mInnerWindowID(0), + mTimeStamp(0), + mInitializedOnMainThread(false), + mIsFromPrivateWindow(false) +{ +} + +nsScriptErrorBase::~nsScriptErrorBase() {} + +void +nsScriptErrorBase::AddNote(nsIScriptErrorNote* note) +{ + mNotes.AppendObject(note); +} + +void +nsScriptErrorBase::InitializeOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mInitializedOnMainThread); + + if (mInnerWindowID) { + nsGlobalWindow* window = + nsGlobalWindow::GetInnerWindowWithId(mInnerWindowID); + if (window) { + nsPIDOMWindowOuter* outer = window->GetOuterWindow(); + if (outer) + mOuterWindowID = outer->WindowID(); + + nsIDocShell* docShell = window->GetDocShell(); + nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell); + + if (loadContext) { + // Never mark exceptions from chrome windows as having come from + // private windows, since we always want them to be reported. + nsIPrincipal* winPrincipal = window->GetPrincipal(); + mIsFromPrivateWindow = loadContext->UsePrivateBrowsing() && + !nsContentUtils::IsSystemPrincipal(winPrincipal); + } + } + } + + mInitializedOnMainThread = true; +} + +// nsIConsoleMessage methods +NS_IMETHODIMP +nsScriptErrorBase::GetMessageMoz(char16_t** result) { + nsresult rv; + + nsAutoCString message; + rv = ToString(message); + if (NS_FAILED(rv)) + return rv; + + *result = UTF8ToNewUnicode(message); + if (!*result) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + + +NS_IMETHODIMP +nsScriptErrorBase::GetLogLevel(uint32_t* aLogLevel) +{ + if (mFlags & (uint32_t)nsIScriptError::infoFlag) { + *aLogLevel = nsIConsoleMessage::info; + } else if (mFlags & (uint32_t)nsIScriptError::warningFlag) { + *aLogLevel = nsIConsoleMessage::warn; + } else { + *aLogLevel = nsIConsoleMessage::error; + } + return NS_OK; +} + +// nsIScriptError methods +NS_IMETHODIMP +nsScriptErrorBase::GetErrorMessage(nsAString& aResult) { + aResult.Assign(mMessage); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetSourceName(nsAString& aResult) { + aResult.Assign(mSourceName); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetSourceLine(nsAString& aResult) { + aResult.Assign(mSourceLine); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetLineNumber(uint32_t* result) { + *result = mLineNumber; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetColumnNumber(uint32_t* result) { + *result = mColumnNumber; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetFlags(uint32_t* result) { + *result = mFlags; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetCategory(char** result) { + *result = ToNewCString(mCategory); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetStack(JS::MutableHandleValue aStack) { + aStack.setUndefined(); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::SetStack(JS::HandleValue aStack) { + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetErrorMessageName(nsAString& aErrorMessageName) { + aErrorMessageName = mMessageName; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) { + mMessageName = aErrorMessageName; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::Init(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const char* category) +{ + return InitWithWindowID(message, sourceName, sourceLine, lineNumber, + columnNumber, flags, + category ? nsDependentCString(category) + : EmptyCString(), + 0); +} + +static void +AssignSourceNameHelper(nsString& aSourceNameDest, const nsAString& aSourceNameSrc) +{ + if (aSourceNameSrc.IsEmpty()) + return; + + aSourceNameDest.Assign(aSourceNameSrc); + + nsCOMPtr<nsIURI> uri; + nsAutoCString pass; + if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), aSourceNameSrc)) && + NS_SUCCEEDED(uri->GetPassword(pass)) && + !pass.IsEmpty()) + { + nsCOMPtr<nsISensitiveInfoHiddenURI> safeUri = do_QueryInterface(uri); + + nsAutoCString loc; + if (safeUri && NS_SUCCEEDED(safeUri->GetSensitiveInfoHiddenSpec(loc))) + aSourceNameDest.Assign(NS_ConvertUTF8toUTF16(loc)); + } +} + +NS_IMETHODIMP +nsScriptErrorBase::InitWithWindowID(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const nsACString& category, + uint64_t aInnerWindowID) +{ + mMessage.Assign(message); + AssignSourceNameHelper(mSourceName, sourceName); + mLineNumber = lineNumber; + mSourceLine.Assign(sourceLine); + mColumnNumber = columnNumber; + mFlags = flags; + mCategory = category; + mTimeStamp = JS_Now() / 1000; + mInnerWindowID = aInnerWindowID; + + if (aInnerWindowID && NS_IsMainThread()) { + InitializeOnMainThread(); + } + + return NS_OK; +} + +static nsresult +ToStringHelper(const char* aSeverity, const nsString& aMessage, + const nsString& aSourceName, const nsString* aSourceLine, + uint32_t aLineNumber, uint32_t aColumnNumber, + nsACString& /*UTF8*/ aResult) +{ + static const char format0[] = + "[%s: \"%s\" {file: \"%s\" line: %d column: %d source: \"%s\"}]"; + static const char format1[] = + "[%s: \"%s\" {file: \"%s\" line: %d}]"; + static const char format2[] = + "[%s: \"%s\"]"; + + char* temp; + char* tempMessage = nullptr; + char* tempSourceName = nullptr; + char* tempSourceLine = nullptr; + + if (!aMessage.IsEmpty()) + tempMessage = ToNewUTF8String(aMessage); + if (!aSourceName.IsEmpty()) + // Use at most 512 characters from mSourceName. + tempSourceName = ToNewUTF8String(StringHead(aSourceName, 512)); + if (aSourceLine && !aSourceLine->IsEmpty()) + // Use at most 512 characters from mSourceLine. + tempSourceLine = ToNewUTF8String(StringHead(*aSourceLine, 512)); + + if (nullptr != tempSourceName && nullptr != tempSourceLine) { + temp = JS_smprintf(format0, + aSeverity, + tempMessage, + tempSourceName, + aLineNumber, + aColumnNumber, + tempSourceLine); + } else if (!aSourceName.IsEmpty()) { + temp = JS_smprintf(format1, + aSeverity, + tempMessage, + tempSourceName, + aLineNumber); + } else { + temp = JS_smprintf(format2, + aSeverity, + tempMessage); + } + + if (nullptr != tempMessage) + free(tempMessage); + if (nullptr != tempSourceName) + free(tempSourceName); + if (nullptr != tempSourceLine) + free(tempSourceLine); + + if (!temp) + return NS_ERROR_OUT_OF_MEMORY; + + aResult.Assign(temp); + JS_smprintf_free(temp); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::ToString(nsACString& /*UTF8*/ aResult) +{ + static const char error[] = "JavaScript Error"; + static const char warning[] = "JavaScript Warning"; + + const char* severity = !(mFlags & JSREPORT_WARNING) ? error : warning; + + return ToStringHelper(severity, mMessage, mSourceName, &mSourceLine, + mLineNumber, mColumnNumber, aResult); +} + +NS_IMETHODIMP +nsScriptErrorBase::GetOuterWindowID(uint64_t* aOuterWindowID) +{ + NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread, + "This can't be safely determined off the main thread, " + "returning an inaccurate value!"); + + if (!mInitializedOnMainThread && NS_IsMainThread()) { + InitializeOnMainThread(); + } + + *aOuterWindowID = mOuterWindowID; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetInnerWindowID(uint64_t* aInnerWindowID) +{ + *aInnerWindowID = mInnerWindowID; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetTimeStamp(int64_t* aTimeStamp) +{ + *aTimeStamp = mTimeStamp; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow) +{ + NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread, + "This can't be safely determined off the main thread, " + "returning an inaccurate value!"); + + if (!mInitializedOnMainThread && NS_IsMainThread()) { + InitializeOnMainThread(); + } + + *aIsFromPrivateWindow = mIsFromPrivateWindow; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::GetNotes(nsIArray** aNotes) +{ + nsresult rv = NS_OK; + nsCOMPtr<nsIMutableArray> array = + do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t len = mNotes.Length(); + for (uint32_t i = 0; i < len; i++) + array->AppendElement(mNotes[i], false); + array.forget(aNotes); + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError) + +nsScriptErrorNote::nsScriptErrorNote() + : mMessage(), + mSourceName(), + mLineNumber(0), + mColumnNumber(0) +{ +} + +nsScriptErrorNote::~nsScriptErrorNote() {} + +void +nsScriptErrorNote::Init(const nsAString& message, + const nsAString& sourceName, + uint32_t lineNumber, + uint32_t columnNumber) +{ + mMessage.Assign(message); + AssignSourceNameHelper(mSourceName, sourceName); + mLineNumber = lineNumber; + mColumnNumber = columnNumber; +} + +// nsIScriptErrorNote methods +NS_IMETHODIMP +nsScriptErrorNote::GetErrorMessage(nsAString& aResult) { + aResult.Assign(mMessage); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorNote::GetSourceName(nsAString& aResult) { + aResult.Assign(mSourceName); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorNote::GetLineNumber(uint32_t* result) { + *result = mLineNumber; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorNote::GetColumnNumber(uint32_t* result) { + *result = mColumnNumber; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorNote::ToString(nsACString& /*UTF8*/ aResult) +{ + return ToStringHelper("JavaScript Note", mMessage, mSourceName, nullptr, + mLineNumber, mColumnNumber, aResult); +} + +NS_IMPL_ISUPPORTS(nsScriptErrorNote, nsIScriptErrorNote) diff --git a/dom/bindings/nsScriptError.h b/dom/bindings/nsScriptError.h new file mode 100644 index 000000000..b8049d0a0 --- /dev/null +++ b/dom/bindings/nsScriptError.h @@ -0,0 +1,109 @@ +/* -*- 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_nsScriptError_h +#define mozilla_dom_nsScriptError_h + +#include "mozilla/Atomics.h" + +#include <stdint.h> + +#include "jsapi.h" +#include "js/RootingAPI.h" + +#include "nsIScriptError.h" +#include "nsString.h" + +class nsScriptErrorNote final : public nsIScriptErrorNote { + public: + nsScriptErrorNote(); + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISCRIPTERRORNOTE + + void Init(const nsAString& message, const nsAString& sourceName, + uint32_t lineNumber, uint32_t columnNumber); + + private: + virtual ~nsScriptErrorNote(); + + nsString mMessage; + nsString mSourceName; + nsString mSourceLine; + uint32_t mLineNumber; + uint32_t mColumnNumber; +}; + +// Definition of nsScriptError.. +class nsScriptErrorBase : public nsIScriptError { +public: + nsScriptErrorBase(); + + NS_DECL_NSICONSOLEMESSAGE + NS_DECL_NSISCRIPTERROR + + void AddNote(nsIScriptErrorNote* note); + +protected: + virtual ~nsScriptErrorBase(); + + void + InitializeOnMainThread(); + + nsCOMArray<nsIScriptErrorNote> mNotes; + nsString mMessage; + nsString mMessageName; + nsString mSourceName; + uint32_t mLineNumber; + nsString mSourceLine; + uint32_t mColumnNumber; + uint32_t mFlags; + nsCString mCategory; + // mOuterWindowID is set on the main thread from InitializeOnMainThread(). + uint64_t mOuterWindowID; + uint64_t mInnerWindowID; + int64_t mTimeStamp; + // mInitializedOnMainThread and mIsFromPrivateWindow are set on the main + // thread from InitializeOnMainThread(). + mozilla::Atomic<bool> mInitializedOnMainThread; + bool mIsFromPrivateWindow; +}; + +class nsScriptError final : public nsScriptErrorBase { +public: + nsScriptError() {} + NS_DECL_THREADSAFE_ISUPPORTS + +private: + virtual ~nsScriptError() {} +}; + +class nsScriptErrorWithStack : public nsScriptErrorBase { +public: + explicit nsScriptErrorWithStack(JS::HandleObject); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack) + + NS_IMETHOD Init(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const char* category) override; + + NS_IMETHOD GetStack(JS::MutableHandleValue) override; + NS_IMETHOD ToString(nsACString& aResult) override; + +private: + virtual ~nsScriptErrorWithStack(); + // Complete stackframe where the error happened. + // Must be SavedFrame object. + JS::Heap<JSObject*> mStack; +}; + +#endif /* mozilla_dom_nsScriptError_h */ diff --git a/dom/bindings/nsScriptErrorWithStack.cpp b/dom/bindings/nsScriptErrorWithStack.cpp new file mode 100644 index 000000000..74c00999f --- /dev/null +++ b/dom/bindings/nsScriptErrorWithStack.cpp @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* 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/. */ + +/* + * nsScriptErrorWithStack implementation. + * a main-thread-only, cycle-collected subclass of nsScriptErrorBase + * that can store a SavedFrame stack trace object. + */ + +#include "nsScriptError.h" +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/dom/ScriptSettings.h" +#include "nsGlobalWindow.h" +#include "nsCycleCollectionParticipant.h" + +using namespace mozilla::dom; + +namespace { + +static nsCString +FormatStackString(JSContext* cx, HandleObject aStack) { + JS::RootedString formattedStack(cx); + + if (!JS::BuildStackString(cx, aStack, &formattedStack)) { + return nsCString(); + } + + nsAutoJSString stackJSString; + if (!stackJSString.init(cx, formattedStack)) { + return nsCString(); + } + + return NS_ConvertUTF16toUTF8(stackJSString.get()); +} + +} + + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptErrorWithStack) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptErrorWithStack) + tmp->mStack = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptErrorWithStack) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsScriptErrorWithStack) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptErrorWithStack) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptErrorWithStack) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptErrorWithStack) + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_INTERFACE_MAP_ENTRY(nsIConsoleMessage) + NS_INTERFACE_MAP_ENTRY(nsIScriptError) +NS_INTERFACE_MAP_END + +nsScriptErrorWithStack::nsScriptErrorWithStack(JS::HandleObject aStack) + : mStack(aStack) +{ + MOZ_ASSERT(NS_IsMainThread(), "You can't use this class on workers."); + mozilla::HoldJSObjects(this); +} + +nsScriptErrorWithStack::~nsScriptErrorWithStack() { + mozilla::DropJSObjects(this); +} + +NS_IMETHODIMP +nsScriptErrorWithStack::Init(const nsAString& message, + const nsAString& sourceName, + const nsAString& sourceLine, + uint32_t lineNumber, + uint32_t columnNumber, + uint32_t flags, + const char* category) +{ + MOZ_CRASH("nsScriptErrorWithStack requires to be initialized with a document, by using InitWithWindowID"); +} + +NS_IMETHODIMP +nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) { + aStack.setObjectOrNull(mStack); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorWithStack::ToString(nsACString& /*UTF8*/ aResult) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCString message; + nsresult rv = nsScriptErrorBase::ToString(message); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mStack) { + aResult.Assign(message); + return NS_OK; + } + + AutoJSAPI jsapi; + if (!jsapi.Init(mStack)) { + return NS_ERROR_FAILURE; + } + + JSContext* cx = jsapi.cx(); + RootedObject stack(cx, mStack); + nsCString stackString = FormatStackString(cx, stack); + nsCString combined = message + NS_LITERAL_CSTRING("\n") + stackString; + aResult.Assign(combined); + + return NS_OK; +} diff --git a/dom/canvas/WebGLBuffer.cpp b/dom/canvas/WebGLBuffer.cpp index 1eaf37ac4..02a8f649f 100644 --- a/dom/canvas/WebGLBuffer.cpp +++ b/dom/canvas/WebGLBuffer.cpp @@ -134,6 +134,7 @@ WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usa if (error) { MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY); mContext->ErrorOutOfMemory("%s: Error from driver: 0x%04x", funcName, error); + mByteLength = 0; return; } } else { diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index 8a20237ff..0510e6898 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -20,6 +20,7 @@ #include "mozilla/gfx/2D.h" #include "mozilla/LinkedList.h" #include "mozilla/UniquePtr.h" +#include "mozilla/WeakPtr.h" #include "nsCycleCollectionNoteChild.h" #include "nsICanvasRenderingContextInternal.h" #include "nsLayoutUtils.h" @@ -299,6 +300,7 @@ class WebGLContext , public WebGLContextUnchecked , public WebGLRectangleObject , public nsWrapperCache + , public SupportsWeakPtr<WebGLContext> { friend class ScopedDrawHelper; friend class ScopedDrawWithTransformFeedback; @@ -342,6 +344,7 @@ public: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext, nsIDOMWebGLRenderingContext) + MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLContext) virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0; diff --git a/dom/canvas/WebGLExtensionDebugShaders.cpp b/dom/canvas/WebGLExtensionDebugShaders.cpp index 75880465e..8399aeb95 100644 --- a/dom/canvas/WebGLExtensionDebugShaders.cpp +++ b/dom/canvas/WebGLExtensionDebugShaders.cpp @@ -29,15 +29,10 @@ WebGLExtensionDebugShaders::GetTranslatedShaderSource(const WebGLShader& shader, { retval.SetIsVoid(true); - if (mIsLost) { - mContext->ErrorInvalidOperation("%s: Extension is lost.", - "getTranslatedShaderSource"); + if (mIsLost || !mContext) { return; } - if (mContext->IsContextLost()) - return; - if (!mContext->ValidateObject("getShaderTranslatedSource: shader", shader)) return; diff --git a/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp index e2e34f14e..da76eeb2d 100644 --- a/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp +++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp @@ -40,8 +40,10 @@ void WebGLExtensionDisjointTimerQuery::DeleteQueryEXT(WebGLQuery* query) const { const char funcName[] = "deleteQueryEXT"; - if (mIsLost) - return; + + if (mIsLost || !mContext) { + return; + } mContext->DeleteQuery(query, funcName); } @@ -50,8 +52,10 @@ bool WebGLExtensionDisjointTimerQuery::IsQueryEXT(const WebGLQuery* query) const { const char funcName[] = "isQueryEXT"; - if (mIsLost) - return false; + + if (mIsLost || !mContext) { + return false; + } return mContext->IsQuery(query, funcName); } @@ -60,8 +64,10 @@ void WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target, WebGLQuery& query) const { const char funcName[] = "beginQueryEXT"; - if (mIsLost) - return; + + if (mIsLost || !mContext) { + return; + } mContext->BeginQuery(target, query, funcName); } @@ -70,8 +76,10 @@ void WebGLExtensionDisjointTimerQuery::EndQueryEXT(GLenum target) const { const char funcName[] = "endQueryEXT"; - if (mIsLost) - return; + + if (mIsLost || !mContext) { + return; + } mContext->EndQuery(target, funcName); } @@ -80,8 +88,10 @@ void WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLQuery& query, GLenum target) const { const char funcName[] = "queryCounterEXT"; - if (mIsLost) - return; + + if (mIsLost || !mContext) { + return; + } if (!mContext->ValidateObject(funcName, query)) return; @@ -95,8 +105,10 @@ WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target, GLen { const char funcName[] = "getQueryEXT"; retval.setNull(); - if (mIsLost) - return; + + if (mIsLost || !mContext) { + return; + } mContext->GetQuery(cx, target, pname, retval, funcName); } @@ -108,8 +120,10 @@ WebGLExtensionDisjointTimerQuery::GetQueryObjectEXT(JSContext* cx, { const char funcName[] = "getQueryObjectEXT"; retval.setNull(); - if (mIsLost) - return; + + if (mIsLost || !mContext) { + return; + } mContext->GetQueryParameter(cx, query, pname, retval, funcName); } diff --git a/dom/canvas/WebGLExtensionDrawBuffers.cpp b/dom/canvas/WebGLExtensionDrawBuffers.cpp index 27aa76cc7..6f386621f 100644 --- a/dom/canvas/WebGLExtensionDrawBuffers.cpp +++ b/dom/canvas/WebGLExtensionDrawBuffers.cpp @@ -36,7 +36,9 @@ void WebGLExtensionDrawBuffers::DrawBuffersWEBGL(const dom::Sequence<GLenum>& buffers) { if (mIsLost) { - mContext->ErrorInvalidOperation("drawBuffersWEBGL: Extension is lost."); + if (mContext) { + mContext->ErrorInvalidOperation("drawBuffersWEBGL: Extension is lost."); + } return; } diff --git a/dom/canvas/WebGLExtensionInstancedArrays.cpp b/dom/canvas/WebGLExtensionInstancedArrays.cpp index 10d0533fe..22b3ec12c 100644 --- a/dom/canvas/WebGLExtensionInstancedArrays.cpp +++ b/dom/canvas/WebGLExtensionInstancedArrays.cpp @@ -28,8 +28,10 @@ WebGLExtensionInstancedArrays::DrawArraysInstancedANGLE(GLenum mode, GLsizei primcount) { if (mIsLost) { - mContext->ErrorInvalidOperation("%s: Extension is lost.", - "drawArraysInstancedANGLE"); + if (mContext) { + mContext->ErrorInvalidOperation("%s: Extension is lost.", + "drawArraysInstancedANGLE"); + } return; } @@ -44,8 +46,10 @@ WebGLExtensionInstancedArrays::DrawElementsInstancedANGLE(GLenum mode, GLsizei primcount) { if (mIsLost) { - mContext->ErrorInvalidOperation("%s: Extension is lost.", - "drawElementsInstancedANGLE"); + if (mContext) { + mContext->ErrorInvalidOperation("%s: Extension is lost.", + "drawElementsInstancedANGLE"); + } return; } @@ -57,8 +61,10 @@ WebGLExtensionInstancedArrays::VertexAttribDivisorANGLE(GLuint index, GLuint divisor) { if (mIsLost) { - mContext->ErrorInvalidOperation("%s: Extension is lost.", - "vertexAttribDivisorANGLE"); + if (mContext) { + mContext->ErrorInvalidOperation("%s: Extension is lost.", + "vertexAttribDivisorANGLE"); + } return; } diff --git a/dom/canvas/WebGLExtensionLoseContext.cpp b/dom/canvas/WebGLExtensionLoseContext.cpp index 020731e63..41f1633d8 100644 --- a/dom/canvas/WebGLExtensionLoseContext.cpp +++ b/dom/canvas/WebGLExtensionLoseContext.cpp @@ -22,12 +22,14 @@ WebGLExtensionLoseContext::~WebGLExtensionLoseContext() void WebGLExtensionLoseContext::LoseContext() { + if (!mContext) return; mContext->LoseContext(); } void WebGLExtensionLoseContext::RestoreContext() { + if (!mContext) return; mContext->RestoreContext(); } diff --git a/dom/canvas/WebGLExtensionVertexArray.cpp b/dom/canvas/WebGLExtensionVertexArray.cpp index 0984582f5..39aa96801 100644 --- a/dom/canvas/WebGLExtensionVertexArray.cpp +++ b/dom/canvas/WebGLExtensionVertexArray.cpp @@ -25,7 +25,7 @@ WebGLExtensionVertexArray::~WebGLExtensionVertexArray() already_AddRefed<WebGLVertexArray> WebGLExtensionVertexArray::CreateVertexArrayOES() { - if (mIsLost) + if (mIsLost || !mContext) return nullptr; return mContext->CreateVertexArray(); @@ -34,7 +34,7 @@ WebGLExtensionVertexArray::CreateVertexArrayOES() void WebGLExtensionVertexArray::DeleteVertexArrayOES(WebGLVertexArray* array) { - if (mIsLost) + if (mIsLost || !mContext) return; mContext->DeleteVertexArray(array); @@ -43,7 +43,7 @@ WebGLExtensionVertexArray::DeleteVertexArrayOES(WebGLVertexArray* array) bool WebGLExtensionVertexArray::IsVertexArrayOES(const WebGLVertexArray* array) { - if (mIsLost) + if (mIsLost || !mContext) return false; return mContext->IsVertexArray(array); @@ -52,7 +52,7 @@ WebGLExtensionVertexArray::IsVertexArrayOES(const WebGLVertexArray* array) void WebGLExtensionVertexArray::BindVertexArrayOES(WebGLVertexArray* array) { - if (mIsLost) + if (mIsLost || !mContext) return; mContext->BindVertexArray(array); diff --git a/dom/canvas/WebGLObjectModel.h b/dom/canvas/WebGLObjectModel.h index b18b790c0..6371c7b03 100644 --- a/dom/canvas/WebGLObjectModel.h +++ b/dom/canvas/WebGLObjectModel.h @@ -6,8 +6,8 @@ #ifndef WEBGLOBJECTMODEL_H_ #define WEBGLOBJECTMODEL_H_ +#include "mozilla/WeakPtr.h" #include "nsCycleCollectionNoteChild.h" - #include "WebGLTypes.h" namespace mozilla { @@ -24,7 +24,7 @@ class WebGLContext; class WebGLContextBoundObject { public: - WebGLContext* const mContext; + const WeakPtr<WebGLContext> mContext; private: const uint32_t mContextGeneration; diff --git a/dom/canvas/WebGLRenderbuffer.cpp b/dom/canvas/WebGLRenderbuffer.cpp index ec076fdbb..32397dd1a 100644 --- a/dom/canvas/WebGLRenderbuffer.cpp +++ b/dom/canvas/WebGLRenderbuffer.cpp @@ -215,6 +215,16 @@ WebGLRenderbuffer::RenderbufferStorage(const char* funcName, uint32_t samples, if (error) { const char* errorName = mContext->ErrorName(error); mContext->GenerateWarning("%s generated error %s", funcName, errorName); + if (error == LOCAL_GL_OUT_OF_MEMORY) { + // Truncate. + mSamples = 0; + mFormat = nullptr; + mWidth = 0; + mHeight = 0; + mImageDataStatus = WebGLImageDataStatus::NoImageData; + + InvalidateStatusOfAttachedFBs(); + } return; } 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/WebGLTexture.cpp b/dom/canvas/WebGLTexture.cpp index 767ff610a..65bb71153 100644 --- a/dom/canvas/WebGLTexture.cpp +++ b/dom/canvas/WebGLTexture.cpp @@ -51,8 +51,6 @@ WebGLTexture::ImageInfo::Clear() WebGLTexture::ImageInfo& WebGLTexture::ImageInfo::operator =(const ImageInfo& a) { - MOZ_ASSERT(a.IsDefined()); - Mutable(mFormat) = a.mFormat; Mutable(mWidth) = a.mWidth; Mutable(mHeight) = a.mHeight; @@ -1216,6 +1214,12 @@ WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, const FloatOrInt& mContext->gl->fTexParameterf(texTarget.get(), pname, clamped.f); } +void WebGLTexture::Truncate() { + for (auto& cur : mImageInfoArr) { + SetImageInfo(&cur, ImageInfo()); + } +} + //////////////////////////////////////////////////////////////////////////////// NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture) diff --git a/dom/canvas/WebGLTexture.h b/dom/canvas/WebGLTexture.h index 66e781f23..8d3024590 100644 --- a/dom/canvas/WebGLTexture.h +++ b/dom/canvas/WebGLTexture.h @@ -386,6 +386,7 @@ public: bool* const out_initFailed); bool IsMipmapCubeComplete() const; + void Truncate(); bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); } diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp index 3839b5d5e..ae60d2a2b 100644 --- a/dom/canvas/WebGLTextureUpload.cpp +++ b/dom/canvas/WebGLTextureUpload.cpp @@ -1178,6 +1178,7 @@ WebGLTexture::TexStorage(const char* funcName, TexTarget target, GLsizei levels, if (error == LOCAL_GL_OUT_OF_MEMORY) { mContext->ErrorOutOfMemory("%s: Ran out of memory during texture allocation.", funcName); + Truncate(); return; } if (error) { @@ -1310,6 +1311,7 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level, if (glError == LOCAL_GL_OUT_OF_MEMORY) { mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.", funcName); + Truncate(); return; } @@ -1398,6 +1400,7 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev if (glError == LOCAL_GL_OUT_OF_MEMORY) { mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.", funcName); + Truncate(); return; } @@ -1514,6 +1517,7 @@ WebGLTexture::CompressedTexImage(const char* funcName, TexImageTarget target, GL blob->mAvailBytes, blob->mPtr); if (error == LOCAL_GL_OUT_OF_MEMORY) { mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.", funcName); + Truncate(); return; } if (error) { @@ -1664,6 +1668,7 @@ WebGLTexture::CompressedTexSubImage(const char* funcName, TexImageTarget target, blob->mAvailBytes, blob->mPtr); if (error == LOCAL_GL_OUT_OF_MEMORY) { mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.", funcName); + Truncate(); return; } if (error) { @@ -1992,7 +1997,7 @@ WebGLTexture::ValidateCopyTexImageForFeedback(const char* funcName, uint32_t lev static bool DoCopyTexOrSubImage(WebGLContext* webgl, const char* funcName, bool isSubImage, - const WebGLTexture* tex, TexImageTarget target, GLint level, + WebGLTexture* tex, TexImageTarget target, GLint level, GLint xWithinSrc, GLint yWithinSrc, uint32_t srcTotalWidth, uint32_t srcTotalHeight, const webgl::FormatUsageInfo* srcUsage, @@ -2069,6 +2074,7 @@ DoCopyTexOrSubImage(WebGLContext* webgl, const char* funcName, bool isSubImage, if (error == LOCAL_GL_OUT_OF_MEMORY) { webgl->ErrorOutOfMemory("%s: Ran out of memory during texture copy.", funcName); + tex->Truncate(); return false; } diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp index c4fcceb90..30dc9b6da 100644 --- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -2385,9 +2385,14 @@ BackgroundVersionChangeTransactionChild::RecvComplete(const nsresult& aResult) database->Close(); } + RefPtr<IDBOpenDBRequest> request = mOpenDBRequest; + MOZ_ASSERT(request); + mTransaction->FireCompleteOrAbortEvents(aResult); - mOpenDBRequest->SetTransaction(nullptr); + request->SetTransaction(nullptr); + request = nullptr; + mOpenDBRequest = nullptr; NoteComplete(); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 79446151c..97e3a4880 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -229,11 +229,6 @@ static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); -#if defined(XP_WIN) -// e10s forced enable pref, defined in nsAppRunner.cpp -extern const char* kForceEnableE10sPref; -#endif - using base::ChildPrivileges; using base::KillProcess; @@ -1275,12 +1270,6 @@ ContentParent::Init() if (nsIPresShell::IsAccessibilityActive()) { #if !defined(XP_WIN) Unused << SendActivateA11y(0); -#else - // On Windows we currently only enable a11y in the content process - // for testing purposes. - if (Preferences::GetBool(kForceEnableE10sPref, false)) { - Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID())); - } #endif } #endif @@ -2573,12 +2562,6 @@ ContentParent::Observe(nsISupports* aSubject, // accessibility gets initiated in chrome process. #if !defined(XP_WIN) Unused << SendActivateA11y(0); -#else - // On Windows we currently only enable a11y in the content process - // for testing purposes. - if (Preferences::GetBool(kForceEnableE10sPref, false)) { - Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID())); - } #endif } else { // If possible, shut down accessibility in content process when diff --git a/dom/media/MediaResource.cpp b/dom/media/MediaResource.cpp index d36783be7..84a8d67fd 100644 --- a/dom/media/MediaResource.cpp +++ b/dom/media/MediaResource.cpp @@ -1525,7 +1525,7 @@ void BaseMediaResource::SetLoadInBackground(bool aLoadInBackground) { NS_WARNING("Null owner in MediaResource::SetLoadInBackground()"); return; } - dom::HTMLMediaElement* element = owner->GetMediaElement(); + RefPtr<dom::HTMLMediaElement> element = owner->GetMediaElement(); if (!element) { NS_WARNING("Null element in MediaResource::SetLoadInBackground()"); return; diff --git a/dom/media/fmp4/MP4Decoder.cpp b/dom/media/fmp4/MP4Decoder.cpp index 6954e9757..b293c251b 100644 --- a/dom/media/fmp4/MP4Decoder.cpp +++ b/dom/media/fmp4/MP4Decoder.cpp @@ -172,7 +172,8 @@ bool MP4Decoder::IsH264(const nsACString& aMimeType) { return aMimeType.EqualsLiteral("video/mp4") || - aMimeType.EqualsLiteral("video/avc"); + aMimeType.EqualsLiteral("video/avc") || + aMimeType.EqualsLiteral("video/webm; codecs=avc1"); } /* static */ diff --git a/dom/media/mediasource/ContainerParser.cpp b/dom/media/mediasource/ContainerParser.cpp index 4ae37d7e9..b4dcfde8a 100644 --- a/dom/media/mediasource/ContainerParser.cpp +++ b/dom/media/mediasource/ContainerParser.cpp @@ -697,6 +697,9 @@ ContainerParser::CreateForMIMEType(const nsACString& aType) if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) { return new WebMContainerParser(aType); } + if (aType.LowerCaseEqualsLiteral("video/x-matroska") || aType.LowerCaseEqualsLiteral("audio/x-matroska")) { + return new WebMContainerParser(aType); + } #ifdef MOZ_FMP4 if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) { diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index 152c0085a..1c276cdc1 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -110,14 +110,16 @@ MediaSource::IsTypeSupported(const nsAString& aType, DecoderDoctorDiagnostics* a } return NS_OK; } - if (mimeType.EqualsASCII("video/webm")) { + if (mimeType.EqualsASCII("video/webm") || + mimeType.EqualsASCII("video/x-matroska")) { if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) || IsWebMForced(aDiagnostics))) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } return NS_OK; } - if (mimeType.EqualsASCII("audio/webm")) { + if (mimeType.EqualsASCII("audio/webm") || + mimeType.EqualsASCII("audio/x-matroska")) { if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) || Preferences::GetBool("media.mediasource.webm.audio.enabled", true))) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index ac6d82411..21fb158b5 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -814,6 +814,8 @@ TrackBuffersManager::CreateDemuxerforMIMEType() ShutdownDemuxers(); if (mType.LowerCaseEqualsLiteral("video/webm") || + mType.LowerCaseEqualsLiteral("video/x-matroska") || + mType.LowerCaseEqualsLiteral("audio/x-matroska") || mType.LowerCaseEqualsLiteral("audio/webm")) { mInputDemuxer = new WebMDemuxer(mCurrentInputBuffer, true /* IsMediaSource*/ ); return; diff --git a/dom/media/webm/WebMDecoder.cpp b/dom/media/webm/WebMDecoder.cpp index 9575d6e42..cbe9ffdb7 100644 --- a/dom/media/webm/WebMDecoder.cpp +++ b/dom/media/webm/WebMDecoder.cpp @@ -42,7 +42,10 @@ WebMDecoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, const bool isWebMAudio = aMIMETypeExcludingCodecs.EqualsASCII("audio/webm"); const bool isWebMVideo = aMIMETypeExcludingCodecs.EqualsASCII("video/webm"); - if (!isWebMAudio && !isWebMVideo) { + const bool isMatroskaAudio = aMIMETypeExcludingCodecs.EqualsASCII("audio/x-matroska") ; + const bool isMatroskaVideo = aMIMETypeExcludingCodecs.EqualsASCII("video/x-matroska") ; + + if (!isWebMAudio && !isWebMVideo && !isMatroskaAudio && !isMatroskaVideo) { return false; } @@ -63,7 +66,7 @@ WebMDecoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, } // Note: Only accept VP8/VP9 in a video content type, not in an audio // content type. - if (isWebMVideo && + if ((isWebMVideo || isMatroskaVideo) && (codec.EqualsLiteral("vp8") || codec.EqualsLiteral("vp8.0") || codec.EqualsLiteral("vp9") || codec.EqualsLiteral("vp9.0"))) { @@ -74,6 +77,15 @@ WebMDecoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, continue; } #endif + + if (IsH264CodecString(codec)) { + continue; + } + + if (IsAACCodecString(codec)) { + continue; + } + // Some unsupported codec. return false; } diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp index 013da9b2c..84b4b506e 100644 --- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -326,6 +326,20 @@ WebMDemuxer::ReadMetadata() case NESTEGG_CODEC_AV1: mInfo.mVideo.mMimeType = "video/webm; codecs=av1"; break; + case NESTEGG_CODEC_AVC1: { + mInfo.mVideo.mMimeType = "video/webm; codecs=avc1"; + + unsigned char* data = 0; + size_t length = 0; + r = nestegg_track_codec_data(context, track, 0, &data, &length); + if (r == -1) { + return NS_ERROR_FAILURE; + } + + mInfo.mVideo.mExtraData = new MediaByteBuffer(length); + mInfo.mVideo.mExtraData->AppendElements(data, length); + break; + } default: NS_WARNING("Unknown WebM video codec"); return NS_ERROR_FAILURE; @@ -408,6 +422,8 @@ WebMDemuxer::ReadMetadata() mInfo.mAudio.mMimeType = "audio/opus"; OpusDataDecoder::AppendCodecDelay(mInfo.mAudio.mCodecSpecificConfig, media::TimeUnit::FromNanoseconds(params.codec_delay).ToMicroseconds()); + } else if (mAudioCodec == NESTEGG_CODEC_AAC) { + mInfo.mAudio.mMimeType = "audio/mp4a-latm"; } mSeekPreroll = params.seek_preroll; mInfo.mAudio.mRate = params.rate; @@ -662,6 +678,9 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl isKeyframe = AOMDecoder::IsKeyframe(sample); break; #endif + case NESTEGG_CODEC_AVC1: + isKeyframe = nestegg_packet_has_keyframe(holder->Packet()); + break; default: NS_WARNING("Cannot detect keyframes in unknown WebM video codec"); return NS_ERROR_FAILURE; @@ -682,7 +701,7 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl dimensions = AOMDecoder::GetFrameSize(sample); break; #endif - } + } if (mLastSeenFrameSize.isSome() && (dimensions != mLastSeenFrameSize.value())) { mInfo.mVideo.mDisplay = dimensions; @@ -749,6 +768,11 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl if (aType == TrackInfo::kVideoTrack) { sample->mTrackInfo = mSharedVideoTrackInfo; } + + if (mVideoCodec == NESTEGG_CODEC_AVC1) { + sample->mExtraData = mInfo.mVideo.mExtraData; + } + aSamples->Push(sample); } return NS_OK; diff --git a/dom/network/UDPSocketChild.cpp b/dom/network/UDPSocketChild.cpp index 6e374ce31..d205e7e8a 100644 --- a/dom/network/UDPSocketChild.cpp +++ b/dom/network/UDPSocketChild.cpp @@ -172,19 +172,28 @@ UDPSocketChild::Bind(nsIUDPSocketInternal* aSocket, NS_ENSURE_ARG(aSocket); - mSocket = aSocket; - AddIPDLReference(); + if (NS_IsMainThread()) { + if (!gNeckoChild->SendPUDPSocketConstructor( + this, IPC::Principal(aPrincipal), mFilterName)) { + return NS_ERROR_FAILURE; + } + } else { + if (!mBackgroundManager) { + return NS_ERROR_NOT_AVAILABLE; + } - if (mBackgroundManager) { // If we want to support a passed-in principal here we'd need to // convert it to a PrincipalInfo MOZ_ASSERT(!aPrincipal); - mBackgroundManager->SendPUDPSocketConstructor(this, void_t(), mFilterName); - } else { - gNeckoChild->SendPUDPSocketConstructor(this, IPC::Principal(aPrincipal), - mFilterName); + if (!mBackgroundManager->SendPUDPSocketConstructor( + this, void_t(), mFilterName)) { + return NS_ERROR_FAILURE; + } } + mSocket = aSocket; + AddIPDLReference(); + SendBind(UDPAddressInfo(nsCString(aHost), aPort), aAddressReuse, aLoopback, recvBufferSize, sendBufferSize); return NS_OK; diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.cpp b/dom/plugins/base/nsPluginStreamListenerPeer.cpp index 665e11ec1..0476315d5 100644 --- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp +++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp @@ -1381,7 +1381,7 @@ nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsICh return NS_ERROR_FAILURE; } - // Don't allow cross-origin 307 POST redirects. + // Don't allow cross-origin 307/308 POST redirects. nsCOMPtr<nsIHttpChannel> oldHttpChannel(do_QueryInterface(oldChannel)); if (oldHttpChannel) { uint32_t responseStatus; @@ -1389,7 +1389,7 @@ nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsICh if (NS_FAILED(rv)) { return rv; } - if (responseStatus == 307) { + if (responseStatus == 307 || responseStatus == 308) { nsAutoCString method; rv = oldHttpChannel->GetRequestMethod(method); if (NS_FAILED(rv)) { diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp index f2cbc8fcf..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" @@ -95,9 +96,14 @@ nsContentSecurityManager::AllowTopLevelNavigationToDataURI(nsIChannel* aChannel) /* static */ nsresult nsContentSecurityManager::CheckFTPSubresourceLoad(nsIChannel* aChannel) { - // We dissallow using FTP resources as a subresource everywhere. + // We dissallow using FTP resources as a subresource almost everywhere. // The only valid way to use FTP resources is loading it as // a top level document. + + // Override blocking if the pref is set to allow. + if (!mozilla::net::nsIOService::BlockFTPSubresources()) { + return NS_OK; + } nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); if (!loadInfo) { @@ -105,22 +111,40 @@ nsContentSecurityManager::CheckFTPSubresourceLoad(nsIChannel* aChannel) } nsContentPolicyType type = loadInfo->GetExternalContentPolicyType(); + + // Allow save-as download of FTP files on HTTP pages. + if (type == nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD) { + return NS_OK; + } + + // Allow direct document requests if (type == nsIContentPolicy::TYPE_DOCUMENT) { return NS_OK; } 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; } + // Allow if it's not the FTP protocol bool isFtpURI = (NS_SUCCEEDED(uri->SchemeIs("ftp", &isFtpURI)) && isFtpURI); if (!isFtpURI) { return NS_OK; } + // Allow loading FTP subresources in top-level FTP documents. + nsIPrincipal* triggeringPrincipal = loadInfo->TriggeringPrincipal(); + nsCOMPtr<nsIURI> tURI; + triggeringPrincipal->GetURI(getter_AddRefs(tURI)); + bool isTrigFtpURI = (NS_SUCCEEDED(tURI->SchemeIs("ftp", &isTrigFtpURI)) && isTrigFtpURI); + if (isTrigFtpURI) { + return NS_OK; + } + + // If we get here, the request is blocked and should be reported. nsCOMPtr<nsIDocument> doc; if (nsINode* node = loadInfo->LoadingNode()) { doc = node->OwnerDoc(); @@ -778,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/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 21f6d8ddf..19c4ded2a 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -988,7 +988,7 @@ Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj) } if (existing) { - js::Wrapper::Renew(cx, existing, obj, wrapper); + js::Wrapper::Renew(existing, obj, wrapper); } return js::Wrapper::New(cx, obj, wrapper); } diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 6bc5c4f96..27eb570e9 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -87,6 +87,7 @@ #include "nsProxyRelease.h" #include "nsQueryObject.h" #include "nsSandboxFlags.h" +#include "nsScriptError.h" #include "nsUTF8Utils.h" #include "prthread.h" #include "xpcpublic.h" @@ -281,27 +282,34 @@ struct WindowAction }; void -LogErrorToConsole(const nsAString& aMessage, - const nsAString& aFilename, - const nsAString& aLine, - uint32_t aLineNumber, - uint32_t aColumnNumber, - uint32_t aFlags, - uint64_t aInnerWindowId) +LogErrorToConsole(const WorkerErrorReport& aReport, uint64_t aInnerWindowId) { AssertIsOnMainThread(); - nsCOMPtr<nsIScriptError> scriptError = - do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); + RefPtr<nsScriptErrorBase> scriptError = new nsScriptError(); NS_WARNING_ASSERTION(scriptError, "Failed to create script error!"); if (scriptError) { - if (NS_FAILED(scriptError->InitWithWindowID(aMessage, aFilename, aLine, - aLineNumber, aColumnNumber, - aFlags, "Web Worker", + nsAutoCString category("Web Worker"); + if (NS_FAILED(scriptError->InitWithWindowID(aReport.mMessage, + aReport.mFilename, + aReport.mLine, + aReport.mLineNumber, + aReport.mColumnNumber, + aReport.mFlags, + category, aInnerWindowId))) { NS_WARNING("Failed to init script error!"); scriptError = nullptr; + + for (size_t i = 0, len = aReport.mNotes.Length(); i < len; i++) { + const WorkerErrorNote& note = aReport.mNotes.ElementAt(i); + + nsScriptErrorNote* noteObject = new nsScriptErrorNote(); + noteObject->Init(note.mMessage, note.mFilename, + note.mLineNumber, note.mColumnNumber); + scriptError->AddNote(noteObject); + } } } @@ -316,23 +324,23 @@ LogErrorToConsole(const nsAString& aMessage, } NS_WARNING("LogMessage failed!"); } else if (NS_SUCCEEDED(consoleService->LogStringMessage( - aMessage.BeginReading()))) { + aReport.mMessage.BeginReading()))) { return; } NS_WARNING("LogStringMessage failed!"); } - NS_ConvertUTF16toUTF8 msg(aMessage); - NS_ConvertUTF16toUTF8 filename(aFilename); + NS_ConvertUTF16toUTF8 msg(aReport.mMessage); + NS_ConvertUTF16toUTF8 filename(aReport.mFilename); static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]"; #ifdef ANDROID __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(), - filename.get(), aLineNumber); + filename.get(), aReport.mLineNumber); #endif - fprintf(stderr, kErrorString, msg.get(), filename.get(), aLineNumber); + fprintf(stderr, kErrorString, msg.get(), filename.get(), aReport.mLineNumber); fflush(stderr); } @@ -536,10 +544,7 @@ private: } if (aWorkerPrivate->IsSharedWorker()) { - aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, EmptyString(), - EmptyString(), - EmptyString(), 0, 0, - JSREPORT_ERROR, + aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, nullptr, /* isErrorEvent */ false); return true; } @@ -1060,15 +1065,7 @@ private: class ReportErrorRunnable final : public WorkerRunnable { - nsString mMessage; - nsString mFilename; - nsString mLine; - uint32_t mLineNumber; - uint32_t mColumnNumber; - uint32_t mFlags; - uint32_t mErrorNumber; - JSExnType mExnType; - bool mMutedError; + WorkerErrorReport mReport; public: // aWorkerPrivate is the worker thread we're on (or the main thread, if null) @@ -1077,11 +1074,7 @@ public: static void ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aFireAtScope, WorkerPrivate* aTarget, - const nsString& aMessage, const nsString& aFilename, - const nsString& aLine, uint32_t aLineNumber, - uint32_t aColumnNumber, uint32_t aFlags, - uint32_t aErrorNumber, JSExnType aExnType, - bool aMutedError, uint64_t aInnerWindowId, + const WorkerErrorReport& aReport, uint64_t aInnerWindowId, JS::Handle<JS::Value> aException = JS::NullHandleValue) { if (aWorkerPrivate) { @@ -1092,16 +1085,16 @@ public: // We should not fire error events for warnings but instead make sure that // they show up in the error console. - if (!JSREPORT_IS_WARNING(aFlags)) { + if (!JSREPORT_IS_WARNING(aReport.mFlags)) { // First fire an ErrorEvent at the worker. RootedDictionary<ErrorEventInit> init(aCx); - if (aMutedError) { + if (aReport.mMutedError) { init.mMessage.AssignLiteral("Script error."); } else { - init.mMessage = aMessage; - init.mFilename = aFilename; - init.mLineno = aLineNumber; + init.mMessage = aReport.mMessage; + init.mFilename = aReport.mFilename; + init.mLineno = aReport.mLineNumber; init.mError = aException; } @@ -1128,7 +1121,8 @@ public: // into an error event on our parent worker! // https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks making this // better. - if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) { + if (aFireAtScope && + (aTarget || aReport.mErrorNumber != JSMSG_OVER_RECURSED)) { JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx)); NS_ASSERTION(global, "This should never be null!"); @@ -1145,8 +1139,8 @@ public: MOZ_ASSERT_IF(globalScope, globalScope->GetWrapperPreserveColor() == global); if (globalScope || IsDebuggerSandbox(global)) { - aWorkerPrivate->ReportErrorToDebugger(aFilename, aLineNumber, - aMessage); + aWorkerPrivate->ReportErrorToDebugger(aReport.mFilename, aReport.mLineNumber, + aReport.mMessage); return; } @@ -1193,28 +1187,20 @@ public: // Now fire a runnable to do the same on the parent's thread if we can. if (aWorkerPrivate) { RefPtr<ReportErrorRunnable> runnable = - new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine, - aLineNumber, aColumnNumber, aFlags, - aErrorNumber, aExnType, aMutedError); + new ReportErrorRunnable(aWorkerPrivate, aReport); runnable->Dispatch(); return; } // Otherwise log an error to the error console. - LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber, - aFlags, aInnerWindowId); + LogErrorToConsole(aReport, aInnerWindowId); } private: - ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, const nsString& aMessage, - const nsString& aFilename, const nsString& aLine, - uint32_t aLineNumber, uint32_t aColumnNumber, - uint32_t aFlags, uint32_t aErrorNumber, - JSExnType aExnType, bool aMutedError) + ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, + const WorkerErrorReport& aReport) : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount), - mMessage(aMessage), mFilename(aFilename), mLine(aLine), - mLineNumber(aLineNumber), mColumnNumber(aColumnNumber), mFlags(aFlags), - mErrorNumber(aErrorNumber), mExnType(aExnType), mMutedError(aMutedError) + mReport(aReport) { } virtual void @@ -1251,9 +1237,7 @@ private: } if (aWorkerPrivate->IsSharedWorker()) { - aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename, - mLine, mLineNumber, - mColumnNumber, mFlags, + aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, &mReport, /* isErrorEvent */ true); return true; } @@ -1267,9 +1251,10 @@ private: swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(), aWorkerPrivate->WorkerName(), aWorkerPrivate->ScriptURL(), - mMessage, - mFilename, mLine, mLineNumber, - mColumnNumber, mFlags, mExnType); + mReport.mMessage, + mReport.mFilename, mReport.mLine, mReport.mLineNumber, + mReport.mColumnNumber, mReport.mFlags, + mReport.mExnType); } return true; } @@ -1289,9 +1274,8 @@ private: return true; } - ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mMessage, - mFilename, mLine, mLineNumber, mColumnNumber, mFlags, - mErrorNumber, mExnType, mMutedError, innerWindowId); + ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mReport, + innerWindowId); return true; } }; @@ -3314,21 +3298,16 @@ template <class Derived> void WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers( JSContext* aCx, - const nsAString& aMessage, - const nsAString& aFilename, - const nsAString& aLine, - uint32_t aLineNumber, - uint32_t aColumnNumber, - uint32_t aFlags, + const WorkerErrorReport* aReport, bool aIsErrorEvent) { AssertIsOnMainThread(); - if (JSREPORT_IS_WARNING(aFlags)) { + if (aIsErrorEvent && JSREPORT_IS_WARNING(aReport->mFlags)) { // Don't fire any events anywhere. Just log to console. // XXXbz should we log to all the consoles of all the relevant windows? - LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber, - aFlags, 0); + MOZ_ASSERT(aReport); + LogErrorToConsole(*aReport, 0); return; } @@ -3357,10 +3336,10 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers( RootedDictionary<ErrorEventInit> errorInit(aCx); errorInit.mBubbles = false; errorInit.mCancelable = true; - errorInit.mMessage = aMessage; - errorInit.mFilename = aFilename; - errorInit.mLineno = aLineNumber; - errorInit.mColno = aColumnNumber; + errorInit.mMessage = aReport->mMessage; + errorInit.mFilename = aReport->mFilename; + errorInit.mLineno = aReport->mLineNumber; + errorInit.mColno = aReport->mColumnNumber; event = ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"), errorInit); @@ -3426,9 +3405,9 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers( MOZ_ASSERT(NS_IsMainThread()); RootedDictionary<ErrorEventInit> init(aCx); - init.mLineno = aLineNumber; - init.mFilename = aFilename; - init.mMessage = aMessage; + init.mLineno = aReport->mLineNumber; + init.mFilename = aReport->mFilename; + init.mMessage = aReport->mMessage; init.mCancelable = true; init.mBubbles = true; @@ -3446,8 +3425,8 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers( // Finally log a warning in the console if no window tried to prevent it. if (shouldLogErrorToConsole) { - LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber, - aFlags, 0); + MOZ_ASSERT(aReport); + LogErrorToConsole(*aReport, 0); } } @@ -4120,7 +4099,10 @@ WorkerDebugger::ReportErrorToDebuggerOnMainThread(const nsAString& aFilename, listeners[index]->OnError(aFilename, aLineno, aMessage); } - LogErrorToConsole(aMessage, aFilename, nsString(), aLineno, 0, 0, 0); + WorkerErrorReport report; + report.mMessage = aMessage; + report.mFilename = aFilename; + LogErrorToConsole(report, 0); } WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent, @@ -5926,6 +5908,47 @@ WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus) } void +WorkerErrorBase::AssignErrorBase(JSErrorBase* aReport) +{ + mFilename = NS_ConvertUTF8toUTF16(aReport->filename); + mLineNumber = aReport->lineno; + mColumnNumber = aReport->column; + mErrorNumber = aReport->errorNumber; +} + +void +WorkerErrorNote::AssignErrorNote(JSErrorNotes::Note* aNote) +{ + WorkerErrorBase::AssignErrorBase(aNote); + xpc::ErrorNote::ErrorNoteToMessageString(aNote, mMessage); +} + +void +WorkerErrorReport::AssignErrorReport(JSErrorReport* aReport) +{ + WorkerErrorBase::AssignErrorBase(aReport); + xpc::ErrorReport::ErrorReportToMessageString(aReport, mMessage); + + mLine.Assign(aReport->linebuf(), aReport->linebufLength()); + mFlags = aReport->flags; + MOZ_ASSERT(aReport->exnType >= JSEXN_FIRST && aReport->exnType < JSEXN_LIMIT); + mExnType = JSExnType(aReport->exnType); + mMutedError = aReport->isMuted; + + if (aReport->notes) { + if (!mNotes.SetLength(aReport->notes->length(), fallible)) { + return; + } + + size_t i = 0; + for (auto&& note : *aReport->notes) { + mNotes.ElementAt(i).AssignErrorNote(note.get()); + i++; + } + } +} + +void WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult, JSErrorReport* aReport) { @@ -5947,32 +5970,17 @@ WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult, } JS_ClearPendingException(aCx); - nsString message, filename, line; - uint32_t lineNumber, columnNumber, flags, errorNumber; - JSExnType exnType = JSEXN_ERR; - bool mutedError = aReport && aReport->isMuted; - + WorkerErrorReport report; if (aReport) { - // We want the same behavior here as xpc::ErrorReport::init here. - xpc::ErrorReport::ErrorReportToMessageString(aReport, message); - - filename = NS_ConvertUTF8toUTF16(aReport->filename); - line.Assign(aReport->linebuf(), aReport->linebufLength()); - lineNumber = aReport->lineno; - columnNumber = aReport->tokenOffset(); - flags = aReport->flags; - errorNumber = aReport->errorNumber; - MOZ_ASSERT(aReport->exnType >= JSEXN_FIRST && aReport->exnType < JSEXN_LIMIT); - exnType = JSExnType(aReport->exnType); + report.AssignErrorReport(aReport); } else { - lineNumber = columnNumber = errorNumber = 0; - flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag; + report.mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag; } - if (message.IsEmpty() && aToStringResult) { + if (report.mMessage.IsEmpty() && aToStringResult) { nsDependentCString toStringResult(aToStringResult.c_str()); - if (!AppendUTF8toUTF16(toStringResult, message, mozilla::fallible)) { + if (!AppendUTF8toUTF16(toStringResult, report.mMessage, mozilla::fallible)) { // Try again, with only a 1 KB string. Do this infallibly this time. // If the user doesn't have 1 KB to spare we're done anyways. uint32_t index = std::min(uint32_t(1024), toStringResult.Length()); @@ -5982,7 +5990,7 @@ WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult, nsDependentCString truncatedToStringResult(aToStringResult.c_str(), index); - AppendUTF8toUTF16(truncatedToStringResult, message); + AppendUTF8toUTF16(truncatedToStringResult, report.mMessage); } } @@ -5991,13 +5999,11 @@ WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult, // Don't want to run the scope's error handler if this is a recursive error or // if we ran out of memory. bool fireAtScope = mErrorHandlerRecursionCount == 1 && - errorNumber != JSMSG_OUT_OF_MEMORY && + report.mErrorNumber != JSMSG_OUT_OF_MEMORY && JS::CurrentGlobalOrNull(aCx); - ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message, - filename, line, lineNumber, - columnNumber, flags, errorNumber, exnType, - mutedError, 0, exn); + ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, report, 0, + exn); mErrorHandlerRecursionCount--; } diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 20a530205..885abccd5 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -140,6 +140,45 @@ public: } }; +class WorkerErrorBase { +public: + nsString mMessage; + nsString mFilename; + uint32_t mLineNumber; + uint32_t mColumnNumber; + uint32_t mErrorNumber; + + WorkerErrorBase() + : mLineNumber(0), + mColumnNumber(0), + mErrorNumber(0) + { } + + void AssignErrorBase(JSErrorBase* aReport); +}; + +class WorkerErrorNote : public WorkerErrorBase { +public: + void AssignErrorNote(JSErrorNotes::Note* aNote); +}; + +class WorkerErrorReport : public WorkerErrorBase { +public: + nsString mLine; + uint32_t mFlags; + JSExnType mExnType; + bool mMutedError; + nsTArray<WorkerErrorNote> mNotes; + + WorkerErrorReport() + : mFlags(0), + mExnType(JSEXN_ERR), + mMutedError(false) + { } + + void AssignErrorReport(JSErrorReport* aReport); +}; + template <class Derived> class WorkerPrivateParent : public DOMEventTargetHelper { @@ -401,12 +440,7 @@ public: void BroadcastErrorToSharedWorkers(JSContext* aCx, - const nsAString& aMessage, - const nsAString& aFilename, - const nsAString& aLine, - uint32_t aLineNumber, - uint32_t aColumnNumber, - uint32_t aFlags, + const WorkerErrorReport* aReport, bool aIsErrorEvent); void diff --git a/dom/workers/moz.build b/dom/workers/moz.build index 4f4b52e4a..9fea84193 100644 --- a/dom/workers/moz.build +++ b/dom/workers/moz.build @@ -94,6 +94,7 @@ LOCAL_INCLUDES += [ '../base', '../system', '/dom/base', + '/dom/bindings', '/xpcom/build', '/xpcom/threads', ] diff --git a/dom/workers/test/test_sharedWorker.html b/dom/workers/test/test_sharedWorker.html index 3d3d4e2c6..c00c4e586 100644 --- a/dom/workers/test/test_sharedWorker.html +++ b/dom/workers/test/test_sharedWorker.html @@ -23,7 +23,7 @@ const errorFilename = href.substring(0, href.lastIndexOf("/") + 1) + filename; const errorLine = 91; - const errorColumn = 0; + const errorColumn = 11; var worker = new SharedWorker(filename); |