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()->HasForcedUniqueInner()) {
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->HasForcedUniqueInner() &&
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 it hasn't been forced to have a unique inner; that is an
546
// indication that its rules have been exposed to CSSOM and so we can't use
547
// it.
548
if (!lookup.Data()->HasForcedUniqueInner()) {
549
RefPtr<StyleSheet> clone = CloneSheet(*lookup.Data());
550
if (!lookup.Data()->GetOwnerNode() && !lookup.Data()->GetParentSheet()) {
551
// The sheet we're cloning isn't actually referenced by anyone. Replace
552
// it in the cache, so that if our CSSOM is later modified we don't end
553
// up with two copies of our inner hanging around.
554
lookup.Data() = clone;
555
}
556
return MakeTuple(std::move(clone), SheetState::Complete);
557
}
558
LOG((" Not cloning due to forced unique inner"));
559
// Remove it now that we know that we're never going to use this stylesheet
560
// again.
561
lookup.Remove();
562
}
563
564
if (aSyncLoad) {
565
return {};
566
}
567
568
if (SheetLoadData* data = mLoadingDatas.Get(&aKey)) {
569
LOG((" From loading: %p", data->mSheet.get()));
570
AssertIncompleteSheetMatches(*data, aKey);
571
return MakeTuple(CloneSheet(*data->mSheet), SheetState::Loading);
572
}
573
574
if (SheetLoadData* data = mPendingDatas.GetWeak(&aKey)) {
575
LOG((" From pending: %p", data->mSheet.get()));
576
AssertIncompleteSheetMatches(*data, aKey);
577
return MakeTuple(CloneSheet(*data->mSheet), SheetState::Pending);
578
}
579
580
return {};
581
}
582
583
size_t Loader::Sheets::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
584
size_t n = aMallocSizeOf(this);
585
586
n += mCompleteSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
587
for (auto iter = mCompleteSheets.ConstIter(); !iter.Done(); iter.Next()) {
588
// If the sheet has a parent, then its parent will report it so we don't
589
// have to worry about it here. Likewise, if aSheet has an owning node, then
590
// the document that node is in will report it.
591
const StyleSheet* sheet = iter.UserData();
592
if (!sheet->GetOwnerNode() && !sheet->GetParentSheet()) {
593
n += sheet->SizeOfIncludingThis(aMallocSizeOf);
594
}
595
}
596
597
n += mInlineSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
598
for (auto iter = mInlineSheets.ConstIter(); !iter.Done(); iter.Next()) {
599
n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
600
// If the sheet has a parent, then its parent will report it so we don't
601
// have to worry about it here.
602
const StyleSheet* sheet = iter.UserData();
603
MOZ_ASSERT(!sheet->GetParentSheet(),
604
"How did an @import rule end up here?");
605
if (!sheet->GetOwnerNode()) {
606
n += sheet->SizeOfIncludingThis(aMallocSizeOf);
607
}
608
}
609
610
// Measurement of the following members may be added later if DMD finds it is
611
// worthwhile:
612
// - mLoadingDatas: transient, and should be small
613
// - mPendingDatas: transient, and should be small
614
return n;
615
}
616
617
/*************************
618
* Loader Implementation *
619
*************************/
620
621
Loader::Loader()
622
: mDocument(nullptr),
623
mDatasToNotifyOn(0),
624
mCompatMode(eCompatibility_FullStandards),
625
mEnabled(true),
626
mReporter(new ConsoleReportCollector()) {}
627
628
Loader::Loader(DocGroup* aDocGroup) : Loader() { mDocGroup = aDocGroup; }
629
630
Loader::Loader(Document* aDocument) : Loader() {
631
MOZ_ASSERT(aDocument, "We should get a valid document from the caller!");
632
mDocument = aDocument;
633
mCompatMode = aDocument->GetCompatibilityMode();
634
}
635
636
Loader::~Loader() {
637
NS_ASSERTION(!mSheets || mSheets->mLoadingDatas.Count() == 0,
638
"How did we get destroyed when there are loading data?");
639
NS_ASSERTION(!mSheets || mSheets->mPendingDatas.Count() == 0,
640
"How did we get destroyed when there are pending data?");
641
// Note: no real need to revoke our stylesheet loaded events -- they
642
// hold strong references to us, so if we're going away that means
643
// they're all done.
644
}
645
646
void Loader::DropDocumentReference(void) {
647
mDocument = nullptr;
648
// Flush out pending datas just so we don't leak by accident. These
649
// loads should short-circuit through the mDocument check in
650
// LoadSheet and just end up in SheetComplete immediately
651
if (mSheets) {
652
StartDeferredLoads();
653
}
654
}
655
656
void Loader::DocumentStyleSheetSetChanged() {
657
MOZ_ASSERT(mDocument);
658
659
// start any pending alternates that aren't alternates anymore
660
if (!mSheets) {
661
return;
662
}
663
664
LoadDataArray arr(mSheets->mPendingDatas.Count());
665
for (auto iter = mSheets->mPendingDatas.Iter(); !iter.Done(); iter.Next()) {
666
RefPtr<SheetLoadData>& data = iter.Data();
667
MOZ_ASSERT(data, "Must have a data");
668
669
// Note that we don't want to affect what the selected style set is, so
670
// use true for aHasAlternateRel.
671
auto isAlternate = data->mLoader->IsAlternateSheet(data->mTitle, true);
672
if (isAlternate == IsAlternate::No) {
673
arr.AppendElement(std::move(data));
674
iter.Remove();
675
}
676
}
677
678
mDatasToNotifyOn += arr.Length();
679
for (RefPtr<SheetLoadData>& data : arr) {
680
--mDatasToNotifyOn;
681
LoadSheet(*data, SheetState::NeedsParser, IsPreload::No);
682
}
683
}
684
685
static const char kCharsetSym[] = "@charset \"";
686
687
static bool GetCharsetFromData(const char* aStyleSheetData,
688
uint32_t aDataLength, nsACString& aCharset) {
689
aCharset.Truncate();
690
if (aDataLength <= sizeof(kCharsetSym) - 1) return false;
691
692
if (strncmp(aStyleSheetData, kCharsetSym, sizeof(kCharsetSym) - 1)) {
693
return false;
694
}
695
696
for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
697
char c = aStyleSheetData[i];
698
if (c == '"') {
699
++i;
700
if (i < aDataLength && aStyleSheetData[i] == ';') {
701
return true;
702
}
703
// fail
704
break;
705
}
706
aCharset.Append(c);
707
}
708
709
// Did not see end quote or semicolon
710
aCharset.Truncate();
711
return false;
712
}
713
714
NotNull<const Encoding*> SheetLoadData::DetermineNonBOMEncoding(
715
nsACString const& aSegment, nsIChannel* aChannel) {
716
const Encoding* encoding;
717
nsAutoCString label;
718
719
// Check HTTP
720
if (aChannel && NS_SUCCEEDED(aChannel->GetContentCharset(label))) {
721
encoding = Encoding::ForLabel(label);
722
if (encoding) {
723
return WrapNotNull(encoding);
724
}
725
}
726
727
// Check @charset
728
auto sniffingLength = aSegment.Length();
729
if (sniffingLength > SNIFFING_BUFFER_SIZE) {
730
sniffingLength = SNIFFING_BUFFER_SIZE;
731
}
732
if (GetCharsetFromData(aSegment.BeginReading(), sniffingLength, label)) {
733
encoding = Encoding::ForLabel(label);
734
if (encoding == UTF_16BE_ENCODING || encoding == UTF_16LE_ENCODING) {
735
return UTF_8_ENCODING;
736
}
737
if (encoding) {
738
return WrapNotNull(encoding);
739
}
740
}
741
742
// Now try the charset on the <link> or processing instruction
743
// that loaded us
744
if (mOwningElement) {
745
nsAutoString label16;
746
mOwningElement->GetCharset(label16);
747
encoding = Encoding::ForLabel(label16);
748
if (encoding) {
749
return WrapNotNull(encoding);
750
}
751
}
752
753
// In the preload case, the value of the charset attribute on <link> comes
754
// in via mPreloadEncoding instead.
755
if (mPreloadEncoding) {
756
return WrapNotNull(mPreloadEncoding);
757
}
758
759
// Try charset from the parent stylesheet.
760
if (mParentData) {
761
encoding = mParentData->mEncoding;
762
if (encoding) {
763
return WrapNotNull(encoding);
764
}
765
}
766
767
if (mLoader->mDocument) {
768
// Use the document charset.
769
return mLoader->mDocument->GetDocumentCharacterSet();
770
}
771
772
return UTF_8_ENCODING;
773
}
774
775
static nsresult VerifySheetIntegrity(const SRIMetadata& aMetadata,
776
nsIChannel* aChannel,
777
const nsACString& aFirst,
778
const nsACString& aSecond,
779
const nsACString& aSourceFileURI,
780
nsIConsoleReportCollector* aReporter) {
781
NS_ENSURE_ARG_POINTER(aReporter);
782
783
if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) {
784
nsAutoCString requestURL;
785
nsCOMPtr<nsIURI> originalURI;
786
if (aChannel &&
787
NS_SUCCEEDED(aChannel->GetOriginalURI(getter_AddRefs(originalURI))) &&
788
originalURI) {
789
originalURI->GetAsciiSpec(requestURL);
790
}
791
MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
792
("VerifySheetIntegrity (unichar stream)"));
793
}
794
795
SRICheckDataVerifier verifier(aMetadata, aSourceFileURI, aReporter);
796
nsresult rv =
797
verifier.Update(aFirst.Length(), (const uint8_t*)aFirst.BeginReading());
798
NS_ENSURE_SUCCESS(rv, rv);
799
rv =
800
verifier.Update(aSecond.Length(), (const uint8_t*)aSecond.BeginReading());
801
NS_ENSURE_SUCCESS(rv, rv);
802
803
return verifier.Verify(aMetadata, aChannel, aSourceFileURI, aReporter);
804
}
805
806
/*
807
* Stream completion code shared by Stylo and the old style system.
808
*
809
* Here we need to check that the load did not give us an http error
810
* page and check the mimetype on the channel to make sure we're not
811
* loading non-text/css data in standards mode.
812
*/
813
nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
814
const nsACString& aBytes1,
815
const nsACString& aBytes2,
816
nsIChannel* aChannel) {
817
LOG(("SheetLoadData::VerifySheetReadyToParse"));
818
NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
819
820
if (mIsCancelled) {
821
// Just return. Don't call SheetComplete -- it's already been
822
// called and calling it again will lead to an extra NS_RELEASE on
823
// this data and a likely crash.
824
return NS_OK;
825
}
826
827
if (!mLoader->mDocument && !mIsNonDocumentSheet) {
828
// Sorry, we don't care about this load anymore
829
LOG_WARN((" No document and not non-document sheet; dropping load"));
830
mLoader->SheetComplete(*this, NS_BINDING_ABORTED);
831
return NS_OK;
832
}
833
834
if (NS_FAILED(aStatus)) {
835
LOG_WARN(
836
(" Load failed: status 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
837
// Handle sheet not loading error because source was a tracking URL (or
838
// fingerprinting, cryptomining, etc).
839
// We make a note of this sheet node by including it in a dedicated
840
// array of blocked tracking nodes under its parent document.
841
//
842
// Multiple sheet load instances might be tied to this request,
843
// we annotate each one linked to a valid owning element (node).
844
if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
845
aStatus)) {
846
if (Document* doc = mLoader->GetDocument()) {
847
for (SheetLoadData* data = this; data; data = data->mNext) {
848
// mOwningElement may be null but AddBlockTrackingNode can cope
849
nsCOMPtr<nsIContent> content =
850
do_QueryInterface(data->mOwningElement);
851
doc->AddBlockedNodeByClassifier(content);
852
}
853
}
854
}
855
mLoader->SheetComplete(*this, aStatus);
856
return NS_OK;
857
}
858
859
if (!aChannel) {
860
mLoader->SheetComplete(*this, NS_OK);
861
return NS_OK;
862
}
863
864
nsCOMPtr<nsIURI> originalURI;
865
aChannel->GetOriginalURI(getter_AddRefs(originalURI));
866
867
// If the channel's original URI is "chrome:", we want that, since
868
// the observer code in nsXULPrototypeCache depends on chrome stylesheets
869
// having a chrome URI. (Whether or not chrome stylesheets come through
870
// this codepath seems nondeterministic.)
871
// Otherwise we want the potentially-HTTP-redirected URI.
872
nsCOMPtr<nsIURI> channelURI;
873
NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
874
875
if (!channelURI || !originalURI) {
876
NS_ERROR("Someone just violated the nsIRequest contract");
877
LOG_WARN((" Channel without a URI. Bad!"));
878
mLoader->SheetComplete(*this, NS_ERROR_UNEXPECTED);
879
return NS_OK;
880
}
881
882
nsCOMPtr<nsIPrincipal> principal;
883
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
884
nsresult result = NS_ERROR_NOT_AVAILABLE;
885
if (secMan) { // Could be null if we already shut down
886
if (mUseSystemPrincipal) {
887
result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
888
} else {
889
result = secMan->GetChannelResultPrincipal(aChannel,
890
getter_AddRefs(principal));
891
}
892
}
893
894
if (NS_FAILED(result)) {
895
LOG_WARN((" Couldn't get principal"));
896
mLoader->SheetComplete(*this, result);
897
return NS_OK;
898
}
899
900
mSheet->SetPrincipal(principal);
901
902
if (mLoaderPrincipal && mSheet->GetCORSMode() == CORS_NONE) {
903
bool subsumed;
904
result = mLoaderPrincipal->Subsumes(principal, &subsumed);
905
if (NS_FAILED(result) || !subsumed) {
906
mIsCrossOriginNoCORS = true;
907
}
908
}
909
910
// If it's an HTTP channel, we want to make sure this is not an
911
// error document we got.
912
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
913
if (httpChannel) {
914
bool requestSucceeded;
915
result = httpChannel->GetRequestSucceeded(&requestSucceeded);
916
if (NS_SUCCEEDED(result) && !requestSucceeded) {
917
LOG((" Load returned an error page"));
918
mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
919
return NS_OK;
920
}
921
922
nsAutoCString sourceMapURL;
923
if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
924
mSheet->SetSourceMapURL(NS_ConvertUTF8toUTF16(sourceMapURL));
925
}
926
}
927
928
nsAutoCString contentType;
929
aChannel->GetContentType(contentType);
930
931
// In standards mode, a style sheet must have one of these MIME
932
// types to be processed at all. In quirks mode, we accept any
933
// MIME type, but only if the style sheet is same-origin with the
934
// requesting document or parent sheet. See bug 524223.
935
936
bool validType = contentType.EqualsLiteral("text/css") ||
937
contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
938
contentType.IsEmpty();
939
940
if (!validType) {
941
const char* errorMessage;
942
uint32_t errorFlag;
943
bool sameOrigin = true;
944
945
if (mLoaderPrincipal) {
946
bool subsumed;
947
result = mLoaderPrincipal->Subsumes(principal, &subsumed);
948
if (NS_FAILED(result) || !subsumed) {
949
sameOrigin = false;
950
}
951
}
952
953
if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
954
errorMessage = "MimeNotCssWarn";
955
errorFlag = nsIScriptError::warningFlag;
956
} else {
957
errorMessage = "MimeNotCss";
958
errorFlag = nsIScriptError::errorFlag;
959
}
960
961
AutoTArray<nsString, 2> strings;
962
CopyUTF8toUTF16(channelURI->GetSpecOrDefault(), *strings.AppendElement());
963
CopyASCIItoUTF16(contentType, *strings.AppendElement());
964
965
nsCOMPtr<nsIURI> referrer = ReferrerInfo()->GetOriginalReferrer();
966
nsContentUtils::ReportToConsole(
967
errorFlag, NS_LITERAL_CSTRING("CSS Loader"), mLoader->mDocument,
968
nsContentUtils::eCSS_PROPERTIES, errorMessage, strings, referrer);
969
970
if (errorFlag == nsIScriptError::errorFlag) {
971
LOG_WARN(
972
(" Ignoring sheet with improper MIME type %s", contentType.get()));
973
mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
974
return NS_OK;
975
}
976
}
977
978
SRIMetadata sriMetadata;
979
mSheet->GetIntegrity(sriMetadata);
980
if (!sriMetadata.IsEmpty()) {
981
nsAutoCString sourceUri;
982
if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
983
mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
984
}
985
nsresult rv = VerifySheetIntegrity(sriMetadata, aChannel, aBytes1, aBytes2,
986
sourceUri, mLoader->mReporter);
987
988
nsCOMPtr<nsILoadGroup> loadGroup;
989
aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
990
if (loadGroup) {
991
mLoader->mReporter->FlushConsoleReports(loadGroup);
992
} else {
993
mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
994
}
995
996
if (NS_FAILED(rv)) {
997
LOG((" Load was blocked by SRI"));
998
MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
999
("css::Loader::OnStreamComplete, bad metadata"));
1000
mLoader->SheetComplete(*this, NS_ERROR_SRI_CORRUPT);
1001
return NS_OK;
1002
}
1003
}
1004
1005
// Enough to set the URIs on mSheet, since any sibling datas we have share
1006
// the same mInner as mSheet and will thus get the same URI.
1007
mSheet->SetURIs(channelURI, originalURI, channelURI);
1008
1009
ReferrerPolicy policy =
1010
nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
1011
nsCOMPtr<nsIReferrerInfo> referrerInfo =
1012
ReferrerInfo::CreateForExternalCSSResources(mSheet, policy);
1013
1014
mSheet->SetReferrerInfo(referrerInfo);
1015
return NS_OK_PARSE_SHEET;
1016
}
1017
1018
Loader::IsAlternate Loader::IsAlternateSheet(const nsAString& aTitle,
1019
bool aHasAlternateRel) {
1020
// A sheet is alternate if it has a nonempty title that doesn't match the
1021
// currently selected style set. But if there _is_ no currently selected
1022
// style set, the sheet wasn't marked as an alternate explicitly, and aTitle
1023
// is nonempty, we should select the style set corresponding to aTitle, since
1024
// that's a preferred sheet.
1025
if (aTitle.IsEmpty()) {
1026
return IsAlternate::No;
1027
}
1028
1029
if (mDocument) {
1030
const nsString& currentSheetSet = mDocument->GetCurrentStyleSheetSet();
1031
if (!aHasAlternateRel && currentSheetSet.IsEmpty()) {
1032
// There's no preferred set yet, and we now have a sheet with a title.
1033
// Make that be the preferred set.
1034
// FIXME(emilio): This is kinda wild, can we do it somewhere else?
1035
mDocument->SetPreferredStyleSheetSet(aTitle);
1036
// We're definitely not an alternate. Also, beware that at this point
1037
// currentSheetSet may dangle.
1038
return IsAlternate::No;
1039
}
1040
1041
if (aTitle.Equals(currentSheetSet)) {
1042
return IsAlternate::No;
1043
}
1044
}
1045
1046
return IsAlternate::Yes;
1047
}
1048
1049
nsresult Loader::CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
1050
nsIPrincipal* aTriggeringPrincipal,
1051
nsIURI* aTargetURI,
1052
nsINode* aRequestingNode,
1053
IsPreload aIsPreload) {
1054
// When performing a system load (e.g. aUseSystemPrincipal = true)
1055
// then aLoadingPrincipal == null; don't consult content policies.
1056
if (!aLoadingPrincipal) {
1057
return NS_OK;
1058
}
1059
1060
nsContentPolicyType contentPolicyType =
1061
aIsPreload == IsPreload::Yes
1062
? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
1063
: nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
1064
1065
nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
1066
aLoadingPrincipal, aTriggeringPrincipal, aRequestingNode,
1067
nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, contentPolicyType);
1068
1069
// snapshot the nonce at load start time for performing CSP checks
1070
if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
1071
nsCOMPtr<Element> element = do_QueryInterface(aRequestingNode);
1072
if (element && element->IsHTMLElement()) {
1073
nsAutoString cspNonce;
1074
element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
1075
secCheckLoadInfo->SetCspNonce(cspNonce);
1076
}
1077
}
1078
1079
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1080
nsresult rv = NS_CheckContentLoadPolicy(
1081
aTargetURI, secCheckLoadInfo, NS_LITERAL_CSTRING("text/css"), &shouldLoad,
1082
nsContentUtils::GetContentPolicy());
1083
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
1084
return NS_ERROR_CONTENT_BLOCKED;
1085
}
1086
return NS_OK;
1087
}
1088
1089
/**
1090
* CreateSheet() creates a StyleSheet object for the given URI.
1091
*
1092
* We check for an existing style sheet object for that uri in various caches
1093
* and clone it if we find it. Cloned sheets will have the title/media/enabled
1094
* state of the sheet they are clones off; make sure to call PrepareSheet() on
1095
* the result of CreateSheet().
1096
*/
1097
Tuple<RefPtr<StyleSheet>, Loader::SheetState> Loader::CreateSheet(
1098
nsIURI* aURI, nsIContent* aLinkingContent, nsIPrincipal* aLoaderPrincipal,
1099
css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
1100
nsIReferrerInfo* aLoadingReferrerInfo, const nsAString& aIntegrity,
1101
bool aSyncLoad) {
1102
MOZ_ASSERT(aURI, "This path is not taken for inline stylesheets");
1103
LOG(("css::Loader::CreateSheet(%s)", aURI->GetSpecOrDefault().get()));
1104
1105
if (!mSheets) {
1106
mSheets = MakeUnique<Sheets>();
1107
}
1108
1109
SheetLoadDataHashKey key(aURI, aLoaderPrincipal, aLoadingReferrerInfo,
1110
aCORSMode, aParsingMode);
1111
auto cacheResult = mSheets->Lookup(key, aSyncLoad);
1112
if (Get<0>(cacheResult)) {
1113
LOG((" Hit cache with state: %s",
1114
gStateStrings[size_t(Get<1>(cacheResult))]));
1115
return cacheResult;
1116
}
1117
1118
nsIURI* sheetURI = aURI;
1119
nsIURI* baseURI = aURI;
1120
nsIURI* originalURI = aURI;
1121
1122
SRIMetadata sriMetadata;
1123
if (!aIntegrity.IsEmpty()) {
1124
MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
1125
("css::Loader::CreateSheet, integrity=%s",
1126
NS_ConvertUTF16toUTF8(aIntegrity).get()));
1127
nsAutoCString sourceUri;
1128
if (mDocument && mDocument->GetDocumentURI()) {
1129
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1130
}
1131
SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
1132
}
1133
1134
auto sheet = MakeRefPtr<StyleSheet>(aParsingMode, aCORSMode, sriMetadata);
1135
sheet->SetURIs(sheetURI, originalURI, baseURI);
1136
nsCOMPtr<nsIReferrerInfo> referrerInfo =
1137
ReferrerInfo::CreateForExternalCSSResources(sheet);
1138
sheet->SetReferrerInfo(referrerInfo);
1139
LOG((" Needs parser"));
1140
return MakeTuple(std::move(sheet), SheetState::NeedsParser);
1141
}
1142
1143
static Loader::MediaMatched MediaListMatches(const MediaList* aMediaList,
1144
const Document* aDocument) {
1145
if (!aMediaList || !aDocument) {
1146
return Loader::MediaMatched::Yes;
1147
}
1148
1149
if (aMediaList->Matches(*aDocument)) {
1150
return Loader::MediaMatched::Yes;
1151
}
1152
1153
return Loader::MediaMatched::No;
1154
}
1155
1156
/**
1157
* PrepareSheet() handles setting the media and title on the sheet, as
1158
* well as setting the enabled state based on the title and whether
1159
* the sheet had "alternate" in its rel.
1160
*/
1161
Loader::MediaMatched Loader::PrepareSheet(
1162
StyleSheet& aSheet, const nsAString& aTitle, const nsAString& aMediaString,
1163
MediaList* aMediaList, IsAlternate aIsAlternate,
1164
IsExplicitlyEnabled aIsExplicitlyEnabled) {
1165
RefPtr<MediaList> mediaList(aMediaList);
1166
1167
if (!aMediaString.IsEmpty()) {
1168
NS_ASSERTION(!aMediaList,
1169
"must not provide both aMediaString and aMediaList");
1170
mediaList = MediaList::Create(aMediaString);
1171
}
1172
1173
aSheet.SetMedia(mediaList);
1174
1175
aSheet.SetTitle(aTitle);
1176
aSheet.SetEnabled(aIsAlternate == IsAlternate::No ||
1177
aIsExplicitlyEnabled == IsExplicitlyEnabled::Yes);
1178
return MediaListMatches(mediaList, mDocument);
1179
}
1180
1181
/**
1182
* InsertSheetInTree handles ordering of sheets in the document or shadow root.
1183
*
1184
* Here we have two types of sheets -- those with linking elements and
1185
* those without. The latter are loaded by Link: headers, and are only added to
1186
* the document.
1187
*
1188
* The following constraints are observed:
1189
* 1) Any sheet with a linking element comes after all sheets without
1190
* linking elements
1191
* 2) Sheets without linking elements are inserted in the order in
1192
* which the inserting requests come in, since all of these are
1193
* inserted during header data processing in the content sink
1194
* 3) Sheets with linking elements are ordered based on document order
1195
* as determined by CompareDocumentPosition.
1196
*/
1197
void Loader::InsertSheetInTree(StyleSheet& aSheet,
1198
nsIContent* aLinkingContent) {
1199
LOG(("css::Loader::InsertSheetInTree"));
1200
MOZ_ASSERT(mDocument, "Must have a document to insert into");
1201
MOZ_ASSERT(!aLinkingContent || aLinkingContent->IsInUncomposedDoc() ||
1202
aLinkingContent->IsInShadowTree(),
1203
"Why would we insert it anywhere?");
1204
1205
nsCOMPtr<nsIStyleSheetLinkingElement> linkingElement =
1206
do_QueryInterface(aLinkingContent);
1207
if (linkingElement) {
1208
linkingElement->SetStyleSheet(&aSheet);
1209
}
1210
1211
ShadowRoot* shadow =
1212
aLinkingContent ? aLinkingContent->GetContainingShadow() : nullptr;
1213
1214
auto& target = shadow ? static_cast<DocumentOrShadowRoot&>(*shadow)
1215
: static_cast<DocumentOrShadowRoot&>(*mDocument);
1216
1217
// XXX Need to cancel pending sheet loads for this element, if any
1218
1219
int32_t sheetCount = target.SheetCount();
1220
1221
/*
1222
* Start the walk at the _end_ of the list, since in the typical
1223
* case we'll just want to append anyway. We want to break out of
1224
* the loop when insertionPoint points to just before the index we
1225
* want to insert at. In other words, when we leave the loop
1226
* insertionPoint is the index of the stylesheet that immediately
1227
* precedes the one we're inserting.
1228
*/
1229
int32_t insertionPoint = sheetCount - 1;
1230
for (; insertionPoint >= 0; --insertionPoint) {
1231
nsINode* sheetOwner = target.SheetAt(insertionPoint)->GetOwnerNode();
1232
if (sheetOwner && !aLinkingContent) {
1233
// Keep moving; all sheets with a sheetOwner come after all
1234
// sheets without a linkingNode
1235
continue;
1236
}
1237
1238
if (!sheetOwner) {
1239
// Aha! The current sheet has no sheet owner, so we want to insert after
1240
// it no matter whether we have a linking content or not.
1241
break;
1242
}
1243
1244
MOZ_ASSERT(aLinkingContent != sheetOwner,
1245
"Why do we still have our old sheet?");
1246
1247
// Have to compare
1248
if (nsContentUtils::PositionIsBefore(sheetOwner, aLinkingContent)) {
1249
// The current sheet comes before us, and it better be the first
1250
// such, because now we break
1251
break;
1252
}
1253
}
1254
1255
++insertionPoint;
1256
1257
if (shadow) {
1258
shadow->InsertSheetAt(insertionPoint, aSheet);
1259
} else {
1260
mDocument->InsertSheetAt(insertionPoint, aSheet);
1261
}
1262
1263
LOG((" Inserting into target (doc: %d) at position %d",
1264
target.AsNode().IsDocument(), insertionPoint));
1265
}
1266
1267
/**
1268
* InsertChildSheet handles ordering of @import-ed sheet in their
1269
* parent sheets. Here we want to just insert based on order of the
1270
* @import rules that imported the sheets. In theory we can't just
1271
* append to the end because the CSSOM can insert @import rules. In
1272
* practice, we get the call to load the child sheet before the CSSOM
1273
* has finished inserting the @import rule, so we have no idea where
1274
* to put it anyway. So just append for now. (In the future if we
1275
* want to insert the sheet at the correct position, we'll need to
1276
* restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
1277
* bug 1220506.)
1278
*/
1279
void Loader::InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet) {
1280
LOG(("css::Loader::InsertChildSheet"));
1281
1282
// child sheets should always start out enabled, even if they got
1283
// cloned off of top-level sheets which were disabled
1284
aSheet.SetEnabled(true);
1285
aParentSheet.PrependStyleSheet(&aSheet);
1286
1287
LOG((" Inserting into parent sheet"));
1288
}
1289
1290
/**
1291
* LoadSheet handles the actual load of a sheet. If the load is
1292
* supposed to be synchronous it just opens a channel synchronously
1293
* using the given uri, wraps the resulting stream in a converter
1294
* stream and calls ParseSheet. Otherwise it tries to look for an
1295
* existing load for this URI and piggyback on it. Failing all that,
1296
* a new load is kicked off asynchronously.
1297
*/
1298
nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState,
1299
IsPreload aIsPreload) {
1300
LOG(("css::Loader::LoadSheet"));
1301
MOZ_ASSERT(aLoadData.mURI, "Need a URI to load");
1302
MOZ_ASSERT(aLoadData.mSheet, "Need a sheet to load into");
1303
MOZ_ASSERT(aSheetState != SheetState::Complete, "Why bother?");
1304
MOZ_ASSERT(!aLoadData.mUseSystemPrincipal || aLoadData.mSyncLoad,
1305
"Shouldn't use system principal for async loads");
1306
NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
1307
1308
LOG_URI(" Load from: '%s'", aLoadData.mURI);
1309
1310
nsresult rv = NS_OK;
1311
1312
if (!mDocument && !aLoadData.mIsNonDocumentSheet) {
1313
// No point starting the load; just release all the data and such.
1314
LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
1315
SheetComplete(aLoadData, NS_BINDING_ABORTED);
1316
return NS_BINDING_ABORTED;
1317
}
1318
1319
SRIMetadata sriMetadata;
1320
aLoadData.mSheet->GetIntegrity(sriMetadata);
1321
1322
if (aLoadData.mSyncLoad) {
1323
LOG((" Synchronous load"));
1324
MOZ_ASSERT(!aLoadData.mObserver, "Observer for a sync load?");
1325
MOZ_ASSERT(aSheetState == SheetState::NeedsParser,
1326
"Sync loads can't reuse existing async loads");
1327
1328
// Create a StreamLoader instance to which we will feed
1329
// the data from the sync load. Do this before creating the
1330
// channel to make error recovery simpler.
1331
nsCOMPtr<nsIStreamListener> streamLoader = new StreamLoader(aLoadData);
1332
1333
if (mDocument) {
1334
mozilla::net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1335
nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
1336
mDocument);
1337
}
1338
1339
nsSecurityFlags securityFlags =
1340
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
1341
nsILoadInfo::SEC_ALLOW_CHROME;
1342
1343
nsContentPolicyType contentPolicyType =
1344
aIsPreload == IsPreload::Yes
1345
? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
1346
: nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
1347
1348
// Just load it
1349
nsCOMPtr<nsIChannel> channel;
1350
// Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
1351
// a node and a principal.
1352
// This is because of a case where the node is the document being styled and
1353
// the principal is the stylesheet (perhaps from a different origin) that is
1354
// applying the styles.
1355
if (aLoadData.mRequestingNode && aLoadData.mLoaderPrincipal) {
1356
rv = NS_NewChannelWithTriggeringPrincipal(
1357
getter_AddRefs(channel), aLoadData.mURI, aLoadData.mRequestingNode,
1358
aLoadData.mLoaderPrincipal, securityFlags, contentPolicyType);
1359
} else {
1360
// either we are loading something inside a document, in which case
1361
// we should always have a requestingNode, or we are loading something
1362
// outside a document, in which case the loadingPrincipal and the
1363
// triggeringPrincipal should always be the systemPrincipal.
1364
auto result = URLPreloader::ReadURI(aLoadData.mURI);
1365
if (result.isOk()) {
1366
nsCOMPtr<nsIInputStream> stream;
1367
MOZ_TRY(
1368
NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap()));
1369
1370
rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aLoadData.mURI,
1371
stream.forget(),
1372
nsContentUtils::GetSystemPrincipal(),
1373
securityFlags, contentPolicyType);
1374
} else {
1375
rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1376
nsContentUtils::GetSystemPrincipal(), securityFlags,
1377
contentPolicyType);
1378
}
1379
}
1380
if (NS_FAILED(rv)) {
1381
LOG_ERROR((" Failed to create channel"));
1382
SheetComplete(aLoadData, rv);
1383
return rv;
1384
}
1385
1386
// snapshot the nonce at load start time for performing CSP checks
1387
if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
1388
nsCOMPtr<Element> element = do_QueryInterface(aLoadData.mRequestingNode);
1389
if (element && element->IsHTMLElement()) {
1390
nsAutoString cspNonce;
1391
element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
1392
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1393
loadInfo->SetCspNonce(cspNonce);
1394
}
1395
}
1396
1397
nsCOMPtr<nsIInputStream> stream;
1398
rv = channel->Open(getter_AddRefs(stream));
1399
1400
if (NS_FAILED(rv)) {
1401
LOG_ERROR((" Failed to open URI synchronously"));
1402
SheetComplete(aLoadData, rv);
1403
return rv;
1404
}
1405
1406
// Force UA sheets to be UTF-8.
1407
// XXX this is only necessary because the default in
1408
// SheetLoadData::OnDetermineCharset is wrong (bug 521039).
1409
channel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
1410
1411
// Manually feed the streamloader the contents of the stream.
1412
// This will call back into OnStreamComplete
1413
// and thence to ParseSheet. Regardless of whether this fails,
1414
// SheetComplete has been called.
1415
return nsSyncLoadService::PushSyncStreamToListener(stream.forget(),
1416
streamLoader, channel);
1417
}
1418
1419
SheetLoadData* existingData = nullptr;
1420
1421
SheetLoadDataHashKey key(aLoadData);
1422
1423
if (aSheetState == SheetState::Loading) {
1424
existingData = mSheets->mLoadingDatas.Get(&key);
1425
NS_ASSERTION(existingData, "CreateSheet lied about the state");
1426
} else if (aSheetState == SheetState::Pending) {
1427
existingData = mSheets->mPendingDatas.GetWeak(&key);
1428
NS_ASSERTION(existingData, "CreateSheet lied about the state");
1429
}
1430
1431
if (existingData) {
1432
LOG((" Glomming on to existing load"));
1433
SheetLoadData* data = existingData;
1434
while (data->mNext) {
1435
data = data->mNext;
1436
}
1437
data->mNext = &aLoadData;
1438
if (aSheetState == SheetState::Pending && !aLoadData.ShouldDefer()) {
1439
// Kick the load off; someone cares about it right away
1440
RefPtr<SheetLoadData> removedData;
1441
mSheets->mPendingDatas.Remove(&key, getter_AddRefs(removedData));
1442
MOZ_ASSERT(removedData == existingData, "Bad loading table");
1443
1444
LOG((" Forcing load of pending data"));
1445
return LoadSheet(*removedData, SheetState::NeedsParser, aIsPreload);
1446
}
1447
// All done here; once the load completes we'll be marked complete
1448
// automatically
1449
return NS_OK;
1450
}
1451
1452
nsCOMPtr<nsILoadGroup> loadGroup;
1453
nsCOMPtr<nsICookieSettings> cookieSettings;
1454
if (mDocument) {
1455
loadGroup = mDocument->GetDocumentLoadGroup();
1456
// load for a document with no loadgrup indicates that something is
1457
// completely bogus, let's bail out early.
1458
if (!loadGroup) {
1459
LOG_ERROR((" Failed to query loadGroup from document"));
1460
SheetComplete(aLoadData, NS_ERROR_UNEXPECTED);
1461
return NS_ERROR_UNEXPECTED;
1462
}
1463
1464
cookieSettings = mDocument->CookieSettings();
1465
}
1466
1467
#ifdef DEBUG
1468
AutoRestore<bool> syncCallbackGuard(mSyncCallback);
1469
mSyncCallback = true;
1470
#endif
1471
1472
CORSMode ourCORSMode = aLoadData.mSheet->GetCORSMode();
1473
nsSecurityFlags securityFlags =
1474
ourCORSMode == CORS_NONE
1475
? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS
1476
: nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
1477
if (ourCORSMode == CORS_ANONYMOUS) {
1478
securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
1479
} else if (ourCORSMode == CORS_USE_CREDENTIALS) {
1480
securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
1481
}
1482
securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
1483
1484
nsContentPolicyType contentPolicyType =
1485
aIsPreload == IsPreload::Yes
1486
? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
1487
: nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
1488
1489
nsCOMPtr<nsIChannel> channel;
1490
// Note we are calling NS_NewChannelWithTriggeringPrincipal here with a node
1491
// and a principal. This is because of a case where the node is the document
1492
// being styled and the principal is the stylesheet (perhaps from a different
1493
// origin) that is applying the styles.
1494
if (aLoadData.mRequestingNode && aLoadData.mLoaderPrincipal) {
1495
rv = NS_NewChannelWithTriggeringPrincipal(
1496
getter_AddRefs(channel), aLoadData.mURI, aLoadData.mRequestingNode,
1497
aLoadData.mLoaderPrincipal, securityFlags, contentPolicyType,
1498
/* PerformanceStorage */ nullptr, loadGroup);
1499
} else {
1500
// either we are loading something inside a document, in which case
1501
// we should always have a requestingNode, or we are loading something
1502
// outside a document, in which case the loadingPrincipal and the
1503
// triggeringPrincipal should always be the systemPrincipal.
1504
rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1505
nsContentUtils::GetSystemPrincipal(), securityFlags,
1506
contentPolicyType, cookieSettings,
1507
/* aPerformanceStorage */ nullptr, loadGroup);
1508
}
1509
1510
if (NS_FAILED(rv)) {
1511
LOG_ERROR((" Failed to create channel"));
1512
SheetComplete(aLoadData, rv);
1513
return rv;
1514
}
1515
1516
// snapshot the nonce at load start time for performing CSP checks
1517
if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
1518
nsCOMPtr<Element> element = do_QueryInterface(aLoadData.mRequestingNode);
1519
if (element && element->IsHTMLElement()) {
1520
nsAutoString cspNonce;
1521
element->GetAttribute(NS_LITERAL_STRING("nonce"), cspNonce);
1522
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1523
loadInfo->SetCspNonce(cspNonce);
1524
}
1525
}
1526
1527
if (!aLoadData.ShouldDefer()) {
1528
if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(channel)) {
1529
cos->AddClassFlags(nsIClassOfService::Leader);
1530
}
1531
}
1532
1533
if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
1534
if (nsCOMPtr<nsIReferrerInfo> referrerInfo = aLoadData.ReferrerInfo()) {
1535
rv = httpChannel->SetReferrerInfo(referrerInfo);
1536
Unused << NS_WARN_IF(NS_FAILED(rv));
1537
}
1538
1539
nsCOMPtr<nsIHttpChannelInternal> internalChannel =
1540
do_QueryInterface(httpChannel);
1541
if (internalChannel) {
1542
rv = internalChannel->SetIntegrityMetadata(
1543
sriMetadata.GetIntegrityString());
1544
NS_ENSURE_SUCCESS(rv, rv);
1545
}
1546
1547
// Set the initiator type
1548
if (nsCOMPtr<nsITimedChannel> timedChannel =
1549
do_QueryInterface(httpChannel)) {
1550
if (aLoadData.mParentData) {
1551
timedChannel->SetInitiatorType(NS_LITERAL_STRING("css"));
1552
1553
// This is a child sheet load.
1554
//
1555
// The resource timing of the sub-resources that a document loads
1556
// should normally be reported to the document. One exception is any
1557
// sub-resources of any cross-origin resources that are loaded. We
1558
// don't mind reporting timing data for a direct child cross-origin
1559
// resource since the resource that linked to it (and hence potentially
1560
// anything in that parent origin) is aware that the cross-origin
1561
// resources is to be loaded. However, we do not want to report
1562
// timings for any sub-resources that a cross-origin resource may load
1563
// since that obviously leaks information about what the cross-origin
1564
// resource loads, which is bad.
1565
//
1566
// In addition to checking whether we're an immediate child resource of
1567
// a cross-origin resource (by checking if mIsCrossOriginNoCORS is set
1568
// to true on our parent), we also check our parent to see whether it
1569
// itself is a sub-resource of a cross-origin resource by checking
1570
// mBlockResourceTiming. If that is set then we too are such a
1571
// sub-resource and so we set the flag on ourself too to propagate it
1572
// on down.
1573
//
1574
if (aLoadData.mParentData->mIsCrossOriginNoCORS ||
1575
aLoadData.mParentData->mBlockResourceTiming) {
1576
// Set a flag so any other stylesheet triggered by this one will
1577
// not be reported
1578
aLoadData.mBlockResourceTiming = true;
1579
1580
// Mark the channel so PerformanceMainThread::AddEntry will not
1581
// report the resource.
1582
timedChannel->SetReportResourceTiming(false);
1583
}
1584
1585
} else {
1586
timedChannel->SetInitiatorType(NS_LITERAL_STRING("link"));
1587
}
1588
}
1589
}
1590
1591
// Now tell the channel we expect text/css data back.... We do
1592
// this before opening it, so it's only treated as a hint.
1593
channel->SetContentType(NS_LITERAL_CSTRING("text/css"));
1594
1595
// We don't have to hold on to the stream loader. The ownership
1596
// model is: Necko owns the stream loader, which owns the load data,
1597
// which owns us
1598
nsCOMPtr<nsIStreamListener> streamLoader = new StreamLoader(aLoadData);
1599
1600
if (mDocument) {
1601
mozilla::net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1602
nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
1603
mDocument);
1604
}
1605
1606
rv = channel->AsyncOpen(streamLoader);
1607
1608
if (NS_FAILED(rv)) {
1609
LOG_ERROR((" Failed to create stream loader"));
1610
SheetComplete(aLoadData, rv);
1611
return rv;
1612
}
1613
1614
mSheets->mLoadingDatas.Put(&key, &aLoadData);
1615
aLoadData.mIsLoading = true;
1616
1617
return NS_OK;
1618
}
1619
1620
/**
1621
* ParseSheet handles parsing the data stream.
1622
*/
1623
Loader::Completed Loader::ParseSheet(const nsACString& aBytes,
1624
SheetLoadData& aLoadData,
1625
AllowAsyncParse aAllowAsync) {
1626
LOG(("css::Loader::ParseSheet"));
1627
AUTO_PROFILER_LABEL("css::Loader::ParseSheet", LAYOUT_CSSParsing);
1628
aLoadData.mIsBeingParsed = true;
1629
1630
// Tell the record/replay system about any sheets that are being parsed,
1631
// so that devtools code can find them later.
1632
if (recordreplay::IsRecordingOrReplaying() && aLoadData.mURI) {
1633
recordreplay::NoteContentParse(
1634
&aLoadData, aLoadData.mURI->GetSpecOrDefault().get(), "text/css",
1635
reinterpret_cast<const Utf8Unit*>(aBytes.BeginReading()),
1636
aBytes.Length());
1637
}
1638
1639
StyleSheet* sheet = aLoadData.mSheet;
1640
MOZ_ASSERT(sheet);
1641
1642
// Some cases, like inline style and UA stylesheets, need to be parsed
1643
// synchronously. The former may trigger child loads, the latter must not.
1644
if (aLoadData.mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
1645
sheet->ParseSheetSync(this, aBytes, &aLoadData, aLoadData.mLineNumber);
1646
aLoadData.mIsBeingParsed = false;
1647
1648
bool noPendingChildren = aLoadData.mPendingChildren == 0;
1649
MOZ_ASSERT_IF(aLoadData.mSyncLoad, noPendingChildren);
1650
if (noPendingChildren) {
1651
SheetComplete(aLoadData, NS_OK);
1652
return Completed::Yes;
1653
}
1654
return Completed::No;
1655
}
1656
1657
// This parse does not need to be synchronous. \o/
1658
//
1659
// Note that we need to block onload because there may be no network requests
1660
// pending.
1661
BlockOnload();
1662
nsCOMPtr<nsISerialEventTarget> target = DispatchTarget();
1663
sheet->ParseSheet(*this, aBytes, aLoadData)
1664
->Then(
1665
target, __func__,
1666
[loadData = RefPtr<SheetLoadData>(&aLoadData)](bool aDummy) {
1667
MOZ_ASSERT(NS_IsMainThread());
1668
loadData->mIsBeingParsed = false;
1669
loadData->mLoader->UnblockOnload(/* aFireSync = */ false);
1670
// If there are no child sheets outstanding, mark us as complete.
1671
// Otherwise, the children are holding strong refs to the data
1672
// and will call SheetComplete() on it when they complete.
1673
if (loadData->mPendingChildren == 0) {
1674
loadData->mLoader->SheetComplete(*loadData, NS_OK);
1675
}
1676
},
1677
[] { MOZ_CRASH("rejected parse promise"); });
1678
return Completed::No;
1679
}
1680
1681
/**
1682
* SheetComplete is the do-it-all cleanup function. It removes the
1683
* load data from the "loading" hashtable, adds the sheet to the
1684
* "completed" hashtable, massages the XUL cache, handles siblings of
1685
* the load data (other loads for the same URI), handles unblocking
1686
* blocked parent loads as needed, and most importantly calls
1687
* NS_RELEASE on the load data to destroy the whole mess.
1688
*/
1689
void Loader::SheetComplete(SheetLoadData& aLoadData, nsresult aStatus) {
1690
LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32,
1691
static_cast<uint32_t>(aStatus)));
1692
1693
// If aStatus is a failure we need to mark this data failed. We also need to
1694
// mark any ancestors of a failing data as failed and any sibling of a
1695
// failing data as failed. Note that SheetComplete is never called on a
1696
// SheetLoadData that is the mNext of some other SheetLoadData.
1697
if (NS_FAILED(aStatus)) {
1698
MarkLoadTreeFailed(aLoadData);
1699
}
1700
1701
if (mDocument) {
1702
mDocument->MaybeWarnAboutZoom();
1703
}
1704
1705
// 8 is probably big enough for all our common cases. It's not likely that
1706
// imports will nest more than 8 deep, and multiple sheets with the same URI
1707
// are rare.
1708
AutoTArray<RefPtr<SheetLoadData>, 8> datasToNotify;
1709
DoSheetComplete(aLoadData, datasToNotify);
1710
1711
// Now it's safe to go ahead and notify observers
1712
uint32_t count = datasToNotify.Length();
1713
mDatasToNotifyOn += count;
1714
for (RefPtr<SheetLoadData>& data : datasToNotify) {
1715
--mDatasToNotifyOn;
1716
1717
MOZ_ASSERT(data, "How did this data get here?");
1718
if (data->mObserver) {
1719
LOG((" Notifying observer %p for data %p. deferred: %d",
1720
data->mObserver.get(), data.get(), data->ShouldDefer()));
1721
data->mObserver->StyleSheetLoaded(data->mSheet, data->ShouldDefer(),
1722
aStatus);
1723
}
1724
1725
nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver>>::ForwardIterator iter(
1726
mObservers);
1727
nsCOMPtr<nsICSSLoaderObserver> obs;
1728
while (iter.HasMore()) {
1729
obs = iter.GetNext();
1730
LOG((" Notifying global observer %p for data %p. deferred: %d",
1731
obs.get(), data.get(), data->ShouldDefer()));
1732
obs->StyleSheetLoaded(data->mSheet, data->ShouldDefer(), aStatus);
1733
}
1734
}
1735
1736
if (mSheets && mSheets->mLoadingDatas.Count() == 0 &&
1737
mSheets->mPendingDatas.Count() > 0) {
1738
LOG((" No more loading sheets; starting deferred loads"));
1739
StartDeferredLoads();
1740
}
1741
}
1742
1743
void Loader::DoSheetComplete(SheetLoadData& aLoadData,
1744
LoadDataArray& aDatasToNotify) {
1745
LOG(("css::Loader::DoSheetComplete"));
1746
MOZ_ASSERT(aLoadData.mSheet, "Must have a sheet");
1747
NS_ASSERTION(mSheets || !aLoadData.mURI,
1748
"mLoadingDatas should be initialized by now.");
1749
1750
// Twiddle the hashtables
1751
if (aLoadData.mURI) {
1752
LOG_URI(" Finished loading: '%s'", aLoadData.mURI);
1753
// Remove the data from the list of loading datas
1754
if (aLoadData.mIsLoading) {
1755
SheetLoadDataHashKey key(aLoadData);
1756
#ifdef DEBUG
1757
SheetLoadData* loadingData;
1758
NS_ASSERTION(mSheets->mLoadingDatas.Get(&key, &loadingData) &&
1759
loadingData == &aLoadData,
1760
"Bad loading table");
1761
#endif
1762
1763
mSheets->mLoadingDatas.Remove(&key);
1764
aLoadData.mIsLoading = false;
1765
}
1766
}
1767
1768
// Go through and deal with the whole linked list.
1769
SheetLoadData* data = &aLoadData;
1770
do {
1771
if (!data->mSheetAlreadyComplete) {
1772
// If mSheetAlreadyComplete, then the sheet could well be modified between
1773
// when we posted the async call to SheetComplete and now, since the sheet
1774
// was page-accessible during that whole time.
1775
MOZ_ASSERT(!data->mSheet->HasForcedUniqueInner(),
1776
"should not get a forced unique inner during parsing");
1777
data->mSheet->SetComplete();
1778
data->ScheduleLoadEventIfNeeded();
1779
}
1780
if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
1781
// Don't notify here so we don't trigger script. Remember the
1782
// info we need to notify, then do it later when it's safe.
1783
aDatasToNotify.AppendElement(data);
1784
1785
// On append failure, just press on. We'll fail to notify the observer,
1786
// but not much we can do about that....
1787
}
1788
1789
NS_ASSERTION(!data->mParentData || data->mParentData->mPendingChildren != 0,
1790
"Broken pending child count on our parent");
1791
1792
// If we have a parent, our parent is no longer being parsed, and
1793
// we are the last pending child, then our load completion
1794
// completes the parent too. Note that the parent _can_ still be
1795
// being parsed (eg if the child (us) failed to open the channel
1796
// or some such).
1797
if (data->mParentData && --(data->mParentData->mPendingChildren) == 0 &&
1798
!data->mParentData->mIsBeingParsed) {
1799
DoSheetComplete(*data->mParentData, aDatasToNotify);
1800
}
1801
1802
data = data->mNext;
1803
} while (