gfx/thebes/gfxGradientCache.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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 "mozilla/gfx/2D.h"
     7 #include "nsTArray.h"
     8 #include "pldhash.h"
     9 #include "nsExpirationTracker.h"
    10 #include "nsClassHashtable.h"
    11 #include "mozilla/Telemetry.h"
    12 #include "gfxGradientCache.h"
    13 #include <time.h>
    15 namespace mozilla {
    16 namespace gfx {
    18 using namespace mozilla;
    20 struct GradientCacheKey : public PLDHashEntryHdr {
    21   typedef const GradientCacheKey& KeyType;
    22   typedef const GradientCacheKey* KeyTypePointer;
    23   enum { ALLOW_MEMMOVE = true };
    24   const nsTArray<GradientStop> mStops;
    25   ExtendMode mExtend;
    26   BackendType mBackendType;
    28   GradientCacheKey(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
    29     : mStops(aStops), mExtend(aExtend), mBackendType(aBackendType)
    30   { }
    32   GradientCacheKey(const GradientCacheKey* aOther)
    33     : mStops(aOther->mStops), mExtend(aOther->mExtend), mBackendType(aOther->mBackendType)
    34   { }
    36   union FloatUint32
    37   {
    38     float    f;
    39     uint32_t u;
    40   };
    42   static PLDHashNumber
    43   HashKey(const KeyTypePointer aKey)
    44   {
    45     PLDHashNumber hash = 0;
    46     FloatUint32 convert;
    47     hash = AddToHash(hash, int(aKey->mBackendType));
    48     hash = AddToHash(hash, int(aKey->mExtend));
    49     for (uint32_t i = 0; i < aKey->mStops.Length(); i++) {
    50       hash = AddToHash(hash, aKey->mStops[i].color.ToABGR());
    51       // Use the float bits as hash, except for the cases of 0.0 and -0.0 which both map to 0
    52       convert.f = aKey->mStops[i].offset;
    53       hash = AddToHash(hash, convert.f ? convert.u : 0);
    54     }
    55     return hash;
    56   }
    58   bool KeyEquals(KeyTypePointer aKey) const
    59   {
    60     bool sameStops = true;
    61     if (aKey->mStops.Length() != mStops.Length()) {
    62       sameStops = false;
    63     } else {
    64       for (uint32_t i = 0; i < mStops.Length(); i++) {
    65         if (mStops[i].color.ToABGR() != aKey->mStops[i].color.ToABGR() ||
    66             mStops[i].offset != aKey->mStops[i].offset) {
    67           sameStops = false;
    68           break;
    69         }
    70       }
    71     }
    73     return sameStops &&
    74            (aKey->mBackendType == mBackendType) &&
    75            (aKey->mExtend == mExtend);
    76   }
    77   static KeyTypePointer KeyToPointer(KeyType aKey)
    78   {
    79     return &aKey;
    80   }
    81 };
    83 /**
    84  * This class is what is cached. It need to be allocated in an object separated
    85  * to the cache entry to be able to be tracked by the nsExpirationTracker.
    86  * */
    87 struct GradientCacheData {
    88   GradientCacheData(GradientStops* aStops, const GradientCacheKey& aKey)
    89     : mStops(aStops),
    90       mKey(aKey)
    91   {}
    93   GradientCacheData(const GradientCacheData& aOther)
    94     : mStops(aOther.mStops),
    95       mKey(aOther.mKey)
    96   { }
    98   nsExpirationState *GetExpirationState() {
    99     return &mExpirationState;
   100   }
   102   nsExpirationState mExpirationState;
   103   const RefPtr<GradientStops> mStops;
   104   GradientCacheKey mKey;
   105 };
   107 /**
   108  * This class implements a cache with no maximum size, that retains the
   109  * gfxPatterns used to draw the gradients.
   110  *
   111  * The key is the nsStyleGradient that defines the gradient, and the size of the
   112  * gradient.
   113  *
   114  * The value is the gfxPattern, and whether or not we perform an optimization
   115  * based on the actual gradient property.
   116  *
   117  * An entry stays in the cache as long as it is used often. As long as a cache
   118  * entry is in the cache, all the references it has are guaranteed to be valid:
   119  * the nsStyleRect for the key, the gfxPattern for the value.
   120  */
   121 class GradientCache MOZ_FINAL : public nsExpirationTracker<GradientCacheData,4>
   122 {
   123   public:
   124     GradientCache()
   125       : nsExpirationTracker<GradientCacheData, 4>(MAX_GENERATION_MS)
   126     {
   127       srand(time(nullptr));
   128       mTimerPeriod = rand() % MAX_GENERATION_MS + 1;
   129       Telemetry::Accumulate(Telemetry::GRADIENT_RETENTION_TIME, mTimerPeriod);
   130     }
   132     virtual void NotifyExpired(GradientCacheData* aObject)
   133     {
   134       // This will free the gfxPattern.
   135       RemoveObject(aObject);
   136       mHashEntries.Remove(aObject->mKey);
   137     }
   139     GradientCacheData* Lookup(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
   140     {
   141       GradientCacheData* gradient =
   142         mHashEntries.Get(GradientCacheKey(aStops, aExtend, aBackendType));
   144       if (gradient) {
   145         MarkUsed(gradient);
   146       }
   148       return gradient;
   149     }
   151     // Returns true if we successfully register the gradient in the cache, false
   152     // otherwise.
   153     bool RegisterEntry(GradientCacheData* aValue)
   154     {
   155       nsresult rv = AddObject(aValue);
   156       if (NS_FAILED(rv)) {
   157         // We are OOM, and we cannot track this object. We don't want stall
   158         // entries in the hash table (since the expiration tracker is responsible
   159         // for removing the cache entries), so we avoid putting that entry in the
   160         // table, which is a good things considering we are short on memory
   161         // anyway, we probably don't want to retain things.
   162         return false;
   163       }
   164       mHashEntries.Put(aValue->mKey, aValue);
   165       return true;
   166     }
   168   protected:
   169     uint32_t mTimerPeriod;
   170     static const uint32_t MAX_GENERATION_MS = 10000;
   171     /**
   172      * FIXME use nsTHashtable to avoid duplicating the GradientCacheKey.
   173      * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
   174      */
   175     nsClassHashtable<GradientCacheKey, GradientCacheData> mHashEntries;
   176 };
   178 static GradientCache* gGradientCache = nullptr;
   180 GradientStops *
   181 gfxGradientCache::GetGradientStops(DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
   182 {
   183   if (!gGradientCache) {
   184     gGradientCache = new GradientCache();
   185   }
   186   GradientCacheData* cached = gGradientCache->Lookup(aStops, aExtend, aDT->GetType());
   187   return cached ? cached->mStops : nullptr;
   188 }
   190 GradientStops *
   191 gfxGradientCache::GetOrCreateGradientStops(DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
   192 {
   193   RefPtr<GradientStops> gs = GetGradientStops(aDT, aStops, aExtend);
   194   if (!gs) {
   195     gs = aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
   196     if (!gs) {
   197       return nullptr;
   198     }
   199     GradientCacheData *cached = new GradientCacheData(gs, GradientCacheKey(aStops, aExtend, aDT->GetType()));
   200     if (!gGradientCache->RegisterEntry(cached)) {
   201       delete cached;
   202     }
   203   }
   204   return gs;
   205 }
   207 void
   208 gfxGradientCache::Shutdown()
   209 {
   210   delete gGradientCache;
   211   gGradientCache = nullptr;
   212 }
   214 }
   215 }

mercurial