Tue, 06 Jan 2015 21:39:09 +0100
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 | } |