From 41d18e74d0af520601ec5debb1da23213f419274 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Sun, 16 Dec 2012 21:13:35 -0500 Subject: [PATCH] Backed out 4 changesets (bug 815471, bug 821023, bug 816374, bug 816362) for reftest failures. --- image/build/nsImageModule.cpp | 8 ++ image/public/imgITools.idl | 25 +--- image/src/Decoder.cpp | 16 +-- image/src/Decoder.h | 1 - image/src/Image.cpp | 5 +- image/src/Image.h | 47 +------ image/src/ImageFactory.cpp | 200 ---------------------------- image/src/ImageFactory.h | 68 ---------- image/src/Makefile.in | 4 - image/src/RasterImage.cpp | 33 +---- image/src/RasterImage.h | 24 ++-- image/src/VectorImage.cpp | 30 +---- image/src/VectorImage.h | 20 +-- image/src/imgLoader.cpp | 3 +- image/src/imgRequest.cpp | 217 ++++++++++++++++++++++++------- image/src/imgRequest.h | 2 +- image/src/imgRequestProxy.cpp | 47 ++----- image/src/imgRequestProxy.h | 7 +- image/src/imgStatusTracker.cpp | 156 +++++++++++----------- image/src/imgStatusTracker.h | 34 ++--- image/src/imgTools.cpp | 55 ++++---- image/src/imgTools.h | 10 +- image/test/unit/test_imgtools.js | 32 +++-- 23 files changed, 380 insertions(+), 664 deletions(-) delete mode 100644 image/src/ImageFactory.cpp delete mode 100644 image/src/ImageFactory.h diff --git a/image/build/nsImageModule.cpp b/image/build/nsImageModule.cpp index b6a35cfe099..25f626ac0c7 100644 --- a/image/build/nsImageModule.cpp +++ b/image/build/nsImageModule.cpp @@ -28,6 +28,11 @@ #include "nsBMPEncoder.h" // objects that just require generic constructors +namespace mozilla { +namespace image { +NS_GENERIC_FACTORY_CONSTRUCTOR(RasterImage) +} +} using namespace mozilla::image; NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(imgLoader, Init) @@ -40,6 +45,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsBMPEncoder) NS_DEFINE_NAMED_CID(NS_IMGLOADER_CID); NS_DEFINE_NAMED_CID(NS_IMGREQUESTPROXY_CID); NS_DEFINE_NAMED_CID(NS_IMGTOOLS_CID); +NS_DEFINE_NAMED_CID(NS_RASTERIMAGE_CID); NS_DEFINE_NAMED_CID(NS_ICOENCODER_CID); NS_DEFINE_NAMED_CID(NS_JPEGENCODER_CID); NS_DEFINE_NAMED_CID(NS_PNGENCODER_CID); @@ -49,6 +55,7 @@ static const mozilla::Module::CIDEntry kImageCIDs[] = { { &kNS_IMGLOADER_CID, false, NULL, imgLoaderConstructor, }, { &kNS_IMGREQUESTPROXY_CID, false, NULL, imgRequestProxyConstructor, }, { &kNS_IMGTOOLS_CID, false, NULL, imgToolsConstructor, }, + { &kNS_RASTERIMAGE_CID, false, NULL, RasterImageConstructor, }, { &kNS_ICOENCODER_CID, false, NULL, nsICOEncoderConstructor, }, { &kNS_JPEGENCODER_CID, false, NULL, nsJPEGEncoderConstructor, }, { &kNS_PNGENCODER_CID, false, NULL, nsPNGEncoderConstructor, }, @@ -61,6 +68,7 @@ static const mozilla::Module::ContractIDEntry kImageContracts[] = { { "@mozilla.org/image/loader;1", &kNS_IMGLOADER_CID }, { "@mozilla.org/image/request;1", &kNS_IMGREQUESTPROXY_CID }, { "@mozilla.org/image/tools;1", &kNS_IMGTOOLS_CID }, + { "@mozilla.org/image/rasterimage;1", &kNS_RASTERIMAGE_CID }, { "@mozilla.org/image/encoder;2?type=image/vnd.microsoft.icon", &kNS_ICOENCODER_CID }, { "@mozilla.org/image/encoder;2?type=image/jpeg", &kNS_JPEGENCODER_CID }, { "@mozilla.org/image/encoder;2?type=image/png", &kNS_PNGENCODER_CID }, diff --git a/image/public/imgITools.idl b/image/public/imgITools.idl index 85e56a870d8..62d3b7e5445 100644 --- a/image/public/imgITools.idl +++ b/image/public/imgITools.idl @@ -14,23 +14,9 @@ interface nsIDOMDocument; interface imgIScriptedNotificationObserver; interface imgINotificationObserver; -[scriptable, builtinclass, uuid(4c2383a4-931c-484d-8c4a-973590f66e3f)] +[scriptable, builtinclass, uuid(98bd5bf9-87eb-4d92-81b1-4cd10c64f7b2)] interface imgITools : nsISupports { - /** - * decodeImage - * 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. - * - * @param aStream - * An input stream for an encoded image file. - * @param aMimeType - * Type of image in the stream. - */ - imgIContainer decodeImage(in nsIInputStream aStream, - in ACString aMimeType); - /** * decodeImageData * Caller provides an input stream and mimetype. We read from the stream @@ -38,9 +24,6 @@ interface imgITools : nsISupports * the resulting imgIContainer. (If the caller already has a container, * it can be provided as input to be reused). * - * This method is deprecated; new code should use |decodeImage|. Callers - * of |decodeImageData| must provide a |null| aContainer argument. - * * @param aStream * An input stream for an encoded image file. * @param aMimeType @@ -51,9 +34,9 @@ interface imgITools : nsISupports * be used. It is an error to pass an already-initialized container * as aContainer. */ - [deprecated] void decodeImageData(in nsIInputStream aStream, - in ACString aMimeType, - inout imgIContainer aContainer); + void decodeImageData(in nsIInputStream aStream, + in ACString aMimeType, + inout imgIContainer aContainer); /** * encodeImage diff --git a/image/src/Decoder.cpp b/image/src/Decoder.cpp index 9fb1a368df1..1d91542253a 100644 --- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -24,7 +24,6 @@ Decoder::Decoder(RasterImage &aImage, imgIDecoderObserver* aObserver) , mSizeDecode(false) , mInFrame(false) , mIsAnimated(false) - , mFirstWrite(true) { } @@ -43,6 +42,10 @@ 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(); + // Implementation-specific initialization InitInternal(); mInitialized = true; @@ -56,9 +59,6 @@ Decoder::InitSharedDecoder() // No re-initializing NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!"); - // Prevent duplicate notifications. - mFirstWrite = false; - // Implementation-specific initialization InitInternal(); mInitialized = true; @@ -71,14 +71,6 @@ Decoder::Write(const char* aBuffer, uint32_t aCount) NS_ABORT_IF_FALSE(!HasDecoderError(), "Not allowed to make more decoder calls after error!"); - // If this is our first write, fire OnStartDecode to support bug 512435. - if (mFirstWrite) { - if (!IsSizeDecode() && mObserver) - mObserver->OnStartDecode(); - - mFirstWrite = false; - } - // If a data error occured, just ignore future data if (HasDataError()) return; diff --git a/image/src/Decoder.h b/image/src/Decoder.h index 978bf298228..a042776381f 100644 --- a/image/src/Decoder.h +++ b/image/src/Decoder.h @@ -192,7 +192,6 @@ private: bool mSizeDecode; bool mInFrame; bool mIsAnimated; - bool mFirstWrite; }; } // namespace image diff --git a/image/src/Image.cpp b/image/src/Image.cpp index 83359a1b519..c0fabe426a4 100644 --- a/image/src/Image.cpp +++ b/image/src/Image.cpp @@ -9,9 +9,8 @@ namespace mozilla { namespace image { // Constructor -Image::Image(imgStatusTracker* aStatusTracker, nsIURI* aURI) : +Image::Image(imgStatusTracker* aStatusTracker) : mInnerWindowId(0), - mURI(aURI), mAnimationConsumers(0), mAnimationMode(kNormalAnimMode), mInitialized(false), @@ -22,7 +21,7 @@ Image::Image(imgStatusTracker* aStatusTracker, nsIURI* aURI) : mStatusTracker = aStatusTracker; mStatusTracker->SetImage(this); } else { - mStatusTracker = new imgStatusTracker(this); + mStatusTracker = new imgStatusTracker(this, nullptr); } } diff --git a/image/src/Image.h b/image/src/Image.h index 67397202ec8..996cd279b93 100644 --- a/image/src/Image.h +++ b/image/src/Image.h @@ -8,9 +8,6 @@ #include "imgIContainer.h" #include "imgStatusTracker.h" -#include "nsIURI.h" -#include "nsIRequest.h" -#include "nsIInputStream.h" namespace mozilla { namespace image { @@ -50,6 +47,7 @@ public: */ virtual nsresult Init(imgIDecoderObserver* aObserver, const char* aMimeType, + const char* aURIString, uint32_t aFlags) = 0; /** @@ -90,51 +88,15 @@ public: uint32_t GetAnimationConsumers() { return mAnimationConsumers; } #endif - /** - * Called from OnDataAvailable when the stream associated with the image has - * received new image data. The arguments are the same as OnDataAvailable's, - * but by separating this functionality into a different method we don't - * interfere with subclasses which wish to implement nsIStreamListener. - * - * Images should not do anything that could send out notifications until they - * have received their first OnImageDataAvailable notification; in - * particular, this means that instantiating decoders should be deferred - * until OnImageDataAvailable is called. - */ - virtual nsresult OnImageDataAvailable(nsIRequest* aRequest, - nsISupports* aContext, - nsIInputStream* aInStr, - uint64_t aSourceOffset, - uint32_t aCount) = 0; - - /** - * Called from OnStopRequest when the image's underlying request completes. - * The arguments are the same as OnStopRequest's, but by separating this - * functionality into a different method we don't interfere with subclasses - * which wish to implement nsIStreamListener. - */ - virtual nsresult OnImageDataComplete(nsIRequest* aRequest, - nsISupports* aContext, - nsresult status) = 0; - - /** - * Called for multipart images to allow for any necessary reinitialization - * when there's a new part to add. - */ - virtual nsresult OnNewSourceData() = 0; - void SetInnerWindowID(uint64_t aInnerWindowId) { mInnerWindowId = aInnerWindowId; } uint64_t InnerWindowID() const { return mInnerWindowId; } - bool HasError() { return mError; } - void SetHasError() { mError = true; } - - nsIURI* GetURI() { return mURI; } + bool HasError() { return mError; } protected: - Image(imgStatusTracker* aStatusTracker, nsIURI* aURI); + Image(imgStatusTracker* aStatusTracker); // Shared functionality for implementors of imgIContainer. Every // implementation of attribute animationMode should forward here. @@ -153,8 +115,7 @@ protected: uint64_t mInnerWindowId; // Member data shared by all implementations of this abstract class - nsRefPtr mStatusTracker; - nsCOMPtr mURI; + nsAutoPtr mStatusTracker; uint32_t mAnimationConsumers; uint16_t mAnimationMode; // Enum values in imgIContainer bool mInitialized:1; // Have we been initalized? diff --git a/image/src/ImageFactory.cpp b/image/src/ImageFactory.cpp deleted file mode 100644 index 8e0017adb5a..00000000000 --- a/image/src/ImageFactory.cpp +++ /dev/null @@ -1,200 +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 "mozilla/Preferences.h" -#include "mozilla/Likely.h" - -#include "nsIHttpChannel.h" -#include "nsSimpleURI.h" - -#include "RasterImage.h" -#include "VectorImage.h" - -#include "ImageFactory.h" - -namespace mozilla { -namespace image { - -const char* SVG_MIMETYPE = "image/svg+xml"; - -// Global preferences related to image containers. -static bool gInitializedPrefCaches = false; -static bool gDecodeOnDraw = false; -static bool gDiscardable = false; - -static void -InitPrefCaches() -{ - Preferences::AddBoolVarCache(&gDiscardable, "image.mem.discardable"); - Preferences::AddBoolVarCache(&gDecodeOnDraw, "image.mem.decodeondraw"); - gInitializedPrefCaches = true; -} - -static uint32_t -ComputeImageFlags(nsIURI* uri, bool isMultiPart) -{ - nsresult rv; - - // We default to the static globals - bool isDiscardable = gDiscardable; - bool doDecodeOnDraw = gDecodeOnDraw; - - // We want UI to be as snappy as possible and not to flicker. Disable discarding - // and decode-on-draw for chrome URLS - bool isChrome = false; - rv = uri->SchemeIs("chrome", &isChrome); - if (NS_SUCCEEDED(rv) && isChrome) - isDiscardable = doDecodeOnDraw = false; - - // We don't want resources like the "loading" icon to be discardable or - // decode-on-draw either. - bool isResource = false; - rv = uri->SchemeIs("resource", &isResource); - if (NS_SUCCEEDED(rv) && isResource) - isDiscardable = doDecodeOnDraw = false; - - // For multipart/x-mixed-replace, we basically want a direct channel to the - // decoder. Disable both for this case as well. - if (isMultiPart) - isDiscardable = doDecodeOnDraw = false; - - // We have all the information we need - uint32_t imageFlags = Image::INIT_FLAG_NONE; - if (isDiscardable) - imageFlags |= Image::INIT_FLAG_DISCARDABLE; - if (doDecodeOnDraw) - imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW; - if (isMultiPart) - imageFlags |= Image::INIT_FLAG_MULTIPART; - - return imageFlags; -} - -/* static */ already_AddRefed -ImageFactory::CreateImage(nsIRequest* aRequest, - imgStatusTracker* aStatusTracker, - const nsCString& aMimeType, - nsIURI* aURI, - bool aIsMultiPart, - uint32_t aInnerWindowId) -{ - // Register our pref observers if we haven't yet. - if (MOZ_UNLIKELY(!gInitializedPrefCaches)) - InitPrefCaches(); - - // Compute the image's initialization flags. - uint32_t imageFlags = ComputeImageFlags(aURI, aIsMultiPart); - - // Select the type of image to create based on MIME type. - if (aMimeType.Equals(SVG_MIMETYPE)) { - return CreateVectorImage(aRequest, aStatusTracker, aMimeType, - aURI, imageFlags, aInnerWindowId); - } else { - return CreateRasterImage(aRequest, aStatusTracker, aMimeType, - aURI, imageFlags, aInnerWindowId); - } -} - -// Marks an image as having an error before returning it. Used with macros like -// NS_ENSURE_SUCCESS, since we guarantee to always return an image even if an -// error occurs, but callers need to be able to tell that this happened. -template -static already_AddRefed -BadImage(nsRefPtr& image) -{ - image->SetHasError(); - return image.forget(); -} - -/* static */ already_AddRefed -ImageFactory::CreateAnonymousImage(const nsCString& aMimeType) -{ - nsresult rv; - - nsRefPtr newImage = new RasterImage(); - - rv = newImage->Init(nullptr, aMimeType.get(), Image::INIT_FLAG_NONE); - NS_ENSURE_SUCCESS(rv, BadImage(newImage)); - - return newImage.forget(); -} - -/* static */ already_AddRefed - -ImageFactory::CreateRasterImage(nsIRequest* aRequest, - imgStatusTracker* aStatusTracker, - const nsCString& aMimeType, - nsIURI* aURI, - uint32_t aImageFlags, - uint32_t aInnerWindowId) -{ - nsresult rv; - - nsRefPtr newImage = new RasterImage(aStatusTracker, aURI); - - rv = newImage->Init(aStatusTracker->GetDecoderObserver(), - aMimeType.get(), aImageFlags); - NS_ENSURE_SUCCESS(rv, BadImage(newImage)); - - newImage->SetInnerWindowID(aInnerWindowId); - - // Use content-length as a size hint for http channels. - nsCOMPtr httpChannel(do_QueryInterface(aRequest)); - if (httpChannel) { - nsAutoCString contentLength; - rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"), - contentLength); - if (NS_SUCCEEDED(rv)) { - int32_t len = contentLength.ToInteger(&rv); - - // Pass anything usable on so that the RasterImage can preallocate - // its source buffer - if (len > 0) { - uint32_t sizeHint = (uint32_t) len; - sizeHint = NS_MIN(sizeHint, 20000000); // Bound by something reasonable - rv = newImage->SetSourceSizeHint(sizeHint); - if (NS_FAILED(rv)) { - // Flush memory, try to get some back, and try again - rv = nsMemory::HeapMinimize(true); - nsresult rv2 = newImage->SetSourceSizeHint(sizeHint); - // If we've still failed at this point, things are going downhill - if (NS_FAILED(rv) || NS_FAILED(rv2)) { - NS_WARNING("About to hit OOM in imagelib!"); - } - } - } - } - } - - return newImage.forget(); -} - -/* static */ already_AddRefed -ImageFactory::CreateVectorImage(nsIRequest* aRequest, - imgStatusTracker* aStatusTracker, - const nsCString& aMimeType, - nsIURI* aURI, - uint32_t aImageFlags, - uint32_t aInnerWindowId) -{ - nsresult rv; - - nsRefPtr newImage = new VectorImage(aStatusTracker, aURI); - - rv = newImage->Init(aStatusTracker->GetDecoderObserver(), - aMimeType.get(), aImageFlags); - NS_ENSURE_SUCCESS(rv, BadImage(newImage)); - - newImage->SetInnerWindowID(aInnerWindowId); - - rv = newImage->OnStartRequest(aRequest, nullptr); - NS_ENSURE_SUCCESS(rv, BadImage(newImage)); - - return newImage.forget(); -} - -} // namespace image -} // namespace mozilla diff --git a/image/src/ImageFactory.h b/image/src/ImageFactory.h deleted file mode 100644 index a285a3e5277..00000000000 --- a/image/src/ImageFactory.h +++ /dev/null @@ -1,68 +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 "nsIURI.h" -#include "nsIRequest.h" - -#include "imgIContainer.h" -#include "imgStatusTracker.h" - -#include "Image.h" - -namespace mozilla { -namespace image { - -extern const char* SVG_MIMETYPE; - -struct ImageFactory -{ - /** - * Creates a new image with the given properties. - * - * @param aRequest The associated request. - * @param aStatusTracker A status tracker for the image to use. - * @param aMimeType The mimetype of the image. - * @param aURI The URI of the image. - * @param aIsMultiPart Whether the image is part of a multipart request. - * @param aInnerWindowId The window this image belongs to. - */ - static already_AddRefed CreateImage(nsIRequest* aRequest, - imgStatusTracker* aStatusTracker, - const nsCString& aMimeType, - nsIURI* aURI, - bool aIsMultiPart, - uint32_t aInnerWindowId); - /** - * Creates a new image which isn't associated with a URI or loaded through - * the usual image loading mechanism. - * - * @param aMimeType The mimetype of the image. - */ - static already_AddRefed CreateAnonymousImage(const nsCString& aMimeType); - - -private: - // Factory functions that create specific types of image containers. - static already_AddRefed CreateRasterImage(nsIRequest* aRequest, - imgStatusTracker* aStatusTracker, - const nsCString& aMimeType, - nsIURI* aURI, - uint32_t aImageFlags, - uint32_t aInnerWindowId); - - static already_AddRefed CreateVectorImage(nsIRequest* aRequest, - imgStatusTracker* aStatusTracker, - const nsCString& aMimeType, - nsIURI* aURI, - uint32_t aImageFlags, - uint32_t aInnerWindowId); - - // This is a static factory class, so disallow instantiation. - virtual ~ImageFactory() = 0; -}; - -} // namespace image -} // namespace mozilla diff --git a/image/src/Makefile.in b/image/src/Makefile.in index ddb7f443ed4..189662404f5 100644 --- a/image/src/Makefile.in +++ b/image/src/Makefile.in @@ -26,7 +26,6 @@ EXPORTS = imgLoader.h \ CPPSRCS = \ Image.cpp \ - ImageFactory.cpp \ Decoder.cpp \ DiscardTracker.cpp \ RasterImage.cpp \ @@ -44,9 +43,6 @@ CPPSRCS = \ # We need to instantiate the decoders LOCAL_INCLUDES += -I$(topsrcdir)/image/decoders -# For URI-related functionality -LOCAL_INCLUDES += -I$(topsrcdir)/netwerk/base/src - # Because SVGDocumentWrapper.cpp includes "nsSVGSVGElement.h" LOCAL_INCLUDES += \ -I$(topsrcdir)/content/svg/content/src \ diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 11edee00457..4bbd784cfa8 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -352,9 +352,8 @@ NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties, #endif //****************************************************************************** -RasterImage::RasterImage(imgStatusTracker* aStatusTracker, - nsIURI* aURI /* = nullptr */) : - Image(aStatusTracker, aURI), // invoke superclass's constructor +RasterImage::RasterImage(imgStatusTracker* aStatusTracker) : + Image(aStatusTracker), // invoke superclass's constructor mSize(0,0), mFrameDecodeFlags(DECODE_FLAGS_DEFAULT), mAnim(nullptr), @@ -442,6 +441,7 @@ RasterImage::Initialize() nsresult RasterImage::Init(imgIDecoderObserver *aObserver, const char* aMimeType, + const char* aURIString, uint32_t aFlags) { // We don't support re-initialization @@ -464,6 +464,7 @@ RasterImage::Init(imgIDecoderObserver *aObserver, // Store initialization data mObserver = do_GetWeakReference(aObserver); mSourceDataMimeType.Assign(aMimeType); + mURIString.Assign(aURIString); mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE); mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW); mMultipart = !!(aFlags & INIT_FLAG_MULTIPART); @@ -687,7 +688,7 @@ RasterImage::ExtractFrame(uint32_t aWhichFrame, // We don't actually have a mimetype in this case. The empty string tells the // init routine not to try to instantiate a decoder. This should be fixed in // bug 505959. - img->Init(nullptr, "", INIT_FLAG_NONE); + img->Init(nullptr, "", "", INIT_FLAG_NONE); img->SetSize(aRegion.width, aRegion.height); img->mDecoded = true; // Also, we need to mark the image as decoded img->mHasBeenDecoded = true; @@ -1804,7 +1805,7 @@ get_header_str (char *buf, char *data, size_t data_len) } nsresult -RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult) +RasterImage::SourceDataComplete() { if (mError) return NS_ERROR_FAILURE; @@ -1866,27 +1867,7 @@ RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult) } nsresult -RasterImage::OnImageDataAvailable(nsIRequest*, - nsISupports*, - nsIInputStream* aInStr, - uint64_t, - uint32_t aCount) -{ - nsresult rv; - - // WriteToRasterImage always consumes everything it gets - // if it doesn't run out of memory - uint32_t bytesRead; - rv = aInStr->ReadSegments(WriteToRasterImage, this, aCount, &bytesRead); - - NS_ABORT_IF_FALSE(bytesRead == aCount || HasError(), - "WriteToRasterImage should consume everything or the image must be in error!"); - - return rv; -} - -nsresult -RasterImage::OnNewSourceData() +RasterImage::NewSourceData() { nsresult rv; diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h index a67dd81ec7d..46150a1bf22 100644 --- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -23,7 +23,6 @@ #include "imgIContainer.h" #include "nsIProperties.h" #include "nsITimer.h" -#include "nsIRequest.h" #include "nsWeakReference.h" #include "nsTArray.h" #include "imgFrame.h" @@ -153,7 +152,7 @@ public: NS_DECL_IMGICONTAINERDEBUG #endif - // (no public constructor - use ImageFactory) + RasterImage(imgStatusTracker* aStatusTracker = nullptr); virtual ~RasterImage(); virtual nsresult StartAnimation(); @@ -162,8 +161,9 @@ public: // Methods inherited from Image nsresult Init(imgIDecoderObserver* aObserver, const char* aMimeType, + const char* aURIString, uint32_t aFlags); - virtual void GetCurrentFrameRect(nsIntRect& aRect) MOZ_OVERRIDE; + void GetCurrentFrameRect(nsIntRect& aRect); // Raster-specific methods static NS_METHOD WriteToRasterImage(nsIInputStream* aIn, void* aClosure, @@ -249,15 +249,11 @@ public: */ nsresult AddSourceData(const char *aBuffer, uint32_t aCount); - virtual nsresult OnImageDataAvailable(nsIRequest* aRequest, - nsISupports* aContext, - nsIInputStream* aInStr, - uint64_t aSourceOffset, - uint32_t aCount) MOZ_OVERRIDE; - virtual nsresult OnImageDataComplete(nsIRequest* aRequest, - nsISupports* aContext, - nsresult aResult) MOZ_OVERRIDE; - virtual nsresult OnNewSourceData() MOZ_OVERRIDE; + /* Called after the all the source data has been added with addSourceData. */ + nsresult SourceDataComplete(); + + /* Called for multipart images when there's a new source image to add. */ + nsresult NewSourceData(); /** * A hint of the number of bytes of source data that the image contains. If @@ -732,11 +728,7 @@ private: // data bool StoringSourceData() const; protected: - RasterImage(imgStatusTracker* aStatusTracker = nullptr, nsIURI* aURI = nullptr); - bool ShouldAnimate(); - - friend class ImageFactory; }; inline NS_IMETHODIMP RasterImage::GetAnimationMode(uint16_t *aAnimationMode) { diff --git a/image/src/VectorImage.cpp b/image/src/VectorImage.cpp index be282ca2f08..0fc3e9f760d 100644 --- a/image/src/VectorImage.cpp +++ b/image/src/VectorImage.cpp @@ -169,9 +169,8 @@ NS_IMPL_ISUPPORTS3(VectorImage, //------------------------------------------------------------------------------ // Constructor / Destructor -VectorImage::VectorImage(imgStatusTracker* aStatusTracker, - nsIURI* aURI /* = nullptr */) : - Image(aStatusTracker, aURI), // invoke superclass's constructor +VectorImage::VectorImage(imgStatusTracker* aStatusTracker) : + Image(aStatusTracker), // invoke superclass's constructor mRestrictedRegion(0, 0, 0, 0), mIsInitialized(false), mIsFullyLoaded(false), @@ -191,6 +190,7 @@ VectorImage::~VectorImage() nsresult VectorImage::Init(imgIDecoderObserver* aObserver, const char* aMimeType, + const char* aURIString, uint32_t aFlags) { // We don't support re-initialization @@ -243,30 +243,6 @@ VectorImage::OutOfProcessSizeOfDecoded() const return 0; } -nsresult -VectorImage::OnImageDataComplete(nsIRequest* aRequest, - nsISupports* aContext, - nsresult aStatus) -{ - return OnStopRequest(aRequest, aContext, aStatus); -} - -nsresult -VectorImage::OnImageDataAvailable(nsIRequest* aRequest, - nsISupports* aContext, - nsIInputStream* aInStr, - uint64_t aSourceOffset, - uint32_t aCount) -{ - return OnDataAvailable(aRequest, aContext, aInStr, aSourceOffset, aCount); -} - -nsresult -VectorImage::OnNewSourceData() -{ - return NS_OK; -} - nsresult VectorImage::StartAnimation() { diff --git a/image/src/VectorImage.h b/image/src/VectorImage.h index 3ae67b8751c..a63bb4d4b9d 100644 --- a/image/src/VectorImage.h +++ b/image/src/VectorImage.h @@ -8,7 +8,6 @@ #include "Image.h" #include "nsIStreamListener.h" -#include "nsIRequest.h" #include "nsWeakReference.h" #include "mozilla/TimeStamp.h" @@ -33,36 +32,25 @@ public: NS_DECL_NSISTREAMLISTENER NS_DECL_IMGICONTAINER - // (no public constructor - use ImageFactory) + VectorImage(imgStatusTracker* aStatusTracker = nullptr); virtual ~VectorImage(); // Methods inherited from Image nsresult Init(imgIDecoderObserver* aObserver, const char* aMimeType, + const char* aURIString, uint32_t aFlags); - virtual void GetCurrentFrameRect(nsIntRect& aRect) MOZ_OVERRIDE; + void GetCurrentFrameRect(nsIntRect& aRect); virtual size_t HeapSizeOfSourceWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const; virtual size_t HeapSizeOfDecodedWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const; virtual size_t NonHeapSizeOfDecoded() const; virtual size_t OutOfProcessSizeOfDecoded() const; - virtual nsresult OnImageDataAvailable(nsIRequest* aRequest, - nsISupports* aContext, - nsIInputStream* aInStr, - uint64_t aSourceOffset, - uint32_t aCount) MOZ_OVERRIDE; - virtual nsresult OnImageDataComplete(nsIRequest* aRequest, - nsISupports* aContext, - nsresult status) MOZ_OVERRIDE; - virtual nsresult OnNewSourceData() MOZ_OVERRIDE; - // Callback for SVGRootRenderingObserver void InvalidateObserver(); protected: - VectorImage(imgStatusTracker* aStatusTracker = nullptr, nsIURI* aURI = nullptr); - virtual nsresult StartAnimation(); virtual nsresult StopAnimation(); virtual bool ShouldAnimate(); @@ -84,8 +72,6 @@ private: // (Only set after mIsFullyLoaded.) bool mHaveRestrictedRegion:1; // Are we a restricted-region clone // created via ExtractFrame? - - friend class ImageFactory; }; inline NS_IMETHODIMP VectorImage::GetAnimationMode(uint16_t *aAnimationMode) { diff --git a/image/src/imgLoader.cpp b/image/src/imgLoader.cpp index aa6cbb0b715..c87934ef25c 100644 --- a/image/src/imgLoader.cpp +++ b/image/src/imgLoader.cpp @@ -703,8 +703,7 @@ nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup aRequest->GetURI(getter_AddRefs(uri)); // init adds itself to imgRequest's list of observers - nsresult rv = proxyRequest->Init(aRequest, &aRequest->GetStatusTracker(), - aLoadGroup, uri, aObserver); + nsresult rv = proxyRequest->Init(&aRequest->GetStatusTracker(), aLoadGroup, uri, aObserver); if (NS_FAILED(rv)) { NS_RELEASE(proxyRequest); return rv; diff --git a/image/src/imgRequest.cpp b/image/src/imgRequest.cpp index dda406eb9e9..2139683e343 100644 --- a/image/src/imgRequest.cpp +++ b/image/src/imgRequest.cpp @@ -17,7 +17,8 @@ #include "imgLoader.h" #include "imgRequestProxy.h" -#include "ImageFactory.h" +#include "RasterImage.h" +#include "VectorImage.h" #include "imgILoader.h" @@ -46,12 +47,29 @@ #include "nsNetUtil.h" #include "nsIProtocolHandler.h" +#include "mozilla/Preferences.h" +#include "mozilla/Likely.h" + #include "DiscardTracker.h" #include "nsAsyncRedirectVerifyHelper.h" +#define SVG_MIMETYPE "image/svg+xml" + using namespace mozilla; using namespace mozilla::image; +static bool gInitializedPrefCaches = false; +static bool gDecodeOnDraw = false; +static bool gDiscardable = false; + +static void +InitPrefCaches() +{ + Preferences::AddBoolVarCache(&gDiscardable, "image.mem.discardable"); + Preferences::AddBoolVarCache(&gDecodeOnDraw, "image.mem.decodeondraw"); + gInitializedPrefCaches = true; +} + #if defined(PR_LOGGING) PRLogModuleInfo * GetImgLog() @@ -71,7 +89,7 @@ NS_IMPL_ISUPPORTS5(imgRequest, imgRequest::imgRequest(imgLoader* aLoader) : mLoader(aLoader) - , mStatusTracker(new imgStatusTracker(nullptr)) + , mStatusTracker(new imgStatusTracker(nullptr, this)) , mValidator(nullptr) , mInnerWindowId(0) , mCORSMode(imgIRequest::CORS_NONE) @@ -80,10 +98,18 @@ imgRequest::imgRequest(imgLoader* aLoader) , mGotData(false) , mIsInCache(false) , mResniffMimeType(false) -{ } +{ + // Register our pref observers if we haven't yet. + if (MOZ_UNLIKELY(!gInitializedPrefCaches)) { + InitPrefCaches(); + } +} imgRequest::~imgRequest() { + // The status tracker can outlive this request, and needs to know it's dying. + GetStatusTracker().ClearRequest(); + if (mURI) { nsAutoCString spec; mURI->GetSpec(spec); @@ -527,10 +553,8 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt // Figure out if we're multipart nsCOMPtr mpchan(do_QueryInterface(aRequest)); - if (mpchan) { - mIsMultiPartChannel = true; - GetStatusTracker().SetIsMultipart(); - } + if (mpchan) + mIsMultiPartChannel = true; // If we're not multipart, we shouldn't have an image yet NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage, @@ -540,13 +564,14 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt // detect the mime type in OnDataAvailable. if (mIsMultiPartChannel && mImage) { mResniffMimeType = true; - - // Tell the image to reinitialize itself. We have to do this in - // OnStartRequest so that its state machine is always in a consistent - // state. - // Note that if our MIME type changes, mImage will be replaced with a - // new object. - mImage->OnNewSourceData(); + if (mImage->GetType() == imgIContainer::TYPE_RASTER) { + // Tell the RasterImage to reinitialize itself. We have to do this in + // OnStartRequest so that its state machine is always in a consistent + // state. + // Note that if our MIME type changes, mImage will be replaced with a + // new object. + static_cast(mImage.get())->NewSourceData(); + } } /* @@ -625,12 +650,21 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, // trigger a failure, since the image might be waiting for more non-optional // data and this is the point where we break the news that it's not coming. if (mImage) { - nsresult rv = mImage->OnImageDataComplete(aRequest, ctxt, status); + nsresult rv; + if (mImage->GetType() == imgIContainer::TYPE_RASTER) { + // Notify the image + rv = static_cast(mImage.get())->SourceDataComplete(); + } else { // imageType == imgIContainer::TYPE_VECTOR + nsCOMPtr imageAsStream = do_QueryInterface(mImage); + NS_ABORT_IF_FALSE(imageAsStream, + "SVG-typed Image failed QI to nsIStreamListener"); + rv = imageAsStream->OnStopRequest(aRequest, ctxt, status); + } - // If we got an error in the OnImageDataComplete() call, we don't want to - // proceed as if nothing bad happened. However, we also want to give - // precedence to failure status codes from necko, since presumably they're - // more meaningful. + // If we got an error in the SourceDataComplete() / OnStopRequest() call, + // we don't want to proceed as if nothing bad happened. However, we also + // want to give precedence to failure status codes from necko, since + // presumably they're more meaningful. if (NS_FAILED(rv) && NS_SUCCEEDED(status)) status = rv; } @@ -726,7 +760,7 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, // type and decoder. // We always reinitialize for SVGs, because they have no way of // reinitializing themselves. - if (mContentType != newType || newType.Equals(SVG_MIMETYPE)) { + if (mContentType != newType || newType.EqualsLiteral(SVG_MIMETYPE)) { mContentType = newType; // If we've resniffed our MIME type and it changed, we need to create a @@ -734,14 +768,23 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, // our own any more. if (mResniffMimeType) { NS_ABORT_IF_FALSE(mIsMultiPartChannel, "Resniffing a non-multipart image"); - - imgStatusTracker* freshTracker = new imgStatusTracker(nullptr); + imgStatusTracker* freshTracker = new imgStatusTracker(nullptr, this); freshTracker->AdoptConsumers(&GetStatusTracker()); mStatusTracker = freshTracker; - - mResniffMimeType = false; } + 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(); + /* set our mimetype as a property */ nsCOMPtr contentType(do_CreateInstance("@mozilla.org/supports-cstring;1")); if (contentType) { @@ -764,37 +807,121 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnDataAvailable", "content type", mContentType.get()); - // Now we can create a new image to hold the data. If we don't have a decoder + // + // Figure out our Image initialization flags + // + + // We default to the static globals + bool isDiscardable = gDiscardable; + bool doDecodeOnDraw = gDecodeOnDraw; + + // We want UI to be as snappy as possible and not to flicker. Disable discarding + // and decode-on-draw for chrome URLS + bool isChrome = false; + rv = mURI->SchemeIs("chrome", &isChrome); + if (NS_SUCCEEDED(rv) && isChrome) + isDiscardable = doDecodeOnDraw = false; + + // We don't want resources like the "loading" icon to be discardable or + // decode-on-draw either. + bool isResource = false; + rv = mURI->SchemeIs("resource", &isResource); + if (NS_SUCCEEDED(rv) && isResource) + isDiscardable = doDecodeOnDraw = false; + + // For multipart/x-mixed-replace, we basically want a direct channel to the + // decoder. Disable both for this case as well. + if (mIsMultiPartChannel) + isDiscardable = doDecodeOnDraw = false; + + // We have all the information we need + uint32_t imageFlags = Image::INIT_FLAG_NONE; + if (isDiscardable) + imageFlags |= Image::INIT_FLAG_DISCARDABLE; + if (doDecodeOnDraw) + imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW; + if (mIsMultiPartChannel) + imageFlags |= Image::INIT_FLAG_MULTIPART; + + // Get our URI string + nsAutoCString uriString; + rv = mURI->GetSpec(uriString); + if (NS_FAILED(rv)) + uriString.Assign(""); + + // 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. - mImage = ImageFactory::CreateImage(aRequest, mStatusTracker, mContentType, - mURI, mIsMultiPartChannel, mInnerWindowId); + rv = mImage->Init(GetStatusTracker().GetDecoderObserver(), + mContentType.get(), uriString.get(), imageFlags); - // Release our copy of the status tracker since the image owns it now. - mStatusTracker = nullptr; + // 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 - // Notify listeners that we have an image. - // XXX(seth): The name of this notification method is pretty misleading. - GetStatusTracker().OnDataAvailable(); - - if (mImage->HasError() && !mIsMultiPartChannel) { // Probably bad mimetype - // We allow multipart images to fail to initialize without cancelling the - // load because subsequent images might be fine; thus only single part - // images end up here. - this->Cancel(NS_ERROR_FAILURE); + this->Cancel(rv); return NS_BINDING_ABORTED; } - NS_ABORT_IF_FALSE(!!GetStatusTracker().GetImage(), "Status tracker should have an image!"); - NS_ABORT_IF_FALSE(mImage, "imgRequest should have an image!"); + if (mImage->GetType() == imgIContainer::TYPE_RASTER) { + /* Use content-length as a size hint for http channels. */ + nsCOMPtr httpChannel(do_QueryInterface(aRequest)); + if (httpChannel) { + nsAutoCString contentLength; + rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"), + contentLength); + if (NS_SUCCEEDED(rv)) { + int32_t len = contentLength.ToInteger(&rv); - if (mDecodeRequested) - mImage->StartDecoding(); + // Pass anything usable on so that the RasterImage can preallocate + // its source buffer + if (len > 0) { + uint32_t sizeHint = (uint32_t) len; + sizeHint = NS_MIN(sizeHint, 20000000); /* Bound by something reasonable */ + RasterImage* rasterImage = static_cast(mImage.get()); + rv = rasterImage->SetSourceSizeHint(sizeHint); + if (NS_FAILED(rv)) { + // Flush memory, try to get some back, and try again + rv = nsMemory::HeapMinimize(true); + nsresult rv2 = rasterImage->SetSourceSizeHint(sizeHint); + // If we've still failed at this point, things are going downhill + if (NS_FAILED(rv) || NS_FAILED(rv2)) { + NS_WARNING("About to hit OOM in imagelib!"); + } + } + } + } + } + } + + if (mImage->GetType() == imgIContainer::TYPE_RASTER) { + // If we were waiting on the image to do something, now's our chance. + if (mDecodeRequested) { + mImage->StartDecoding(); + } + } else { // mImage->GetType() == imgIContainer::TYPE_VECTOR + nsCOMPtr imageAsStream = do_QueryInterface(mImage); + NS_ABORT_IF_FALSE(imageAsStream, + "SVG-typed Image failed QI to nsIStreamListener"); + imageAsStream->OnStartRequest(aRequest, nullptr); + } } } - // Notify the image that it has new data. - rv = mImage->OnImageDataAvailable(aRequest, ctxt, inStr, sourceOffset, count); - + if (mImage->GetType() == imgIContainer::TYPE_RASTER) { + // WriteToRasterImage always consumes everything it gets + // if it doesn't run out of memory + uint32_t bytesRead; + rv = inStr->ReadSegments(RasterImage::WriteToRasterImage, + static_cast(mImage), + count, &bytesRead); + NS_ABORT_IF_FALSE(bytesRead == count || mImage->HasError(), + "WriteToRasterImage should consume everything or the image must be in error!"); + } else { // mImage->GetType() == imgIContainer::TYPE_VECTOR + nsCOMPtr imageAsStream = do_QueryInterface(mImage); + rv = imageAsStream->OnDataAvailable(aRequest, ctxt, inStr, + sourceOffset, count); + } if (NS_FAILED(rv)) { PR_LOG(GetImgLog(), PR_LOG_WARNING, ("[this=%p] imgRequest::OnDataAvailable -- " diff --git a/image/src/imgRequest.h b/image/src/imgRequest.h index d716fe04137..4172c2d1f7f 100644 --- a/image/src/imgRequest.h +++ b/image/src/imgRequest.h @@ -198,7 +198,7 @@ private: // The principal of this image. nsCOMPtr mPrincipal; // Status-tracker -- transferred to mImage, when it gets instantiated - nsRefPtr mStatusTracker; + nsAutoPtr mStatusTracker; nsRefPtr mImage; nsCOMPtr mProperties; nsCOMPtr mSecurityInfo; diff --git a/image/src/imgRequestProxy.cpp b/image/src/imgRequestProxy.cpp index 097c6fa9b90..0d99f461429 100644 --- a/image/src/imgRequestProxy.cpp +++ b/image/src/imgRequestProxy.cpp @@ -148,19 +148,17 @@ imgRequestProxy::~imgRequestProxy() } } -nsresult imgRequestProxy::Init(imgRequest* aOwner, - imgStatusTracker* aStatusTracker, +nsresult imgRequestProxy::Init(imgStatusTracker* aStatusTracker, nsILoadGroup* aLoadGroup, - nsIURI* aURI, - imgINotificationObserver* aObserver) + nsIURI* aURI, imgINotificationObserver* aObserver) { NS_PRECONDITION(!GetOwner() && !mListener, "imgRequestProxy is already initialized"); - LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request", aOwner); + LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request", aStatusTracker->GetRequest()); NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init"); - mBehaviour->SetOwner(aOwner); + mBehaviour->SetOwner(aStatusTracker->GetRequest()); 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 @@ -586,8 +584,7 @@ nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver, // XXXldb That's not true anymore. Stuff from imgLoader adds the // request to the loadgroup. clone->SetLoadFlags(mLoadFlags); - nsresult rv = clone->Init(mBehaviour->GetOwner(), &GetStatusTracker(), - mLoadGroup, mURI, aObserver); + nsresult rv = clone->Init(&GetStatusTracker(), mLoadGroup, mURI, aObserver); if (NS_FAILED(rv)) return rv; @@ -682,20 +679,6 @@ NS_IMETHODIMP imgRequestProxy::GetHasTransferredData(bool* hasData) /** imgIDecoderObserver methods **/ -void imgRequestProxy::OnStartDecode() -{ - // This notification is deliberately not propagated since there are no - // listeners who care about it. - if (GetOwner()) { - // 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. - GetOwner()->ResetCacheEntry(); - } -} - void imgRequestProxy::OnStartContainer() { LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStartContainer"); @@ -740,15 +723,9 @@ void imgRequestProxy::OnStopDecode() mListener->Notify(this, imgINotificationObserver::DECODE_COMPLETE, nullptr); } - if (GetOwner()) { - // We finished the decode, and thus have the decoded frames. Update the cache - // entry size to take this into account. - GetOwner()->UpdateCacheEntrySize(); - - // Multipart needs reset for next OnStartContainer. - if (GetOwner()->GetMultipart()) - mSentStartContainer = false; - } + // Multipart needs reset for next OnStartContainer + if (GetOwner() && GetOwner()->GetMultipart()) + mSentStartContainer = false; } void imgRequestProxy::OnDiscard() @@ -760,10 +737,6 @@ void imgRequestProxy::OnDiscard() nsCOMPtr kungFuDeathGrip(mListener); mListener->Notify(this, imgINotificationObserver::DISCARD, nullptr); } - if (GetOwner()) { - // Update the cache entry size, since we just got rid of frame data. - GetOwner()->UpdateCacheEntrySize(); - } } void imgRequestProxy::OnImageIsAnimated() @@ -913,7 +886,7 @@ imgRequestProxy::GetStaticRequest(imgRequestProxy** aReturn) nsCOMPtr currentPrincipal; GetImagePrincipal(getter_AddRefs(currentPrincipal)); nsRefPtr req = new imgRequestProxyStatic(frame, currentPrincipal); - req->Init(nullptr, &frame->GetStatusTracker(), nullptr, mURI, nullptr); + req->Init(&frame->GetStatusTracker(), nullptr, mURI, nullptr); NS_ADDREF(*aReturn = req); @@ -929,7 +902,7 @@ void imgRequestProxy::NotifyListener() if (GetOwner()) { // Send the notifications to our listener asynchronously. - GetStatusTracker().Notify(this); + GetStatusTracker().Notify(GetOwner(), 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. diff --git a/image/src/imgRequestProxy.h b/image/src/imgRequestProxy.h index e3aee6da45a..94897d8e3b5 100644 --- a/image/src/imgRequestProxy.h +++ b/image/src/imgRequestProxy.h @@ -58,11 +58,9 @@ public: // Callers to Init or ChangeOwner are required to call NotifyListener after // (although not immediately after) doing so. - nsresult Init(imgRequest* aOwner, - imgStatusTracker* aStatusTracker, + nsresult Init(imgStatusTracker* aStatusTracker, nsILoadGroup *aLoadGroup, - nsIURI* aURI, - imgINotificationObserver *aObserver); + nsIURI* aURI, imgINotificationObserver *aObserver); nsresult ChangeOwner(imgRequest *aNewOwner); // this will change mOwner. Do not call this if the previous // owner has already sent notifications out! @@ -138,7 +136,6 @@ protected: // notifications. /* non-virtual imgIDecoderObserver methods */ - void OnStartDecode (); void OnStartContainer (); void OnFrameUpdate (const nsIntRect * aRect); void OnStopFrame (); diff --git a/image/src/imgStatusTracker.cpp b/image/src/imgStatusTracker.cpp index da846fe5277..8b7cb3972c1 100644 --- a/image/src/imgStatusTracker.cpp +++ b/image/src/imgStatusTracker.cpp @@ -6,6 +6,7 @@ #include "imgStatusTracker.h" +#include "imgRequest.h" #include "imgIContainer.h" #include "imgRequestProxy.h" #include "Image.h" @@ -51,14 +52,7 @@ NS_IMETHODIMP imgStatusTrackerObserver::OnStartDecode() NS_ABORT_IF_FALSE(mTracker->GetImage(), "OnStartDecode callback before we've created our image"); - mTracker->RecordStartDecode(); - - nsTObserverArray::ForwardIterator iter(mTracker->mConsumers); - while (iter.HasMore()) { - mTracker->SendStartDecode(iter.GetNext()); - } - - if (mTracker->IsMultipart()) { + if (mTracker->GetRequest() && !mTracker->GetRequest()->GetMultipart()) { MOZ_ASSERT(!mTracker->mBlockingOnload); mTracker->mBlockingOnload = true; @@ -70,6 +64,15 @@ NS_IMETHODIMP imgStatusTrackerObserver::OnStartDecode() } } + /* 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. + */ + if (mTracker->GetRequest()) { + mTracker->GetRequest()->ResetCacheEntry(); + } + return NS_OK; } @@ -132,6 +135,20 @@ NS_IMETHODIMP imgStatusTrackerObserver::OnStopFrame() 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 os = mozilla::services::GetObserverService(); + if (os) { + nsCOMPtr 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) { @@ -139,6 +156,12 @@ NS_IMETHODIMP imgStatusTrackerObserver::OnStopDecode(nsresult aStatus) 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. + if (mTracker->GetRequest()) { + mTracker->GetRequest()->UpdateCacheEntrySize(); + } + bool preexistingError = mTracker->GetImageStatus() == imgIRequest::STATUS_ERROR; mTracker->RecordStopDecode(aStatus); @@ -152,8 +175,8 @@ NS_IMETHODIMP imgStatusTrackerObserver::OnStopDecode(nsresult aStatus) // block onload, but then hit an error before we get to our first frame. mTracker->MaybeUnblockOnload(); - if (NS_FAILED(aStatus) && !preexistingError) { - mTracker->FireFailureNotification(); + if (NS_FAILED(aStatus) && !preexistingError && mTracker->GetRequest()) { + FireFailureNotification(mTracker->GetRequest()); } return NS_OK; @@ -173,6 +196,11 @@ NS_IMETHODIMP imgStatusTrackerObserver::OnDiscard() mTracker->RecordDiscard(); + // Update the cache entry size, since we just got rid of frame data + if (mTracker->GetRequest()) { + mTracker->GetRequest()->UpdateCacheEntrySize(); + } + nsTObserverArray::ForwardIterator iter(mTracker->mConsumers); while (iter.HasMore()) { mTracker->SendDiscard(iter.GetNext()); @@ -197,21 +225,21 @@ NS_IMETHODIMP imgStatusTrackerObserver::OnImageIsAnimated() // imgStatusTracker methods -imgStatusTracker::imgStatusTracker(Image* aImage) +imgStatusTracker::imgStatusTracker(Image* aImage, imgRequest* aRequest) : mImage(aImage), - mTrackerObserver(new imgStatusTrackerObserver(this)), + mRequest(aRequest), mState(0), mImageStatus(imgIRequest::STATUS_NONE), - mIsMultipart(false), mHadLastPart(false), - mBlockingOnload(false) + mBlockingOnload(false), + mTrackerObserver(new imgStatusTrackerObserver(this)) {} imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther) : mImage(aOther.mImage), + mRequest(aOther.mRequest), mState(aOther.mState), mImageStatus(aOther.mImageStatus), - mIsMultipart(aOther.mIsMultipart), mHadLastPart(aOther.mHadLastPart), mBlockingOnload(aOther.mBlockingOnload) // Note: we explicitly don't copy mRequestRunnable, because it won't be @@ -246,20 +274,22 @@ imgStatusTracker::GetImageStatus() const class imgRequestNotifyRunnable : public nsRunnable { public: - imgRequestNotifyRunnable(imgStatusTracker* aTracker, imgRequestProxy* aRequestProxy) - : mTracker(aTracker) + imgRequestNotifyRunnable(imgRequest* request, imgRequestProxy* requestproxy) + : mRequest(request) { - mProxies.AppendElement(aRequestProxy); + mProxies.AppendElement(requestproxy); } NS_IMETHOD Run() { + imgStatusTracker& statusTracker = mRequest->GetStatusTracker(); + for (uint32_t i = 0; i < mProxies.Length(); ++i) { mProxies[i]->SetNotificationsDeferred(false); - mTracker->SyncNotify(mProxies[i]); + statusTracker.SyncNotify(mProxies[i]); } - mTracker->mRequestRunnable = nullptr; + statusTracker.mRequestRunnable = nullptr; return NS_OK; } @@ -271,22 +301,19 @@ class imgRequestNotifyRunnable : public nsRunnable private: friend class imgStatusTracker; - nsRefPtr mTracker; - nsTArray< nsRefPtr > mProxies; + nsRefPtr mRequest; + nsTArray > mProxies; }; void -imgStatusTracker::Notify(imgRequestProxy* proxy) +imgStatusTracker::Notify(imgRequest* request, imgRequestProxy* proxy) { #ifdef PR_LOGGING - if (GetImage() && GetImage()->GetURI()) { - nsCOMPtr uri(GetImage()->GetURI()); - nsAutoCString spec; - uri->GetSpec(spec); - LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", spec.get()); - } else { - LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", ""); - } + nsCOMPtr uri; + request->GetURI(getter_AddRefs(uri)); + nsAutoCString spec; + uri->GetSpec(spec); + LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", spec.get()); #endif proxy->SetNotificationsDeferred(true); @@ -295,10 +322,13 @@ imgStatusTracker::Notify(imgRequestProxy* proxy) // to its list of proxies to be notified. This ensures we don't unnecessarily // delay onload. imgRequestNotifyRunnable* runnable = static_cast(mRequestRunnable.get()); - if (runnable) { + if (runnable && runnable->mRequest == request) { runnable->AddProxy(proxy); } else { - mRequestRunnable = new imgRequestNotifyRunnable(this, proxy); + // It's okay to overwrite an existing mRequestRunnable, because adding a + // new proxy is strictly a performance optimization. The notification will + // always happen, regardless of whether we hold a reference to a runnable. + mRequestRunnable = new imgRequestNotifyRunnable(request, proxy); NS_DispatchToCurrentThread(mRequestRunnable); } } @@ -368,29 +398,26 @@ imgStatusTracker::SyncNotify(imgRequestProxy* proxy) if (mState & stateHasSize) proxy->OnStartContainer(); - // OnStartDecode - if (mState & stateDecodeStarted) - proxy->OnStartDecode(); - // BlockOnload if (mState & stateBlockingOnload) proxy->BlockOnload(); if (mImage) { - // 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); + int16_t imageType = mImage->GetType(); + // Send frame messages (OnDataAvailable, OnStopFrame) + if (imageType == imgIContainer::TYPE_VECTOR || + static_cast(mImage)->GetNumFrames() > 0) { - // If there's any content in this frame at all (always true for - // vector images, true for raster images that have decoded at - // least one frame) then send OnFrameUpdate. - if (!r.IsEmpty()) + // 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); - if (mState & stateFrameStopped) - proxy->OnStopFrame(); + if (mState & stateFrameStopped) + proxy->OnStopFrame(); + } // OnImageIsAnimated bool isAnimated = false; @@ -472,24 +499,10 @@ void imgStatusTracker::RecordDecoded() { NS_ABORT_IF_FALSE(mImage, "RecordDecoded called before we have an Image"); - mState |= stateDecodeStarted | stateDecodeStopped | stateFrameStopped; + mState |= stateDecodeStopped | stateFrameStopped; mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE; } -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) { @@ -631,7 +644,6 @@ imgStatusTracker::RecordStartRequest() mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE; mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE; mState &= ~stateRequestStarted; - mState &= ~stateDecodeStarted; mState &= ~stateDecodeStopped; mState &= ~stateRequestStopped; mState &= ~stateBlockingOnload; @@ -693,8 +705,8 @@ imgStatusTracker::OnStopRequest(bool aLastPart, SendStopRequest(srIter.GetNext(), aLastPart, aStatus); } - if (NS_FAILED(aStatus) && !preexistingError) { - FireFailureNotification(); + if (NS_FAILED(aStatus) && !preexistingError && GetRequest()) { + FireFailureNotification(GetRequest()); } } @@ -756,15 +768,7 @@ imgStatusTracker::MaybeUnblockOnload() } void -imgStatusTracker::FireFailureNotification() +imgStatusTracker::ClearRequest() { - // Some kind of problem has happened with image decoding. - // Report the URI to net:failed-to-process-uri-conent observers. - nsIURI* uri = GetImage()->GetURI(); - if (uri) { - nsCOMPtr os = mozilla::services::GetObserverService(); - if (os) { - os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr); - } - } + mRequest = nullptr; } diff --git a/image/src/imgStatusTracker.h b/image/src/imgStatusTracker.h index 0b2c2974db1..360d9d69f5f 100644 --- a/image/src/imgStatusTracker.h +++ b/image/src/imgStatusTracker.h @@ -8,6 +8,7 @@ #define imgStatusTracker_h__ class imgIContainer; +class imgRequest; class imgRequestProxy; class imgStatusNotifyRunnable; class imgRequestNotifyRunnable; @@ -20,7 +21,6 @@ class Image; } // namespace mozilla -#include "mozilla/RefPtr.h" #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "nsTObserverArray.h" @@ -32,7 +32,6 @@ class Image; enum { stateRequestStarted = 1u << 0, stateHasSize = 1u << 1, - stateDecodeStarted = 1u << 2, stateDecodeStopped = 1u << 3, stateFrameStopped = 1u << 4, stateRequestStopped = 1u << 5, @@ -71,13 +70,13 @@ private: * and the notifications will be replayed to the proxy asynchronously. */ -class imgStatusTracker : public mozilla::RefCounted +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); + imgStatusTracker(mozilla::image::Image* aImage, imgRequest* aRequest); imgStatusTracker(const imgStatusTracker& aOther); // Image-setter, for imgStatusTrackers created by imgRequest::Init, which @@ -86,15 +85,12 @@ public: // without an image. void SetImage(mozilla::image::Image* aImage); - // Inform this status tracker that it is associated with a multipart image. - void SetIsMultipart() { mIsMultipart = true; } - // Schedule an asynchronous "replaying" of all the notifications that would // have to happen to put us in the current state. // We will also take note of any notifications that happen between the time // Notify() is called and when we call SyncNotify on |proxy|, and replay them // as well. - void Notify(imgRequestProxy* proxy); + void Notify(imgRequest* request, imgRequestProxy* proxy); // Schedule an asynchronous "replaying" of all the notifications that would // have to happen to put us in the state we are in right now. @@ -152,8 +148,6 @@ public: void RecordDecoded(); /* non-virtual imgIDecoderObserver methods */ - void RecordStartDecode(); - void SendStartDecode(imgRequestProxy* aProxy); void RecordStartContainer(imgIContainer* aContainer); void SendStartContainer(imgRequestProxy* aProxy); void RecordDataAvailable(); @@ -192,10 +186,12 @@ public: void MaybeUnblockOnload(); - bool IsMultipart() const { return mIsMultipart; } + // Null out any reference to an associated image request + void ClearRequest(); // 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(); } @@ -204,24 +200,22 @@ private: friend class imgRequestNotifyRunnable; friend class imgStatusTrackerObserver; - void FireFailureNotification(); - nsCOMPtr mRequestRunnable; - // Weak pointer to the image. The image owns the status tracker. + // 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. 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 mConsumers; nsRefPtr mTrackerObserver; - - uint32_t mState; - uint32_t mImageStatus; - bool mIsMultipart : 1; - bool mHadLastPart : 1; - bool mBlockingOnload : 1; }; #endif diff --git a/image/src/imgTools.cpp b/image/src/imgTools.cpp index 75052217123..4a2d289f658 100644 --- a/image/src/imgTools.cpp +++ b/image/src/imgTools.cpp @@ -22,7 +22,7 @@ #include "nsStreamUtils.h" #include "nsNetUtil.h" #include "nsContentUtils.h" -#include "ImageFactory.h" +#include "RasterImage.h" #include "ScriptedNotificationObserver.h" #include "imgIScriptedNotificationObserver.h" @@ -47,33 +47,32 @@ imgTools::~imgTools() /* destructor code */ } + NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr, const nsACString& aMimeType, imgIContainer **aContainer) -{ - NS_ABORT_IF_FALSE(*aContainer == nullptr, - "Cannot provide an existing image container to DecodeImageData"); - - return DecodeImage(aInStr, aMimeType, aContainer); -} - -NS_IMETHODIMP imgTools::DecodeImage(nsIInputStream* aInStr, - const nsACString& aMimeType, - imgIContainer **aContainer) { nsresult rv; - nsRefPtr image; + RasterImage* image; // convenience alias for *aContainer NS_ENSURE_ARG_POINTER(aInStr); - // Create a new image container to hold the decoded data. - nsAutoCString mimeType(aMimeType); - image = ImageFactory::CreateAnonymousImage(mimeType); + // If the caller didn't provide an imgIContainer, create one. + if (*aContainer) { + NS_ABORT_IF_FALSE((*aContainer)->GetType() == imgIContainer::TYPE_RASTER, + "wrong type of imgIContainer for decoding into"); + image = static_cast(*aContainer); + } else { + *aContainer = image = new RasterImage(); + NS_ADDREF(image); + } - if (image->HasError()) - return NS_ERROR_FAILURE; + // Initialize the Image. If we're using the one from the caller, we + // require that it not be initialized. + nsCString mimeType(aMimeType); + rv = image->Init(nullptr, mimeType.get(), "", Image::INIT_FLAG_NONE); + NS_ENSURE_SUCCESS(rv, rv); - // Prepare the input stream. nsCOMPtr inStream = aInStr; if (!NS_InputStreamIsBuffered(aInStr)) { nsCOMPtr bufStream; @@ -82,21 +81,27 @@ NS_IMETHODIMP imgTools::DecodeImage(nsIInputStream* aInStr, inStream = bufStream; } - // Figure out how much data we've been passed. + // Figure out how much data we've been passed uint64_t length; rv = inStream->Available(&length); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); - // Send the source data to the Image. - rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0, uint32_t(length)); + // Send the source data to the Image. WriteToRasterImage always + // consumes everything it gets if it doesn't run out of memory. + uint32_t bytesRead; + rv = inStream->ReadSegments(RasterImage::WriteToRasterImage, + static_cast(image), + (uint32_t)length, &bytesRead); NS_ENSURE_SUCCESS(rv, rv); - // Let the Image know we've sent all the data. - rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK); + NS_ABORT_IF_FALSE(bytesRead == length || image->HasError(), + "WriteToRasterImage should consume everything or the image must be in error!"); + + // Let the Image know we've sent all the data + rv = image->SourceDataComplete(); NS_ENSURE_SUCCESS(rv, rv); - // All done. - NS_ADDREF(*aContainer = image.get()); + // All done return NS_OK; } diff --git a/image/src/imgTools.h b/image/src/imgTools.h index be39ebc0ae4..02ccbab56c8 100644 --- a/image/src/imgTools.h +++ b/image/src/imgTools.h @@ -8,11 +8,11 @@ #include "gfxContext.h" #define NS_IMGTOOLS_CID \ -{ /* 4c2383a4-931c-484d-8c4a-973590f66e3f */ \ - 0x4c2383a4, \ - 0x931c, \ - 0x484d, \ - {0x8c, 0x4a, 0x97, 0x35, 0x90, 0xf6, 0x6e, 0x3f} \ +{ /* fd9a9e8a-a77b-496a-b7bb-263df9715149 */ \ + 0xfd9a9e8a, \ + 0xa77b, \ + 0x496a, \ + {0xb7, 0xbb, 0x26, 0x3d, 0xf9, 0x71, 0x51, 0x49} \ } class imgTools : public imgITools diff --git a/image/test/unit/test_imgtools.js b/image/test/unit/test_imgtools.js index ee4e05e486d..adee0520d55 100644 --- a/image/test/unit/test_imgtools.js +++ b/image/test/unit/test_imgtools.js @@ -149,8 +149,6 @@ var imgFile = do_get_file(imgName); var istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 8415); -// Use decodeImageData for this test even though it's deprecated to ensure that -// it correctly forwards to decodeImage and continues to work. var outParam = { value: null }; imgTools.decodeImageData(istream, inMimeType, outParam); var container = outParam.value; @@ -211,7 +209,9 @@ imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 3494); -container = imgTools.decodeImage(istream, inMimeType); +outParam = {}; +imgTools.decodeImageData(istream, inMimeType, outParam); +container = outParam.value; // It's not easy to look at the pixel values from JS, so just // check the container's size. @@ -273,7 +273,9 @@ imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 1406); -container = imgTools.decodeImage(istream, inMimeType); +outParam = { value: null }; +imgTools.decodeImageData(istream, inMimeType, outParam); +container = outParam.value; // It's not easy to look at the pixel values from JS, so just // check the container's size. @@ -331,7 +333,9 @@ imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 1809); -container = imgTools.decodeImage(istream, inMimeType); +outParam = { value: null }; +imgTools.decodeImageData(istream, inMimeType, outParam); +container = outParam.value; // It's not easy to look at the pixel values from JS, so just // check the container's size. @@ -439,7 +443,9 @@ imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 3494); -container = imgTools.decodeImage(istream, inMimeType); +outParam = {}; +imgTools.decodeImageData(istream, inMimeType, outParam); +container = outParam.value; // It's not easy to look at the pixel values from JS, so just // check the container's size. @@ -657,7 +663,9 @@ for(var i=0; i