Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /*
8 : * cache of re-usable nsCSSRuleProcessors for given sets of style sheets
9 : */
10 :
11 : #include "RuleProcessorCache.h"
12 :
13 : #include <algorithm>
14 : #include "mozilla/CSSStyleSheet.h"
15 : #include "nsCSSRuleProcessor.h"
16 : #include "nsThreadUtils.h"
17 :
18 : using namespace mozilla;
19 :
20 8 : NS_IMPL_ISUPPORTS(RuleProcessorCache, nsIMemoryReporter)
21 :
22 0 : MOZ_DEFINE_MALLOC_SIZE_OF(RuleProcessorCacheMallocSizeOf)
23 :
24 : NS_IMETHODIMP
25 0 : RuleProcessorCache::CollectReports(nsIHandleReportCallback* aHandleReport,
26 : nsISupports* aData, bool aAnonymize)
27 : {
28 0 : MOZ_COLLECT_REPORT(
29 : "explicit/layout/rule-processor-cache", KIND_HEAP, UNITS_BYTES,
30 : SizeOfIncludingThis(RuleProcessorCacheMallocSizeOf),
31 0 : "Memory used for cached rule processors.");
32 :
33 0 : return NS_OK;
34 : }
35 :
36 0 : RuleProcessorCache::~RuleProcessorCache()
37 : {
38 0 : UnregisterWeakMemoryReporter(this);
39 :
40 0 : for (Entry& e : mEntries) {
41 0 : for (DocumentEntry& de : e.mDocumentEntries) {
42 0 : if (de.mRuleProcessor->GetExpirationState()->IsTracked()) {
43 0 : mExpirationTracker.RemoveObject(de.mRuleProcessor);
44 : }
45 0 : de.mRuleProcessor->SetInRuleProcessorCache(false);
46 : }
47 : }
48 0 : }
49 :
50 : void
51 2 : RuleProcessorCache::InitMemoryReporter()
52 : {
53 2 : RegisterWeakMemoryReporter(this);
54 2 : }
55 :
56 : /* static */ bool
57 72 : RuleProcessorCache::EnsureGlobal()
58 : {
59 72 : MOZ_ASSERT(NS_IsMainThread());
60 :
61 72 : if (gShutdown) {
62 0 : return false;
63 : }
64 :
65 72 : if (!gRuleProcessorCache) {
66 2 : gRuleProcessorCache = new RuleProcessorCache;
67 2 : gRuleProcessorCache->InitMemoryReporter();
68 : }
69 72 : return true;
70 : }
71 :
72 : /* static */ void
73 0 : RuleProcessorCache::RemoveSheet(CSSStyleSheet* aSheet)
74 : {
75 0 : if (!EnsureGlobal()) {
76 0 : return;
77 : }
78 0 : gRuleProcessorCache->DoRemoveSheet(aSheet);
79 : }
80 :
81 : #ifdef DEBUG
82 : /* static */ bool
83 15 : RuleProcessorCache::HasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
84 : {
85 15 : if (!EnsureGlobal()) {
86 0 : return false;
87 : }
88 15 : return gRuleProcessorCache->DoHasRuleProcessor(aRuleProcessor);
89 : }
90 : #endif
91 :
92 : /* static */ void
93 0 : RuleProcessorCache::RemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
94 : {
95 0 : if (!EnsureGlobal()) {
96 0 : return;
97 : }
98 0 : gRuleProcessorCache->DoRemoveRuleProcessor(aRuleProcessor);
99 : }
100 :
101 : /* static */ nsCSSRuleProcessor*
102 34 : RuleProcessorCache::GetRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
103 : nsPresContext* aPresContext)
104 : {
105 34 : if (!EnsureGlobal()) {
106 0 : return nullptr;
107 : }
108 34 : return gRuleProcessorCache->DoGetRuleProcessor(aSheets, aPresContext);
109 : }
110 :
111 : /* static */ void
112 10 : RuleProcessorCache::PutRuleProcessor(
113 : const nsTArray<CSSStyleSheet*>& aSheets,
114 : nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
115 : const nsDocumentRuleResultCacheKey& aCacheKey,
116 : nsCSSRuleProcessor* aRuleProcessor)
117 : {
118 10 : if (!EnsureGlobal()) {
119 0 : return;
120 : }
121 10 : gRuleProcessorCache->DoPutRuleProcessor(aSheets, Move(aDocumentRulesInSheets),
122 10 : aCacheKey, aRuleProcessor);
123 : }
124 :
125 : /* static */ void
126 3 : RuleProcessorCache::StartTracking(nsCSSRuleProcessor* aRuleProcessor)
127 : {
128 3 : if (!EnsureGlobal()) {
129 0 : return;
130 : }
131 3 : return gRuleProcessorCache->DoStartTracking(aRuleProcessor);
132 : }
133 :
134 : /* static */ void
135 10 : RuleProcessorCache::StopTracking(nsCSSRuleProcessor* aRuleProcessor)
136 : {
137 10 : if (!EnsureGlobal()) {
138 0 : return;
139 : }
140 10 : return gRuleProcessorCache->DoStopTracking(aRuleProcessor);
141 : }
142 :
143 : void
144 0 : RuleProcessorCache::DoRemoveSheet(CSSStyleSheet* aSheet)
145 : {
146 0 : auto last = std::remove_if(mEntries.begin(), mEntries.end(),
147 0 : HasSheet_ThenRemoveRuleProcessors(this, aSheet));
148 0 : mEntries.TruncateLength(last - mEntries.begin());
149 0 : }
150 :
151 : nsCSSRuleProcessor*
152 34 : RuleProcessorCache::DoGetRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
153 : nsPresContext* aPresContext)
154 : {
155 145 : for (Entry& e : mEntries) {
156 135 : if (e.mSheets == aSheets) {
157 24 : for (DocumentEntry& de : e.mDocumentEntries) {
158 24 : if (de.mCacheKey.Matches(aPresContext, e.mDocumentRulesInSheets)) {
159 24 : return de.mRuleProcessor;
160 : }
161 : }
162 : // Entry::mSheets is unique; if we matched aSheets but didn't
163 : // find a matching DocumentEntry, we won't find one later in
164 : // mEntries.
165 0 : return nullptr;
166 : }
167 : }
168 10 : return nullptr;
169 : }
170 :
171 : void
172 10 : RuleProcessorCache::DoPutRuleProcessor(
173 : const nsTArray<CSSStyleSheet*>& aSheets,
174 : nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
175 : const nsDocumentRuleResultCacheKey& aCacheKey,
176 : nsCSSRuleProcessor* aRuleProcessor)
177 : {
178 10 : MOZ_ASSERT(!aRuleProcessor->IsInRuleProcessorCache());
179 :
180 10 : Entry* entry = nullptr;
181 39 : for (Entry& e : mEntries) {
182 29 : if (e.mSheets == aSheets) {
183 0 : entry = &e;
184 0 : break;
185 : }
186 : }
187 :
188 10 : if (!entry) {
189 10 : entry = mEntries.AppendElement();
190 10 : entry->mSheets = aSheets;
191 10 : entry->mDocumentRulesInSheets = aDocumentRulesInSheets;
192 79 : for (CSSStyleSheet* sheet : aSheets) {
193 69 : sheet->SetInRuleProcessorCache();
194 : }
195 : } else {
196 0 : MOZ_ASSERT(entry->mDocumentRulesInSheets == aDocumentRulesInSheets,
197 : "DocumentRule array shouldn't have changed");
198 : }
199 :
200 : #ifdef DEBUG
201 10 : for (DocumentEntry& de : entry->mDocumentEntries) {
202 0 : MOZ_ASSERT(de.mCacheKey != aCacheKey,
203 : "should not have duplicate document cache keys");
204 : }
205 : #endif
206 :
207 10 : DocumentEntry* documentEntry = entry->mDocumentEntries.AppendElement();
208 10 : documentEntry->mCacheKey = aCacheKey;
209 10 : documentEntry->mRuleProcessor = aRuleProcessor;
210 10 : aRuleProcessor->SetInRuleProcessorCache(true);
211 10 : }
212 :
213 : #ifdef DEBUG
214 : bool
215 15 : RuleProcessorCache::DoHasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
216 : {
217 135 : for (Entry& e : mEntries) {
218 240 : for (DocumentEntry& de : e.mDocumentEntries) {
219 120 : if (de.mRuleProcessor == aRuleProcessor) {
220 0 : return true;
221 : }
222 : }
223 : }
224 15 : return false;
225 : }
226 : #endif
227 :
228 : void
229 0 : RuleProcessorCache::DoRemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
230 : {
231 0 : MOZ_ASSERT(aRuleProcessor->IsInRuleProcessorCache());
232 :
233 0 : aRuleProcessor->SetInRuleProcessorCache(false);
234 0 : mExpirationTracker.RemoveObjectIfTracked(aRuleProcessor);
235 0 : for (Entry& e : mEntries) {
236 0 : for (size_t i = 0; i < e.mDocumentEntries.Length(); i++) {
237 0 : if (e.mDocumentEntries[i].mRuleProcessor == aRuleProcessor) {
238 0 : e.mDocumentEntries.RemoveElementAt(i);
239 0 : return;
240 : }
241 : }
242 : }
243 :
244 0 : MOZ_ASSERT_UNREACHABLE("should have found rule processor");
245 : }
246 :
247 : void
248 3 : RuleProcessorCache::DoStartTracking(nsCSSRuleProcessor* aRuleProcessor)
249 : {
250 3 : mExpirationTracker.AddObject(aRuleProcessor);
251 3 : }
252 :
253 : void
254 10 : RuleProcessorCache::DoStopTracking(nsCSSRuleProcessor* aRuleProcessor)
255 : {
256 10 : mExpirationTracker.RemoveObjectIfTracked(aRuleProcessor);
257 10 : }
258 :
259 : size_t
260 0 : RuleProcessorCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
261 : {
262 0 : size_t n = aMallocSizeOf(this);
263 :
264 0 : int count = 0;
265 0 : n += mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
266 0 : for (Entry& e : mEntries) {
267 0 : n += e.mDocumentEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
268 0 : for (DocumentEntry& de : e.mDocumentEntries) {
269 0 : count++;
270 0 : n += de.mRuleProcessor->SizeOfIncludingThis(aMallocSizeOf);
271 : }
272 : }
273 :
274 0 : return n;
275 : }
276 :
277 : void
278 10 : RuleProcessorCache::ExpirationTracker::RemoveObjectIfTracked(
279 : nsCSSRuleProcessor* aRuleProcessor)
280 : {
281 10 : if (aRuleProcessor->GetExpirationState()->IsTracked()) {
282 0 : RemoveObject(aRuleProcessor);
283 : }
284 10 : }
285 :
286 : bool RuleProcessorCache::gShutdown = false;
287 3 : mozilla::StaticRefPtr<RuleProcessorCache> RuleProcessorCache::gRuleProcessorCache;
|