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 #include "SharedStyleSheetCache.h"
8
9 #include "mozilla/MemoryReporting.h"
10 #include "mozilla/StoragePrincipalHelper.h"
11 #include "mozilla/StyleSheet.h"
12 #include "mozilla/css/SheetLoadData.h"
13 #include "mozilla/dom/ContentParent.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/ServoBindings.h"
16 #include "nsContentUtils.h"
17 #include "nsXULPrototypeCache.h"
18
19 extern mozilla::LazyLogModule sCssLoaderLog;
20
21 #define LOG(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Debug, args)
22
23 namespace mozilla {
24
25 using css::SheetLoadData;
26 using SheetState = css::Loader::SheetState;
27 using LoadDataArray = css::Loader::LoadDataArray;
28 using IsAlternate = css::Loader::IsAlternate;
29
30 SharedStyleSheetCache* SharedStyleSheetCache::sInstance;
31
Clear(nsIPrincipal * aForPrincipal,const nsACString * aBaseDomain)32 void SharedStyleSheetCache::Clear(nsIPrincipal* aForPrincipal,
33 const nsACString* aBaseDomain) {
34 using ContentParent = dom::ContentParent;
35
36 if (XRE_IsParentProcess()) {
37 auto forPrincipal = aForPrincipal ? Some(RefPtr(aForPrincipal)) : Nothing();
38 auto baseDomain = aBaseDomain ? Some(nsCString(*aBaseDomain)) : Nothing();
39
40 for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
41 Unused << cp->SendClearStyleSheetCache(forPrincipal, baseDomain);
42 }
43 }
44
45 if (!sInstance) {
46 return;
47 }
48
49 // No filter, clear all.
50 if (!aForPrincipal && !aBaseDomain) {
51 sInstance->mCompleteSheets.Clear();
52 return;
53 }
54
55 for (auto iter = sInstance->mCompleteSheets.Iter(); !iter.Done();
56 iter.Next()) {
57 const bool shouldRemove = [&] {
58 if (aForPrincipal && iter.Key().Principal()->Equals(aForPrincipal)) {
59 return true;
60 }
61 if (!aBaseDomain) {
62 return false;
63 }
64 // Clear by baseDomain.
65 nsIPrincipal* partitionPrincipal = iter.Key().PartitionPrincipal();
66
67 // Clear entries with matching base domain. This includes entries
68 // which are partitioned under other top level sites (= have a
69 // partitionKey set).
70 nsAutoCString principalBaseDomain;
71 nsresult rv = partitionPrincipal->GetBaseDomain(principalBaseDomain);
72 if (NS_SUCCEEDED(rv) && principalBaseDomain.Equals(*aBaseDomain)) {
73 return true;
74 }
75
76 // Clear entries partitioned under aBaseDomain.
77 return StoragePrincipalHelper::PartitionKeyHasBaseDomain(
78 partitionPrincipal->OriginAttributesRef().mPartitionKey,
79 *aBaseDomain);
80 }();
81
82 if (shouldRemove) {
83 iter.Remove();
84 }
85 }
86 }
87
Create()88 already_AddRefed<SharedStyleSheetCache> SharedStyleSheetCache::Create() {
89 MOZ_DIAGNOSTIC_ASSERT(!sInstance);
90 RefPtr<SharedStyleSheetCache> cache = new SharedStyleSheetCache();
91 sInstance = cache.get();
92 RegisterWeakMemoryReporter(cache.get());
93 return cache.forget();
94 }
95
~SharedStyleSheetCache()96 SharedStyleSheetCache::~SharedStyleSheetCache() {
97 MOZ_DIAGNOSTIC_ASSERT(sInstance == this);
98 UnregisterWeakMemoryReporter(this);
99 sInstance = nullptr;
100 }
101
NS_IMPL_ISUPPORTS(SharedStyleSheetCache,nsIMemoryReporter)102 NS_IMPL_ISUPPORTS(SharedStyleSheetCache, nsIMemoryReporter)
103
104 MOZ_DEFINE_MALLOC_SIZE_OF(SharedStyleSheetCacheMallocSizeOf)
105
106 NS_IMETHODIMP
107 SharedStyleSheetCache::CollectReports(nsIHandleReportCallback* aHandleReport,
108 nsISupports* aData, bool aAnonymize) {
109 MOZ_COLLECT_REPORT("explicit/layout/style-sheet-cache/document-shared",
110 KIND_HEAP, UNITS_BYTES,
111 SizeOfIncludingThis(SharedStyleSheetCacheMallocSizeOf),
112 "Memory used for SharedStyleSheetCache to share style "
113 "sheets across documents (not to be confused with "
114 "GlobalStyleSheetCache)");
115 return NS_OK;
116 }
117
CloneSheet(StyleSheet & aSheet)118 static RefPtr<StyleSheet> CloneSheet(StyleSheet& aSheet) {
119 return aSheet.Clone(nullptr, nullptr, nullptr, nullptr);
120 }
121
AssertComplete(const StyleSheet & aSheet)122 static void AssertComplete(const StyleSheet& aSheet) {
123 // This sheet came from the XUL cache or SharedStyleSheetCache; it better be a
124 // complete sheet.
125 MOZ_ASSERT(aSheet.IsComplete(),
126 "Sheet thinks it's not complete while we think it is");
127 }
128
AssertIncompleteSheetMatches(const SheetLoadData & aData,const SheetLoadDataHashKey & aKey)129 static void AssertIncompleteSheetMatches(const SheetLoadData& aData,
130 const SheetLoadDataHashKey& aKey) {
131 MOZ_ASSERT(aKey.Principal()->Equals(aData.mTriggeringPrincipal),
132 "Principals should be the same");
133 MOZ_ASSERT(!aData.mSheet->HasForcedUniqueInner(),
134 "CSSOM shouldn't allow access to incomplete sheets");
135 }
136
Expired() const137 bool SharedStyleSheetCache::CompleteSheet::Expired() const {
138 return mExpirationTime &&
139 mExpirationTime <= nsContentUtils::SecondsFromPRTime(PR_Now());
140 }
141
Lookup(css::Loader & aLoader,const SheetLoadDataHashKey & aKey,bool aSyncLoad)142 SharedStyleSheetCache::CacheResult SharedStyleSheetCache::Lookup(
143 css::Loader& aLoader, const SheetLoadDataHashKey& aKey, bool aSyncLoad) {
144 nsIURI* uri = aKey.URI();
145 LOG(("SharedStyleSheetCache::Lookup(%s)", uri->GetSpecOrDefault().get()));
146
147 // Try to find first in the XUL prototype cache.
148 if (dom::IsChromeURI(uri)) {
149 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
150 if (cache && cache->IsEnabled()) {
151 if (StyleSheet* sheet = cache->GetStyleSheet(uri)) {
152 LOG((" From XUL cache: %p", sheet));
153 AssertComplete(*sheet);
154
155 // See below, we always clone on insertion so we can guarantee the
156 // stylesheet is not modified.
157 MOZ_ASSERT(!sheet->HasForcedUniqueInner());
158
159 // We need to check the parsing mode manually because the XUL cache only
160 // keys off the URI. But we should fix that!
161 if (sheet->ParsingMode() == aKey.ParsingMode()) {
162 aLoader.DidHitCompleteSheetCache(aKey, nullptr);
163 return {CloneSheet(*sheet), SheetState::Complete};
164 }
165
166 LOG((" Not cloning due to mismatched parsing mode"));
167 }
168 }
169 }
170
171 // Now complete sheets.
172 if (auto lookup = mCompleteSheets.Lookup(aKey)) {
173 const CompleteSheet& completeSheet = lookup.Data();
174 // We can assert the stylesheet has not been modified, as we clone it on
175 // insertion.
176 StyleSheet& cachedSheet = *completeSheet.mSheet;
177 LOG((" From completed: %p, bypass: %d, expired: %d", &cachedSheet,
178 aLoader.ShouldBypassCache(), completeSheet.Expired()));
179
180 if ((!aLoader.ShouldBypassCache() && !completeSheet.Expired()) ||
181 aLoader.mLoadsPerformed.Contains(aKey)) {
182 LOG(
183 (" Not expired yet, or previously loaded already in "
184 "that document"));
185
186 AssertComplete(cachedSheet);
187 MOZ_ASSERT(cachedSheet.ParsingMode() == aKey.ParsingMode());
188 MOZ_ASSERT(!cachedSheet.HasForcedUniqueInner());
189 MOZ_ASSERT(!cachedSheet.HasModifiedRules());
190
191 RefPtr<StyleSheet> clone = CloneSheet(cachedSheet);
192 MOZ_ASSERT(!clone->HasForcedUniqueInner());
193 MOZ_ASSERT(!clone->HasModifiedRules());
194
195 aLoader.DidHitCompleteSheetCache(aKey, completeSheet.mUseCounters.get());
196 return {std::move(clone), SheetState::Complete};
197 }
198 }
199
200 if (aSyncLoad) {
201 return {};
202 }
203
204 if (SheetLoadData* data = mLoadingDatas.Get(aKey)) {
205 LOG((" From loading: %p", data->mSheet.get()));
206 AssertIncompleteSheetMatches(*data, aKey);
207 return {CloneSheet(*data->mSheet), SheetState::Loading};
208 }
209
210 if (SheetLoadData* data = mPendingDatas.GetWeak(aKey)) {
211 LOG((" From pending: %p", data->mSheet.get()));
212 AssertIncompleteSheetMatches(*data, aKey);
213 return {CloneSheet(*data->mSheet), SheetState::Pending};
214 }
215
216 return {};
217 }
218
WillStartPendingLoad(SheetLoadData & aData)219 void SharedStyleSheetCache::WillStartPendingLoad(SheetLoadData& aData) {
220 SheetLoadData* curr = &aData;
221 do {
222 MOZ_DIAGNOSTIC_ASSERT(curr->mLoader->mPendingLoadCount,
223 "Where did this pending load come from?");
224 --curr->mLoader->mPendingLoadCount;
225 } while ((curr = curr->mNext));
226 }
227
CoalesceLoad(const SheetLoadDataHashKey & aKey,SheetLoadData & aNewLoad,SheetState aExistingLoadState)228 bool SharedStyleSheetCache::CoalesceLoad(const SheetLoadDataHashKey& aKey,
229 SheetLoadData& aNewLoad,
230 SheetState aExistingLoadState) {
231 MOZ_ASSERT(SheetLoadDataHashKey(aNewLoad).KeyEquals(aKey));
232 SheetLoadData* existingData = nullptr;
233 if (aExistingLoadState == SheetState::Loading) {
234 existingData = mLoadingDatas.Get(aKey);
235 MOZ_ASSERT(existingData, "CreateSheet lied about the state");
236 } else if (aExistingLoadState == SheetState::Pending) {
237 existingData = mPendingDatas.GetWeak(aKey);
238 MOZ_ASSERT(existingData, "CreateSheet lied about the state");
239 }
240
241 if (!existingData) {
242 return false;
243 }
244
245 if (aExistingLoadState == SheetState::Pending && !aNewLoad.ShouldDefer()) {
246 // Kick the load off; someone cares about it right away
247 RefPtr<SheetLoadData> removedData;
248 mPendingDatas.Remove(aKey, getter_AddRefs(removedData));
249 MOZ_ASSERT(removedData == existingData, "Bad loading table");
250
251 WillStartPendingLoad(*removedData);
252
253 // We insert to the front instead of the back, to keep the invariant that
254 // the front sheet always is the one that triggers the load.
255 aNewLoad.mNext = std::move(removedData);
256 LOG((" Forcing load of pending data"));
257 return false;
258 }
259
260 LOG((" Glomming on to existing load"));
261 SheetLoadData* data = existingData;
262 while (data->mNext) {
263 data = data->mNext;
264 }
265 data->mNext = &aNewLoad;
266
267 return true;
268 }
269
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const270 size_t SharedStyleSheetCache::SizeOfIncludingThis(
271 MallocSizeOf aMallocSizeOf) const {
272 size_t n = aMallocSizeOf(this);
273
274 n += mCompleteSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
275 for (const auto& data : mCompleteSheets.Values()) {
276 n += data.mSheet->SizeOfIncludingThis(aMallocSizeOf);
277 n += aMallocSizeOf(data.mUseCounters.get());
278 }
279
280 // Measurement of the following members may be added later if DMD finds it is
281 // worthwhile:
282 // - mLoadingDatas: transient, and should be small
283 // - mPendingDatas: transient, and should be small
284 return n;
285 }
286
DeferSheetLoad(const SheetLoadDataHashKey & aKey,SheetLoadData & aData)287 void SharedStyleSheetCache::DeferSheetLoad(const SheetLoadDataHashKey& aKey,
288 SheetLoadData& aData) {
289 MOZ_ASSERT(SheetLoadDataHashKey(aData).KeyEquals(aKey));
290 MOZ_DIAGNOSTIC_ASSERT(!aData.mNext, "Should only defer loads once");
291
292 aData.mMustNotify = true;
293 mPendingDatas.InsertOrUpdate(aKey, RefPtr{&aData});
294 }
295
LoadStarted(const SheetLoadDataHashKey & aKey,SheetLoadData & aData)296 void SharedStyleSheetCache::LoadStarted(const SheetLoadDataHashKey& aKey,
297 SheetLoadData& aData) {
298 MOZ_ASSERT(aData.mURI, "No load required?");
299 MOZ_ASSERT(!aData.mIsLoading, "Already loading? How?");
300 MOZ_ASSERT(SheetLoadDataHashKey(aData).KeyEquals(aKey));
301 aData.mIsLoading = true;
302 mLoadingDatas.InsertOrUpdate(aKey, &aData);
303 }
304
LoadCompleted(SharedStyleSheetCache * aCache,SheetLoadData & aData,nsresult aStatus)305 void SharedStyleSheetCache::LoadCompleted(SharedStyleSheetCache* aCache,
306 SheetLoadData& aData,
307 nsresult aStatus) {
308 // If aStatus is a failure we need to mark this data failed. We also need to
309 // mark any ancestors of a failing data as failed and any sibling of a
310 // failing data as failed. Note that SheetComplete is never called on a
311 // SheetLoadData that is the mNext of some other SheetLoadData.
312 nsresult cancelledStatus = aStatus;
313 if (NS_FAILED(aStatus)) {
314 css::Loader::MarkLoadTreeFailed(aData);
315 } else {
316 cancelledStatus = NS_BINDING_ABORTED;
317 SheetLoadData* data = &aData;
318 do {
319 if (data->mIsCancelled) {
320 // We only need to mark loads for this loader as cancelled, so as to not
321 // fire error events in unrelated documents.
322 css::Loader::MarkLoadTreeFailed(*data, data->mLoader);
323 }
324 } while ((data = data->mNext));
325 }
326
327 // 8 is probably big enough for all our common cases. It's not likely that
328 // imports will nest more than 8 deep, and multiple sheets with the same URI
329 // are rare.
330 AutoTArray<RefPtr<SheetLoadData>, 8> datasToNotify;
331 LoadCompletedInternal(aCache, aData, datasToNotify);
332
333 // Now it's safe to go ahead and notify observers
334 for (RefPtr<SheetLoadData>& data : datasToNotify) {
335 auto status = data->mIsCancelled ? cancelledStatus : aStatus;
336 data->mLoader->NotifyObservers(*data, status);
337 }
338 }
339
LoadCompletedInternal(SharedStyleSheetCache * aCache,SheetLoadData & aData,nsTArray<RefPtr<SheetLoadData>> & aDatasToNotify)340 void SharedStyleSheetCache::LoadCompletedInternal(
341 SharedStyleSheetCache* aCache, SheetLoadData& aData,
342 nsTArray<RefPtr<SheetLoadData>>& aDatasToNotify) {
343 if (aData.mIsLoading) {
344 MOZ_ASSERT(aCache);
345 SheetLoadDataHashKey key(aData);
346 Maybe<SheetLoadData*> loadingData = aCache->mLoadingDatas.Extract(key);
347 MOZ_DIAGNOSTIC_ASSERT(loadingData);
348 MOZ_DIAGNOSTIC_ASSERT(loadingData.value() == &aData);
349 Unused << loadingData;
350 aData.mIsLoading = false;
351 }
352
353 // Go through and deal with the whole linked list.
354 SheetLoadData* data = &aData;
355 do {
356 MOZ_DIAGNOSTIC_ASSERT(!data->mSheetCompleteCalled);
357 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
358 data->mSheetCompleteCalled = true;
359 #endif
360
361 if (!data->mSheetAlreadyComplete) {
362 // If mSheetAlreadyComplete, then the sheet could well be modified between
363 // when we posted the async call to SheetComplete and now, since the sheet
364 // was page-accessible during that whole time.
365
366 // HasForcedUniqueInner() is okay if the sheet is constructed, because
367 // constructed sheets are always unique and they may be set to complete
368 // multiple times if their rules are replaced via Replace()
369 MOZ_ASSERT(data->mSheet->IsConstructed() ||
370 !data->mSheet->HasForcedUniqueInner(),
371 "should not get a forced unique inner during parsing");
372 // Insert the sheet into the tree now the sheet has loaded, but only if
373 // the sheet is still relevant, and if this is a top-level sheet.
374 const bool needInsertIntoTree = [&] {
375 if (!data->mLoader->GetDocument()) {
376 // Not a document load, nothing to do.
377 return false;
378 }
379 if (data->IsPreload()) {
380 // Preloads are not supposed to be observable.
381 return false;
382 }
383 if (data->mSheet->IsConstructed()) {
384 // Constructable sheets are not in the regular stylesheet tree.
385 return false;
386 }
387 if (data->mIsChildSheet) {
388 // A child sheet, those will get exposed from the parent, no need to
389 // insert them into the tree.
390 return false;
391 }
392 if (data->mOwningNode != data->mSheet->GetOwnerNode()) {
393 // The sheet was already removed from the tree and is no longer the
394 // current sheet of the owning node, we can bail.
395 return false;
396 }
397 return true;
398 }();
399
400 if (needInsertIntoTree) {
401 data->mLoader->InsertSheetInTree(*data->mSheet, data->mOwningNode);
402 }
403 data->mSheet->SetComplete();
404 data->ScheduleLoadEventIfNeeded();
405 } else if (data->mSheet->IsApplicable()) {
406 if (dom::Document* doc = data->mLoader->GetDocument()) {
407 // We post these events for devtools, even though the applicable state
408 // has not actually changed, to make the cache not observable.
409 doc->PostStyleSheetApplicableStateChangeEvent(*data->mSheet);
410 }
411 }
412
413 aDatasToNotify.AppendElement(data);
414
415 NS_ASSERTION(!data->mParentData || data->mParentData->mPendingChildren != 0,
416 "Broken pending child count on our parent");
417
418 // If we have a parent, our parent is no longer being parsed, and
419 // we are the last pending child, then our load completion
420 // completes the parent too. Note that the parent _can_ still be
421 // being parsed (eg if the child (us) failed to open the channel
422 // or some such).
423 if (data->mParentData && --(data->mParentData->mPendingChildren) == 0 &&
424 !data->mParentData->mIsBeingParsed) {
425 LoadCompletedInternal(aCache, *data->mParentData, aDatasToNotify);
426 }
427
428 data = data->mNext;
429 } while (data);
430
431 if (aCache) {
432 aCache->InsertIntoCompleteCacheIfNeeded(aData);
433 }
434 }
435
InsertIntoCompleteCacheIfNeeded(SheetLoadData & aData)436 void SharedStyleSheetCache::InsertIntoCompleteCacheIfNeeded(
437 SheetLoadData& aData) {
438 MOZ_ASSERT(aData.mLoader->GetDocument(),
439 "We only cache document-associated sheets");
440 LOG(("SharedStyleSheetCache::InsertIntoCompleteCacheIfNeeded"));
441 // If we ever start doing this for failed loads, we'll need to adjust the
442 // PostLoadEvent code that thinks anything already complete must have loaded
443 // succesfully.
444 if (aData.mLoadFailed) {
445 LOG((" Load failed, bailing"));
446 return;
447 }
448
449 // If this sheet came from the cache already, there's no need to override
450 // anything.
451 if (aData.mSheetAlreadyComplete) {
452 LOG((" Sheet came from the cache, bailing"));
453 return;
454 }
455
456 if (!aData.mURI) {
457 LOG((" Inline or constructable style sheet, bailing"));
458 // Inline sheet caching happens in Loader::mInlineSheets.
459 // Constructable sheets are not worth caching, they're always unique.
460 return;
461 }
462
463 // We need to clone the sheet on insertion to the cache because otherwise the
464 // stylesheets can keep alive full windows alive via either their JS wrapper,
465 // or via StyleSheet::mRelevantGlobal.
466 //
467 // If this ever changes, then you also need to fix up the memory reporting in
468 // both SizeOfIncludingThis and nsXULPrototypeCache::CollectMemoryReports.
469 RefPtr<StyleSheet> sheet = CloneSheet(*aData.mSheet);
470
471 if (dom::IsChromeURI(aData.mURI)) {
472 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
473 if (cache && cache->IsEnabled()) {
474 if (!cache->GetStyleSheet(aData.mURI)) {
475 LOG((" Putting sheet in XUL prototype cache"));
476 NS_ASSERTION(sheet->IsComplete(),
477 "Should only be caching complete sheets");
478
479 // NOTE: If we stop cloning sheets before insertion, we need to change
480 // nsXULPrototypeCache::CollectMemoryReports() to stop using
481 // SizeOfIncludingThis() because it will no longer own the sheets.
482 cache->PutStyleSheet(std::move(sheet));
483 }
484 }
485 } else {
486 LOG((" Putting style sheet in shared cache: %s",
487 aData.mURI->GetSpecOrDefault().get()));
488 SheetLoadDataHashKey key(aData);
489 MOZ_ASSERT(sheet->IsComplete(), "Should only be caching complete sheets");
490
491 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
492 for (const auto& entry : mCompleteSheets) {
493 if (!key.KeyEquals(entry.GetKey())) {
494 MOZ_DIAGNOSTIC_ASSERT(entry.GetData().mSheet != sheet,
495 "Same sheet, different keys?");
496 } else {
497 MOZ_ASSERT(
498 entry.GetData().Expired() || aData.mLoader->ShouldBypassCache(),
499 "Overriding existing complete entry?");
500 }
501 }
502 #endif
503
504 UniquePtr<StyleUseCounters> counters;
505 if (aData.mUseCounters) {
506 // TODO(emilio): Servo_UseCounters_Clone() or something?
507 counters = Servo_UseCounters_Create().Consume();
508 Servo_UseCounters_Merge(counters.get(), aData.mUseCounters.get());
509 }
510
511 mCompleteSheets.InsertOrUpdate(
512 key, CompleteSheet{aData.mExpirationTime, std::move(counters),
513 std::move(sheet)});
514 }
515 }
516
StartDeferredLoadsForLoader(css::Loader & aLoader,StartLoads aStartLoads)517 void SharedStyleSheetCache::StartDeferredLoadsForLoader(
518 css::Loader& aLoader, StartLoads aStartLoads) {
519 using PendingLoad = css::Loader::PendingLoad;
520
521 LoadDataArray arr;
522 for (auto iter = mPendingDatas.Iter(); !iter.Done(); iter.Next()) {
523 bool startIt = false;
524 SheetLoadData* data = iter.Data();
525 do {
526 if (data->mLoader == &aLoader) {
527 // Note that we don't want to affect what the selected style set is, so
528 // use true for aHasAlternateRel.
529 if (aStartLoads != StartLoads::IfNonAlternate ||
530 aLoader.IsAlternateSheet(iter.Data()->mTitle, true) !=
531 IsAlternate::Yes) {
532 startIt = true;
533 break;
534 }
535 }
536 } while ((data = data->mNext));
537 if (startIt) {
538 arr.AppendElement(std::move(iter.Data()));
539 iter.Remove();
540 }
541 }
542 for (auto& data : arr) {
543 WillStartPendingLoad(*data);
544 data->mLoader->LoadSheet(*data, SheetState::NeedsParser, PendingLoad::Yes);
545 }
546 }
547
CancelDeferredLoadsForLoader(css::Loader & aLoader)548 void SharedStyleSheetCache::CancelDeferredLoadsForLoader(css::Loader& aLoader) {
549 LoadDataArray arr;
550
551 for (auto iter = mPendingDatas.Iter(); !iter.Done(); iter.Next()) {
552 RefPtr<SheetLoadData>& first = iter.Data();
553 SheetLoadData* prev = nullptr;
554 SheetLoadData* current = iter.Data();
555 do {
556 if (current->mLoader != &aLoader) {
557 prev = current;
558 current = current->mNext;
559 continue;
560 }
561 // Detach the load from the list, mark it as cancelled, and then below
562 // call SheetComplete on it.
563 RefPtr<SheetLoadData> strong =
564 prev ? std::move(prev->mNext) : std::move(first);
565 MOZ_ASSERT(strong == current);
566 if (prev) {
567 prev->mNext = std::move(strong->mNext);
568 current = prev->mNext;
569 } else {
570 first = std::move(strong->mNext);
571 current = first;
572 }
573 strong->mIsCancelled = true;
574 arr.AppendElement(std::move(strong));
575 } while (current);
576
577 if (!first) {
578 iter.Remove();
579 }
580 }
581
582 for (auto& data : arr) {
583 aLoader.SheetComplete(*data, NS_BINDING_ABORTED);
584 }
585 }
586
CancelLoadsForLoader(css::Loader & aLoader)587 void SharedStyleSheetCache::CancelLoadsForLoader(css::Loader& aLoader) {
588 CancelDeferredLoadsForLoader(aLoader);
589
590 // We can't stop in-progress loads because some other loader may care about
591 // them.
592 for (SheetLoadData* data : mLoadingDatas.Values()) {
593 MOZ_DIAGNOSTIC_ASSERT(data,
594 "We weren't properly notified and the load was "
595 "incorrectly dropped on the floor");
596 for (; data; data = data->mNext) {
597 if (data->mLoader == &aLoader) {
598 data->mIsCancelled = true;
599 }
600 }
601 }
602 }
603
RegisterLoader(css::Loader & aLoader)604 void SharedStyleSheetCache::RegisterLoader(css::Loader& aLoader) {
605 MOZ_ASSERT(aLoader.GetDocument());
606 mLoaderPrincipalRefCnt.LookupOrInsert(aLoader.GetDocument()->NodePrincipal(),
607 0) += 1;
608 }
609
UnregisterLoader(css::Loader & aLoader)610 void SharedStyleSheetCache::UnregisterLoader(css::Loader& aLoader) {
611 MOZ_ASSERT(aLoader.GetDocument());
612 nsIPrincipal* prin = aLoader.GetDocument()->NodePrincipal();
613 auto lookup = mLoaderPrincipalRefCnt.Lookup(prin);
614 MOZ_RELEASE_ASSERT(lookup);
615 MOZ_RELEASE_ASSERT(lookup.Data());
616 if (!--lookup.Data()) {
617 lookup.Remove();
618 // TODO(emilio): Do this off a timer or something maybe.
619 for (auto iter = mCompleteSheets.Iter(); !iter.Done(); iter.Next()) {
620 if (iter.Key().LoaderPrincipal()->Equals(prin)) {
621 iter.Remove();
622 }
623 }
624 }
625 }
626
627 } // namespace mozilla
628
629 #undef LOG
630