Source code

Revision control

Other Tools

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
/* loading of CSS style sheets using the network APIs */
8
9
#include "mozilla/css/Loader.h"
10
11
#include "mozilla/ArrayUtils.h"
12
#include "mozilla/dom/DocGroup.h"
13
#include "mozilla/dom/SRILogHelper.h"
14
#include "mozilla/IntegerPrintfMacros.h"
15
#include "mozilla/AutoRestore.h"
16
#include "mozilla/LoadInfo.h"
17
#include "mozilla/Logging.h"
18
#include "mozilla/MemoryReporting.h"
19
#include "mozilla/SystemGroup.h"
20
#include "mozilla/ResultExtensions.h"
21
#include "mozilla/URLPreloader.h"
22
#include "nsIRunnable.h"
23
#include "nsITimedChannel.h"
24
#include "nsSyncLoadService.h"
25
#include "nsCOMPtr.h"
26
#include "nsString.h"
27
#include "nsIContent.h"
28
#include "nsIContentInlines.h"
29
#include "mozilla/dom/Document.h"
30
#include "nsIURI.h"
31
#include "nsNetUtil.h"
32
#include "nsIProtocolHandler.h"
33
#include "nsContentUtils.h"
34
#include "nsIScriptSecurityManager.h"
35
#include "nsContentPolicyUtils.h"
36
#include "nsIHttpChannel.h"
37
#include "nsIHttpChannelInternal.h"
38
#include "nsIClassOfService.h"
39
#include "nsIScriptError.h"
40
#include "nsMimeTypes.h"
41
#include "nsIStyleSheetLinkingElement.h"
42
#include "nsICSSLoaderObserver.h"
43
#include "nsThreadUtils.h"
44
#include "nsGkAtoms.h"
45
#include "nsIThreadInternal.h"
46
#include "nsINetworkPredictor.h"
47
#include "nsStringStream.h"
48
#include "mozilla/dom/MediaList.h"
49
#include "mozilla/dom/ShadowRoot.h"
50
#include "mozilla/dom/URL.h"
51
#include "mozilla/net/UrlClassifierFeatureFactory.h"
52
#include "mozilla/AsyncEventDispatcher.h"
53
#include "mozilla/ServoBindings.h"
54
#include "mozilla/StyleSheet.h"
55
#include "mozilla/StyleSheetInlines.h"
56
#include "mozilla/ConsoleReportCollector.h"
57
#include "mozilla/ServoUtils.h"
58
#include "mozilla/css/StreamLoader.h"
59
#include "ReferrerInfo.h"
60
61
#ifdef MOZ_XUL
62
# include "nsXULPrototypeCache.h"
63
#endif
64
65
#include "nsError.h"
66
67
#include "nsIContentSecurityPolicy.h"
68
#include "mozilla/dom/SRICheck.h"
69
70
#include "mozilla/Encoding.h"
71
72
using namespace mozilla::dom;
73
74
// 1024 bytes is specified in https://drafts.csswg.org/css-syntax/
75
#define SNIFFING_BUFFER_SIZE 1024
76
77
/**
78
* OVERALL ARCHITECTURE
79
*
80
* The CSS Loader gets requests to load various sorts of style sheets:
81
* inline style from <style> elements, linked style, @import-ed child
82
* sheets, non-document sheets. The loader handles the following tasks:
83
* 1) Creation of the actual style sheet objects: CreateSheet()
84
* 2) setting of the right media, title, enabled state, etc on the
85
* sheet: PrepareSheet()
86
* 3) Insertion of the sheet in the proper cascade order:
87
* InsertSheetInTree() and InsertChildSheet()
88
* 4) Load of the sheet: LoadSheet() including security checks
89
* 5) Parsing of the sheet: ParseSheet()
90
* 6) Cleanup: SheetComplete()
91
*
92
* The detailed documentation for these functions is found with the
93
* function implementations.
94
*
95
* The following helper object is used:
96
* SheetLoadData -- a small class that is used to store all the
97
* information needed for the loading of a sheet;
98
* this class handles listening for the stream
99
* loader completion and also handles charset
100
* determination.
101
*/
102
103
static mozilla::LazyLogModule sCssLoaderLog("nsCSSLoader");
104
105
static mozilla::LazyLogModule gSriPRLog("SRI");
106
107
#define LOG_ERROR(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Error, args)
108
#define LOG_WARN(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Warning, args)
109
#define LOG_DEBUG(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Debug, args)
110
#define LOG(args) LOG_DEBUG(args)
111
112
#define LOG_ERROR_ENABLED() \
113
MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Error)
114
#define LOG_WARN_ENABLED() \
115
MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Warning)
116
#define LOG_DEBUG_ENABLED() \
117
MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Debug)
118
#define LOG_ENABLED() LOG_DEBUG_ENABLED()
119
120
#define LOG_URI(format, uri) \
121
PR_BEGIN_MACRO \
122
NS_ASSERTION(uri, "Logging null uri"); \
123
if (LOG_ENABLED()) { \
124
LOG((format, uri->GetSpecOrDefault().get())); \
125
} \
126
PR_END_MACRO
127
128
// And some convenience strings...
129
static const char* const gStateStrings[] = {"Unknown", "NeedsParser", "Pending",
130
"Loading", "Complete"};
131
132
namespace mozilla {
133
134
class SheetLoadDataHashKey : public nsURIHashKey {
135
public:
136
typedef SheetLoadDataHashKey* KeyType;
137
typedef const SheetLoadDataHashKey* KeyTypePointer;
138
139
explicit SheetLoadDataHashKey(const SheetLoadDataHashKey* aKey)
140
: nsURIHashKey(aKey->mKey),
141
mPrincipal(aKey->mPrincipal),
142
mReferrerInfo(aKey->mReferrerInfo),
143
mCORSMode(aKey->mCORSMode),
144
mParsingMode(aKey->mParsingMode) {
145
MOZ_COUNT_CTOR(SheetLoadDataHashKey);
146
}
147
148
SheetLoadDataHashKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
149
nsIReferrerInfo* aReferrerInfo, CORSMode aCORSMode,
150
css::SheetParsingMode aParsingMode)
151
: nsURIHashKey(aURI),
152
mPrincipal(aPrincipal),
153
mReferrerInfo(aReferrerInfo),
154
mCORSMode(aCORSMode),
155
mParsingMode(aParsingMode) {
156
MOZ_COUNT_CTOR(SheetLoadDataHashKey);
157
}
158
159
SheetLoadDataHashKey(SheetLoadDataHashKey&& toMove)
160
: nsURIHashKey(std::move(toMove)),
161
mPrincipal(std::move(toMove.mPrincipal)),
162
mReferrerInfo(std::move(toMove.mReferrerInfo)),
163
mCORSMode(std::move(toMove.mCORSMode)),
164
mParsingMode(std::move(toMove.mParsingMode)) {
165
MOZ_COUNT_CTOR(SheetLoadDataHashKey);
166
}
167
168
explicit SheetLoadDataHashKey(css::SheetLoadData&);
169
170
~SheetLoadDataHashKey() { MOZ_COUNT_DTOR(SheetLoadDataHashKey); }
171
172
SheetLoadDataHashKey* GetKey() const {
173
return const_cast<SheetLoadDataHashKey*>(this);
174
}
175
const SheetLoadDataHashKey* GetKeyPointer() const { return this; }
176
177
bool KeyEquals(const SheetLoadDataHashKey* aKey) const {
178
if (!nsURIHashKey::KeyEquals(aKey->mKey)) {
179
return false;
180
}
181
182
if (!mPrincipal != !aKey->mPrincipal) {
183
// One or the other has a principal, but not both... not equal
184
return false;
185
}
186
187
if (mCORSMode != aKey->mCORSMode) {
188
// Different CORS modes; we don't match
189
return false;
190
}
191
192
if (mParsingMode != aKey->mParsingMode) {
193
return false;
194
}
195
196
bool eq;
197
if (NS_FAILED(mReferrerInfo->Equals(aKey->mReferrerInfo, &eq)) || !eq) {
198
return false;
199
}
200
201
return !mPrincipal ||
202
(NS_SUCCEEDED(mPrincipal->Equals(aKey->mPrincipal, &eq)) && eq);
203
}
204
205
static const SheetLoadDataHashKey* KeyToPointer(SheetLoadDataHashKey* aKey) {
206
return aKey;
207
}
208
static PLDHashNumber HashKey(const SheetLoadDataHashKey* aKey) {
209
return nsURIHashKey::HashKey(aKey->mKey);
210
}
211
212
nsIURI* GetURI() const { return nsURIHashKey::GetKey(); }
213
214
nsIPrincipal* GetPrincipal() const { return mPrincipal; }
215
216
css::SheetParsingMode ParsingMode() const { return mParsingMode; }
217
218
enum { ALLOW_MEMMOVE = true };
219
220
protected:
221
nsCOMPtr<nsIPrincipal> mPrincipal;
222
nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
223
CORSMode mCORSMode;
224
css::SheetParsingMode mParsingMode;
225
};
226
227
SheetLoadDataHashKey::SheetLoadDataHashKey(css::SheetLoadData& aLoadData)
228
: nsURIHashKey(aLoadData.mURI),
229
mPrincipal(aLoadData.mLoaderPrincipal),
230
mReferrerInfo(aLoadData.ReferrerInfo()),
231
mCORSMode(aLoadData.mSheet->GetCORSMode()),
232
mParsingMode(aLoadData.mSheet->ParsingMode()) {
233
MOZ_COUNT_CTOR(SheetLoadDataHashKey);
234
}
235
} // namespace mozilla
236
237
namespace mozilla {
238
namespace css {
239
240
/********************************
241
* SheetLoadData implementation *
242
********************************/
243
NS_IMPL_ISUPPORTS(SheetLoadData, nsIRunnable, nsIThreadObserver)
244
245
SheetLoadData::SheetLoadData(
246
Loader* aLoader, const nsAString& aTitle, nsIURI* aURI, StyleSheet* aSheet,
247
bool aSyncLoad, nsIStyleSheetLinkingElement* aOwningElement,
248
IsAlternate aIsAlternate, MediaMatched aMediaMatches,
249
nsICSSLoaderObserver* aObserver, nsIPrincipal* aLoaderPrincipal,
250
nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode)
251
: mLoader(aLoader),
252
mTitle(aTitle),
253
mEncoding(nullptr),
254
mURI(aURI),
255
mLineNumber(1),
256
mSheet(aSheet),
257
mNext(nullptr),
258
mPendingChildren(0),
259
mSyncLoad(aSyncLoad),
260
mIsNonDocumentSheet(false),
261
mIsLoading(false),
262
mIsBeingParsed(false),
263
mIsCancelled(false),
264
mMustNotify(false),
265
mWasAlternate(aIsAlternate == IsAlternate::Yes),
266
mMediaMatched(aMediaMatches == MediaMatched::Yes),
267
mUseSystemPrincipal(false),
268
mSheetAlreadyComplete(false),
269
mIsCrossOriginNoCORS(false),
270
mBlockResourceTiming(false),
271
mLoadFailed(false),
272
mOwningElement(aOwningElement),
273
mObserver(aObserver),
274
mLoaderPrincipal(aLoaderPrincipal),
275
mReferrerInfo(aReferrerInfo),
276
mRequestingNode(aRequestingNode),
277
mPreloadEncoding(nullptr) {
278
MOZ_ASSERT(mLoader, "Must have a loader!");
279
}
280
281
SheetLoadData::SheetLoadData(Loader* aLoader, nsIURI* aURI, StyleSheet* aSheet,
282
SheetLoadData* aParentData,
283
nsICSSLoaderObserver* aObserver,
284
nsIPrincipal* aLoaderPrincipal,
285
nsIReferrerInfo* aReferrerInfo,
286
nsINode* aRequestingNode)
287
: mLoader(aLoader),
288
mEncoding(nullptr),
289
mURI(aURI),
290
mLineNumber(1),
291
mSheet(aSheet),
292
mNext(nullptr),
293
mParentData(aParentData),
294
mPendingChildren(0),
295
mSyncLoad(false),
296
mIsNonDocumentSheet(false),
297
mIsLoading(false),
298
mIsBeingParsed(false),
299
mIsCancelled(false),
300
mMustNotify(false),
301
mWasAlternate(false),
302
mMediaMatched(true),
303
mUseSystemPrincipal(false),
304
mSheetAlreadyComplete(false),
305
mIsCrossOriginNoCORS(false),
306
mBlockResourceTiming(false),
307
mLoadFailed(false),
308
mOwningElement(nullptr),
309
mObserver(aObserver),
310
mLoaderPrincipal(aLoaderPrincipal),
311
mReferrerInfo(aReferrerInfo),
312
mRequestingNode(aRequestingNode),
313
mPreloadEncoding(nullptr) {
314
MOZ_ASSERT(mLoader, "Must have a loader!");
315
if (mParentData) {
316
mSyncLoad = mParentData->mSyncLoad;
317
mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet;
318
mUseSystemPrincipal = mParentData->mUseSystemPrincipal;
319
++(mParentData->mPendingChildren);
320
}
321
322
MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
323
"Shouldn't use system principal for async loads");
324
}
325
326
SheetLoadData::SheetLoadData(Loader* aLoader, nsIURI* aURI, StyleSheet* aSheet,
327
bool aSyncLoad, bool aUseSystemPrincipal,
328
const Encoding* aPreloadEncoding,
329
nsICSSLoaderObserver* aObserver,
330
nsIPrincipal* aLoaderPrincipal,
331
nsIReferrerInfo* aReferrerInfo,
332
nsINode* aRequestingNode)
333
: mLoader(aLoader),
334
mEncoding(nullptr),
335
mURI(aURI),
336
mLineNumber(1),
337
mSheet(aSheet),
338
mNext(nullptr),
339
mPendingChildren(0),
340
mSyncLoad(aSyncLoad),
341
mIsNonDocumentSheet(true),
342
mIsLoading(false),
343
mIsBeingParsed(false),
344
mIsCancelled(false),
345
mMustNotify(false),
346
mWasAlternate(false),
347
mMediaMatched(true),
348
mUseSystemPrincipal(aUseSystemPrincipal),
349
mSheetAlreadyComplete(false),
350
mIsCrossOriginNoCORS(false),
351
mBlockResourceTiming(false),
352
mLoadFailed(false),
353
mOwningElement(nullptr),
354
mObserver(aObserver),
355
mLoaderPrincipal(aLoaderPrincipal),
356
mReferrerInfo(aReferrerInfo),
357
mRequestingNode(aRequestingNode),
358
mPreloadEncoding(aPreloadEncoding) {
359
MOZ_ASSERT(mLoader, "Must have a loader!");
360
MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
361
"Shouldn't use system principal for async loads");
362
}
363
364
SheetLoadData::~SheetLoadData() {
365
// Do this iteratively to avoid blowing up the stack.
366
RefPtr<SheetLoadData> next = mNext.forget();
367
while (next) {
368
next = next->mNext.forget();
369
}
370
}
371
372
NS_IMETHODIMP
373
SheetLoadData::Run() {
374
mLoader->HandleLoadEvent(*this);
375
return NS_OK;
376
}
377
378
NS_IMETHODIMP
379
SheetLoadData::OnDispatchedEvent() { return NS_OK; }
380
381
NS_IMETHODIMP
382
SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread, bool aMayWait) {
383
// XXXkhuey this is insane!
384
// We want to fire our load even before or after event processing,
385
// whichever comes first.
386
FireLoadEvent(aThread);
387
return NS_OK;
388
}
389
390
NS_IMETHODIMP
391
SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread,
392
bool aEventWasProcessed) {
393
// XXXkhuey this too!
394
// We want to fire our load even before or after event processing,
395
// whichever comes first.
396
FireLoadEvent(aThread);
397
return NS_OK;
398
}
399
400
void SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread) {
401
// First remove ourselves as a thread observer. But we need to keep
402
// ourselves alive while doing that!
403
RefPtr<SheetLoadData> kungFuDeathGrip(this);
404
aThread->RemoveObserver(this);
405
406
// Now fire the event
407
nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
408
NS_ASSERTION(node, "How did that happen???");
409
410
nsContentUtils::DispatchTrustedEvent(
411
node->OwnerDoc(), node,
412
mLoadFailed ? NS_LITERAL_STRING("error") : NS_LITERAL_STRING("load"),
413
CanBubble::eNo, Cancelable::eNo);
414
415
// And unblock onload
416
mLoader->UnblockOnload(true);
417
}
418
419
void SheetLoadData::ScheduleLoadEventIfNeeded() {
420
if (!mOwningElement) {
421
return;
422
}
423
424
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
425
nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
426
if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
427
// Make sure to block onload here
428
mLoader->BlockOnload();
429
}
430
}
431
432
/*********************
433
* Style sheet reuse *
434
*********************/
435
436
bool LoaderReusableStyleSheets::FindReusableStyleSheet(
437
nsIURI* aURL, RefPtr<StyleSheet>& aResult) {
438
MOZ_ASSERT(aURL);
439
for (size_t i = mReusableSheets.Length(); i > 0; --i) {
440
size_t index = i - 1;
441
bool sameURI;
442
MOZ_ASSERT(mReusableSheets[index]->GetOriginalURI());
443
nsresult rv =
444
aURL->Equals(mReusableSheets[index]->GetOriginalURI(), &sameURI);
445
if (!NS_FAILED(rv) && sameURI) {
446
aResult = mReusableSheets[index];
447
mReusableSheets.RemoveElementAt(index);
448
return true;
449
}
450
}
451
return false;
452
}
453
454
// A struct keeping alive various records of sheets that are loading, deferred,
455
// or already loaded (the later one for caching purposes).
456
struct Loader::Sheets {
457
nsRefPtrHashtable<SheetLoadDataHashKey, StyleSheet> mCompleteSheets;
458
459
nsRefPtrHashtable<SheetLoadDataHashKey, SheetLoadData> mPendingDatas;
460
461
// The SheetLoadData pointers in mLoadingDatas below are weak references.
462
nsDataHashtable<SheetLoadDataHashKey, SheetLoadData*> mLoadingDatas;
463
464
nsRefPtrHashtable<nsStringHashKey, StyleSheet> mInlineSheets;
465
466
RefPtr<StyleSheet> LookupInline(const nsAString&);
467
468
// A cache hit or miss. It is a miss if the `StyleSheet` is null.
469
using CacheResult = Tuple<RefPtr<StyleSheet>, SheetState>;
470
CacheResult Lookup(SheetLoadDataHashKey&, bool aSyncLoad);
471
472
size_t SizeOfIncludingThis(MallocSizeOf) const;
473
};
474
475
RefPtr<StyleSheet> Loader::Sheets::LookupInline(const nsAString& aBuffer) {
476
auto result = mInlineSheets.Lookup(aBuffer);
477
if (!result) {
478
return nullptr;
479
}
480
if (result.Data()->HasModifiedRules()) {
481
// Remove it now that we know that we're never going to use this stylesheet
482
// again.
483
result.Remove();
484
return nullptr;
485
}
486
return result.Data()->Clone(nullptr, nullptr, nullptr, nullptr);
487
}
488
489
static void AssertComplete(const StyleSheet& aSheet) {
490
// This sheet came from the XUL cache or our per-document hashtable; it
491
// better be a complete sheet.
492
MOZ_ASSERT(aSheet.IsComplete(),
493
"Sheet thinks it's not complete while we think it is");
494
}
495
496
static void AssertIncompleteSheetMatches(const SheetLoadData& aData,
497
const SheetLoadDataHashKey& aKey) {
498
#ifdef DEBUG
499
bool debugEqual;
500
MOZ_ASSERT((!aKey.GetPrincipal() && !aData.mLoaderPrincipal) ||
501
(aKey.GetPrincipal() && aData.mLoaderPrincipal &&
502
NS_SUCCEEDED(aKey.GetPrincipal()->Equals(
503
aData.mLoaderPrincipal, &debugEqual)) &&
504
debugEqual),
505
"Principals should be the same");
506
#endif
507
MOZ_ASSERT(!aData.mSheet->HasForcedUniqueInner(),
508
"CSSOM shouldn't allow access to incomplete sheets");
509
}
510
511
auto Loader::Sheets::Lookup(SheetLoadDataHashKey& aKey, bool aSyncLoad)
512
-> CacheResult {
513
auto CloneSheet = [](StyleSheet& aSheet) -> RefPtr<StyleSheet> {
514
return aSheet.Clone(nullptr, nullptr, nullptr, nullptr);
515
};
516
517
nsIURI* uri = aKey.GetURI();
518
// Try to find first in the XUL prototype cache.
519
#ifdef MOZ_XUL
520
if (IsChromeURI(uri)) {
521
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
522
if (cache && cache->IsEnabled()) {
523
if (StyleSheet* sheet = cache->GetStyleSheet(uri)) {
524
LOG((" From XUL cache: %p", sheet));
525
AssertComplete(*sheet);
526
// We need to check the parsing mode manually because the XUL cache only
527
// keys off the URI. See below for the unique inner check.
528
if (!sheet->HasModifiedRules() &&
529
sheet->ParsingMode() == aKey.ParsingMode()) {
530
return MakeTuple(CloneSheet(*sheet), SheetState::Complete);
531
}
532
LOG(
533
(" Not cloning due to forced unique inner or mismatched "
534
"parsing mode"));
535
}
536
}
537
}
538
#endif
539
540
// Now complete sheets.
541
if (auto lookup = mCompleteSheets.Lookup(&aKey)) {
542
LOG((" From completed: %p", lookup.Data().get()));
543
AssertComplete(*lookup.Data());
544
MOZ_ASSERT(lookup.Data()->ParsingMode() == aKey.ParsingMode());
545
// Make sure the stylesheet hasn't been modified, as otherwise it may not
546
// contain the rules we care about.
547
if (!lookup.Data()->HasModifiedRules()) {
548
RefPtr<StyleSheet>& cachedSheet = lookup.Data();
549
RefPtr<StyleSheet> clone = CloneSheet(*cachedSheet);
550
MOZ_ASSERT(!clone->HasForcedUniqueInner());
551
MOZ_ASSERT(!clone->HasModifiedRules());
552
553
const bool oldSheetIsWorthKeeping = ([&cachedSheet] {
554
// If our current stylesheet in the cache has been touched by CSSOM, we
555
// need to do a full copy of it. The new clone still hasn't been
556
// touched, so we have better odds of doing a less-expensive clone in
557
// the future.
558
if (cachedSheet->HasForcedUniqueInner()) {
559
return false;
560
}
561
// The sheet we're cloning isn't actually referenced by anyone. Replace
562
// it in the cache, so that if our CSSOM is later modified we don't end
563
// up with two copies of our inner hanging around.
564
if (!cachedSheet->GetOwnerNode() && !cachedSheet->GetParentSheet()) {
565
return false;
566
}
567
return true;
568
}());
569
570
if (!oldSheetIsWorthKeeping) {
571
cachedSheet = clone;
572
}
573
574
return MakeTuple(std::move(clone), SheetState::Complete);
575
}
576
LOG((" Not cloning due to modified rules"));
577
// Remove it now that we know that we're never going to use this stylesheet
578
// again.
579
lookup.Remove();
580
}
581
582
if (aSyncLoad) {
583
return {};
584
}
585
586
if (SheetLoadData* data = mLoadingDatas.Get(&aKey)) {
587
LOG((" From loading: %p", data->mSheet.get()));
588
AssertIncompleteSheetMatches(*data, aKey);
589
return MakeTuple(CloneSheet(*data->mSheet), SheetState::Loading);
590
}
591
592
if (SheetLoadData* data = mPendingDatas.GetWeak(&aKey)) {
593
LOG((" From pending: %p", data->mSheet.get()));
594
AssertIncompleteSheetMatches(*data, aKey);
595
return MakeTuple(CloneSheet(*data->mSheet), SheetState::Pending);
596
}
597
598
return {};
599
}
600
601
size_t Loader::Sheets::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
602
size_t n = aMallocSizeOf(this);
603
604
n += mCompleteSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
605
for (auto iter = mCompleteSheets.ConstIter(); !iter.Done(); iter.Next()) {
606
// If the sheet has a parent, then its parent will report it so we don't
607
// have to worry about it here. Likewise, if aSheet has an owning node, then
608
// the document that node is in will report it.
609
const StyleSheet* sheet = iter.UserData();
610
if (!sheet->GetOwnerNode() && !sheet->GetParentSheet()) {
611
n += sheet->SizeOfIncludingThis(aMallocSizeOf);
612
}
613
}
614
615
n += mInlineSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
616
for (auto iter = mInlineSheets.ConstIter(); !iter.Done(); iter.Next()) {
617
n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
618
// If the sheet has a parent, then its parent will report it so we don't
619
// have to worry about it here.
620
const StyleSheet* sheet = iter.UserData();
621
MOZ_ASSERT(!sheet->GetParentSheet(),
622
"How did an @import rule end up here?");
623
if (!sheet->GetOwnerNode()) {
624
n += sheet->SizeOfIncludingThis(aMallocSizeOf);
625
}
626
}
627
628
// Measurement of the following members may be added later if DMD finds it is
629
// worthwhile:
630
// - mLoadingDatas: transient, and should be small
631
// - mPendingDatas: transient, and should be small
632
return n;
633
}
634
635
/*************************
636
* Loader Implementation *
637
*************************/
638
639
Loader::Loader()
640
: mDocument(nullptr),
641
mDatasToNotifyOn(0),
642
mCompatMode(eCompatibility_FullStandards),
643
mEnabled(true),
644
mReporter(new ConsoleReportCollector()) {}
645
646
Loader::Loader(DocGroup* aDocGroup) : Loader() { mDocGroup = aDocGroup; }
647
648
Loader::Loader(Document* aDocument) : Loader() {
649
MOZ_ASSERT(aDocument, "We should get a valid document from the caller!");
650
mDocument = aDocument;
651
mCompatMode = aDocument->GetCompatibilityMode();
652
}
653
654
Loader::~Loader() {
655
NS_ASSERTION(!mSheets || mSheets->mLoadingDatas.Count() == 0,
656
"How did we get destroyed when there are loading data?");
657
NS_ASSERTION(!mSheets || mSheets->mPendingDatas.Count() == 0,
658
"How did we get destroyed when there are pending data?");
659
// Note: no real need to revoke our stylesheet loaded events -- they
660
// hold strong references to us, so if we're going away that means
661
// they're all done.
662
}
663
664
void Loader::DropDocumentReference(void) {
665
mDocument = nullptr;
666
// Flush out pending datas just so we don't leak by accident. These
667
// loads should short-circuit through the mDocument check in
668
// LoadSheet and just end up in SheetComplete immediately
669
if (mSheets) {
670
StartDeferredLoads();
671
}
672
}
673
674
void Loader::DocumentStyleSheetSetChanged() {
675
MOZ_ASSERT(mDocument);
676
677
// start any pending alternates that aren't alternates anymore
678
if (!mSheets) {
679
return;
680
}
681
682
LoadDataArray arr(mSheets->mPendingDatas.Count());
683
for (auto iter = mSheets->mPendingDatas.Iter(); !iter.Done(); iter.Next()) {
684
RefPtr<SheetLoadData>& data = iter.Data();
685
MOZ_ASSERT(data, "Must have a data");
686
687
// Note that we don't want to affect what the selected style set is, so
688
// use true for aHasAlternateRel.
689
auto isAlternate = data->mLoader->IsAlternateSheet(data->mTitle, true);
690
if (isAlternate == IsAlternate::No) {
691
arr.AppendElement(std::move(data));
692
iter.Remove();
693
}
694
}
695
696
mDatasToNotifyOn += arr.Length();
697
for (RefPtr<SheetLoadData>& data : arr) {
698
--mDatasToNotifyOn;
699
LoadSheet(*data, SheetState::NeedsParser, IsPreload::No);
700
}
701
}
702
703
static const char kCharsetSym[] = "@charset \"";
704
705
static bool GetCharsetFromData(const char* aStyleSheetData,
706
uint32_t aDataLength, nsACString& aCharset) {
707
aCharset.Truncate();
708
if (aDataLength <= sizeof(kCharsetSym) - 1) return false;
709
710
if (strncmp(aStyleSheetData, kCharsetSym, sizeof(kCharsetSym) - 1)) {
711
return false;
712
}
713
714
for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
715
char c = aStyleSheetData[i];
716
if (c == '"') {
717
++i;
718
if (i < aDataLength && aStyleSheetData[i] == ';') {
719
return true;
720
}
721
// fail
722
break;
723
}
724
aCharset.Append(c);
725
}
726
727
// Did not see end quote or semicolon
728
aCharset.Truncate();
729
return false;
730
}
731
732
NotNull<const Encoding*> SheetLoadData::DetermineNonBOMEncoding(
733
nsACString const& aSegment, nsIChannel* aChannel) {
734
const Encoding* encoding;
735
nsAutoCString label;
736
737
// Check HTTP
738
if (aChannel && NS_SUCCEEDED(aChannel->GetContentCharset(label))) {
739
encoding = Encoding::ForLabel(label);
740
if (encoding) {
741
return WrapNotNull(encoding);
742
}
743
}
744
745
// Check @charset
746
auto sniffingLength = aSegment.Length();
747
if (sniffingLength > SNIFFING_BUFFER_SIZE) {
748
sniffingLength = SNIFFING_BUFFER_SIZE;
749
}
750
if (GetCharsetFromData(aSegment.BeginReading(), sniffingLength, label)) {
751
encoding = Encoding::ForLabel(label);
752
if (encoding == UTF_16BE_ENCODING || encoding == UTF_16LE_ENCODING) {
753
return UTF_8_ENCODING;
754
}
755
if (encoding) {
756
return WrapNotNull(encoding);
757
}
758
}
759
760
// Now try the charset on the <link> or processing instruction
761
// that loaded us
762
if (mOwningElement) {
763
nsAutoString label16;
764
mOwningElement->GetCharset(label16);
765
encoding = Encoding::ForLabel(label16);
766
if (encoding) {
767
return WrapNotNull(encoding);
768
}
769
}
770
771
// In the preload case, the value of the charset attribute on <link> comes
772
// in via mPreloadEncoding instead.
773
if (mPreloadEncoding) {
774
return WrapNotNull(mPreloadEncoding);
775
}
776
777
// Try charset from the parent stylesheet.
778
if (mParentData) {
779
encoding = mParentData->mEncoding;
780
if (encoding) {
781
return WrapNotNull(encoding);
782
}
783
}
784
785
if (mLoader->mDocument) {
786
// Use the document charset.
787
return mLoader->mDocument->GetDocumentCharacterSet();
788
}
789
790
return UTF_8_ENCODING;
791
}
792
793
static nsresult VerifySheetIntegrity(const SRIMetadata& aMetadata,
794
nsIChannel* aChannel,
795
const nsACString& aFirst,
796
const nsACString& aSecond,
797
const nsACString& aSourceFileURI,
798
nsIConsoleReportCollector* aReporter) {
799
NS_ENSURE_ARG_POINTER(aReporter);
800
801
if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), LogLevel::Debug)) {
802
nsAutoCString requestURL;
803
nsCOMPtr<nsIURI> originalURI;
804
if (aChannel &&
805
NS_SUCCEEDED(aChannel->GetOriginalURI(getter_AddRefs(originalURI))) &&
806
originalURI) {
807
originalURI->GetAsciiSpec(requestURL);
808
}
809
MOZ_LOG(SRILogHelper::GetSriLog(), LogLevel::Debug,
810
("VerifySheetIntegrity (unichar stream)"));
811
}
812
813
SRICheckDataVerifier verifier(aMetadata, aSourceFileURI, aReporter);
814
nsresult rv =
815
verifier.Update(aFirst.Length(), (const uint8_t*)aFirst.BeginReading());
816
NS_ENSURE_SUCCESS(rv, rv);
817
rv =
818
verifier.Update(aSecond.Length(), (const uint8_t*)aSecond.BeginReading());
819
NS_ENSURE_SUCCESS(rv, rv);
820
821
return verifier.Verify(aMetadata, aChannel, aSourceFileURI, aReporter);
822
}
823
824
/*
825
* Stream completion code shared by Stylo and the old style system.
826
*
827
* Here we need to check that the load did not give us an http error
828
* page and check the mimetype on the channel to make sure we're not
829
* loading non-text/css data in standards mode.
830
*/
831
nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
832
const nsACString& aBytes1,
833
const nsACString& aBytes2,
834
nsIChannel* aChannel) {
835
LOG(("SheetLoadData::VerifySheetReadyToParse"));
836
NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
837
838
if (mIsCancelled) {
839
// Just return. Don't call SheetComplete -- it's already been
840
// called and calling it again will lead to an extra NS_RELEASE on
841
// this data and a likely crash.
842
return NS_OK;
843
}
844
845
if (!mLoader->mDocument && !mIsNonDocumentSheet) {
846
// Sorry, we don't care about this load anymore
847
LOG_WARN((" No document and not non-document sheet; dropping load"));
848
mLoader->SheetComplete(*this, NS_BINDING_ABORTED);
849
return NS_OK;
850
}
851
852
if (NS_FAILED(aStatus)) {
853
LOG_WARN(
854
(" Load failed: status 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
855
// Handle sheet not loading error because source was a tracking URL (or
856
// fingerprinting, cryptomining, etc).
857
// We make a note of this sheet node by including it in a dedicated
858
// array of blocked tracking nodes under its parent document.
859
//
860
// Multiple sheet load instances might be tied to this request,
861
// we annotate each one linked to a valid owning element (node).
862
if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
863
aStatus)) {
864
if (Document* doc = mLoader->GetDocument()) {
865
for (SheetLoadData* data = this; data; data = data->mNext) {
866
// mOwningElement may be null but AddBlockTrackingNode can cope
867
nsCOMPtr<nsIContent> content =
868
do_QueryInterface(data->mOwningElement);
869
doc->AddBlockedNodeByClassifier(content);
870
}
871
}
872
}
873
mLoader->SheetComplete(*this, aStatus);
874
return NS_OK;
875
}
876
877
if (!aChannel) {
878
mLoader->SheetComplete(*this, NS_OK);
879
return NS_OK;
880
}
881
882
nsCOMPtr<nsIURI> originalURI;
883
aChannel->GetOriginalURI(getter_AddRefs(originalURI));
884
885
// If the channel's original URI is "chrome:", we want that, since
886
// the observer code in nsXULPrototypeCache depends on chrome stylesheets
887
// having a chrome URI. (Whether or not chrome stylesheets come through
888
// this codepath seems nondeterministic.)
889
// Otherwise we want the potentially-HTTP-redirected URI.
890
nsCOMPtr<nsIURI> channelURI;
891
NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
892
893
if (!channelURI || !originalURI) {
894
NS_ERROR("Someone just violated the nsIRequest contract");
895
LOG_WARN((" Channel without a URI. Bad!"));
896
mLoader->SheetComplete(*this, NS_ERROR_UNEXPECTED);
897
return NS_OK;
898
}
899
900
nsCOMPtr<nsIPrincipal> principal;
901
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
902
nsresult result = NS_ERROR_NOT_AVAILABLE;
903
if (secMan) { // Could be null if we already shut down
904
if (mUseSystemPrincipal) {
905
result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
906
} else {
907
result = secMan->GetChannelResultPrincipal(aChannel,
908
getter_AddRefs(principal));
909
}
910
}
911
912
if (NS_FAILED(result)) {
913
LOG_WARN((" Couldn't get principal"));
914
mLoader->SheetComplete(*this, result);
915
return NS_OK;
916
}
917
918
mSheet->SetPrincipal(principal);
919
920
if (mLoaderPrincipal && mSheet->GetCORSMode() == CORS_NONE) {
921
bool subsumed;
922
result = mLoaderPrincipal->Subsumes(principal, &subsumed);
923
if (NS_FAILED(result) || !subsumed) {
924
mIsCrossOriginNoCORS = true;
925
}
926
}
927
928
// If it's an HTTP channel, we want to make sure this is not an
929
// error document we got.
930
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
931
if (httpChannel) {
932
bool requestSucceeded;
933
result = httpChannel->GetRequestSucceeded(&requestSucceeded);
934
if (NS_SUCCEEDED(result) && !requestSucceeded) {
935
LOG((" Load returned an error page"));
936
mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
937
return NS_OK;
938
}
939
940
nsAutoCString sourceMapURL;
941
if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
942
mSheet->SetSourceMapURL(NS_ConvertUTF8toUTF16(sourceMapURL));
943
}
944
}
945
946
nsAutoCString contentType;
947
aChannel->GetContentType(contentType);
948
949
// In standards mode, a style sheet must have one of these MIME
950
// types to be processed at all. In quirks mode, we accept any
951
// MIME type, but only if the style sheet is same-origin with the
952
// requesting document or parent sheet. See bug 524223.
953
954
bool validType = contentType.EqualsLiteral("text/css") ||
955
contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
956
contentType.IsEmpty();
957
958
if (!validType) {
959
const char* errorMessage;
960
uint32_t errorFlag;
961
bool sameOrigin = true;
962
963
if (mLoaderPrincipal) {
964
bool subsumed;
965
result = mLoaderPrincipal->Subsumes(principal, &subsumed);
966
if (NS_FAILED(result) || !subsumed) {
967
sameOrigin = false;
968
}
969
}
970
971
if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
972
errorMessage = "MimeNotCssWarn";
973
errorFlag = nsIScriptError::warningFlag;
974
} else {
975
errorMessage = "MimeNotCss";
976
errorFlag = nsIScriptError::errorFlag;
977
}
978
979
AutoTArray<nsString, 2> strings;
980
CopyUTF8toUTF16(channelURI->GetSpecOrDefault(), *strings.AppendElement());
981
CopyASCIItoUTF16(contentType, *strings.AppendElement());
982
983
nsCOMPtr<nsIURI> referrer = ReferrerInfo()->GetOriginalReferrer();
984
nsContentUtils::ReportToConsole(
985
errorFlag, NS_LITERAL_CSTRING("CSS Loader"), mLoader->mDocument,
986
nsContentUtils::eCSS_PROPERTIES, errorMessage, strings, referrer);
987
988
if (errorFlag == nsIScriptError::errorFlag) {
989
LOG_WARN(
990
(" Ignoring sheet with improper MIME type %s", contentType.get()));
991
mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
992
return NS_OK;
993
}
994
}
995
996
SRIMetadata sriMetadata;
997
mSheet->GetIntegrity(sriMetadata);
998
if (!sriMetadata.IsEmpty()) {
999
nsAutoCString sourceUri;
1000
if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
1001
mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1002
}
1003
nsresult rv = VerifySheetIntegrity(sriMetadata, aChannel, aBytes1, aBytes2,
1004
sourceUri, mLoader->mReporter);
1005
1006
nsCOMPtr<nsILoadGroup> loadGroup;
1007
aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
1008
if (loadGroup) {
1009
mLoader->mReporter->FlushConsoleReports(loadGroup);
1010
} else {
1011
mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
1012
}
1013
1014
if (NS_FAILED(rv)) {
1015
LOG((" Load was blocked by SRI"));
1016
MOZ_LOG(gSriPRLog, LogLevel::Debug,
1017
("css::Loader::OnStreamComplete, bad metadata"));
1018
mLoader->SheetComplete(*this, NS_ERROR_SRI_CORRUPT);
1019
return NS_OK;
1020
}
1021
}
1022
1023
// Enough to set the URIs on mSheet, since any sibling datas we have share
1024
// the same mInner as mSheet and will thus get the same URI.
1025
mSheet->SetURIs(channelURI, originalURI, channelURI);
1026
1027
ReferrerPolicy policy =
1028
nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
1029
nsCOMPtr<nsIReferrerInfo> referrerInfo =
1030
ReferrerInfo::CreateForExternalCSSResources(mSheet, policy);
1031
1032
mSheet->SetReferrerInfo(referrerInfo);
1033
return NS_OK_PARSE_SHEET;
1034
}
1035
1036
Loader::IsAlternate Loader::IsAlternateSheet(const nsAString& aTitle,
1037
bool aHasAlternateRel) {
1038
// A sheet is alternate if it has a nonempty title that doesn't match the
1039
// currently selected style set. But if there _is_ no currently selected
1040
// style set, the sheet wasn't marked as an alternate explicitly, and aTitle
1041
// is nonempty, we should select the style set corresponding to aTitle, since
1042
// that's a preferred sheet.
1043
if (aTitle.IsEmpty()) {
1044
return IsAlternate::No;
1045
}
1046
1047
if (mDocument) {
1048
const nsString& currentSheetSet = mDocument->GetCurrentStyleSheetSet();
1049
if (!aHasAlternateRel && currentSheetSet.IsEmpty()) {
1050
// There's no preferred set yet, and we now have a sheet with a title.
1051
// Make that be the preferred set.
1052
// FIXME(emilio): This is kinda wild, can we do it somewhere else?
1053
mDocument->SetPreferredStyleSheetSet(aTitle);
1054
// We're definitely not an alternate. Also, beware that at this point
1055
// currentSheetSet may dangle.
1056
return IsAlternate::No;
1057
}
1058
1059
if (aTitle.Equals(currentSheetSet)) {
1060
return IsAlternate::No;
1061
}
1062
}
1063
1064
return IsAlternate::Yes;
1065
}
1066
1067
nsresult Loader::CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
1068
nsIPrincipal* aTriggeringPrincipal,
1069
nsIURI* aTargetURI,
1070
nsINode* aRequestingNode,
1071
IsPreload aIsPreload) {
1072
// When performing a system load (e.g. aUseSystemPrincipal = true)
1073
// then aLoadingPrincipal == null; don't consult content policies.
1074
if (!aLoadingPrincipal) {
1075
return NS_OK;
1076
}
1077
1078
nsContentPolicyType contentPolicyType =
1079
aIsPreload == IsPreload::No
1080
? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1081
: nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
1082
1083
nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
1084
aLoadingPrincipal, aTriggeringPrincipal, aRequestingNode,
1085
nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, contentPolicyType);
1086
1087
// snapshot the nonce at load start time for performing CSP checks
1088
if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
1089
nsCOMPtr<Element> element = do_QueryInterface(aRequestingNode);
1090
if (element && element->IsHTMLElement()) {
1091
nsAutoString cspNonce;
1092
element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
1093
secCheckLoadInfo->SetCspNonce(cspNonce);
1094
}
1095
}
1096
1097
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1098
nsresult rv = NS_CheckContentLoadPolicy(
1099
aTargetURI, secCheckLoadInfo, NS_LITERAL_CSTRING("text/css"), &shouldLoad,
1100
nsContentUtils::GetContentPolicy());
1101
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
1102
return NS_ERROR_CONTENT_BLOCKED;
1103
}
1104
return NS_OK;
1105
}
1106
1107
/**
1108
* CreateSheet() creates a StyleSheet object for the given URI.
1109
*
1110
* We check for an existing style sheet object for that uri in various caches
1111
* and clone it if we find it. Cloned sheets will have the title/media/enabled
1112
* state of the sheet they are clones off; make sure to call PrepareSheet() on
1113
* the result of CreateSheet().
1114
*/
1115
Tuple<RefPtr<StyleSheet>, Loader::SheetState> Loader::CreateSheet(
1116
nsIURI* aURI, nsIContent* aLinkingContent, nsIPrincipal* aLoaderPrincipal,
1117
css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
1118
nsIReferrerInfo* aLoadingReferrerInfo, const nsAString& aIntegrity,
1119
bool aSyncLoad) {
1120
MOZ_ASSERT(aURI, "This path is not taken for inline stylesheets");
1121
LOG(("css::Loader::CreateSheet(%s)", aURI->GetSpecOrDefault().get()));
1122
1123
if (!mSheets) {
1124
mSheets = MakeUnique<Sheets>();
1125
}
1126
1127
SheetLoadDataHashKey key(aURI, aLoaderPrincipal, aLoadingReferrerInfo,
1128
aCORSMode, aParsingMode);
1129
auto cacheResult = mSheets->Lookup(key, aSyncLoad);
1130
if (Get<0>(cacheResult)) {
1131
LOG((" Hit cache with state: %s",
1132
gStateStrings[size_t(Get<1>(cacheResult))]));
1133
return cacheResult;
1134
}
1135
1136
nsIURI* sheetURI = aURI;
1137
nsIURI* baseURI = aURI;
1138
nsIURI* originalURI = aURI;
1139
1140
SRIMetadata sriMetadata;
1141
if (!aIntegrity.IsEmpty()) {
1142
MOZ_LOG(gSriPRLog, LogLevel::Debug,
1143
("css::Loader::CreateSheet, integrity=%s",
1144
NS_ConvertUTF16toUTF8(aIntegrity).get()));
1145
nsAutoCString sourceUri;
1146
if (mDocument && mDocument->GetDocumentURI()) {
1147
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1148
}
1149
SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
1150
}
1151
1152
auto sheet = MakeRefPtr<StyleSheet>(aParsingMode, aCORSMode, sriMetadata);
1153
sheet->SetURIs(sheetURI, originalURI, baseURI);
1154
nsCOMPtr<nsIReferrerInfo> referrerInfo =
1155
ReferrerInfo::CreateForExternalCSSResources(sheet);
1156
sheet->SetReferrerInfo(referrerInfo);
1157
LOG((" Needs parser"));
1158
return MakeTuple(std::move(sheet), SheetState::NeedsParser);
1159
}
1160
1161
static Loader::MediaMatched MediaListMatches(const MediaList* aMediaList,
1162
const Document* aDocument) {
1163
if (!aMediaList || !aDocument) {
1164
return Loader::MediaMatched::Yes;
1165
}
1166
1167
if (aMediaList->Matches(*aDocument)) {
1168
return Loader::MediaMatched::Yes;
1169
}
1170
1171
return Loader::MediaMatched::No;
1172
}
1173
1174
/**
1175
* PrepareSheet() handles setting the media and title on the sheet, as
1176
* well as setting the enabled state based on the title and whether
1177
* the sheet had "alternate" in its rel.
1178
*/
1179
Loader::MediaMatched Loader::PrepareSheet(
1180
StyleSheet& aSheet, const nsAString& aTitle, const nsAString& aMediaString,
1181
MediaList* aMediaList, IsAlternate aIsAlternate,
1182
IsExplicitlyEnabled aIsExplicitlyEnabled) {
1183
RefPtr<MediaList> mediaList(aMediaList);
1184
1185
if (!aMediaString.IsEmpty()) {
1186
NS_ASSERTION(!aMediaList,
1187
"must not provide both aMediaString and aMediaList");
1188
mediaList = MediaList::Create(aMediaString);
1189
}
1190
1191
aSheet.SetMedia(mediaList);
1192
1193
aSheet.SetTitle(aTitle);
1194
aSheet.SetEnabled(aIsAlternate == IsAlternate::No ||
1195
aIsExplicitlyEnabled == IsExplicitlyEnabled::Yes);
1196
return MediaListMatches(mediaList, mDocument);
1197
}
1198
1199
/**
1200
* InsertSheetInTree handles ordering of sheets in the document or shadow root.
1201
*
1202
* Here we have two types of sheets -- those with linking elements and
1203
* those without. The latter are loaded by Link: headers, and are only added to
1204
* the document.
1205
*
1206
* The following constraints are observed:
1207
* 1) Any sheet with a linking element comes after all sheets without
1208
* linking elements
1209
* 2) Sheets without linking elements are inserted in the order in
1210
* which the inserting requests come in, since all of these are
1211
* inserted during header data processing in the content sink
1212
* 3) Sheets with linking elements are ordered based on document order
1213
* as determined by CompareDocumentPosition.
1214
*/
1215
void Loader::InsertSheetInTree(StyleSheet& aSheet,
1216
nsIContent* aLinkingContent) {
1217
LOG(("css::Loader::InsertSheetInTree"));
1218
MOZ_ASSERT(mDocument, "Must have a document to insert into");
1219
MOZ_ASSERT(!aLinkingContent || aLinkingContent->IsInUncomposedDoc() ||
1220
aLinkingContent->IsInShadowTree(),
1221
"Why would we insert it anywhere?");
1222
1223
nsCOMPtr<nsIStyleSheetLinkingElement> linkingElement =
1224
do_QueryInterface(aLinkingContent);
1225
if (linkingElement) {
1226
linkingElement->SetStyleSheet(&aSheet);
1227
}
1228
1229
ShadowRoot* shadow =
1230
aLinkingContent ? aLinkingContent->GetContainingShadow() : nullptr;
1231
1232
auto& target = shadow ? static_cast<DocumentOrShadowRoot&>(*shadow)
1233
: static_cast<DocumentOrShadowRoot&>(*mDocument);
1234
1235
// XXX Need to cancel pending sheet loads for this element, if any
1236
1237
int32_t sheetCount = target.SheetCount();
1238
1239
/*
1240
* Start the walk at the _end_ of the list, since in the typical
1241
* case we'll just want to append anyway. We want to break out of
1242
* the loop when insertionPoint points to just before the index we
1243
* want to insert at. In other words, when we leave the loop
1244
* insertionPoint is the index of the stylesheet that immediately
1245
* precedes the one we're inserting.
1246
*/
1247
int32_t insertionPoint = sheetCount - 1;
1248
for (; insertionPoint >= 0; --insertionPoint) {
1249
nsINode* sheetOwner = target.SheetAt(insertionPoint)->GetOwnerNode();
1250
if (sheetOwner && !aLinkingContent) {
1251
// Keep moving; all sheets with a sheetOwner come after all
1252
// sheets without a linkingNode
1253
continue;
1254
}
1255
1256
if (!sheetOwner) {
1257
// Aha! The current sheet has no sheet owner, so we want to insert after
1258
// it no matter whether we have a linking content or not.
1259
break;
1260
}
1261
1262
MOZ_ASSERT(aLinkingContent != sheetOwner,
1263
"Why do we still have our old sheet?");
1264
1265
// Have to compare
1266
if (nsContentUtils::PositionIsBefore(sheetOwner, aLinkingContent)) {
1267
// The current sheet comes before us, and it better be the first
1268
// such, because now we break
1269
break;
1270
}
1271
}
1272
1273
++insertionPoint;
1274
1275
if (shadow) {
1276
shadow->InsertSheetAt(insertionPoint, aSheet);
1277
} else {
1278
mDocument->InsertSheetAt(insertionPoint, aSheet);
1279
}
1280
1281
LOG((" Inserting into target (doc: %d) at position %d",
1282
target.AsNode().IsDocument(), insertionPoint));
1283
}
1284
1285
/**
1286
* InsertChildSheet handles ordering of @import-ed sheet in their
1287
* parent sheets. Here we want to just insert based on order of the
1288
* @import rules that imported the sheets. In theory we can't just
1289
* append to the end because the CSSOM can insert @import rules. In
1290
* practice, we get the call to load the child sheet before the CSSOM
1291
* has finished inserting the @import rule, so we have no idea where
1292
* to put it anyway. So just append for now. (In the future if we
1293
* want to insert the sheet at the correct position, we'll need to
1294
* restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
1295
* bug 1220506.)
1296
*/
1297
void Loader::InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet) {
1298
LOG(("css::Loader::InsertChildSheet"));
1299
1300
// child sheets should always start out enabled, even if they got
1301
// cloned off of top-level sheets which were disabled
1302
aSheet.SetEnabled(true);
1303
aParentSheet.AppendStyleSheet(aSheet);
1304
1305
LOG((" Inserting into parent sheet"));
1306
}
1307
1308
/**
1309
* LoadSheet handles the actual load of a sheet. If the load is
1310
* supposed to be synchronous it just opens a channel synchronously
1311
* using the given uri, wraps the resulting stream in a converter
1312
* stream and calls ParseSheet. Otherwise it tries to look for an
1313
* existing load for this URI and piggyback on it. Failing all that,
1314
* a new load is kicked off asynchronously.
1315
*/
1316
nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState,
1317
IsPreload aIsPreload) {
1318
LOG(("css::Loader::LoadSheet"));
1319
MOZ_ASSERT(aLoadData.mURI, "Need a URI to load");
1320
MOZ_ASSERT(aLoadData.mSheet, "Need a sheet to load into");
1321
MOZ_ASSERT(aSheetState != SheetState::Complete, "Why bother?");
1322
MOZ_ASSERT(!aLoadData.mUseSystemPrincipal || aLoadData.mSyncLoad,
1323
"Shouldn't use system principal for async loads");
1324
NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
1325
1326
LOG_URI(" Load from: '%s'", aLoadData.mURI);
1327
1328
nsresult rv = NS_OK;
1329
1330
if (!mDocument && !aLoadData.mIsNonDocumentSheet) {
1331
// No point starting the load; just release all the data and such.
1332
LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
1333
SheetComplete(aLoadData, NS_BINDING_ABORTED);
1334
return NS_BINDING_ABORTED;
1335
}
1336
1337
SRIMetadata sriMetadata;
1338
aLoadData.mSheet->GetIntegrity(sriMetadata);
1339
1340
if (aLoadData.mSyncLoad) {
1341
LOG((" Synchronous load"));
1342
MOZ_ASSERT(!aLoadData.mObserver, "Observer for a sync load?");
1343
MOZ_ASSERT(aSheetState == SheetState::NeedsParser,
1344
"Sync loads can't reuse existing async loads");
1345
1346
// Create a StreamLoader instance to which we will feed
1347
// the data from the sync load. Do this before creating the
1348
// channel to make error recovery simpler.
1349
nsCOMPtr<nsIStreamListener> streamLoader = new StreamLoader(aLoadData);
1350
1351
if (mDocument) {
1352
net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1353
nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
1354
mDocument);
1355
}
1356
1357
nsSecurityFlags securityFlags =
1358
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
1359
nsILoadInfo::SEC_ALLOW_CHROME;
1360
1361
nsContentPolicyType contentPolicyType =
1362
aIsPreload == IsPreload::No
1363
? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1364
: nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
1365
1366
// Just load it
1367
nsCOMPtr<nsIChannel> channel;
1368
// Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
1369
// a node and a principal.
1370
// This is because of a case where the node is the document being styled and
1371
// the principal is the stylesheet (perhaps from a different origin) that is
1372
// applying the styles.
1373
if (aLoadData.mRequestingNode && aLoadData.mLoaderPrincipal) {
1374
rv = NS_NewChannelWithTriggeringPrincipal(
1375
getter_AddRefs(channel), aLoadData.mURI, aLoadData.mRequestingNode,
1376
aLoadData.mLoaderPrincipal, securityFlags, contentPolicyType);
1377
} else {
1378
// either we are loading something inside a document, in which case
1379
// we should always have a requestingNode, or we are loading something
1380
// outside a document, in which case the loadingPrincipal and the
1381
// triggeringPrincipal should always be the systemPrincipal.
1382
auto result = URLPreloader::ReadURI(aLoadData.mURI);
1383
if (result.isOk()) {
1384
nsCOMPtr<nsIInputStream> stream;
1385
MOZ_TRY(
1386
NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap()));
1387
1388
rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aLoadData.mURI,
1389
stream.forget(),
1390
nsContentUtils::GetSystemPrincipal(),
1391
securityFlags, contentPolicyType);
1392
} else {
1393
rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1394
nsContentUtils::GetSystemPrincipal(), securityFlags,
1395
contentPolicyType);
1396
}
1397
}
1398
if (NS_FAILED(rv)) {
1399
LOG_ERROR((" Failed to create channel"));
1400
SheetComplete(aLoadData, rv);
1401
return rv;
1402
}
1403
1404
// snapshot the nonce at load start time for performing CSP checks
1405
if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
1406
nsCOMPtr<Element> element = do_QueryInterface(aLoadData.mRequestingNode);
1407
if (element && element->IsHTMLElement()) {
1408
nsAutoString cspNonce;
1409
element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
1410
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1411
loadInfo->SetCspNonce(cspNonce);
1412
}
1413
}
1414
1415
nsCOMPtr<nsIInputStream> stream;
1416
rv = channel->Open(getter_AddRefs(stream));
1417
1418
if (NS_FAILED(rv)) {
1419
LOG_ERROR((" Failed to open URI synchronously"));
1420
SheetComplete(aLoadData, rv);
1421
return rv;
1422
}
1423
1424
// Force UA sheets to be UTF-8.
1425
// XXX this is only necessary because the default in
1426
// SheetLoadData::OnDetermineCharset is wrong (bug 521039).
1427
channel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
1428
1429
// Manually feed the streamloader the contents of the stream.
1430
// This will call back into OnStreamComplete
1431
// and thence to ParseSheet. Regardless of whether this fails,
1432
// SheetComplete has been called.
1433
return nsSyncLoadService::PushSyncStreamToListener(stream.forget(),
1434
streamLoader, channel);
1435
}
1436
1437
SheetLoadData* existingData = nullptr;
1438
1439
SheetLoadDataHashKey key(aLoadData);
1440
1441
if (aSheetState == SheetState::Loading) {
1442
existingData = mSheets->mLoadingDatas.Get(&key);
1443
NS_ASSERTION(existingData, "CreateSheet lied about the state");
1444
} else if (aSheetState == SheetState::Pending) {
1445
existingData = mSheets->mPendingDatas.GetWeak(&key);
1446
NS_ASSERTION(existingData, "CreateSheet lied about the state");
1447
}
1448
1449
if (existingData) {
1450
LOG((" Glomming on to existing load"));
1451
SheetLoadData* data = existingData;
1452
while (data->mNext) {
1453
data = data->mNext;
1454
}
1455
data->mNext = &aLoadData;
1456
if (aSheetState == SheetState::Pending && !aLoadData.ShouldDefer()) {
1457
// Kick the load off; someone cares about it right away
1458
RefPtr<SheetLoadData> removedData;
1459
mSheets->mPendingDatas.Remove(&key, getter_AddRefs(removedData));
1460
MOZ_ASSERT(removedData == existingData, "Bad loading table");
1461
1462
LOG((" Forcing load of pending data"));
1463
return LoadSheet(*removedData, SheetState::NeedsParser, aIsPreload);
1464
}
1465
// All done here; once the load completes we'll be marked complete
1466
// automatically
1467
return NS_OK;
1468
}
1469
1470
nsCOMPtr<nsILoadGroup> loadGroup;
1471
nsCOMPtr<nsICookieSettings> cookieSettings;
1472
if (mDocument) {
1473
loadGroup = mDocument->GetDocumentLoadGroup();
1474
// load for a document with no loadgrup indicates that something is
1475
// completely bogus, let's bail out early.
1476
if (!loadGroup) {
1477
LOG_ERROR((" Failed to query loadGroup from document"));
1478
SheetComplete(aLoadData, NS_ERROR_UNEXPECTED);
1479
return NS_ERROR_UNEXPECTED;
1480
}
1481
1482
cookieSettings = mDocument->CookieSettings();
1483
}
1484
1485
#ifdef DEBUG
1486
AutoRestore<bool> syncCallbackGuard(mSyncCallback);
1487
mSyncCallback = true;
1488
#endif
1489
1490
CORSMode ourCORSMode = aLoadData.mSheet->GetCORSMode();
1491
nsSecurityFlags securityFlags =
1492
ourCORSMode == CORS_NONE
1493
? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS
1494
: nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
1495
if (ourCORSMode == CORS_ANONYMOUS) {
1496
securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
1497
} else if (ourCORSMode == CORS_USE_CREDENTIALS) {
1498
securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
1499
}
1500
securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
1501
1502
nsContentPolicyType contentPolicyType =
1503
aIsPreload == IsPreload::No
1504
? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1505
: nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
1506
1507
nsCOMPtr<nsIChannel> channel;
1508
// Note we are calling NS_NewChannelWithTriggeringPrincipal here with a node
1509
// and a principal. This is because of a case where the node is the document
1510
// being styled and the principal is the stylesheet (perhaps from a different
1511
// origin) that is applying the styles.
1512
if (aLoadData.mRequestingNode && aLoadData.mLoaderPrincipal) {
1513
rv = NS_NewChannelWithTriggeringPrincipal(
1514
getter_AddRefs(channel), aLoadData.mURI, aLoadData.mRequestingNode,
1515
aLoadData.mLoaderPrincipal, securityFlags, contentPolicyType,
1516
/* PerformanceStorage */ nullptr, loadGroup);
1517
} else {
1518
// either we are loading something inside a document, in which case
1519
// we should always have a requestingNode, or we are loading something
1520
// outside a document, in which case the loadingPrincipal and the
1521
// triggeringPrincipal should always be the systemPrincipal.
1522
rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1523
nsContentUtils::GetSystemPrincipal(), securityFlags,
1524
contentPolicyType, cookieSettings,
1525
/* aPerformanceStorage */ nullptr, loadGroup);
1526
}
1527
1528
if (NS_FAILED(rv)) {
1529
LOG_ERROR((" Failed to create channel"));
1530
SheetComplete(aLoadData, rv);
1531
return rv;
1532
}
1533
1534
// snapshot the nonce at load start time for performing CSP checks
1535
if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
1536
nsCOMPtr<Element> element = do_QueryInterface(aLoadData.mRequestingNode);
1537
if (element && element->IsHTMLElement()) {
1538
nsAutoString cspNonce;
1539
element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
1540
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1541
loadInfo->SetCspNonce(cspNonce);
1542
}
1543
}
1544
1545
if (!aLoadData.ShouldDefer()) {
1546
if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(channel)) {
1547
cos->AddClassFlags(nsIClassOfService::Leader);
1548
}
1549
if (aIsPreload == IsPreload::FromLink) {
1550
if (nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(channel)) {
1551
sp->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
1552
}
1553
}
1554
}
1555
1556
if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
1557
if (nsCOMPtr<nsIReferrerInfo> referrerInfo = aLoadData.ReferrerInfo()) {
1558
rv = httpChannel->SetReferrerInfo(referrerInfo);
1559
Unused << NS_WARN_IF(NS_FAILED(rv));
1560
}
1561
1562
nsCOMPtr<nsIHttpChannelInternal> internalChannel =
1563
do_QueryInterface(httpChannel);
1564
if (internalChannel) {
1565
rv = internalChannel->SetIntegrityMetadata(
1566
sriMetadata.GetIntegrityString());
1567
NS_ENSURE_SUCCESS(rv, rv);
1568
}
1569
1570
// Set the initiator type
1571
if (nsCOMPtr<nsITimedChannel> timedChannel =
1572
do_QueryInterface(httpChannel)) {
1573
if (aLoadData.mParentData) {
1574
timedChannel->SetInitiatorType(NS_LITERAL_STRING("css"));
1575
1576
// This is a child sheet load.
1577
//
1578
// The resource timing of the sub-resources that a document loads
1579
// should normally be reported to the document. One exception is any
1580
// sub-resources of any cross-origin resources that are loaded. We
1581
// don't mind reporting timing data for a direct child cross-origin
1582
// resource since the resource that linked to it (and hence potentially
1583
// anything in that parent origin) is aware that the cross-origin
1584
// resources is to be loaded. However, we do not want to report
1585
// timings for any sub-resources that a cross-origin resource may load
1586
// since that obviously leaks information about what the cross-origin
1587
// resource loads, which is bad.
1588
//
1589
// In addition to checking whether we're an immediate child resource of
1590
// a cross-origin resource (by checking if mIsCrossOriginNoCORS is set
1591
// to true on our parent), we also check our parent to see whether it
1592
// itself is a sub-resource of a cross-origin resource by checking
1593
// mBlockResourceTiming. If that is set then we too are such a
1594
// sub-resource and so we set the flag on ourself too to propagate it
1595
// on down.
1596
//
1597
if (aLoadData.mParentData->mIsCrossOriginNoCORS ||
1598
aLoadData.mParentData->mBlockResourceTiming) {
1599
// Set a flag so any other stylesheet triggered by this one will
1600
// not be reported
1601
aLoadData.mBlockResourceTiming = true;
1602
1603
// Mark the channel so PerformanceMainThread::AddEntry will not
1604
// report the resource.
1605
timedChannel->SetReportResourceTiming(false);
1606
}
1607
1608
} else {
1609
timedChannel->SetInitiatorType(NS_LITERAL_STRING("link"));
1610
}
1611
}
1612
}
1613
1614
// Now tell the channel we expect text/css data back.... We do
1615
// this before opening it, so it's only treated as a hint.
1616
channel->SetContentType(NS_LITERAL_CSTRING("text/css"));
1617
1618
// We don't have to hold on to the stream loader. The ownership
1619
// model is: Necko owns the stream loader, which owns the load data,
1620
// which owns us
1621
nsCOMPtr<nsIStreamListener> streamLoader = new StreamLoader(aLoadData);
1622
1623
if (mDocument) {
1624
net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1625
nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, mDocument);
1626
}
1627
1628
rv = channel->AsyncOpen(streamLoader);
1629
1630
if (NS_FAILED(rv)) {
1631
LOG_ERROR((" Failed to create stream loader"));
1632
SheetComplete(aLoadData, rv);
1633
return rv;
1634
}
1635
1636
mSheets->mLoadingDatas.Put(&key, &aLoadData);
1637
aLoadData.mIsLoading = true;
1638
1639
return NS_OK;
1640
}
1641
1642
/**
1643
* ParseSheet handles parsing the data stream.
1644
*/
1645
Loader::Completed Loader::ParseSheet(const nsACString& aBytes,
1646
SheetLoadData& aLoadData,
1647
AllowAsyncParse aAllowAsync) {
1648
LOG(("css::Loader::ParseSheet"));
1649
AUTO_PROFILER_LABEL("css::Loader::ParseSheet", LAYOUT_CSSParsing);
1650
aLoadData.mIsBeingParsed = true;
1651
1652
// Tell the record/replay system about any sheets that are being parsed,
1653
// so that devtools code can find them later.
1654
if (recordreplay::IsRecordingOrReplaying() && aLoadData.mURI) {
1655
recordreplay::NoteContentParse(
1656
&aLoadData, aLoadData.mURI->GetSpecOrDefault().get(), "text/css",
1657
reinterpret_cast<const Utf8Unit*>(aBytes.BeginReading()),
1658
aBytes.Length());
1659
}
1660
1661
StyleSheet* sheet = aLoadData.mSheet;
1662
MOZ_ASSERT(sheet);
1663
1664
// Some cases, like inline style and UA stylesheets, need to be parsed
1665
// synchronously. The former may trigger child loads, the latter must not.
1666
if (aLoadData.mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
1667
sheet->ParseSheetSync(this, aBytes, &aLoadData, aLoadData.mLineNumber);
1668
aLoadData.mIsBeingParsed = false;
1669
1670
bool noPendingChildren = aLoadData.mPendingChildren == 0;
1671
MOZ_ASSERT_IF(aLoadData.mSyncLoad, noPendingChildren);
1672
if (noPendingChildren) {
1673
SheetComplete(aLoadData, NS_OK);
1674
return Completed::Yes;
1675
}
1676
return Completed::No;
1677
}
1678
1679
// This parse does not need to be synchronous. \o/
1680
//
1681
// Note that we need to block onload because there may be no network requests
1682
// pending.
1683
BlockOnload();
1684
nsCOMPtr<nsISerialEventTarget> target = DispatchTarget();
1685
sheet->ParseSheet(*this, aBytes, aLoadData)
1686
->Then(
1687
target, __func__,
1688
[loadData = RefPtr<SheetLoadData>(&aLoadData)](bool aDummy) {
1689
MOZ_ASSERT(NS_IsMainThread());
1690
loadData->mIsBeingParsed = false;
1691
loadData->mLoader->UnblockOnload(/* aFireSync = */ false);
1692
// If there are no child sheets outstanding, mark us as complete.
1693
// Otherwise, the children are holding strong refs to the data
1694
// and will call SheetComplete() on it when they complete.
1695
if (loadData->mPendingChildren == 0) {
1696
loadData->mLoader->SheetComplete(*loadData, NS_OK);
1697
}
1698
},
1699
[] { MOZ_CRASH("rejected parse promise"); });
1700
return Completed::No;
1701
}
1702
1703
/**
1704
* SheetComplete is the do-it-all cleanup function. It removes the
1705
* load data from the "loading" hashtable, adds the sheet to the
1706
* "completed" hashtable, massages the XUL cache, handles siblings of
1707
* the load data (other loads for the same URI), handles unblocking
1708
* blocked parent loads as needed, and most importantly calls
1709
* NS_RELEASE on the load data to destroy the whole mess.
1710
*/
1711
void Loader::SheetComplete(SheetLoadData& aLoadData, nsresult aStatus) {
1712
LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32,
1713
static_cast<uint32_t>(aStatus)));
1714
1715
// If aStatus is a failure we need to mark this data failed. We also need to
1716
// mark any ancestors of a failing data as failed and any sibling of a
1717
// failing data as failed. Note that SheetComplete is never called on a
1718
// SheetLoadData that is the mNext of some other SheetLoadData.
1719
if (NS_FAILED(aStatus)) {
1720
MarkLoadTreeFailed(aLoadData);
1721
}
1722
1723
if (mDocument) {
1724
mDocument->MaybeWarnAboutZoom();
1725
}
1726
1727
// 8 is probably big enough for all our common cases. It's not likely that
1728
// imports will nest more than 8 deep, and multiple sheets with the same URI
1729
// are rare.
1730
AutoTArray<RefPtr<SheetLoadData>, 8> datasToNotify;
1731
DoSheetComplete(aLoadData, datasToNotify);
1732
1733
// Now it's safe to go ahead and notify observers
1734
uint32_t count = datasToNotify.Length();
1735
mDatasToNotifyOn += count;
1736
for (RefPtr<SheetLoadData>& data : datasToNotify) {
1737
--mDatasToNotifyOn;
1738
1739
MOZ_ASSERT(data, "How did this data get here?");
1740
if (data->mObserver) {
1741
LOG((" Notifying observer %p for data %p. deferred: %d",
1742
data->mObserver.get(), data.get(), data->ShouldDefer()));
1743
data->mObserver->StyleSheetLoaded(data->mSheet, data->ShouldDefer(),
1744
aStatus);
1745
}
1746
1747
nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver>>::ForwardIterator iter(
1748
mObservers);
1749
nsCOMPtr<nsICSSLoaderObserver> obs;
1750
while (iter.HasMore()) {
1751
obs = iter.GetNext();
1752
LOG((" Notifying global observer %p for data %p. deferred: %d",
1753
obs.get(), data.get(), data->ShouldDefer()));
1754
obs->StyleSheetLoaded(data->mSheet, data->ShouldDefer(), aStatus);
1755
}
1756
}
1757
1758
if (mSheets && mSheets->mLoadingDatas.Count() == 0 &&
1759
mSheets->mPendingDatas.Count() > 0) {
1760
LOG((" No more loading sheets; starting deferred loads"));
1761
StartDeferredLoads();
1762
}
1763
}
1764
1765
void Loader::DoSheetComplete(SheetLoadData& aLoadData,
1766
LoadDataArray& aDatasToNotify) {
1767
LOG(("css::Loader::DoSheetComplete"));
1768
MOZ_ASSERT(aLoadData.mSheet, "Must have a sheet");
1769
NS_ASSERTION(mSheets || !aLoadData.mURI,
1770
"mLoadingDatas should be initialized by now.");
1771
1772
// Twiddle the hashtables
1773
if (aLoadData.mURI) {
1774
LOG_URI(" Finished loading: '%s'", aLoadData.mURI);
1775
// Remove the data from the list of loading datas
1776
if (aLoadData.mIsLoading) {
1777
SheetLoadDataHashKey key(aLoadData);
1778
#ifdef DEBUG
1779
SheetLoadData* loadingData;
1780
NS_ASSERTION(mSheets->mLoadingDatas.Get(&key, &loadingData) &&
1781
loadingData == &aLoadData,
1782
"Bad loading table");
1783
#endif
1784
1785
mSheets->mLoadingDatas.Remove(&key);
1786
aLoadData.mIsLoading = false;
1787
}
1788
}
1789
1790
// Go through and deal with the whole linked list.
1791
SheetLoadData* data = &aLoadData;
1792
do {
1793
if (!data->mSheetAlreadyComplete) {
1794
// If mSheetAlreadyComplete, then the sheet could well be modified between
1795
// when we posted the async call to SheetComplete and now, since the sheet
1796
// was page-accessible during that whole time.
1797
MOZ_ASSERT(!data->mSheet->HasForcedUniqueInner(),
1798
"should not get a forced unique inner during parsing");
1799
data->mSheet->SetComplete();
1800
data->ScheduleLoadEventIfNeeded();
1801
}
1802
if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
1803
// Don't notify here so we don't trigger script. Remember the
1804
// info we need to notify, then do it later when it's safe.
1805
aDatasToNotify.AppendElement(data);
1806
1807
// On append failure, just press on. We'll fail to notify the observer,
1808
// but not much we can do about that....
1809
}
1810