From ef988eb9310b38ed12794f9f5b99392d24df2c55 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Mon, 13 Aug 2012 15:58:53 -0700 Subject: [PATCH] Bug 697230: Part 2 - Push onload blocking logic down into imagelib. r=joe sr=bz --- content/base/public/nsINode.h | 15 +++ content/base/src/nsGenConImageContent.cpp | 8 +- content/base/src/nsImageLoadingContent.cpp | 112 +++++++----------- content/base/src/nsImageLoadingContent.h | 5 +- .../html/content/src/nsHTMLImageElement.cpp | 5 +- .../html/content/src/nsHTMLInputElement.cpp | 3 +- .../html/content/src/nsHTMLObjectElement.cpp | 1 + .../content/src/nsHTMLSharedObjectElement.cpp | 1 + content/svg/content/src/nsSVGFilters.cpp | 5 +- content/svg/content/src/nsSVGImageElement.cpp | 4 +- image/public/Makefile.in | 1 + image/public/imgIOnloadBlocker.idl | 64 ++++++++++ image/src/imgRequest.cpp | 69 +++++++++-- image/src/imgRequest.h | 1 + image/src/imgRequestProxy.cpp | 29 +++++ image/src/imgRequestProxy.h | 4 + image/src/imgStatusTracker.cpp | 35 ++++++ image/src/imgStatusTracker.h | 12 +- image/test/unit/image_load_helpers.js | 2 +- 19 files changed, 291 insertions(+), 85 deletions(-) create mode 100644 image/public/imgIOnloadBlocker.idl diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h index 189d64265507..231616b325f5 100644 --- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -1672,6 +1672,21 @@ extern const nsIID kThisPtrOffsetsSID; NS_OFFSET_AND_INTERFACE_TABLE_END \ NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE +#define NS_NODE_INTERFACE_TABLE9(_class, _i1, _i2, _i3, _i4, _i5, _i6, _i7, \ + _i8, _i9) \ + NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(_class) \ + NS_INTERFACE_TABLE_ENTRY(_class, _i1) \ + NS_INTERFACE_TABLE_ENTRY(_class, _i2) \ + NS_INTERFACE_TABLE_ENTRY(_class, _i3) \ + NS_INTERFACE_TABLE_ENTRY(_class, _i4) \ + NS_INTERFACE_TABLE_ENTRY(_class, _i5) \ + NS_INTERFACE_TABLE_ENTRY(_class, _i6) \ + NS_INTERFACE_TABLE_ENTRY(_class, _i7) \ + NS_INTERFACE_TABLE_ENTRY(_class, _i8) \ + NS_INTERFACE_TABLE_ENTRY(_class, _i9) \ + NS_OFFSET_AND_INTERFACE_TABLE_END \ + NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE + NS_DEFINE_STATIC_IID_ACCESSOR(nsINode, NS_INODE_IID) diff --git a/content/base/src/nsGenConImageContent.cpp b/content/base/src/nsGenConImageContent.cpp index 1f55fcf7e0e7..49ffc4a232d4 100644 --- a/content/base/src/nsGenConImageContent.cpp +++ b/content/base/src/nsGenConImageContent.cpp @@ -48,8 +48,12 @@ public: NS_DECL_ISUPPORTS_INHERITED }; -NS_IMPL_ISUPPORTS_INHERITED3(nsGenConImageContent, nsXMLElement, - nsIImageLoadingContent, imgIContainerObserver, imgIDecoderObserver) +NS_IMPL_ISUPPORTS_INHERITED4(nsGenConImageContent, + nsXMLElement, + nsIImageLoadingContent, + imgIContainerObserver, + imgIDecoderObserver, + imgIOnloadBlocker) nsresult NS_NewGenConImageContent(nsIContent** aResult, already_AddRefed aNodeInfo, diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index 0b84507503f3..53ffb3746c2a 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -153,23 +153,6 @@ nsImageLoadingContent::OnStartDecode(imgIRequest* aRequest) { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); - // Onload blocking. This only applies for the current request. - if (aRequest == mCurrentRequest) { - - // Determine whether this is a background request (this can be the case - // with multipart/x-mixed-replace images, for example). - PRUint32 loadFlags; - nsresult rv = aRequest->GetLoadFlags(&loadFlags); - bool background = - (NS_SUCCEEDED(rv) && (loadFlags & nsIRequest::LOAD_BACKGROUND)); - - // Block onload for non-background requests - if (!background) { - NS_ABORT_IF_FALSE(!mBlockingOnload, "Shouldn't already be blocking"); - SetBlockingOnload(true); - } - } - LOOP_OVER_OBSERVERS(OnStartDecode(aRequest)); return NS_OK; } @@ -215,10 +198,6 @@ nsImageLoadingContent::OnStopFrame(imgIRequest* aRequest, { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); - // If we're blocking a load, one frame is enough - if (aRequest == mCurrentRequest) - SetBlockingOnload(false); - LOOP_OVER_OBSERVERS(OnStopFrame(aRequest, aFrame)); return NS_OK; } @@ -229,15 +208,6 @@ nsImageLoadingContent::OnStopContainer(imgIRequest* aRequest, { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); - // This is really hacky. We need to handle the case where we start decoding, - // block onload, but then hit an error before we get to our first frame. In - // theory we would just hook in at OnStopDecode, but OnStopDecode is broken - // until we fix bug 505385. OnStopContainer is actually going away at that - // point. So for now we take advantage of the fact that OnStopContainer is - // always fired in the decoders at the same time as OnStopDecode. - if (aRequest == mCurrentRequest) - SetBlockingOnload(false); - LOOP_OVER_OBSERVERS(OnStopContainer(aRequest, aContainer)); return NS_OK; } @@ -309,7 +279,7 @@ nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest, FireEvent(NS_LITERAL_STRING("error")); } - nsCOMPtr thisNode = do_QueryInterface(this); + nsCOMPtr thisNode = do_QueryInterface(static_cast(this)); nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement()); return NS_OK; @@ -598,6 +568,36 @@ NS_IMETHODIMP nsImageLoadingContent::ForceReload() return LoadImage(currentURI, true, true, nullptr, nsIRequest::VALIDATE_ALWAYS); } +NS_IMETHODIMP +nsImageLoadingContent::BlockOnload(imgIRequest* aRequest) +{ + if (aRequest != mCurrentRequest) { + return NS_OK; + } + + nsIDocument* doc = GetOurCurrentDoc(); + if (doc) { + doc->BlockOnload(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsImageLoadingContent::UnblockOnload(imgIRequest* aRequest) +{ + if (aRequest != mCurrentRequest) { + return NS_OK; + } + + nsIDocument* doc = GetOurCurrentDoc(); + if (doc) { + doc->UnblockOnload(false); + } + + return NS_OK; +} + /* * Non-interface methods */ @@ -691,7 +691,7 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, // We use the principal of aDocument to avoid having to QI |this| an extra // time. It should always be the same as the principal of this node. #ifdef DEBUG - nsCOMPtr thisContent = do_QueryInterface(this); + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); NS_ABORT_IF_FALSE(thisContent && thisContent->NodePrincipal() == aDocument->NodePrincipal(), "Principal mismatch?"); @@ -699,8 +699,11 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI, // Are we blocked? PRInt16 cpDecision = nsIContentPolicy::REJECT_REQUEST; - nsContentUtils::CanLoadImage(aNewURI, this, aDocument, - aDocument->NodePrincipal(), &cpDecision); + nsContentUtils::CanLoadImage(aNewURI, + static_cast(this), + aDocument, + aDocument->NodePrincipal(), + &cpDecision); if (!NS_CP_ACCEPTED(cpDecision)) { FireEvent(NS_LITERAL_STRING("error")); SetBlockedRequest(aNewURI, cpDecision); @@ -806,7 +809,7 @@ nsImageLoadingContent::UpdateImageState(bool aNotify) return; } - nsCOMPtr thisContent = do_QueryInterface(this); + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); if (!thisContent) { return; } @@ -870,7 +873,8 @@ nsImageLoadingContent::UseAsPrimaryRequest(imgIRequest* aRequest, nsIDocument* nsImageLoadingContent::GetOurOwnerDoc() { - nsCOMPtr thisContent = do_QueryInterface(this); + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); NS_ENSURE_TRUE(thisContent, nullptr); return thisContent->OwnerDoc(); @@ -879,7 +883,8 @@ nsImageLoadingContent::GetOurOwnerDoc() nsIDocument* nsImageLoadingContent::GetOurCurrentDoc() { - nsCOMPtr thisContent = do_QueryInterface(this); + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); NS_ENSURE_TRUE(thisContent, nullptr); return thisContent->GetCurrentDoc(); @@ -888,7 +893,8 @@ nsImageLoadingContent::GetOurCurrentDoc() nsIFrame* nsImageLoadingContent::GetOurPrimaryFrame() { - nsCOMPtr thisContent = do_QueryInterface(this); + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); return thisContent->GetPrimaryFrame(); } @@ -911,7 +917,7 @@ nsImageLoadingContent::StringToURI(const nsAString& aSpec, NS_PRECONDITION(aURI, "Null out param"); // (1) Get the base URI - nsCOMPtr thisContent = do_QueryInterface(this); + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); NS_ASSERTION(thisContent, "An image loading content must be an nsIContent"); nsCOMPtr baseURL = thisContent->GetBaseURI(); @@ -933,7 +939,7 @@ nsImageLoadingContent::FireEvent(const nsAString& aEventType) // loops in cases when onLoad handlers reset the src and the new src is in // cache. - nsCOMPtr thisNode = do_QueryInterface(this); + nsCOMPtr thisNode = do_QueryInterface(static_cast(this)); nsRefPtr event = new nsLoadBlockingAsyncDOMEvent(thisNode, aEventType, false, false); @@ -1080,10 +1086,6 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason) mCurrentRequest->CancelAndForgetObserver(aReason); mCurrentRequest = nullptr; mCurrentRequestFlags = 0; - - // We only block onload during the decoding of "current" images. This one is - // going away, so we should unblock unconditionally here. - SetBlockingOnload(false); } void @@ -1147,28 +1149,6 @@ nsImageLoadingContent::HaveSize(imgIRequest *aImage) return (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_SIZE_AVAILABLE)); } -void -nsImageLoadingContent::SetBlockingOnload(bool aBlocking) -{ - // If we're already in the desired state, we have nothing to do - if (mBlockingOnload == aBlocking) - return; - - // Get the document - nsIDocument* doc = GetOurOwnerDoc(); - - if (doc) { - // Take the appropriate action - if (aBlocking) - doc->BlockOnload(); - else - doc->UnblockOnload(false); - - // Update our state - mBlockingOnload = aBlocking; - } -} - void nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, diff --git a/content/base/src/nsImageLoadingContent.h b/content/base/src/nsImageLoadingContent.h index 1bd0a2f4e4cd..90186f5852e8 100644 --- a/content/base/src/nsImageLoadingContent.h +++ b/content/base/src/nsImageLoadingContent.h @@ -15,6 +15,7 @@ #include "imgIContainerObserver.h" #include "imgIDecoderObserver.h" +#include "imgIOnloadBlocker.h" #include "mozilla/CORSMode.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" // NS_CONTENT_DELETE_LIST_MEMBER @@ -27,7 +28,8 @@ class nsIDocument; class imgILoader; class nsIIOService; -class nsImageLoadingContent : public nsIImageLoadingContent +class nsImageLoadingContent : public nsIImageLoadingContent, + public imgIOnloadBlocker { /* METHODS */ public: @@ -37,6 +39,7 @@ public: NS_DECL_IMGICONTAINEROBSERVER NS_DECL_IMGIDECODEROBSERVER NS_DECL_NSIIMAGELOADINGCONTENT + NS_DECL_IMGIONLOADBLOCKER protected: /** diff --git a/content/html/content/src/nsHTMLImageElement.cpp b/content/html/content/src/nsHTMLImageElement.cpp index 3673a8113a66..a46558f78331 100644 --- a/content/html/content/src/nsHTMLImageElement.cpp +++ b/content/html/content/src/nsHTMLImageElement.cpp @@ -89,12 +89,13 @@ DOMCI_NODE_DATA(HTMLImageElement, nsHTMLImageElement) // QueryInterface implementation for nsHTMLImageElement NS_INTERFACE_TABLE_HEAD(nsHTMLImageElement) - NS_HTML_CONTENT_INTERFACE_TABLE5(nsHTMLImageElement, + NS_HTML_CONTENT_INTERFACE_TABLE6(nsHTMLImageElement, nsIDOMHTMLImageElement, nsIJSNativeInitializer, imgIDecoderObserver, nsIImageLoadingContent, - imgIContainerObserver) + imgIContainerObserver, + imgIOnloadBlocker) NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLImageElement, nsGenericHTMLElement) NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLImageElement) diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index 76f87f991e30..f2630dd82c22 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -616,13 +616,14 @@ DOMCI_NODE_DATA(HTMLInputElement, nsHTMLInputElement) // QueryInterface implementation for nsHTMLInputElement NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLInputElement) - NS_HTML_CONTENT_INTERFACE_TABLE8(nsHTMLInputElement, + NS_HTML_CONTENT_INTERFACE_TABLE9(nsHTMLInputElement, nsIDOMHTMLInputElement, nsITextControlElement, nsIPhonetic, imgIDecoderObserver, nsIImageLoadingContent, imgIContainerObserver, + imgIOnloadBlocker, nsIDOMNSEditableElement, nsIConstraintValidation) NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLInputElement, diff --git a/content/html/content/src/nsHTMLObjectElement.cpp b/content/html/content/src/nsHTMLObjectElement.cpp index 46e3bf6a5ddc..f146372df778 100644 --- a/content/html/content/src/nsHTMLObjectElement.cpp +++ b/content/html/content/src/nsHTMLObjectElement.cpp @@ -206,6 +206,7 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLObjectElement) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIFrameLoaderOwner) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIObjectLoadingContent) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIImageLoadingContent) + NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, imgIOnloadBlocker) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, imgIContainerObserver) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIInterfaceRequestor) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIChannelEventSink) diff --git a/content/html/content/src/nsHTMLSharedObjectElement.cpp b/content/html/content/src/nsHTMLSharedObjectElement.cpp index be68783c563e..8354963a0624 100644 --- a/content/html/content/src/nsHTMLSharedObjectElement.cpp +++ b/content/html/content/src/nsHTMLSharedObjectElement.cpp @@ -254,6 +254,7 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLSharedObjectElement) NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIObjectLoadingContent) NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, imgIDecoderObserver) NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIImageLoadingContent) + NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, imgIOnloadBlocker) NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIInterfaceRequestor) NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIChannelEventSink) NS_OFFSET_AND_INTERFACE_TABLE_END diff --git a/content/svg/content/src/nsSVGFilters.cpp b/content/svg/content/src/nsSVGFilters.cpp index 1cb746974c48..d15d1eb41a4a 100644 --- a/content/svg/content/src/nsSVGFilters.cpp +++ b/content/svg/content/src/nsSVGFilters.cpp @@ -5490,11 +5490,12 @@ NS_IMPL_RELEASE_INHERITED(nsSVGFEImageElement,nsSVGFEImageElementBase) DOMCI_NODE_DATA(SVGFEImageElement, nsSVGFEImageElement) NS_INTERFACE_TABLE_HEAD(nsSVGFEImageElement) - NS_NODE_INTERFACE_TABLE8(nsSVGFEImageElement, nsIDOMNode, nsIDOMElement, + NS_NODE_INTERFACE_TABLE9(nsSVGFEImageElement, nsIDOMNode, nsIDOMElement, nsIDOMSVGElement, nsIDOMSVGFilterPrimitiveStandardAttributes, nsIDOMSVGFEImageElement, nsIDOMSVGURIReference, - imgIDecoderObserver, nsIImageLoadingContent) + imgIDecoderObserver, nsIImageLoadingContent, + imgIOnloadBlocker) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFEImageElement) NS_INTERFACE_MAP_END_INHERITING(nsSVGFEImageElementBase) diff --git a/content/svg/content/src/nsSVGImageElement.cpp b/content/svg/content/src/nsSVGImageElement.cpp index 16f80e256453..5fb930024c52 100644 --- a/content/svg/content/src/nsSVGImageElement.cpp +++ b/content/svg/content/src/nsSVGImageElement.cpp @@ -39,11 +39,11 @@ NS_IMPL_RELEASE_INHERITED(nsSVGImageElement,nsSVGImageElementBase) DOMCI_NODE_DATA(SVGImageElement, nsSVGImageElement) NS_INTERFACE_TABLE_HEAD(nsSVGImageElement) - NS_NODE_INTERFACE_TABLE8(nsSVGImageElement, nsIDOMNode, nsIDOMElement, + NS_NODE_INTERFACE_TABLE9(nsSVGImageElement, nsIDOMNode, nsIDOMElement, nsIDOMSVGElement, nsIDOMSVGTests, nsIDOMSVGImageElement, nsIDOMSVGURIReference, imgIDecoderObserver, - nsIImageLoadingContent) + nsIImageLoadingContent, imgIOnloadBlocker) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGImageElement) NS_INTERFACE_MAP_END_INHERITING(nsSVGImageElementBase) diff --git a/image/public/Makefile.in b/image/public/Makefile.in index 8da0b64d2ba1..8e189f85456f 100644 --- a/image/public/Makefile.in +++ b/image/public/Makefile.in @@ -23,6 +23,7 @@ XPIDLSRCS = \ imgIDecoderObserver.idl \ imgIEncoder.idl \ imgILoader.idl \ + imgIOnloadBlocker.idl \ imgIRequest.idl \ imgITools.idl \ $(NULL) diff --git a/image/public/imgIOnloadBlocker.idl b/image/public/imgIOnloadBlocker.idl new file mode 100644 index 000000000000..f2c31bcaffbe --- /dev/null +++ b/image/public/imgIOnloadBlocker.idl @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Kyle Huey (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface imgIRequest; + +[uuid(dc126d90-0ee0-4683-b942-2fa66e443abc)] +interface imgIOnloadBlocker : nsISupports +{ + /** + * blockOnload + * Called when it is appropriate to block onload for the given imgIRequest. + * + * @param aRequest + * The request that should block onload. + */ + void blockOnload(in imgIRequest aRequest); + + /** + * unblockOnload + * Called when it is appropriate to unblock onload for the given + * imgIRequest. + * + * @param aRequest + * The request that should unblock onload. + */ + void unblockOnload(in imgIRequest aRequest); +}; diff --git a/image/src/imgRequest.cpp b/image/src/imgRequest.cpp index 87717271adcb..5da6e737deb2 100644 --- a/image/src/imgRequest.cpp +++ b/image/src/imgRequest.cpp @@ -83,7 +83,7 @@ imgRequest::imgRequest() : mValidator(nullptr), mImageSniffers("image-sniffing-services"), mInnerWindowId(0), mCORSMode(imgIRequest::CORS_NONE), mDecodeRequested(false), mIsMultiPartChannel(false), mGotData(false), - mIsInCache(false) + mIsInCache(false), mBlockingOnload(false) { // Register our pref observers if we haven't yet. if (NS_UNLIKELY(!gInitializedPrefCaches)) { @@ -278,6 +278,18 @@ void imgRequest::Cancel(nsresult aStatus) LOG_SCOPE(gImgLog, "imgRequest::Cancel"); imgStatusTracker& statusTracker = GetStatusTracker(); + + if (mBlockingOnload) { + mBlockingOnload = false; + + statusTracker.RecordUnblockOnload(); + + nsTObserverArray::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + statusTracker.SendUnblockOnload(iter.GetNext()); + } + } + statusTracker.RecordCancel(); RemoveFromCache(); @@ -511,11 +523,24 @@ NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request) "OnStartDecode callback before we've created our image"); - mImage->GetStatusTracker().RecordStartDecode(); + imgStatusTracker& tracker = mImage->GetStatusTracker(); + tracker.RecordStartDecode(); nsTObserverArray::ForwardIterator iter(mObservers); while (iter.HasMore()) { - mImage->GetStatusTracker().SendStartDecode(iter.GetNext()); + tracker.SendStartDecode(iter.GetNext()); + } + + if (!mIsMultiPartChannel) { + MOZ_ASSERT(!mBlockingOnload); + mBlockingOnload = true; + + tracker.RecordBlockOnload(); + + nsTObserverArray::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + tracker.SendBlockOnload(iter.GetNext()); + } } /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which @@ -602,11 +627,23 @@ NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request, NS_ABORT_IF_FALSE(mImage, "OnStopFrame callback before we've created our image"); - mImage->GetStatusTracker().RecordStopFrame(frame); + imgStatusTracker& tracker = mImage->GetStatusTracker(); + tracker.RecordStopFrame(frame); nsTObserverArray::ForwardIterator iter(mObservers); while (iter.HasMore()) { - mImage->GetStatusTracker().SendStopFrame(iter.GetNext(), frame); + tracker.SendStopFrame(iter.GetNext(), frame); + } + + if (mBlockingOnload) { + mBlockingOnload = false; + + tracker.RecordUnblockOnload(); + + nsTObserverArray::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + tracker.SendUnblockOnload(iter.GetNext()); + } } return NS_OK; @@ -620,11 +657,29 @@ NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request, NS_ABORT_IF_FALSE(mImage, "OnDataContainer callback before we've created our image"); - mImage->GetStatusTracker().RecordStopContainer(image); + imgStatusTracker& tracker = mImage->GetStatusTracker(); + tracker.RecordStopContainer(image); nsTObserverArray::ForwardIterator iter(mObservers); while (iter.HasMore()) { - mImage->GetStatusTracker().SendStopContainer(iter.GetNext(), image); + tracker.SendStopContainer(iter.GetNext(), image); + } + + // This is really hacky. We need to handle the case where we start decoding, + // block onload, but then hit an error before we get to our first frame. In + // theory we would just hook in at OnStopDecode, but OnStopDecode is broken + // until we fix bug 505385. OnStopContainer is actually going away at that + // point. So for now we take advantage of the fact that OnStopContainer is + // always fired in the decoders at the same time as OnStopDecode. + if (mBlockingOnload) { + mBlockingOnload = false; + + tracker.RecordUnblockOnload(); + + nsTObserverArray::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + tracker.SendUnblockOnload(iter.GetNext()); + } } return NS_OK; diff --git a/image/src/imgRequest.h b/image/src/imgRequest.h index 3c2d1bc57c4c..467991a7e22a 100644 --- a/image/src/imgRequest.h +++ b/image/src/imgRequest.h @@ -230,6 +230,7 @@ private: bool mIsMultiPartChannel : 1; bool mGotData : 1; bool mIsInCache : 1; + bool mBlockingOnload : 1; }; #endif diff --git a/image/src/imgRequestProxy.cpp b/image/src/imgRequestProxy.cpp index e670a79d2e82..ba6f46b823a8 100644 --- a/image/src/imgRequestProxy.cpp +++ b/image/src/imgRequestProxy.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "imgRequestProxy.h" +#include "imgIOnloadBlocker.h" #include "nsIInputStream.h" #include "nsIComponentManager.h" @@ -771,6 +772,34 @@ void imgRequestProxy::OnStopRequest(bool lastPart) } } +void imgRequestProxy::BlockOnload() +{ +#ifdef PR_LOGGING + nsCAutoString name; + GetName(name); + LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::BlockOnload", "name", name.get()); +#endif + + nsCOMPtr blocker = do_QueryInterface(mListener); + if (blocker) { + blocker->BlockOnload(this); + } +} + +void imgRequestProxy::UnblockOnload() +{ +#ifdef PR_LOGGING + nsCAutoString name; + GetName(name); + LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::UnblockOnload", "name", name.get()); +#endif + + nsCOMPtr blocker = do_QueryInterface(mListener); + if (blocker) { + blocker->UnblockOnload(this); + } +} + void imgRequestProxy::NullOutListener() { // If we have animation consumers, then they don't matter anymore diff --git a/image/src/imgRequestProxy.h b/image/src/imgRequestProxy.h index 316c0e9d87f4..f9bfdb41ea3b 100644 --- a/image/src/imgRequestProxy.h +++ b/image/src/imgRequestProxy.h @@ -153,6 +153,10 @@ protected: void OnStartRequest(); void OnStopRequest(bool aLastPart); + /* non-virtual imgIOnloadBlocker methods */ + void BlockOnload(); + void UnblockOnload(); + /* Finish up canceling ourselves */ void DoCancel(nsresult status); diff --git a/image/src/imgStatusTracker.cpp b/image/src/imgStatusTracker.cpp index f2003a4acdfe..4a57887255eb 100644 --- a/image/src/imgStatusTracker.cpp +++ b/image/src/imgStatusTracker.cpp @@ -200,6 +200,10 @@ imgStatusTracker::SyncNotify(imgRequestProxy* proxy) if (mState & stateDecodeStarted) proxy->OnStartDecode(); + // BlockOnload + if (mState & stateBlockingOnload) + proxy->BlockOnload(); + if (mImage) { PRInt16 imageType = mImage->GetType(); // Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame) @@ -483,6 +487,7 @@ imgStatusTracker::RecordStartRequest() mState &= ~stateDecodeStarted; mState &= ~stateDecodeStopped; mState &= ~stateRequestStopped; + mState &= ~stateBlockingOnload; mState |= stateRequestStarted; } @@ -515,3 +520,33 @@ imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsres aProxy->OnStopRequest(aLastPart); } } + +void +imgStatusTracker::RecordBlockOnload() +{ + MOZ_ASSERT(!(mState & stateBlockingOnload)); + mState |= stateBlockingOnload; +} + +void +imgStatusTracker::SendBlockOnload(imgRequestProxy* aProxy) +{ + if (!aProxy->NotificationsDeferred()) { + aProxy->BlockOnload(); + } +} + +void +imgStatusTracker::RecordUnblockOnload() +{ + MOZ_ASSERT(mState & stateBlockingOnload); + mState &= ~stateBlockingOnload; +} + +void +imgStatusTracker::SendUnblockOnload(imgRequestProxy* aProxy) +{ + if (!aProxy->NotificationsDeferred()) { + aProxy->UnblockOnload(); + } +} diff --git a/image/src/imgStatusTracker.h b/image/src/imgStatusTracker.h index 25f6adb39406..0fe1bc003224 100644 --- a/image/src/imgStatusTracker.h +++ b/image/src/imgStatusTracker.h @@ -31,7 +31,8 @@ enum { stateDecodeStarted = PR_BIT(2), stateDecodeStopped = PR_BIT(3), stateFrameStopped = PR_BIT(4), - stateRequestStopped = PR_BIT(5) + stateRequestStopped = PR_BIT(5), + stateBlockingOnload = PR_BIT(6) }; /* @@ -141,6 +142,15 @@ public: void RecordStopRequest(bool aLastPart, nsresult aStatus); void SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus); + /* non-virtual imgIOnloadBlocker methods */ + // NB: If UnblockOnload is sent, and then we are asked to replay the + // notifications, we will not send a BlockOnload/UnblockOnload pair. This + // is different from all the other notifications. + void RecordBlockOnload(); + void SendBlockOnload(imgRequestProxy* aProxy); + void RecordUnblockOnload(); + void SendUnblockOnload(imgRequestProxy* aProxy); + private: friend class imgStatusNotifyRunnable; friend class imgRequestNotifyRunnable; diff --git a/image/test/unit/image_load_helpers.js b/image/test/unit/image_load_helpers.js index 367b5f8f2c12..44a02b1a12cb 100644 --- a/image/test/unit/image_load_helpers.js +++ b/image/test/unit/image_load_helpers.js @@ -75,7 +75,7 @@ function ImageListener(start_callback, stop_callback) // We have to cancel the request when we're done with it to break any // reference loops! - aRequest.cancel(0); + aRequest.cancelAndForgetObserver(0); this.state |= STOP_REQUEST;