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.

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

mercurial