From 978d4a7dac33493595e8a9d1ef23f9ffcd7fd51f Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Sun, 18 Aug 2019 15:42:59 +0800 Subject: [PATCH] ported from UXP: - Allow matroska and avc (h.264) content in matroska/webm containers for video element and MSE. (18f9b185) - Alow AAC audio codec data in matroska/webm streams. Allow CRC32 elements in matroska cluster elements. (6b6aa59f) - Truncate Buffer/Texture on GL_OOM. (3f9e299b) - Bug 1550498 (b0a8ed21) - Don't allow cross-origin POST redirects on 308 codes. (8b7daa53) - Bug 1548822 (b7807cd0) - Do not allow the ^ character to appear in the hostname. (f046b917) - Add CheckedInt check for GL texture uploads. (227b2360) - Implement a threadsafe & revised version of http2PushedStream. (17a4b4f6d) - Convert dom/base/nsImageLoadingContent.cpp to use AsyncOpen2 and followups along with it (1445670 and 1373780 part 2 and 3) (deae241f) and ported part of tenfourfox changes: - #559: M1550498 M1548822 M1540759(partial) M1528481(+WeakPtr for Http2Stream) M1555523 M1552541 (46496870) - #559: M1547266 (97b8db0b) --- dom/base/nsContentPolicy.cpp | 21 ++++ dom/base/nsContentUtils.cpp | 9 +- dom/base/nsDocument.cpp | 22 +++-- dom/base/nsDocument.h | 6 +- dom/base/nsGlobalWindow.cpp | 10 +- dom/base/nsIDocument.h | 10 +- dom/base/nsIImageLoadingContent.idl | 9 ++ dom/base/nsImageLoadingContent.cpp | 99 ++++++++++--------- dom/base/nsImageLoadingContent.h | 17 ++-- dom/canvas/WebGLBuffer.cpp | 1 + dom/canvas/WebGLRenderbuffer.cpp | 10 ++ dom/canvas/WebGLTexture.cpp | 6 ++ dom/canvas/WebGLTexture.h | 1 + dom/canvas/WebGLTextureUpload.cpp | 8 +- dom/media/fmp4/MP4Decoder.cpp | 3 +- dom/media/mediasource/ContainerParser.cpp | 4 + dom/media/mediasource/MediaSource.cpp | 3 +- dom/media/mediasource/TrackBuffersManager.cpp | 1 + dom/media/webm/WebMDecoder.cpp | 9 ++ dom/media/webm/WebMDemuxer.cpp | 26 ++++- .../base/nsPluginStreamListenerPeer.cpp | 4 +- dom/security/nsContentSecurityManager.cpp | 3 + gfx/gl/GLUploadHelpers.cpp | 40 +++++++- image/imgLoader.cpp | 1 + media/libnestegg/include/nestegg.h | 2 + media/libnestegg/src/nestegg.c | 28 +++++- netwerk/base/nsStandardURL.cpp | 2 +- netwerk/protocol/http/Http2Push.cpp | 82 ++++++++++++++- netwerk/protocol/http/Http2Push.h | 18 ++++ netwerk/protocol/http/Http2Session.cpp | 71 ++++++++----- netwerk/protocol/http/Http2Stream.cpp | 4 +- netwerk/protocol/http/Http2Stream.h | 3 + netwerk/protocol/http/HttpChannelChild.cpp | 30 +++--- netwerk/protocol/http/nsCORSListenerProxy.cpp | 3 + netwerk/protocol/http/nsHttpChannel.cpp | 8 +- netwerk/protocol/http/nsHttpChannel.h | 7 +- netwerk/protocol/http/nsHttpConnectionMgr.cpp | 32 ++++-- netwerk/protocol/http/nsHttpHandler.cpp | 2 +- netwerk/protocol/http/nsHttpHandler.h | 3 +- netwerk/protocol/http/nsHttpTransaction.h | 18 ++-- parser/html/nsHtml5TreeOpExecutor.cpp | 5 +- 41 files changed, 477 insertions(+), 164 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 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 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 1d7d54922..984163032 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -8516,12 +8516,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 22a4f2aee..c6e5d0181 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -9363,19 +9363,23 @@ already_AddRefed 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 element and found a URI from a source previous to // this image, use it. sourceURL = mPreloadPictureFoundSource; + isImgSet = true; } else { // Otherwise try to use this as a source HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr, aSrcsetAttr, aSizesAttr, NullString(), NullString(), sourceURL); + isImgSet = !aSrcsetAttr.IsEmpty(); } // Empty sources are not loaded by (i.e. not resolved to the baseURI) @@ -9393,6 +9397,8 @@ nsDocument::ResolvePreloadImage(nsIURI *aBaseURI, return nullptr; } + *aIsImgSet = isImgSet; + // We don't clear mPreloadPictureFoundSource because subsequent tags in // this this share the same (though this is not valid per // spec) @@ -9401,16 +9407,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(this)) || - !nsContentUtils::CanLoadImage(uri, static_cast(this), - this, NodePrincipal(), &blockingStatus, - nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD)) { + if (nsContentUtils::IsImageInCache(uri, static_cast(this))) { return; } @@ -9429,6 +9431,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 request; nsresult rv = @@ -9442,7 +9448,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 d3c35df7f..94bdd1071 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -963,11 +963,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 473a39567..0c3b4dcda 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2407,11 +2407,13 @@ nsGlobalWindow::WouldReuseInnerWindow(nsIDocument* aNewDocument) } bool equal; - if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(), - &equal)) && - equal) { + if (NS_SUCCEEDED( + BasePrincipal::Cast(mDoc->NodePrincipal())-> + EqualsConsideringDomain(aNewDocument->NodePrincipal(), + &equal))) { + // Return the result. If true (bug 1552541): // The origin is the same. - return true; + return equal; } return false; diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index a4844ba33..0c5ad9bd8 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2309,21 +2309,27 @@ public: * nesting and possible sources, which are used to inform URL selection * responsive or images. Unset attributes are expected * to be marked void. + * If this image is for or , aIsImgSet will be set to + * true, false otherwise. */ virtual already_AddRefed 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 or 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 @@ -103,6 +103,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. */ diff --git a/dom/base/nsImageLoadingContent.cpp b/dom/base/nsImageLoadingContent.cpp index d25dd6319..edbdac763 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" @@ -95,6 +96,7 @@ nsImageLoadingContent::nsImageLoadingContent() mStateChangerDepth(0), mCurrentRequestRegistered(false), mPendingRequestRegistered(false), + mIsStartingImageLoad(false), mFrameCreateCalled(false) { if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) { @@ -790,6 +792,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. @@ -819,6 +826,21 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, } } + AutoRestore 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 @@ -849,23 +871,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(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) { @@ -883,7 +890,6 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, referrerPolicy = imgReferrerPolicy; } - // Not blocked. Do the load. RefPtr& req = PrepareNextRequest(aImageLoadType); nsCOMPtr content = do_QueryInterface(static_cast(this)); @@ -937,7 +943,6 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, FireEvent(NS_LITERAL_STRING("error")); FireEvent(NS_LITERAL_STRING("loadend")); - return NS_OK; } return NS_OK; @@ -1217,46 +1222,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& @@ -1267,7 +1268,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) { @@ -1286,7 +1287,7 @@ RefPtr& 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 85db2bd2c..8a5bb2294 100644 --- a/dom/base/nsImageLoadingContent.h +++ b/dom/base/nsImageLoadingContent.h @@ -302,18 +302,11 @@ protected: */ RefPtr& 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 */ @@ -455,6 +448,14 @@ private: 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; + // True when FrameCreate has been called but FrameDestroy has not. bool mFrameCreateCalled; }; diff --git a/dom/canvas/WebGLBuffer.cpp b/dom/canvas/WebGLBuffer.cpp index f202c9950..30d49f330 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/WebGLRenderbuffer.cpp b/dom/canvas/WebGLRenderbuffer.cpp index 2dcbcbb54..2c00c79f7 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(funcName); + } return; } diff --git a/dom/canvas/WebGLTexture.cpp b/dom/canvas/WebGLTexture.cpp index 02684bd3a..233ff1d26 100644 --- a/dom/canvas/WebGLTexture.cpp +++ b/dom/canvas/WebGLTexture.cpp @@ -1222,6 +1222,12 @@ WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, const FloatOrInt& mContext->gl->fTexParameterf(texTarget.get(), pname, clamped.f); } +void WebGLTexture::Truncate(const char* funcName) { + for (auto& cur : mImageInfoArr) { + SetImageInfo(funcName, &cur, ImageInfo()); + } +} + //////////////////////////////////////////////////////////////////////////////// NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture) diff --git a/dom/canvas/WebGLTexture.h b/dom/canvas/WebGLTexture.h index 62a6c61cc..af9015118 100644 --- a/dom/canvas/WebGLTexture.h +++ b/dom/canvas/WebGLTexture.h @@ -387,6 +387,7 @@ public: bool* const out_initFailed); bool IsMipmapCubeComplete() const; + void Truncate(const char* funcName); bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); } diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp index 274e3ffcf..ffcd9d691 100644 --- a/dom/canvas/WebGLTextureUpload.cpp +++ b/dom/canvas/WebGLTextureUpload.cpp @@ -1170,6 +1170,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(funcName); return; } if (error) { @@ -1302,6 +1303,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(funcName); return; } @@ -1390,6 +1392,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(funcName); return; } @@ -1506,6 +1509,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(funcName); return; } if (error) { @@ -1656,6 +1660,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(funcName); return; } if (error) { @@ -1998,7 +2003,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, @@ -2075,6 +2080,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(funcName); return false; } diff --git a/dom/media/fmp4/MP4Decoder.cpp b/dom/media/fmp4/MP4Decoder.cpp index 232554026..5bb26c098 100644 --- a/dom/media/fmp4/MP4Decoder.cpp +++ b/dom/media/fmp4/MP4Decoder.cpp @@ -186,7 +186,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 d26e5654b..8877ce970 100644 --- a/dom/media/mediasource/ContainerParser.cpp +++ b/dom/media/mediasource/ContainerParser.cpp @@ -701,6 +701,10 @@ ContainerParser::CreateForMIMEType(const MediaContainerType& aType) return new WebMContainerParser(aType); } + if (aType.Type() == MEDIAMIMETYPE("video/x-matroska")) { + return new WebMContainerParser(aType); + } + #ifdef MOZ_FMP4 if (aType.Type() == MEDIAMIMETYPE("video/mp4") || aType.Type() == MEDIAMIMETYPE("audio/mp4")) { diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index 36f928a49..e765e412c 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -113,7 +113,8 @@ MediaSource::IsTypeSupported(const nsAString& aType, DecoderDoctorDiagnostics* a } return NS_OK; } - if (mimeType == MEDIAMIMETYPE("video/webm")) { + if (mimeType == MEDIAMIMETYPE("video/webm") || + mimeType == MEDIAMIMETYPE("video/x-matroska")) { if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) || IsWebMForced(aDiagnostics))) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index aa3712a07..b4466ed01 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -814,6 +814,7 @@ TrackBuffersManager::CreateDemuxerforMIMEType() ShutdownDemuxers(); if (mType.Type() == MEDIAMIMETYPE("video/webm") || + mType.Type() == MEDIAMIMETYPE("video/x-matroska") || mType.Type() == MEDIAMIMETYPE("audio/webm")) { mInputDemuxer = new WebMDemuxer(mCurrentInputBuffer, true /* IsMediaSource*/ ); return; diff --git a/dom/media/webm/WebMDecoder.cpp b/dom/media/webm/WebMDecoder.cpp index 5cb755582..c2850555e 100644 --- a/dom/media/webm/WebMDecoder.cpp +++ b/dom/media/webm/WebMDecoder.cpp @@ -60,6 +60,15 @@ WebMDecoder::IsSupportedType(const MediaContainerType& aContainerType) 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 c6fb65a4f..efdd1f8bc 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; @@ -409,6 +423,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; @@ -678,6 +694,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; @@ -698,7 +717,7 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl dimensions = AOMDecoder::GetFrameSize(sample); break; #endif - } + } if (mLastSeenFrameSize.isSome() && (dimensions != mLastSeenFrameSize.value())) { mInfo.mVideo.mDisplay = dimensions; @@ -774,6 +793,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/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 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 f51428448..63f675c92 100644 --- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -12,6 +12,7 @@ #include "nsMixedContentBlocker.h" #include "nsCDefaultURIFixup.h" #include "nsIURIFixup.h" +#include "nsIImageLoadingContent.h" #include "nsINestedURI.h" #include "mozilla/dom/Element.h" @@ -732,6 +733,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/gfx/gl/GLUploadHelpers.cpp b/gfx/gl/GLUploadHelpers.cpp index ca1c890a4..27b8a247c 100644 --- a/gfx/gl/GLUploadHelpers.cpp +++ b/gfx/gl/GLUploadHelpers.cpp @@ -178,7 +178,22 @@ TexSubImage2DWithoutUnpackSubimage(GLContext* gl, // isn't supported. We make a copy of the texture data we're using, // such that we're using the whole row of data in the copy. This turns // out to be more efficient than uploading row-by-row; see bug 698197. - unsigned char* newPixels = new (fallible) unsigned char[width*height*pixelsize]; + + // Width and height are never more than 16384. At 16Ki*16Ki, 4Bpp is 1GiB, but + // if we allow 8Bpp (16-bit channels, or higher) here, that's 2GiB+, which would + // overflow on 32-bit. + MOZ_ASSERT(width <= 16384); + MOZ_ASSERT(height <= 16384); + MOZ_ASSERT(pixelsize < 8); + + const auto size = CheckedInt(width) * height * pixelsize; + if (!size.isValid()) { + // This should never happen, but we use a defensive check. + MOZ_ASSERT_UNREACHABLE("Unacceptable size calculated.!"); + return; + } + + unsigned char* newPixels = new (fallible) unsigned char[size.value()]; if (newPixels) { unsigned char* rowDest = newPixels; @@ -303,7 +318,22 @@ TexImage2DHelper(GLContext* gl, GLsizei paddedWidth = RoundUpPow2((uint32_t)width); GLsizei paddedHeight = RoundUpPow2((uint32_t)height); - GLvoid* paddedPixels = new unsigned char[paddedWidth * paddedHeight * pixelsize]; + // Width and height are never more than 16384. At 16Ki*16Ki, 4Bpp + // is 1GiB, but if we allow 8Bpp (or higher) here, that's 2GiB, + // which would overflow on 32-bit. + MOZ_ASSERT(width <= 16384); + MOZ_ASSERT(height <= 16384); + MOZ_ASSERT(pixelsize < 8); + + const auto size = + CheckedInt(paddedWidth) * paddedHeight * pixelsize; + if (!size.isValid()) { + // This should never happen, but we use a defensive check. + MOZ_ASSERT_UNREACHABLE("Unacceptable size calculated.!"); + return; + } + + GLvoid* paddedPixels = new unsigned char[size.value()]; // Pad out texture data to be in a POT sized buffer for uploading to // a POT sized texture @@ -483,13 +513,17 @@ UploadImageDataToTexture(GLContext* gl, surfaceFormat = SurfaceFormat::A8; break; default: - NS_ASSERTION(false, "Unhandled image surface format!"); + MOZ_ASSERT_UNREACHABLE(false, "Unhandled image surface format!"); } if (aOutUploadSize) { *aOutUploadSize = 0; } + if (surfaceFormat == gfx::SurfaceFormat::UNKNOWN) { + return gfx::SurfaceFormat::UNKNOWN; + } + if (aNeedInit || !CanUploadSubTextures(gl)) { // If the texture needs initialized, or we are unable to // upload sub textures, then initialize and upload the entire diff --git a/image/imgLoader.cpp b/image/imgLoader.cpp index 3106a86d1..be1ec95c5 100644 --- a/image/imgLoader.cpp +++ b/image/imgLoader.cpp @@ -1697,6 +1697,7 @@ imgLoader::ValidateRequestWithNewChannel(imgRequest* request, rv = newChannel->AsyncOpen2(listener); if (NS_WARN_IF(NS_FAILED(rv))) { + req->CancelAndForgetObserver(rv); return false; } diff --git a/media/libnestegg/include/nestegg.h b/media/libnestegg/include/nestegg.h index 2baa50bc5..2a9f08f5d 100644 --- a/media/libnestegg/include/nestegg.h +++ b/media/libnestegg/include/nestegg.h @@ -72,6 +72,8 @@ extern "C" { #define NESTEGG_CODEC_VP9 2 /**< Track uses Google On2 VP9 codec. */ #define NESTEGG_CODEC_OPUS 3 /**< Track uses Xiph Opus codec. */ #define NESTEGG_CODEC_AV1 4 /**< Track uses AOMedia AV1 codec. */ +#define NESTEGG_CODEC_AVC1 5 /**< Track uses AVC1 'h264' */ +#define NESTEGG_CODEC_AAC 6 /**< Track uses AAC 'mp4a' */ #define NESTEGG_CODEC_UNKNOWN INT_MAX /**< Track uses unknown codec. */ #define NESTEGG_VIDEO_MONO 0 /**< Track is mono video. */ diff --git a/media/libnestegg/src/nestegg.c b/media/libnestegg/src/nestegg.c index 61c30ec6b..051bc50fa 100644 --- a/media/libnestegg/src/nestegg.c +++ b/media/libnestegg/src/nestegg.c @@ -157,6 +157,8 @@ enum ebml_type_enum { #define TRACK_ID_AV1 "V_AV1" #define TRACK_ID_VORBIS "A_VORBIS" #define TRACK_ID_OPUS "A_OPUS" +#define TRACK_ID_AVC1 "V_MPEG4/ISO/AVC" +#define TRACK_ID_AAC "A_AAC" /* Track Encryption */ #define CONTENT_ENC_ALGO_AES 5 @@ -2401,6 +2403,12 @@ nestegg_track_codec_id(nestegg * ctx, unsigned int track) if (strcmp(codec_id, TRACK_ID_OPUS) == 0) return NESTEGG_CODEC_OPUS; + if (strcmp(codec_id, TRACK_ID_AVC1) == 0) + return NESTEGG_CODEC_AVC1; + + if (strcmp(codec_id, TRACK_ID_AAC) == 0) + return NESTEGG_CODEC_AAC; + return NESTEGG_CODEC_UNKNOWN; } @@ -2421,7 +2429,8 @@ nestegg_track_codec_data_count(nestegg * ctx, unsigned int track, codec_id = nestegg_track_codec_id(ctx, track); - if (codec_id == NESTEGG_CODEC_OPUS) { + if (codec_id == NESTEGG_CODEC_OPUS || + codec_id == NESTEGG_CODEC_AAC) { *count = 1; return 0; } @@ -2459,7 +2468,9 @@ nestegg_track_codec_data(nestegg * ctx, unsigned int track, unsigned int item, return -1; if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS && - nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS) + nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS && + nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_AVC1 && + nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_AAC) return -1; if (ne_get_binary(entry->codec_private, &codec_private) != 0) @@ -2772,6 +2783,19 @@ nestegg_read_packet(nestegg * ctx, nestegg_packet ** pkt) if (r != 1) return r; + /* Some files have a crc32 element, since it also has to be first it + conflicts with the timecode spec. Just ignore it */ + if (id == ID_CRC32) { + ctx->log(ctx, NESTEGG_LOG_DEBUG, + "read_packet: skipping crc element in a cluster"); + r = ne_io_read_skip(ctx->io, size); + if (r != 1) + return r; + r = ne_read_element(ctx, &id, &size); + if (r != 1) + return r; + } + /* Timecode must be the first element in a Cluster, per spec. */ if (id != ID_TIMECODE) return -1; diff --git a/netwerk/base/nsStandardURL.cpp b/netwerk/base/nsStandardURL.cpp index 746ac016d..32a7c5c10 100644 --- a/netwerk/base/nsStandardURL.cpp +++ b/netwerk/base/nsStandardURL.cpp @@ -521,7 +521,7 @@ nsStandardURL::ValidIPv6orHostname(const char *host, uint32_t length) } const char *end = host + length; - if (end != net_FindCharInSet(host, end, CONTROL_CHARACTERS " #/:?@[\\]*<>|\"")) { + if (end != net_FindCharInSet(host, end, CONTROL_CHARACTERS " #/:?@[\\]*<>|\"^")) { // We still allow % because it is in the ID of addons. // Any percent encoded ASCII characters that are not allowed in the // hostname are not percent decoded, and will be parsed just fine. diff --git a/netwerk/protocol/http/Http2Push.cpp b/netwerk/protocol/http/Http2Push.cpp index b6fc485e2..57b64272e 100644 --- a/netwerk/protocol/http/Http2Push.cpp +++ b/netwerk/protocol/http/Http2Push.cpp @@ -30,8 +30,8 @@ class CallChannelOnPush final : public Runnable { Http2PushedStream *pushStream) : mAssociatedChannel(associatedChannel) , mPushedURI(pushedURI) - , mPushedStream(pushStream) { + mPushedStreamWrapper = new Http2PushedStreamWrapper(pushStream); } NS_IMETHOD Run() override @@ -40,21 +40,95 @@ class CallChannelOnPush final : public Runnable { RefPtr channel; CallQueryInterface(mAssociatedChannel, channel.StartAssignment()); MOZ_ASSERT(channel); - if (channel && NS_SUCCEEDED(channel->OnPush(mPushedURI, mPushedStream))) { + if (channel && + NS_SUCCEEDED(channel->OnPush(mPushedURI, mPushedStreamWrapper))) { return NS_OK; } LOG3(("Http2PushedStream Orphan %p failed OnPush\n", this)); - mPushedStream->OnPushFailed(); + mPushedStreamWrapper->OnPushFailed(); return NS_OK; } private: nsCOMPtr mAssociatedChannel; const nsCString mPushedURI; - Http2PushedStream *mPushedStream; + RefPtr mPushedStreamWrapper; }; +// Because WeakPtr isn't thread-safe we must ensure that the object is destroyed +// on the socket thread, so any Release() called on a different thread is +// dispatched to the socket thread. +bool Http2PushedStreamWrapper::DispatchRelease() { + if (PR_GetCurrentThread() == gSocketThread) { + return false; + } + + gSocketTransportService->Dispatch( + NewNonOwningRunnableMethod(this, &Http2PushedStreamWrapper::Release), + NS_DISPATCH_NORMAL); + + return true; +} + +NS_IMPL_ADDREF(Http2PushedStreamWrapper) +NS_IMETHODIMP_(MozExternalRefCountType) +Http2PushedStreamWrapper::Release() { + nsrefcnt count = mRefCnt - 1; + if (DispatchRelease()) { + // Redispatched to the socket thread. + return count; + } + + MOZ_ASSERT(0 != mRefCnt, "dup release"); + count = --mRefCnt; + NS_LOG_RELEASE(this, count, "Http2PushedStreamWrapper"); + + if (0 == count) { + mRefCnt = 1; + delete (this); + return 0; + } + + return count; +} + +NS_INTERFACE_MAP_BEGIN(Http2PushedStreamWrapper) +NS_INTERFACE_MAP_END + +Http2PushedStreamWrapper::Http2PushedStreamWrapper( + Http2PushedStream* aPushStream) { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "not on socket thread"); + mStream = aPushStream; + mRequestString = aPushStream->GetRequestString(); +} + +Http2PushedStreamWrapper::~Http2PushedStreamWrapper() { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "not on socket thread"); +} + +Http2PushedStream* Http2PushedStreamWrapper::GetStream() { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "not on socket thread"); + if (mStream) { + Http2Stream* stream = mStream; + return static_cast(stream); + } + return nullptr; +} + +void Http2PushedStreamWrapper::OnPushFailed() { + if (PR_GetCurrentThread() == gSocketThread) { + if (mStream) { + Http2Stream* stream = mStream; + static_cast(stream)->OnPushFailed(); + } + } else { + gSocketTransportService->Dispatch( + NewRunnableMethod(this, &Http2PushedStreamWrapper::OnPushFailed), + NS_DISPATCH_NORMAL); + } +} + ////////////////////////////////////////// // Http2PushedStream ////////////////////////////////////////// diff --git a/netwerk/protocol/http/Http2Push.h b/netwerk/protocol/http/Http2Push.h index fd39eb2c7..c58f27652 100644 --- a/netwerk/protocol/http/Http2Push.h +++ b/netwerk/protocol/http/Http2Push.h @@ -123,6 +123,24 @@ private: uint32_t mBufferedHTTP1Consumed; }; +class Http2PushedStreamWrapper : public nsISupports { + public: + NS_DECL_THREADSAFE_ISUPPORTS + bool DispatchRelease(); + + explicit Http2PushedStreamWrapper(Http2PushedStream* aPushStream); + + nsCString& GetRequestString() { return mRequestString; } + Http2PushedStream* GetStream(); + void OnPushFailed(); + + private: + virtual ~Http2PushedStreamWrapper(); + + nsCString mRequestString; + WeakPtr mStream; +}; + } // namespace net } // namespace mozilla diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index 2fa745e22..afbbdeb90 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -387,12 +387,24 @@ Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction, if (mClosed || mShouldGoAway) { nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction(); - if (trans && !trans->GetPushedStream()) { - LOG3(("Http2Session::AddStream %p atrans=%p trans=%p session unusable - resched.\n", - this, aHttpTransaction, trans)); - aHttpTransaction->SetConnection(nullptr); - gHttpHandler->InitiateTransaction(trans, trans->Priority()); - return true; + if (trans) { + RefPtr pushedStreamWrapper; + pushedStreamWrapper = trans->GetPushedStream(); + if (!pushedStreamWrapper || !pushedStreamWrapper->GetStream()) { + LOG3( + ("Http2Session::AddStream %p atrans=%p trans=%p session unusable - " + "resched.\n", this, aHttpTransaction, trans)); + aHttpTransaction->SetConnection(nullptr); + nsresult rv = + gHttpHandler->InitiateTransaction(trans, trans->Priority()); + if (NS_FAILED(rv)) { + LOG3( + ("Http2Session::AddStream %p atrans=%p trans=%p failed to " + "initiate transaction (%08x).\n", + this, aHttpTransaction, trans, static_cast(rv))); + } + return true; + } } } @@ -1726,8 +1738,9 @@ Http2Session::RecvPushPromise(Http2Session *self) RefPtr transactionBuffer = new Http2PushTransactionBuffer(); transactionBuffer->SetConnection(self); - Http2PushedStream *pushedStream = - new Http2PushedStream(transactionBuffer, self, associatedStream, promisedID); + nsAutoPtr pushedStream( + new Http2PushedStream(transactionBuffer, self, associatedStream, promisedID) + ); rv = pushedStream->ConvertPushHeaders(&self->mDecompressor, self->mDecompressBuffer, @@ -1736,7 +1749,6 @@ Http2Session::RecvPushPromise(Http2Session *self) if (rv == NS_ERROR_NOT_IMPLEMENTED) { LOG3(("Http2Session::PushPromise Semantics not Implemented\n")); self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID); - delete pushedStream; self->ResetDownstreamState(); return NS_OK; } @@ -1745,7 +1757,6 @@ Http2Session::RecvPushPromise(Http2Session *self) // This means the decompression completed ok, but there was a problem with // the decoded headers. Reset the stream and go away. self->GenerateRstStream(PROTOCOL_ERROR, promisedID); - delete pushedStream; self->ResetDownstreamState(); return NS_OK; } else if (NS_FAILED(rv)) { @@ -1754,14 +1765,17 @@ Http2Session::RecvPushPromise(Http2Session *self) return rv; } + WeakPtr pushedWeak = pushedStream.forget(); + // Ownership of the pushed stream is by the transaction hash, just as it // is for a client initiated stream. Errors that aren't fatal to the // whole session must call cleanupStream() after this point in order // to remove the stream from that hash. - self->mStreamTransactionHash.Put(transactionBuffer, pushedStream); - self->mPushedStreams.AppendElement(pushedStream); + self->mStreamTransactionHash.Put(transactionBuffer, pushedWeak); + self->mPushedStreams.AppendElement( + static_cast(pushedWeak.get())); - if (self->RegisterStreamID(pushedStream, promisedID) == kDeadStreamID) { + if (self->RegisterStreamID(pushedWeak, promisedID) == kDeadStreamID) { LOG3(("Http2Session::RecvPushPromise registerstreamid failed\n")); self->mGoAwayReason = INTERNAL_ERROR; return NS_ERROR_FAILURE; @@ -1773,23 +1787,24 @@ Http2Session::RecvPushPromise(Http2Session *self) // Fake the request side of the pushed HTTP transaction. Sets up hash // key and origin uint32_t notUsed; - pushedStream->ReadSegments(nullptr, 1, ¬Used); + pushedWeak->ReadSegments(nullptr, 1, ¬Used); nsAutoCString key; - if (!pushedStream->GetHashKey(key)) { + if (!static_cast(pushedWeak.get())->GetHashKey(key)) { LOG3(("Http2Session::RecvPushPromise one of :authority :scheme :path missing from push\n")); - self->CleanupStream(pushedStream, NS_ERROR_FAILURE, PROTOCOL_ERROR); + self->CleanupStream(pushedWeak, NS_ERROR_FAILURE, PROTOCOL_ERROR); self->ResetDownstreamState(); return NS_OK; } + // does the pushed origin belong on this connection? RefPtr associatedURL, pushedURL; rv = Http2Stream::MakeOriginURL(associatedStream->Origin(), associatedURL); if (NS_SUCCEEDED(rv)) { - rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedURL); + rv = Http2Stream::MakeOriginURL(pushedWeak->Origin(), pushedURL); } LOG3(("Http2Session::RecvPushPromise %p checking %s == %s", self, - associatedStream->Origin().get(), pushedStream->Origin().get())); + associatedStream->Origin().get(), pushedWeak->Origin().get())); bool match = false; if (NS_SUCCEEDED(rv)) { rv = associatedURL->Equals(pushedURL, &match); @@ -1797,36 +1812,38 @@ Http2Session::RecvPushPromise(Http2Session *self) if (NS_FAILED(rv)) { // Fallback to string equality of origins. This won't be guaranteed to be as // liberal as we want it to be, but it will at least be safe - match = associatedStream->Origin().Equals(pushedStream->Origin()); + match = associatedStream->Origin().Equals(pushedWeak->Origin()); } if (!match) { LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin " "associated origin %s .. pushed origin %s\n", self, - associatedStream->Origin().get(), pushedStream->Origin().get())); - self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR); + associatedStream->Origin().get(), pushedWeak->Origin().get())); + self->CleanupStream(pushedWeak, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR); self->ResetDownstreamState(); return NS_OK; } - if (pushedStream->TryOnPush()) { + if (static_cast(pushedWeak.get())->TryOnPush()) { LOG3(("Http2Session::RecvPushPromise %p channel implements nsIHttpPushListener " - "stream %p will not be placed into session cache.\n", self, pushedStream)); + "stream %p will not be placed into session cache.\n", self, pushedWeak.get())); } else { LOG3(("Http2Session::RecvPushPromise %p place stream into session cache\n", self)); - if (!cache->RegisterPushedStreamHttp2(key, pushedStream)) { + if (!cache->RegisterPushedStreamHttp2( + key, static_cast(pushedWeak.get()))) { + // This only happens if they've already pushed us this item. LOG3(("Http2Session::RecvPushPromise registerPushedStream Failed\n")); - self->CleanupStream(pushedStream, NS_ERROR_FAILURE, INTERNAL_ERROR); + self->CleanupStream(pushedWeak, NS_ERROR_FAILURE, INTERNAL_ERROR); self->ResetDownstreamState(); return NS_OK; } } - pushedStream->SetHTTPState(Http2Stream::RESERVED_BY_REMOTE); + pushedWeak->SetHTTPState(Http2Stream::RESERVED_BY_REMOTE); static_assert(Http2Stream::kWorstPriority >= 0, "kWorstPriority out of range"); uint8_t priorityWeight = (nsISupportsPriority::PRIORITY_LOWEST + 1) - (Http2Stream::kWorstPriority - Http2Stream::kNormalPriority); - pushedStream->SetPriority(Http2Stream::kWorstPriority); + pushedWeak->SetPriority(Http2Stream::kWorstPriority); self->GeneratePriority(promisedID, priorityWeight); self->ResetDownstreamState(); return NS_OK; diff --git a/netwerk/protocol/http/Http2Stream.cpp b/netwerk/protocol/http/Http2Stream.cpp index 7a8f96855..37ecc0600 100644 --- a/netwerk/protocol/http/Http2Stream.cpp +++ b/netwerk/protocol/http/Http2Stream.cpp @@ -443,12 +443,14 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, requestContext->GetSpdyPushCache(&cache); } + RefPtr pushedStreamWrapper; Http2PushedStream *pushedStream = nullptr; // If a push stream is attached to the transaction via onPush, match only with that // one. This occurs when a push was made with in conjunction with a nsIHttpPushListener nsHttpTransaction *trans = mTransaction->QueryHttpTransaction(); - if (trans && (pushedStream = trans->TakePushedStream())) { + if (trans && (pushedStreamWrapper = trans->TakePushedStream()) && + (pushedStream = pushedStreamWrapper->GetStream())) { if (pushedStream->mSession == mSession) { LOG3(("Pushed Stream match based on OnPush correlation %p", pushedStream)); } else { diff --git a/netwerk/protocol/http/Http2Stream.h b/netwerk/protocol/http/Http2Stream.h index 8783eefed..7deb05bb0 100644 --- a/netwerk/protocol/http/Http2Stream.h +++ b/netwerk/protocol/http/Http2Stream.h @@ -14,6 +14,7 @@ #include "nsAHttpTransaction.h" #include "nsISupportsPriority.h" #include "SimpleBuffer.h" +#include "mozilla/WeakPtr.h" class nsIInputStream; class nsIOutputStream; @@ -28,8 +29,10 @@ class Http2Decompressor; class Http2Stream : public nsAHttpSegmentReader , public nsAHttpSegmentWriter + , public SupportsWeakPtr { public: + MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Http2Stream) NS_DECL_NSAHTTPSEGMENTREADER NS_DECL_NSAHTTPSEGMENTWRITER diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index aa83d0cee..ab7912795 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -543,13 +543,14 @@ HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { LOG(("HttpChannelChild::DoOnStartRequest [this=%p]\n", this)); - // In theory mListener should not be null, but in practice sometimes it is. - MOZ_ASSERT(mListener); - if (!mListener) { - Cancel(NS_ERROR_FAILURE); - return; + nsresult rv; + if (MOZ_LIKELY(mListener)) { + nsCOMPtr listener(mListener); + rv = listener->OnStartRequest(aRequest, aContext); + } else { + rv = NS_ERROR_UNEXPECTED; } - nsresult rv = mListener->OnStartRequest(aRequest, aContext); + if (NS_FAILED(rv)) { Cancel(rv); return; @@ -804,9 +805,12 @@ HttpChannelChild::DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, if (mCanceled) return; - nsresult rv = mListener->OnDataAvailable(aRequest, aContext, aStream, offset, count); - if (NS_FAILED(rv)) { - Cancel(rv); + if (MOZ_LIKELY(mListener)) { + nsCOMPtr listener(mListener); + nsresult rv = listener->OnDataAvailable(aRequest, aContext, aStream, offset, count); + if (NS_FAILED(rv)) { + Cancel(rv); + } } } @@ -985,11 +989,11 @@ HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest, nsresult aChannelStatus, MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice"); - // In theory mListener should not be null, but in practice sometimes it is. - MOZ_ASSERT(mListener); - if (mListener) { - mListener->OnStopRequest(aRequest, aContext, mStatus); + if (MOZ_LIKELY(mListener)) { + nsCOMPtr listener(mListener); + listener->OnStopRequest(aRequest, aContext, mStatus); } + mOnStopRequestCalled = true; mListener = nullptr; diff --git a/netwerk/protocol/http/nsCORSListenerProxy.cpp b/netwerk/protocol/http/nsCORSListenerProxy.cpp index b0cd1c49e..86b9a8463 100644 --- a/netwerk/protocol/http/nsCORSListenerProxy.cpp +++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp @@ -912,6 +912,9 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, NS_ENSURE_SUCCESS(rv, rv); } + // TODO: Bug 1353683 + // consider calling SetBlockedRequest in nsCORSListenerProxy::UpdateChannel + // // Check that the uri is ok to load rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(mRequestingPrincipal, uri, diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 1cfcdd841..b517597b3 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -6915,7 +6915,8 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st if (mListener) { MOZ_ASSERT(!mOnStartRequestCalled, "We should not call OnStartRequest twice."); - mListener->OnStartRequest(this, mListenerContext); + nsCOMPtr listener(mListener); + listener->OnStartRequest(this, mListenerContext); mOnStartRequestCalled = true; } else { NS_WARNING("OnStartRequest skipped because of null listener"); @@ -8100,7 +8101,7 @@ nsHttpChannel::AwaitingCacheCallbacks() } void -nsHttpChannel::SetPushedStream(Http2PushedStream *stream) +nsHttpChannel::SetPushedStream(Http2PushedStreamWrapper *stream) { MOZ_ASSERT(stream); MOZ_ASSERT(!mPushedStream); @@ -8108,7 +8109,8 @@ nsHttpChannel::SetPushedStream(Http2PushedStream *stream) } nsresult -nsHttpChannel::OnPush(const nsACString &url, Http2PushedStream *pushedStream) +nsHttpChannel::OnPush(const nsACString &url, + Http2PushedStreamWrapper *pushedStream) { MOZ_ASSERT(NS_IsMainThread()); LOG(("nsHttpChannel::OnPush [this=%p]\n", this)); diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 7578b1173..99071252c 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -128,7 +128,8 @@ public: nsIURI *aProxyURI, const nsID& aChannelId) override; - nsresult OnPush(const nsACString &uri, Http2PushedStream *pushedStream); + nsresult OnPush(const nsACString &uri, + Http2PushedStreamWrapper *pushedStream); static bool IsRedirectStatus(uint32_t status); @@ -456,7 +457,7 @@ private: nsresult OpenCacheInputStream(nsICacheEntry* cacheEntry, bool startBuffering, bool checkingAppCacheEntry); - void SetPushedStream(Http2PushedStream *stream); + void SetPushedStream(Http2PushedStreamWrapper *stream); void MaybeWarnAboutAppCache(); @@ -592,7 +593,7 @@ private: // Needed for accurate DNS timing RefPtr mDNSPrefetch; - Http2PushedStream *mPushedStream; + RefPtr mPushedStream; // True if the channel's principal was found on a phishing, malware, or // tracking (if tracking protection is enabled) blocklist bool mLocalBlocklist; diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index ce46a5899..317bd835a 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -376,8 +376,12 @@ nsHttpConnectionMgr::VerifyTraffic() nsresult nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI) { + RefPtr ci; + if (aCI) { + ci = aCI->Clone(); + } return PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup, - 0, aCI); + 0, ci); } class SpeculativeConnectArgs : public ARefBase @@ -507,9 +511,13 @@ nsHttpConnectionMgr::UpdateParam(nsParamName name, uint16_t value) } nsresult -nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci) +nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo* aCI) { - LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get())); + LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", aCI->HashKey().get())); + RefPtr ci; + if (aCI) { + ci = aCI->Clone(); + } return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci); } @@ -1863,13 +1871,17 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans) trans->SetPendingTime(); - Http2PushedStream *pushedStream = trans->GetPushedStream(); - if (pushedStream) { - LOG((" ProcessNewTransaction %p tied to h2 session push %p\n", - trans, pushedStream->Session())); - return pushedStream->Session()-> - AddStream(trans, trans->Priority(), false, nullptr) ? - NS_OK : NS_ERROR_UNEXPECTED; + RefPtr pushedStreamWrapper = + trans->GetPushedStream(); + if (pushedStreamWrapper) { + Http2PushedStream* pushedStream = pushedStreamWrapper->GetStream(); + if (pushedStream) { + LOG((" ProcessNewTransaction %p tied to h2 session push %p\n", + trans, pushedStream->Session())); + return pushedStream->Session()-> + AddStream(trans, trans->Priority(), false, nullptr) ? + NS_OK : NS_ERROR_UNEXPECTED; + } } nsresult rv = NS_OK; diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 6fd0ff3bb..e604c82c8 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -2361,7 +2361,7 @@ nsHttpHandler::SpeculativeConnectInternal(nsIURI *aURI, originAttributes.StripAttributes(OriginAttributes::STRIP_ADDON_ID); } - auto *ci = + RefPtr ci = new nsHttpConnectionInfo(host, port, EmptyCString(), username, nullptr, originAttributes, usingSSL); ci->SetAnonymous(anonymous); diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index b40a9c619..09a668baa 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -235,7 +235,8 @@ public: uint32_t caps = 0) { TickleWifi(callbacks); - return mConnMgr->SpeculativeConnect(ci, callbacks, caps); + RefPtr clone = ci->Clone(); + return mConnMgr->SpeculativeConnect(clone, callbacks, caps); } // Alternate Services Maps are main thread only diff --git a/netwerk/protocol/http/nsHttpTransaction.h b/netwerk/protocol/http/nsHttpTransaction.h index 62c1c67ad..5015275b2 100644 --- a/netwerk/protocol/http/nsHttpTransaction.h +++ b/netwerk/protocol/http/nsHttpTransaction.h @@ -136,14 +136,16 @@ public: nsHttpTransaction *QueryHttpTransaction() override { return this; } - Http2PushedStream *GetPushedStream() { return mPushedStream; } - Http2PushedStream *TakePushedStream() - { - Http2PushedStream *r = mPushedStream; - mPushedStream = nullptr; - return r; + already_AddRefed GetPushedStream() { + //return do_AddRef(mPushedStream); // XXX: add this support to RefPtr.h + RefPtr ref(mPushedStream); + return ref.forget(); } - void SetPushedStream(Http2PushedStream *push) { mPushedStream = push; } + already_AddRefed TakePushedStream() { + return mPushedStream.forget(); + } + + void SetPushedStream(Http2PushedStreamWrapper* push) { mPushedStream = push; } uint32_t InitialRwin() const { return mInitialRwin; }; bool ChannelPipeFull() { return mWaitingOnPipeOut; } @@ -269,7 +271,7 @@ private: // so far been skipped. uint32_t mInvalidResponseBytesRead; - Http2PushedStream *mPushedStream; + RefPtr mPushedStream; uint32_t mInitialRwin; nsHttpChunkedDecoder *mChunkedDecoder; diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index 554041b37..48d8fe2e0 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -956,8 +956,9 @@ nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL, const nsAString& aImageReferrerPolicy) { nsCOMPtr baseURI = BaseURIForPreload(); + bool isImgSet = false; nsCOMPtr uri = mDocument->ResolvePreloadImage(baseURI, aURL, aSrcset, - aSizes); + aSizes, &isImgSet); if (uri && ShouldPreloadURI(uri)) { // use document wide referrer policy mozilla::net::ReferrerPolicy referrerPolicy = mSpeculationReferrerPolicy; @@ -971,7 +972,7 @@ nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL, } } - mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy); + mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy, isImgSet); } }