author | Steve Workman <sworkman@mozilla.com> |
Sat, 28 Sep 2013 11:28:42 -0700 | |
changeset 149158 | 799dffa9306bf8c3311b0f827a09e78eaa93e56f |
parent 149157 | 736a590cb652fcc98eaeedb494fa419280d515c8 |
child 149159 | 68378e8901f30478d03d6e093a95587706bee0f0 |
push id | 25374 |
push user | cbook@mozilla.com |
push date | Sun, 29 Sep 2013 09:37:16 +0000 |
treeherder | mozilla-central@8f805d3ef377 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | seth |
bugs | 867755 |
milestone | 27.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/image/build/nsImageModule.cpp +++ b/image/build/nsImageModule.cpp @@ -2,16 +2,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/. */ #include "mozilla/ModuleUtils.h" #include "nsMimeTypes.h" +#include "ImageFactory.h" #include "RasterImage.h" #include "imgLoader.h" #include "imgRequest.h" #include "imgRequestProxy.h" #include "imgTools.h" #include "DiscardTracker.h" @@ -76,16 +77,17 @@ static const mozilla::Module::CategoryEn { "content-sniffing-services", "@mozilla.org/image/loader;1", "@mozilla.org/image/loader;1" }, { nullptr } }; static nsresult imglib_Initialize() { mozilla::image::DiscardTracker::Initialize(); + mozilla::image::ImageFactory::Initialize(); mozilla::image::RasterImage::Initialize(); imgLoader::GlobalInit(); return NS_OK; } static void imglib_Shutdown() {
--- a/image/src/Image.cpp +++ b/image/src/Image.cpp @@ -5,25 +5,20 @@ #include "nsMimeTypes.h" #include "Image.h" namespace mozilla { namespace image { -#ifdef DEBUG_imagelib -static const bool allowOMTURIAccess = true; -#else -static const bool allowOMTURIAccess = false; -#endif - // Constructor -ImageResource::ImageResource(imgStatusTracker* aStatusTracker, nsIURI* aURI) : - mURI(new nsMainThreadPtrHolder<nsIURI>(aURI, allowOMTURIAccess)), +ImageResource::ImageResource(imgStatusTracker* aStatusTracker, + ImageURL* aURI) : + mURI(aURI), mInnerWindowId(0), mAnimationConsumers(0), mAnimationMode(kNormalAnimMode), mInitialized(false), mAnimating(false), mError(false) { if (aStatusTracker) {
--- a/image/src/Image.h +++ b/image/src/Image.h @@ -4,18 +4,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef MOZILLA_IMAGELIB_IMAGE_H_ #define MOZILLA_IMAGELIB_IMAGE_H_ #include "mozilla/MemoryReporting.h" #include "imgIContainer.h" #include "imgStatusTracker.h" -#include "nsIURI.h" -#include "nsProxyRelease.h" // for nsMainThreadPtrHolder and nsMainThreadPtrHandle +#include "ImageURL.h" class nsIRequest; class nsIInputStream; namespace mozilla { namespace image { class Image : public imgIContainer @@ -127,17 +126,17 @@ public: virtual nsresult OnNewSourceData() = 0; virtual void SetInnerWindowID(uint64_t aInnerWindowId) = 0; virtual uint64_t InnerWindowID() const = 0; virtual bool HasError() = 0; virtual void SetHasError() = 0; - virtual nsIURI* GetURI() = 0; + virtual ImageURL* GetURI() = 0; }; class ImageResource : public Image { public: virtual imgStatusTracker& GetStatusTracker() MOZ_OVERRIDE { return *mStatusTracker; } virtual uint32_t SizeOfData() MOZ_OVERRIDE; @@ -154,20 +153,21 @@ public: virtual bool HasError() MOZ_OVERRIDE { return mError; } virtual void SetHasError() MOZ_OVERRIDE { mError = true; } /* * Returns a non-AddRefed pointer to the URI associated with this image. * Illegal to use off-main-thread. */ - virtual nsIURI* GetURI() MOZ_OVERRIDE { return mURI.get(); } + virtual ImageURL* GetURI() MOZ_OVERRIDE { return mURI.get(); } protected: - ImageResource(imgStatusTracker* aStatusTracker, nsIURI* aURI); + ImageResource(imgStatusTracker* aStatusTracker, + ImageURL* aURI); // Shared functionality for implementors of imgIContainer. Every // implementation of attribute animationMode should forward here. nsresult GetAnimationModeInternal(uint16_t *aAnimationMode); nsresult SetAnimationModeInternal(uint16_t aAnimationMode); /** * Decides whether animation should or should not be happening, @@ -183,17 +183,17 @@ protected: return mAnimationConsumers > 0 && mAnimationMode != kDontAnimMode; } virtual nsresult StartAnimation() = 0; virtual nsresult StopAnimation() = 0; // Member data shared by all implementations of this abstract class nsRefPtr<imgStatusTracker> mStatusTracker; - nsMainThreadPtrHandle<nsIURI> mURI; + nsRefPtr<ImageURL> mURI; uint64_t mInnerWindowId; uint32_t mAnimationConsumers; uint16_t mAnimationMode; // Enum values in imgIContainer bool mInitialized:1; // Have we been initalized? bool mAnimating:1; // Are we currently animating? bool mError:1; // Error handling };
--- a/image/src/ImageFactory.cpp +++ b/image/src/ImageFactory.cpp @@ -8,17 +8,16 @@ #include "mozilla/Preferences.h" #include "mozilla/Likely.h" #include "nsIHttpChannel.h" #include "nsIFileChannel.h" #include "nsIFile.h" #include "nsMimeTypes.h" -#include "nsIURI.h" #include "nsIRequest.h" #include "RasterImage.h" #include "VectorImage.h" #include "Image.h" #include "nsMediaFragmentURIParser.h" #include "ImageFactory.h" @@ -26,26 +25,29 @@ namespace mozilla { namespace image { // Global preferences related to image containers. static bool gInitializedPrefCaches = false; static bool gDecodeOnDraw = false; static bool gDiscardable = false; -static void -InitPrefCaches() +/*static*/ void +ImageFactory::Initialize() { - Preferences::AddBoolVarCache(&gDiscardable, "image.mem.discardable"); - Preferences::AddBoolVarCache(&gDecodeOnDraw, "image.mem.decodeondraw"); - gInitializedPrefCaches = true; + MOZ_ASSERT(NS_IsMainThread()); + if (!gInitializedPrefCaches) { + Preferences::AddBoolVarCache(&gDiscardable, "image.mem.discardable"); + Preferences::AddBoolVarCache(&gDecodeOnDraw, "image.mem.decodeondraw"); + gInitializedPrefCaches = true; + } } static uint32_t -ComputeImageFlags(nsIURI* uri, bool isMultiPart) +ComputeImageFlags(ImageURL* 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 @@ -78,23 +80,22 @@ ComputeImageFlags(nsIURI* uri, bool isMu return imageFlags; } /* static */ already_AddRefed<Image> ImageFactory::CreateImage(nsIRequest* aRequest, imgStatusTracker* aStatusTracker, const nsCString& aMimeType, - nsIURI* aURI, + ImageURL* aURI, bool aIsMultiPart, uint32_t aInnerWindowId) { - // Register our pref observers if we haven't yet. - if (MOZ_UNLIKELY(!gInitializedPrefCaches)) - InitPrefCaches(); + MOZ_ASSERT(gInitializedPrefCaches, + "Pref observers should have been initialized already"); // 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.EqualsLiteral(IMAGE_SVG_XML)) { return CreateVectorImage(aRequest, aStatusTracker, aMimeType, aURI, imageFlags, aInnerWindowId); @@ -170,17 +171,17 @@ GetContentSize(nsIRequest* aRequest) // Fallback - neither http nor file. We'll use dynamic allocation. return 0; } /* static */ already_AddRefed<Image> ImageFactory::CreateRasterImage(nsIRequest* aRequest, imgStatusTracker* aStatusTracker, const nsCString& aMimeType, - nsIURI* aURI, + ImageURL* aURI, uint32_t aImageFlags, uint32_t aInnerWindowId) { nsresult rv; nsRefPtr<RasterImage> newImage = new RasterImage(aStatusTracker, aURI); rv = newImage->Init(aMimeType.get(), aImageFlags); @@ -201,29 +202,31 @@ ImageFactory::CreateRasterImage(nsIReque 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!"); } } } - mozilla::net::nsMediaFragmentURIParser parser(aURI); + nsAutoCString ref; + aURI->GetRef(ref); + mozilla::net::nsMediaFragmentURIParser parser(ref); if (parser.HasResolution()) { newImage->SetRequestedResolution(parser.GetResolution()); } return newImage.forget(); } /* static */ already_AddRefed<Image> ImageFactory::CreateVectorImage(nsIRequest* aRequest, imgStatusTracker* aStatusTracker, const nsCString& aMimeType, - nsIURI* aURI, + ImageURL* aURI, uint32_t aImageFlags, uint32_t aInnerWindowId) { nsresult rv; nsRefPtr<VectorImage> newImage = new VectorImage(aStatusTracker, aURI); rv = newImage->Init(aMimeType.get(), aImageFlags);
--- a/image/src/ImageFactory.h +++ b/image/src/ImageFactory.h @@ -3,67 +3,74 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef MOZILLA_IMAGELIB_IMAGEFACTORY_H_ #define MOZILLA_IMAGELIB_IMAGEFACTORY_H_ #include "nsCOMPtr.h" +#include "nsProxyRelease.h" class nsCString; class nsIRequest; -class nsIURI; class imgStatusTracker; namespace mozilla { namespace image { class Image; +class ImageURL; class ImageFactory { public: /** + * Registers vars with Preferences. Should only be called on the main thread. + */ + static void Initialize(); + + /** * Creates a new image with the given properties. + * Can be called on or off the main thread. * * @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<Image> CreateImage(nsIRequest* aRequest, imgStatusTracker* aStatusTracker, const nsCString& aMimeType, - nsIURI* aURI, + ImageURL* 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<Image> CreateAnonymousImage(const nsCString& aMimeType); private: // Factory functions that create specific types of image containers. static already_AddRefed<Image> CreateRasterImage(nsIRequest* aRequest, imgStatusTracker* aStatusTracker, const nsCString& aMimeType, - nsIURI* aURI, + ImageURL* aURI, uint32_t aImageFlags, uint32_t aInnerWindowId); static already_AddRefed<Image> CreateVectorImage(nsIRequest* aRequest, imgStatusTracker* aStatusTracker, const nsCString& aMimeType, - nsIURI* aURI, + ImageURL* aURI, uint32_t aImageFlags, uint32_t aInnerWindowId); // This is a static factory class, so disallow instantiation. virtual ~ImageFactory() = 0; }; } // namespace image
new file mode 100644 --- /dev/null +++ b/image/src/ImageURL.h @@ -0,0 +1,86 @@ +/* -*- 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/. */ + +#ifndef MOZILLA_IMAGELIB_IMAGEURL_H_ +#define MOZILLA_IMAGELIB_IMAGEURL_H_ + +#include "nsIURI.h" +#include "MainThreadUtils.h" +#include "nsNetUtil.h" + +namespace mozilla { +namespace image { + +/** ImageURL + * + * nsStandardURL is not threadsafe, so this class is created to hold only the + * necessary URL data required for image loading and decoding. + * + * Note: Although several APIs have the same or similar prototypes as those + * found in nsIURI/nsStandardURL, the class does not implement nsIURI. This is + * intentional; functionality is limited, and is only useful for imagelib code. + * By not implementing nsIURI, external code cannot unintentionally be given an + * nsIURI pointer with this limited class behind it; instead, conversion to a + * fully implemented nsIURI is required (e.g. through NS_NewURI). + */ +class ImageURL +{ +public: + explicit ImageURL(nsIURI* aURI) { + MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!"); + aURI->GetSpec(mSpec); + aURI->GetScheme(mScheme); + aURI->GetRef(mRef); + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::image::ImageURL) + + nsresult GetSpec(nsACString &result) { + result = mSpec; + return NS_OK; + } + + nsresult GetScheme(nsACString &result) { + result = mScheme; + return NS_OK; + } + + nsresult SchemeIs(const char *scheme, bool *result) + { + NS_PRECONDITION(scheme, "scheme is null"); + NS_PRECONDITION(result, "result is null"); + + *result = mScheme.Equals(scheme); + return NS_OK; + } + + nsresult GetRef(nsACString &result) { + result = mRef; + return NS_OK; + } + + already_AddRefed<nsIURI> ToIURI() { + MOZ_ASSERT(NS_IsMainThread(), + "Convert to nsIURI on main thread only; it is not threadsafe."); + nsCOMPtr<nsIURI> newURI; + NS_NewURI(getter_AddRefs(newURI), mSpec); + return newURI.forget(); + } + +private: + // Since this is a basic storage class, no duplication of spec parsing is + // included in the functionality. Instead, the class depends upon the + // parsing implementation in the nsIURI class used in object construction. + // This means each field is stored separately, but since only a few are + // required, this small memory tradeoff for threadsafe usage should be ok. + nsAutoCString mSpec; + nsAutoCString mScheme; + nsAutoCString mRef; +}; + +} // namespace image +} // namespace mozilla + +#endif // MOZILLA_IMAGELIB_IMAGEURL_H_
--- a/image/src/ImageWrapper.cpp +++ b/image/src/ImageWrapper.cpp @@ -129,17 +129,17 @@ ImageWrapper::HasError() } void ImageWrapper::SetHasError() { mInnerImage->SetHasError(); } -nsIURI* +ImageURL* ImageWrapper::GetURI() { return mInnerImage->GetURI(); } // Methods inherited from XPCOM interfaces. NS_IMPL_ISUPPORTS1(ImageWrapper, imgIContainer)
--- a/image/src/ImageWrapper.h +++ b/image/src/ImageWrapper.h @@ -53,17 +53,17 @@ public: virtual nsresult OnNewSourceData() MOZ_OVERRIDE; virtual void SetInnerWindowID(uint64_t aInnerWindowId) MOZ_OVERRIDE; virtual uint64_t InnerWindowID() const MOZ_OVERRIDE; virtual bool HasError() MOZ_OVERRIDE; virtual void SetHasError() MOZ_OVERRIDE; - virtual nsIURI* GetURI() MOZ_OVERRIDE; + virtual ImageURL* GetURI() MOZ_OVERRIDE; protected: ImageWrapper(Image* aInnerImage) : mInnerImage(aInnerImage) { NS_ABORT_IF_FALSE(aInnerImage, "Cannot wrap a null image"); }
--- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -370,17 +370,17 @@ static nsCOMPtr<nsIThread> sScaleWorkerT NS_IMPL_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties) #else NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties, imgIContainerDebug) #endif //****************************************************************************** RasterImage::RasterImage(imgStatusTracker* aStatusTracker, - nsIURI* aURI /* = nullptr */) : + ImageURL* aURI /* = nullptr */) : ImageResource(aStatusTracker, aURI), // invoke superclass's constructor mSize(0,0), mFrameDecodeFlags(DECODE_FLAGS_DEFAULT), mMultipartDecodedFrame(nullptr), mAnim(nullptr), mLockCount(0), mDecodeCount(0), #ifdef DEBUG @@ -2183,16 +2183,20 @@ RasterImage::RequestDecode() { return RequestDecodeCore(SYNCHRONOUS_NOTIFY); } /* void startDecode() */ NS_IMETHODIMP RasterImage::StartDecoding() { + if (!NS_IsMainThread()) { + return NS_DispatchToMainThread( + NS_NewRunnableMethod(this, &RasterImage::StartDecoding)); + } // Here we are explicitly trading off flashing for responsiveness in the case // that we're redecoding an image (see bug 845147). return RequestDecodeCore(mHasBeenDecoded ? SYNCHRONOUS_NOTIFY : SYNCHRONOUS_NOTIFY_AND_SOME_DECODE); } bool RasterImage::IsDecoded() @@ -3035,16 +3039,23 @@ RasterImage::DecodePool::Singleton() MOZ_ASSERT(NS_IsMainThread()); sSingleton = new DecodePool(); ClearOnShutdown(&sSingleton); } return sSingleton; } +already_AddRefed<nsIEventTarget> +RasterImage::DecodePool::GetEventTarget() +{ + nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool); + return target.forget(); +} + RasterImage::DecodePool::DecodePool() : mThreadPoolMutex("Thread Pool") { if (gMultithreadedDecoding) { mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID); if (mThreadPool) { mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder")); uint32_t limit;
--- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -241,16 +241,20 @@ public: uint64_t aSourceOffset, uint32_t aCount) MOZ_OVERRIDE; virtual nsresult OnImageDataComplete(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus, bool aLastPart) MOZ_OVERRIDE; virtual nsresult OnNewSourceData() MOZ_OVERRIDE; + static already_AddRefed<nsIEventTarget> GetEventTarget() { + return DecodePool::Singleton()->GetEventTarget(); + } + /** * A hint of the number of bytes of source data that the image contains. If * called early on, this can help reduce copying and reallocations by * appropriately preallocating the source data buffer. * * We take this approach rather than having the source data management code do * something more complicated (like chunklisting) because HTTP is by far the * dominant source of images, and the Content-Length header is quite reliable. @@ -409,16 +413,24 @@ private: * bytes or we get the image's size. Note that this done on a best-effort * basis; if the size is burried too deep in the image, we'll give up. * * @return NS_ERROR if an error is encountered, and NS_OK otherwise. (Note * that we return NS_OK even when the size was not found.) */ nsresult DecodeUntilSizeAvailable(RasterImage* aImg); + /** + * Returns an event target interface to the thread pool; primarily for + * OnDataAvailable delivery off main thread. + * + * @return An nsIEventTarget interface to mThreadPool. + */ + already_AddRefed<nsIEventTarget> GetEventTarget(); + virtual ~DecodePool(); private: /* statics */ static StaticRefPtr<DecodePool> sSingleton; private: /* methods */ DecodePool(); @@ -727,17 +739,18 @@ private: // data // Helpers bool CanDiscard(); bool CanForciblyDiscard(); bool DiscardingActive(); bool StoringSourceData() const; protected: - RasterImage(imgStatusTracker* aStatusTracker = nullptr, nsIURI* aURI = nullptr); + RasterImage(imgStatusTracker* aStatusTracker = nullptr, + ImageURL* aURI = nullptr); bool ShouldAnimate(); friend class ImageFactory; }; inline NS_IMETHODIMP RasterImage::GetAnimationMode(uint16_t *aAnimationMode) { return GetAnimationModeInternal(aAnimationMode);
--- a/image/src/VectorImage.cpp +++ b/image/src/VectorImage.cpp @@ -297,17 +297,17 @@ NS_IMPL_ISUPPORTS3(VectorImage, imgIContainer, nsIStreamListener, nsIRequestObserver) //------------------------------------------------------------------------------ // Constructor / Destructor VectorImage::VectorImage(imgStatusTracker* aStatusTracker, - nsIURI* aURI /* = nullptr */) : + ImageURL* aURI /* = nullptr */) : ImageResource(aStatusTracker, aURI), // invoke superclass's constructor mIsInitialized(false), mIsFullyLoaded(false), mIsDrawing(false), mHaveAnimations(false), mHasPendingInvalidation(false) { }
--- a/image/src/VectorImage.h +++ b/image/src/VectorImage.h @@ -70,17 +70,18 @@ public: // Callback for SVGParseCompleteListener. void OnSVGDocumentParsed(); // Callbacks for SVGLoadEventListener. void OnSVGDocumentLoaded(); void OnSVGDocumentError(); protected: - VectorImage(imgStatusTracker* aStatusTracker = nullptr, nsIURI* aURI = nullptr); + VectorImage(imgStatusTracker* aStatusTracker = nullptr, + ImageURL* aURI = nullptr); virtual nsresult StartAnimation(); virtual nsresult StopAnimation(); virtual bool ShouldAnimate(); private: void CancelAllListeners();
--- a/image/src/imgLoader.cpp +++ b/image/src/imgLoader.cpp @@ -4,16 +4,17 @@ * 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/Attributes.h" #include "mozilla/Preferences.h" #include "mozilla/ClearOnShutdown.h" #include "ImageLogging.h" +#include "nsPrintfCString.h" #include "imgLoader.h" #include "imgRequestProxy.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsCrossSiteListenerProxy.h" #include "nsNetUtil.h" @@ -509,26 +510,26 @@ void imgCacheEntry::Touch(bool updateTim UpdateCache(); } void imgCacheEntry::UpdateCache(int32_t diff /* = 0 */) { // Don't update the cache if we've been removed from it or it doesn't care // about our size or usage. if (!Evicted() && HasNoProxies()) { - nsCOMPtr<nsIURI> uri; + nsRefPtr<ImageURL> uri; mRequest->GetURI(getter_AddRefs(uri)); mLoader->CacheEntriesChanged(uri, diff); } } void imgCacheEntry::SetHasNoProxies(bool hasNoProxies) { #if defined(PR_LOGGING) - nsCOMPtr<nsIURI> uri; + nsRefPtr<ImageURL> uri; mRequest->GetURI(getter_AddRefs(uri)); nsAutoCString spec; if (uri) uri->GetSpec(spec); if (hasNoProxies) LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies true", "uri", spec.get()); else LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies false", "uri", spec.get()); @@ -642,17 +643,17 @@ nsresult imgLoader::CreateNewProxyForReq imgRequestProxy *proxyRequest = new imgRequestProxy(); NS_ADDREF(proxyRequest); /* It is important to call |SetLoadFlags()| before calling |Init()| because |Init()| adds the request to the loadgroup. */ proxyRequest->SetLoadFlags(aLoadFlags); - nsCOMPtr<nsIURI> uri; + nsRefPtr<ImageURL> uri; aRequest->GetURI(getter_AddRefs(uri)); // init adds itself to imgRequest's list of observers nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, uri, aObserver); if (NS_FAILED(rv)) { NS_RELEASE(proxyRequest); return rv; } @@ -700,17 +701,17 @@ void imgCacheExpirationTracker::NotifyEx { // Hold on to a reference to this entry, because the expiration tracker // mechanism doesn't. nsRefPtr<imgCacheEntry> kungFuDeathGrip(entry); #if defined(PR_LOGGING) nsRefPtr<imgRequest> req(entry->GetRequest()); if (req) { - nsCOMPtr<nsIURI> uri; + nsRefPtr<ImageURL> uri; req->GetURI(getter_AddRefs(uri)); nsAutoCString spec; uri->GetSpec(spec); LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheExpirationTracker::NotifyExpired", "entry", spec.get()); } #endif // We can be called multiple times on the same entry. Don't do work multiple @@ -771,32 +772,43 @@ void imgLoader::VerifyCacheSizes() trackersize++; NS_ABORT_IF_FALSE(queuesize == trackersize, "Queue and tracker sizes out of sync!"); NS_ABORT_IF_FALSE(queuesize <= cachesize, "Queue has more elements than cache!"); #endif } imgLoader::imgCacheTable & imgLoader::GetCache(nsIURI *aURI) { + MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!"); bool chrome = false; aURI->SchemeIs("chrome", &chrome); - if (chrome) - return mChromeCache; - else - return mCache; + return chrome ? mChromeCache : mCache; } imgCacheQueue & imgLoader::GetCacheQueue(nsIURI *aURI) { + MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!"); bool chrome = false; aURI->SchemeIs("chrome", &chrome); - if (chrome) - return mChromeCacheQueue; - else - return mCacheQueue; + return chrome ? mChromeCacheQueue : mCacheQueue; + +} + +imgLoader::imgCacheTable & imgLoader::GetCache(ImageURL *aURI) +{ + bool chrome = false; + aURI->SchemeIs("chrome", &chrome); + return chrome ? mChromeCache : mCache; +} + +imgCacheQueue & imgLoader::GetCacheQueue(ImageURL *aURI) +{ + bool chrome = false; + aURI->SchemeIs("chrome", &chrome); + return chrome ? mChromeCacheQueue : mCacheQueue; } void imgLoader::GlobalInit() { gCacheObserver = new imgCacheObserver(); NS_ADDREF(gCacheObserver); nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); @@ -1013,17 +1025,17 @@ bool imgLoader::PutIntoCache(nsIURI *key } nsRefPtr<imgRequest> request = entry->GetRequest(); request->SetIsInCache(true); return true; } -bool imgLoader::SetHasNoProxies(nsIURI *key, imgCacheEntry *entry) +bool imgLoader::SetHasNoProxies(ImageURL *key, imgCacheEntry *entry) { #if defined(PR_LOGGING) nsAutoCString spec; key->GetSpec(spec); LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::SetHasNoProxies", "uri", spec.get()); #endif @@ -1043,17 +1055,17 @@ bool imgLoader::SetHasNoProxies(nsIURI * } imgCacheTable &cache = GetCache(key); CheckCacheLimits(cache, queue); return true; } -bool imgLoader::SetHasProxies(nsIURI *key) +bool imgLoader::SetHasProxies(ImageURL *key) { VerifyCacheSizes(); imgCacheTable &cache = GetCache(key); nsAutoCString spec; key->GetSpec(spec); @@ -1070,17 +1082,17 @@ bool imgLoader::SetHasProxies(nsIURI *ke entry->SetHasNoProxies(false); return true; } return false; } -void imgLoader::CacheEntriesChanged(nsIURI *uri, int32_t sizediff /* = 0 */) +void imgLoader::CacheEntriesChanged(ImageURL *uri, int32_t sizediff /* = 0 */) { imgCacheQueue &queue = GetCacheQueue(uri); queue.MarkDirty(); queue.UpdateSize(sizediff); } void imgLoader::CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue) { @@ -1093,17 +1105,17 @@ void imgLoader::CheckCacheLimits(imgCach // Remove the first entry in the queue. nsRefPtr<imgCacheEntry> entry(queue.Pop()); NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer"); #if defined(PR_LOGGING) nsRefPtr<imgRequest> req(entry->GetRequest()); if (req) { - nsCOMPtr<nsIURI> uri; + nsRefPtr<ImageURL> uri; req->GetURI(getter_AddRefs(uri)); nsAutoCString spec; uri->GetSpec(spec); LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::CheckCacheLimits", "entry", spec.get()); } #endif if (entry) @@ -1185,27 +1197,30 @@ bool imgLoader::ValidateRequestWithNewCh nsRefPtr<nsProgressNotificationProxy> progressproxy = new nsProgressNotificationProxy(newChannel, req); if (!progressproxy) return false; nsRefPtr<imgCacheValidator> hvc = new imgCacheValidator(progressproxy, this, request, aCX, forcePrincipalCheck); - nsCOMPtr<nsIStreamListener> listener = hvc.get(); + // Casting needed here to get past multiple inheritance. + nsCOMPtr<nsIStreamListener> listener = + do_QueryInterface(static_cast<nsIThreadRetargetableStreamListener*>(hvc)); + NS_ENSURE_TRUE(listener, false); // We must set the notification callbacks before setting up the // CORS listener, because that's also interested inthe // notification callbacks. newChannel->SetNotificationCallbacks(hvc); if (aCORSMode != imgIRequest::CORS_NONE) { bool withCredentials = aCORSMode == imgIRequest::CORS_USE_CREDENTIALS; nsRefPtr<nsCORSListenerProxy> corsproxy = - new nsCORSListenerProxy(hvc, aLoadingPrincipal, withCredentials); + new nsCORSListenerProxy(listener, aLoadingPrincipal, withCredentials); rv = corsproxy->Init(newChannel); if (NS_FAILED(rv)) { return false; } listener = corsproxy; } @@ -1348,27 +1363,48 @@ bool imgLoader::ValidateEntry(imgCacheEn aReferrerURI, aLoadGroup, aObserver, aCX, aLoadFlags, aProxyRequest, aPolicy, aLoadingPrincipal, aCORSMode); } return !validateRequest; } - bool imgLoader::RemoveFromCache(nsIURI *aKey) { + MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!"); + + if (!aKey) return false; + + imgCacheTable &cache = GetCache(aKey); + imgCacheQueue &queue = GetCacheQueue(aKey); + + nsAutoCString spec; + aKey->GetSpec(spec); + + return RemoveFromCache(spec, cache, queue); +} + +bool imgLoader::RemoveFromCache(ImageURL *aKey) +{ if (!aKey) return false; imgCacheTable &cache = GetCache(aKey); imgCacheQueue &queue = GetCacheQueue(aKey); nsAutoCString spec; aKey->GetSpec(spec); + return RemoveFromCache(spec, cache, queue); +} + +bool imgLoader::RemoveFromCache(nsCString& spec, + imgCacheTable &cache, + imgCacheQueue &queue) +{ LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::RemoveFromCache", "uri", spec.get()); nsRefPtr<imgCacheEntry> entry; if (cache.Get(spec, getter_AddRefs(entry)) && entry) { cache.Remove(spec); NS_ABORT_IF_FALSE(!entry->Evicted(), "Evicting an already-evicted cache entry!"); @@ -1391,17 +1427,17 @@ bool imgLoader::RemoveFromCache(nsIURI * } bool imgLoader::RemoveFromCache(imgCacheEntry *entry) { LOG_STATIC_FUNC(GetImgLog(), "imgLoader::RemoveFromCache entry"); nsRefPtr<imgRequest> request = entry->GetRequest(); if (request) { - nsCOMPtr<nsIURI> key; + nsRefPtr<ImageURL> key; if (NS_SUCCEEDED(request->GetURI(getter_AddRefs(key))) && key) { imgCacheTable &cache = GetCache(key); imgCacheQueue &queue = GetCacheQueue(key); nsAutoCString spec; key->GetSpec(spec); LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::RemoveFromCache", "entry's uri", spec.get()); @@ -1996,17 +2032,20 @@ nsresult imgLoader::GetMimeTypeFromConte /** * proxy stream listener class used to handle multipart/x-mixed-replace */ #include "nsIRequest.h" #include "nsIStreamConverterService.h" -NS_IMPL_ISUPPORTS2(ProxyListener, nsIStreamListener, nsIRequestObserver) +NS_IMPL_ISUPPORTS3(ProxyListener, + nsIStreamListener, + nsIThreadRetargetableStreamListener, + nsIRequestObserver) ProxyListener::ProxyListener(nsIStreamListener *dest) : mDestListener(dest) { /* member initializers and constructor code */ } ProxyListener::~ProxyListener() @@ -2073,21 +2112,40 @@ ProxyListener::OnDataAvailable(nsIReques uint32_t count) { if (!mDestListener) return NS_ERROR_FAILURE; return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count); } +/** nsThreadRetargetableStreamListener methods **/ +NS_IMETHODIMP +ProxyListener::CheckListenerChain() +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!"); + nsresult rv = NS_OK; + nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener = + do_QueryInterface(mDestListener, &rv); + if (retargetableListener) { + rv = retargetableListener->CheckListenerChain(); + } + PR_LOG(GetImgLog(), PR_LOG_DEBUG, + ("ProxyListener::CheckListenerChain %s [this=%p listener=%p rv=%x]", + (NS_SUCCEEDED(rv) ? "success" : "failure"), + this, (nsIStreamListener*)mDestListener, rv)); + return rv; +} + /** * http validate class. check a channel for a 304 */ -NS_IMPL_ISUPPORTS5(imgCacheValidator, nsIStreamListener, nsIRequestObserver, +NS_IMPL_ISUPPORTS6(imgCacheValidator, nsIStreamListener, nsIRequestObserver, + nsIThreadRetargetableStreamListener, nsIChannelEventSink, nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback) imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress, imgLoader* loader, imgRequest *request, void *aContext, bool forcePrincipalCheckForCacheEntry) : mProgressProxy(progress), mRequest(request), @@ -2164,17 +2222,21 @@ NS_IMETHODIMP imgCacheValidator::OnStart return NS_OK; } } // We can't load out of cache. We have to create a whole new request for the // data that's coming in off the channel. nsCOMPtr<nsIURI> uri; - mRequest->GetURI(getter_AddRefs(uri)); + { + nsRefPtr<ImageURL> imageURL; + mRequest->GetURI(getter_AddRefs(imageURL)); + uri = imageURL->ToIURI(); + } #if defined(PR_LOGGING) nsAutoCString spec; uri->GetSpec(spec); LOG_MSG_WITH_PARAM(GetImgLog(), "imgCacheValidator::OnStartRequest creating new request", "uri", spec.get()); #endif int32_t corsmode = mRequest->GetCORSMode(); @@ -2240,16 +2302,34 @@ imgCacheValidator::OnDataAvailable(nsIRe uint32_t _retval; inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &_retval); return NS_OK; } return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count); } +/** nsIThreadRetargetableStreamListener methods **/ + +NS_IMETHODIMP +imgCacheValidator::CheckListenerChain() +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!"); + nsresult rv = NS_OK; + nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener = + do_QueryInterface(mDestListener, &rv); + if (retargetableListener) { + rv = retargetableListener->CheckListenerChain(); + } + PR_LOG(GetImgLog(), PR_LOG_DEBUG, + ("[this=%p] imgCacheValidator::CheckListenerChain -- rv %d=%s", + this, NS_SUCCEEDED(rv) ? "succeeded" : "failed", rv)); + return rv; +} + /** nsIInterfaceRequestor methods **/ NS_IMETHODIMP imgCacheValidator::GetInterface(const nsIID & aIID, void **aResult) { if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) return QueryInterface(aIID, aResult); return mProgressProxy->GetInterface(aIID, aResult);
--- a/image/src/imgLoader.h +++ b/image/src/imgLoader.h @@ -14,26 +14,33 @@ #include "nsWeakReference.h" #include "nsIContentSniffer.h" #include "nsRefPtrHashtable.h" #include "nsExpirationTracker.h" #include "nsAutoPtr.h" #include "imgRequest.h" #include "nsIProgressEventSink.h" #include "nsIChannel.h" +#include "nsIThreadRetargetableStreamListener.h" #include "imgIRequest.h" class imgLoader; class imgRequestProxy; class imgINotificationObserver; class nsILoadGroup; class imgCacheExpirationTracker; class imgMemoryReporter; class nsIChannelPolicy; +namespace mozilla { +namespace image { +class ImageURL; +} +} + class imgCacheEntry { public: imgCacheEntry(imgLoader* loader, imgRequest *request, bool aForcePrincipalCheck); ~imgCacheEntry(); nsrefcnt AddRef() { @@ -200,16 +207,19 @@ private: class imgLoader : public imgILoader, public nsIContentSniffer, public imgICache, public nsSupportsWeakReference, public nsIObserver { public: + typedef mozilla::image::ImageURL ImageURL; + typedef nsRefPtrHashtable<nsCStringHashKey, imgCacheEntry> imgCacheTable; + NS_DECL_ISUPPORTS NS_DECL_IMGILOADER NS_DECL_NSICONTENTSNIFFER NS_DECL_IMGICACHE NS_DECL_NSIOBSERVER imgLoader(); virtual ~imgLoader(); @@ -258,16 +268,20 @@ public: nsresult ClearChromeImageCache(); nsresult ClearImageCache(); void MinimizeCaches(); nsresult InitCache(); bool RemoveFromCache(nsIURI *aKey); + bool RemoveFromCache(ImageURL *aKey); + bool RemoveFromCache(nsCString &spec, + imgCacheTable &cache, + imgCacheQueue &queue); bool RemoveFromCache(imgCacheEntry *entry); bool PutIntoCache(nsIURI *key, imgCacheEntry *entry); // Returns true if we should prefer evicting cache entry |two| over cache // entry |one|. // This mixes units in the worst way, but provides reasonable results. inline static bool CompareCacheEntries(const nsRefPtr<imgCacheEntry> &one, @@ -299,18 +313,18 @@ public: // // Once an imgRequest has no imgRequestProxies, it should notify us by // calling HasNoObservers(), and null out its cache entry pointer. // // Upon having a proxy start observing again, it should notify us by calling // HasObservers(). The request's cache entry will be re-set before this // happens, by calling imgRequest::SetCacheEntry() when an entry with no // observers is re-requested. - bool SetHasNoProxies(nsIURI *key, imgCacheEntry *entry); - bool SetHasProxies(nsIURI *key); + bool SetHasNoProxies(ImageURL *key, imgCacheEntry *entry); + bool SetHasProxies(ImageURL *key); private: // methods bool ValidateEntry(imgCacheEntry *aEntry, nsIURI *aKey, nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI, nsILoadGroup *aLoadGroup, imgINotificationObserver *aObserver, nsISupports *aCX, nsLoadFlags aLoadFlags, bool aCanMakeNewChannel, @@ -331,25 +345,24 @@ private: // methods int32_t aCORSMode); nsresult CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup, imgINotificationObserver *aObserver, nsLoadFlags aLoadFlags, imgRequestProxy **_retval); void ReadAcceptHeaderPref(); - - typedef nsRefPtrHashtable<nsCStringHashKey, imgCacheEntry> imgCacheTable; - nsresult EvictEntries(imgCacheTable &aCacheToClear); nsresult EvictEntries(imgCacheQueue &aQueueToClear); imgCacheTable &GetCache(nsIURI *aURI); imgCacheQueue &GetCacheQueue(nsIURI *aURI); - void CacheEntriesChanged(nsIURI *aURI, int32_t sizediff = 0); + imgCacheTable &GetCache(ImageURL *aURI); + imgCacheQueue &GetCacheQueue(ImageURL *aURI); + void CacheEntriesChanged(ImageURL *aURI, int32_t sizediff = 0); void CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue); private: // data friend class imgCacheEntry; friend class imgMemoryReporter; imgCacheTable mCache; imgCacheQueue mCacheQueue; @@ -370,26 +383,29 @@ private: // data /** * proxy stream listener class used to handle multipart/x-mixed-replace */ #include "nsCOMPtr.h" #include "nsIStreamListener.h" +#include "nsIThreadRetargetableStreamListener.h" class ProxyListener : public nsIStreamListener + , public nsIThreadRetargetableStreamListener { public: ProxyListener(nsIStreamListener *dest); virtual ~ProxyListener(); /* additional members */ NS_DECL_ISUPPORTS NS_DECL_NSISTREAMLISTENER + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER private: nsCOMPtr<nsIStreamListener> mDestListener; }; /** * A class that implements nsIProgressEventSink and forwards all calls to it to @@ -422,28 +438,30 @@ class nsProgressNotificationProxy MOZ_FI /** * validate checker */ #include "nsCOMArray.h" class imgCacheValidator : public nsIStreamListener, + public nsIThreadRetargetableStreamListener, public nsIChannelEventSink, public nsIInterfaceRequestor, public nsIAsyncVerifyRedirectCallback { public: imgCacheValidator(nsProgressNotificationProxy* progress, imgLoader* loader, imgRequest *request, void *aContext, bool forcePrincipalCheckForCacheEntry); virtual ~imgCacheValidator(); void AddProxy(imgRequestProxy *aProxy); NS_DECL_ISUPPORTS + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER NS_DECL_NSISTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK private: nsCOMPtr<nsIStreamListener> mDestListener;
--- a/image/src/imgRequest.cpp +++ b/image/src/imgRequest.cpp @@ -7,19 +7,21 @@ #include "imgRequest.h" #include "ImageLogging.h" #include "imgLoader.h" #include "imgRequestProxy.h" #include "imgStatusTracker.h" #include "ImageFactory.h" #include "Image.h" +#include "RasterImage.h" #include "nsIChannel.h" #include "nsICachingChannel.h" +#include "nsIThreadRetargetableRequest.h" #include "nsIInputStream.h" #include "nsIMultiPartChannel.h" #include "nsIHttpChannel.h" #include "nsIApplicationCache.h" #include "nsIApplicationCacheChannel.h" #include "nsMimeTypes.h" #include "nsIInterfaceRequestorUtils.h" @@ -42,18 +44,19 @@ GetImgLog() { static PRLogModuleInfo *sImgLog; if (!sImgLog) sImgLog = PR_NewLogModule("imgRequest"); return sImgLog; } #endif -NS_IMPL_ISUPPORTS5(imgRequest, +NS_IMPL_ISUPPORTS6(imgRequest, nsIStreamListener, nsIRequestObserver, + nsIThreadRetargetableStreamListener, nsIChannelEventSink, nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback) imgRequest::imgRequest(imgLoader* aLoader) : mLoader(aLoader) , mStatusTracker(new imgStatusTracker(nullptr)) , mValidator(nullptr) @@ -80,27 +83,30 @@ nsresult imgRequest::Init(nsIURI *aURI, nsIURI *aCurrentURI, nsIRequest *aRequest, nsIChannel *aChannel, imgCacheEntry *aCacheEntry, void *aLoadId, nsIPrincipal* aLoadingPrincipal, int32_t aCORSMode) { + MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!"); + LOG_FUNC(GetImgLog(), "imgRequest::Init"); 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"); - mURI = aURI; + // Use ImageURL to ensure access to URI data off main thread. + mURI = new ImageURL(aURI); mCurrentURI = aCurrentURI; mRequest = aRequest; mChannel = aChannel; mTimedChannel = do_QueryInterface(mChannel); mLoadingPrincipal = aLoadingPrincipal; mCORSMode = aCORSMode; @@ -243,24 +249,31 @@ void imgRequest::Cancel(nsresult aStatus LOG_SCOPE(GetImgLog(), "imgRequest::Cancel"); imgStatusTracker& statusTracker = GetStatusTracker(); statusTracker.MaybeUnblockOnload(); statusTracker.RecordCancel(); - RemoveFromCache(); + if (NS_IsMainThread()) { + RemoveFromCache(); + } else { + NS_DispatchToMainThread( + NS_NewRunnableMethod(this, &imgRequest::RemoveFromCache)); + } if (mRequest && statusTracker.IsLoading()) mRequest->Cancel(aStatus); } -nsresult imgRequest::GetURI(nsIURI **aURI) +nsresult imgRequest::GetURI(ImageURL **aURI) { + MOZ_ASSERT(aURI); + LOG_FUNC(GetImgLog(), "imgRequest::GetURI"); if (mURI) { *aURI = mURI; NS_ADDREF(*aURI); return NS_OK; } @@ -565,16 +578,36 @@ NS_IMETHODIMP imgRequest::OnStartRequest mApplicationCache = GetApplicationCache(aRequest); // Shouldn't we be dead already if this gets hit? Probably multipart/x-mixed-replace... if (GetStatusTracker().ConsumerCount() == 0) { this->Cancel(NS_IMAGELIB_ERROR_FAILURE); } + // Try to retarget OnDataAvailable to a decode thread. + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest); + nsCOMPtr<nsIThreadRetargetableRequest> retargetable = + do_QueryInterface(aRequest); + if (httpChannel && retargetable && + !(mIsMultiPartChannel && mImage)) { + nsAutoCString mimeType; + nsresult rv = httpChannel->GetContentType(mimeType); + if (NS_SUCCEEDED(rv) && !mimeType.EqualsLiteral(IMAGE_SVG_XML)) { + // Image object not created until OnDataAvailable, so forward to static + // DecodePool directly. + nsCOMPtr<nsIEventTarget> target = RasterImage::GetEventTarget(); + rv = retargetable->RetargetDeliveryTo(target); + } + PR_LOG(GetImgLog(), PR_LOG_WARNING, + ("[this=%p] imgRequest::OnStartRequest -- " + "RetargetDeliveryTo rv %d=%s\n", + this, NS_SUCCEEDED(rv) ? "succeeded" : "failed", rv)); + } + return NS_OK; } /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status) { LOG_FUNC(GetImgLog(), "imgRequest::OnStopRequest"); @@ -639,16 +672,25 @@ struct mimetype_closure { nsACString* newType; }; /* prototype for these defined below */ static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment, uint32_t toOffset, uint32_t count, uint32_t *writeCount); +/** nsThreadRetargetableStreamListener methods **/ +NS_IMETHODIMP +imgRequest::CheckListenerChain() +{ + // TODO Might need more checking here. + NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!"); + return NS_OK; +} + /** nsIStreamListener methods **/ /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, uint64_t sourceOffset, uint32_t count) { @@ -715,38 +757,24 @@ imgRequest::OnDataAvailable(nsIRequest * if (resniffMimeType) { NS_ABORT_IF_FALSE(mIsMultiPartChannel, "Resniffing a non-multipart image"); imgStatusTracker* freshTracker = new imgStatusTracker(nullptr); freshTracker->AdoptConsumers(&GetStatusTracker()); mStatusTracker = freshTracker; } - /* 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); - } - - /* set our content disposition as a property */ - nsAutoCString disposition; - if (chan) { - chan->GetContentDispositionHeader(disposition); - } - if (!disposition.IsEmpty()) { - nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1")); - if (contentDisposition) { - contentDisposition->SetData(disposition); - mProperties->Set("content-disposition", contentDisposition); - } - } + SetProperties(chan); LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnDataAvailable", "content type", mContentType.get()); + // XXX If server lied about mimetype and it's SVG, we may need to copy + // the data and dispatch back to the main thread, AND tell the channel to + // dispatch there in the future. + // Now we can create a new image to hold the data. 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, static_cast<uint32_t>(mInnerWindowId)); // Release our copy of the status tracker since the image owns it now. mStatusTracker = nullptr; @@ -780,16 +808,68 @@ imgRequest::OnDataAvailable(nsIRequest * "copy to RasterImage failed\n", this)); this->Cancel(NS_IMAGELIB_ERROR_FAILURE); return NS_BINDING_ABORTED; } return NS_OK; } +class SetPropertiesEvent : public nsRunnable +{ +public: + SetPropertiesEvent(imgRequest* aImgRequest, nsIChannel* aChan) + : mImgRequest(aImgRequest) + , mChan(aChan) + { + MOZ_ASSERT(!NS_IsMainThread(), "Should be created off the main thread"); + MOZ_ASSERT(aImgRequest, "aImgRequest cannot be null"); + } + NS_IMETHOD Run() + { + MOZ_ASSERT(NS_IsMainThread(), "Should run on the main thread only"); + MOZ_ASSERT(mImgRequest, "mImgRequest cannot be null"); + mImgRequest->SetProperties(mChan); + return NS_OK; + } +private: + nsRefPtr<imgRequest> mImgRequest; + nsCOMPtr<nsIChannel> mChan; +}; + +void +imgRequest::SetProperties(nsIChannel* aChan) +{ + // Force execution on main thread since some property objects are non + // threadsafe. + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(new SetPropertiesEvent(this, aChan)); + return; + } + /* 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); + } + + /* set our content disposition as a property */ + nsAutoCString disposition; + if (aChan) { + aChan->GetContentDispositionHeader(disposition); + } + if (!disposition.IsEmpty()) { + nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1")); + if (contentDisposition) { + contentDisposition->SetData(disposition); + mProperties->Set("content-disposition", contentDisposition); + } + } +} + static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* data, const char* fromRawSegment, uint32_t toOffset, uint32_t count, uint32_t *writeCount) { mimetype_closure* closure = static_cast<mimetype_closure*>(data);
--- a/image/src/imgRequest.h +++ b/image/src/imgRequest.h @@ -5,20 +5,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef imgRequest_h__ #define imgRequest_h__ #include "nsIChannelEventSink.h" #include "nsIInterfaceRequestor.h" #include "nsIStreamListener.h" +#include "nsIThreadRetargetableStreamListener.h" #include "nsIPrincipal.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" +#include "nsProxyRelease.h" #include "nsStringGlue.h" #include "nsError.h" #include "nsIAsyncVerifyRedirectCallback.h" class imgCacheValidator; class imgStatusTracker; class imgLoader; class imgRequestProxy; @@ -29,29 +31,32 @@ class nsIApplicationCache; class nsIProperties; class nsIRequest; class nsITimedChannel; class nsIURI; namespace mozilla { namespace image { class Image; +class ImageURL; } // namespace image } // namespace mozilla class imgRequest : public nsIStreamListener, + public nsIThreadRetargetableStreamListener, public nsIChannelEventSink, public nsIInterfaceRequestor, public nsIAsyncVerifyRedirectCallback { public: + typedef mozilla::image::ImageURL ImageURL; imgRequest(imgLoader* aLoader); virtual ~imgRequest(); - NS_DECL_ISUPPORTS + NS_DECL_THREADSAFE_ISUPPORTS nsresult Init(nsIURI *aURI, nsIURI *aCurrentURI, nsIRequest *aRequest, nsIChannel *aChannel, imgCacheEntry *aCacheEntry, void *aLoadId, nsIPrincipal* aLoadingPrincipal, @@ -116,17 +121,18 @@ public: 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); + // OK to use on any thread. + nsresult GetURI(ImageURL **aURI); private: friend class imgCacheEntry; friend class imgRequestProxy; friend class imgLoader; friend class imgCacheValidator; friend class imgStatusTracker; friend class imgCacheExpirationTracker; @@ -171,30 +177,35 @@ private: // try to update or modify the image cache. void SetIsInCache(bool cacheable); bool IsBlockingOnload() const; void SetBlockingOnload(bool block) const; public: NS_DECL_NSISTREAMLISTENER + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK + // Sets properties for this image; will dispatch to main thread if needed. + void SetProperties(nsIChannel* aChan); + private: friend class imgMemoryReporter; // Weak reference to parent loader; this request cannot outlive its owner. imgLoader* mLoader; nsCOMPtr<nsIRequest> mRequest; // The original URI we were loaded with. This is the same as the URI we are - // keyed on in the cache. - nsCOMPtr<nsIURI> mURI; + // keyed on in the cache. We store a string here to avoid off main thread + // refcounting issues with nsStandardURL. + nsRefPtr<ImageURL> mURI; // The URI of the resource we ended up loading after all redirects, etc. nsCOMPtr<nsIURI> mCurrentURI; // The principal of the document which loaded this image. Used when validating for CORS. nsCOMPtr<nsIPrincipal> mLoadingPrincipal; // The principal of this image. nsCOMPtr<nsIPrincipal> mPrincipal; // Status-tracker -- transferred to mImage, when it gets instantiated nsRefPtr<imgStatusTracker> mStatusTracker;
--- a/image/src/imgRequestProxy.cpp +++ b/image/src/imgRequestProxy.cpp @@ -8,16 +8,17 @@ #include "imgIOnloadBlocker.h" #include "Image.h" #include "ImageOps.h" #include "nsError.h" #include "ImageLogging.h" #include "nsCRTGlue.h" #include "imgINotificationObserver.h" +#include "nsNetUtil.h" using namespace mozilla::image; // The split of imgRequestProxy and imgRequestProxyStatic means that // certain overridden functions need to be usable in the destructor. // Since virtual functions can't be used in that way, this class // provides a behavioural trait for each class to use instead. class ProxyBehaviour @@ -141,17 +142,17 @@ imgRequestProxy::~imgRequestProxy() */ mCanceled = true; GetOwner()->RemoveProxy(this, NS_OK); } } nsresult imgRequestProxy::Init(imgRequest* aOwner, nsILoadGroup* aLoadGroup, - nsIURI* aURI, + ImageURL* aURI, imgINotificationObserver* aObserver) { NS_PRECONDITION(!GetOwner() && !mListener, "imgRequestProxy is already initialized"); LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request", aOwner); NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init"); @@ -499,16 +500,24 @@ NS_IMETHODIMP imgRequestProxy::GetImageS *aStatus = GetStatusTracker().GetImageStatus(); return NS_OK; } /* readonly attribute nsIURI URI; */ NS_IMETHODIMP imgRequestProxy::GetURI(nsIURI **aURI) { + MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI"); + nsCOMPtr<nsIURI> uri = mURI->ToIURI(); + uri.forget(aURI); + return NS_OK; +} + +nsresult imgRequestProxy::GetURI(ImageURL **aURI) +{ if (!mURI) return NS_ERROR_FAILURE; NS_ADDREF(*aURI = mURI); return NS_OK; }
--- a/image/src/imgRequestProxy.h +++ b/image/src/imgRequestProxy.h @@ -31,40 +31,42 @@ class imgINotificationObserver; class imgRequestNotifyRunnable; class imgStatusNotifyRunnable; class nsIntRect; class ProxyBehaviour; namespace mozilla { namespace image { class Image; +class ImageURL; } // namespace image } // namespace mozilla class imgRequestProxy : public imgIRequest, public nsISupportsPriority, public nsISecurityInfoProvider, public nsITimedChannel { public: + typedef mozilla::image::ImageURL ImageURL; NS_DECL_ISUPPORTS NS_DECL_IMGIREQUEST NS_DECL_NSIREQUEST NS_DECL_NSISUPPORTSPRIORITY 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(imgRequest* aOwner, nsILoadGroup *aLoadGroup, - nsIURI* aURI, + ImageURL* aURI, imgINotificationObserver *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); @@ -101,16 +103,18 @@ public: // IncrementAnimationConsumers. This is necessary since we need // to do it before the proxy itself is destroyed. See // imgRequest::RemoveProxy void ClearAnimationConsumers(); virtual nsresult Clone(imgINotificationObserver* aObserver, imgRequestProxy** aClone); nsresult GetStaticRequest(imgRequestProxy** aReturn); + nsresult GetURI(ImageURL **aURI); + protected: friend class imgStatusTracker; friend class imgStatusNotifyRunnable; friend class imgRequestNotifyRunnable; class imgCancelRunnable; friend class imgCancelRunnable; @@ -189,17 +193,17 @@ public: protected: nsAutoPtr<ProxyBehaviour> mBehaviour; private: friend class imgCacheValidator; friend imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis); // The URI of our request. - nsCOMPtr<nsIURI> mURI; + nsRefPtr<ImageURL> mURI; // 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; nsCOMPtr<nsILoadGroup> mLoadGroup; nsLoadFlags mLoadFlags;
--- a/image/src/imgStatusTracker.cpp +++ b/image/src/imgStatusTracker.cpp @@ -6,16 +6,17 @@ #include "imgStatusTracker.h" #include "imgIContainer.h" #include "imgRequestProxy.h" #include "imgDecoderObserver.h" #include "Image.h" #include "ImageLogging.h" +#include "nsNetUtil.h" #include "nsIObserverService.h" #include "mozilla/Assertions.h" #include "mozilla/Services.h" using namespace mozilla::image; class imgStatusTrackerNotifyingObserver : public imgDecoderObserver @@ -386,17 +387,17 @@ class imgRequestNotifyRunnable : public nsTArray< nsRefPtr<imgRequestProxy> > mProxies; }; void imgStatusTracker::Notify(imgRequestProxy* proxy) { #ifdef PR_LOGGING if (GetImage() && GetImage()->GetURI()) { - nsCOMPtr<nsIURI> uri(GetImage()->GetURI()); + nsRefPtr<ImageURL> 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", "<unknown>"); } #endif @@ -439,17 +440,17 @@ class imgStatusNotifyRunnable : public n nsRefPtr<Image> mImage; nsRefPtr<imgRequestProxy> mProxy; }; void imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy) { #ifdef PR_LOGGING - nsCOMPtr<nsIURI> uri; + nsRefPtr<ImageURL> uri; proxy->GetURI(getter_AddRefs(uri)); nsAutoCString spec; uri->GetSpec(spec); LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::NotifyCurrentState", "uri", spec.get()); #endif proxy->SetNotificationsDeferred(true); @@ -627,17 +628,17 @@ imgStatusTracker::CloneForRecording() imgStatusTracker* clone = new imgStatusTracker(*this); return clone; } void imgStatusTracker::SyncNotify(imgRequestProxy* proxy) { #ifdef PR_LOGGING - nsCOMPtr<nsIURI> uri; + nsRefPtr<ImageURL> uri; proxy->GetURI(getter_AddRefs(uri)); nsAutoCString spec; uri->GetSpec(spec); LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgStatusTracker::SyncNotify", "uri", spec.get()); #endif nsIntRect r; if (mImage) { @@ -1066,17 +1067,22 @@ imgStatusTracker::RecordError() void imgStatusTracker::FireFailureNotification() { MOZ_ASSERT(NS_IsMainThread()); // Some kind of problem has happened with image decoding. // Report the URI to net:failed-to-process-uri-conent observers. if (GetImage()) { - nsCOMPtr<nsIURI> uri = GetImage()->GetURI(); + // Should be on main thread, so ok to create a new nsIURI. + nsCOMPtr<nsIURI> uri; + { + nsRefPtr<ImageURL> threadsafeUriData = GetImage()->GetURI(); + uri = threadsafeUriData ? threadsafeUriData->ToIURI() : nullptr; + } if (uri) { nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) { os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr); } } } }
--- a/netwerk/base/src/nsMediaFragmentURIParser.cpp +++ b/netwerk/base/src/nsMediaFragmentURIParser.cpp @@ -20,16 +20,22 @@ namespace mozilla { namespace net { nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsIURI* aURI) : mClipUnit(eClipUnit_Pixel) { nsAutoCString ref; aURI->GetRef(ref); Parse(ref); } +nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsCString& aRef) + : mClipUnit(eClipUnit_Pixel) +{ + Parse(aRef); +} + bool nsMediaFragmentURIParser::ParseNPT(nsDependentSubstring aString) { nsDependentSubstring original(aString); if (aString.Length() > 4 && aString[0] == 'n' && aString[1] == 'p' && aString[2] == 't' && aString[3] == ':') { aString.Rebind(aString, 4); }
--- a/netwerk/base/src/nsMediaFragmentURIParser.h +++ b/netwerk/base/src/nsMediaFragmentURIParser.h @@ -30,16 +30,19 @@ enum ClipUnit }; class nsMediaFragmentURIParser { public: // Create a parser with the provided URI. nsMediaFragmentURIParser(nsIURI* aURI); + // Create a parser with the provided URI reference portion. + nsMediaFragmentURIParser(nsCString& aRef); + // True if a valid temporal media fragment indicated a start time. bool HasStartTime() const { return !mStart.empty(); } // If a valid temporal media fragment indicated a start time, returns // it in units of seconds. If not, defaults to 0. double GetStartTime() const { return mStart.ref(); } // True if a valid temporal media fragment indicated an end time.