content/canvas/src/CanvasImageCache.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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

mercurial