Bug 1081926 - Fallback on a simple image lookup when the normal lookup fails. r=mattwoodrow

This commit is contained in:
Michael Wu 2014-10-29 16:17:00 +01:00
parent 9f20b8bd1c
commit 8429e6cc7f
4 changed files with 129 additions and 1 deletions

View File

@ -82,6 +82,36 @@ public:
nsAutoPtr<ImageCacheEntryData> mData;
};
class SimpleImageCacheEntry : public PLDHashEntryHdr {
public:
typedef imgIRequest& KeyType;
typedef const imgIRequest* KeyTypePointer;
explicit SimpleImageCacheEntry(KeyTypePointer aKey)
: mRequest(const_cast<imgIRequest*>(aKey))
{}
SimpleImageCacheEntry(const SimpleImageCacheEntry &toCopy)
: mRequest(toCopy.mRequest)
, mSourceSurface(toCopy.mSourceSurface)
{}
~SimpleImageCacheEntry() {}
bool KeyEquals(KeyTypePointer key) const
{
return key == mRequest;
}
static KeyTypePointer KeyToPointer(KeyType key) { return &key; }
static PLDHashNumber HashKey(KeyTypePointer key)
{
return NS_PTR_TO_UINT32(key) >> 2;
}
enum { ALLOW_MEMMOVE = true };
nsCOMPtr<imgIRequest> mRequest;
RefPtr<SourceSurface> mSourceSurface;
};
static bool sPrefsInitialized = false;
static int32_t sCanvasImageCacheLimit = 0;
@ -99,10 +129,12 @@ public:
mTotal -= aObject->SizeInBytes();
RemoveObject(aObject);
// Deleting the entry will delete aObject since the entry owns aObject
mSimpleCache.RemoveEntry(*aObject->mRequest);
mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas));
}
nsTHashtable<ImageCacheEntry> mCache;
nsTHashtable<SimpleImageCacheEntry> mSimpleCache;
size_t mTotal;
nsRefPtr<ImageCacheObserver> mImageCacheObserver;
};
@ -230,6 +262,12 @@ CanvasImageCache::NotifyDrawImage(Element* aImage,
entry->mData->mSize = aSize;
gImageCache->mTotal += entry->mData->SizeInBytes();
if (entry->mData->mRequest) {
SimpleImageCacheEntry* simpleentry =
gImageCache->mSimpleCache.PutEntry(*entry->mData->mRequest);
simpleentry->mSourceSurface = aSource;
}
}
if (!sCanvasImageCacheLimit)
@ -263,6 +301,29 @@ CanvasImageCache::Lookup(Element* aImage,
return entry->mData->mSourceSurface;
}
SourceSurface*
CanvasImageCache::SimpleLookup(Element* aImage)
{
if (!gImageCache)
return nullptr;
nsCOMPtr<imgIRequest> request;
nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage);
if (!ilc)
return nullptr;
ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
getter_AddRefs(request));
if (!request)
return nullptr;
SimpleImageCacheEntry* entry = gImageCache->mSimpleCache.GetEntry(*request);
if (!entry)
return nullptr;
return entry->mSourceSurface;
}
NS_IMPL_ISUPPORTS(CanvasImageCacheShutdownObserver, nsIObserver)
NS_IMETHODIMP

View File

@ -44,6 +44,13 @@ public:
static SourceSurface* Lookup(dom::Element* aImage,
dom::HTMLCanvasElement* aCanvas,
gfxIntSize* aSize);
/**
* This is the same as Lookup, except it works on any image recently drawn
* into any canvas. Security checks need to be done again if using the
* results from this.
*/
static SourceSurface* SimpleLookup(dom::Element* aImage);
};
}

View File

@ -3880,6 +3880,56 @@ ExtractSubrect(SourceSurface* aSurface, mgfx::Rect* aSourceRect, DrawTarget* aTa
return subrectDT->Snapshot();
}
// Acts like nsLayoutUtils::SurfaceFromElement, but it'll attempt
// to pull a SourceSurface from our cache. This allows us to avoid
// reoptimizing surfaces if content and canvas backends are different.
nsLayoutUtils::SurfaceFromElementResult
CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement)
{
nsLayoutUtils::SurfaceFromElementResult res;
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
if (!imageLoader) {
return res;
}
nsCOMPtr<imgIRequest> imgRequest;
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
getter_AddRefs(imgRequest));
if (!imgRequest) {
return res;
}
uint32_t status;
if (NS_FAILED(imgRequest->GetImageStatus(&status)) ||
!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
return res;
}
nsCOMPtr<nsIPrincipal> principal;
if (NS_FAILED(imgRequest->GetImagePrincipal(getter_AddRefs(principal))) ||
!principal) {
return res;
}
res.mSourceSurface = CanvasImageCache::SimpleLookup(aElement);
if (!res.mSourceSurface) {
return res;
}
int32_t corsmode = imgIRequest::CORS_NONE;
if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
res.mCORSUsed = corsmode != imgIRequest::CORS_NONE;
}
res.mSize = ThebesIntSize(res.mSourceSurface->GetSize());
res.mPrincipal = principal.forget();
res.mIsWriteOnly = false;
res.mImageRequest = imgRequest.forget();
return res;
}
//
// image
//
@ -3940,8 +3990,15 @@ CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image
// of animated images. We also don't want to rasterize vector images.
uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME |
nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS;
// The cache lookup can miss even if the image is already in the cache
// if the image is coming from a different element or cached for a
// different canvas. This covers the case when we miss due to caching
// for a different canvas, but CanvasImageCache should be fixed if we
// see misses due to different elements drawing the same image.
nsLayoutUtils::SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
CachedSurfaceFromElement(element);
if (!res.mSourceSurface)
res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
// Spec says to silently do nothing if the element is still loading.

View File

@ -714,6 +714,9 @@ protected:
*/
void UpdateFilter();
nsLayoutUtils::SurfaceFromElementResult
CachedSurfaceFromElement(Element* aElement);
void DrawImage(const HTMLImageOrCanvasOrVideoElement &imgElt,
double sx, double sy, double sw, double sh,
double dx, double dy, double dw, double dh,