Skip to content

Commit

Permalink
Bug 1891319. r=gfx-reviewers,lsalzman a=pascalc
Browse files Browse the repository at this point in the history
  • Loading branch information
aosmond committed May 21, 2024
1 parent a5facc5 commit 01d0d6c
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 31 deletions.
64 changes: 44 additions & 20 deletions dom/canvas/CanvasImageCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "imgIRequest.h"
#include "mozilla/dom/Element.h"
#include "nsTHashtable.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "nsContentUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/UniquePtr.h"
Expand All @@ -26,11 +25,11 @@ using namespace gfx;
* due to CORS security.
*/
struct ImageCacheKey {
ImageCacheKey(imgIContainer* aImage, HTMLCanvasElement* aCanvas,
ImageCacheKey(imgIContainer* aImage, CanvasRenderingContext2D* aContext,
BackendType aBackendType)
: mImage(aImage), mCanvas(aCanvas), mBackendType(aBackendType) {}
: mImage(aImage), mContext(aContext), mBackendType(aBackendType) {}
nsCOMPtr<imgIContainer> mImage;
HTMLCanvasElement* mCanvas;
CanvasRenderingContext2D* mContext;
BackendType mBackendType;
};

Expand All @@ -41,23 +40,23 @@ struct ImageCacheKey {
struct ImageCacheEntryData {
ImageCacheEntryData(const ImageCacheEntryData& aOther)
: mImage(aOther.mImage),
mCanvas(aOther.mCanvas),
mContext(aOther.mContext),
mBackendType(aOther.mBackendType),
mSourceSurface(aOther.mSourceSurface),
mSize(aOther.mSize),
mIntrinsicSize(aOther.mIntrinsicSize),
mCropRect(aOther.mCropRect) {}
explicit ImageCacheEntryData(const ImageCacheKey& aKey)
: mImage(aKey.mImage),
mCanvas(aKey.mCanvas),
mContext(aKey.mContext),
mBackendType(aKey.mBackendType) {}

nsExpirationState* GetExpirationState() { return &mState; }
size_t SizeInBytes() { return mSize.width * mSize.height * 4; }

// Key
nsCOMPtr<imgIContainer> mImage;
HTMLCanvasElement* mCanvas;
CanvasRenderingContext2D* mContext;
BackendType mBackendType;
// Value
RefPtr<SourceSurface> mSourceSurface;
Expand All @@ -79,13 +78,13 @@ class ImageCacheEntry : public PLDHashEntryHdr {
~ImageCacheEntry() = default;

bool KeyEquals(KeyTypePointer key) const {
return mData->mImage == key->mImage && mData->mCanvas == key->mCanvas &&
return mData->mImage == key->mImage && mData->mContext == key->mContext &&
mData->mBackendType == key->mBackendType;
}

static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
static PLDHashNumber HashKey(KeyTypePointer key) {
return HashGeneric(key->mImage.get(), key->mCanvas, key->mBackendType);
return HashGeneric(key->mImage.get(), key->mContext, key->mBackendType);
}
enum { ALLOW_MEMMOVE = true };

Expand Down Expand Up @@ -152,7 +151,7 @@ class ImageCache final : public nsExpirationTracker<ImageCacheEntryData, 4> {
AllCanvasImageCacheKey(aObject->mImage, aObject->mBackendType));

// Deleting the entry will delete aObject since the entry owns aObject.
mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas,
mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mContext,
aObject->mBackendType));
}

Expand Down Expand Up @@ -264,11 +263,33 @@ static already_AddRefed<imgIContainer> GetImageContainer(dom::Element* aImage) {
return imgContainer.forget();
}

void CanvasImageCache::NotifyCanvasDestroyed(
CanvasRenderingContext2D* aContext) {
MOZ_ASSERT(aContext);

if (!NS_IsMainThread() || !gImageCache) {
return;
}

for (auto i = gImageCache->mCache.Iter(); !i.Done(); i.Next()) {
ImageCacheEntryData* data = i.Get()->mData.get();
if (data->mContext == aContext) {
gImageCache->RemoveObject(data);
gImageCache->mAllCanvasCache.RemoveEntry(
AllCanvasImageCacheKey(data->mImage, data->mBackendType));
i.Remove();
}
}
}

void CanvasImageCache::NotifyDrawImage(
Element* aImage, HTMLCanvasElement* aCanvas, DrawTarget* aTarget,
Element* aImage, CanvasRenderingContext2D* aContext, DrawTarget* aTarget,
SourceSurface* aSource, const IntSize& aSize, const IntSize& aIntrinsicSize,
const Maybe<IntRect>& aCropRect) {
if (!aTarget) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aContext);

if (!aTarget || !aContext) {
return;
}

Expand All @@ -285,7 +306,7 @@ void CanvasImageCache::NotifyDrawImage(

BackendType backendType = aTarget->GetBackendType();
AllCanvasImageCacheKey allCanvasCacheKey(imgContainer, backendType);
ImageCacheKey canvasCacheKey(imgContainer, aCanvas, backendType);
ImageCacheKey canvasCacheKey(imgContainer, aContext, backendType);
ImageCacheEntry* entry = gImageCache->mCache.PutEntry(canvasCacheKey);

if (entry) {
Expand All @@ -311,6 +332,8 @@ void CanvasImageCache::NotifyDrawImage(

SourceSurface* CanvasImageCache::LookupAllCanvas(Element* aImage,
DrawTarget* aTarget) {
MOZ_ASSERT(NS_IsMainThread());

if (!gImageCache || !aTarget) {
return nullptr;
}
Expand All @@ -329,12 +352,13 @@ SourceSurface* CanvasImageCache::LookupAllCanvas(Element* aImage,
return entry->mSourceSurface;
}

SourceSurface* CanvasImageCache::LookupCanvas(Element* aImage,
HTMLCanvasElement* aCanvas,
DrawTarget* aTarget,
IntSize* aSizeOut,
IntSize* aIntrinsicSizeOut,
Maybe<IntRect>* aCropRectOut) {
SourceSurface* CanvasImageCache::LookupCanvas(
Element* aImage, CanvasRenderingContext2D* aContext, DrawTarget* aTarget,
IntSize* aSizeOut, IntSize* aIntrinsicSizeOut,
Maybe<IntRect>* aCropRectOut) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aContext);

if (!gImageCache || !aTarget) {
return nullptr;
}
Expand All @@ -351,7 +375,7 @@ SourceSurface* CanvasImageCache::LookupCanvas(Element* aImage,
// optimized surface given to a Skia backend would cause a readback. For
// details, see bug 1794442.
ImageCacheEntry* entry = gImageCache->mCache.GetEntry(
ImageCacheKey(imgContainer, aCanvas, aTarget->GetBackendType()));
ImageCacheKey(imgContainer, aContext, aTarget->GetBackendType()));
if (!entry) {
return nullptr;
}
Expand Down
15 changes: 10 additions & 5 deletions dom/canvas/CanvasImageCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace mozilla {
namespace dom {
class Element;
class HTMLCanvasElement;
class CanvasRenderingContext2D;
} // namespace dom
namespace gfx {
class DrawTarget;
Expand All @@ -30,18 +30,23 @@ class CanvasImageCache {

public:
/**
* Notify that image element aImage was drawn to aCanvas element
* Notify that image element aImage was drawn to aContext canvas
* using the first frame of aRequest's image. The data for the surface is
* in aSurface, and the image size is in aSize. aIntrinsicSize is the size
* the surface is intended to be rendered at.
*/
static void NotifyDrawImage(dom::Element* aImage,
dom::HTMLCanvasElement* aCanvas,
dom::CanvasRenderingContext2D* aContext,
gfx::DrawTarget* aTarget, SourceSurface* aSource,
const gfx::IntSize& aSize,
const gfx::IntSize& aIntrinsicSize,
const Maybe<gfx::IntRect>& aCropRect);

/**
* Notify that aContext is being destroyed.
*/
static void NotifyCanvasDestroyed(dom::CanvasRenderingContext2D* aContext);

/**
* Check whether aImage has recently been drawn any canvas. If we return
* a non-null surface, then the same image was recently drawn into a canvas.
Expand All @@ -50,11 +55,11 @@ class CanvasImageCache {
gfx::DrawTarget* aTarget);

/**
* Like the top above, but restricts the lookup to only aCanvas. This is
* Like the top above, but restricts the lookup to only aContext. This is
* required for CORS security.
*/
static SourceSurface* LookupCanvas(dom::Element* aImage,
dom::HTMLCanvasElement* aCanvas,
dom::CanvasRenderingContext2D* aContext,
gfx::DrawTarget* aTarget,
gfx::IntSize* aSizeOut,
gfx::IntSize* aIntrinsicSizeOut,
Expand Down
11 changes: 5 additions & 6 deletions dom/canvas/CanvasRenderingContext2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,7 @@ CanvasRenderingContext2D::CanvasRenderingContext2D(
}

CanvasRenderingContext2D::~CanvasRenderingContext2D() {
CanvasImageCache::NotifyCanvasDestroyed(this);
RemovePostRefreshObserver();
RemoveShutdownObserver();
ResetBitmap();
Expand Down Expand Up @@ -5541,9 +5542,8 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
element = video;
}

srcSurf =
CanvasImageCache::LookupCanvas(element, mCanvasElement, mTarget,
&imgSize, &intrinsicImgSize, &cropRect);
srcSurf = CanvasImageCache::LookupCanvas(element, this, mTarget, &imgSize,
&intrinsicImgSize, &cropRect);
}

DirectDrawInfo drawInfo;
Expand Down Expand Up @@ -5617,9 +5617,8 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,

if (srcSurf) {
if (res.mImageRequest) {
CanvasImageCache::NotifyDrawImage(element, mCanvasElement, mTarget,
srcSurf, imgSize, intrinsicImgSize,
cropRect);
CanvasImageCache::NotifyDrawImage(element, this, mTarget, srcSurf,
imgSize, intrinsicImgSize, cropRect);
}
} else {
drawInfo = res.mDrawInfo;
Expand Down

0 comments on commit 01d0d6c

Please sign in to comment.