author | Josh Matthews <josh@joshmatthews.net> |
Thu, 11 Oct 2012 21:58:24 -0400 | |
changeset 117972 | 2adc0ce03dba4ba6548c1a0673a4c9e4d18b473a |
parent 117966 | d35c748b03b0f222767442747c7eab9fa911e9e4 |
child 117973 | ea7bf4553d66eb343c0f6e9a7cc071d1eb54c40c |
push id | 1997 |
push user | akeybl@mozilla.com |
push date | Mon, 07 Jan 2013 21:25:26 +0000 |
treeherder | mozilla-beta@4baf45cdcf21 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 585385 |
milestone | 19.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -59,17 +59,17 @@ class nsIScriptSecurityManager; class nsTextFragment; class nsIJSContextStack; class nsIThreadJSContextStack; class nsIParser; class nsIParserService; class nsIIOService; class nsIURI; class imgIContainer; -class imgINotificationObserver; +class imgIDecoderObserver; class imgIRequest; class imgILoader; class imgICache; class nsIImageLoadingContent; class nsIDOMHTMLFormElement; class nsIDOMDocument; class nsIConsoleService; class nsIStringBundleService; @@ -656,17 +656,17 @@ public: * @param aObserver the observer for the image load * @param aLoadFlags the load flags to use. See nsIRequest * @return the imgIRequest for the image load */ static nsresult LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument, nsIPrincipal* aLoadingPrincipal, nsIURI* aReferrer, - imgINotificationObserver* aObserver, + imgIDecoderObserver* aObserver, int32_t aLoadFlags, imgIRequest** aRequest); /** * Obtain an image loader that respects the given document/channel's privacy status. * Null document/channel arguments return the public image loader. */ static imgILoader* GetImgLoaderForDocument(nsIDocument* aDoc);
--- a/content/base/public/nsIImageLoadingContent.idl +++ b/content/base/public/nsIImageLoadingContent.idl @@ -1,14 +1,14 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "imgINotificationObserver.idl" +#include "imgIDecoderObserver.idl" interface imgIRequest; interface nsIChannel; interface nsIStreamListener; interface nsIURI; interface nsIDocument; interface nsIFrame; @@ -29,18 +29,18 @@ interface nsIFrame; * observers to check which request they are getting notifications for. * * Observers added in mid-load will not get any notifications they * missed. We should NOT freeze this interface without considering * this issue. (It could be that the image status on imgIRequest is * sufficient, when combined with the imageBlockingStatus information.) */ -[scriptable, builtinclass, uuid(497bfb9b-d996-4d1e-a647-8137b0cfc876)] -interface nsIImageLoadingContent : imgINotificationObserver +[scriptable, uuid(4bf1a7c5-6edb-4191-a257-e31a90f6aa85)] +interface nsIImageLoadingContent : imgIDecoderObserver { /** * Request types. Image loading content nodes attempt to do atomic * image changes when the image url is changed. This means that * when the url changes the new image load will start, but the old * image will remain the "current" request until the new image is * fully loaded. At that point, the old "current" request will be * discarded and the "pending" request will become "current". @@ -70,24 +70,24 @@ interface nsIImageLoadingContent : imgIN * Notifications from ongoing image loads will be passed to all * registered observers. Notifications for all request types, * current and pending, will be passed through. * * @param aObserver the observer to register * * @throws NS_ERROR_OUT_OF_MEMORY */ - void addObserver(in imgINotificationObserver aObserver); + void addObserver(in imgIDecoderObserver aObserver); /** * Used to unregister an image decoder observer. * * @param aObserver the observer to unregister */ - void removeObserver(in imgINotificationObserver aObserver); + void removeObserver(in imgIDecoderObserver aObserver); /** * Accessor to get the image requests * * @param aRequestType a value saying which request is wanted * * @return the imgIRequest object (may be null, even when no error * is thrown)
--- a/content/base/src/Makefile.in +++ b/content/base/src/Makefile.in @@ -23,16 +23,17 @@ EXPORTS = \ nsGkAtomList.h \ nsGkAtoms.h \ nsNodeInfoManager.h \ nsNodeUtils.h \ nsPropertyTable.h \ nsRange.h \ nsScriptLoader.h \ nsStubDocumentObserver.h \ + nsStubImageDecoderObserver.h \ nsStubMutationObserver.h \ nsTextFragment.h \ mozAutoDocUpdate.h \ nsFrameMessageManager.h \ nsAttrAndChildArray.h \ nsAttrValue.h \ nsAttrValueInlines.h \ nsCrossSiteListenerProxy.h \ @@ -103,16 +104,17 @@ CPPSRCS = \ nsObjectLoadingContent.cpp \ nsPlainTextSerializer.cpp \ nsPropertyTable.cpp \ nsRange.cpp \ nsReferencedElement.cpp \ nsScriptElement.cpp \ nsScriptLoader.cpp \ nsStubDocumentObserver.cpp \ + nsStubImageDecoderObserver.cpp \ nsStubMutationObserver.cpp \ nsStyledElement.cpp \ nsStyleLinkElement.cpp \ nsSyncLoadService.cpp \ nsTextFragment.cpp \ nsTextNode.cpp \ nsTraversal.cpp \ nsTreeSanitizer.cpp \
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -46,17 +46,17 @@ #include "nsIContentSink.h" #include "nsContentList.h" #include "nsIHTMLDocument.h" #include "nsIDOMHTMLFormElement.h" #include "nsIDOMHTMLElement.h" #include "nsIForm.h" #include "nsIFormControl.h" #include "nsGkAtoms.h" -#include "imgINotificationObserver.h" +#include "imgIDecoderObserver.h" #include "imgIRequest.h" #include "imgIContainer.h" #include "imgILoader.h" #include "nsDocShellCID.h" #include "nsIImageLoadingContent.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsILoadGroup.h" @@ -2729,17 +2729,17 @@ nsContentUtils::IsImageInCache(nsIURI* a nsresult rv = cache->FindEntryProperties(aURI, getter_AddRefs(props)); return (NS_SUCCEEDED(rv) && props); } // static nsresult nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument, nsIPrincipal* aLoadingPrincipal, nsIURI* aReferrer, - imgINotificationObserver* aObserver, int32_t aLoadFlags, + imgIDecoderObserver* aObserver, int32_t aLoadFlags, imgIRequest** aRequest) { NS_PRECONDITION(aURI, "Must have a URI"); NS_PRECONDITION(aLoadingDocument, "Must have a document"); NS_PRECONDITION(aLoadingPrincipal, "Must have a principal"); NS_PRECONDITION(aRequest, "Null out param"); imgILoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument); @@ -2772,17 +2772,17 @@ nsContentUtils::LoadImage(nsIURI* aURI, // XXXbz using "documentURI" for the initialDocumentURI is not quite // right, but the best we can do here... return imgLoader->LoadImage(aURI, /* uri to load */ documentURI, /* initialDocumentURI */ aReferrer, /* referrer */ aLoadingPrincipal, /* loading principal */ loadGroup, /* loadgroup */ - aObserver, /* imgINotificationObserver */ + aObserver, /* imgIDecoderObserver */ aLoadingDocument, /* uniquification key */ aLoadFlags, /* load flags */ nullptr, /* cache key */ nullptr, /* existing request*/ channelPolicy, /* CSP info */ aRequest); }
--- a/content/base/src/nsGenConImageContent.cpp +++ b/content/base/src/nsGenConImageContent.cpp @@ -43,20 +43,21 @@ public: private: virtual ~nsGenConImageContent(); public: NS_DECL_ISUPPORTS_INHERITED }; -NS_IMPL_ISUPPORTS_INHERITED3(nsGenConImageContent, +NS_IMPL_ISUPPORTS_INHERITED4(nsGenConImageContent, nsXMLElement, nsIImageLoadingContent, - imgINotificationObserver, + imgIContainerObserver, + imgIDecoderObserver, imgIOnloadBlocker) nsresult NS_NewGenConImageContent(nsIContent** aResult, already_AddRefed<nsINodeInfo> aNodeInfo, imgIRequest* aImageRequest) { NS_PRECONDITION(aImageRequest, "Must have request!"); nsGenConImageContent *it = new nsGenConImageContent(aNodeInfo);
--- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -117,73 +117,124 @@ nsImageLoadingContent::~nsImageLoadingCo observer = next) { \ next = observer->mNext; \ if (observer->mObserver) { \ observer->mObserver->func_; \ } \ } \ PR_END_MACRO + /* - * imgINotificationObserver impl + * imgIContainerObserver impl */ NS_IMETHODIMP -nsImageLoadingContent::Notify(imgIRequest* aRequest, - int32_t aType, - const nsIntRect* aData) +nsImageLoadingContent::FrameChanged(imgIRequest* aRequest, + imgIContainer* aContainer, + const nsIntRect* aDirtyRect) { - if (aType == imgINotificationObserver::IS_ANIMATED) { - return OnImageIsAnimated(aRequest); - } - - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - // We should definitely have a request here - NS_ABORT_IF_FALSE(aRequest, "no request?"); - - NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest, - "Unknown request"); - } - + LOOP_OVER_OBSERVERS(FrameChanged(aRequest, aContainer, aDirtyRect)); + return NS_OK; +} + +/* + * imgIDecoderObserver impl + */ +NS_IMETHODIMP +nsImageLoadingContent::OnStartRequest(imgIRequest* aRequest) +{ NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); - LOOP_OVER_OBSERVERS(Notify(aRequest, aType, aData)); - - if (aType == imgINotificationObserver::SIZE_AVAILABLE) { - // Have to check for state changes here, since we might have been in - // the LOADING state before. - UpdateImageState(true); - } + LOOP_OVER_OBSERVERS(OnStartRequest(aRequest)); + return NS_OK; +} - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - uint32_t reqStatus; - aRequest->GetImageStatus(&reqStatus); - nsresult status = - reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; - return OnStopRequest(aRequest, status); - } +NS_IMETHODIMP +nsImageLoadingContent::OnStartDecode(imgIRequest* aRequest) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); + LOOP_OVER_OBSERVERS(OnStartDecode(aRequest)); return NS_OK; } -nsresult -nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, - nsresult aStatus) +NS_IMETHODIMP +nsImageLoadingContent::OnStartContainer(imgIRequest* aRequest, + imgIContainer* aContainer) { - uint32_t oldStatus; - aRequest->GetImageStatus(&oldStatus); + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); + + LOOP_OVER_OBSERVERS(OnStartContainer(aRequest, aContainer)); + + // Have to check for state changes here, since we might have been in + // the LOADING state before. + UpdateImageState(true); + return NS_OK; +} + +NS_IMETHODIMP +nsImageLoadingContent::OnStartFrame(imgIRequest* aRequest, + uint32_t aFrame) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); + + LOOP_OVER_OBSERVERS(OnStartFrame(aRequest, aFrame)); + return NS_OK; +} + +NS_IMETHODIMP +nsImageLoadingContent::OnDataAvailable(imgIRequest* aRequest, + bool aCurrentFrame, + const nsIntRect* aRect) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); + + LOOP_OVER_OBSERVERS(OnDataAvailable(aRequest, aCurrentFrame, aRect)); + return NS_OK; +} - //XXXjdm This occurs when we have a pending request created, then another - // pending request replaces it before the first one is finished. - // This begs the question of what the correct behaviour is; we used - // to not have to care because we ran this code in OnStopDecode which - // wasn't called when the first request was cancelled. For now, I choose - // to punt when the given request doesn't appear to have terminated in - // an expected state. - if (!(oldStatus & (imgIRequest::STATUS_ERROR | imgIRequest::STATUS_LOAD_COMPLETE))) - return NS_OK; +NS_IMETHODIMP +nsImageLoadingContent::OnStopFrame(imgIRequest* aRequest, + uint32_t aFrame) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); + + LOOP_OVER_OBSERVERS(OnStopFrame(aRequest, aFrame)); + return NS_OK; +} + +NS_IMETHODIMP +nsImageLoadingContent::OnStopContainer(imgIRequest* aRequest, + imgIContainer* aContainer) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); + + LOOP_OVER_OBSERVERS(OnStopContainer(aRequest, aContainer)); + return NS_OK; +} + +// Warning - This isn't actually fired when decode is complete. Rather, it's +// fired when load is complete. See bug 505385, and in the mean time use +// OnStopContainer. +NS_IMETHODIMP +nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest, + nsresult aStatus, + const PRUnichar* aStatusArg) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); + + // We should definitely have a request here + NS_ABORT_IF_FALSE(aRequest, "no request?"); + + NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest, + "Unknown request"); + LOOP_OVER_OBSERVERS(OnStopDecode(aRequest, aStatus, aStatusArg)); + + // XXXbholley - When we fix bug 505385, everything here should go in + // OnStopRequest. // Our state may change. Watch it. AutoStateChanger changer(this, true); // If the pending request is loaded, switch to it. if (aRequest == mPendingRequest) { MakePendingRequestCurrent(); } @@ -228,28 +279,48 @@ nsImageLoadingContent::OnStopRequest(img } nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement()); return NS_OK; } -nsresult +NS_IMETHODIMP +nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, bool aLastPart) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); + + LOOP_OVER_OBSERVERS(OnStopRequest(aRequest, aLastPart)); + + return NS_OK; +} + +NS_IMETHODIMP nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest) { bool* requestFlag = GetRegisteredFlagForRequest(aRequest); if (requestFlag) { nsLayoutUtils::RegisterImageRequest(GetFramePresContext(), aRequest, requestFlag); } return NS_OK; } +NS_IMETHODIMP +nsImageLoadingContent::OnDiscard(imgIRequest *aRequest) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); + + LOOP_OVER_OBSERVERS(OnDiscard(aRequest)); + + return NS_OK; +} + /* * nsIImageLoadingContent impl */ NS_IMETHODIMP nsImageLoadingContent::GetLoadingEnabled(bool *aLoadingEnabled) { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); @@ -275,17 +346,17 @@ nsImageLoadingContent::GetImageBlockingS NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); NS_PRECONDITION(aStatus, "Null out param"); *aStatus = mImageBlockingStatus; return NS_OK; } NS_IMETHODIMP -nsImageLoadingContent::AddObserver(imgINotificationObserver* aObserver) +nsImageLoadingContent::AddObserver(imgIDecoderObserver* aObserver) { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); NS_ENSURE_ARG_POINTER(aObserver); if (!mObserverList.mObserver) { mObserverList.mObserver = aObserver; // Don't touch the linking of the list! @@ -303,17 +374,17 @@ nsImageLoadingContent::AddObserver(imgIN if (! observer->mNext) { return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; } NS_IMETHODIMP -nsImageLoadingContent::RemoveObserver(imgINotificationObserver* aObserver) +nsImageLoadingContent::RemoveObserver(imgIDecoderObserver* aObserver) { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); NS_ENSURE_ARG_POINTER(aObserver); if (mObserverList.mObserver == aObserver) { mObserverList.mObserver = nullptr; // Don't touch the linking of the list!
--- a/content/base/src/nsImageLoadingContent.h +++ b/content/base/src/nsImageLoadingContent.h @@ -8,17 +8,18 @@ * A base class which implements nsIImageLoadingContent and can be * subclassed by various content nodes that want to provide image * loading functionality (eg <img>, <object>, etc). */ #ifndef nsImageLoadingContent_h__ #define nsImageLoadingContent_h__ -#include "imgINotificationObserver.h" +#include "imgIContainerObserver.h" +#include "imgIDecoderObserver.h" #include "imgIOnloadBlocker.h" #include "mozilla/CORSMode.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" // NS_CONTENT_DELETE_LIST_MEMBER #include "nsEventStates.h" #include "nsIImageLoadingContent.h" #include "nsIRequest.h" @@ -30,17 +31,18 @@ class nsIIOService; class nsImageLoadingContent : public nsIImageLoadingContent, public imgIOnloadBlocker { /* METHODS */ public: nsImageLoadingContent(); virtual ~nsImageLoadingContent(); - NS_DECL_IMGINOTIFICATIONOBSERVER + NS_DECL_IMGICONTAINEROBSERVER + NS_DECL_IMGIDECODEROBSERVER NS_DECL_NSIIMAGELOADINGCONTENT NS_DECL_IMGIONLOADBLOCKER protected: /** * LoadImage is called by subclasses when the appropriate * attributes (eg 'src' for <img> tags) change. The string passed * in is the new uri string; this consolidates the code for getting @@ -154,37 +156,34 @@ protected: */ virtual mozilla::CORSMode GetCORSMode(); // Subclasses are *required* to call BindToTree/UnbindFromTree. void BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers); void UnbindFromTree(bool aDeep, bool aNullParent); - nsresult OnStopRequest(imgIRequest* aRequest, nsresult aStatus); - nsresult OnImageIsAnimated(imgIRequest *aRequest); - private: /** * Struct used to manage the image observers. */ struct ImageObserver { - ImageObserver(imgINotificationObserver* aObserver) : + ImageObserver(imgIDecoderObserver* aObserver) : mObserver(aObserver), mNext(nullptr) { MOZ_COUNT_CTOR(ImageObserver); } ~ImageObserver() { MOZ_COUNT_DTOR(ImageObserver); NS_CONTENT_DELETE_LIST_MEMBER(ImageObserver, this, mNext); } - nsCOMPtr<imgINotificationObserver> mObserver; + nsCOMPtr<imgIDecoderObserver> mObserver; ImageObserver* mNext; }; /** * Struct to report state changes */ struct AutoStateChanger { AutoStateChanger(nsImageLoadingContent* aImageContent,
--- a/content/base/src/nsObjectLoadingContent.h +++ b/content/base/src/nsObjectLoadingContent.h @@ -93,16 +93,23 @@ class nsObjectLoadingContent : public ns NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSIFRAMELOADEROWNER NS_DECL_NSIOBJECTLOADINGCONTENT NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSICHANNELEVENTSINK +#ifdef HAVE_CPP_AMBIGUITY_RESOLVING_USING + // Fix gcc compile warnings + using nsImageLoadingContent::OnStartRequest; + using nsImageLoadingContent::OnDataAvailable; + using nsImageLoadingContent::OnStopRequest; +#endif + /** * Object state. This is a bitmask of NS_EVENT_STATEs epresenting the * current state of the object. */ nsEventStates ObjectState() const; ObjectType Type() { return mType; }
new file mode 100644 --- /dev/null +++ b/content/base/src/nsStubImageDecoderObserver.cpp @@ -0,0 +1,89 @@ +/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsStubImageDecoderObserver.h" + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnStartRequest(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnStartDecode(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnStartContainer(imgIRequest *aRequest, + imgIContainer *aContainer) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnStartFrame(imgIRequest *aRequest, + uint32_t aFrame) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnDataAvailable(imgIRequest *aRequest, + bool aCurrentFrame, + const nsIntRect * aRect) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnStopFrame(imgIRequest *aRequest, + uint32_t aFrame) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnStopContainer(imgIRequest *aRequest, + imgIContainer *aContainer) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnStopDecode(imgIRequest *aRequest, + nsresult status, + const PRUnichar *statusArg) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnStopRequest(imgIRequest *aRequest, + bool aIsLastPart) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnDiscard(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::OnImageIsAnimated(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStubImageDecoderObserver::FrameChanged(imgIRequest* aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) +{ + return NS_OK; +}
new file mode 100644 --- /dev/null +++ b/content/base/src/nsStubImageDecoderObserver.h @@ -0,0 +1,35 @@ +/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ +/* 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/. */ + +/* + * nsStubImageDecoderObserver is an implementation of the imgIDecoderObserver + * interface (except for the methods on nsISupports) that is intended to be + * used as a base class within the content/layout library. All methods do + * nothing. + */ + +#ifndef nsStubImageDecoderObserver_h_ +#define nsStubImageDecoderObserver_h_ + +#include "imgIDecoderObserver.h" + +/** + * There are two advantages to inheriting from nsStubImageDecoderObserver + * rather than directly from imgIDecoderObserver: + * 1. smaller compiled code size (since there's no need for the code + * for the empty virtual function implementations for every + * imgIDecoderObserver implementation) + * 2. the performance of document's loop over observers benefits from + * the fact that more of the functions called are the same (which + * can reduce instruction cache misses and perhaps improve branch + * prediction) + */ +class nsStubImageDecoderObserver : public imgIDecoderObserver { +public: + NS_DECL_IMGICONTAINEROBSERVER + NS_DECL_IMGIDECODEROBSERVER +}; + +#endif /* !defined(nsStubImageDecoderObserver_h_) */
--- a/content/html/content/src/nsHTMLImageElement.cpp +++ b/content/html/content/src/nsHTMLImageElement.cpp @@ -24,17 +24,17 @@ #include "nsGUIEvent.h" #include "nsContentPolicyUtils.h" #include "nsIDOMWindow.h" #include "nsFocusManager.h" #include "imgIContainer.h" #include "imgILoader.h" #include "imgIRequest.h" -#include "imgINotificationObserver.h" +#include "imgIDecoderObserver.h" #include "nsILoadGroup.h" #include "nsRuleData.h" #include "nsIJSContextStack.h" #include "nsIDOMHTMLMapElement.h" #include "nsEventDispatcher.h" @@ -84,22 +84,23 @@ nsHTMLImageElement::~nsHTMLImageElement( NS_IMPL_ADDREF_INHERITED(nsHTMLImageElement, nsGenericElement) NS_IMPL_RELEASE_INHERITED(nsHTMLImageElement, nsGenericElement) 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, - imgIOnloadBlocker, - imgINotificationObserver) + imgIContainerObserver, + imgIOnloadBlocker) NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLImageElement, nsGenericHTMLElement) NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLImageElement) NS_IMPL_ELEMENT_CLONE(nsHTMLImageElement)
--- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -637,22 +637,23 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_ADDREF_INHERITED(nsHTMLInputElement, nsGenericElement) NS_IMPL_RELEASE_INHERITED(nsHTMLInputElement, nsGenericElement) 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, - imgINotificationObserver, + imgIDecoderObserver, nsIImageLoadingContent, + imgIContainerObserver, imgIOnloadBlocker, nsIDOMNSEditableElement, nsIConstraintValidation) NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLInputElement, nsGenericHTMLFormElement) NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLInputElement) // nsIConstraintValidation
--- a/content/html/content/src/nsHTMLObjectElement.cpp +++ b/content/html/content/src/nsHTMLObjectElement.cpp @@ -180,23 +180,24 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_ADDREF_INHERITED(nsHTMLObjectElement, nsGenericElement) NS_IMPL_RELEASE_INHERITED(nsHTMLObjectElement, nsGenericElement) DOMCI_NODE_DATA(HTMLObjectElement, nsHTMLObjectElement) NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLObjectElement) NS_HTML_CONTENT_INTERFACE_TABLE_BEGIN(nsHTMLObjectElement) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIDOMHTMLObjectElement) - NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, imgINotificationObserver) + NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, imgIDecoderObserver) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIRequestObserver) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIStreamListener) 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) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIConstraintValidation) NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIDOMGetSVGDocument) NS_OFFSET_AND_INTERFACE_TABLE_END NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLObjectElement, nsGenericHTMLFormElement) NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLObjectElement)
--- a/content/html/content/src/nsHTMLSharedObjectElement.cpp +++ b/content/html/content/src/nsHTMLSharedObjectElement.cpp @@ -229,18 +229,19 @@ nsHTMLSharedObjectElement::GetClassInfoI } NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLSharedObjectElement) NS_HTML_CONTENT_INTERFACE_TABLE_AMBIGUOUS_BEGIN(nsHTMLSharedObjectElement, nsIDOMHTMLAppletElement) NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIRequestObserver) NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIStreamListener) NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIFrameLoaderOwner) + NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, imgIContainerObserver) NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIObjectLoadingContent) - NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, imgINotificationObserver) + 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 NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE_AMBIGUOUS(nsHTMLSharedObjectElement, nsGenericHTMLElement, nsIDOMHTMLAppletElement)
--- a/content/html/content/test/test_bug389797.html +++ b/content/html/content/test/test_bug389797.html @@ -67,17 +67,17 @@ function HTML_TAG(aTagName, aImplClass) if (arguments.length > 3) { for (i = 0; i < arguments[3].length; ++i) { interfacesNonClassinfo[aTagName].push(arguments[3][i]); } } } const objectIfaces = [ - "imgINotificationObserver", "nsIRequestObserver", "nsIStreamListener", + "imgIDecoderObserver", "nsIRequestObserver", "nsIStreamListener", "nsIFrameLoaderOwner", "nsIObjectLoadingContent", "nsIInterfaceRequestor", "nsIChannelEventSink" ]; var objectIfaces2 = []; for each (var iface in objectIfaces) { objectIfaces2.push(iface); } @@ -155,19 +155,19 @@ HTML_TAG("head", "Head"); HTML_TAG("header", "") HTML_TAG("hgroup", "") HTML_TAG("hr", "HR"); HTML_TAG("html", "Html"); HTML_TAG("i", ""); HTML_TAG("iframe", "IFrame", [ "nsIDOMGetSVGDocument", "nsIDOMMozBrowserFrame" ], [ "nsIFrameLoaderOwner" ]); HTML_TAG("image", "Span"); -HTML_TAG("img", "Image", [], [ "imgINotificationObserver", +HTML_TAG("img", "Image", [], [ "imgIDecoderObserver", "nsIImageLoadingContent" ]); -HTML_TAG("input", "Input", [], [ "imgINotificationObserver", +HTML_TAG("input", "Input", [], [ "imgIDecoderObserver", "nsIImageLoadingContent", "nsIDOMNSEditableElement" ]); HTML_TAG("ins", "Mod"); HTML_TAG("kbd", ""); HTML_TAG("keygen", "Span"); HTML_TAG("label", "Label"); HTML_TAG("legend", "Legend"); HTML_TAG("li", "LI");
--- a/content/html/document/src/ImageDocument.cpp +++ b/content/html/document/src/ImageDocument.cpp @@ -13,17 +13,17 @@ #include "nsIDOMEvent.h" #include "nsIDOMKeyEvent.h" #include "nsIDOMMouseEvent.h" #include "nsIDOMEventListener.h" #include "nsGkAtoms.h" #include "imgIRequest.h" #include "imgILoader.h" #include "imgIContainer.h" -#include "imgINotificationObserver.h" +#include "nsStubImageDecoderObserver.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsStyleContext.h" #include "nsAutoPtr.h" #include "nsStyleSet.h" #include "nsIChannel.h" #include "nsIContentPolicy.h" #include "nsContentPolicyUtils.h" @@ -59,17 +59,17 @@ public: virtual ~ImageListener(); /* nsIRequestObserver */ NS_IMETHOD OnStartRequest(nsIRequest* request, nsISupports *ctxt); }; class ImageDocument : public MediaDocument , public nsIImageDocument - , public imgINotificationObserver + , public nsStubImageDecoderObserver , public nsIDOMEventListener { public: ImageDocument(); virtual ~ImageDocument(); NS_DECL_ISUPPORTS_INHERITED @@ -84,17 +84,22 @@ public: nsIContentSink* aSink = nullptr); virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject); virtual void Destroy(); virtual void OnPageShow(bool aPersisted, nsIDOMEventTarget* aDispatchStartTarget); NS_DECL_NSIIMAGEDOCUMENT - NS_DECL_IMGINOTIFICATIONOBSERVER + + // imgIDecoderObserver (override nsStubImageDecoderObserver) + NS_IMETHOD OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage); + NS_IMETHOD OnStopContainer(imgIRequest* aRequest, imgIContainer* aImage); + NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult aStatus, const PRUnichar *aStatusArg); + NS_IMETHOD OnDiscard(imgIRequest *aRequest); // nsIDOMEventListener NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent); NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ImageDocument, MediaDocument) friend class ImageListener; @@ -113,19 +118,16 @@ protected: float GetRatio() { return NS_MIN((float)mVisibleWidth / mImageWidth, (float)mVisibleHeight / mImageHeight); } void ResetZoomLevel(); float GetZoomLevel(); - nsresult OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage); - nsresult OnStopRequest(imgIRequest *aRequest, nsresult aStatus); - nsCOMPtr<nsIContent> mImageContent; int32_t mVisibleWidth; int32_t mVisibleHeight; int32_t mImageWidth; int32_t mImageHeight; bool mResizeImageByDefault; @@ -231,17 +233,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_ADDREF_INHERITED(ImageDocument, MediaDocument) NS_IMPL_RELEASE_INHERITED(ImageDocument, MediaDocument) DOMCI_NODE_DATA(ImageDocument, ImageDocument) NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(ImageDocument) NS_HTML_DOCUMENT_INTERFACE_TABLE_BEGIN(ImageDocument) NS_INTERFACE_TABLE_ENTRY(ImageDocument, nsIImageDocument) - NS_INTERFACE_TABLE_ENTRY(ImageDocument, imgINotificationObserver) + NS_INTERFACE_TABLE_ENTRY(ImageDocument, imgIDecoderObserver) + NS_INTERFACE_TABLE_ENTRY(ImageDocument, imgIContainerObserver) NS_INTERFACE_TABLE_ENTRY(ImageDocument, nsIDOMEventListener) NS_OFFSET_AND_INTERFACE_TABLE_END NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ImageDocument) NS_INTERFACE_MAP_END_INHERITING(MediaDocument) nsresult @@ -501,70 +504,46 @@ ImageDocument::ToggleImageSize() ResetZoomLevel(); ShrinkToFit(); } return NS_OK; } NS_IMETHODIMP -ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) -{ - if (aType == imgINotificationObserver::SIZE_AVAILABLE) { - nsCOMPtr<imgIContainer> image; - aRequest->GetImage(getter_AddRefs(image)); - return OnStartContainer(aRequest, image); - } - - if (aType == imgINotificationObserver::DECODE_COMPLETE) { - if (mImageContent) { - // Update the background-color of the image only after the - // image has been decoded to prevent flashes of just the - // background-color. - mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, - NS_LITERAL_STRING("decoded"), true); - } - } - - if (aType == imgINotificationObserver::DISCARD) { - // mImageContent can be null if the document is already destroyed - if (mImageContent) { - // Remove any decoded-related styling when the image is unloaded. - mImageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, - true); - } - } - - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - uint32_t reqStatus; - aRequest->GetImageStatus(&reqStatus); - nsresult status = - reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; - return OnStopRequest(aRequest, status); - } - - return NS_OK; -} - -nsresult ImageDocument::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage) { aImage->GetWidth(&mImageWidth); aImage->GetHeight(&mImageHeight); nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this, &ImageDocument::DefaultCheckOverflowing); nsContentUtils::AddScriptRunner(runnable); UpdateTitleAndCharset(); return NS_OK; } -nsresult -ImageDocument::OnStopRequest(imgIRequest *aRequest, - nsresult aStatus) +NS_IMETHODIMP +ImageDocument::OnStopContainer(imgIRequest* aRequest, imgIContainer* aImage) +{ + if (mImageContent) { + // Update the background-color of the image only after the + // image has been decoded to prevent flashes of just the + // background-color. + mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, + NS_LITERAL_STRING("decoded"), true); + } + + return NS_OK; +} + +NS_IMETHODIMP +ImageDocument::OnStopDecode(imgIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aStatusArg) { UpdateTitleAndCharset(); // mImageContent can be null if the document is already destroyed if (NS_FAILED(aStatus) && mStringBundle && mImageContent) { nsAutoCString src; mDocumentURI->GetSpec(src); NS_ConvertUTF8toUTF16 srcString(src); @@ -576,16 +555,28 @@ ImageDocument::OnStopRequest(imgIRequest mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, errorMsg, false); } return NS_OK; } NS_IMETHODIMP +ImageDocument::OnDiscard(imgIRequest *aRequest) +{ + // mImageContent can be null if the document is already destroyed + if (mImageContent) { + // Remove any decoded-related styling when the image is unloaded. + mImageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, + true); + } + return NS_OK; +} + +NS_IMETHODIMP ImageDocument::HandleEvent(nsIDOMEvent* aEvent) { nsAutoString eventType; aEvent->GetType(eventType); if (eventType.EqualsLiteral("resize")) { CheckOverflowing(false); } else if (eventType.EqualsLiteral("click") && mClickResizingEnabled) {
--- a/content/svg/content/src/nsSVGFilters.cpp +++ b/content/svg/content/src/nsSVGFilters.cpp @@ -5500,17 +5500,17 @@ NS_IMPL_RELEASE_INHERITED(nsSVGFEImageEl DOMCI_NODE_DATA(SVGFEImageElement, nsSVGFEImageElement) NS_INTERFACE_TABLE_HEAD(nsSVGFEImageElement) NS_NODE_INTERFACE_TABLE9(nsSVGFEImageElement, nsIDOMNode, nsIDOMElement, nsIDOMSVGElement, nsIDOMSVGFilterPrimitiveStandardAttributes, nsIDOMSVGFEImageElement, nsIDOMSVGURIReference, - imgINotificationObserver, nsIImageLoadingContent, + imgIDecoderObserver, nsIImageLoadingContent, imgIOnloadBlocker) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFEImageElement) NS_INTERFACE_MAP_END_INHERITING(nsSVGFEImageElementBase) //---------------------------------------------------------------------- // Implementation nsSVGFEImageElement::nsSVGFEImageElement(already_AddRefed<nsINodeInfo> aNodeInfo) @@ -5751,37 +5751,53 @@ nsSVGFEImageElement::GetPreserveAspectRa nsSVGElement::StringAttributesInfo nsSVGFEImageElement::GetStringInfo() { return StringAttributesInfo(mStringAttributes, sStringInfo, ArrayLength(sStringInfo)); } //---------------------------------------------------------------------- -// imgINotificationObserver methods +// imgIDecoderObserver methods + +NS_IMETHODIMP +nsSVGFEImageElement::OnStopDecode(imgIRequest *aRequest, + nsresult status, + const PRUnichar *statusArg) +{ + nsresult rv = + nsImageLoadingContent::OnStopDecode(aRequest, status, statusArg); + Invalidate(); + return rv; +} NS_IMETHODIMP -nsSVGFEImageElement::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) -{ - nsresult rv = nsImageLoadingContent::Notify(aRequest, aType, aData); - - if (aType == imgINotificationObserver::SIZE_AVAILABLE) { - // Request a decode - nsCOMPtr<imgIContainer> container; - aRequest->GetImage(getter_AddRefs(container)); - NS_ABORT_IF_FALSE(container, "who sent the notification then?"); - container->StartDecoding(); - } - - if (aType == imgINotificationObserver::LOAD_COMPLETE || - aType == imgINotificationObserver::FRAME_UPDATE || - aType == imgINotificationObserver::SIZE_AVAILABLE) { - Invalidate(); - } - +nsSVGFEImageElement::FrameChanged(imgIRequest* aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) +{ + nsresult rv = + nsImageLoadingContent::FrameChanged(aRequest, aContainer, aDirtyRect); + Invalidate(); + return rv; +} + +NS_IMETHODIMP +nsSVGFEImageElement::OnStartContainer(imgIRequest *aRequest, + imgIContainer *aContainer) +{ + nsresult rv = + nsImageLoadingContent::OnStartContainer(aRequest, aContainer); + + // Request a decode + NS_ABORT_IF_FALSE(aContainer, "who sent the notification then?"); + aContainer->StartDecoding(); + + // We have a size - invalidate + Invalidate(); return rv; } //---------------------------------------------------------------------- // helper methods void nsSVGFEImageElement::Invalidate()
--- a/content/svg/content/src/nsSVGFilters.h +++ b/content/svg/content/src/nsSVGFilters.h @@ -262,17 +262,26 @@ public: virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, const nsAttrValue* aValue, bool aNotify); virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers); virtual void UnbindFromTree(bool aDeep, bool aNullParent); virtual nsEventStates IntrinsicState() const; - NS_IMETHODIMP Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData); + // imgIDecoderObserver + NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status, + const PRUnichar *statusArg); + // imgIContainerObserver + NS_IMETHOD FrameChanged(imgIRequest* aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect); + // imgIContainerObserver + NS_IMETHOD OnStartContainer(imgIRequest *aRequest, + imgIContainer *aContainer); void MaybeLoadSVGImage(); virtual nsXPCClassInfo* GetClassInfo(); virtual nsIDOMNode* AsDOMNode() { return this; } private: // Invalidate users of the filter containing this element.
--- a/content/svg/content/src/nsSVGImageElement.cpp +++ b/content/svg/content/src/nsSVGImageElement.cpp @@ -5,17 +5,17 @@ #include "mozilla/Util.h" #include "nsSVGImageElement.h" #include "nsCOMPtr.h" #include "nsIURI.h" #include "nsNetUtil.h" #include "imgIContainer.h" -#include "imgINotificationObserver.h" +#include "imgIDecoderObserver.h" #include "gfxContext.h" using namespace mozilla; nsSVGElement::LengthInfo nsSVGImageElement::sLengthInfo[4] = { { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, @@ -37,17 +37,17 @@ NS_IMPL_ADDREF_INHERITED(nsSVGImageEleme NS_IMPL_RELEASE_INHERITED(nsSVGImageElement,nsSVGImageElementBase) DOMCI_NODE_DATA(SVGImageElement, nsSVGImageElement) NS_INTERFACE_TABLE_HEAD(nsSVGImageElement) NS_NODE_INTERFACE_TABLE9(nsSVGImageElement, nsIDOMNode, nsIDOMElement, nsIDOMSVGElement, nsIDOMSVGTests, nsIDOMSVGImageElement, - nsIDOMSVGURIReference, imgINotificationObserver, + nsIDOMSVGURIReference, imgIDecoderObserver, nsIImageLoadingContent, imgIOnloadBlocker) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGImageElement) NS_INTERFACE_MAP_END_INHERITING(nsSVGImageElementBase) //---------------------------------------------------------------------- // Implementation nsSVGImageElement::nsSVGImageElement(already_AddRefed<nsINodeInfo> aNodeInfo)
--- a/image/decoders/nsICODecoder.cpp +++ b/image/decoders/nsICODecoder.cpp @@ -453,20 +453,20 @@ nsICODecoder::WriteInternal(const char* // If we have a BMP if (!mIsPNG && mContainedDecoder && mPos >= mImageOffset + BITMAPINFOSIZE) { uint16_t numColors = GetNumColors(); if (numColors == (uint16_t)-1) { PostDataError(); return; } // Feed the actual image data (not including headers) into the BMP decoder - uint32_t bmpDataOffset = mDirEntry.mImageOffset + BITMAPINFOSIZE; - uint32_t bmpDataEnd = mDirEntry.mImageOffset + BITMAPINFOSIZE + - static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetCompressedImageSize() + - 4 * numColors; + int32_t bmpDataOffset = mDirEntry.mImageOffset + BITMAPINFOSIZE; + int32_t bmpDataEnd = mDirEntry.mImageOffset + BITMAPINFOSIZE + + static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetCompressedImageSize() + + 4 * numColors; // If we are feeding in the core image data, but we have not yet // reached the ICO's 'AND buffer mask' if (mPos >= bmpDataOffset && mPos < bmpDataEnd) { // Figure out how much data the BMP decoder wants uint32_t toFeed = bmpDataEnd - mPos; if (toFeed > aCount) {
--- a/image/encoders/ico/nsICOEncoder.cpp +++ b/image/encoders/ico/nsICOEncoder.cpp @@ -273,17 +273,17 @@ nsICOEncoder::ParseOptions(const nsAStri // From format: format=<png|bmp>;bpp=<bpp_value> // to format: [0] = format=<png|bmp>, [1] = bpp=<bpp_value> nsTArray<nsCString> nameValuePairs; if (!ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs)) { return NS_ERROR_INVALID_ARG; } // For each name/value pair in the set - for (unsigned i = 0; i < nameValuePairs.Length(); ++i) { + for (int i = 0; i < nameValuePairs.Length(); ++i) { // Split the name value pair [0] = name, [1] = value nsTArray<nsCString> nameValuePair; if (!ParseString(nameValuePairs[i], '=', nameValuePair)) { return NS_ERROR_INVALID_ARG; } if (nameValuePair.Length() != 2) { return NS_ERROR_INVALID_ARG;
--- a/image/public/Makefile.in +++ b/image/public/Makefile.in @@ -18,17 +18,15 @@ EXPORTS = ImageLogging.h XPIDLSRCS = \ imgICache.idl \ imgIContainer.idl \ imgIContainerDebug.idl \ imgIContainerObserver.idl \ imgIDecoderObserver.idl \ imgIEncoder.idl \ imgILoader.idl \ - imgINotificationObserver.idl \ imgIOnloadBlocker.idl \ imgIRequest.idl \ - imgIScriptedNotificationObserver.idl \ imgITools.idl \ $(NULL) include $(topsrcdir)/config/rules.mk
--- a/image/public/imgIContainerObserver.idl +++ b/image/public/imgIContainerObserver.idl @@ -5,22 +5,25 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" %{C++ #include "nsRect.h" %} +interface imgIContainer; interface imgIRequest; [ptr] native nsIntRect(nsIntRect); /** * imgIContainerObserver interface * * @author Stuart Parmenter <pavlov@netscape.com> * @version 0.1 */ -[scriptable, uuid(f4aaf410-e88f-4036-b91c-610c9c11d825)] +[scriptable, uuid(f01efdb3-4b20-4313-a636-a2aa01a4ef5d)] interface imgIContainerObserver : nsISupports { - [noscript] void frameChanged([const] in nsIntRect aDirtyRect); + [noscript] void frameChanged(in imgIRequest aRequest, + in imgIContainer aContainer, + [const] in nsIntRect aDirtyRect); };
--- a/image/public/imgIDecoderObserver.idl +++ b/image/public/imgIDecoderObserver.idl @@ -38,83 +38,103 @@ interface imgIContainer; * all, some, or none of the notifications may fire before the call returns. * * This interface will be cleaned up in bug 505385. * * @author Stuart Parmenter <pavlov@netscape.com> * @version 0.1 * @see imagelib2 */ -[scriptable, uuid(0089cf0c-210c-4b44-9ab0-8099b32bcf64)] +[scriptable, uuid(5ca71b89-1a2f-475f-881d-d76c1531c4c8)] interface imgIDecoderObserver : imgIContainerObserver { /** * Load notification. * * called at the same time that nsIRequestObserver::onStartRequest would be * (used only for observers of imgIRequest objects, which are nsIRequests, * not imgIDecoder objects) */ - void onStartRequest(); + void onStartRequest(in imgIRequest aRequest); /** * Decode notification. * * Called as soon as the image begins getting decoded. This does not include * "header-only" decodes used by decode-on-draw to parse the width/height * out of the image. Thus, it is a decode notification only. */ - void onStartDecode(); + void onStartDecode(in imgIRequest aRequest); /** * Load notification. * * Called once enough data has been loaded from the network that we were able * to parse the width/height from the image. By the time this callback is been * called, the size has been set on the container and STATUS_SIZE_AVAILABLE * has been set on the associated imgRequest. */ - void onStartContainer(); + void onStartContainer(in imgIRequest aRequest, in imgIContainer aContainer); + + /** + * Decode notification. + * + * called when each frame is created. + */ + void onStartFrame(in imgIRequest aRequest, in unsigned long aFrame); /** * Decode notification. * * called when there is more to paint. */ - [noscript] void onDataAvailable([const] in nsIntRect aRect); + [noscript] void onDataAvailable(in imgIRequest aRequest, in boolean aCurrentFrame, [const] in nsIntRect aRect); /** * Decode notification. * * called when a frame is finished decoding. */ - void onStopFrame(); + void onStopFrame(in imgIRequest aRequest, in unsigned long aFrame); + + /** + * Do not implement this. It is useless and going away. + */ + void onStopContainer(in imgIRequest aRequest, in imgIContainer aContainer); /** * Notification for when an image is known to be animated. This should be * fired at the earliest possible time. */ - void onImageIsAnimated(); + void onImageIsAnimated(in imgIRequest aRequest); /** - * Decode notification. + * In theory a decode notification, but currently a load notification. * - * Called when all decoding has terminated. + * Ideally this would be called when the decode is complete. Unfortunately, + * this is currently the only way to signal decoding errors to consumers, + * and the only decoding errors that consumers care about (indeed, the only + * ones that they're prepared to hear about) are failures to instantiate the + * decoder (<img src="foo.html"> for example). Thus, currently this is just + * a companion to onStopDecode to signal success or failure. This will be + * revisited in bug 505385. If you're thinking of doing something new with + * this, please talk to bholley first. */ - void onStopDecode(in nsresult status); + void onStopDecode(in imgIRequest aRequest, in nsresult status, + in wstring statusArg); /** * Load notification. * * called at the same time that nsIRequestObserver::onStopRequest would be * (used only for observers of imgIRequest objects, which are nsIRequests, * not imgIDecoder objects) */ - void onStopRequest(in boolean aIsLastPart); + void onStopRequest(in imgIRequest aRequest, in boolean aIsLastPart); /** * Called when the decoded image data is discarded. This means that the frames * no longer exist in decoded form, and any attempt to access or draw the * image will initiate a new series of progressive decode notifications. */ - void onDiscard(); + void onDiscard(in imgIRequest aRequest); };
--- a/image/public/imgILoader.idl +++ b/image/public/imgILoader.idl @@ -1,17 +1,17 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" -interface imgINotificationObserver; +interface imgIDecoderObserver; interface imgIRequest; interface nsIChannel; interface nsILoadGroup; interface nsIPrincipal; interface nsIStreamListener; interface nsIURI; @@ -56,17 +56,17 @@ interface imgILoader : nsISupports * make sure to Cancel() the resulting request before the observer * goes away. */ imgIRequest loadImage(in nsIURI aURI, in nsIURI aInitialDocumentURL, in nsIURI aReferrerURI, in nsIPrincipal aLoadingPrincipal, in nsILoadGroup aLoadGroup, - in imgINotificationObserver aObserver, + in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest, in nsIChannelPolicy channelPolicy); /** * Start the load and decode of an image. @@ -81,12 +81,12 @@ interface imgILoader : nsISupports * not interested in the data. @aChannel will be canceled for you in * this case. * * libpr0n does NOT keep a strong ref to the observer; this prevents * reference cycles. This means that callers of loadImageWithChannel should * make sure to Cancel() the resulting request before the observer goes away. */ imgIRequest loadImageWithChannel(in nsIChannel aChannel, - in imgINotificationObserver aObserver, + in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener aListener); };
deleted file mode 100644 --- a/image/public/imgINotificationObserver.idl +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface imgIRequest; - -%{C++ -#include "nsRect.h" -%} - -[ptr] native nsIntRect(nsIntRect); - -[scriptable, builtinclass, uuid(90b3d21c-317d-4d96-93c0-12add64a26bf)] -interface imgINotificationObserver : nsISupports -{ - const long SIZE_AVAILABLE = 1; - const long FRAME_UPDATE = 2; - const long FRAME_COMPLETE = 3; - const long LOAD_COMPLETE = 4; - const long DECODE_COMPLETE = 5; - const long DISCARD = 6; - const long IS_ANIMATED = 7; - - [noscript] void notify(in imgIRequest aProxy, in long aType, [const] in nsIntRect aRect); -};
--- a/image/public/imgIRequest.idl +++ b/image/public/imgIRequest.idl @@ -3,28 +3,28 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" #include "nsIRequest.idl" interface imgIContainer; -interface imgINotificationObserver; +interface imgIDecoderObserver; interface nsIURI; interface nsIPrincipal; /** * imgIRequest interface * * @author Stuart Parmenter <stuart@mozilla.com> * @version 0.1 * @see imagelib2 */ -[scriptable, uuid(3ea9fc87-2e97-45bf-b373-d1dd253a0b5e)] +[scriptable, uuid(a5a785a8-9881-11e1-aaff-001fbc092072)] interface imgIRequest : nsIRequest { /** * the image container... * @return the image object associated with the request. * @attention NEED DOCS */ readonly attribute imgIContainer image; @@ -77,27 +77,27 @@ interface imgIRequest : nsIRequest /** * The URI the image load was started with. Note that this might not be the * actual URI for the image (e.g. if HTTP redirects happened during the * load). */ readonly attribute nsIURI URI; - readonly attribute imgINotificationObserver notificationObserver; + readonly attribute imgIDecoderObserver decoderObserver; readonly attribute string mimeType; /** * Clone this request; the returned request will have aObserver as the * observer. aObserver will be notified synchronously (before the clone() * call returns) with all the notifications that have already been dispatched * for this image load. */ - imgIRequest clone(in imgINotificationObserver aObserver); + imgIRequest clone(in imgIDecoderObserver aObserver); /** * The principal gotten from the channel the image was loaded from. */ readonly attribute nsIPrincipal imagePrincipal; /** * Whether the request is multipart (ie, multipart/x-mixed-replace)
deleted file mode 100644 --- a/image/public/imgIScriptedNotificationObserver.idl +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface imgIRequest; - -[scriptable, uuid(10be55b3-2029-41a7-a975-538efed250ed)] -interface imgIScriptedNotificationObserver : nsISupports -{ - void sizeAvailable(in imgIRequest aRequest); - void frameUpdate(in imgIRequest aRequest); - void frameComplete(in imgIRequest aRequest); - void loadComplete(in imgIRequest aRequest); - void decodeComplete(in imgIRequest aRequest); - void discard(in imgIRequest aRequest); - void isAnimated(in imgIRequest aRequest); -};
--- a/image/public/imgITools.idl +++ b/image/public/imgITools.idl @@ -6,20 +6,18 @@ #include "nsISupports.idl" interface nsIInputStream; interface imgIContainer; interface imgILoader; interface imgICache; interface nsIDOMDocument; -interface imgIScriptedNotificationObserver; -interface imgINotificationObserver; -[scriptable, uuid(98bd5bf9-87eb-4d92-81b1-4cd10c64f7b2)] +[scriptable, uuid(53dd1cbe-cb9f-4d9e-8104-1ab72851c88e)] interface imgITools : nsISupports { /** * decodeImageData * Caller provides an input stream and mimetype. We read from the stream * and decompress it (according to the specified mime type) and return * the resulting imgIContainer. (If the caller already has a container, * it can be provided as input to be reused). @@ -120,17 +118,9 @@ interface imgITools : nsISupports */ nsIInputStream encodeCroppedImage(in imgIContainer aContainer, in ACString aMimeType, in long aOffsetX, in long aOffsetY, in long aWidth, in long aHeight, [optional] in AString outputOptions); - - /** - * Create a wrapper around a scripted notification observer (ordinarily - * imgINotificationObserver cannot be implemented from scripts). - * - * @param aObserver The scripted observer to wrap - */ - imgINotificationObserver createScriptedObserver(in imgIScriptedNotificationObserver aObserver); };
--- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -40,17 +40,17 @@ Decoder::~Decoder() void Decoder::Init() { // No re-initializing NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!"); // Fire OnStartDecode at init time to support bug 512435 if (!IsSizeDecode() && mObserver) - mObserver->OnStartDecode(); + mObserver->OnStartDecode(nullptr); // Implementation-specific initialization InitInternal(); mInitialized = true; } // Initializes a decoder whose aImage and aObserver is already being used by a // parent decoder @@ -119,17 +119,18 @@ Decoder::Finish() bool salvage = !HasDecoderError() && mImage.GetNumFrames(); // If we're salvaging, say we finished decoding if (salvage) mImage.DecodingComplete(); // Fire teardown notifications if (mObserver) { - mObserver->OnStopDecode(salvage ? NS_OK : NS_ERROR_FAILURE); + mObserver->OnStopContainer(nullptr, &mImage); + mObserver->OnStopDecode(nullptr, salvage ? NS_OK : NS_ERROR_FAILURE, nullptr); } } } void Decoder::FinishSharedDecoder() { if (!HasError()) { @@ -161,17 +162,18 @@ Decoder::FlushInvalidations() mImage.GetWidth(&width); mImage.GetHeight(&height); nsIntRect mImageBound(0, 0, width, height); mInvalidRect.Inflate(1); mInvalidRect = mInvalidRect.Intersect(mImageBound); #endif - mObserver->OnDataAvailable(&mInvalidRect); + bool isCurrentFrame = mImage.GetCurrentFrameIndex() == (mFrameCount - 1); + mObserver->OnDataAvailable(nullptr, isCurrentFrame, &mInvalidRect); } // Clear the invalidation rectangle mInvalidRect.SetEmpty(); } /* * Hook stubs. Override these as necessary in decoder implementations. @@ -192,17 +194,17 @@ Decoder::PostSize(int32_t aWidth, int32_ NS_ABORT_IF_FALSE(aWidth >= 0, "Width can't be negative!"); NS_ABORT_IF_FALSE(aHeight >= 0, "Height can't be negative!"); // Tell the image mImage.SetSize(aWidth, aHeight); // Notify the observer if (mObserver) - mObserver->OnStartContainer(); + mObserver->OnStartContainer(nullptr, &mImage); } void Decoder::PostFrameStart() { // We shouldn't already be mid-frame NS_ABORT_IF_FALSE(!mInFrame, "Starting new frame but not done with old one!"); @@ -215,36 +217,40 @@ Decoder::PostFrameStart() mFrameCount++; mInFrame = true; // Decoder implementations should only call this method if they successfully // appended the frame to the image. So mFrameCount should always match that // reported by the Image. NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(), "Decoder frame count doesn't match image's!"); + + // Fire notification + if (mObserver) + mObserver->OnStartFrame(nullptr, mFrameCount - 1); // frame # is zero-indexed } void Decoder::PostFrameStop() { // We should be mid-frame NS_ABORT_IF_FALSE(mInFrame, "Stopping frame when we didn't start one!"); // Update our state mInFrame = false; // Flush any invalidations before we finish the frame FlushInvalidations(); // Fire notifications if (mObserver) { - mObserver->OnStopFrame(); + mObserver->OnStopFrame(nullptr, mFrameCount - 1); // frame # is zero-indexed if (mFrameCount > 1 && !mIsAnimated) { mIsAnimated = true; - mObserver->OnImageIsAnimated(); + mObserver->OnImageIsAnimated(nullptr); } } } void Decoder::PostInvalidation(nsIntRect& aRect) { // We should be mid-frame @@ -267,17 +273,18 @@ Decoder::PostDecodeDone() bool isNonPremult = GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA; for (int i = 0; i < frames; i++) { mImage.SetFrameAsNonPremult(i, isNonPremult); } // Notify mImage.DecodingComplete(); if (mObserver) { - mObserver->OnStopDecode(NS_OK); + mObserver->OnStopContainer(nullptr, &mImage); + mObserver->OnStopDecode(nullptr, NS_OK, nullptr); } } void Decoder::PostDataError() { mDataError = true; }
--- a/image/src/Image.cpp +++ b/image/src/Image.cpp @@ -16,17 +16,17 @@ Image::Image(imgStatusTracker* aStatusTr mInitialized(false), mAnimating(false), mError(false) { if (aStatusTracker) { mStatusTracker = aStatusTracker; mStatusTracker->SetImage(this); } else { - mStatusTracker = new imgStatusTracker(this, nullptr); + mStatusTracker = new imgStatusTracker(this); } } uint32_t Image::SizeOfData() { if (mError) return 0;
--- a/image/src/Makefile.in +++ b/image/src/Makefile.in @@ -22,17 +22,16 @@ EXPORTS = imgLoader.h \ imgRequest.h \ $(NULL) CPPSRCS = \ Image.cpp \ Decoder.cpp \ DiscardTracker.cpp \ RasterImage.cpp \ - ScriptedNotificationObserver.cpp \ SVGDocumentWrapper.cpp \ VectorImage.cpp \ imgFrame.cpp \ imgLoader.cpp \ imgRequest.cpp \ imgRequestProxy.cpp \ imgTools.cpp \ imgStatusTracker.cpp \
--- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -509,17 +509,17 @@ RasterImage::RequestRefresh(const mozill // Notify listeners that our frame has actually changed, but do this only // once for all frames that we've now passed (if AdvanceFrame() was called // more than once). #ifdef DEBUG mFramesNotified++; #endif UpdateImageContainer(); - observer->FrameChanged(&dirtyRect); + observer->FrameChanged(nullptr, this, &dirtyRect); } } //****************************************************************************** /* [noscript] imgIContainer extractFrame(uint32_t aWhichFrame, * [const] in nsIntRect aRegion, * in uint32_t aFlags); */ NS_IMETHODIMP @@ -1495,17 +1495,17 @@ RasterImage::ResetAnimation() UpdateImageContainer(); // Note - We probably want to kick off a redecode somewhere around here when // we fix bug 500402. // Update display if we were animating before nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver)); if (mAnimating && observer) - observer->FrameChanged(&(mAnim->firstFrameRefreshArea)); + observer->FrameChanged(nullptr, this, &(mAnim->firstFrameRefreshArea)); if (ShouldAnimate()) { StartAnimation(); // The animation may not have been running before, if mAnimationFinished // was false (before we changed it to true in this function). So, mark the // animation as running. mAnimating = true; } @@ -2279,17 +2279,17 @@ RasterImage::Discard(bool force) mFrames.Clear(); // Flag that we no longer have decoded frames for this image mDecoded = false; // Notify that we discarded nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver)); if (observer) - observer->OnDiscard(); + observer->OnDiscard(nullptr); if (force) DiscardTracker::Remove(&mDiscardTrackerNode); // Log PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG, ("CompressedImageAccounting: discarded uncompressed image " "data from RasterImage %p (%s) - %d frames (cached count: %d); " @@ -2769,17 +2769,17 @@ RasterImage::DrawWorker::Run() if (request->stopped) { ScaleRequest::Stop(request->image); } nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(request->image->mObserver)); if (request->done && observer) { imgFrame *scaledFrame = request->dstFrame.get(); scaledFrame->ImageUpdated(scaledFrame->GetRect()); nsIntRect frameRect = request->srcFrame->GetRect(); - observer->FrameChanged(&frameRect); + observer->FrameChanged(nullptr, request->image, &frameRect); } } return NS_OK; } void RasterImage::DrawWorker::RequestDraw(RasterImage* aImg)
deleted file mode 100644 --- a/image/src/ScriptedNotificationObserver.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "ScriptedNotificationObserver.h" -#include "imgIScriptedNotificationObserver.h" -#include "nsCycleCollectionParticipant.h" - -using namespace mozilla::image; - -NS_IMPL_CYCLE_COLLECTION_1(ScriptedNotificationObserver, mInner) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptedNotificationObserver) - NS_INTERFACE_MAP_ENTRY(imgINotificationObserver) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptedNotificationObserver) -NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptedNotificationObserver) - -ScriptedNotificationObserver::ScriptedNotificationObserver( - imgIScriptedNotificationObserver* aInner) -: mInner(aInner) -{ -} - -NS_IMETHODIMP -ScriptedNotificationObserver::Notify(imgIRequest* aRequest, - int32_t aType, - const nsIntRect* /*aUnused*/) -{ - if (aType == imgINotificationObserver::SIZE_AVAILABLE) - return mInner->SizeAvailable(aRequest); - if (aType == imgINotificationObserver::FRAME_UPDATE) - return mInner->FrameUpdate(aRequest); - if (aType == imgINotificationObserver::FRAME_COMPLETE) - return mInner->FrameComplete(aRequest); - if (aType == imgINotificationObserver::DECODE_COMPLETE) - return mInner->DecodeComplete(aRequest); - if (aType == imgINotificationObserver::LOAD_COMPLETE) - return mInner->LoadComplete(aRequest); - if (aType == imgINotificationObserver::DISCARD) - return mInner->Discard(aRequest); - if (aType == imgINotificationObserver::IS_ANIMATED) - return mInner->IsAnimated(aRequest); - return NS_OK; -}
deleted file mode 100644 --- a/image/src/ScriptedNotificationObserver.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "imgINotificationObserver.h" -#include "nsCOMPtr.h" -#include "nsCycleCollectionParticipant.h" - -class imgIScriptedNotificationObserver; - -namespace mozilla { -namespace image { - -class ScriptedNotificationObserver : public imgINotificationObserver -{ -public: - ScriptedNotificationObserver(imgIScriptedNotificationObserver* aInner); - virtual ~ScriptedNotificationObserver() {} - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_IMGINOTIFICATIONOBSERVER - NS_DECL_CYCLE_COLLECTION_CLASS(ScriptedNotificationObserver) - -private: - nsCOMPtr<imgIScriptedNotificationObserver> mInner; -}; - -}}
--- a/image/src/VectorImage.cpp +++ b/image/src/VectorImage.cpp @@ -676,21 +676,21 @@ VectorImage::OnStopRequest(nsIRequest* a // Start listening to our image for rendering updates mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this); // Tell *our* observers that we're done loading nsCOMPtr<imgIDecoderObserver> observer = do_QueryReferent(mObserver); if (observer) { // NOTE: This signals that width/height are available. - observer->OnStartContainer(); + observer->OnStartContainer(nullptr, this); - observer->FrameChanged(&nsIntRect::GetMaxSizedIntRect()); - observer->OnStopFrame(); - observer->OnStopDecode(NS_OK); + observer->FrameChanged(nullptr, this, &nsIntRect::GetMaxSizedIntRect()); + observer->OnStopFrame(nullptr, 0); + observer->OnStopDecode(nullptr, NS_OK, nullptr); } EvaluateAnimation(); return rv; } //------------------------------------------------------------------------------ // nsIStreamListener method @@ -717,19 +717,19 @@ VectorImage::OnDataAvailable(nsIRequest* void VectorImage::InvalidateObserver() { if (!mObserver) return; nsCOMPtr<imgIContainerObserver> containerObs(do_QueryReferent(mObserver)); if (containerObs) { - containerObs->FrameChanged(&nsIntRect::GetMaxSizedIntRect()); + containerObs->FrameChanged(nullptr, this, &nsIntRect::GetMaxSizedIntRect()); } nsCOMPtr<imgIDecoderObserver> decoderObs(do_QueryReferent(mObserver)); if (decoderObs) { - decoderObs->OnStopFrame(); + decoderObs->OnStopFrame(nullptr, imgIContainer::FRAME_CURRENT); } } } // namespace image } // namespace mozilla
--- a/image/src/imgLoader.cpp +++ b/image/src/imgLoader.cpp @@ -675,17 +675,17 @@ imgCacheQueue::iterator imgCacheQueue::e return mQueue.end(); } imgCacheQueue::const_iterator imgCacheQueue::end() const { return mQueue.end(); } nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup, - imgINotificationObserver *aObserver, + imgIDecoderObserver *aObserver, nsLoadFlags aLoadFlags, imgIRequest *aProxyRequest, imgIRequest **_retval) { LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::CreateNewProxyForRequest", "imgRequest", aRequest); /* XXX If we move decoding onto separate threads, we should save off the calling thread here and pass it off to |proxyRequest| so that it call proxy calls to |aObserver|. @@ -703,17 +703,17 @@ nsresult imgLoader::CreateNewProxyForReq |Init()| adds the request to the loadgroup. */ proxyRequest->SetLoadFlags(aLoadFlags); nsCOMPtr<nsIURI> uri; aRequest->GetURI(getter_AddRefs(uri)); // init adds itself to imgRequest's list of observers - nsresult rv = proxyRequest->Init(&aRequest->GetStatusTracker(), aLoadGroup, uri, aObserver); + nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, aRequest->mImage, uri, aObserver); if (NS_FAILED(rv)) { NS_RELEASE(proxyRequest); return rv; } // transfer reference to caller *_retval = static_cast<imgIRequest*>(proxyRequest); @@ -1157,17 +1157,17 @@ void imgLoader::CheckCacheLimits(imgCach } } bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request, nsIURI *aURI, nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI, nsILoadGroup *aLoadGroup, - imgINotificationObserver *aObserver, + imgIDecoderObserver *aObserver, nsISupports *aCX, nsLoadFlags aLoadFlags, imgIRequest *aExistingRequest, imgIRequest **aProxyRequest, nsIChannelPolicy *aPolicy, nsIPrincipal* aLoadingPrincipal, int32_t aCORSMode) { @@ -1279,17 +1279,17 @@ bool imgLoader::ValidateRequestWithNewCh } } bool imgLoader::ValidateEntry(imgCacheEntry *aEntry, nsIURI *aURI, nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI, nsILoadGroup *aLoadGroup, - imgINotificationObserver *aObserver, + imgIDecoderObserver *aObserver, nsISupports *aCX, nsLoadFlags aLoadFlags, bool aCanMakeNewChannel, imgIRequest *aExistingRequest, imgIRequest **aProxyRequest, nsIChannelPolicy *aPolicy, nsIPrincipal* aLoadingPrincipal, int32_t aCORSMode) @@ -1516,24 +1516,24 @@ nsresult imgLoader::EvictEntries(imgCach #define LOAD_FLAGS_CACHE_MASK (nsIRequest::LOAD_BYPASS_CACHE | \ nsIRequest::LOAD_FROM_CACHE) #define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS | \ nsIRequest::VALIDATE_NEVER | \ nsIRequest::VALIDATE_ONCE_PER_SESSION) -/* imgIRequest loadImage (in nsIURI aURI, in nsIURI initialDocumentURI, in nsIPrincipal loadingPrincipal, in nsILoadGroup aLoadGroup, in imgINotificationObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */ +/* imgIRequest loadImage (in nsIURI aURI, in nsIURI initialDocumentURI, in nsIPrincipal loadingPrincipal, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI, nsIPrincipal* aLoadingPrincipal, nsILoadGroup *aLoadGroup, - imgINotificationObserver *aObserver, + imgIDecoderObserver *aObserver, nsISupports *aCX, nsLoadFlags aLoadFlags, nsISupports *aCacheKey, imgIRequest *aRequest, nsIChannelPolicy *aPolicy, imgIRequest **_retval) { VerifyCacheSizes(); @@ -1779,18 +1779,18 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIUR return rv; } NS_ASSERTION(*_retval, "imgLoader::LoadImage -- no return value"); return NS_OK; } -/* imgIRequest loadImageWithChannel(in nsIChannel channel, in imgINotificationObserver aObserver, in nsISupports cx, out nsIStreamListener); */ -NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgINotificationObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgIRequest **_retval) +/* imgIRequest loadImageWithChannel(in nsIChannel channel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener); */ +NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgIRequest **_retval) { NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer"); MOZ_ASSERT(NS_UsePrivateBrowsing(channel) == mRespectPrivacy); nsRefPtr<imgRequest> request; nsCOMPtr<nsIURI> uri;
--- a/image/src/imgLoader.h +++ b/image/src/imgLoader.h @@ -25,17 +25,17 @@ #ifdef LOADER_THREADSAFE #include "prlock.h" #endif class imgLoader; class imgRequest; class imgRequestProxy; class imgIRequest; -class imgINotificationObserver; +class imgIDecoderObserver; class nsILoadGroup; class imgCacheExpirationTracker; class imgMemoryReporter; class imgCacheEntry { public: imgCacheEntry(imgLoader* loader, imgRequest *request, bool aForcePrincipalCheck); @@ -284,37 +284,37 @@ public: bool SetHasProxies(nsIURI *key); private: // methods bool ValidateEntry(imgCacheEntry *aEntry, nsIURI *aKey, nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI, nsILoadGroup *aLoadGroup, - imgINotificationObserver *aObserver, nsISupports *aCX, + imgIDecoderObserver *aObserver, nsISupports *aCX, nsLoadFlags aLoadFlags, bool aCanMakeNewChannel, imgIRequest *aExistingRequest, imgIRequest **aProxyRequest, nsIChannelPolicy *aPolicy, nsIPrincipal* aLoadingPrincipal, int32_t aCORSMode); bool ValidateRequestWithNewChannel(imgRequest *request, nsIURI *aURI, nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI, nsILoadGroup *aLoadGroup, - imgINotificationObserver *aObserver, + imgIDecoderObserver *aObserver, nsISupports *aCX, nsLoadFlags aLoadFlags, imgIRequest *aExistingRequest, imgIRequest **aProxyRequest, nsIChannelPolicy *aPolicy, nsIPrincipal* aLoadingPrincipal, int32_t aCORSMode); nsresult CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup, - imgINotificationObserver *aObserver, + imgIDecoderObserver *aObserver, nsLoadFlags aLoadFlags, imgIRequest *aRequestProxy, imgIRequest **_retval); void ReadAcceptHeaderPref(); typedef nsRefPtrHashtable<nsCStringHashKey, imgCacheEntry> imgCacheTable;
--- a/image/src/imgRequest.cpp +++ b/image/src/imgRequest.cpp @@ -66,33 +66,35 @@ InitPrefCaches() Preferences::AddBoolVarCache(&gDecodeOnDraw, "image.mem.decodeondraw"); gInitializedPrefCaches = true; } #if defined(PR_LOGGING) PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest"); #endif -NS_IMPL_ISUPPORTS5(imgRequest, +NS_IMPL_ISUPPORTS8(imgRequest, + imgIDecoderObserver, imgIContainerObserver, nsIStreamListener, nsIRequestObserver, + nsISupportsWeakReference, nsIChannelEventSink, nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback) imgRequest::imgRequest(imgLoader* aLoader) : mLoader(aLoader) - , mStatusTracker(new imgStatusTracker(nullptr, this)) , mValidator(nullptr) , mImageSniffers("image-sniffing-services") , mInnerWindowId(0) , mCORSMode(imgIRequest::CORS_NONE) , mDecodeRequested(false) , mIsMultiPartChannel(false) , mGotData(false) , mIsInCache(false) + , mBlockingOnload(false) , mResniffMimeType(false) { // Register our pref observers if we haven't yet. if (NS_UNLIKELY(!gInitializedPrefCaches)) { InitPrefCaches(); } } @@ -120,16 +122,18 @@ nsresult imgRequest::Init(nsIURI *aURI, NS_ABORT_IF_FALSE(!mImage, "Multiple calls to init"); NS_ABORT_IF_FALSE(aURI, "No uri"); NS_ABORT_IF_FALSE(aCurrentURI, "No current uri"); NS_ABORT_IF_FALSE(aRequest, "No request"); NS_ABORT_IF_FALSE(aChannel, "No channel"); mProperties = do_CreateInstance("@mozilla.org/properties;1"); + mStatusTracker = new imgStatusTracker(nullptr); + mURI = aURI; mCurrentURI = aCurrentURI; mRequest = aRequest; mChannel = aChannel; mTimedChannel = do_QueryInterface(mChannel); mLoadingPrincipal = aLoadingPrincipal; mCORSMode = aCORSMode; @@ -167,56 +171,63 @@ void imgRequest::SetCacheEntry(imgCacheE mCacheEntry = entry; } bool imgRequest::HasCacheEntry() const { return mCacheEntry != nullptr; } -void imgRequest::ResetCacheEntry() -{ - if (HasCacheEntry()) { - mCacheEntry->SetDataSize(0); - } -} - void imgRequest::AddProxy(imgRequestProxy *proxy) { NS_PRECONDITION(proxy, "null imgRequestProxy passed in"); LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy); // If we're empty before adding, we have to tell the loader we now have // proxies. - if (GetStatusTracker().ConsumerCount() == 0) { + if (mObservers.IsEmpty()) { NS_ABORT_IF_FALSE(mURI, "Trying to SetHasProxies without key uri."); mLoader->SetHasProxies(mURI); } - GetStatusTracker().AddConsumer(proxy); + // If we don't have any current observers, we should restart any animation. + if (mImage && !HaveProxyWithObserver(proxy) && proxy->HasObserver()) { + LOG_MSG(gImgLog, "imgRequest::AddProxy", "resetting animation"); + + mImage->ResetAnimation(); + } + + proxy->SetPrincipal(mPrincipal); + + mObservers.AppendElementUnlessExists(proxy); } nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus) { LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy); // This will remove our animation consumers, so after removing // this proxy, we don't end up without proxies with observers, but still // have animation consumers. proxy->ClearAnimationConsumers(); + if (!mObservers.RemoveElement(proxy)) { + // Not one of our proxies; we're done + return NS_OK; + } + // Let the status tracker do its thing before we potentially call Cancel() // below, because Cancel() may result in OnStopRequest being called back // before Cancel() returns, leaving the image in a different state then the // one it was in at this point. + imgStatusTracker& statusTracker = GetStatusTracker(); - if (!statusTracker.RemoveConsumer(proxy, aStatus, !aNotify)) - return NS_OK; + statusTracker.EmulateRequestFinished(proxy, aStatus); - if (statusTracker.ConsumerCount() == 0) { + if (mObservers.IsEmpty()) { // If we have no observers, there's nothing holding us alive. If we haven't // been cancelled and thus removed from the cache, tell the image loader so // we can be evicted from the cache. if (mCacheEntry) { NS_ABORT_IF_FALSE(mURI, "Removing last observer without key uri."); mLoader->SetHasNoProxies(mURI, mCacheEntry); } @@ -226,17 +237,17 @@ nsresult imgRequest::RemoveProxy(imgRequ mURI->GetSpec(spec); LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy no cache entry", "uri", spec.get()); } #endif /* If |aStatus| is a failure code, then cancel the load if it is still in progress. Otherwise, let the load continue, keeping 'this' in the cache with no observers. This way, if a proxy is destroyed without calling cancel on it, it won't leak - and won't leave a bad pointer in the observer list. + and won't leave a bad pointer in mObservers. */ if (statusTracker.IsLoading() && NS_FAILED(aStatus)) { LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "load in progress. canceling"); this->Cancel(NS_BINDING_ABORTED); } /* break the cycle from the cache entry. */ @@ -269,17 +280,26 @@ void imgRequest::CancelAndAbort(nsresult void imgRequest::Cancel(nsresult aStatus) { /* The Cancel() method here should only be called by this class. */ LOG_SCOPE(gImgLog, "imgRequest::Cancel"); imgStatusTracker& statusTracker = GetStatusTracker(); - statusTracker.MaybeUnblockOnload(); + if (mBlockingOnload) { + mBlockingOnload = false; + + statusTracker.RecordUnblockOnload(); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + statusTracker.SendUnblockOnload(iter.GetNext()); + } + } statusTracker.RecordCancel(); RemoveFromCache(); if (mRequest && statusTracker.IsLoading()) mRequest->Cancel(aStatus); } @@ -317,16 +337,34 @@ void imgRequest::RemoveFromCache() mLoader->RemoveFromCache(mCacheEntry); else mLoader->RemoveFromCache(mURI); } mCacheEntry = nullptr; } +bool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const +{ + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + imgRequestProxy* proxy; + while (iter.HasMore()) { + proxy = iter.GetNext(); + if (proxy == aProxyToIgnore) { + continue; + } + + if (proxy->HasObserver()) { + return true; + } + } + + return false; +} + int32_t imgRequest::Priority() const { int32_t priority = nsISupportsPriority::PRIORITY_NORMAL; nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest); if (p) p->GetPriority(&priority); return priority; } @@ -335,17 +373,17 @@ void imgRequest::AdjustPriority(imgReque { // only the first proxy is allowed to modify the priority of this image load. // // XXX(darin): this is probably not the most optimal algorithm as we may want // to increase the priority of requests that have a lot of proxies. the key // concern though is that image loads remain lower priority than other pieces // of content such as link clicks, CSS, and JS. // - if (!GetStatusTracker().FirstConsumerIs(proxy)) + if (mObservers.SafeElementAt(0, nullptr) != proxy) return; nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest); if (p) p->AdjustPriority(delta); } void imgRequest::SetIsInCache(bool incache) @@ -467,16 +505,299 @@ imgRequest::StartDecoding() } // Otherwise, flag to do it when we get the image mDecodeRequested = true; return NS_OK; } + + +/** imgIContainerObserver methods **/ + +/* [noscript] void frameChanged (in imgIRequest request, + in imgIContainer container, + in nsIntRect dirtyRect); */ +NS_IMETHODIMP imgRequest::FrameChanged(imgIRequest *request, + imgIContainer *container, + const nsIntRect *dirtyRect) +{ + LOG_SCOPE(gImgLog, "imgRequest::FrameChanged"); + NS_ABORT_IF_FALSE(mImage, + "FrameChanged callback before we've created our image"); + + mImage->GetStatusTracker().RecordFrameChanged(container, dirtyRect); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + mImage->GetStatusTracker().SendFrameChanged(iter.GetNext(), container, dirtyRect); + } + + return NS_OK; +} + +/** imgIDecoderObserver methods **/ + +/* void onStartDecode (in imgIRequest request); */ +NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request) +{ + LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode"); + NS_ABORT_IF_FALSE(mImage, + "OnStartDecode callback before we've created our image"); + + + imgStatusTracker& tracker = mImage->GetStatusTracker(); + tracker.RecordStartDecode(); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + tracker.SendStartDecode(iter.GetNext()); + } + + if (!mIsMultiPartChannel) { + MOZ_ASSERT(!mBlockingOnload); + mBlockingOnload = true; + + tracker.RecordBlockOnload(); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + tracker.SendBlockOnload(iter.GetNext()); + } + } + + /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which + indicates the beginning of a new decode. + The cache entry's size therefore needs to be reset to 0 here. If we do not do this, + the code in imgRequest::OnStopFrame will continue to increase the data size cumulatively. + */ + if (mCacheEntry) + mCacheEntry->SetDataSize(0); + + return NS_OK; +} + +NS_IMETHODIMP imgRequest::OnStartRequest(imgIRequest *aRequest) +{ + NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest"); + return NS_OK; +} + +/* void onStartContainer (in imgIRequest request, in imgIContainer image); */ +NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *image) +{ + LOG_SCOPE(gImgLog, "imgRequest::OnStartContainer"); + + NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!"); + if (!image) return NS_ERROR_UNEXPECTED; + + NS_ABORT_IF_FALSE(mImage, + "OnStartContainer callback before we've created our image"); + NS_ABORT_IF_FALSE(image == mImage, + "OnStartContainer callback from an image we don't own"); + mImage->GetStatusTracker().RecordStartContainer(image); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + mImage->GetStatusTracker().SendStartContainer(iter.GetNext(), image); + } + + return NS_OK; +} + +/* void onStartFrame (in imgIRequest request, in unsigned long frame); */ +NS_IMETHODIMP imgRequest::OnStartFrame(imgIRequest *request, + uint32_t frame) +{ + LOG_SCOPE(gImgLog, "imgRequest::OnStartFrame"); + NS_ABORT_IF_FALSE(mImage, + "OnStartFrame callback before we've created our image"); + + mImage->GetStatusTracker().RecordStartFrame(frame); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + mImage->GetStatusTracker().SendStartFrame(iter.GetNext(), frame); + } + + return NS_OK; +} + +/* [noscript] void onDataAvailable (in imgIRequest request, in boolean aCurrentFrame, [const] in nsIntRect rect); */ +NS_IMETHODIMP imgRequest::OnDataAvailable(imgIRequest *request, + bool aCurrentFrame, + const nsIntRect * rect) +{ + LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable"); + NS_ABORT_IF_FALSE(mImage, + "OnDataAvailable callback before we've created our image"); + + mImage->GetStatusTracker().RecordDataAvailable(aCurrentFrame, rect); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + mImage->GetStatusTracker().SendDataAvailable(iter.GetNext(), aCurrentFrame, rect); + } + + return NS_OK; +} + +/* void onStopFrame (in imgIRequest request, in unsigned long frame); */ +NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request, + uint32_t frame) +{ + LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame"); + NS_ABORT_IF_FALSE(mImage, + "OnStopFrame callback before we've created our image"); + + imgStatusTracker& tracker = mImage->GetStatusTracker(); + tracker.RecordStopFrame(frame); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + tracker.SendStopFrame(iter.GetNext(), frame); + } + + if (mBlockingOnload) { + mBlockingOnload = false; + + tracker.RecordUnblockOnload(); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + tracker.SendUnblockOnload(iter.GetNext()); + } + } + + return NS_OK; +} + +/* void onStopContainer (in imgIRequest request, in imgIContainer image); */ +NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request, + imgIContainer *image) +{ + LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer"); + NS_ABORT_IF_FALSE(mImage, + "OnDataContainer callback before we've created our image"); + + imgStatusTracker& tracker = mImage->GetStatusTracker(); + tracker.RecordStopContainer(image); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + 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<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + tracker.SendUnblockOnload(iter.GetNext()); + } + } + + return NS_OK; +} + +/* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */ +NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aStatusArg) +{ + LOG_SCOPE(gImgLog, "imgRequest::OnStopDecode"); + NS_ABORT_IF_FALSE(mImage, + "OnDataDecode callback before we've created our image"); + + // We finished the decode, and thus have the decoded frames. Update the cache + // entry size to take this into account. + UpdateCacheEntrySize(); + + mImage->GetStatusTracker().RecordStopDecode(aStatus, aStatusArg); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + mImage->GetStatusTracker().SendStopDecode(iter.GetNext(), aStatus, + aStatusArg); + } + + if (NS_FAILED(aStatus)) { + // Some kind of problem has happened with image decoding. + // Report the URI to net:failed-to-process-uri-conent observers. + + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); + if (os) + os->NotifyObservers(mURI, "net:failed-to-process-uri-content", nullptr); + } + + // RasterImage and everything below it is completely correct and + // bulletproof about its handling of decoder notifications. + // Unfortunately, here and above we have to make some gross and + // inappropriate use of things to get things to work without + // completely overhauling the decoder observer interface (this will, + // thankfully, happen in bug 505385). From imgRequest and above (for + // the time being), OnStopDecode is just a companion to OnStopRequest + // that signals success or failure of the _load_ (not the _decode_). + // Within imgStatusTracker, we ignore OnStopDecode notifications from the + // decoder and RasterImage and generate our own every time we send + // OnStopRequest. From within SendStopDecode, we actually send + // OnStopContainer. For more information, see bug 435296. + + return NS_OK; +} + +NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest, + bool aLastPart) +{ + NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest"); + return NS_OK; +} + +/* void onDiscard (in imgIRequest request); */ +NS_IMETHODIMP imgRequest::OnDiscard(imgIRequest *aRequest) +{ + NS_ABORT_IF_FALSE(mImage, + "OnDiscard callback before we've created our image"); + + mImage->GetStatusTracker().RecordDiscard(); + + // Update the cache entry size, since we just got rid of frame data + UpdateCacheEntrySize(); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + mImage->GetStatusTracker().SendDiscard(iter.GetNext()); + } + + return NS_OK; +} + +NS_IMETHODIMP imgRequest::OnImageIsAnimated(imgIRequest *aRequest) +{ + NS_ABORT_IF_FALSE(mImage, + "OnImageIsAnimated callback before we've created our image"); + mImage->GetStatusTracker().RecordImageIsAnimated(); + + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + mImage->GetStatusTracker().SendImageIsAnimated(iter.GetNext()); + } + + return NS_OK; +} + /** nsIRequestObserver methods **/ /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt) { LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest"); // Figure out if we're multipart @@ -512,40 +833,52 @@ NS_IMETHODIMP imgRequest::OnStartRequest if (!mRequest) { NS_ASSERTION(mpchan, "We should have an mRequest here unless we're multipart"); nsCOMPtr<nsIChannel> chan; mpchan->GetBaseChannel(getter_AddRefs(chan)); mRequest = chan; } - GetStatusTracker().OnStartRequest(); + imgStatusTracker& statusTracker = GetStatusTracker(); + statusTracker.RecordStartRequest(); nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); if (channel) channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo)); + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + statusTracker.SendStartRequest(iter.GetNext()); + } + /* Get our principal */ nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest)); if (chan) { nsCOMPtr<nsIScriptSecurityManager> secMan = do_GetService("@mozilla.org/scriptsecuritymanager;1"); if (secMan) { nsresult rv = secMan->GetChannelPrincipal(chan, getter_AddRefs(mPrincipal)); if (NS_FAILED(rv)) { return rv; } + + // Tell all of our proxies that we have a principal. + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + iter.GetNext()->SetPrincipal(mPrincipal); + } } } SetCacheValidation(mCacheEntry, aRequest); // Shouldn't we be dead already if this gets hit? Probably multipart/x-mixed-replace... - if (GetStatusTracker().ConsumerCount() == 0) { + if (mObservers.IsEmpty()) { this->Cancel(NS_IMAGELIB_ERROR_FAILURE); } return NS_OK; } /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status) @@ -605,17 +938,21 @@ NS_IMETHODIMP imgRequest::OnStopRequest( // loading compressed source data, which is part of our size calculus. UpdateCacheEntrySize(); } else { // stops animations, removes from cache this->Cancel(status); } - GetStatusTracker().OnStopRequest(lastPart, status); + /* notify the kids */ + nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mObservers); + while (srIter.HasMore()) { + statusTracker.SendStopRequest(srIter.GetNext(), lastPart, status); + } mTimedChannel = nullptr; return NS_OK; } struct mimetype_closure { imgRequest* request; @@ -691,32 +1028,34 @@ imgRequest::OnDataAvailable(nsIRequest * if (mContentType != newType || newType.EqualsLiteral(SVG_MIMETYPE)) { mContentType = newType; // If we've resniffed our MIME type and it changed, we need to create a // new status tracker to give to the image, because we don't have one of // our own any more. if (mResniffMimeType) { NS_ABORT_IF_FALSE(mIsMultiPartChannel, "Resniffing a non-multipart image"); - imgStatusTracker* freshTracker = new imgStatusTracker(nullptr, this); - freshTracker->AdoptConsumers(&GetStatusTracker()); - mStatusTracker = freshTracker; + mStatusTracker = new imgStatusTracker(nullptr); } mResniffMimeType = false; /* now we have mimetype, so we can infer the image type that we want */ if (mContentType.EqualsLiteral(SVG_MIMETYPE)) { mImage = new VectorImage(mStatusTracker.forget()); } else { mImage = new RasterImage(mStatusTracker.forget()); } mImage->SetInnerWindowID(mInnerWindowId); - GetStatusTracker().OnDataAvailable(); + // Notify any imgRequestProxys that are observing us that we have an Image. + nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers); + while (iter.HasMore()) { + iter.GetNext()->SetImage(mImage); + } /* set our mimetype as a property */ nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1")); if (contentType) { contentType->SetData(mContentType); mProperties->Set("type", contentType); } @@ -775,18 +1114,17 @@ imgRequest::OnDataAvailable(nsIRequest * nsAutoCString uriString; rv = mURI->GetSpec(uriString); if (NS_FAILED(rv)) uriString.Assign("<unknown image URI>"); // Initialize the image that we created above. For RasterImages, this // instantiates a decoder behind the scenes, so if we don't have a decoder // for this mimetype we'll find out about it here. - rv = mImage->Init(GetStatusTracker().GetDecoderObserver(), - mContentType.get(), uriString.get(), imageFlags); + rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags); // We allow multipart images to fail to initialize without cancelling the // load because subsequent images might be fine. if (NS_FAILED(rv) && !mIsMultiPartChannel) { // Probably bad mimetype this->Cancel(rv); return NS_BINDING_ABORTED; }
--- a/image/src/imgRequest.h +++ b/image/src/imgRequest.h @@ -2,29 +2,33 @@ * * 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 imgRequest_h__ #define imgRequest_h__ +#include "imgIDecoderObserver.h" + #include "nsIChannelEventSink.h" #include "nsIContentSniffer.h" #include "nsIInterfaceRequestor.h" #include "nsIRequest.h" #include "nsIProperties.h" #include "nsIStreamListener.h" #include "nsIURI.h" #include "nsIPrincipal.h" #include "nsITimedChannel.h" #include "nsCategoryCache.h" #include "nsCOMPtr.h" #include "nsStringGlue.h" +#include "nsTObserverArray.h" +#include "nsWeakReference.h" #include "nsError.h" #include "imgIRequest.h" #include "nsIAsyncVerifyRedirectCallback.h" class imgCacheValidator; class imgStatusTracker; class imgLoader; class imgRequestProxy; @@ -33,17 +37,19 @@ class imgMemoryReporter; class imgRequestNotifyRunnable; namespace mozilla { namespace image { class Image; } // namespace image } // namespace mozilla -class imgRequest : public nsIStreamListener, +class imgRequest : public imgIDecoderObserver, + public nsIStreamListener, + public nsSupportsWeakReference, public nsIChannelEventSink, public nsIInterfaceRequestor, public nsIAsyncVerifyRedirectCallback { public: imgRequest(imgLoader* aLoader); virtual ~imgRequest(); @@ -99,84 +105,80 @@ public: // The principal for the document that loaded this image. Used when trying to // validate a CORS image load. already_AddRefed<nsIPrincipal> GetLoadingPrincipal() const { nsCOMPtr<nsIPrincipal> principal = mLoadingPrincipal; return principal.forget(); } - // Return the imgStatusTracker associated with this imgRequest. It may live - // in |mStatusTracker| or in |mImage.mStatusTracker|, depending on whether - // mImage has been instantiated yet. - imgStatusTracker& GetStatusTracker(); - - // Get the current principal of the image. No AddRefing. - inline nsIPrincipal* GetPrincipal() const { return mPrincipal.get(); }; - - // Resize the cache entry to 0 if it exists - void ResetCacheEntry(); - - // Update the cache entry size based on the image container - void UpdateCacheEntrySize(); - - nsresult GetURI(nsIURI **aURI); - private: friend class imgCacheEntry; friend class imgRequestProxy; friend class imgLoader; friend class imgCacheValidator; friend class imgStatusTracker; friend class imgCacheExpirationTracker; friend class imgRequestNotifyRunnable; inline void SetLoadId(void *aLoadId) { mLoadId = aLoadId; } void Cancel(nsresult aStatus); void RemoveFromCache(); + nsresult GetURI(nsIURI **aURI); nsresult GetSecurityInfo(nsISupports **aSecurityInfo); inline const char *GetMimeType() const { return mContentType.get(); } inline nsIProperties *Properties() { return mProperties; } + + // Return the imgStatusTracker associated with this imgRequest. It may live + // in |mStatusTracker| or in |mImage.mStatusTracker|, depending on whether + // mImage has been instantiated yet.. + imgStatusTracker& GetStatusTracker(); // Reset the cache entry after we've dropped our reference to it. Used by the // imgLoader when our cache entry is re-requested after we've dropped our // reference to it. void SetCacheEntry(imgCacheEntry *entry); // Returns whether we've got a reference to the cache entry. bool HasCacheEntry() const; + // Return true if at least one of our proxies, excluding + // aProxyToIgnore, has an observer. aProxyToIgnore may be null. + bool HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const; + // Return the priority of the underlying network request, or return // PRIORITY_NORMAL if it doesn't support nsISupportsPriority. int32_t Priority() const; // Adjust the priority of the underlying network request by the given delta // on behalf of the given proxy. void AdjustPriority(imgRequestProxy *aProxy, int32_t aDelta); // Return whether we've seen some data at this point bool HasTransferredData() const { return mGotData; } // Set whether this request is stored in the cache. If it isn't, regardless // of whether this request has a non-null mCacheEntry, this imgRequest won't // try to update or modify the image cache. void SetIsInCache(bool cacheable); - bool IsBlockingOnload() const; - void SetBlockingOnload(bool block) const; + // Update the cache entry size based on the image container + void UpdateCacheEntrySize(); public: + NS_DECL_IMGIDECODEROBSERVER + NS_DECL_IMGICONTAINEROBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK private: friend class imgMemoryReporter; @@ -196,16 +198,18 @@ private: // Status-tracker -- transferred to mImage, when it gets instantiated nsAutoPtr<imgStatusTracker> mStatusTracker; nsRefPtr<mozilla::image::Image> mImage; nsCOMPtr<nsIProperties> mProperties; nsCOMPtr<nsISupports> mSecurityInfo; nsCOMPtr<nsIChannel> mChannel; nsCOMPtr<nsIInterfaceRequestor> mPrevChannelSink; + nsTObserverArray<imgRequestProxy*> mObservers; + nsCOMPtr<nsITimedChannel> mTimedChannel; nsCString mContentType; nsRefPtr<imgCacheEntry> mCacheEntry; /* we hold on to this to this so long as we have observers */ void *mLoadId;
--- a/image/src/imgRequestProxy.cpp +++ b/image/src/imgRequestProxy.cpp @@ -35,27 +35,28 @@ NS_INTERFACE_MAP_BEGIN(imgRequestProxy) NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) NS_INTERFACE_MAP_ENTRY(nsISecurityInfoProvider) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel, TimedChannel() != nullptr) NS_INTERFACE_MAP_END imgRequestProxy::imgRequestProxy() : mOwner(nullptr), mURI(nullptr), + mImage(nullptr), + mPrincipal(nullptr), mListener(nullptr), mLoadFlags(nsIRequest::LOAD_NORMAL), mLockCount(0), mAnimationConsumers(0), mCanceled(false), mIsInLoadGroup(false), mListenerIsStrongRef(false), mDecodeRequested(false), mDeferNotifications(false), - mSentStartContainer(false), - mOwnerHasImage(false) + mSentStartContainer(false) { /* member initializers and constructor code */ } imgRequestProxy::~imgRequestProxy() { /* destructor code */ @@ -83,37 +84,36 @@ imgRequestProxy::~imgRequestProxy() the last observer. This allows the image to continue to download and be cached even if no one is using it currently. */ mOwner->RemoveProxy(this, NS_OK); } } } -nsresult imgRequestProxy::Init(imgStatusTracker* aStatusTracker, - nsILoadGroup* aLoadGroup, - nsIURI* aURI, imgINotificationObserver* aObserver) +nsresult imgRequestProxy::Init(imgRequest* request, nsILoadGroup* aLoadGroup, Image* aImage, + nsIURI* aURI, imgIDecoderObserver* aObserver) { NS_PRECONDITION(!mOwner && !mListener, "imgRequestProxy is already initialized"); - LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request", aStatusTracker->GetRequest()); + LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request", request); NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init"); - mOwner = aStatusTracker->GetRequest(); - mOwnerHasImage = !!aStatusTracker->GetImage(); + mOwner = request; mListener = aObserver; // Make sure to addref mListener before the AddProxy call below, since // that call might well want to release it if the imgRequest has // already seen OnStopRequest. if (mListener) { mListenerIsStrongRef = true; NS_ADDREF(mListener); } mLoadGroup = aLoadGroup; + mImage = aImage; mURI = aURI; // Note: AddProxy won't send all the On* notifications immediately if (mOwner) mOwner->AddProxy(this); return NS_OK; } @@ -127,48 +127,51 @@ nsresult imgRequestProxy::ChangeOwner(im uint32_t oldLockCount = mLockCount; while (mLockCount) UnlockImage(); // If we're holding animation requests, undo them. uint32_t oldAnimationConsumers = mAnimationConsumers; ClearAnimationConsumers(); - nsRefPtr<imgRequest> oldOwner = mOwner; - mOwner = aNewOwner; - mOwnerHasImage = !!GetStatusTracker().GetImage(); + // Even if we are cancelled, we MUST change our image, because the image + // holds our status, and the status must always be correct. + mImage = aNewOwner->mImage; // If we were locked, apply the locks here for (uint32_t i = 0; i < oldLockCount; i++) LockImage(); if (mCanceled) { // If we had animation requests, restore them before exiting // (otherwise we restore them later below) for (uint32_t i = 0; i < oldAnimationConsumers; i++) IncrementAnimationConsumers(); return NS_OK; } // Were we decoded before? bool wasDecoded = false; - if (GetImage() && - (GetStatusTracker().GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE)) { + if (mImage && + (mImage->GetStatusTracker().GetImageStatus() & + imgIRequest::STATUS_FRAME_COMPLETE)) { wasDecoded = true; } - oldOwner->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER, false); + mOwner->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER); // If we had animation requests, restore them here. Note that we // do this *after* RemoveProxy, which clears out animation consumers // (see bug 601723). for (uint32_t i = 0; i < oldAnimationConsumers; i++) IncrementAnimationConsumers(); + mOwner = aNewOwner; + mOwner->AddProxy(this); // If we were decoded, or if we'd previously requested a decode, request a // decode on the new image if (wasDecoded || mDecodeRequested) mOwner->StartDecoding(); return NS_OK; @@ -321,64 +324,65 @@ imgRequestProxy::RequestDecode() } /* void lockImage (); */ NS_IMETHODIMP imgRequestProxy::LockImage() { mLockCount++; - if (GetImage()) - return GetImage()->LockImage(); + if (mImage) + return mImage->LockImage(); return NS_OK; } /* void unlockImage (); */ NS_IMETHODIMP imgRequestProxy::UnlockImage() { NS_ABORT_IF_FALSE(mLockCount > 0, "calling unlock but no locks!"); mLockCount--; - if (GetImage()) - return GetImage()->UnlockImage(); + if (mImage) + return mImage->UnlockImage(); return NS_OK; } /* void requestDiscard (); */ NS_IMETHODIMP imgRequestProxy::RequestDiscard() { - if (GetImage()) - return GetImage()->RequestDiscard(); + if (mImage) { + return mImage->RequestDiscard(); + } return NS_OK; } NS_IMETHODIMP imgRequestProxy::IncrementAnimationConsumers() { mAnimationConsumers++; - if (GetImage()) - GetImage()->IncrementAnimationConsumers(); + if (mImage) + mImage->IncrementAnimationConsumers(); return NS_OK; } NS_IMETHODIMP imgRequestProxy::DecrementAnimationConsumers() { // We may get here if some responsible code called Increment, // then called us, but we have meanwhile called ClearAnimationConsumers // because we needed to get rid of them earlier (see // imgRequest::RemoveProxy), and hence have nothing left to // decrement. (In such a case we got rid of the animation consumers // early, but not the observer.) if (mAnimationConsumers > 0) { mAnimationConsumers--; - if (GetImage()) - GetImage()->DecrementAnimationConsumers(); + if (mImage) + mImage->DecrementAnimationConsumers(); } return NS_OK; } void imgRequestProxy::ClearAnimationConsumers() { while (mAnimationConsumers > 0) @@ -425,17 +429,17 @@ NS_IMETHODIMP imgRequestProxy::SetLoadFl /* attribute imgIContainer image; */ NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer * *aImage) { // It's possible that our owner has an image but hasn't notified us of it - // that'll happen if we get Canceled before the owner instantiates its image // (because Canceling unregisters us as a listener on mOwner). If we're // in that situation, just grab the image off of mOwner. - imgIContainer* imageToReturn = GetImage() ? GetImage() : mOwner->mImage.get(); + imgIContainer* imageToReturn = mImage ? mImage : mOwner->mImage; if (!imageToReturn) return NS_ERROR_FAILURE; NS_ADDREF(*aImage = imageToReturn); return NS_OK; } @@ -454,21 +458,21 @@ NS_IMETHODIMP imgRequestProxy::GetURI(ns if (!mURI) return NS_ERROR_FAILURE; NS_ADDREF(*aURI = mURI); return NS_OK; } -/* readonly attribute imgINotificationObserver notificationObserver; */ -NS_IMETHODIMP imgRequestProxy::GetNotificationObserver(imgINotificationObserver **aObserver) +/* readonly attribute imgIDecoderObserver decoderObserver; */ +NS_IMETHODIMP imgRequestProxy::GetDecoderObserver(imgIDecoderObserver **aDecoderObserver) { - *aObserver = mListener; - NS_IF_ADDREF(*aObserver); + *aDecoderObserver = mListener; + NS_IF_ADDREF(*aDecoderObserver); return NS_OK; } /* readonly attribute string mimeType; */ NS_IMETHODIMP imgRequestProxy::GetMimeType(char **aMimeType) { if (!mOwner) return NS_ERROR_FAILURE; @@ -477,76 +481,61 @@ NS_IMETHODIMP imgRequestProxy::GetMimeTy if (!type) return NS_ERROR_FAILURE; *aMimeType = NS_strdup(type); return NS_OK; } -static imgRequestProxy* NewProxy(imgRequestProxy* /*aThis*/) -{ - return new imgRequestProxy(); -} - -imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis) -{ - nsCOMPtr<nsIPrincipal> currentPrincipal; - aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal)); - return new imgRequestProxyStatic( - static_cast<imgRequestProxyStatic*>(aThis)->mImage, currentPrincipal); -} - -NS_IMETHODIMP imgRequestProxy::Clone(imgINotificationObserver* aObserver, +NS_IMETHODIMP imgRequestProxy::Clone(imgIDecoderObserver* aObserver, imgIRequest** aClone) { - return PerformClone(aObserver, NewProxy, aClone); -} - -nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver, - imgRequestProxy* (aAllocFn)(imgRequestProxy*), - imgIRequest** aClone) -{ NS_PRECONDITION(aClone, "Null out param"); LOG_SCOPE(gImgLog, "imgRequestProxy::Clone"); *aClone = nullptr; - nsRefPtr<imgRequestProxy> clone = aAllocFn(this); + nsRefPtr<imgRequestProxy> clone = new imgRequestProxy(); // It is important to call |SetLoadFlags()| before calling |Init()| because // |Init()| adds the request to the loadgroup. // When a request is added to a loadgroup, its load flags are merged // with the load flags of the loadgroup. // XXXldb That's not true anymore. Stuff from imgLoader adds the // request to the loadgroup. clone->SetLoadFlags(mLoadFlags); - nsresult rv = clone->Init(&GetStatusTracker(), mLoadGroup, mURI, aObserver); + nsresult rv = clone->Init(mOwner, mLoadGroup, + mImage ? mImage : mOwner->mImage, + mURI, aObserver); if (NS_FAILED(rv)) return rv; + clone->SetPrincipal(mPrincipal); + // Assign to *aClone before calling Notify so that if the caller expects to // only be notified for requests it's already holding pointers to it won't be // surprised. NS_ADDREF(*aClone = clone); // This is wrong!!! We need to notify asynchronously, but there's code that // assumes that we don't. This will be fixed in bug 580466. clone->SyncNotifyListener(); return NS_OK; } /* readonly attribute nsIPrincipal imagePrincipal; */ NS_IMETHODIMP imgRequestProxy::GetImagePrincipal(nsIPrincipal **aPrincipal) { - if (!mOwner) + if (!mPrincipal) return NS_ERROR_FAILURE; - NS_ADDREF(*aPrincipal = mOwner->GetPrincipal()); + NS_ADDREF(*aPrincipal = mPrincipal); + return NS_OK; } /* readonly attribute bool multipart; */ NS_IMETHODIMP imgRequestProxy::GetMultipart(bool *aMultipart) { if (!mOwner) return NS_ERROR_FAILURE; @@ -607,113 +596,168 @@ NS_IMETHODIMP imgRequestProxy::GetHasTra *hasData = mOwner->HasTransferredData(); } else { // The safe thing to do is to claim we have data *hasData = true; } return NS_OK; } +/** imgIContainerObserver methods **/ + +void imgRequestProxy::FrameChanged(imgIContainer *container, + const nsIntRect *dirtyRect) +{ + LOG_FUNC(gImgLog, "imgRequestProxy::FrameChanged"); + + if (mListener && !mCanceled) { + // Hold a ref to the listener while we call it, just in case. + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->FrameChanged(this, container, dirtyRect); + } +} + /** imgIDecoderObserver methods **/ -void imgRequestProxy::OnStartContainer() +void imgRequestProxy::OnStartDecode() +{ + LOG_FUNC(gImgLog, "imgRequestProxy::OnStartDecode"); + + if (mListener && !mCanceled) { + // Hold a ref to the listener while we call it, just in case. + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnStartDecode(this); + } +} + +void imgRequestProxy::OnStartContainer(imgIContainer *image) { LOG_FUNC(gImgLog, "imgRequestProxy::OnStartContainer"); if (mListener && !mCanceled && !mSentStartContainer) { // Hold a ref to the listener while we call it, just in case. - nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); - mListener->Notify(this, imgINotificationObserver::SIZE_AVAILABLE, nullptr); + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnStartContainer(this, image); mSentStartContainer = true; } } -void imgRequestProxy::OnFrameUpdate(const nsIntRect * rect) +void imgRequestProxy::OnStartFrame(uint32_t frame) +{ + LOG_FUNC(gImgLog, "imgRequestProxy::OnStartFrame"); + + if (mListener && !mCanceled) { + // Hold a ref to the listener while we call it, just in case. + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnStartFrame(this, frame); + } +} + +void imgRequestProxy::OnDataAvailable(bool aCurrentFrame, const nsIntRect * rect) { LOG_FUNC(gImgLog, "imgRequestProxy::OnDataAvailable"); if (mListener && !mCanceled) { // Hold a ref to the listener while we call it, just in case. - nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); - mListener->Notify(this, imgINotificationObserver::FRAME_UPDATE, rect); + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnDataAvailable(this, aCurrentFrame, rect); } } -void imgRequestProxy::OnStopFrame() +void imgRequestProxy::OnStopFrame(uint32_t frame) { LOG_FUNC(gImgLog, "imgRequestProxy::OnStopFrame"); if (mListener && !mCanceled) { // Hold a ref to the listener while we call it, just in case. - nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); - mListener->Notify(this, imgINotificationObserver::FRAME_COMPLETE, nullptr); + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnStopFrame(this, frame); } } -void imgRequestProxy::OnStopDecode() +void imgRequestProxy::OnStopContainer(imgIContainer *image) +{ + LOG_FUNC(gImgLog, "imgRequestProxy::OnStopContainer"); + + if (mListener && !mCanceled) { + // Hold a ref to the listener while we call it, just in case. + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnStopContainer(this, image); + } + + // Multipart needs reset for next OnStartContainer + if (mOwner && mOwner->GetMultipart()) + mSentStartContainer = false; +} + +void imgRequestProxy::OnStopDecode(nsresult status, const PRUnichar *statusArg) { LOG_FUNC(gImgLog, "imgRequestProxy::OnStopDecode"); if (mListener && !mCanceled) { // Hold a ref to the listener while we call it, just in case. - nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); - mListener->Notify(this, imgINotificationObserver::DECODE_COMPLETE, nullptr); + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnStopDecode(this, status, statusArg); } - - // Multipart needs reset for next OnStartContainer - if (mOwner && mOwner->GetMultipart()) - mSentStartContainer = false; } void imgRequestProxy::OnDiscard() { LOG_FUNC(gImgLog, "imgRequestProxy::OnDiscard"); if (mListener && !mCanceled) { // Hold a ref to the listener while we call it, just in case. - nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); - mListener->Notify(this, imgINotificationObserver::DISCARD, nullptr); + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnDiscard(this); } } void imgRequestProxy::OnImageIsAnimated() { LOG_FUNC(gImgLog, "imgRequestProxy::OnImageIsAnimated"); if (mListener && !mCanceled) { // Hold a ref to the listener while we call it, just in case. - nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); - mListener->Notify(this, imgINotificationObserver::IS_ANIMATED, nullptr); + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnImageIsAnimated(this); } } void imgRequestProxy::OnStartRequest() { #ifdef PR_LOGGING nsAutoCString name; GetName(name); LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStartRequest", "name", name.get()); #endif + + // Notify even if mCanceled, since OnStartRequest is guaranteed by the + // nsIStreamListener contract so it makes sense to do the same here. + if (mListener) { + // Hold a ref to the listener while we call it, just in case. + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnStartRequest(this); + } } void imgRequestProxy::OnStopRequest(bool lastPart) { #ifdef PR_LOGGING nsAutoCString name; GetName(name); LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStopRequest", "name", name.get()); #endif // There's all sorts of stuff here that could kill us (the OnStopRequest call // on the listener, the removal from the loadgroup, the release of the // listener, etc). Don't let them do it. nsCOMPtr<imgIRequest> kungFuDeathGrip(this); if (mListener) { // Hold a ref to the listener while we call it, just in case. - nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); - mListener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr); + nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener); + mListener->OnStopRequest(this, lastPart); } // If we're expecting more data from a multipart channel, re-add ourself // to the loadgroup so that the document doesn't lose track of the load. // If the request is already a background request and there's more data // coming, we can just leave the request in the loadgroup as-is. if (lastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) { RemoveFromLoadGroup(lastPart); @@ -725,17 +769,17 @@ void imgRequestProxy::OnStopRequest(bool } } if (mListenerIsStrongRef) { NS_PRECONDITION(mListener, "How did that happen?"); // Drop our strong ref to the listener now that we're done with // everything. Note that this can cancel us and other fun things // like that. Don't add anything in this method after this point. - imgINotificationObserver* obs = mListener; + imgIDecoderObserver* obs = mListener; mListenerIsStrongRef = false; NS_RELEASE(obs); } } void imgRequestProxy::BlockOnload() { #ifdef PR_LOGGING @@ -767,150 +811,116 @@ void imgRequestProxy::UnblockOnload() void imgRequestProxy::NullOutListener() { // If we have animation consumers, then they don't matter anymore if (mListener) ClearAnimationConsumers(); if (mListenerIsStrongRef) { // Releasing could do weird reentery stuff, so just play it super-safe - nsCOMPtr<imgINotificationObserver> obs; + nsCOMPtr<imgIDecoderObserver> obs; obs.swap(mListener); mListenerIsStrongRef = false; } else { mListener = nullptr; } } NS_IMETHODIMP imgRequestProxy::GetStaticRequest(imgIRequest** aReturn) { *aReturn = nullptr; - mozilla::image::Image* image = GetImage(); bool animated; - if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) { + if (!mImage || (NS_SUCCEEDED(mImage->GetAnimated(&animated)) && !animated)) { // Early exit - we're not animated, so we don't have to do anything. NS_ADDREF(*aReturn = this); return NS_OK; } // We are animated. We need to extract the current frame from this image. int32_t w = 0; int32_t h = 0; - image->GetWidth(&w); - image->GetHeight(&h); + mImage->GetWidth(&w); + mImage->GetHeight(&h); nsIntRect rect(0, 0, w, h); nsCOMPtr<imgIContainer> currentFrame; - nsresult rv = image->ExtractFrame(imgIContainer::FRAME_CURRENT, rect, - imgIContainer::FLAG_SYNC_DECODE, - getter_AddRefs(currentFrame)); + nsresult rv = mImage->ExtractFrame(imgIContainer::FRAME_CURRENT, rect, + imgIContainer::FLAG_SYNC_DECODE, + getter_AddRefs(currentFrame)); if (NS_FAILED(rv)) return rv; nsRefPtr<Image> frame = static_cast<Image*>(currentFrame.get()); // Create a static imgRequestProxy with our new extracted frame. - nsCOMPtr<nsIPrincipal> currentPrincipal; - GetImagePrincipal(getter_AddRefs(currentPrincipal)); - nsRefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frame, currentPrincipal); - req->Init(&frame->GetStatusTracker(), nullptr, mURI, nullptr); + nsRefPtr<imgRequestProxy> req = new imgRequestProxy(); + req->Init(nullptr, nullptr, frame, mURI, nullptr); + req->SetPrincipal(mPrincipal); NS_ADDREF(*aReturn = req); return NS_OK; } +void imgRequestProxy::SetPrincipal(nsIPrincipal *aPrincipal) +{ + mPrincipal = aPrincipal; +} + void imgRequestProxy::NotifyListener() { // It would be nice to notify the observer directly in the status tracker // instead of through the proxy, but there are several places we do extra // processing when we receive notifications (like OnStopRequest()), and we // need to check mCanceled everywhere too. if (mOwner) { // Send the notifications to our listener asynchronously. GetStatusTracker().Notify(mOwner, this); } else { // We don't have an imgRequest, so we can only notify the clone of our // current state, but we still have to do that asynchronously. - NS_ABORT_IF_FALSE(GetImage(), + NS_ABORT_IF_FALSE(mImage, "if we have no imgRequest, we should have an Image"); - GetStatusTracker().NotifyCurrentState(this); + mImage->GetStatusTracker().NotifyCurrentState(this); } } void imgRequestProxy::SyncNotifyListener() { // It would be nice to notify the observer directly in the status tracker // instead of through the proxy, but there are several places we do extra // processing when we receive notifications (like OnStopRequest()), and we // need to check mCanceled everywhere too. GetStatusTracker().SyncNotify(this); } void -imgRequestProxy::SetHasImage() +imgRequestProxy::SetImage(Image* aImage) { - Image* image = GetStatusTracker().GetImage(); + NS_ABORT_IF_FALSE(aImage, "Setting null image"); + NS_ABORT_IF_FALSE(!mImage || mOwner->GetMultipart(), + "Setting image when we already have one"); - mOwnerHasImage = true; + mImage = aImage; // Apply any locks we have for (uint32_t i = 0; i < mLockCount; ++i) - image->LockImage(); + mImage->LockImage(); // Apply any animation consumers we have for (uint32_t i = 0; i < mAnimationConsumers; i++) - image->IncrementAnimationConsumers(); + mImage->IncrementAnimationConsumers(); } imgStatusTracker& -imgRequestProxy::GetStatusTracker() const +imgRequestProxy::GetStatusTracker() { // NOTE: It's possible that our mOwner has an Image that it didn't notify // us about, if we were Canceled before its Image was constructed. // (Canceling removes us as an observer, so mOwner has no way to notify us). // That's why this method uses mOwner->GetStatusTracker() instead of just // mOwner->mStatusTracker -- we might have a null mImage and yet have an // mOwner with a non-null mImage (and a null mStatusTracker pointer). - return mOwner->GetStatusTracker(); -} - -mozilla::image::Image* -imgRequestProxy::GetImage() const -{ - if (!mOwnerHasImage) - return nullptr; - return GetStatusTracker().GetImage(); + return mImage ? mImage->GetStatusTracker() : mOwner->GetStatusTracker(); } - -////////////////// imgRequestProxyStatic methods - -NS_IMETHODIMP imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal **aPrincipal) -{ - if (!mPrincipal) - return NS_ERROR_FAILURE; - - NS_ADDREF(*aPrincipal = mPrincipal); - - return NS_OK; -} - -mozilla::image::Image* -imgRequestProxyStatic::GetImage() const -{ - return mImage; -} - -imgStatusTracker& -imgRequestProxyStatic::GetStatusTracker() const -{ - return mImage->GetStatusTracker(); -} - -NS_IMETHODIMP -imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver, - imgIRequest** aClone) -{ - return PerformClone(aObserver, NewStaticProxy, aClone); -}
--- a/image/src/imgRequestProxy.h +++ b/image/src/imgRequestProxy.h @@ -3,17 +3,17 @@ * 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 imgRequestProxy_h__ #define imgRequestProxy_h__ #include "imgIRequest.h" -#include "imgINotificationObserver.h" +#include "imgIDecoderObserver.h" #include "nsISecurityInfoProvider.h" #include "nsIRequestObserver.h" #include "nsIChannel.h" #include "nsILoadGroup.h" #include "nsISupportsPriority.h" #include "nsITimedChannel.h" #include "nsCOMPtr.h" @@ -52,30 +52,32 @@ public: NS_DECL_NSISECURITYINFOPROVIDER // nsITimedChannel declared below imgRequestProxy(); virtual ~imgRequestProxy(); // Callers to Init or ChangeOwner are required to call NotifyListener after // (although not immediately after) doing so. - nsresult Init(imgStatusTracker* aStatusTracker, - nsILoadGroup *aLoadGroup, - nsIURI* aURI, imgINotificationObserver *aObserver); + nsresult Init(imgRequest *request, nsILoadGroup *aLoadGroup, + mozilla::image::Image* aImage, + nsIURI* aURI, imgIDecoderObserver *aObserver); nsresult ChangeOwner(imgRequest *aNewOwner); // this will change mOwner. Do not call this if the previous // owner has already sent notifications out! void AddToLoadGroup(); void RemoveFromLoadGroup(bool releaseLoadGroup); inline bool HasObserver() const { return mListener != nullptr; } + void SetPrincipal(nsIPrincipal *aPrincipal); + // Asynchronously notify this proxy's listener of the current state of the // image, and, if we have an imgRequest mOwner, any status changes that // happen between the time this function is called and the time the // notification is scheduled. void NotifyListener(); // Synchronously notify this proxy's listener of the current state of the // image. Only use this function if you are currently servicing an @@ -88,18 +90,19 @@ public: { return mDeferNotifications; } void SetNotificationsDeferred(bool aDeferNotifications) { mDeferNotifications = aDeferNotifications; } - // XXXbholley - This eventually gets folded into the new notification API. - void SetHasImage(); + // Setter for our |mImage| pointer, for imgRequest to use, once it + // instantiates an Image. + void SetImage(mozilla::image::Image* aImage); // Removes all animation consumers that were created with // IncrementAnimationConsumers. This is necessary since we need // to do it before the proxy itself is destroyed. See // imgRequest::RemoveProxy void ClearAnimationConsumers(); protected: @@ -127,23 +130,30 @@ protected: nsresult mStatus; }; // The following notification functions are protected to ensure that (friend // class) imgStatusTracker is the only class allowed to send us // notifications. /* non-virtual imgIDecoderObserver methods */ - void OnStartContainer (); - void OnFrameUpdate (const nsIntRect * aRect); - void OnStopFrame (); - void OnStopDecode (); + void OnStartDecode (); + void OnStartContainer (imgIContainer *aContainer); + void OnStartFrame (uint32_t aFrame); + void OnDataAvailable (bool aCurrentFrame, const nsIntRect * aRect); + void OnStopFrame (uint32_t aFrame); + void OnStopContainer (imgIContainer *aContainer); + void OnStopDecode (nsresult status, const PRUnichar *statusArg); void OnDiscard (); void OnImageIsAnimated (); + /* non-virtual imgIContainerObserver methods */ + void FrameChanged(imgIContainer *aContainer, + const nsIntRect *aDirtyRect); + /* non-virtual sort-of-nsIRequestObserver methods */ void OnStartRequest(); void OnStopRequest(bool aLastPart); /* non-virtual imgIOnloadBlocker methods */ void BlockOnload(); void UnblockOnload(); @@ -156,31 +166,25 @@ protected: void DoRemoveFromLoadGroup() { RemoveFromLoadGroup(true); } // Return the imgStatusTracker associated with mOwner and/or mImage. It may // live either on mOwner or mImage, depending on whether // (a) we have an mOwner at all // (b) whether mOwner has instantiated its image yet - virtual imgStatusTracker& GetStatusTracker() const; + imgStatusTracker& GetStatusTracker(); nsITimedChannel* TimedChannel() { if (!mOwner) return nullptr; return mOwner->mTimedChannel; } - virtual mozilla::image::Image* GetImage() const; - - nsresult PerformClone(imgINotificationObserver* aObserver, - imgRequestProxy* (aAllocFn)(imgRequestProxy*), - imgIRequest** aClone); - public: NS_FORWARD_SAFE_NSITIMEDCHANNEL(TimedChannel()) private: friend class imgCacheValidator; // We maintain the following invariant: // The proxy is registered at most with a single imgRequest as an observer, @@ -188,20 +192,28 @@ private: // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer // from whatever request it was registered with (if any). This, in turn, // means that imgRequest::mObservers will not have any stale pointers in it. nsRefPtr<imgRequest> mOwner; // The URI of our request. nsCOMPtr<nsIURI> mURI; + // The image we represent. Is null until data has been received, and is then + // set by imgRequest. + nsRefPtr<mozilla::image::Image> mImage; + + // Our principal. Is null until data has been received from the channel, and + // is then set by imgRequest. + nsCOMPtr<nsIPrincipal> mPrincipal; + // mListener is only promised to be a weak ref (see imgILoader.idl), // but we actually keep a strong ref to it until we've seen our // first OnStopRequest. - imgINotificationObserver* mListener; + imgIDecoderObserver* mListener; nsCOMPtr<nsILoadGroup> mLoadGroup; nsLoadFlags mLoadFlags; uint32_t mLockCount; uint32_t mAnimationConsumers; bool mCanceled; bool mIsInLoadGroup; bool mListenerIsStrongRef; @@ -209,49 +221,11 @@ private: // Whether we want to defer our notifications by the non-virtual Observer // interfaces as image loads proceed. bool mDeferNotifications; // We only want to send OnStartContainer once for each proxy, but we might // get multiple OnStartContainer calls. bool mSentStartContainer; - - protected: - bool mOwnerHasImage; -}; - -// Used for static image proxies for which no requests are available, so -// certain behaviours must be overridden to compensate. -class imgRequestProxyStatic : public imgRequestProxy -{ - -public: - imgRequestProxyStatic(mozilla::image::Image* aImage, - nsIPrincipal* aPrincipal) - : mImage(aImage) - , mPrincipal(aPrincipal) - { - mOwnerHasImage = true; - }; - - NS_IMETHOD GetImagePrincipal(nsIPrincipal** aPrincipal) MOZ_OVERRIDE; - virtual imgStatusTracker& GetStatusTracker() const MOZ_OVERRIDE; - - NS_IMETHOD Clone(imgINotificationObserver* aObserver, - imgIRequest** aClone) MOZ_OVERRIDE; - -protected: - friend imgRequestProxy* NewStaticProxy(imgRequestProxy*); - - // Our image. We have to hold a strong reference here, because that's normally - // the job of the underlying request. - nsRefPtr<mozilla::image::Image> mImage; - - // Our principal. We have to cache it, rather than accessing the underlying - // request on-demand, because static proxies don't have an underlying request. - nsCOMPtr<nsIPrincipal> mPrincipal; - - mozilla::image::Image* GetImage() const MOZ_OVERRIDE; - using imgRequestProxy::GetImage; }; #endif // imgRequestProxy_h__
--- a/image/src/imgStatusTracker.cpp +++ b/image/src/imgStatusTracker.cpp @@ -7,240 +7,41 @@ #include "imgStatusTracker.h" #include "imgRequest.h" #include "imgIContainer.h" #include "imgRequestProxy.h" #include "Image.h" #include "ImageLogging.h" #include "RasterImage.h" -#include "nsIObserverService.h" - -#include "mozilla/Util.h" -#include "mozilla/Assertions.h" -#include "mozilla/Services.h" using namespace mozilla::image; -NS_IMPL_ISUPPORTS3(imgStatusTrackerObserver, - imgIDecoderObserver, - imgIContainerObserver, - nsISupportsWeakReference) - -/** imgIContainerObserver methods **/ - -/* [noscript] void frameChanged (in nsIntRect dirtyRect); */ -NS_IMETHODIMP imgStatusTrackerObserver::FrameChanged(const nsIntRect *dirtyRect) -{ - LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::FrameChanged"); - NS_ABORT_IF_FALSE(mTracker->GetImage(), - "FrameChanged callback before we've created our image"); - - mTracker->RecordFrameChanged(dirtyRect); - - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers); - while (iter.HasMore()) { - mTracker->SendFrameChanged(iter.GetNext(), dirtyRect); - } - - return NS_OK; -} - -/** imgIDecoderObserver methods **/ - -NS_IMETHODIMP imgStatusTrackerObserver::OnStartDecode() +static nsresult +GetResultFromImageStatus(uint32_t aStatus) { - LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStartDecode"); - NS_ABORT_IF_FALSE(mTracker->GetImage(), - "OnStartDecode callback before we've created our image"); - - if (!mTracker->GetRequest()->GetMultipart()) { - MOZ_ASSERT(!mTracker->mBlockingOnload); - mTracker->mBlockingOnload = true; - - mTracker->RecordBlockOnload(); - - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers); - while (iter.HasMore()) { - mTracker->SendBlockOnload(iter.GetNext()); - } - } - - /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which - indicates the beginning of a new decode. - The cache entry's size therefore needs to be reset to 0 here. If we do not do this, - the code in imgStatusTrackerObserver::OnStopFrame will continue to increase the data size cumulatively. - */ - mTracker->GetRequest()->ResetCacheEntry(); - - return NS_OK; -} - -NS_IMETHODIMP imgStatusTrackerObserver::OnStartRequest() -{ - NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest"); - return NS_OK; -} - -/* void onStartContainer (); */ -NS_IMETHODIMP imgStatusTrackerObserver::OnStartContainer() -{ - LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStartContainer"); - - NS_ABORT_IF_FALSE(mTracker->GetImage(), - "OnStartContainer callback before we've created our image"); - mTracker->RecordStartContainer(mTracker->GetImage()); - - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers); - while (iter.HasMore()) { - mTracker->SendStartContainer(iter.GetNext()); - } - - return NS_OK; -} - -/* [noscript] void onDataAvailable ([const] in nsIntRect rect); */ -NS_IMETHODIMP imgStatusTrackerObserver::OnDataAvailable(const nsIntRect * rect) -{ - LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnDataAvailable"); - NS_ABORT_IF_FALSE(mTracker->GetImage(), - "OnDataAvailable callback before we've created our image"); - - mTracker->RecordDataAvailable(); - - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers); - while (iter.HasMore()) { - mTracker->SendDataAvailable(iter.GetNext(), rect); - } - + if (aStatus & imgIRequest::STATUS_ERROR) + return NS_IMAGELIB_ERROR_FAILURE; + if (aStatus & imgIRequest::STATUS_LOAD_COMPLETE) + return NS_IMAGELIB_SUCCESS_LOAD_FINISHED; return NS_OK; } -/* void onStopFrame (); */ -NS_IMETHODIMP imgStatusTrackerObserver::OnStopFrame() -{ - LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStopFrame"); - NS_ABORT_IF_FALSE(mTracker->GetImage(), - "OnStopFrame callback before we've created our image"); - - mTracker->RecordStopFrame(); - - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers); - while (iter.HasMore()) { - mTracker->SendStopFrame(iter.GetNext()); - } - - mTracker->MaybeUnblockOnload(); - - return NS_OK; -} - -static void -FireFailureNotification(imgRequest* aRequest) -{ - // Some kind of problem has happened with image decoding. - // Report the URI to net:failed-to-process-uri-conent observers. - - nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); - if (os) { - nsCOMPtr<nsIURI> uri; - aRequest->GetURI(getter_AddRefs(uri)); - os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr); - } -} - -/* void onStopDecode (in nsresult status); */ -NS_IMETHODIMP imgStatusTrackerObserver::OnStopDecode(nsresult aStatus) -{ - LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStopDecode"); - NS_ABORT_IF_FALSE(mTracker->GetImage(), - "OnStopDecode callback before we've created our image"); - - // We finished the decode, and thus have the decoded frames. Update the cache - // entry size to take this into account. - mTracker->GetRequest()->UpdateCacheEntrySize(); - - bool preexistingError = mTracker->GetImageStatus() == imgIRequest::STATUS_ERROR; - - mTracker->RecordStopDecode(aStatus); - - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers); - while (iter.HasMore()) { - mTracker->SendStopDecode(iter.GetNext(), aStatus); - } - - // 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. - mTracker->MaybeUnblockOnload(); - - if (NS_FAILED(aStatus) && !preexistingError) { - FireFailureNotification(mTracker->GetRequest()); - } - - return NS_OK; -} - -NS_IMETHODIMP imgStatusTrackerObserver::OnStopRequest(bool aLastPart) -{ - NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest"); - return NS_OK; -} - -/* void onDiscard (); */ -NS_IMETHODIMP imgStatusTrackerObserver::OnDiscard() -{ - NS_ABORT_IF_FALSE(mTracker->GetImage(), - "OnDiscard callback before we've created our image"); - - mTracker->RecordDiscard(); - - // Update the cache entry size, since we just got rid of frame data - mTracker->GetRequest()->UpdateCacheEntrySize(); - - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers); - while (iter.HasMore()) { - mTracker->SendDiscard(iter.GetNext()); - } - - return NS_OK; -} - -NS_IMETHODIMP imgStatusTrackerObserver::OnImageIsAnimated() -{ - NS_ABORT_IF_FALSE(mTracker->GetImage(), - "OnImageIsAnimated callback before we've created our image"); - mTracker->RecordImageIsAnimated(); - - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers); - while (iter.HasMore()) { - mTracker->SendImageIsAnimated(iter.GetNext()); - } - - return NS_OK; -} - -// imgStatusTracker methods - -imgStatusTracker::imgStatusTracker(Image* aImage, imgRequest* aRequest) +imgStatusTracker::imgStatusTracker(Image* aImage) : mImage(aImage), - mRequest(aRequest), mState(0), mImageStatus(imgIRequest::STATUS_NONE), - mHadLastPart(false), - mBlockingOnload(false), - mTrackerObserver(new imgStatusTrackerObserver(this)) + mHadLastPart(false) {} imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther) : mImage(aOther.mImage), - mRequest(aOther.mRequest), mState(aOther.mState), mImageStatus(aOther.mImageStatus), - mHadLastPart(aOther.mHadLastPart), - mBlockingOnload(aOther.mBlockingOnload) + mHadLastPart(aOther.mHadLastPart) // Note: we explicitly don't copy mRequestRunnable, because it won't be // nulled out when the mRequestRunnable's Run function eventually gets // called. {} void imgStatusTracker::SetImage(Image* aImage) { @@ -388,102 +189,87 @@ imgStatusTracker::SyncNotify(imgRequestP nsCOMPtr<imgIRequest> kungFuDeathGrip(proxy); // OnStartRequest if (mState & stateRequestStarted) proxy->OnStartRequest(); // OnStartContainer if (mState & stateHasSize) - proxy->OnStartContainer(); + proxy->OnStartContainer(mImage); + + // OnStartDecode + if (mState & stateDecodeStarted) + proxy->OnStartDecode(); // BlockOnload if (mState & stateBlockingOnload) proxy->BlockOnload(); if (mImage) { int16_t imageType = mImage->GetType(); - // Send frame messages (OnDataAvailable, OnStopFrame) + // Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame) if (imageType == imgIContainer::TYPE_VECTOR || static_cast<RasterImage*>(mImage)->GetNumFrames() > 0) { + uint32_t frame = (imageType == imgIContainer::TYPE_VECTOR) ? + 0 : static_cast<RasterImage*>(mImage)->GetCurrentFrameIndex(); + + proxy->OnStartFrame(frame); + // OnDataAvailable // XXX - Should only send partial rects here, but that needs to // wait until we fix up the observer interface nsIntRect r; mImage->GetCurrentFrameRect(r); - proxy->OnFrameUpdate(&r); + proxy->OnDataAvailable(frame, &r); if (mState & stateFrameStopped) - proxy->OnStopFrame(); + proxy->OnStopFrame(frame); } // OnImageIsAnimated bool isAnimated = false; nsresult rv = mImage->GetAnimated(&isAnimated); if (NS_SUCCEEDED(rv) && isAnimated) { proxy->OnImageIsAnimated(); } } + // See bug 505385 and imgRequest::OnStopDecode for more information on why we + // call OnStopContainer based on stateDecodeStopped, and why OnStopDecode is + // called with OnStopRequest. if (mState & stateDecodeStopped) { NS_ABORT_IF_FALSE(mImage, "stopped decoding without ever having an image?"); - proxy->OnStopDecode(); + proxy->OnStopContainer(mImage); } if (mState & stateRequestStopped) { + proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nullptr); proxy->OnStopRequest(mHadLastPart); } } void imgStatusTracker::EmulateRequestFinished(imgRequestProxy* aProxy, nsresult aStatus) { nsCOMPtr<imgIRequest> kungFuDeathGrip(aProxy); - // In certain cases the request might not have started yet. - // We still need to fulfill the contract. - if (!(mState & stateRequestStarted)) { - aProxy->OnStartRequest(); - } - if (mState & stateBlockingOnload) { aProxy->UnblockOnload(); } if (!(mState & stateRequestStopped)) { aProxy->OnStopRequest(true); } } void -imgStatusTracker::AddConsumer(imgRequestProxy* aConsumer) -{ - mConsumers.AppendElementUnlessExists(aConsumer); -} - -// XXX - The last two arguments should go away. -bool -imgStatusTracker::RemoveConsumer(imgRequestProxy* aConsumer, - nsresult aStatus, - bool aOnlySendStopRequest) -{ - // Remove the proxy from the list. - bool removed = mConsumers.RemoveElement(aConsumer); - - // Consumers can get confused if they don't get all the proper teardown - // notifications. Part ways on good terms. - if (removed) - EmulateRequestFinished(aConsumer, aStatus, aOnlySendStopRequest); - return removed; -} - -void imgStatusTracker::RecordCancel() { if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL)) mImageStatus |= imgIRequest::STATUS_ERROR; } void imgStatusTracker::RecordLoaded() @@ -493,100 +279,147 @@ imgStatusTracker::RecordLoaded() mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE | imgIRequest::STATUS_LOAD_COMPLETE; mHadLastPart = true; } void imgStatusTracker::RecordDecoded() { NS_ABORT_IF_FALSE(mImage, "RecordDecoded called before we have an Image"); - mState |= stateDecodeStopped | stateFrameStopped; + mState |= stateDecodeStarted | stateDecodeStopped | stateFrameStopped; mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE; } +/* non-virtual imgIDecoderObserver methods */ +void +imgStatusTracker::RecordStartDecode() +{ + NS_ABORT_IF_FALSE(mImage, "RecordStartDecode without an Image"); + mState |= stateDecodeStarted; +} + +void +imgStatusTracker::SendStartDecode(imgRequestProxy* aProxy) +{ + if (!aProxy->NotificationsDeferred()) + aProxy->OnStartDecode(); +} + void imgStatusTracker::RecordStartContainer(imgIContainer* aContainer) { NS_ABORT_IF_FALSE(mImage, "RecordStartContainer called before we have an Image"); NS_ABORT_IF_FALSE(mImage == aContainer, "RecordStartContainer called with wrong Image"); mState |= stateHasSize; mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE; } void -imgStatusTracker::SendStartContainer(imgRequestProxy* aProxy) +imgStatusTracker::SendStartContainer(imgRequestProxy* aProxy, imgIContainer* aContainer) { if (!aProxy->NotificationsDeferred()) - aProxy->OnStartContainer(); + aProxy->OnStartContainer(aContainer); } void -imgStatusTracker::RecordDataAvailable() +imgStatusTracker::RecordStartFrame(uint32_t aFrame) +{ + NS_ABORT_IF_FALSE(mImage, "RecordStartFrame called before we have an Image"); + // no bookkeeping necessary here - this is implied by imgIContainer's number + // of frames +} + +void +imgStatusTracker::SendStartFrame(imgRequestProxy* aProxy, uint32_t aFrame) +{ + if (!aProxy->NotificationsDeferred()) + aProxy->OnStartFrame(aFrame); +} + +void +imgStatusTracker::RecordDataAvailable(bool aCurrentFrame, const nsIntRect* aRect) { NS_ABORT_IF_FALSE(mImage, "RecordDataAvailable called before we have an Image"); // no bookkeeping necessary here - this is implied by imgIContainer's // number of frames and frame rect } void -imgStatusTracker::SendDataAvailable(imgRequestProxy* aProxy, - const nsIntRect* aRect) +imgStatusTracker::SendDataAvailable(imgRequestProxy* aProxy, bool aCurrentFrame, + const nsIntRect* aRect) { if (!aProxy->NotificationsDeferred()) - aProxy->OnFrameUpdate(aRect); + aProxy->OnDataAvailable(aCurrentFrame, aRect); } void -imgStatusTracker::RecordStopFrame() +imgStatusTracker::RecordStopFrame(uint32_t aFrame) { NS_ABORT_IF_FALSE(mImage, "RecordStopFrame called before we have an Image"); mState |= stateFrameStopped; mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE; } void -imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy) +imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy, uint32_t aFrame) { if (!aProxy->NotificationsDeferred()) - aProxy->OnStopFrame(); + aProxy->OnStopFrame(aFrame); } void -imgStatusTracker::RecordStopDecode(nsresult aStatus) +imgStatusTracker::RecordStopContainer(imgIContainer* aContainer) +{ + NS_ABORT_IF_FALSE(mImage, + "RecordStopContainer called before we have an Image"); + // No-op: see imgRequest::OnStopDecode for more information +} + +void +imgStatusTracker::SendStopContainer(imgRequestProxy* aProxy, imgIContainer* aContainer) +{ + // No-op: see imgRequest::OnStopDecode for more information +} + +void +imgStatusTracker::RecordStopDecode(nsresult aStatus, const PRUnichar* statusArg) { NS_ABORT_IF_FALSE(mImage, "RecordStopDecode called before we have an Image"); mState |= stateDecodeStopped; - if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR) + if (NS_SUCCEEDED(aStatus)) mImageStatus |= imgIRequest::STATUS_DECODE_COMPLETE; // If we weren't successful, clear all success status bits and set error. else mImageStatus = imgIRequest::STATUS_ERROR; } void -imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy, - nsresult aStatus) +imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus, + const PRUnichar* statusArg) { + // See imgRequest::OnStopDecode for more information on why we call + // OnStopContainer from here this, and why imgRequestProxy::OnStopDecode() is + // called from OnStopRequest() and SyncNotify(). if (!aProxy->NotificationsDeferred()) - aProxy->OnStopDecode(); + aProxy->OnStopContainer(mImage); } void imgStatusTracker::RecordDiscard() { NS_ABORT_IF_FALSE(mImage, "RecordDiscard called before we have an Image"); // Clear the state bits we no longer deserve. - uint32_t stateBitsToClear = stateDecodeStopped; + uint32_t stateBitsToClear = stateDecodeStarted | stateDecodeStopped; mState &= ~stateBitsToClear; // Clear the status bits we no longer deserve. uint32_t statusBitsToClear = imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE; mImageStatus &= ~statusBitsToClear; } @@ -612,115 +445,77 @@ void imgStatusTracker::SendDiscard(imgRequestProxy* aProxy) { if (!aProxy->NotificationsDeferred()) aProxy->OnDiscard(); } /* non-virtual imgIContainerObserver methods */ void -imgStatusTracker::RecordFrameChanged(const nsIntRect* aDirtyRect) +imgStatusTracker::RecordFrameChanged(imgIContainer* aContainer, + const nsIntRect* aDirtyRect) { NS_ABORT_IF_FALSE(mImage, "RecordFrameChanged called before we have an Image"); // no bookkeeping necessary here - this is only for in-frame updates, which we // don't fire while we're recording } void -imgStatusTracker::SendFrameChanged(imgRequestProxy* aProxy, +imgStatusTracker::SendFrameChanged(imgRequestProxy* aProxy, imgIContainer* aContainer, const nsIntRect* aDirtyRect) { if (!aProxy->NotificationsDeferred()) - aProxy->OnFrameUpdate(aDirtyRect); + aProxy->FrameChanged(aContainer, aDirtyRect); } /* non-virtual sort-of-nsIRequestObserver methods */ void imgStatusTracker::RecordStartRequest() { // We're starting a new load, so clear any status and state bits indicating // load/decode mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL; mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE; mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE; mState &= ~stateRequestStarted; + mState &= ~stateDecodeStarted; mState &= ~stateDecodeStopped; mState &= ~stateRequestStopped; mState &= ~stateBlockingOnload; mState |= stateRequestStarted; } void imgStatusTracker::SendStartRequest(imgRequestProxy* aProxy) { if (!aProxy->NotificationsDeferred()) aProxy->OnStartRequest(); } void -imgStatusTracker::OnStartRequest() -{ - RecordStartRequest(); - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers); - while (iter.HasMore()) { - SendStartRequest(iter.GetNext()); - } -} - -void -imgStatusTracker::RecordStopRequest(bool aLastPart, - nsresult aStatus) +imgStatusTracker::RecordStopRequest(bool aLastPart, nsresult aStatus) { mHadLastPart = aLastPart; mState |= stateRequestStopped; // If we were successful in loading, note that the image is complete. - if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR) + if (NS_SUCCEEDED(aStatus)) mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE; - else - mImageStatus = imgIRequest::STATUS_ERROR; -} - -void -imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy, - bool aLastPart, - nsresult aStatus) -{ - if (!aProxy->NotificationsDeferred()) { - aProxy->OnStopRequest(aLastPart); - } } void -imgStatusTracker::OnStopRequest(bool aLastPart, - nsresult aStatus) +imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus) { - bool preexistingError = mImageStatus == imgIRequest::STATUS_ERROR; - - RecordStopRequest(aLastPart, aStatus); - /* notify the kids */ - nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mConsumers); - while (srIter.HasMore()) { - SendStopRequest(srIter.GetNext(), aLastPart, aStatus); - } - - if (NS_FAILED(aStatus) && !preexistingError) { - FireFailureNotification(GetRequest()); - } -} - -void -imgStatusTracker::OnDataAvailable() -{ - // Notify any imgRequestProxys that are observing us that we have an Image. - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers); - while (iter.HasMore()) { - iter.GetNext()->SetHasImage(); + // See bug 505385 and imgRequest::OnStopDecode for more information on why + // OnStopDecode is called with OnStopRequest. + if (!aProxy->NotificationsDeferred()) { + aProxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nullptr); + aProxy->OnStopRequest(aLastPart); } } void imgStatusTracker::RecordBlockOnload() { MOZ_ASSERT(!(mState & stateBlockingOnload)); mState |= stateBlockingOnload; @@ -743,25 +538,8 @@ imgStatusTracker::RecordUnblockOnload() void imgStatusTracker::SendUnblockOnload(imgRequestProxy* aProxy) { if (!aProxy->NotificationsDeferred()) { aProxy->UnblockOnload(); } } - -void -imgStatusTracker::MaybeUnblockOnload() -{ - if (!mBlockingOnload) { - return; - } - - mBlockingOnload = false; - - RecordUnblockOnload(); - - nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers); - while (iter.HasMore()) { - SendUnblockOnload(iter.GetNext()); - } -}
--- a/image/src/imgStatusTracker.h +++ b/image/src/imgStatusTracker.h @@ -7,63 +7,38 @@ #ifndef imgStatusTracker_h__ #define imgStatusTracker_h__ class imgIContainer; class imgRequest; class imgRequestProxy; class imgStatusNotifyRunnable; class imgRequestNotifyRunnable; -class imgStatusTracker; struct nsIntRect; namespace mozilla { namespace image { class Image; } // namespace image } // namespace mozilla #include "nsCOMPtr.h" -#include "nsAutoPtr.h" -#include "nsTObserverArray.h" #include "nsIRunnable.h" #include "nscore.h" -#include "nsWeakReference.h" -#include "imgIDecoderObserver.h" enum { stateRequestStarted = PR_BIT(0), stateHasSize = PR_BIT(1), + stateDecodeStarted = PR_BIT(2), stateDecodeStopped = PR_BIT(3), stateFrameStopped = PR_BIT(4), stateRequestStopped = PR_BIT(5), stateBlockingOnload = PR_BIT(6) }; -class imgStatusTrackerObserver : public imgIDecoderObserver, - public nsSupportsWeakReference -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_IMGIDECODEROBSERVER - NS_DECL_IMGICONTAINEROBSERVER - - imgStatusTrackerObserver(imgStatusTracker* aTracker) - : mTracker(aTracker) {} - - virtual ~imgStatusTrackerObserver() {} - - void SetTracker(imgStatusTracker* aTracker) { - mTracker = aTracker; - } - -private: - imgStatusTracker* mTracker; -}; - /* * The image status tracker is a class that encapsulates all the loading and * decoding status about an Image, and makes it possible to send notifications * to imgRequestProxys, both synchronously (i.e., the status now) and * asynchronously (the status later). * * When a new proxy needs to be notified of the current state of an image, call * the Notify() method on this class with the relevant proxy as its argument, @@ -71,17 +46,17 @@ private: */ class imgStatusTracker { public: // aImage is the image that this status tracker will pass to the // imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be // alive as long as this instance is, because we hold a weak reference to it. - imgStatusTracker(mozilla::image::Image* aImage, imgRequest* aRequest); + imgStatusTracker(mozilla::image::Image* aImage); imgStatusTracker(const imgStatusTracker& aOther); // Image-setter, for imgStatusTrackers created by imgRequest::Init, which // are created before their Image is created. This method should only // be called once, and only on an imgStatusTracker that was initialized // without an image. void SetImage(mozilla::image::Image* aImage); @@ -105,31 +80,16 @@ public: // OnStartRequest). void SyncNotify(imgRequestProxy* proxy); // Send some notifications that would be necessary to make |proxy| believe // the request is finished downloading and decoding. We only send // OnStopRequest and UnblockOnload, and only if necessary. void EmulateRequestFinished(imgRequestProxy* proxy, nsresult aStatus); - // We manage a set of consumers that are using an image and thus concerned - // with its status. Weak pointers. - void AddConsumer(imgRequestProxy* aConsumer); - bool RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus, bool aOnlySendStopRequest); - size_t ConsumerCount() const { return mConsumers.Length(); }; - - // This is intentionally non-general because its sole purpose is to support an - // some obscure network priority logic in imgRequest. That stuff could probably - // be improved, but it's too scary to mess with at the moment. - bool FirstConsumerIs(imgRequestProxy* aConsumer) { - return mConsumers.SafeElementAt(0, nullptr) == aConsumer; - } - - void AdoptConsumers(imgStatusTracker* aTracker) { mConsumers = aTracker->mConsumers; } - // Returns whether we are in the process of loading; that is, whether we have // not received OnStopRequest. bool IsLoading() const; // Get the current image status (as in imgIRequest). uint32_t GetImageStatus() const; // Following are all the notification methods. You must call the Record @@ -139,80 +99,67 @@ public: // Call when the request is being cancelled. void RecordCancel(); // Shorthand for recording all the load notifications: StartRequest, // StartContainer, StopRequest. void RecordLoaded(); // Shorthand for recording all the decode notifications: StartDecode, - // StartFrame, DataAvailable, StopFrame, StopDecode. + // StartFrame, DataAvailable, StopFrame, StopContainer, StopDecode. void RecordDecoded(); /* non-virtual imgIDecoderObserver methods */ + void RecordStartDecode(); + void SendStartDecode(imgRequestProxy* aProxy); void RecordStartContainer(imgIContainer* aContainer); - void SendStartContainer(imgRequestProxy* aProxy); - void RecordDataAvailable(); - void SendDataAvailable(imgRequestProxy* aProxy, const nsIntRect* aRect); - void RecordStopFrame(); - void SendStopFrame(imgRequestProxy* aProxy); - void RecordStopDecode(nsresult statusg); - void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus); + void SendStartContainer(imgRequestProxy* aProxy, imgIContainer* aContainer); + void RecordStartFrame(uint32_t aFrame); + void SendStartFrame(imgRequestProxy* aProxy, uint32_t aFrame); + void RecordDataAvailable(bool aCurrentFrame, const nsIntRect* aRect); + void SendDataAvailable(imgRequestProxy* aProxy, bool aCurrentFrame, const nsIntRect* aRect); + void RecordStopFrame(uint32_t aFrame); + void SendStopFrame(imgRequestProxy* aProxy, uint32_t aFrame); + void RecordStopContainer(imgIContainer* aContainer); + void SendStopContainer(imgRequestProxy* aProxy, imgIContainer* aContainer); + void RecordStopDecode(nsresult status, const PRUnichar* statusArg); + void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus, const PRUnichar* statusArg); void RecordDiscard(); void SendDiscard(imgRequestProxy* aProxy); void RecordImageIsAnimated(); void SendImageIsAnimated(imgRequestProxy *aProxy); /* non-virtual imgIContainerObserver methods */ - void RecordFrameChanged(const nsIntRect* aDirtyRect); - void SendFrameChanged(imgRequestProxy* aProxy, const nsIntRect* aDirtyRect); + void RecordFrameChanged(imgIContainer* aContainer, + const nsIntRect* aDirtyRect); + void SendFrameChanged(imgRequestProxy* aProxy, imgIContainer* aContainer, + const nsIntRect* aDirtyRect); /* non-virtual sort-of-nsIRequestObserver methods */ void RecordStartRequest(); void SendStartRequest(imgRequestProxy* aProxy); void RecordStopRequest(bool aLastPart, nsresult aStatus); void SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus); - void OnStartRequest(); - void OnDataAvailable(); - void OnStopRequest(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); - void MaybeUnblockOnload(); - - // Weak pointer getters - no AddRefs. - inline mozilla::image::Image* GetImage() const { return mImage; }; - inline imgRequest* GetRequest() const { return mRequest; }; - - inline imgIDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); } - private: friend class imgStatusNotifyRunnable; friend class imgRequestNotifyRunnable; - friend class imgStatusTrackerObserver; nsCOMPtr<nsIRunnable> mRequestRunnable; - // Weak pointers to the image and request. The request owns the image, and - // the image (or the request, if there's no image) owns the status tracker. + // A weak pointer to the Image, because it owns us, and we + // can't create a cycle. mozilla::image::Image* mImage; - imgRequest* mRequest; uint32_t mState; uint32_t mImageStatus; bool mHadLastPart; - bool mBlockingOnload; - - // List of proxies attached to the image. Each proxy represents a consumer - // using the image. - nsTObserverArray<imgRequestProxy*> mConsumers; - - nsRefPtr<imgStatusTrackerObserver> mTrackerObserver; }; #endif
--- a/image/src/imgTools.cpp +++ b/image/src/imgTools.cpp @@ -18,18 +18,16 @@ #include "nsStringStream.h" #include "nsComponentManagerUtils.h" #include "nsWeakReference.h" #include "nsIInterfaceRequestorUtils.h" #include "nsStreamUtils.h" #include "nsNetUtil.h" #include "nsContentUtils.h" #include "RasterImage.h" -#include "ScriptedNotificationObserver.h" -#include "imgIScriptedNotificationObserver.h" using namespace mozilla::image; class nsIDOMDocument; class nsIDocument; /* ========== imgITools implementation ========== */ @@ -273,23 +271,16 @@ NS_IMETHODIMP imgTools::GetFirstImageFra NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(frame, NS_ERROR_NOT_AVAILABLE); NS_ENSURE_TRUE(frame->Width() && frame->Height(), NS_ERROR_FAILURE); frame.forget(aSurface); return NS_OK; } -NS_IMETHODIMP imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner, - imgINotificationObserver** aObserver) -{ - NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner)); - return NS_OK; -} - NS_IMETHODIMP imgTools::GetImgLoaderForDocument(nsIDOMDocument* aDoc, imgILoader** aLoader) { nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc); NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc)); return NS_OK; }
--- a/image/test/mochitest/imgutils.js +++ b/image/test/mochitest/imgutils.js @@ -116,19 +116,20 @@ function getImagePref(pref) default: throw new Error("Unknown pref type"); } } else return null; } -// JS implementation of imgIScriptedNotificationObserver with stubs for all of its methods. +// JS implementation of imgIDecoderObserver with stubs for all of its methods. function ImageDecoderObserverStub() { - this.sizeAvailable = function sizeAvailable(aRequest) {} - this.frameComplete = function frameComplete(aRequest) {} - this.decodeComplete = function decodeComplete(aRequest) {} - this.loadComplete = function loadComplete(aRequest) {} - this.frameUpdate = function frameUpdate(aRequest) {} - this.discard = function discard(aRequest) {} - this.isAnimated = function isAnimated(aRequest) {} + this.onStartRequest = function onStartRequest(aRequest) {} + this.onStartDecode = function onStartDecode(aRequest) {} + this.onStartContainer = function onStartContainer(aRequest, aContainer) {} + this.onStartFrame = function onStartFrame(aRequest, aFrame) {} + this.onStopFrame = function onStopFrame(aRequest, aFrame) {} + this.onStopContainer = function onStopContainer(aRequest, aContainer) {} + this.onStopDecode = function onStopDecode(aRequest, status, statusArg) {} + this.onStopRequest = function onStopRequest(aRequest, aIsLastPart) {} }
--- a/image/test/mochitest/test_animSVGImage.html +++ b/image/test/mochitest/test_animSVGImage.html @@ -21,17 +21,16 @@ https://bugzilla.mozilla.org/show_bug.cg <pre id="test"> <script type="application/javascript;version=1.8"> /** Test for Bug 610419**/ SimpleTest.waitForExplicitFinish(); const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes) -const Cc = Components.classes; const Ci = Components.interfaces; const gImg = document.getElementsByTagName("img")[0]; var gMyDecoderObserver; // value will be set in main() var gReferenceSnapshot; // value will be set in takeReferenceSnapshot() var gOnStopFrameCounter = 0; var gIsTestFinished = false; @@ -49,17 +48,17 @@ function takeReferenceSnapshot() { // Re-hide reference div, and take another snapshot to be sure it's gone referenceDiv.style.display = "none"; let blankSnapshot2 = snapshotWindow(window, false); ok(compareSnapshots(blankSnapshot, blankSnapshot2, true)[0], "reference div should disappear when it becomes display:none"); } -function myOnStopFrame(aRequest) { +function myOnStopFrame(aRequest, aFrame) { gOnStopFrameCounter++; ok(true, "myOnStopFrame called"); let currentSnapshot = snapshotWindow(window, false); if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) { // SUCCESS! ok(true, "Animated image looks correct, " + "at call #" + gOnStopFrameCounter + " to onStopFrame"); cleanUpAndFinish(); @@ -85,21 +84,18 @@ function cleanUpAndFinish() { SimpleTest.finish(); gIsTestFinished = true; } function main() { takeReferenceSnapshot(); // Create, customize & attach decoder observer - observer = new ImageDecoderObserverStub(); - observer.frameComplete = myOnStopFrame; - gMyDecoderObserver = - Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(observer); + gMyDecoderObserver = new ImageDecoderObserverStub(); + gMyDecoderObserver.onStopFrame = myOnStopFrame; let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent); imgLoadingContent.addObserver(gMyDecoderObserver); // We want to test the cold loading behavior, so clear cache in case an // earlier test got our image in there already. clearImageCache(); // kick off image-loading! myOnStopFrame handles the rest.
--- a/image/test/unit/async_load_tests.js +++ b/image/test/unit/async_load_tests.js @@ -40,47 +40,39 @@ function getCloneStopCallback(original_l // but they aren't synchronous right now. function checkClone(other_listener, aRequest) { do_test_pending(); // For as long as clone notification is synchronous, we can't test the clone state reliably. var listener = new ImageListener(null, function(foo, bar) { do_test_finished(); } /*getCloneStopCallback(other_listener)*/); listener.synchronous = false; - var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(listener); - var clone = aRequest.clone(outer); + var clone = aRequest.clone(listener); } // Ensure that all the callbacks were called on aRequest. function checkAllCallbacks(listener, aRequest) { - do_check_neq(listener.state & SIZE_AVAILABLE, 0); - do_check_neq(listener.state & FRAME_COMPLETE, 0); - do_check_neq(listener.state & DECODE_COMPLETE, 0); - do_check_neq(listener.state & LOAD_COMPLETE, 0); do_check_eq(listener.state, ALL_BITS); do_test_finished(); } function secondLoadDone(oldlistener, aRequest) { do_test_pending(); try { var staticrequest = aRequest.getStaticRequest(); // For as long as clone notification is synchronous, we can't test the // clone state reliably. var listener = new ImageListener(null, checkAllCallbacks); listener.synchronous = false; - var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(listener); - var staticrequestclone = staticrequest.clone(outer); + var staticrequestclone = staticrequest.clone(listener); } catch(e) { // We can't create a static request. Most likely the request we started // with didn't load successfully. do_test_finished(); } run_loadImageWithChannel_tests(); @@ -89,38 +81,36 @@ function secondLoadDone(oldlistener, aRe // Load the request a second time. This should come from the image cache, and // therefore would be at most risk of being served synchronously. function checkSecondLoad() { do_test_pending(); var listener = new ImageListener(checkClone, secondLoadDone); - var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(listener); - requests.push(gCurrentLoader.loadImage(uri, null, null, null, null, outer, null, 0, null, null, null)); + requests.push(gCurrentLoader.loadImage(uri, null, null, null, null, listener, null, 0, null, null, null)); listener.synchronous = false; } function firstLoadDone(oldlistener, aRequest) { checkSecondLoad(uri); do_test_finished(); } // Return a closure that allows us to check the stream listener's status when the // image starts loading. function getChannelLoadImageStartCallback(streamlistener) { return function channelLoadStart(imglistener, aRequest) { - // We must not have received all status before we get this start callback. + // We must not have received any status before we get this start callback. // If we have, we've broken people's expectations by delaying events from a // channel we were given. - do_check_eq(streamlistener.requestStatus & STOP_REQUEST, 0); + do_check_eq(streamlistener.requestStatus, 0); checkClone(imglistener, aRequest); } } // Return a closure that allows us to check the stream listener's status when the // image finishes loading. function getChannelLoadImageStopCallback(streamlistener, next) @@ -146,20 +136,18 @@ function checkSecondChannelLoad() var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); var channel = ioService.newChannelFromURI(uri); var channellistener = new ChannelListener(); channel.asyncOpen(channellistener, null); var listener = new ImageListener(getChannelLoadImageStartCallback(channellistener), getChannelLoadImageStopCallback(channellistener, all_done_callback)); - var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(listener); var outlistener = {}; - requests.push(gCurrentLoader.loadImageWithChannel(channel, outer, null, outlistener)); + requests.push(gCurrentLoader.loadImageWithChannel(channel, listener, null, outlistener)); channellistener.outputListener = outlistener.value; listener.synchronous = false; } function run_loadImageWithChannel_tests() { // To ensure we're testing what we expect to, create a new loader and cache. @@ -170,20 +158,18 @@ function run_loadImageWithChannel_tests( var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); var channel = ioService.newChannelFromURI(uri); var channellistener = new ChannelListener(); channel.asyncOpen(channellistener, null); var listener = new ImageListener(getChannelLoadImageStartCallback(channellistener), getChannelLoadImageStopCallback(channellistener, checkSecondChannelLoad)); - var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(listener); var outlistener = {}; - requests.push(gCurrentLoader.loadImageWithChannel(channel, outer, null, outlistener)); + requests.push(gCurrentLoader.loadImageWithChannel(channel, listener, null, outlistener)); channellistener.outputListener = outlistener.value; listener.synchronous = false; } function all_done_callback() { server.stop(function() { do_test_finished(); }); @@ -191,36 +177,32 @@ function all_done_callback() function startImageCallback(otherCb) { return function(listener, request) { // Make sure we can load the same image immediately out of the cache. do_test_pending(); var listener2 = new ImageListener(null, function(foo, bar) { do_test_finished(); }); - var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(listener2); - requests.push(gCurrentLoader.loadImage(uri, null, null, null, null, outer, null, 0, null, null, null)); + requests.push(gCurrentLoader.loadImage(uri, null, null, null, null, listener2, null, 0, null, null, null)); listener2.synchronous = false; // Now that we've started another load, chain to the callback. otherCb(listener, request); } } var gCurrentLoader; function run_test() { gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader); do_test_pending(); var listener = new ImageListener(startImageCallback(checkClone), firstLoadDone); - var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(listener); - var req = gCurrentLoader.loadImage(uri, null, null, null, null, outer, null, 0, null, null, null); + var req = gCurrentLoader.loadImage(uri, null, null, null, null, listener, null, 0, null, null, null); requests.push(req); // Ensure that we don't cause any mayhem when we lock an image. req.lockImage(); listener.synchronous = false; }
--- a/image/test/unit/image_load_helpers.js +++ b/image/test/unit/image_load_helpers.js @@ -1,71 +1,92 @@ /* * Helper structures to track callbacks from image and channel loads. */ -// START_REQUEST and STOP_REQUEST are used by ChannelListener, and +// One bit per callback that imageListener below implements. Stored in +// ImageListener.state. +// START_REQUEST and STOP_REQUEST are also reused by ChannelListener, and // stored in ChannelListener.requestStatus. const START_REQUEST = 0x01; -const STOP_REQUEST = 0x02; -const DATA_AVAILABLE = 0x04; - -// One bit per callback that imageListener below implements. Stored in -// ImageListener.state. -const SIZE_AVAILABLE = 0x01; -const FRAME_UPDATE = 0x02; -const FRAME_COMPLETE = 0x04; -const LOAD_COMPLETE = 0x08; -const DECODE_COMPLETE = 0x10; -const ALL_BITS = SIZE_AVAILABLE | FRAME_COMPLETE | DECODE_COMPLETE | LOAD_COMPLETE; +const START_DECODE = 0x02; +const START_CONTAINER = 0x04; +const START_FRAME = 0x08; +const STOP_FRAME = 0x10; +const STOP_CONTAINER = 0x20; +const STOP_DECODE = 0x40; +const STOP_REQUEST = 0x80; +const ALL_BITS = 0xFF; // An implementation of imgIDecoderObserver with the ability to call specified // functions on onStartRequest and onStopRequest. function ImageListener(start_callback, stop_callback) { - this.sizeAvailable = function onSizeAvailable(aRequest) + this.onStartRequest = function onStartRequest(aRequest) { do_check_false(this.synchronous); - this.state |= SIZE_AVAILABLE; + this.state |= START_REQUEST; if (this.start_callback) this.start_callback(this, aRequest); } - this.frameComplete = function onFrameComplete(aRequest) + this.onStartDecode = function onStartDecode(aRequest) + { + do_check_false(this.synchronous); + + this.state |= START_DECODE; + } + this.onStartContainer = function onStartContainer(aRequest, aContainer) + { + do_check_false(this.synchronous); + + this.state |= START_CONTAINER; + } + this.onStartFrame = function onStartFrame(aRequest, aFrame) + { + do_check_false(this.synchronous); + + this.state |= START_FRAME; + } + this.onStopFrame = function onStopFrame(aRequest, aFrame) { do_check_false(this.synchronous); - this.state |= FRAME_COMPLETE; + this.state |= STOP_FRAME; } - this.decodeComplete = function onDecodeComplete(aRequest) + this.onStopContainer = function onStopContainer(aRequest, aContainer) { do_check_false(this.synchronous); - this.state |= DECODE_COMPLETE; + this.state |= STOP_CONTAINER; } - this.loadComplete = function onLoadcomplete(aRequest) + this.onStopDecode = function onStopDecode(aRequest, status, statusArg) { do_check_false(this.synchronous); + this.state |= STOP_DECODE; + } + this.onStopRequest = function onStopRequest(aRequest, aIsLastPart) + { + do_check_false(this.synchronous); + + // onStopDecode must always be called before, and with, onStopRequest. See + // imgRequest::OnStopDecode for more information. + do_check_true(!!(this.state & STOP_DECODE)); + // We have to cancel the request when we're done with it to break any // reference loops! aRequest.cancelAndForgetObserver(0); - this.state |= LOAD_COMPLETE; + this.state |= STOP_REQUEST; if (this.stop_callback) this.stop_callback(this, aRequest); } - this.frameUpdate = function onFrameUpdate(aRequest) - { - } - this.isAnimated = function onIsAnimated() - { - } // Initialize the synchronous flag to true to start. This must be set to // false before exiting to the event loop! this.synchronous = true; // A function to call when onStartRequest is called. this.start_callback = start_callback; @@ -92,18 +113,16 @@ function ChannelListener() this.requestStatus |= START_REQUEST; } this.onDataAvailable = function onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount) { if (this.outputListener) this.outputListener.onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount); - - this.requestStatus |= DATA_AVAILABLE; } this.onStopRequest = function onStopRequest(aRequest, aContext, aStatusCode) { if (this.outputListener) this.outputListener.onStopRequest(aRequest, aContext, aStatusCode); // If we failed (or were canceled - failure is implied if canceled),
--- a/image/test/unit/test_private_channel.js +++ b/image/test/unit/test_private_channel.js @@ -50,35 +50,31 @@ var gImgPath = 'http://localhost:8088/im function setup_chan(path, isPrivate, callback) { var uri = gIoService.newURI(gImgPath, null, null); var chan = gIoService.newChannelFromURI(uri); chan.notificationCallbacks = new NotificationCallbacks(isPrivate); var channelListener = new ChannelListener(); chan.asyncOpen(channelListener, null); var listener = new ImageListener(null, callback); + listeners.push(listener); var outlistener = {}; var loader = isPrivate ? gPrivateLoader : gPublicLoader; - var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(listener); - listeners.push(outer); - requests.push(loader.loadImageWithChannel(chan, outer, null, outlistener)); + requests.push(loader.loadImageWithChannel(chan, listener, null, outlistener)); channelListener.outputListener = outlistener.value; listener.synchronous = false; } function loadImage(isPrivate, callback) { var listener = new ImageListener(null, callback); - var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .createScriptedObserver(listener); var uri = gIoService.newURI(gImgPath, null, null); var loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup); loadGroup.notificationCallbacks = new NotificationCallbacks(isPrivate); var loader = isPrivate ? gPrivateLoader : gPublicLoader; - requests.push(loader.loadImage(uri, null, null, null, loadGroup, outer, null, 0, null, null, null)); + requests.push(loader.loadImage(uri, null, null, null, loadGroup, listener, null, 0, null, null, null)); listener.synchronous = false; } function run_loadImage_tests() { let cs = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService); cs.evictEntries(Ci.nsICache.STORE_ANYWHERE); gHits = 0;
--- a/layout/generic/nsBulletFrame.cpp +++ b/layout/generic/nsBulletFrame.cpp @@ -1426,46 +1426,19 @@ nsBulletFrame::GetMinWidth(nsRenderingCo nsBulletFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) { nsHTMLReflowMetrics metrics; DISPLAY_PREF_WIDTH(this, metrics.width); GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f); return metrics.width; } -NS_IMETHODIMP -nsBulletFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) -{ - if (aType == imgINotificationObserver::SIZE_AVAILABLE) { - nsCOMPtr<imgIContainer> image; - aRequest->GetImage(getter_AddRefs(image)); - return OnStartContainer(aRequest, image); - } - if (aType == imgINotificationObserver::FRAME_UPDATE) { - // The image has changed. - // Invalidate the entire content area. Maybe it's not optimal but it's simple and - // always correct, and I'll be a stunned mullet if it ever matters for performance - InvalidateFrame(); - } - - if (aType == imgINotificationObserver::IS_ANIMATED) { - // Register the image request with the refresh driver now that we know it's - // animated. - if (aRequest == mImageRequest) { - nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest, - &mRequestRegistered); - } - } - - return NS_OK; -} - -nsresult nsBulletFrame::OnStartContainer(imgIRequest *aRequest, - imgIContainer *aImage) +NS_IMETHODIMP nsBulletFrame::OnStartContainer(imgIRequest *aRequest, + imgIContainer *aImage) { if (!aImage) return NS_ERROR_INVALID_ARG; if (!aRequest) return NS_ERROR_INVALID_ARG; uint32_t status; aRequest->GetImageStatus(&status); if (status & imgIRequest::STATUS_ERROR) { return NS_OK; @@ -1497,16 +1470,70 @@ nsresult nsBulletFrame::OnStartContainer // Ensure the animation (if any) is started. Note: There is no // corresponding call to Decrement for this. This Increment will be // 'cleaned up' by the Request when it is destroyed, but only then. aRequest->IncrementAnimationConsumers(); return NS_OK; } +NS_IMETHODIMP nsBulletFrame::OnDataAvailable(imgIRequest *aRequest, + bool aCurrentFrame, + const nsIntRect *aRect) +{ + // The image has changed. + // Invalidate the entire content area. Maybe it's not optimal but it's simple and + // always correct, and I'll be a stunned mullet if it ever matters for performance + InvalidateFrame(); + + return NS_OK; +} + +NS_IMETHODIMP nsBulletFrame::OnStopDecode(imgIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aStatusArg) +{ + // XXX should the bulletframe do anything if the image failed to load? + // it didn't in the old code... + +#if 0 + if (NS_FAILED(aStatus)) { + // We failed to load the image. Notify the pres shell + if (NS_FAILED(aStatus) && (mImageRequest == aRequest || !mImageRequest)) { + imageFailed = true; + } + } +#endif + + return NS_OK; +} + +NS_IMETHODIMP nsBulletFrame::OnImageIsAnimated(imgIRequest* aRequest) +{ + // Register the image request with the refresh driver now that we know it's + // animated. + if (aRequest == mImageRequest) { + nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest, + &mRequestRegistered); + } + + return NS_OK; +} + +NS_IMETHODIMP nsBulletFrame::FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) +{ + // Invalidate the entire content area. Maybe it's not optimal but it's simple and + // always correct. + InvalidateFrame(); + + return NS_OK; +} + void nsBulletFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup) { if (!aPresContext) return; NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer"); @@ -1592,26 +1619,65 @@ nsBulletFrame::GetBaseline() const -NS_IMPL_ISUPPORTS1(nsBulletListener, imgINotificationObserver) +NS_IMPL_ISUPPORTS2(nsBulletListener, imgIDecoderObserver, imgIContainerObserver) nsBulletListener::nsBulletListener() : mFrame(nullptr) { } nsBulletListener::~nsBulletListener() { } -NS_IMETHODIMP -nsBulletListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) +NS_IMETHODIMP nsBulletListener::OnStartContainer(imgIRequest *aRequest, + imgIContainer *aImage) { if (!mFrame) return NS_ERROR_FAILURE; - return mFrame->Notify(aRequest, aType, aData); + + return mFrame->OnStartContainer(aRequest, aImage); +} + +NS_IMETHODIMP nsBulletListener::OnDataAvailable(imgIRequest *aRequest, + bool aCurrentFrame, + const nsIntRect *aRect) +{ + if (!mFrame) + return NS_OK; + + return mFrame->OnDataAvailable(aRequest, aCurrentFrame, aRect); } + +NS_IMETHODIMP nsBulletListener::OnStopDecode(imgIRequest *aRequest, + nsresult status, + const PRUnichar *statusArg) +{ + if (!mFrame) + return NS_OK; + + return mFrame->OnStopDecode(aRequest, status, statusArg); +} + +NS_IMETHODIMP nsBulletListener::OnImageIsAnimated(imgIRequest *aRequest) +{ + if (!mFrame) + return NS_OK; + + return mFrame->OnImageIsAnimated(aRequest); +} + +NS_IMETHODIMP nsBulletListener::FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) +{ + if (!mFrame) + return NS_OK; + + return mFrame->FrameChanged(aRequest, aContainer, aDirtyRect); +}
--- a/layout/generic/nsBulletFrame.h +++ b/layout/generic/nsBulletFrame.h @@ -9,31 +9,42 @@ #define nsBulletFrame_h___ #include "mozilla/Attributes.h" #include "nsFrame.h" #include "nsStyleContext.h" #include "imgIRequest.h" #include "imgIDecoderObserver.h" -#include "imgINotificationObserver.h" +#include "nsStubImageDecoderObserver.h" #define BULLET_FRAME_IMAGE_LOADING NS_FRAME_STATE_BIT(63) #define BULLET_FRAME_HAS_FONT_INFLATION NS_FRAME_STATE_BIT(62) class nsBulletFrame; -class nsBulletListener : public imgINotificationObserver +class nsBulletListener : public nsStubImageDecoderObserver { public: nsBulletListener(); virtual ~nsBulletListener(); NS_DECL_ISUPPORTS - NS_DECL_IMGINOTIFICATIONOBSERVER + // imgIDecoderObserver (override nsStubImageDecoderObserver) + NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage); + NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame, + const nsIntRect *aRect); + NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status, + const PRUnichar *statusArg); + NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest); + + // imgIContainerObserver (override nsStubImageDecoderObserver) + NS_IMETHOD FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *dirtyRect); void SetFrame(nsBulletFrame *frame) { mFrame = frame; } private: nsBulletFrame *mFrame; }; /** @@ -45,18 +56,16 @@ public: NS_DECL_FRAMEARENA_HELPERS nsBulletFrame(nsStyleContext* aContext) : nsFrame(aContext) { } virtual ~nsBulletFrame(); - NS_IMETHOD Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData); - // nsIFrame virtual void DestroyFrom(nsIFrame* aDestructRoot); NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists); virtual nsIAtom* GetType() const MOZ_OVERRIDE; virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE; #ifdef DEBUG @@ -71,16 +80,28 @@ public: virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; // nsBulletFrame int32_t SetListItemOrdinal(int32_t aNextOrdinal, bool* aChanged, int32_t aIncrement); + NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage); + NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, + bool aCurrentFrame, + const nsIntRect *aRect); + NS_IMETHOD OnStopDecode(imgIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aStatusArg); + NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest); + NS_IMETHOD FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect); + /* get list item text, without '.' */ static bool AppendCounterText(int32_t aListStyleType, int32_t aOrdinal, nsString& aResult); /* get list item text, with '.' */ bool GetListItemText(const nsStyleList& aStyleList, nsString& aResult); @@ -94,18 +115,16 @@ public: float GetFontSizeInflation() const; bool HasFontSizeInflation() const { return (GetStateBits() & BULLET_FRAME_HAS_FONT_INFLATION) != 0; } void SetFontSizeInflation(float aInflation); protected: - nsresult OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage); - void GetDesiredSize(nsPresContext* aPresContext, nsRenderingContext *aRenderingContext, nsHTMLReflowMetrics& aMetrics, float aFontSizeInflation); void GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup); nsMargin mPadding;
--- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -135,18 +135,17 @@ NS_NewImageFrame(nsIPresShell* aPresShel NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame) nsImageFrame::nsImageFrame(nsStyleContext* aContext) : ImageFrameSuper(aContext), mComputedSize(0, 0), mIntrinsicRatio(0, 0), - mDisplayingIcon(false), - mFirstFrameComplete(false) + mDisplayingIcon(false) { // We assume our size is not constrained and we haven't gotten an // initial reflow yet, so don't touch those flags. mIntrinsicSize.width.SetCoordValue(0); mIntrinsicSize.height.SetCoordValue(0); } nsImageFrame::~nsImageFrame() @@ -522,44 +521,16 @@ nsImageFrame::ShouldCreateImageFrameFor( } } } return useSizedBox; } nsresult -nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) -{ - if (aType == imgINotificationObserver::SIZE_AVAILABLE) { - nsCOMPtr<imgIContainer> image; - aRequest->GetImage(getter_AddRefs(image)); - return OnStartContainer(aRequest, image); - } - - if (aType == imgINotificationObserver::FRAME_UPDATE) { - return OnDataAvailable(aRequest, aData); - } - - if (aType == imgINotificationObserver::FRAME_COMPLETE) { - mFirstFrameComplete = true; - } - - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - uint32_t imgStatus; - aRequest->GetImageStatus(&imgStatus); - nsresult status = - imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; - return OnStopRequest(aRequest, status); - } - - return NS_OK; -} - -nsresult nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage) { if (!aImage) return NS_ERROR_INVALID_ARG; /* Get requested animation policy from the pres context: * normal = 0 * one frame = 1 * one loop = 2 @@ -588,60 +559,61 @@ nsImageFrame::OnStartContainer(imgIReque } } return NS_OK; } nsresult nsImageFrame::OnDataAvailable(imgIRequest *aRequest, + bool aCurrentFrame, const nsIntRect *aRect) { - if (mFirstFrameComplete) { - nsCOMPtr<imgIContainer> container; - aRequest->GetImage(getter_AddRefs(container)); - return FrameChanged(aRequest, container); - } - // XXX do we need to make sure that the reflow from the // OnStartContainer has been processed before we start calling // invalidate? NS_ENSURE_ARG_POINTER(aRect); if (!(mState & IMAGE_GOTINITIALREFLOW)) { // Don't bother to do anything; we have a reflow coming up! return NS_OK; } if (IsPendingLoad(aRequest)) { // We don't care return NS_OK; } + // Don't invalidate if the current visible frame isn't the one the data is + // from + if (!aCurrentFrame) + return NS_OK; + #ifdef DEBUG_decode printf("Source rect (%d,%d,%d,%d)\n", aRect->x, aRect->y, aRect->width, aRect->height); #endif if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) { InvalidateFrame(nsDisplayItem::TYPE_IMAGE); InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK); } else { nsRect invalid = SourceRectToDest(*aRect); InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE); InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK); } - + return NS_OK; } nsresult -nsImageFrame::OnStopRequest(imgIRequest *aRequest, - nsresult aStatus) +nsImageFrame::OnStopDecode(imgIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aStatusArg) { // Check what request type we're dealing with nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); NS_ASSERTION(imageLoader, "Who's notifying us??"); int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST; imageLoader->GetRequestType(aRequest, &loadType); if (loadType != nsIImageLoadingContent::CURRENT_REQUEST && loadType != nsIImageLoadingContent::PENDING_REQUEST) { @@ -690,17 +662,18 @@ nsImageFrame::NotifyNewCurrentRequest(im // Update border+content to account for image change InvalidateFrame(); } } } nsresult nsImageFrame::FrameChanged(imgIRequest *aRequest, - imgIContainer *aContainer) + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) { if (!GetStyleVisibility()->IsVisible()) { return NS_OK; } if (IsPendingLoad(aContainer)) { // We don't care about it return NS_OK; @@ -1916,17 +1889,17 @@ nsresult nsImageFrame::LoadIcons(nsPresC rv = LoadIcon(brokenSrc, aPresContext, getter_AddRefs(gIconLoad->mBrokenImage)); return rv; } NS_IMPL_ISUPPORTS2(nsImageFrame::IconLoad, nsIObserver, - imgINotificationObserver) + imgIDecoderObserver) static const char* kIconLoadPrefs[] = { "browser.display.force_inline_alttext", "browser.display.show_image_placeholders", nullptr }; nsImageFrame::IconLoad::IconLoad() @@ -1973,52 +1946,165 @@ void nsImageFrame::IconLoad::GetPrefs() { mPrefForceInlineAltText = Preferences::GetBool("browser.display.force_inline_alttext"); mPrefShowPlaceholders = Preferences::GetBool("browser.display.show_image_placeholders", true); } + + NS_IMETHODIMP -nsImageFrame::IconLoad::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) +nsImageFrame::IconLoad::OnStartRequest(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsImageFrame::IconLoad::OnStartDecode(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsImageFrame::IconLoad::OnStartContainer(imgIRequest *aRequest, + imgIContainer *aContainer) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsImageFrame::IconLoad::OnStartFrame(imgIRequest *aRequest, + uint32_t aFrame) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsImageFrame::IconLoad::OnDataAvailable(imgIRequest *aRequest, + bool aCurrentFrame, + const nsIntRect * aRect) { - if (aType != imgINotificationObserver::LOAD_COMPLETE && - aType != imgINotificationObserver::FRAME_UPDATE) { - return NS_OK; - } + return NS_OK; +} + +NS_IMETHODIMP +nsImageFrame::IconLoad::OnStopFrame(imgIRequest *aRequest, + uint32_t aFrame) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsImageFrame::IconLoad::OnStopContainer(imgIRequest *aRequest, + imgIContainer *aContainer) +{ + return NS_OK; +} +NS_IMETHODIMP +nsImageFrame::IconLoad::OnStopDecode(imgIRequest *aRequest, + nsresult status, + const PRUnichar *statusArg) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsImageFrame::IconLoad::OnImageIsAnimated(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsImageFrame::IconLoad::OnStopRequest(imgIRequest *aRequest, + bool aIsLastPart) +{ nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers); nsImageFrame *frame; while (iter.HasMore()) { frame = iter.GetNext(); frame->InvalidateFrame(); } return NS_OK; } -NS_IMPL_ISUPPORTS1(nsImageListener, imgINotificationObserver) +NS_IMETHODIMP +nsImageFrame::IconLoad::OnDiscard(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsImageFrame::IconLoad::FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) +{ + nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers); + nsImageFrame *frame; + while (iter.HasMore()) { + frame = iter.GetNext(); + frame->InvalidateFrame(); + } + + return NS_OK; +} + + + +NS_IMPL_ISUPPORTS2(nsImageListener, imgIDecoderObserver, imgIContainerObserver) nsImageListener::nsImageListener(nsImageFrame *aFrame) : mFrame(aFrame) { } nsImageListener::~nsImageListener() { } -NS_IMETHODIMP -nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) +NS_IMETHODIMP nsImageListener::OnStartContainer(imgIRequest *aRequest, + imgIContainer *aImage) +{ + if (!mFrame) + return NS_ERROR_FAILURE; + + return mFrame->OnStartContainer(aRequest, aImage); +} + +NS_IMETHODIMP nsImageListener::OnDataAvailable(imgIRequest *aRequest, + bool aCurrentFrame, + const nsIntRect *aRect) { if (!mFrame) return NS_ERROR_FAILURE; - return mFrame->Notify(aRequest, aType, aData); + return mFrame->OnDataAvailable(aRequest, aCurrentFrame, aRect); +} + +NS_IMETHODIMP nsImageListener::OnStopDecode(imgIRequest *aRequest, + nsresult status, + const PRUnichar *statusArg) +{ + if (!mFrame) + return NS_ERROR_FAILURE; + + return mFrame->OnStopDecode(aRequest, status, statusArg); +} + +NS_IMETHODIMP nsImageListener::FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) +{ + if (!mFrame) + return NS_ERROR_FAILURE; + + return mFrame->FrameChanged(aRequest, aContainer, aDirtyRect); } static bool IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame) { if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode()) return false; // Check if the parent of the closest nsBlockFrame has auto width.
--- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -7,17 +7,18 @@ #ifndef nsImageFrame_h___ #define nsImageFrame_h___ #include "nsSplittableFrame.h" #include "nsIIOService.h" #include "nsIObserver.h" -#include "imgINotificationObserver.h" +#include "nsStubImageDecoderObserver.h" +#include "imgIDecoderObserver.h" #include "nsDisplayList.h" #include "imgIContainer.h" #include "mozilla/Attributes.h" class nsIFrame; class nsImageMap; class nsIURI; @@ -34,24 +35,33 @@ class nsImageLoadingContent; namespace mozilla { namespace layers { class ImageContainer; class ImageLayer; class LayerManager; } } -class nsImageListener : public imgINotificationObserver +class nsImageListener : public nsStubImageDecoderObserver { public: nsImageListener(nsImageFrame *aFrame); virtual ~nsImageListener(); NS_DECL_ISUPPORTS - NS_DECL_IMGINOTIFICATIONOBSERVER + // imgIDecoderObserver (override nsStubImageDecoderObserver) + NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage); + NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame, + const nsIntRect *aRect); + NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status, + const PRUnichar *statusArg); + // imgIContainerObserver (override nsStubImageDecoderObserver) + NS_IMETHOD FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *dirtyRect); void SetFrame(nsImageFrame *frame) { mFrame = frame; } private: nsImageFrame *mFrame; }; #define IMAGE_SIZECONSTRAINED NS_FRAME_STATE_BIT(20) @@ -122,18 +132,16 @@ public: static void ReleaseGlobals() { if (gIconLoad) { gIconLoad->Shutdown(); NS_RELEASE(gIconLoad); } NS_IF_RELEASE(sIOService); } - nsresult Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData); - /** * Function to test whether aContent, which has aStyleContext as its style, * should get an image frame. Note that this method is only used by the * frame constructor; it's only here because it uses gIconLoad for now. */ static bool ShouldCreateImageFrameFor(mozilla::dom::Element* aElement, nsStyleContext* aStyleContext); @@ -208,21 +216,24 @@ protected: void PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt, const nsRect& aDirtyRect, imgIContainer* aImage, uint32_t aFlags); protected: friend class nsImageListener; friend class nsImageLoadingContent; nsresult OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage); - nsresult OnDataAvailable(imgIRequest *aRequest, const nsIntRect *rect); - nsresult OnStopRequest(imgIRequest *aRequest, - nsresult aStatus); + nsresult OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame, + const nsIntRect *rect); + nsresult OnStopDecode(imgIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aStatusArg); nsresult FrameChanged(imgIRequest *aRequest, - imgIContainer *aContainer); + imgIContainer *aContainer, + const nsIntRect *aDirtyRect); /** * Notification that aRequest will now be the current request. */ void NotifyNewCurrentRequest(imgIRequest *aRequest, nsresult aStatus); private: // random helpers inline void SpecToURI(const nsAString& aSpec, nsIIOService *aIOService, @@ -272,51 +283,51 @@ private: /** * Function to convert a dirty rect in the source image to a dirty * rect for the image frame. */ nsRect SourceRectToDest(const nsIntRect & aRect); nsImageMap* mImageMap; - nsCOMPtr<imgINotificationObserver> mListener; + nsCOMPtr<imgIDecoderObserver> mListener; nsSize mComputedSize; nsIFrame::IntrinsicSize mIntrinsicSize; nsSize mIntrinsicRatio; bool mDisplayingIcon; - bool mFirstFrameComplete; static nsIIOService* sIOService; /* loading / broken image icon support */ // XXXbz this should be handled by the prescontext, I think; that // way we would have a single iconload per mozilla session instead // of one per document... // LoadIcons: initiate the loading of the static icons used to show // loading / broken images nsresult LoadIcons(nsPresContext *aPresContext); nsresult LoadIcon(const nsAString& aSpec, nsPresContext *aPresContext, imgIRequest **aRequest); class IconLoad MOZ_FINAL : public nsIObserver, - public imgINotificationObserver { + public imgIDecoderObserver { // private class that wraps the data and logic needed for // broken image and loading image icons public: IconLoad(); void Shutdown(); NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER - NS_DECL_IMGINOTIFICATIONOBSERVER + NS_DECL_IMGICONTAINEROBSERVER + NS_DECL_IMGIDECODEROBSERVER void AddIconObserver(nsImageFrame *frame) { NS_ABORT_IF_FALSE(!mIconObservers.Contains(frame), "Observer shouldn't aleady be in array"); mIconObservers.AppendElement(frame); } void RemoveIconObserver(nsImageFrame *frame) {
--- a/layout/style/ImageLoader.cpp +++ b/layout/style/ImageLoader.cpp @@ -51,18 +51,18 @@ ImageLoader::DropDocumentReference() void ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest, nsIFrame* aFrame) { MOZ_ASSERT(mRequestToFrameMap.IsInitialized() && mFrameToRequestMap.IsInitialized() && mImages.IsInitialized()); - nsCOMPtr<imgINotificationObserver> observer; - aRequest->GetNotificationObserver(getter_AddRefs(observer)); + nsCOMPtr<imgIDecoderObserver> observer; + aRequest->GetDecoderObserver(getter_AddRefs(observer)); if (!observer) { // The request has already been canceled, so ignore it. This is ok because // we're not going to get any more notifications from a canceled request. return; } MOZ_ASSERT(observer == this); @@ -152,18 +152,18 @@ ImageLoader::DisassociateRequestFromFram RequestSet* requestSet = nullptr; MOZ_ASSERT(mRequestToFrameMap.IsInitialized() && mFrameToRequestMap.IsInitialized() && mImages.IsInitialized()); #ifdef DEBUG { - nsCOMPtr<imgINotificationObserver> observer; - aRequest->GetNotificationObserver(getter_AddRefs(observer)); + nsCOMPtr<imgIDecoderObserver> observer; + aRequest->GetDecoderObserver(getter_AddRefs(observer)); MOZ_ASSERT(!observer || observer == this); } #endif mRequestToFrameMap.Get(aRequest, &frameSet); mFrameToRequestMap.Get(aFrame, &requestSet); if (frameSet) { @@ -329,58 +329,35 @@ ImageLoader::DoRedraw(FrameSet* aFrameSe } } } NS_IMPL_ADDREF(ImageLoader) NS_IMPL_RELEASE(ImageLoader) NS_INTERFACE_MAP_BEGIN(ImageLoader) - NS_INTERFACE_MAP_ENTRY(imgINotificationObserver) + NS_INTERFACE_MAP_ENTRY(imgIDecoderObserver) + NS_INTERFACE_MAP_ENTRY(imgIContainerObserver) NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker) NS_INTERFACE_MAP_END NS_IMETHODIMP -ImageLoader::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) -{ - if (aType == imgINotificationObserver::SIZE_AVAILABLE) { - nsCOMPtr<imgIContainer> image; - aRequest->GetImage(getter_AddRefs(image)); - return OnStartContainer(aRequest, image); - } - - if (aType == imgINotificationObserver::IS_ANIMATED) { - return OnImageIsAnimated(aRequest); - } - - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - return OnStopFrame(aRequest); - } - - if (aType == imgINotificationObserver::FRAME_UPDATE) { - return FrameChanged(aRequest); - } - - return NS_OK; -} - -nsresult ImageLoader::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage) { nsPresContext* presContext = GetPresContext(); if (!presContext) { return NS_OK; } aImage->SetAnimationMode(presContext->ImageAnimationMode()); return NS_OK; } -nsresult +NS_IMETHODIMP ImageLoader::OnImageIsAnimated(imgIRequest* aRequest) { if (!mDocument) { return NS_OK; } FrameSet* frameSet = nullptr; if (!mRequestToFrameMap.Get(aRequest, &frameSet)) { @@ -394,18 +371,18 @@ ImageLoader::OnImageIsAnimated(imgIReque nsLayoutUtils::RegisterImageRequest(presContext, aRequest, nullptr); } return NS_OK; } -nsresult -ImageLoader::OnStopFrame(imgIRequest *aRequest) +NS_IMETHODIMP +ImageLoader::OnStopFrame(imgIRequest *aRequest, uint32_t aFrame) { if (!mDocument || mInClone) { return NS_OK; } FrameSet* frameSet = nullptr; if (!mRequestToFrameMap.Get(aRequest, &frameSet)) { return NS_OK; @@ -413,18 +390,20 @@ ImageLoader::OnStopFrame(imgIRequest *aR NS_ASSERTION(frameSet, "This should never be null!"); DoRedraw(frameSet); return NS_OK; } -nsresult -ImageLoader::FrameChanged(imgIRequest *aRequest) +NS_IMETHODIMP +ImageLoader::FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) { if (!mDocument || mInClone) { return NS_OK; } FrameSet* frameSet = nullptr; if (!mRequestToFrameMap.Get(aRequest, &frameSet)) { return NS_OK;
--- a/layout/style/ImageLoader.h +++ b/layout/style/ImageLoader.h @@ -7,29 +7,29 @@ #include "nsAutoPtr.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" #include "nsInterfaceHashtable.h" #include "nsCSSValue.h" #include "imgIRequest.h" #include "imgIOnloadBlocker.h" -#include "imgINotificationObserver.h" +#include "nsStubImageDecoderObserver.h" #include "mozilla/Attributes.h" class nsIFrame; class nsIDocument; class nsPresContext; class nsIURI; class nsIPrincipal; namespace mozilla { namespace css { -class ImageLoader MOZ_FINAL : public imgINotificationObserver, +class ImageLoader MOZ_FINAL : public nsStubImageDecoderObserver, public imgIOnloadBlocker { public: typedef mozilla::css::ImageValue Image; ImageLoader(nsIDocument* aDocument) : mDocument(aDocument), mInClone(false) { @@ -37,17 +37,29 @@ public: mRequestToFrameMap.Init(); mFrameToRequestMap.Init(); mImages.Init(); } NS_DECL_ISUPPORTS NS_DECL_IMGIONLOADBLOCKER - NS_DECL_IMGINOTIFICATIONOBSERVER + + // imgIDecoderObserver (override nsStubImageDecoderObserver) + NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage); + NS_IMETHOD OnStopFrame(imgIRequest *aRequest, uint32_t aFrame); + NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest); + // Do not override OnDataAvailable since background images are not + // displayed incrementally; they are displayed after the entire image + // has been loaded. + + // imgIContainerObserver (override nsStubImageDecoderObserver) + NS_IMETHOD FrameChanged(imgIRequest* aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect); void DropDocumentReference(); void MaybeRegisterCSSImage(Image* aImage); void DeregisterCSSImage(Image* aImage); void AssociateRequestToFrame(imgIRequest* aRequest, nsIFrame* aFrame); @@ -86,24 +98,16 @@ private: nsPresContext* GetPresContext(); void DoRedraw(FrameSet* aFrameSet); static PLDHashOperator SetAnimationModeEnumerator(nsISupports* aKey, FrameSet* aValue, void* aClosure); - nsresult OnStartContainer(imgIRequest *aRequest, imgIContainer* aImage); - nsresult OnStopFrame(imgIRequest *aRequest); - nsresult OnImageIsAnimated(imgIRequest *aRequest); - nsresult FrameChanged(imgIRequest* aRequest); - // Do not override OnDataAvailable since background images are not - // displayed incrementally; they are displayed after the entire image - // has been loaded. - // A map of imgIRequests to the nsIFrames that are using them. RequestToFrameMap mRequestToFrameMap; // A map of nsIFrames to the imgIRequests they use. FrameToRequestMap mFrameToRequestMap; // A weak pointer to our document. Nulled out by DropDocumentReference. nsIDocument* mDocument;
--- a/layout/svg/nsSVGImageFrame.cpp +++ b/layout/svg/nsSVGImageFrame.cpp @@ -6,35 +6,44 @@ // Keep in (case-insensitive) order: #include "gfxContext.h" #include "gfxMatrix.h" #include "gfxPlatform.h" #include "imgIContainer.h" #include "nsIDOMSVGImageElement.h" #include "nsLayoutUtils.h" #include "nsRenderingContext.h" -#include "imgINotificationObserver.h" +#include "nsStubImageDecoderObserver.h" #include "nsSVGEffects.h" #include "nsSVGImageElement.h" #include "nsSVGPathGeometryFrame.h" #include "nsSVGSVGElement.h" #include "nsSVGUtils.h" #include "SVGContentUtils.h" using namespace mozilla; class nsSVGImageFrame; -class nsSVGImageListener MOZ_FINAL : public imgINotificationObserver +class nsSVGImageListener MOZ_FINAL : public nsStubImageDecoderObserver { public: nsSVGImageListener(nsSVGImageFrame *aFrame); NS_DECL_ISUPPORTS - NS_DECL_IMGINOTIFICATIONOBSERVER + // imgIDecoderObserver (override nsStubImageDecoderObserver) + NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status, + const PRUnichar *statusArg); + // imgIContainerObserver (override nsStubImageDecoderObserver) + NS_IMETHOD FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect); + // imgIContainerObserver (override nsStubImageDecoderObserver) + NS_IMETHOD OnStartContainer(imgIRequest *aRequest, + imgIContainer *aContainer); void SetFrame(nsSVGImageFrame *frame) { mFrame = frame; } private: nsSVGImageFrame *mFrame; }; typedef nsSVGPathGeometryFrame nsSVGImageFrameBase; @@ -84,17 +93,17 @@ public: private: gfxMatrix GetRasterImageTransform(int32_t aNativeWidth, int32_t aNativeHeight, uint32_t aFor); gfxMatrix GetVectorImageTransform(uint32_t aFor); bool TransformContextForPainting(gfxContext* aGfxContext); - nsCOMPtr<imgINotificationObserver> mListener; + nsCOMPtr<imgIDecoderObserver> mListener; nsCOMPtr<imgIContainer> mImageContainer; friend class nsSVGImageListener; }; //---------------------------------------------------------------------- // Implementation @@ -546,40 +555,55 @@ nsSVGImageFrame::GetHitTestFlags() } return flags; } //---------------------------------------------------------------------- // nsSVGImageListener implementation -NS_IMPL_ISUPPORTS1(nsSVGImageListener, imgINotificationObserver) +NS_IMPL_ISUPPORTS2(nsSVGImageListener, + imgIDecoderObserver, + imgIContainerObserver) nsSVGImageListener::nsSVGImageListener(nsSVGImageFrame *aFrame) : mFrame(aFrame) { } -NS_IMETHODIMP -nsSVGImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) +NS_IMETHODIMP nsSVGImageListener::OnStopDecode(imgIRequest *aRequest, + nsresult status, + const PRUnichar *statusArg) +{ + if (!mFrame) + return NS_ERROR_FAILURE; + + nsSVGUtils::InvalidateAndScheduleReflowSVG(mFrame); + return NS_OK; +} + +NS_IMETHODIMP nsSVGImageListener::FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) { if (!mFrame) return NS_ERROR_FAILURE; - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - nsSVGUtils::InvalidateAndScheduleReflowSVG(mFrame); - } + // No new dimensions, so we don't need to call + // nsSVGUtils::InvalidateAndScheduleBoundsUpdate. + nsSVGEffects::InvalidateRenderingObservers(mFrame); + nsSVGUtils::InvalidateBounds(mFrame); + return NS_OK; +} - if (aType == imgINotificationObserver::FRAME_UPDATE) { - // No new dimensions, so we don't need to call - // nsSVGUtils::InvalidateAndScheduleBoundsUpdate. - nsSVGEffects::InvalidateRenderingObservers(mFrame); - nsSVGUtils::InvalidateBounds(mFrame); - } +NS_IMETHODIMP nsSVGImageListener::OnStartContainer(imgIRequest *aRequest, + imgIContainer *aContainer) +{ + // Called once the resource's dimensions have been obtained. - if (aType == imgINotificationObserver::SIZE_AVAILABLE) { - // Called once the resource's dimensions have been obtained. - aRequest->GetImage(getter_AddRefs(mFrame->mImageContainer)); - nsSVGUtils::InvalidateAndScheduleReflowSVG(mFrame); - } + if (!mFrame) + return NS_ERROR_FAILURE; + + mFrame->mImageContainer = aContainer; + nsSVGUtils::InvalidateAndScheduleReflowSVG(mFrame); return NS_OK; }
--- a/layout/xul/base/src/nsImageBoxFrame.cpp +++ b/layout/xul/base/src/nsImageBoxFrame.cpp @@ -183,17 +183,17 @@ NS_IMETHODIMP nsImageBoxFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* aPrevInFlow) { if (!mListener) { nsImageBoxListener *listener = new nsImageBoxListener(); NS_ADDREF(listener); listener->SetFrame(this); - listener->QueryInterface(NS_GET_IID(imgINotificationObserver), getter_AddRefs(mListener)); + listener->QueryInterface(NS_GET_IID(imgIDecoderObserver), getter_AddRefs(mListener)); NS_RELEASE(listener); } mSuppressStyleCheck = true; nsresult rv = nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow); mSuppressStyleCheck = false; UpdateLoadFlags(); @@ -572,50 +572,19 @@ nsImageBoxFrame::GetType() const #ifdef DEBUG NS_IMETHODIMP nsImageBoxFrame::GetFrameName(nsAString& aResult) const { return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult); } #endif -nsresult -nsImageBoxFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) -{ - if (aType == imgINotificationObserver::SIZE_AVAILABLE) { - nsCOMPtr<imgIContainer> image; - aRequest->GetImage(getter_AddRefs(image)); - return OnStartContainer(aRequest, image); - } - if (aType == imgINotificationObserver::DECODE_COMPLETE) { - return OnStopDecode(aRequest); - } - - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - uint32_t imgStatus; - aRequest->GetImageStatus(&imgStatus); - nsresult status = - imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; - return OnStopRequest(aRequest, status); - } - - if (aType == imgINotificationObserver::IS_ANIMATED) { - return OnImageIsAnimated(aRequest); - } - - if (aType == imgINotificationObserver::FRAME_UPDATE) { - return FrameChanged(aRequest); - } - - return NS_OK; -} - -nsresult nsImageBoxFrame::OnStartContainer(imgIRequest *request, - imgIContainer *image) +NS_IMETHODIMP nsImageBoxFrame::OnStartContainer(imgIRequest *request, + imgIContainer *image) { NS_ENSURE_ARG_POINTER(image); // Ensure the animation (if any) is started. Note: There is no // corresponding call to Decrement for this. This Increment will be // 'cleaned up' by the Request when it is destroyed, but only then. request->IncrementAnimationConsumers(); @@ -629,71 +598,113 @@ nsresult nsImageBoxFrame::OnStartContain if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { PresContext()->PresShell()-> FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); } return NS_OK; } -nsresult nsImageBoxFrame::OnStopDecode(imgIRequest *request) +NS_IMETHODIMP nsImageBoxFrame::OnStopContainer(imgIRequest *request, + imgIContainer *image) { nsBoxLayoutState state(PresContext()); this->Redraw(state); return NS_OK; } -nsresult nsImageBoxFrame::OnStopRequest(imgIRequest *request, - nsresult aStatus) +NS_IMETHODIMP nsImageBoxFrame::OnStopDecode(imgIRequest *request, + nsresult aStatus, + const PRUnichar *statusArg) { if (NS_SUCCEEDED(aStatus)) // Fire an onload DOM event. FireImageDOMEvent(mContent, NS_LOAD); else { // Fire an onerror DOM event. mIntrinsicSize.SizeTo(0, 0); PresContext()->PresShell()-> FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); FireImageDOMEvent(mContent, NS_LOAD_ERROR); } return NS_OK; } -nsresult nsImageBoxFrame::OnImageIsAnimated(imgIRequest *aRequest) +NS_IMETHODIMP nsImageBoxFrame::OnImageIsAnimated(imgIRequest *aRequest) { // Register with our refresh driver, if we're animated. nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest, &mRequestRegistered); return NS_OK; } -nsresult nsImageBoxFrame::FrameChanged(imgIRequest *aRequest) +NS_IMETHODIMP nsImageBoxFrame::FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) { if ((0 == mRect.width) || (0 == mRect.height)) { return NS_OK; } InvalidateLayer(nsDisplayItem::TYPE_XUL_IMAGE); return NS_OK; } -NS_IMPL_ISUPPORTS1(nsImageBoxListener, imgINotificationObserver) +NS_IMPL_ISUPPORTS2(nsImageBoxListener, imgIDecoderObserver, imgIContainerObserver) nsImageBoxListener::nsImageBoxListener() { } nsImageBoxListener::~nsImageBoxListener() { } -NS_IMETHODIMP -nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData) +NS_IMETHODIMP nsImageBoxListener::OnStartContainer(imgIRequest *request, + imgIContainer *image) +{ + if (!mFrame) + return NS_OK; + + return mFrame->OnStartContainer(request, image); +} + +NS_IMETHODIMP nsImageBoxListener::OnStopContainer(imgIRequest *request, + imgIContainer *image) { if (!mFrame) return NS_OK; - return mFrame->Notify(request, aType, aData); + return mFrame->OnStopContainer(request, image); +} + +NS_IMETHODIMP nsImageBoxListener::OnStopDecode(imgIRequest *request, + nsresult status, + const PRUnichar *statusArg) +{ + if (!mFrame) + return NS_OK; + + return mFrame->OnStopDecode(request, status, statusArg); } + +NS_IMETHODIMP nsImageBoxListener::OnImageIsAnimated(imgIRequest* aRequest) +{ + if (!mFrame) + return NS_OK; + + return mFrame->OnImageIsAnimated(aRequest); +} + +NS_IMETHODIMP nsImageBoxListener::FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) +{ + if (!mFrame) + return NS_ERROR_FAILURE; + + return mFrame->FrameChanged(aRequest, aContainer, aDirtyRect); +} +
--- a/layout/xul/base/src/nsImageBoxFrame.h +++ b/layout/xul/base/src/nsImageBoxFrame.h @@ -6,30 +6,40 @@ #define nsImageBoxFrame_h___ #include "mozilla/Attributes.h" #include "nsLeafBoxFrame.h" #include "imgILoader.h" #include "imgIRequest.h" #include "imgIContainer.h" -#include "imgINotificationObserver.h" +#include "nsStubImageDecoderObserver.h" class nsImageBoxFrame; class nsDisplayXULImage; -class nsImageBoxListener : public imgINotificationObserver +class nsImageBoxListener : public nsStubImageDecoderObserver { public: nsImageBoxListener(); virtual ~nsImageBoxListener(); NS_DECL_ISUPPORTS - NS_DECL_IMGINOTIFICATIONOBSERVER + // imgIDecoderObserver (override nsStubImageDecoderObserver) + NS_IMETHOD OnStartContainer(imgIRequest *request, imgIContainer *image); + NS_IMETHOD OnStopContainer(imgIRequest *request, imgIContainer *image); + NS_IMETHOD OnStopDecode(imgIRequest *request, nsresult status, + const PRUnichar *statusArg); + NS_IMETHOD OnImageIsAnimated(imgIRequest* aRequest); + + // imgIContainerObserver (override nsStubImageDecoderObserver) + NS_IMETHOD FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect); void SetFrame(nsImageBoxFrame *frame) { mFrame = frame; } private: nsImageBoxFrame *mFrame; }; class nsImageBoxFrame : public nsLeafBoxFrame @@ -38,18 +48,16 @@ public: friend class nsDisplayXULImage; NS_DECL_FRAMEARENA_HELPERS virtual nsSize GetPrefSize(nsBoxLayoutState& aBoxLayoutState); virtual nsSize GetMinSize(nsBoxLayoutState& aBoxLayoutState); virtual nscoord GetBoxAscent(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; virtual void MarkIntrinsicWidthsDirty() MOZ_OVERRIDE; - nsresult Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData); - friend nsIFrame* NS_NewImageBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); NS_IMETHOD Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* asPrevInFlow) MOZ_OVERRIDE; NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, @@ -76,45 +84,51 @@ public: * image using the new load flags. */ void UpdateLoadFlags(); NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) MOZ_OVERRIDE; + NS_IMETHOD OnStartContainer(imgIRequest *request, imgIContainer *image); + NS_IMETHOD OnStopContainer(imgIRequest *request, imgIContainer *image); + NS_IMETHOD OnStopDecode(imgIRequest *request, + nsresult status, + const PRUnichar *statusArg); + NS_IMETHOD OnImageIsAnimated(imgIRequest* aRequest); + + NS_IMETHOD FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect); + virtual ~nsImageBoxFrame(); void PaintImage(nsRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsPoint aPt, uint32_t aFlags); already_AddRefed<mozilla::layers::ImageContainer> GetContainer(); protected: nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext); virtual void GetImageSize(); private: - nsresult OnStartContainer(imgIRequest *request, imgIContainer *image); - nsresult OnStopDecode(imgIRequest *request); - nsresult OnStopRequest(imgIRequest *request, nsresult status); - nsresult OnImageIsAnimated(imgIRequest* aRequest); - nsresult FrameChanged(imgIRequest *aRequest); nsRect mSubRect; ///< If set, indicates that only the portion of the image specified by the rect should be used. nsSize mIntrinsicSize; nsSize mImageSize; // Boolean variable to determine if the current image request has been // registered with the refresh driver. bool mRequestRegistered; nsCOMPtr<imgIRequest> mImageRequest; - nsCOMPtr<imgINotificationObserver> mListener; + nsCOMPtr<imgIDecoderObserver> mListener; int32_t mLoadFlags; bool mUseSrcAttr; ///< Whether or not the image src comes from an attribute. bool mSuppressStyleCheck; }; // class nsImageBoxFrame class nsDisplayXULImage : public nsDisplayImageContainer {
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp @@ -2112,18 +2112,18 @@ nsTreeBodyFrame::GetImage(int32_t aRowIn bool animated = true; // Assuming animated is the safe option // We can only call GetAnimated if we're decoded if (*aResult && (status & imgIRequest::STATUS_DECODE_COMPLETE)) (*aResult)->GetAnimated(&animated); if ((!(status & imgIRequest::STATUS_LOAD_COMPLETE)) || animated) { // We either aren't done loading, or we're animating. Add our row as a listener for invalidations. - nsCOMPtr<imgINotificationObserver> obs; - imgReq->GetNotificationObserver(getter_AddRefs(obs)); + nsCOMPtr<imgIDecoderObserver> obs; + imgReq->GetDecoderObserver(getter_AddRefs(obs)); if (obs) { static_cast<nsTreeImageListener*> (obs.get())->AddCell(aRowIndex, aCol); } return NS_OK; } } @@ -2135,21 +2135,21 @@ nsTreeBodyFrame::GetImage(int32_t aRowIn if (!listener) return NS_ERROR_OUT_OF_MEMORY; if (!mCreatedListeners.PutEntry(listener)) { return NS_ERROR_FAILURE; } listener->AddCell(aRowIndex, aCol); - nsCOMPtr<imgINotificationObserver> imgNotificationObserver = listener; + nsCOMPtr<imgIDecoderObserver> imgDecoderObserver = listener; nsCOMPtr<imgIRequest> imageRequest; if (styleRequest) { - styleRequest->Clone(imgNotificationObserver, getter_AddRefs(imageRequest)); + styleRequest->Clone(imgDecoderObserver, getter_AddRefs(imageRequest)); } else { nsIDocument* doc = mContent->GetDocument(); if (!doc) // The page is currently being torn down. Why bother. return NS_ERROR_FAILURE; nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI(); @@ -2164,17 +2164,17 @@ nsTreeBodyFrame::GetImage(int32_t aRowIn // XXXbz what's the origin principal for this stuff that comes from our // view? I guess we should assume that it's the node's principal... if (nsContentUtils::CanLoadImage(srcURI, mContent, doc, mContent->NodePrincipal())) { nsresult rv = nsContentUtils::LoadImage(srcURI, doc, mContent->NodePrincipal(), doc->GetDocumentURI(), - imgNotificationObserver, + imgDecoderObserver, nsIRequest::LOAD_NORMAL, getter_AddRefs(imageRequest)); NS_ENSURE_SUCCESS(rv, rv); } } listener->UnsuppressInvalidation(); @@ -2182,17 +2182,17 @@ nsTreeBodyFrame::GetImage(int32_t aRowIn return NS_ERROR_FAILURE; // We don't want discarding/decode-on-draw for xul images imageRequest->StartDecoding(); imageRequest->LockImage(); // In a case it was already cached. imageRequest->GetImage(aResult); - nsTreeImageCacheEntry cacheEntry(imageRequest, imgNotificationObserver); + nsTreeImageCacheEntry cacheEntry(imageRequest, imgDecoderObserver); mImageCache.Put(imageSrc, cacheEntry); } return NS_OK; } nsRect nsTreeBodyFrame::GetImageSize(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext, nsStyleContext* aStyleContext) {
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.h +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.h @@ -15,33 +15,33 @@ #include "nsITimer.h" #include "nsIReflowCallback.h" #include "nsTArray.h" #include "nsTreeStyleCache.h" #include "nsTreeColumns.h" #include "nsAutoPtr.h" #include "nsDataHashtable.h" #include "imgIRequest.h" -#include "imgINotificationObserver.h" +#include "imgIDecoderObserver.h" #include "nsScrollbarFrame.h" #include "nsThreadUtils.h" #include "mozilla/LookAndFeel.h" class nsOverflowChecker; class nsTreeImageListener; // An entry in the tree's image cache struct nsTreeImageCacheEntry { nsTreeImageCacheEntry() {} - nsTreeImageCacheEntry(imgIRequest *aRequest, imgINotificationObserver *aListener) + nsTreeImageCacheEntry(imgIRequest *aRequest, imgIDecoderObserver *aListener) : request(aRequest), listener(aListener) {} nsCOMPtr<imgIRequest> request; - nsCOMPtr<imgINotificationObserver> listener; + nsCOMPtr<imgIDecoderObserver> listener; }; // The actual frame that paints the cells and rows. class nsTreeBodyFrame MOZ_FINAL : public nsLeafBoxFrame , public nsICSSPseudoComparator , public nsIScrollbarMediator , public nsIReflowCallback
--- a/layout/xul/base/src/tree/src/nsTreeImageListener.cpp +++ b/layout/xul/base/src/tree/src/nsTreeImageListener.cpp @@ -3,51 +3,67 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsTreeImageListener.h" #include "nsITreeBoxObject.h" #include "imgIRequest.h" #include "imgIContainer.h" -NS_IMPL_ISUPPORTS1(nsTreeImageListener, imgINotificationObserver) +NS_IMPL_ISUPPORTS2(nsTreeImageListener, imgIDecoderObserver, imgIContainerObserver) nsTreeImageListener::nsTreeImageListener(nsTreeBodyFrame* aTreeFrame) : mTreeFrame(aTreeFrame), mInvalidationSuppressed(true), mInvalidationArea(nullptr) { } nsTreeImageListener::~nsTreeImageListener() { delete mInvalidationArea; } NS_IMETHODIMP -nsTreeImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) +nsTreeImageListener::OnImageIsAnimated(imgIRequest *aRequest) { - if (aType == imgINotificationObserver::IS_ANIMATED) { - return mTreeFrame ? mTreeFrame->OnImageIsAnimated(aRequest) : NS_OK; + if (!mTreeFrame) { + return NS_OK; } - if (aType == imgINotificationObserver::SIZE_AVAILABLE) { - // Ensure the animation (if any) is started. Note: There is no - // corresponding call to Decrement for this. This Increment will be - // 'cleaned up' by the Request when it is destroyed, but only then. - aRequest->IncrementAnimationConsumers(); - } + return mTreeFrame->OnImageIsAnimated(aRequest); +} - if (aType == imgINotificationObserver::FRAME_UPDATE) { - Invalidate(); - } - +NS_IMETHODIMP nsTreeImageListener::OnStartContainer(imgIRequest *aRequest, + imgIContainer *aImage) +{ + // Ensure the animation (if any) is started. Note: There is no + // corresponding call to Decrement for this. This Increment will be + // 'cleaned up' by the Request when it is destroyed, but only then. + aRequest->IncrementAnimationConsumers(); return NS_OK; } +NS_IMETHODIMP nsTreeImageListener::OnDataAvailable(imgIRequest *aRequest, + bool aCurrentFrame, + const nsIntRect *aRect) +{ + Invalidate(); + return NS_OK; +} + +NS_IMETHODIMP nsTreeImageListener::FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect) +{ + Invalidate(); + return NS_OK; +} + + void nsTreeImageListener::AddCell(int32_t aIndex, nsITreeColumn* aCol) { if (!mInvalidationArea) { mInvalidationArea = new InvalidationArea(aCol); mInvalidationArea->AddRow(aIndex); } else {
--- a/layout/xul/base/src/tree/src/nsTreeImageListener.h +++ b/layout/xul/base/src/tree/src/nsTreeImageListener.h @@ -4,28 +4,37 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsTreeImageListener_h__ #define nsTreeImageListener_h__ #include "nsString.h" #include "nsCOMPtr.h" #include "nsITreeColumns.h" +#include "nsStubImageDecoderObserver.h" #include "nsTreeBodyFrame.h" #include "mozilla/Attributes.h" // This class handles image load observation. -class nsTreeImageListener MOZ_FINAL : public imgINotificationObserver +class nsTreeImageListener MOZ_FINAL : public nsStubImageDecoderObserver { public: nsTreeImageListener(nsTreeBodyFrame *aTreeFrame); ~nsTreeImageListener(); NS_DECL_ISUPPORTS - NS_DECL_IMGINOTIFICATIONOBSERVER + // imgIDecoderObserver (override nsStubImageDecoderObserver) + NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage); + NS_IMETHOD OnImageIsAnimated(imgIRequest* aRequest); + NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame, + const nsIntRect *aRect); + // imgIContainerObserver (override nsStubImageDecoderObserver) + NS_IMETHOD FrameChanged(imgIRequest *aRequest, + imgIContainer *aContainer, + const nsIntRect *aDirtyRect); NS_IMETHOD ClearFrame(); friend class nsTreeBodyFrame; protected: void UnsuppressInvalidation() { mInvalidationSuppressed = false; } void Invalidate();
--- a/toolkit/system/gnome/nsAlertsIconListener.cpp +++ b/toolkit/system/gnome/nsAlertsIconListener.cpp @@ -46,18 +46,18 @@ static void notify_closed_marshal(GClosu NS_ABORT_IF_FALSE(n_param_values >= 1, "No object in params"); nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*>(closure->data); alert->SendClosed(); NS_RELEASE(alert); } -NS_IMPL_ISUPPORTS3(nsAlertsIconListener, imgINotificationObserver, - nsIObserver, nsISupportsWeakReference) +NS_IMPL_ISUPPORTS4(nsAlertsIconListener, imgIContainerObserver, + imgIDecoderObserver, nsIObserver, nsISupportsWeakReference) nsAlertsIconListener::nsAlertsIconListener() : mLoadedFrame(false), mNotification(NULL) { if (!libNotifyHandle && !libNotifyNotAvail) { libNotifyHandle = dlopen("libnotify.so.4", RTLD_LAZY); if (!libNotifyHandle) { @@ -85,49 +85,110 @@ nsAlertsIconListener::nsAlertsIconListen nsAlertsIconListener::~nsAlertsIconListener() { if (mIconRequest) mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED); // Don't dlclose libnotify as it uses atexit(). } NS_IMETHODIMP -nsAlertsIconListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) +nsAlertsIconListener::OnStartRequest(imgIRequest* aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsAlertsIconListener::OnStartDecode(imgIRequest* aRequest) { - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - return OnStopRequest(aRequest); - } + return NS_OK; +} + - if (aType == imgINotificationObserver::FRAME_COMPLETE) { - return OnStopFrame(aRequest); - } +NS_IMETHODIMP +nsAlertsIconListener::OnStartContainer(imgIRequest* aRequest, + imgIContainer* aContainer) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsAlertsIconListener::OnStartFrame(imgIRequest* aRequest, + uint32_t aFrame) +{ return NS_OK; } -nsresult -nsAlertsIconListener::OnStopRequest(imgIRequest* aRequest) + +NS_IMETHODIMP +nsAlertsIconListener::OnDataAvailable(imgIRequest* aRequest, + bool aCurrentFrame, + const nsIntRect* aRect) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsAlertsIconListener::OnStopContainer(imgIRequest* aRequest, + imgIContainer* aContainer) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsAlertsIconListener::OnStopDecode(imgIRequest* aRequest, + nsresult status, + const PRUnichar* statusArg) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsAlertsIconListener::FrameChanged(imgIRequest* aRequest, + imgIContainer* aContainer, + const nsIntRect* aDirtyRect) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsAlertsIconListener::OnStopRequest(imgIRequest* aRequest, + bool aIsLastPart) { uint32_t imgStatus = imgIRequest::STATUS_ERROR; nsresult rv = aRequest->GetImageStatus(&imgStatus); NS_ENSURE_SUCCESS(rv, rv); if (imgStatus == imgIRequest::STATUS_ERROR && !mLoadedFrame) { // We have an error getting the image. Display the notification with no icon. ShowAlert(NULL); } if (mIconRequest) { mIconRequest->Cancel(NS_BINDING_ABORTED); mIconRequest = nullptr; } return NS_OK; } -nsresult -nsAlertsIconListener::OnStopFrame(imgIRequest* aRequest) +NS_IMETHODIMP +nsAlertsIconListener::OnDiscard(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsAlertsIconListener::OnImageIsAnimated(imgIRequest *aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsAlertsIconListener::OnStopFrame(imgIRequest* aRequest, + uint32_t aFrame) { if (aRequest != mIconRequest) return NS_ERROR_FAILURE; if (mLoadedFrame) return NS_OK; // only use one frame nsCOMPtr<imgIContainer> image;
--- a/toolkit/system/gnome/nsAlertsIconListener.h +++ b/toolkit/system/gnome/nsAlertsIconListener.h @@ -2,53 +2,51 @@ /* 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 nsAlertsIconListener_h__ #define nsAlertsIconListener_h__ #include "nsCOMPtr.h" -#include "imgINotificationObserver.h" +#include "imgIDecoderObserver.h" #include "nsStringAPI.h" #include "nsIObserver.h" #include "nsWeakReference.h" #include <gdk-pixbuf/gdk-pixbuf.h> class imgIRequest; struct NotifyNotification; -class nsAlertsIconListener : public imgINotificationObserver, +class nsAlertsIconListener : public imgIDecoderObserver, public nsIObserver, public nsSupportsWeakReference { public: NS_DECL_ISUPPORTS - NS_DECL_IMGINOTIFICATIONOBSERVER + NS_DECL_IMGICONTAINEROBSERVER + NS_DECL_IMGIDECODEROBSERVER NS_DECL_NSIOBSERVER nsAlertsIconListener(); virtual ~nsAlertsIconListener(); nsresult InitAlertAsync(const nsAString & aImageUrl, const nsAString & aAlertTitle, const nsAString & aAlertText, bool aAlertTextClickable, const nsAString & aAlertCookie, nsIObserver * aAlertListener); void SendCallback(); void SendClosed(); protected: - nsresult OnStopRequest(imgIRequest* aRequest); - nsresult OnStopFrame(imgIRequest* aRequest); - /** * The only difference between libnotify.so.4 and libnotify.so.1 for these symbols * is that notify_notification_new takes three arguments in libnotify.so.4 and * four in libnotify.so.1. * Passing the fourth argument as NULL is binary compatible. */ typedef void (*NotifyActionCallback)(NotifyNotification*, char*, gpointer); typedef bool (*notify_is_initted_t)(void);
--- a/widget/cocoa/nsMenuItemIconX.h +++ b/widget/cocoa/nsMenuItemIconX.h @@ -6,37 +6,38 @@ /* * Retrieves and displays icons in native menu items on Mac OS X. */ #ifndef nsMenuItemIconX_h_ #define nsMenuItemIconX_h_ #include "nsCOMPtr.h" -#include "imgINotificationObserver.h" +#include "imgIDecoderObserver.h" class nsIURI; class nsIContent; class imgIRequest; class nsMenuObjectX; #import <Cocoa/Cocoa.h> -class nsMenuItemIconX : public imgINotificationObserver +class nsMenuItemIconX : public imgIDecoderObserver { public: nsMenuItemIconX(nsMenuObjectX* aMenuItem, nsIContent* aContent, NSMenuItem* aNativeMenuItem); private: virtual ~nsMenuItemIconX(); public: NS_DECL_ISUPPORTS - NS_DECL_IMGINOTIFICATIONOBSERVER + NS_DECL_IMGICONTAINEROBSERVER + NS_DECL_IMGIDECODEROBSERVER // SetupIcon succeeds if it was able to set up the icon, or if there should // be no icon, in which case it clears any existing icon but still succeeds. nsresult SetupIcon(); // GetIconURI fails if the item should not have any icon. nsresult GetIconURI(nsIURI** aIconURI); @@ -46,18 +47,16 @@ public: // Unless we take precautions, we may outlive the object that created us // (mMenuObject, which owns our native menu item (mNativeMenuItem)). // Destroy() should be called from mMenuObject's destructor to prevent // this from happening. See bug 499600. void Destroy(); protected: - nsresult OnStopFrame(imgIRequest* aRequest); - nsCOMPtr<nsIContent> mContent; nsCOMPtr<imgIRequest> mIconRequest; nsMenuObjectX* mMenuObject; // [weak] nsIntRect mImageRegionRect; bool mLoadedIcon; bool mSetIcon; NSMenuItem* mNativeMenuItem; // [weak] };
--- a/widget/cocoa/nsMenuItemIconX.mm +++ b/widget/cocoa/nsMenuItemIconX.mm @@ -37,17 +37,17 @@ static const uint32_t kIconComponents = static const uint32_t kIconBitsPerPixel = kIconBitsPerComponent * kIconComponents; static const uint32_t kIconBytesPerRow = kIconWidth * kIconBitsPerPixel / 8; static const uint32_t kIconBytes = kIconBytesPerRow * kIconHeight; typedef NS_STDCALL_FUNCPROTO(nsresult, GetRectSideMethod, nsIDOMRect, GetBottom, (nsIDOMCSSPrimitiveValue**)); -NS_IMPL_ISUPPORTS1(nsMenuItemIconX, imgINotificationObserver) +NS_IMPL_ISUPPORTS2(nsMenuItemIconX, imgIContainerObserver, imgIDecoderObserver) nsMenuItemIconX::nsMenuItemIconX(nsMenuObjectX* aMenuItem, nsIContent* aContent, NSMenuItem* aNativeMenuItem) : mContent(aContent) , mMenuObject(aMenuItem) , mLoadedIcon(false) , mSetIcon(false) @@ -313,38 +313,67 @@ nsMenuItemIconX::LoadIcon(nsIURI* aIconU mIconRequest->StartDecoding(); return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } // -// imgINotificationObserver +// imgIContainerObserver +// + +NS_IMETHODIMP +nsMenuItemIconX::FrameChanged(imgIRequest* aRequest, + imgIContainer* aContainer, + const nsIntRect* aDirtyRect) +{ + return NS_OK; +} + +// +// imgIDecoderObserver // NS_IMETHODIMP -nsMenuItemIconX::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) +nsMenuItemIconX::OnStartRequest(imgIRequest* aRequest) { - if (aType == imgINotificationObserver::FRAME_COMPLETE) { - return OnStopFrame(aRequest); - } + return NS_OK; +} - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - if (mIconRequest && mIconRequest == aRequest) { - mIconRequest->Cancel(NS_BINDING_ABORTED); - mIconRequest = nullptr; - } - } +NS_IMETHODIMP +nsMenuItemIconX::OnStartDecode(imgIRequest* aRequest) +{ + return NS_OK; +} +NS_IMETHODIMP +nsMenuItemIconX::OnStartContainer(imgIRequest* aRequest, + imgIContainer* aContainer) +{ return NS_OK; } -nsresult -nsMenuItemIconX::OnStopFrame(imgIRequest* aRequest) +NS_IMETHODIMP +nsMenuItemIconX::OnStartFrame(imgIRequest* aRequest, uint32_t aFrame) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMenuItemIconX::OnDataAvailable(imgIRequest* aRequest, + bool aCurrentFrame, + const nsIntRect* aRect) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMenuItemIconX::OnStopFrame(imgIRequest* aRequest, + uint32_t aFrame) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; if (aRequest != mIconRequest) return NS_ERROR_FAILURE; // Only support one frame. if (mLoadedIcon) @@ -457,8 +486,46 @@ nsMenuItemIconX::OnStopFrame(imgIRequest mLoadedIcon = true; mSetIcon = true; return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } + +NS_IMETHODIMP +nsMenuItemIconX::OnStopContainer(imgIRequest* aRequest, + imgIContainer* aContainer) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMenuItemIconX::OnStopDecode(imgIRequest* aRequest, + nsresult status, + const PRUnichar* statusArg) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMenuItemIconX::OnStopRequest(imgIRequest* aRequest, + bool aIsLastPart) +{ + if (mIconRequest && mIconRequest == aRequest) { + mIconRequest->Cancel(NS_BINDING_ABORTED); + mIconRequest = nullptr; + } + return NS_OK; +} + +NS_IMETHODIMP +nsMenuItemIconX::OnDiscard(imgIRequest* aRequest) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMenuItemIconX::OnImageIsAnimated(imgIRequest* aRequest) +{ + return NS_OK; +}