Bug 867755 - Support OnDataAvailable and OnStopRequest off main thread for image loading r=seth

This commit is contained in:
Steve Workman 2013-09-28 11:28:42 -07:00
parent 6f48aab55b
commit 1b740124a5
21 changed files with 436 additions and 101 deletions

View File

@ -7,6 +7,7 @@
#include "mozilla/ModuleUtils.h"
#include "nsMimeTypes.h"
#include "ImageFactory.h"
#include "RasterImage.h"
#include "imgLoader.h"
@ -81,6 +82,7 @@ static nsresult
imglib_Initialize()
{
mozilla::image::DiscardTracker::Initialize();
mozilla::image::ImageFactory::Initialize();
mozilla::image::RasterImage::Initialize();
imgLoader::GlobalInit();
return NS_OK;

View File

@ -10,15 +10,10 @@
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),

View File

@ -9,8 +9,7 @@
#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;
@ -132,7 +131,7 @@ public:
virtual bool HasError() = 0;
virtual void SetHasError() = 0;
virtual nsIURI* GetURI() = 0;
virtual ImageURL* GetURI() = 0;
};
class ImageResource : public Image
@ -159,10 +158,11 @@ public:
* 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.
@ -188,7 +188,7 @@ protected:
// 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

View File

@ -13,7 +13,6 @@
#include "nsIFileChannel.h"
#include "nsIFile.h"
#include "nsMimeTypes.h"
#include "nsIURI.h"
#include "nsIRequest.h"
#include "RasterImage.h"
@ -31,16 +30,19 @@ 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;
@ -83,13 +85,12 @@ ComputeImageFlags(nsIURI* uri, bool isMultiPart)
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);
@ -175,7 +176,7 @@ GetContentSize(nsIRequest* aRequest)
ImageFactory::CreateRasterImage(nsIRequest* aRequest,
imgStatusTracker* aStatusTracker,
const nsCString& aMimeType,
nsIURI* aURI,
ImageURL* aURI,
uint32_t aImageFlags,
uint32_t aInnerWindowId)
{
@ -206,7 +207,9 @@ ImageFactory::CreateRasterImage(nsIRequest* aRequest,
}
}
mozilla::net::nsMediaFragmentURIParser parser(aURI);
nsAutoCString ref;
aURI->GetRef(ref);
mozilla::net::nsMediaFragmentURIParser parser(ref);
if (parser.HasResolution()) {
newImage->SetRequestedResolution(parser.GetResolution());
}
@ -218,7 +221,7 @@ ImageFactory::CreateRasterImage(nsIRequest* aRequest,
ImageFactory::CreateVectorImage(nsIRequest* aRequest,
imgStatusTracker* aStatusTracker,
const nsCString& aMimeType,
nsIURI* aURI,
ImageURL* aURI,
uint32_t aImageFlags,
uint32_t aInnerWindowId)
{

View File

@ -8,22 +8,29 @@
#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.
@ -35,7 +42,7 @@ public:
static already_AddRefed<Image> CreateImage(nsIRequest* aRequest,
imgStatusTracker* aStatusTracker,
const nsCString& aMimeType,
nsIURI* aURI,
ImageURL* aURI,
bool aIsMultiPart,
uint32_t aInnerWindowId);
/**
@ -51,14 +58,14 @@ private:
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);

86
image/src/ImageURL.h Normal file
View File

@ -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_

View File

@ -134,7 +134,7 @@ ImageWrapper::SetHasError()
mInnerImage->SetHasError();
}
nsIURI*
ImageURL*
ImageWrapper::GetURI()
{
return mInnerImage->GetURI();

View File

@ -58,7 +58,7 @@ public:
virtual bool HasError() MOZ_OVERRIDE;
virtual void SetHasError() MOZ_OVERRIDE;
virtual nsIURI* GetURI() MOZ_OVERRIDE;
virtual ImageURL* GetURI() MOZ_OVERRIDE;
protected:
ImageWrapper(Image* aInnerImage)

View File

@ -375,7 +375,7 @@ NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
//******************************************************************************
RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
nsIURI* aURI /* = nullptr */) :
ImageURL* aURI /* = nullptr */) :
ImageResource(aStatusTracker, aURI), // invoke superclass's constructor
mSize(0,0),
mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
@ -2188,6 +2188,10 @@ RasterImage::RequestDecode()
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 ?
@ -3040,6 +3044,13 @@ RasterImage::DecodePool::Singleton()
return sSingleton;
}
already_AddRefed<nsIEventTarget>
RasterImage::DecodePool::GetEventTarget()
{
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
return target.forget();
}
RasterImage::DecodePool::DecodePool()
: mThreadPoolMutex("Thread Pool")
{

View File

@ -246,6 +246,10 @@ public:
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
@ -414,6 +418,14 @@ private:
*/
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 */
@ -732,7 +744,8 @@ private: // data
bool StoringSourceData() const;
protected:
RasterImage(imgStatusTracker* aStatusTracker = nullptr, nsIURI* aURI = nullptr);
RasterImage(imgStatusTracker* aStatusTracker = nullptr,
ImageURL* aURI = nullptr);
bool ShouldAnimate();

View File

@ -302,7 +302,7 @@ NS_IMPL_ISUPPORTS3(VectorImage,
// Constructor / Destructor
VectorImage::VectorImage(imgStatusTracker* aStatusTracker,
nsIURI* aURI /* = nullptr */) :
ImageURL* aURI /* = nullptr */) :
ImageResource(aStatusTracker, aURI), // invoke superclass's constructor
mIsInitialized(false),
mIsFullyLoaded(false),

View File

@ -75,7 +75,8 @@ public:
void OnSVGDocumentError();
protected:
VectorImage(imgStatusTracker* aStatusTracker = nullptr, nsIURI* aURI = nullptr);
VectorImage(imgStatusTracker* aStatusTracker = nullptr,
ImageURL* aURI = nullptr);
virtual nsresult StartAnimation();
virtual nsresult StopAnimation();

View File

@ -9,6 +9,7 @@
#include "mozilla/ClearOnShutdown.h"
#include "ImageLogging.h"
#include "nsPrintfCString.h"
#include "imgLoader.h"
#include "imgRequestProxy.h"
@ -514,7 +515,7 @@ 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);
}
@ -523,7 +524,7 @@ void imgCacheEntry::UpdateCache(int32_t diff /* = 0 */)
void imgCacheEntry::SetHasNoProxies(bool hasNoProxies)
{
#if defined(PR_LOGGING)
nsCOMPtr<nsIURI> uri;
nsRefPtr<ImageURL> uri;
mRequest->GetURI(getter_AddRefs(uri));
nsAutoCString spec;
if (uri)
@ -647,7 +648,7 @@ nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup
*/
proxyRequest->SetLoadFlags(aLoadFlags);
nsCOMPtr<nsIURI> uri;
nsRefPtr<ImageURL> uri;
aRequest->GetURI(getter_AddRefs(uri));
// init adds itself to imgRequest's list of observers
@ -705,7 +706,7 @@ void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *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);
@ -776,22 +777,33 @@ void imgLoader::VerifyCacheSizes()
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()
@ -1018,7 +1030,7 @@ bool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry)
return true;
}
bool imgLoader::SetHasNoProxies(nsIURI *key, imgCacheEntry *entry)
bool imgLoader::SetHasNoProxies(ImageURL *key, imgCacheEntry *entry)
{
#if defined(PR_LOGGING)
nsAutoCString spec;
@ -1048,7 +1060,7 @@ bool imgLoader::SetHasNoProxies(nsIURI *key, imgCacheEntry *entry)
return true;
}
bool imgLoader::SetHasProxies(nsIURI *key)
bool imgLoader::SetHasProxies(ImageURL *key)
{
VerifyCacheSizes();
@ -1075,7 +1087,7 @@ bool imgLoader::SetHasProxies(nsIURI *key)
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();
@ -1098,7 +1110,7 @@ void imgLoader::CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue)
#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);
@ -1190,7 +1202,10 @@ bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
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
@ -1200,7 +1215,7 @@ bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
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;
@ -1353,8 +1368,22 @@ bool imgLoader::ValidateEntry(imgCacheEntry *aEntry,
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;
@ -1364,6 +1393,13 @@ bool imgLoader::RemoveFromCache(nsIURI *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;
@ -1396,7 +1432,7 @@ bool imgLoader::RemoveFromCache(imgCacheEntry *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);
@ -2001,7 +2037,10 @@ nsresult imgLoader::GetMimeTypeFromContent(const char* aContents, uint32_t aLeng
#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)
@ -2078,11 +2117,30 @@ ProxyListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
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)
@ -2169,7 +2227,11 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
// 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;
@ -2245,6 +2307,24 @@ imgCacheValidator::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
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)

View File

@ -19,6 +19,7 @@
#include "imgRequest.h"
#include "nsIProgressEventSink.h"
#include "nsIChannel.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "imgIRequest.h"
class imgLoader;
@ -29,6 +30,12 @@ class imgCacheExpirationTracker;
class imgMemoryReporter;
class nsIChannelPolicy;
namespace mozilla {
namespace image {
class ImageURL;
}
}
class imgCacheEntry
{
public:
@ -205,6 +212,9 @@ class imgLoader : public imgILoader,
public nsIObserver
{
public:
typedef mozilla::image::ImageURL ImageURL;
typedef nsRefPtrHashtable<nsCStringHashKey, imgCacheEntry> imgCacheTable;
NS_DECL_ISUPPORTS
NS_DECL_IMGILOADER
NS_DECL_NSICONTENTSNIFFER
@ -263,6 +273,10 @@ public:
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);
@ -304,8 +318,8 @@ public:
// 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
@ -336,15 +350,14 @@ private: // methods
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
@ -375,8 +388,10 @@ private: // data
#include "nsCOMPtr.h"
#include "nsIStreamListener.h"
#include "nsIThreadRetargetableStreamListener.h"
class ProxyListener : public nsIStreamListener
, public nsIThreadRetargetableStreamListener
{
public:
ProxyListener(nsIStreamListener *dest);
@ -385,6 +400,7 @@ public:
/* additional members */
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
private:
@ -427,6 +443,7 @@ class nsProgressNotificationProxy MOZ_FINAL
#include "nsCOMArray.h"
class imgCacheValidator : public nsIStreamListener,
public nsIThreadRetargetableStreamListener,
public nsIChannelEventSink,
public nsIInterfaceRequestor,
public nsIAsyncVerifyRedirectCallback
@ -439,6 +456,7 @@ public:
void AddProxy(imgRequestProxy *aProxy);
NS_DECL_ISUPPORTS
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSICHANNELEVENTSINK

View File

@ -12,9 +12,11 @@
#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"
@ -47,8 +49,9 @@ GetImgLog()
}
#endif
NS_IMPL_ISUPPORTS5(imgRequest,
NS_IMPL_ISUPPORTS6(imgRequest,
nsIStreamListener, nsIRequestObserver,
nsIThreadRetargetableStreamListener,
nsIChannelEventSink,
nsIInterfaceRequestor,
nsIAsyncVerifyRedirectCallback)
@ -85,6 +88,8 @@ nsresult imgRequest::Init(nsIURI *aURI,
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");
@ -95,7 +100,8 @@ nsresult imgRequest::Init(nsIURI *aURI,
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;
@ -248,14 +254,21 @@ void imgRequest::Cancel(nsresult aStatus)
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) {
@ -570,6 +583,26 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt
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;
}
@ -644,6 +677,15 @@ struct mimetype_closure
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); */
@ -720,28 +762,14 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
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,
@ -785,6 +813,58 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
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,

View File

@ -10,10 +10,12 @@
#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"
@ -34,19 +36,22 @@ 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,
@ -121,7 +126,8 @@ public:
// 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;
@ -176,11 +182,15 @@ private:
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;
@ -188,8 +198,9 @@ private:
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.

View File

@ -13,6 +13,7 @@
#include "ImageLogging.h"
#include "nsCRTGlue.h"
#include "imgINotificationObserver.h"
#include "nsNetUtil.h"
using namespace mozilla::image;
@ -146,7 +147,7 @@ imgRequestProxy::~imgRequestProxy()
nsresult imgRequestProxy::Init(imgRequest* aOwner,
nsILoadGroup* aLoadGroup,
nsIURI* aURI,
ImageURL* aURI,
imgINotificationObserver* aObserver)
{
NS_PRECONDITION(!GetOwner() && !mListener, "imgRequestProxy is already initialized");
@ -503,6 +504,14 @@ NS_IMETHODIMP imgRequestProxy::GetImageStatus(uint32_t *aStatus)
/* 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;

View File

@ -36,6 +36,7 @@ class ProxyBehaviour;
namespace mozilla {
namespace image {
class Image;
class ImageURL;
} // namespace image
} // namespace mozilla
@ -45,6 +46,7 @@ class imgRequestProxy : public imgIRequest,
public nsITimedChannel
{
public:
typedef mozilla::image::ImageURL ImageURL;
NS_DECL_ISUPPORTS
NS_DECL_IMGIREQUEST
NS_DECL_NSIREQUEST
@ -59,7 +61,7 @@ public:
// (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
@ -106,6 +108,8 @@ public:
virtual nsresult Clone(imgINotificationObserver* aObserver, imgRequestProxy** aClone);
nsresult GetStaticRequest(imgRequestProxy** aReturn);
nsresult GetURI(ImageURL **aURI);
protected:
friend class imgStatusTracker;
friend class imgStatusNotifyRunnable;
@ -194,7 +198,7 @@ private:
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

View File

@ -11,6 +11,7 @@
#include "imgDecoderObserver.h"
#include "Image.h"
#include "ImageLogging.h"
#include "nsNetUtil.h"
#include "nsIObserverService.h"
#include "mozilla/Assertions.h"
@ -391,7 +392,7 @@ 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());
@ -444,7 +445,7 @@ void
imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy)
{
#ifdef PR_LOGGING
nsCOMPtr<nsIURI> uri;
nsRefPtr<ImageURL> uri;
proxy->GetURI(getter_AddRefs(uri));
nsAutoCString spec;
uri->GetSpec(spec);
@ -632,7 +633,7 @@ void
imgStatusTracker::SyncNotify(imgRequestProxy* proxy)
{
#ifdef PR_LOGGING
nsCOMPtr<nsIURI> uri;
nsRefPtr<ImageURL> uri;
proxy->GetURI(getter_AddRefs(uri));
nsAutoCString spec;
uri->GetSpec(spec);
@ -1071,7 +1072,12 @@ imgStatusTracker::FireFailureNotification()
// 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) {

View File

@ -25,6 +25,12 @@ nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsIURI* aURI)
Parse(ref);
}
nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsCString& aRef)
: mClipUnit(eClipUnit_Pixel)
{
Parse(aRef);
}
bool nsMediaFragmentURIParser::ParseNPT(nsDependentSubstring aString)
{
nsDependentSubstring original(aString);

View File

@ -35,6 +35,9 @@ 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(); }