Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "CanvasImageCache.h"
7 #include "nsIImageLoadingContent.h"
8 #include "nsExpirationTracker.h"
9 #include "imgIRequest.h"
10 #include "mozilla/dom/Element.h"
11 #include "nsTHashtable.h"
12 #include "mozilla/dom/HTMLCanvasElement.h"
13 #include "nsContentUtils.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/gfx/2D.h"
17 namespace mozilla {
19 using namespace dom;
20 using namespace gfx;
22 struct ImageCacheKey {
23 ImageCacheKey(Element* aImage, HTMLCanvasElement* aCanvas)
24 : mImage(aImage), mCanvas(aCanvas) {}
25 Element* mImage;
26 HTMLCanvasElement* mCanvas;
27 };
29 struct ImageCacheEntryData {
30 ImageCacheEntryData(const ImageCacheEntryData& aOther)
31 : mImage(aOther.mImage)
32 , mILC(aOther.mILC)
33 , mCanvas(aOther.mCanvas)
34 , mRequest(aOther.mRequest)
35 , mSourceSurface(aOther.mSourceSurface)
36 , mSize(aOther.mSize)
37 {}
38 ImageCacheEntryData(const ImageCacheKey& aKey)
39 : mImage(aKey.mImage)
40 , mILC(nullptr)
41 , mCanvas(aKey.mCanvas)
42 {}
44 nsExpirationState* GetExpirationState() { return &mState; }
46 size_t SizeInBytes() { return mSize.width * mSize.height * 4; }
48 // Key
49 nsRefPtr<Element> mImage;
50 nsIImageLoadingContent* mILC;
51 nsRefPtr<HTMLCanvasElement> mCanvas;
52 // Value
53 nsCOMPtr<imgIRequest> mRequest;
54 RefPtr<SourceSurface> mSourceSurface;
55 gfxIntSize mSize;
56 nsExpirationState mState;
57 };
59 class ImageCacheEntry : public PLDHashEntryHdr {
60 public:
61 typedef ImageCacheKey KeyType;
62 typedef const ImageCacheKey* KeyTypePointer;
64 ImageCacheEntry(const KeyType *key) :
65 mData(new ImageCacheEntryData(*key)) {}
66 ImageCacheEntry(const ImageCacheEntry &toCopy) :
67 mData(new ImageCacheEntryData(*toCopy.mData)) {}
68 ~ImageCacheEntry() {}
70 bool KeyEquals(KeyTypePointer key) const
71 {
72 return mData->mImage == key->mImage && mData->mCanvas == key->mCanvas;
73 }
75 static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
76 static PLDHashNumber HashKey(KeyTypePointer key)
77 {
78 return HashGeneric(key->mImage, key->mCanvas);
79 }
80 enum { ALLOW_MEMMOVE = true };
82 nsAutoPtr<ImageCacheEntryData> mData;
83 };
85 static bool sPrefsInitialized = false;
86 static int32_t sCanvasImageCacheLimit = 0;
88 class ImageCache MOZ_FINAL : public nsExpirationTracker<ImageCacheEntryData,4> {
89 public:
90 // We use 3 generations of 1 second each to get a 2-3 seconds timeout.
91 enum { GENERATION_MS = 1000 };
92 ImageCache()
93 : nsExpirationTracker<ImageCacheEntryData,4>(GENERATION_MS)
94 , mTotal(0)
95 {
96 if (!sPrefsInitialized) {
97 sPrefsInitialized = true;
98 Preferences::AddIntVarCache(&sCanvasImageCacheLimit, "canvas.image.cache.limit", 0);
99 }
100 }
101 ~ImageCache() {
102 AgeAllGenerations();
103 }
105 virtual void NotifyExpired(ImageCacheEntryData* aObject)
106 {
107 mTotal -= aObject->SizeInBytes();
108 RemoveObject(aObject);
109 // Deleting the entry will delete aObject since the entry owns aObject
110 mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas));
111 }
113 nsTHashtable<ImageCacheEntry> mCache;
114 size_t mTotal;
115 };
117 static ImageCache* gImageCache = nullptr;
119 class CanvasImageCacheShutdownObserver MOZ_FINAL : public nsIObserver
120 {
121 public:
122 NS_DECL_ISUPPORTS
123 NS_DECL_NSIOBSERVER
124 };
126 void
127 CanvasImageCache::NotifyDrawImage(Element* aImage,
128 HTMLCanvasElement* aCanvas,
129 imgIRequest* aRequest,
130 SourceSurface* aSource,
131 const gfxIntSize& aSize)
132 {
133 if (!gImageCache) {
134 gImageCache = new ImageCache();
135 nsContentUtils::RegisterShutdownObserver(new CanvasImageCacheShutdownObserver());
136 }
138 ImageCacheEntry* entry = gImageCache->mCache.PutEntry(ImageCacheKey(aImage, aCanvas));
139 if (entry) {
140 if (entry->mData->mSourceSurface) {
141 // We are overwriting an existing entry.
142 gImageCache->mTotal -= entry->mData->SizeInBytes();
143 gImageCache->RemoveObject(entry->mData);
144 }
145 gImageCache->AddObject(entry->mData);
147 nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage);
148 if (ilc) {
149 ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
150 getter_AddRefs(entry->mData->mRequest));
151 }
152 entry->mData->mILC = ilc;
153 entry->mData->mSourceSurface = aSource;
154 entry->mData->mSize = aSize;
156 gImageCache->mTotal += entry->mData->SizeInBytes();
157 }
159 if (!sCanvasImageCacheLimit)
160 return;
162 // Expire the image cache early if its larger than we want it to be.
163 while (gImageCache->mTotal > size_t(sCanvasImageCacheLimit))
164 gImageCache->AgeOneGeneration();
165 }
167 SourceSurface*
168 CanvasImageCache::Lookup(Element* aImage,
169 HTMLCanvasElement* aCanvas,
170 gfxIntSize* aSize)
171 {
172 if (!gImageCache)
173 return nullptr;
175 ImageCacheEntry* entry = gImageCache->mCache.GetEntry(ImageCacheKey(aImage, aCanvas));
176 if (!entry || !entry->mData->mILC)
177 return nullptr;
179 nsCOMPtr<imgIRequest> request;
180 entry->mData->mILC->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(request));
181 if (request != entry->mData->mRequest)
182 return nullptr;
184 gImageCache->MarkUsed(entry->mData);
186 *aSize = entry->mData->mSize;
187 return entry->mData->mSourceSurface;
188 }
190 NS_IMPL_ISUPPORTS(CanvasImageCacheShutdownObserver, nsIObserver)
192 NS_IMETHODIMP
193 CanvasImageCacheShutdownObserver::Observe(nsISupports *aSubject,
194 const char *aTopic,
195 const char16_t *aData)
196 {
197 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
198 delete gImageCache;
199 gImageCache = nullptr;
201 nsContentUtils::UnregisterShutdownObserver(this);
202 }
204 return NS_OK;
205 }
207 } // namespace mozilla