mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 638241. Use a cache to avoid redoing SurfaceForElement and DoDrawImageSecurityCheck. r=bzbarsky
This commit is contained in:
parent
c83de15a66
commit
0788ccd0dd
196
content/canvas/src/CanvasImageCache.cpp
Normal file
196
content/canvas/src/CanvasImageCache.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Corporation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Robert O'Callahan <robert@ocallahan.org>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "CanvasImageCache.h"
|
||||
#include "nsIImageLoadingContent.h"
|
||||
#include "nsExpirationTracker.h"
|
||||
#include "imgIRequest.h"
|
||||
#include "gfxASurface.h"
|
||||
#include "gfxPoint.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsHTMLCanvasElement.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct ImageCacheKey {
|
||||
ImageCacheKey(nsIDOMElement* aImage, nsHTMLCanvasElement* aCanvas)
|
||||
: mImage(aImage), mCanvas(aCanvas) {}
|
||||
nsIDOMElement* mImage;
|
||||
nsHTMLCanvasElement* mCanvas;
|
||||
};
|
||||
|
||||
struct ImageCacheEntryData {
|
||||
ImageCacheEntryData(const ImageCacheEntryData& aOther)
|
||||
: mImage(aOther.mImage)
|
||||
, mCanvas(aOther.mCanvas)
|
||||
, mRequest(aOther.mRequest)
|
||||
, mSurface(aOther.mSurface)
|
||||
, mSize(aOther.mSize)
|
||||
{}
|
||||
ImageCacheEntryData(const ImageCacheKey& aKey)
|
||||
: mImage(aKey.mImage)
|
||||
, mCanvas(aKey.mCanvas)
|
||||
{}
|
||||
|
||||
nsExpirationState* GetExpirationState() { return &mState; }
|
||||
|
||||
// Key
|
||||
nsCOMPtr<nsIDOMElement> mImage;
|
||||
nsRefPtr<nsHTMLCanvasElement> mCanvas;
|
||||
// Value
|
||||
nsCOMPtr<imgIRequest> mRequest;
|
||||
nsRefPtr<gfxASurface> mSurface;
|
||||
gfxIntSize mSize;
|
||||
nsExpirationState mState;
|
||||
};
|
||||
|
||||
class ImageCacheEntry : public PLDHashEntryHdr {
|
||||
public:
|
||||
typedef ImageCacheKey KeyType;
|
||||
typedef const ImageCacheKey* KeyTypePointer;
|
||||
|
||||
ImageCacheEntry(const KeyType *key) :
|
||||
mData(new ImageCacheEntryData(*key)) {}
|
||||
ImageCacheEntry(const ImageCacheEntry &toCopy) :
|
||||
mData(new ImageCacheEntryData(*toCopy.mData)) {}
|
||||
~ImageCacheEntry() {}
|
||||
|
||||
PRBool KeyEquals(KeyTypePointer key) const
|
||||
{
|
||||
return mData->mImage == key->mImage && mData->mCanvas == key->mCanvas;
|
||||
}
|
||||
|
||||
static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
|
||||
static PLDHashNumber HashKey(KeyTypePointer key)
|
||||
{
|
||||
return (NS_PTR_TO_INT32(key->mImage) ^ NS_PTR_TO_INT32(key->mCanvas)) >> 2;
|
||||
}
|
||||
enum { ALLOW_MEMMOVE = PR_TRUE };
|
||||
|
||||
nsAutoPtr<ImageCacheEntryData> mData;
|
||||
};
|
||||
|
||||
class ImageCache : public nsExpirationTracker<ImageCacheEntryData,4> {
|
||||
public:
|
||||
// We use 3 generations of 1 second each to get a 2-3 seconds timeout.
|
||||
enum { GENERATION_MS = 1000 };
|
||||
ImageCache()
|
||||
: nsExpirationTracker<ImageCacheEntryData,4>(GENERATION_MS)
|
||||
{
|
||||
mCache.Init();
|
||||
}
|
||||
~ImageCache() {
|
||||
AgeAllGenerations();
|
||||
}
|
||||
|
||||
virtual void NotifyExpired(ImageCacheEntryData* aObject)
|
||||
{
|
||||
RemoveObject(aObject);
|
||||
// Deleting the entry will delete aObject since the entry owns aObject
|
||||
mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas));
|
||||
}
|
||||
|
||||
nsTHashtable<ImageCacheEntry> mCache;
|
||||
};
|
||||
|
||||
static ImageCache* gImageCache = nsnull;
|
||||
|
||||
void
|
||||
CanvasImageCache::NotifyDrawImage(nsIDOMElement* aImage,
|
||||
nsHTMLCanvasElement* aCanvas,
|
||||
imgIRequest* aRequest,
|
||||
gfxASurface* aSurface,
|
||||
const gfxIntSize& aSize)
|
||||
{
|
||||
if (!gImageCache) {
|
||||
gImageCache = new ImageCache();
|
||||
}
|
||||
|
||||
ImageCacheEntry* entry = gImageCache->mCache.PutEntry(ImageCacheKey(aImage, aCanvas));
|
||||
if (entry) {
|
||||
if (entry->mData->mSurface) {
|
||||
// We are overwriting an existing entry.
|
||||
gImageCache->RemoveObject(entry->mData);
|
||||
}
|
||||
gImageCache->AddObject(entry->mData);
|
||||
|
||||
nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage);
|
||||
if (ilc) {
|
||||
ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
||||
getter_AddRefs(entry->mData->mRequest));
|
||||
}
|
||||
entry->mData->mSurface = aSurface;
|
||||
entry->mData->mSize = aSize;
|
||||
}
|
||||
}
|
||||
|
||||
gfxASurface*
|
||||
CanvasImageCache::Lookup(nsIDOMElement* aImage,
|
||||
nsHTMLCanvasElement* aCanvas,
|
||||
gfxIntSize* aSize)
|
||||
{
|
||||
if (!gImageCache)
|
||||
return nsnull;
|
||||
|
||||
ImageCacheEntry* entry = gImageCache->mCache.GetEntry(ImageCacheKey(aImage, aCanvas));
|
||||
if (!entry)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage);
|
||||
if (!ilc)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<imgIRequest> request;
|
||||
ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(request));
|
||||
if (request != entry->mData->mRequest)
|
||||
return nsnull;
|
||||
|
||||
gImageCache->MarkUsed(entry->mData);
|
||||
|
||||
*aSize = entry->mData->mSize;
|
||||
return entry->mData->mSurface;
|
||||
}
|
||||
|
||||
void
|
||||
CanvasImageCache::Shutdown()
|
||||
{
|
||||
delete gImageCache;
|
||||
gImageCache = nsnull;
|
||||
}
|
||||
|
||||
}
|
77
content/canvas/src/CanvasImageCache.h
Normal file
77
content/canvas/src/CanvasImageCache.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Corporation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Robert O'Callahan <robert@ocallahan.org>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef CANVASIMAGECACHE_H_
|
||||
#define CANVASIMAGECACHE_H_
|
||||
|
||||
class nsIDOMElement;
|
||||
class nsHTMLCanvasElement;
|
||||
class imgIRequest;
|
||||
class gfxASurface;
|
||||
struct gfxIntSize;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CanvasImageCache {
|
||||
public:
|
||||
/**
|
||||
* Notify that image element aImage was (or is about to be) drawn to aCanvas
|
||||
* using the first frame of aRequest's image. The data for the surface is
|
||||
* in aSurface, and the image size is in aSize.
|
||||
*/
|
||||
static void NotifyDrawImage(nsIDOMElement* aImage,
|
||||
nsHTMLCanvasElement* aCanvas,
|
||||
imgIRequest* aRequest,
|
||||
gfxASurface* aSurface,
|
||||
const gfxIntSize& aSize);
|
||||
|
||||
/**
|
||||
* Check whether aImage has recently been drawn into aCanvas. If we return
|
||||
* a non-null surface, then the image was recently drawn into the canvas
|
||||
* (with the same image request) and the returned surface contains the image
|
||||
* data, and the image size will be returned in aSize.
|
||||
*/
|
||||
static gfxASurface* Lookup(nsIDOMElement* aImage,
|
||||
nsHTMLCanvasElement* aCanvas,
|
||||
gfxIntSize* aSize);
|
||||
|
||||
static void Shutdown();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CANVASIMAGECACHE_H_ */
|
@ -52,6 +52,7 @@ EXPORTS = \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
CanvasImageCache.cpp \
|
||||
CanvasUtils.cpp \
|
||||
nsCanvasRenderingContext2D.cpp \
|
||||
$(NULL)
|
||||
|
@ -111,18 +111,13 @@
|
||||
#include "gfxUtils.h"
|
||||
|
||||
#include "nsFrameManager.h"
|
||||
|
||||
#include "nsFrameLoader.h"
|
||||
|
||||
#include "nsBidiPresUtils.h"
|
||||
|
||||
#include "Layers.h"
|
||||
|
||||
#include "CanvasUtils.h"
|
||||
|
||||
#include "nsIMemoryReporter.h"
|
||||
|
||||
#include "nsStyleUtil.h"
|
||||
#include "CanvasImageCache.h"
|
||||
|
||||
#ifdef MOZ_IPC
|
||||
# include <algorithm>
|
||||
@ -3324,35 +3319,45 @@ nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1,
|
||||
gfxMatrix matrix;
|
||||
nsRefPtr<gfxPattern> pattern;
|
||||
nsRefPtr<gfxPath> path;
|
||||
gfxIntSize imgSize;
|
||||
nsRefPtr<gfxASurface> imgsurf =
|
||||
CanvasImageCache::Lookup(imgElt, HTMLCanvasElement(), &imgSize);
|
||||
|
||||
// The canvas spec says that drawImage should draw the first frame
|
||||
// of animated images
|
||||
PRUint32 sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
|
||||
nsLayoutUtils::SurfaceFromElementResult res =
|
||||
nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags);
|
||||
if (!res.mSurface) {
|
||||
// Spec says to silently do nothing if the element is still loading.
|
||||
return res.mIsStillLoading ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if (!imgsurf) {
|
||||
// The canvas spec says that drawImage should draw the first frame
|
||||
// of animated images
|
||||
PRUint32 sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
|
||||
nsLayoutUtils::SurfaceFromElementResult res =
|
||||
nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags);
|
||||
if (!res.mSurface) {
|
||||
// Spec says to silently do nothing if the element is still loading.
|
||||
return res.mIsStillLoading ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
#ifndef WINCE
|
||||
// On non-CE, force a copy if we're using drawImage with our destination
|
||||
// as a source to work around some Cairo self-copy semantics issues.
|
||||
if (res.mSurface == mSurface) {
|
||||
sfeFlags |= nsLayoutUtils::SFE_WANT_NEW_SURFACE;
|
||||
res = nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags);
|
||||
if (!res.mSurface)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
// On non-CE, force a copy if we're using drawImage with our destination
|
||||
// as a source to work around some Cairo self-copy semantics issues.
|
||||
if (res.mSurface == mSurface) {
|
||||
sfeFlags |= nsLayoutUtils::SFE_WANT_NEW_SURFACE;
|
||||
res = nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags);
|
||||
if (!res.mSurface)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsRefPtr<gfxASurface> imgsurf = res.mSurface;
|
||||
nsCOMPtr<nsIPrincipal> principal = res.mPrincipal;
|
||||
gfxIntSize imgSize = res.mSize;
|
||||
PRBool forceWriteOnly = res.mIsWriteOnly;
|
||||
imgsurf = res.mSurface.forget();
|
||||
imgSize = res.mSize;
|
||||
|
||||
if (mCanvasElement)
|
||||
CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(), principal, forceWriteOnly);
|
||||
if (mCanvasElement) {
|
||||
CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
|
||||
res.mPrincipal, res.mIsWriteOnly);
|
||||
}
|
||||
|
||||
if (res.mImageRequest) {
|
||||
CanvasImageCache::NotifyDrawImage(imgElt, HTMLCanvasElement(),
|
||||
res.mImageRequest, imgsurf, imgSize);
|
||||
}
|
||||
}
|
||||
|
||||
gfxContextPathAutoSaveRestore pathSR(mThebes, PR_FALSE);
|
||||
|
||||
|
@ -3767,8 +3767,6 @@ nsLayoutUtils::SurfaceFromElement(nsIDOMElement *aElement,
|
||||
gfxUtils::UnpremultiplyImageSurface(static_cast<gfxImageSurface*>(surf.get()));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = node->NodePrincipal();
|
||||
|
||||
result.mSurface = surf;
|
||||
result.mSize = size;
|
||||
result.mPrincipal = node->NodePrincipal();
|
||||
@ -3821,7 +3819,7 @@ nsLayoutUtils::SurfaceFromElement(nsIDOMElement *aElement,
|
||||
|
||||
result.mSurface = surf;
|
||||
result.mSize = size;
|
||||
result.mPrincipal = principal;
|
||||
result.mPrincipal = principal.forget();
|
||||
result.mIsWriteOnly = PR_FALSE;
|
||||
|
||||
return result;
|
||||
@ -3926,13 +3924,13 @@ nsLayoutUtils::SurfaceFromElement(nsIDOMElement *aElement,
|
||||
|
||||
result.mSurface = gfxsurf;
|
||||
result.mSize = gfxIntSize(imgWidth, imgHeight);
|
||||
result.mPrincipal = principal;
|
||||
|
||||
result.mPrincipal = principal.forget();
|
||||
// SVG images could have <foreignObject> and/or <image> elements that load
|
||||
// content from another domain. For safety, they make the canvas write-only.
|
||||
// XXXdholbert We could probably be more permissive here if we check that our
|
||||
// helper SVG document has no elements that could load remote content.
|
||||
result.mIsWriteOnly = (imgContainer->GetType() == imgIContainer::TYPE_VECTOR);
|
||||
result.mImageRequest = imgRequest.forget();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1271,7 +1271,7 @@ public:
|
||||
};
|
||||
|
||||
struct SurfaceFromElementResult {
|
||||
SurfaceFromElementResult() : mIsStillLoading(PR_FALSE) {}
|
||||
SurfaceFromElementResult() : mIsWriteOnly(PR_TRUE), mIsStillLoading(PR_FALSE) {}
|
||||
|
||||
/* mSurface will contain the resulting surface, or will be NULL on error */
|
||||
nsRefPtr<gfxASurface> mSurface;
|
||||
@ -1279,11 +1279,13 @@ public:
|
||||
gfxIntSize mSize;
|
||||
/* The principal associated with the element whose surface was returned */
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
/* The image request, if the element is an nsIImageLoadingContent */
|
||||
nsCOMPtr<imgIRequest> mImageRequest;
|
||||
/* Whether the element was "write only", that is, the bits should not be exposed to content */
|
||||
PRBool mIsWriteOnly;
|
||||
PRPackedBool mIsWriteOnly;
|
||||
/* Whether the element was still loading. Some consumers need to handle
|
||||
this case specially. */
|
||||
PRBool mIsStillLoading;
|
||||
PRPackedBool mIsStillLoading;
|
||||
};
|
||||
|
||||
static SurfaceFromElementResult SurfaceFromElement(nsIDOMElement *aElement,
|
||||
|
@ -322,6 +322,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \
|
||||
-I$(srcdir)/../xul/content/src \
|
||||
-I$(srcdir)/../xul/base/src \
|
||||
-I$(topsrcdir)/content/base/src \
|
||||
-I$(topsrcdir)/content/canvas/src \
|
||||
-I$(topsrcdir)/content/html/content/src \
|
||||
-I$(topsrcdir)/content/html/document/src \
|
||||
-I$(topsrcdir)/content/html/style/src \
|
||||
|
@ -129,9 +129,12 @@
|
||||
#include "nsContentSink.h"
|
||||
#include "nsFrameMessageManager.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
#include "CanvasImageCache.h"
|
||||
|
||||
extern void NS_ShutdownChainItemPool();
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsrefcnt nsLayoutStatics::sLayoutStaticRefcnt = 0;
|
||||
|
||||
nsresult
|
||||
@ -292,6 +295,7 @@ nsLayoutStatics::Initialize()
|
||||
void
|
||||
nsLayoutStatics::Shutdown()
|
||||
{
|
||||
CanvasImageCache::Shutdown();
|
||||
nsFrameScriptExecutor::Shutdown();
|
||||
nsFocusManager::Shutdown();
|
||||
#ifdef MOZ_XUL
|
||||
|
Loading…
Reference in New Issue
Block a user