Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/style/Loader.cpp
Line
Count
Source (jump to first uncovered line)
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/LoadInfo.h"
16
#include "mozilla/Logging.h"
17
#include "mozilla/MemoryReporting.h"
18
#include "mozilla/StyleSheetInlines.h"
19
#include "mozilla/SystemGroup.h"
20
#include "mozilla/ResultExtensions.h"
21
#include "mozilla/URLPreloader.h"
22
#include "nsIRunnable.h"
23
#include "nsSyncLoadService.h"
24
#include "nsCOMPtr.h"
25
#include "nsString.h"
26
#include "nsIContent.h"
27
#include "nsIDocument.h"
28
#include "nsIURI.h"
29
#include "nsNetUtil.h"
30
#include "nsIProtocolHandler.h"
31
#include "nsContentUtils.h"
32
#include "nsIScriptSecurityManager.h"
33
#include "nsContentPolicyUtils.h"
34
#include "nsIHttpChannel.h"
35
#include "nsIHttpChannelInternal.h"
36
#include "nsIClassOfService.h"
37
#include "nsIScriptError.h"
38
#include "nsMimeTypes.h"
39
#include "nsIStyleSheetLinkingElement.h"
40
#include "nsICSSLoaderObserver.h"
41
#include "nsThreadUtils.h"
42
#include "nsGkAtoms.h"
43
#include "nsIThreadInternal.h"
44
#include "nsINetworkPredictor.h"
45
#include "nsStringStream.h"
46
#include "mozilla/dom/MediaList.h"
47
#include "mozilla/dom/ShadowRoot.h"
48
#include "mozilla/dom/URL.h"
49
#include "mozilla/AsyncEventDispatcher.h"
50
#include "mozilla/ServoBindings.h"
51
#include "mozilla/StyleSheet.h"
52
#include "mozilla/StyleSheetInlines.h"
53
#include "mozilla/ConsoleReportCollector.h"
54
#include "mozilla/ServoUtils.h"
55
#include "mozilla/css/StreamLoader.h"
56
57
#ifdef MOZ_XUL
58
#include "nsXULPrototypeCache.h"
59
#endif
60
61
#include "nsError.h"
62
63
#include "nsIContentSecurityPolicy.h"
64
#include "mozilla/dom/SRICheck.h"
65
66
#include "mozilla/Encoding.h"
67
#include "mozilla/Logging.h"
68
69
using namespace mozilla::dom;
70
71
// 1024 bytes is specified in https://drafts.csswg.org/css-syntax/
72
0
#define SNIFFING_BUFFER_SIZE 1024
73
74
/**
75
 * OVERALL ARCHITECTURE
76
 *
77
 * The CSS Loader gets requests to load various sorts of style sheets:
78
 * inline style from <style> elements, linked style, @import-ed child
79
 * sheets, non-document sheets.  The loader handles the following tasks:
80
 * 1) Creation of the actual style sheet objects: CreateSheet()
81
 * 2) setting of the right media, title, enabled state, etc on the
82
 *    sheet: PrepareSheet()
83
 * 3) Insertion of the sheet in the proper cascade order:
84
 *    InsertSheetInTree() and InsertChildSheet()
85
 * 4) Load of the sheet: LoadSheet() including security checks
86
 * 5) Parsing of the sheet: ParseSheet()
87
 * 6) Cleanup: SheetComplete()
88
 *
89
 * The detailed documentation for these functions is found with the
90
 * function implementations.
91
 *
92
 * The following helper object is used:
93
 *    SheetLoadData -- a small class that is used to store all the
94
 *                     information needed for the loading of a sheet;
95
 *                     this class handles listening for the stream
96
 *                     loader completion and also handles charset
97
 *                     determination.
98
 */
99
100
static mozilla::LazyLogModule sCssLoaderLog("nsCSSLoader");
101
102
static mozilla::LazyLogModule gSriPRLog("SRI");
103
104
0
#define LOG_ERROR(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Error, args)
105
0
#define LOG_WARN(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Warning, args)
106
0
#define LOG_DEBUG(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Debug, args)
107
0
#define LOG(args) LOG_DEBUG(args)
108
109
#define LOG_ERROR_ENABLED() MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Error)
110
#define LOG_WARN_ENABLED() MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Warning)
111
0
#define LOG_DEBUG_ENABLED() MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Debug)
112
0
#define LOG_ENABLED() LOG_DEBUG_ENABLED()
113
114
#define LOG_URI(format, uri)                        \
115
0
  PR_BEGIN_MACRO                                    \
116
0
    NS_ASSERTION(uri, "Logging null uri");          \
117
0
    if (LOG_ENABLED()) {                            \
118
0
      LOG((format, uri->GetSpecOrDefault().get())); \
119
0
    }                                               \
120
0
  PR_END_MACRO
121
122
// And some convenience strings...
123
static const char* const gStateStrings[] = {
124
  "eSheetStateUnknown",
125
  "eSheetNeedsParser",
126
  "eSheetPending",
127
  "eSheetLoading",
128
  "eSheetComplete"
129
};
130
131
namespace mozilla {
132
133
URIPrincipalReferrerPolicyAndCORSModeHashKey::
134
URIPrincipalReferrerPolicyAndCORSModeHashKey(css::SheetLoadData* aLoadData)
135
  : nsURIHashKey(aLoadData->mURI)
136
  , mPrincipal(aLoadData->mLoaderPrincipal)
137
  , mCORSMode(aLoadData->mSheet->GetCORSMode())
138
  , mReferrerPolicy(aLoadData->mSheet->GetReferrerPolicy())
139
0
{
140
0
  MOZ_COUNT_CTOR(URIPrincipalReferrerPolicyAndCORSModeHashKey);
141
0
}
142
} // namespace mozilla
143
144
namespace mozilla {
145
namespace css {
146
147
/********************************
148
 * SheetLoadData implementation *
149
 ********************************/
150
NS_IMPL_ISUPPORTS(SheetLoadData, nsIRunnable,
151
                  nsIThreadObserver)
152
153
SheetLoadData::SheetLoadData(Loader* aLoader,
154
                             const nsAString& aTitle,
155
                             nsIURI* aURI,
156
                             StyleSheet* aSheet,
157
                             bool aSyncLoad,
158
                             nsIStyleSheetLinkingElement* aOwningElement,
159
                             IsAlternate aIsAlternate,
160
                             MediaMatched aMediaMatches,
161
                             nsICSSLoaderObserver* aObserver,
162
                             nsIPrincipal* aLoaderPrincipal,
163
                             nsINode* aRequestingNode)
164
  : mLoader(aLoader)
165
  , mTitle(aTitle)
166
  , mEncoding(nullptr)
167
  , mURI(aURI)
168
  , mLineNumber(1)
169
  , mSheet(aSheet)
170
  , mNext(nullptr)
171
  , mPendingChildren(0)
172
  , mSyncLoad(aSyncLoad)
173
  , mIsNonDocumentSheet(false)
174
  , mIsLoading(false)
175
  , mIsBeingParsed(false)
176
  , mIsCancelled(false)
177
  , mMustNotify(false)
178
  , mWasAlternate(aIsAlternate == IsAlternate::Yes)
179
  , mMediaMatched(aMediaMatches == MediaMatched::Yes)
180
  , mUseSystemPrincipal(false)
181
  , mSheetAlreadyComplete(false)
182
  , mIsCrossOriginNoCORS(false)
183
  , mBlockResourceTiming(false)
184
  , mLoadFailed(false)
185
  , mOwningElement(aOwningElement)
186
  , mObserver(aObserver)
187
  , mLoaderPrincipal(aLoaderPrincipal)
188
  , mRequestingNode(aRequestingNode)
189
  , mPreloadEncoding(nullptr)
190
0
{
191
0
  MOZ_ASSERT(mLoader, "Must have a loader!");
192
0
}
193
194
SheetLoadData::SheetLoadData(Loader* aLoader,
195
                             nsIURI* aURI,
196
                             StyleSheet* aSheet,
197
                             SheetLoadData* aParentData,
198
                             nsICSSLoaderObserver* aObserver,
199
                             nsIPrincipal* aLoaderPrincipal,
200
                             nsINode* aRequestingNode)
201
  : mLoader(aLoader)
202
  , mEncoding(nullptr)
203
  , mURI(aURI)
204
  , mLineNumber(1)
205
  , mSheet(aSheet)
206
  , mNext(nullptr)
207
  , mParentData(aParentData)
208
  , mPendingChildren(0)
209
  , mSyncLoad(false)
210
  , mIsNonDocumentSheet(false)
211
  , mIsLoading(false)
212
  , mIsBeingParsed(false)
213
  , mIsCancelled(false)
214
  , mMustNotify(false)
215
  , mWasAlternate(false)
216
  , mMediaMatched(true)
217
  , mUseSystemPrincipal(false)
218
  , mSheetAlreadyComplete(false)
219
  , mIsCrossOriginNoCORS(false)
220
  , mBlockResourceTiming(false)
221
  , mLoadFailed(false)
222
  , mOwningElement(nullptr)
223
  , mObserver(aObserver)
224
  , mLoaderPrincipal(aLoaderPrincipal)
225
  , mRequestingNode(aRequestingNode)
226
  , mPreloadEncoding(nullptr)
227
0
{
228
0
  MOZ_ASSERT(mLoader, "Must have a loader!");
229
0
  if (mParentData) {
230
0
    mSyncLoad = mParentData->mSyncLoad;
231
0
    mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet;
232
0
    mUseSystemPrincipal = mParentData->mUseSystemPrincipal;
233
0
    ++(mParentData->mPendingChildren);
234
0
  }
235
0
236
0
  MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
237
0
             "Shouldn't use system principal for async loads");
238
0
}
239
240
SheetLoadData::SheetLoadData(Loader* aLoader,
241
                             nsIURI* aURI,
242
                             StyleSheet* aSheet,
243
                             bool aSyncLoad,
244
                             bool aUseSystemPrincipal,
245
                             const Encoding* aPreloadEncoding,
246
                             nsICSSLoaderObserver* aObserver,
247
                             nsIPrincipal* aLoaderPrincipal,
248
                             nsINode* aRequestingNode)
249
  : mLoader(aLoader)
250
  , mEncoding(nullptr)
251
  , mURI(aURI)
252
  , mLineNumber(1)
253
  , mSheet(aSheet)
254
  , mNext(nullptr)
255
  , mPendingChildren(0)
256
  , mSyncLoad(aSyncLoad)
257
  , mIsNonDocumentSheet(true)
258
  , mIsLoading(false)
259
  , mIsBeingParsed(false)
260
  , mIsCancelled(false)
261
  , mMustNotify(false)
262
  , mWasAlternate(false)
263
  , mMediaMatched(true)
264
  , mUseSystemPrincipal(aUseSystemPrincipal)
265
  , mSheetAlreadyComplete(false)
266
  , mIsCrossOriginNoCORS(false)
267
  , mBlockResourceTiming(false)
268
  , mLoadFailed(false)
269
  , mOwningElement(nullptr)
270
  , mObserver(aObserver)
271
  , mLoaderPrincipal(aLoaderPrincipal)
272
  , mRequestingNode(aRequestingNode)
273
  , mPreloadEncoding(aPreloadEncoding)
274
0
{
275
0
  MOZ_ASSERT(mLoader, "Must have a loader!");
276
0
  MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
277
0
             "Shouldn't use system principal for async loads");
278
0
}
279
280
SheetLoadData::~SheetLoadData()
281
0
{
282
0
  NS_CSS_NS_RELEASE_LIST_MEMBER(SheetLoadData, this, mNext);
283
0
}
284
285
NS_IMETHODIMP
286
SheetLoadData::Run()
287
0
{
288
0
  mLoader->HandleLoadEvent(this);
289
0
  return NS_OK;
290
0
}
291
292
NS_IMETHODIMP
293
SheetLoadData::OnDispatchedEvent()
294
0
{
295
0
  return NS_OK;
296
0
}
297
298
NS_IMETHODIMP
299
SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread,
300
                                  bool aMayWait)
301
0
{
302
0
  // XXXkhuey this is insane!
303
0
  // We want to fire our load even before or after event processing,
304
0
  // whichever comes first.
305
0
  FireLoadEvent(aThread);
306
0
  return NS_OK;
307
0
}
308
309
NS_IMETHODIMP
310
SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread,
311
                                     bool aEventWasProcessed)
312
0
{
313
0
  // XXXkhuey this too!
314
0
  // We want to fire our load even before or after event processing,
315
0
  // whichever comes first.
316
0
  FireLoadEvent(aThread);
317
0
  return NS_OK;
318
0
}
319
320
void
321
SheetLoadData::FireLoadEvent(nsIThreadInternal* aThread)
322
0
{
323
0
324
0
  // First remove ourselves as a thread observer.  But we need to keep
325
0
  // ourselves alive while doing that!
326
0
  RefPtr<SheetLoadData> kungFuDeathGrip(this);
327
0
  aThread->RemoveObserver(this);
328
0
329
0
  // Now fire the event
330
0
  nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
331
0
  NS_ASSERTION(node, "How did that happen???");
332
0
333
0
  nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(),
334
0
                                       node,
335
0
                                       mLoadFailed ?
336
0
                                         NS_LITERAL_STRING("error") :
337
0
                                         NS_LITERAL_STRING("load"),
338
0
                                       CanBubble::eNo, Cancelable::eNo);
339
0
340
0
  // And unblock onload
341
0
  mLoader->UnblockOnload(true);
342
0
}
343
344
void
345
SheetLoadData::ScheduleLoadEventIfNeeded()
346
0
{
347
0
  if (!mOwningElement) {
348
0
    return;
349
0
  }
350
0
351
0
  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
352
0
  nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
353
0
  if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
354
0
    // Make sure to block onload here
355
0
    mLoader->BlockOnload();
356
0
  }
357
0
}
358
359
/*********************
360
 * Style sheet reuse *
361
 *********************/
362
363
bool
364
LoaderReusableStyleSheets::FindReusableStyleSheet(nsIURI* aURL,
365
                                                  RefPtr<StyleSheet>& aResult)
366
0
{
367
0
  MOZ_ASSERT(aURL);
368
0
  for (size_t i = mReusableSheets.Length(); i > 0; --i) {
369
0
    size_t index = i - 1;
370
0
    bool sameURI;
371
0
    MOZ_ASSERT(mReusableSheets[index]->GetOriginalURI());
372
0
    nsresult rv = aURL->Equals(mReusableSheets[index]->GetOriginalURI(),
373
0
                               &sameURI);
374
0
    if (!NS_FAILED(rv) && sameURI) {
375
0
      aResult = mReusableSheets[index];
376
0
      mReusableSheets.RemoveElementAt(index);
377
0
      return true;
378
0
    }
379
0
  }
380
0
  return false;
381
0
}
382
383
/*************************
384
 * Loader Implementation *
385
 *************************/
386
387
Loader::Loader()
388
  : mDocument(nullptr)
389
  , mDatasToNotifyOn(0)
390
  , mCompatMode(eCompatibility_FullStandards)
391
  , mEnabled(true)
392
  , mReporter(new ConsoleReportCollector())
393
#ifdef DEBUG
394
  , mSyncCallback(false)
395
#endif
396
0
{
397
0
}
398
399
Loader::Loader(DocGroup* aDocGroup)
400
  : Loader()
401
0
{
402
0
  mDocGroup = aDocGroup;
403
0
}
404
405
Loader::Loader(nsIDocument* aDocument)
406
  : Loader()
407
0
{
408
0
  mDocument = aDocument;
409
0
  MOZ_ASSERT(mDocument, "We should get a valid document from the caller!");
410
0
}
411
412
Loader::~Loader()
413
0
{
414
0
  NS_ASSERTION(!mSheets || mSheets->mLoadingDatas.Count() == 0,
415
0
               "How did we get destroyed when there are loading data?");
416
0
  NS_ASSERTION(!mSheets || mSheets->mPendingDatas.Count() == 0,
417
0
               "How did we get destroyed when there are pending data?");
418
0
  // Note: no real need to revoke our stylesheet loaded events -- they
419
0
  // hold strong references to us, so if we're going away that means
420
0
  // they're all done.
421
0
}
422
423
void
424
Loader::DropDocumentReference(void)
425
0
{
426
0
  mDocument = nullptr;
427
0
  // Flush out pending datas just so we don't leak by accident.  These
428
0
  // loads should short-circuit through the mDocument check in
429
0
  // LoadSheet and just end up in SheetComplete immediately
430
0
  if (mSheets) {
431
0
    StartDeferredLoads();
432
0
  }
433
0
}
434
435
void
436
Loader::DocumentStyleSheetSetChanged()
437
0
{
438
0
  MOZ_ASSERT(mDocument);
439
0
440
0
  // start any pending alternates that aren't alternates anymore
441
0
  if (!mSheets) {
442
0
    return;
443
0
  }
444
0
445
0
  LoadDataArray arr(mSheets->mPendingDatas.Count());
446
0
  for (auto iter = mSheets->mPendingDatas.Iter(); !iter.Done(); iter.Next()) {
447
0
    SheetLoadData* data = iter.Data();
448
0
    MOZ_ASSERT(data, "Must have a data");
449
0
450
0
    // Note that we don't want to affect what the selected style set is, so
451
0
    // use true for aHasAlternateRel.
452
0
    auto isAlternate = data->mLoader->IsAlternateSheet(data->mTitle, true);
453
0
    if (isAlternate == IsAlternate::No) {
454
0
      arr.AppendElement(data);
455
0
      iter.Remove();
456
0
    }
457
0
  }
458
0
459
0
  mDatasToNotifyOn += arr.Length();
460
0
  for (uint32_t i = 0; i < arr.Length(); ++i) {
461
0
    --mDatasToNotifyOn;
462
0
    LoadSheet(arr[i], eSheetNeedsParser, false);
463
0
  }
464
0
}
465
466
static const char kCharsetSym[] = "@charset \"";
467
468
static bool GetCharsetFromData(const char* aStyleSheetData,
469
                               uint32_t aDataLength,
470
                               nsACString& aCharset)
471
0
{
472
0
  aCharset.Truncate();
473
0
  if (aDataLength <= sizeof(kCharsetSym) - 1)
474
0
    return false;
475
0
476
0
  if (strncmp(aStyleSheetData,
477
0
              kCharsetSym,
478
0
              sizeof(kCharsetSym) - 1)) {
479
0
    return false;
480
0
  }
481
0
482
0
  for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
483
0
    char c = aStyleSheetData[i];
484
0
    if (c == '"') {
485
0
      ++i;
486
0
      if (i < aDataLength && aStyleSheetData[i] == ';') {
487
0
        return true;
488
0
      }
489
0
      // fail
490
0
      break;
491
0
    }
492
0
    aCharset.Append(c);
493
0
  }
494
0
495
0
  // Did not see end quote or semicolon
496
0
  aCharset.Truncate();
497
0
  return false;
498
0
}
499
500
NotNull<const Encoding*>
501
SheetLoadData::DetermineNonBOMEncoding(nsACString const& aSegment,
502
                                       nsIChannel* aChannel)
503
0
{
504
0
  const Encoding* encoding;
505
0
  nsAutoCString label;
506
0
507
0
  // Check HTTP
508
0
  if (aChannel && NS_SUCCEEDED(aChannel->GetContentCharset(label))) {
509
0
    encoding = Encoding::ForLabel(label);
510
0
    if (encoding) {
511
0
      return WrapNotNull(encoding);
512
0
    }
513
0
  }
514
0
515
0
  // Check @charset
516
0
  auto sniffingLength = aSegment.Length();
517
0
  if (sniffingLength > SNIFFING_BUFFER_SIZE) {
518
0
    sniffingLength = SNIFFING_BUFFER_SIZE;
519
0
  }
520
0
  if (GetCharsetFromData(aSegment.BeginReading(), sniffingLength, label)) {
521
0
    encoding = Encoding::ForLabel(label);
522
0
    if (encoding == UTF_16BE_ENCODING || encoding == UTF_16LE_ENCODING) {
523
0
      return UTF_8_ENCODING;
524
0
    }
525
0
    if (encoding) {
526
0
      return WrapNotNull(encoding);
527
0
    }
528
0
  }
529
0
530
0
  // Now try the charset on the <link> or processing instruction
531
0
  // that loaded us
532
0
  if (mOwningElement) {
533
0
    nsAutoString label16;
534
0
    mOwningElement->GetCharset(label16);
535
0
    encoding = Encoding::ForLabel(label16);
536
0
    if (encoding) {
537
0
      return WrapNotNull(encoding);
538
0
    }
539
0
  }
540
0
541
0
  // In the preload case, the value of the charset attribute on <link> comes
542
0
  // in via mPreloadEncoding instead.
543
0
  if (mPreloadEncoding) {
544
0
    return WrapNotNull(mPreloadEncoding);
545
0
  }
546
0
547
0
  // Try charset from the parent stylesheet.
548
0
  if (mParentData) {
549
0
    encoding = mParentData->mEncoding;
550
0
    if (encoding) {
551
0
      return WrapNotNull(encoding);
552
0
    }
553
0
  }
554
0
555
0
  if (mLoader->mDocument) {
556
0
    // Use the document charset.
557
0
    return mLoader->mDocument->GetDocumentCharacterSet();
558
0
  }
559
0
560
0
  return UTF_8_ENCODING;
561
0
}
562
563
already_AddRefed<nsIURI>
564
SheetLoadData::GetReferrerURI()
565
0
{
566
0
  nsCOMPtr<nsIURI> uri;
567
0
  if (mParentData)
568
0
    uri = mParentData->mSheet->GetSheetURI();
569
0
  if (!uri && mLoader->mDocument)
570
0
    uri = mLoader->mDocument->GetDocumentURI();
571
0
  return uri.forget();
572
0
}
573
574
void
575
SheetLoadData::SetReferrerPolicyFromHeader(nsIChannel* aChannel)
576
0
{
577
0
  net::ReferrerPolicy policy =
578
0
    nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
579
0
  if (policy == net::RP_Unset ||
580
0
      policy == mSheet->GetReferrerPolicy()) {
581
0
    return;
582
0
  }
583
0
584
0
  URIPrincipalReferrerPolicyAndCORSModeHashKey oldKey(mURI,
585
0
                                                      mLoaderPrincipal,
586
0
                                                      mSheet->GetCORSMode(),
587
0
                                                      mSheet->GetReferrerPolicy());
588
0
589
0
  mSheet->SetReferrerPolicy(policy);
590
0
  mLoader->UpdateLoadingData(&oldKey, this);
591
0
}
592
593
static nsresult
594
VerifySheetIntegrity(const SRIMetadata& aMetadata,
595
                     nsIChannel* aChannel,
596
                     const nsACString& aFirst,
597
                     const nsACString& aSecond,
598
                     const nsACString& aSourceFileURI,
599
                     nsIConsoleReportCollector* aReporter)
600
0
{
601
0
  NS_ENSURE_ARG_POINTER(aReporter);
602
0
603
0
  if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) {
604
0
    nsAutoCString requestURL;
605
0
    nsCOMPtr<nsIURI> originalURI;
606
0
    if (aChannel &&
607
0
        NS_SUCCEEDED(aChannel->GetOriginalURI(getter_AddRefs(originalURI))) &&
608
0
        originalURI) {
609
0
      originalURI->GetAsciiSpec(requestURL);
610
0
    }
611
0
    MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
612
0
            ("VerifySheetIntegrity (unichar stream)"));
613
0
  }
614
0
615
0
  SRICheckDataVerifier verifier(aMetadata, aSourceFileURI, aReporter);
616
0
  nsresult rv =
617
0
    verifier.Update(aFirst.Length(), (const uint8_t*)aFirst.BeginReading());
618
0
  NS_ENSURE_SUCCESS(rv, rv);
619
0
  rv =
620
0
    verifier.Update(aSecond.Length(), (const uint8_t*)aSecond.BeginReading());
621
0
  NS_ENSURE_SUCCESS(rv, rv);
622
0
623
0
  return verifier.Verify(aMetadata, aChannel, aSourceFileURI, aReporter);
624
0
}
625
626
/*
627
 * Stream completion code shared by Stylo and the old style system.
628
 *
629
 * Here we need to check that the load did not give us an http error
630
 * page and check the mimetype on the channel to make sure we're not
631
 * loading non-text/css data in standards mode.
632
 */
633
nsresult
634
SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
635
                                       const nsACString& aBytes1,
636
                                       const nsACString& aBytes2,
637
                                       nsIChannel* aChannel)
638
0
{
639
0
  LOG(("SheetLoadData::VerifySheetReadyToParse"));
640
0
  NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
641
0
642
0
  if (mIsCancelled) {
643
0
    // Just return.  Don't call SheetComplete -- it's already been
644
0
    // called and calling it again will lead to an extra NS_RELEASE on
645
0
    // this data and a likely crash.
646
0
    return NS_OK;
647
0
  }
648
0
649
0
  if (!mLoader->mDocument && !mIsNonDocumentSheet) {
650
0
    // Sorry, we don't care about this load anymore
651
0
    LOG_WARN(("  No document and not non-document sheet; dropping load"));
652
0
    mLoader->SheetComplete(this, NS_BINDING_ABORTED);
653
0
    return NS_OK;
654
0
  }
655
0
656
0
  if (NS_FAILED(aStatus)) {
657
0
    LOG_WARN(("  Load failed: status 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
658
0
    // Handle sheet not loading error because source was a tracking URL.
659
0
    // We make a note of this sheet node by including it in a dedicated
660
0
    // array of blocked tracking nodes under its parent document.
661
0
    //
662
0
    // Multiple sheet load instances might be tied to this request,
663
0
    // we annotate each one linked to a valid owning element (node).
664
0
    if (aStatus == NS_ERROR_TRACKING_URI) {
665
0
      nsIDocument* doc = mLoader->GetDocument();
666
0
      if (doc) {
667
0
        for (SheetLoadData* data = this; data; data = data->mNext) {
668
0
          // mOwningElement may be null but AddBlockTrackingNode can cope
669
0
          nsCOMPtr<nsIContent> content = do_QueryInterface(data->mOwningElement);
670
0
          doc->AddBlockedTrackingNode(content);
671
0
        }
672
0
      }
673
0
    }
674
0
    mLoader->SheetComplete(this, aStatus);
675
0
    return NS_OK;
676
0
  }
677
0
678
0
  if (!aChannel) {
679
0
    mLoader->SheetComplete(this, NS_OK);
680
0
    return NS_OK;
681
0
  }
682
0
683
0
  nsCOMPtr<nsIURI> originalURI;
684
0
  aChannel->GetOriginalURI(getter_AddRefs(originalURI));
685
0
686
0
  // If the channel's original URI is "chrome:", we want that, since
687
0
  // the observer code in nsXULPrototypeCache depends on chrome stylesheets
688
0
  // having a chrome URI.  (Whether or not chrome stylesheets come through
689
0
  // this codepath seems nondeterministic.)
690
0
  // Otherwise we want the potentially-HTTP-redirected URI.
691
0
  nsCOMPtr<nsIURI> channelURI;
692
0
  NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
693
0
694
0
  if (!channelURI || !originalURI) {
695
0
    NS_ERROR("Someone just violated the nsIRequest contract");
696
0
    LOG_WARN(("  Channel without a URI.  Bad!"));
697
0
    mLoader->SheetComplete(this, NS_ERROR_UNEXPECTED);
698
0
    return NS_OK;
699
0
  }
700
0
701
0
  nsCOMPtr<nsIPrincipal> principal;
702
0
  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
703
0
  nsresult result = NS_ERROR_NOT_AVAILABLE;
704
0
  if (secMan) {  // Could be null if we already shut down
705
0
    if (mUseSystemPrincipal) {
706
0
      result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
707
0
    } else {
708
0
      result =
709
0
        secMan->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
710
0
    }
711
0
  }
712
0
713
0
  if (NS_FAILED(result)) {
714
0
    LOG_WARN(("  Couldn't get principal"));
715
0
    mLoader->SheetComplete(this, result);
716
0
    return NS_OK;
717
0
  }
718
0
719
0
  mSheet->SetPrincipal(principal);
720
0
721
0
  if (mLoaderPrincipal && mSheet->GetCORSMode() == CORS_NONE) {
722
0
    bool subsumed;
723
0
    result = mLoaderPrincipal->Subsumes(principal, &subsumed);
724
0
    if (NS_FAILED(result) || !subsumed) {
725
0
      mIsCrossOriginNoCORS = true;
726
0
    }
727
0
  }
728
0
729
0
  // If it's an HTTP channel, we want to make sure this is not an
730
0
  // error document we got.
731
0
  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
732
0
  if (httpChannel) {
733
0
    bool requestSucceeded;
734
0
    result = httpChannel->GetRequestSucceeded(&requestSucceeded);
735
0
    if (NS_SUCCEEDED(result) && !requestSucceeded) {
736
0
      LOG(("  Load returned an error page"));
737
0
      mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
738
0
      return NS_OK;
739
0
    }
740
0
741
0
    nsAutoCString sourceMapURL;
742
0
    if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
743
0
      mSheet->SetSourceMapURL(NS_ConvertUTF8toUTF16(sourceMapURL));
744
0
    }
745
0
  }
746
0
747
0
  nsAutoCString contentType;
748
0
  aChannel->GetContentType(contentType);
749
0
750
0
  // In standards mode, a style sheet must have one of these MIME
751
0
  // types to be processed at all.  In quirks mode, we accept any
752
0
  // MIME type, but only if the style sheet is same-origin with the
753
0
  // requesting document or parent sheet.  See bug 524223.
754
0
755
0
  bool validType = contentType.EqualsLiteral("text/css") ||
756
0
    contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
757
0
    contentType.IsEmpty();
758
0
759
0
  if (!validType) {
760
0
    const char *errorMessage;
761
0
    uint32_t errorFlag;
762
0
    bool sameOrigin = true;
763
0
764
0
    if (mLoaderPrincipal) {
765
0
      bool subsumed;
766
0
      result = mLoaderPrincipal->Subsumes(principal, &subsumed);
767
0
      if (NS_FAILED(result) || !subsumed) {
768
0
        sameOrigin = false;
769
0
      }
770
0
    }
771
0
772
0
    if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) {
773
0
      errorMessage = "MimeNotCssWarn";
774
0
      errorFlag = nsIScriptError::warningFlag;
775
0
    } else {
776
0
      errorMessage = "MimeNotCss";
777
0
      errorFlag = nsIScriptError::errorFlag;
778
0
    }
779
0
780
0
    const nsString& specUTF16 =
781
0
      NS_ConvertUTF8toUTF16(channelURI->GetSpecOrDefault());
782
0
    const nsString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType);
783
0
    const char16_t *strings[] = { specUTF16.get(), ctypeUTF16.get() };
784
0
785
0
    nsCOMPtr<nsIURI> referrer = GetReferrerURI();
786
0
    nsContentUtils::ReportToConsole(errorFlag,
787
0
                                    NS_LITERAL_CSTRING("CSS Loader"),
788
0
                                    mLoader->mDocument,
789
0
                                    nsContentUtils::eCSS_PROPERTIES,
790
0
                                    errorMessage,
791
0
                                    strings, ArrayLength(strings),
792
0
                                    referrer);
793
0
794
0
    if (errorFlag == nsIScriptError::errorFlag) {
795
0
      LOG_WARN(("  Ignoring sheet with improper MIME type %s",
796
0
                contentType.get()));
797
0
      mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
798
0
      return NS_OK;
799
0
    }
800
0
  }
801
0
802
0
  SRIMetadata sriMetadata;
803
0
  mSheet->GetIntegrity(sriMetadata);
804
0
  if (sriMetadata.IsEmpty()) {
805
0
    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
806
0
    if (loadInfo && loadInfo->GetEnforceSRI()) {
807
0
      LOG(("  Load was blocked by SRI"));
808
0
      MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
809
0
              ("css::Loader::OnStreamComplete, required SRI not found"));
810
0
      mLoader->SheetComplete(this, NS_ERROR_SRI_CORRUPT);
811
0
      // log the failed load to web console
812
0
      nsCOMPtr<nsIContentSecurityPolicy> csp;
813
0
      loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
814
0
      nsAutoCString spec;
815
0
      mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(spec);
816
0
      // line number unknown. mRequestingNode doesn't bear this info.
817
0
      csp->LogViolationDetails(
818
0
        nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_STYLE,
819
0
        nullptr, // triggering element
820
0
        NS_ConvertUTF8toUTF16(spec), EmptyString(),
821
0
        0, 0, EmptyString(), EmptyString());
822
0
      return NS_OK;
823
0
    }
824
0
  } else {
825
0
    nsAutoCString sourceUri;
826
0
    if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
827
0
      mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
828
0
    }
829
0
    nsresult rv = VerifySheetIntegrity(
830
0
      sriMetadata, aChannel, aBytes1, aBytes2, sourceUri, mLoader->mReporter);
831
0
832
0
    nsCOMPtr<nsILoadGroup> loadGroup;
833
0
    aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
834
0
    if (loadGroup) {
835
0
      mLoader->mReporter->FlushConsoleReports(loadGroup);
836
0
    } else {
837
0
      mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
838
0
    }
839
0
840
0
    if (NS_FAILED(rv)) {
841
0
      LOG(("  Load was blocked by SRI"));
842
0
      MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
843
0
              ("css::Loader::OnStreamComplete, bad metadata"));
844
0
      mLoader->SheetComplete(this, NS_ERROR_SRI_CORRUPT);
845
0
      return NS_OK;
846
0
    }
847
0
  }
848
0
849
0
  SetReferrerPolicyFromHeader(aChannel);
850
0
851
0
  // Enough to set the URIs on mSheet, since any sibling datas we have share
852
0
  // the same mInner as mSheet and will thus get the same URI.
853
0
  mSheet->SetURIs(channelURI, originalURI, channelURI);
854
0
  return NS_OK_PARSE_SHEET;
855
0
}
856
857
Loader::IsAlternate
858
Loader::IsAlternateSheet(const nsAString& aTitle, bool aHasAlternateRel)
859
0
{
860
0
  // A sheet is alternate if it has a nonempty title that doesn't match the
861
0
  // currently selected style set.  But if there _is_ no currently selected
862
0
  // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
863
0
  // is nonempty, we should select the style set corresponding to aTitle, since
864
0
  // that's a preferred sheet.
865
0
  if (aTitle.IsEmpty()) {
866
0
    return IsAlternate::No;
867
0
  }
868
0
869
0
  if (mDocument) {
870
0
    const nsString& currentSheetSet = mDocument->GetCurrentStyleSheetSet();
871
0
    if (!aHasAlternateRel && currentSheetSet.IsEmpty()) {
872
0
      // There's no preferred set yet, and we now have a sheet with a title.
873
0
      // Make that be the preferred set.
874
0
      // FIXME(emilio): This is kinda wild, can we do it somewhere else?
875
0
      mDocument->SetPreferredStyleSheetSet(aTitle);
876
0
      // We're definitely not an alternate. Also, beware that at this point
877
0
      // currentSheetSet may dangle.
878
0
      return IsAlternate::No;
879
0
    }
880
0
881
0
    if (aTitle.Equals(currentSheetSet)) {
882
0
      return IsAlternate::No;
883
0
    }
884
0
  }
885
0
886
0
  return IsAlternate::Yes;
887
0
}
888
889
void
890
Loader::UpdateLoadingData(URIPrincipalReferrerPolicyAndCORSModeHashKey* aOldKey,
891
                          SheetLoadData* aData)
892
0
{
893
0
  MOZ_ASSERT(mSheets, "Must have sheets!");
894
0
  MOZ_ASSERT(aData->mIsLoading, "data must be loading");
895
0
896
0
  DebugOnly<bool> removed = mSheets->mLoadingDatas.Remove(aOldKey);
897
0
  MOZ_ASSERT(removed, "Can't find data to remove!!!");
898
0
899
0
  URIPrincipalReferrerPolicyAndCORSModeHashKey newKey(aData);
900
0
  mSheets->mLoadingDatas.Put(&newKey, aData);
901
0
}
902
903
nsresult
904
Loader::CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
905
                           nsIPrincipal* aTriggeringPrincipal,
906
                           nsIURI* aTargetURI,
907
                           nsINode* aRequestingNode,
908
                           bool aIsPreload)
909
0
{
910
0
  // When performing a system load (e.g. aUseSystemPrincipal = true)
911
0
  // then aLoadingPrincipal == null; don't consult content policies.
912
0
  if (!aLoadingPrincipal) {
913
0
    return NS_OK;
914
0
  }
915
0
916
0
  nsContentPolicyType contentPolicyType =
917
0
    aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
918
0
               : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
919
0
920
0
  nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
921
0
    new net::LoadInfo(aLoadingPrincipal,
922
0
                      aTriggeringPrincipal,
923
0
                      aRequestingNode,
924
0
                      nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
925
0
                      contentPolicyType);
926
0
927
0
  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
928
0
  nsresult rv = NS_CheckContentLoadPolicy(aTargetURI,
929
0
                                          secCheckLoadInfo,
930
0
                                          NS_LITERAL_CSTRING("text/css"),
931
0
                                          &shouldLoad,
932
0
                                          nsContentUtils::GetContentPolicy());
933
0
   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
934
0
     return NS_ERROR_CONTENT_BLOCKED;
935
0
   }
936
0
   return NS_OK;
937
0
}
938
939
/**
940
 * CreateSheet() creates a CSSStyleSheet object for the given URI,
941
 * if any.  If there is no URI given, we just create a new style sheet
942
 * object.  Otherwise, we check for an existing style sheet object for
943
 * that uri in various caches and clone it if we find it.  Cloned
944
 * sheets will have the title/media/enabled state of the sheet they
945
 * are clones off; make sure to call PrepareSheet() on the result of
946
 * CreateSheet().
947
 */
948
nsresult
949
Loader::CreateSheet(nsIURI* aURI,
950
                    nsIContent* aLinkingContent,
951
                    nsIPrincipal* aLoaderPrincipal,
952
                    css::SheetParsingMode aParsingMode,
953
                    CORSMode aCORSMode,
954
                    ReferrerPolicy aReferrerPolicy,
955
                    const nsAString& aIntegrity,
956
                    bool aSyncLoad,
957
                    StyleSheetState& aSheetState,
958
                    RefPtr<StyleSheet>* aSheet)
959
0
{
960
0
  LOG(("css::Loader::CreateSheet"));
961
0
  MOZ_ASSERT(aSheet, "Null out param!");
962
0
963
0
  if (!mSheets) {
964
0
    mSheets = MakeUnique<Sheets>();
965
0
  }
966
0
967
0
  *aSheet = nullptr;
968
0
  aSheetState = eSheetStateUnknown;
969
0
970
0
  if (aURI) {
971
0
    aSheetState = eSheetComplete;
972
0
    RefPtr<StyleSheet> sheet;
973
0
974
0
    // First, the XUL cache
975
0
#ifdef MOZ_XUL
976
0
    if (IsChromeURI(aURI)) {
977
0
      nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
978
0
      if (cache && cache->IsEnabled()) {
979
0
        sheet = cache->GetStyleSheet(aURI);
980
0
        LOG(("  From XUL cache: %p", sheet.get()));
981
0
      }
982
0
    }
983
0
#endif
984
0
985
0
    bool fromCompleteSheets = false;
986
0
    if (!sheet) {
987
0
      // Then our per-document complete sheets.
988
0
      URIPrincipalReferrerPolicyAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode, aReferrerPolicy);
989
0
990
0
      StyleSheet* completeSheet = nullptr;
991
0
      mSheets->mCompleteSheets.Get(&key, &completeSheet);
992
0
      sheet = completeSheet;
993
0
      LOG(("  From completed: %p", sheet.get()));
994
0
995
0
      fromCompleteSheets = !!sheet;
996
0
    }
997
0
998
0
    if (sheet) {
999
0
      // This sheet came from the XUL cache or our per-document hashtable; it
1000
0
      // better be a complete sheet.
1001
0
      NS_ASSERTION(sheet->IsComplete(),
1002
0
                   "Sheet thinks it's not complete while we think it is");
1003
0
1004
0
      // Make sure it hasn't been forced to have a unique inner;
1005
0
      // that is an indication that its rules have been exposed to
1006
0
      // CSSOM and so we can't use it.
1007
0
      //
1008
0
      // Similarly, if the sheet doesn't have the right parsing mode just bail.
1009
0
      if (sheet->HasForcedUniqueInner() ||
1010
0
          sheet->ParsingMode() != aParsingMode) {
1011
0
        LOG(("  Not cloning completed sheet %p because it has a "
1012
0
             "forced unique inner or the wrong parsing mode",
1013
0
             sheet.get()));
1014
0
        sheet = nullptr;
1015
0
        fromCompleteSheets = false;
1016
0
      }
1017
0
    }
1018
0
1019
0
    // Then loading sheets
1020
0
    if (!sheet && !aSyncLoad) {
1021
0
      aSheetState = eSheetLoading;
1022
0
      SheetLoadData* loadData = nullptr;
1023
0
      URIPrincipalReferrerPolicyAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode, aReferrerPolicy);
1024
0
      mSheets->mLoadingDatas.Get(&key, &loadData);
1025
0
      if (loadData) {
1026
0
        sheet = loadData->mSheet;
1027
0
        LOG(("  From loading: %p", sheet.get()));
1028
0
1029
#ifdef DEBUG
1030
        bool debugEqual;
1031
        NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
1032
                     (aLoaderPrincipal && loadData->mLoaderPrincipal &&
1033
                      NS_SUCCEEDED(aLoaderPrincipal->
1034
                                   Equals(loadData->mLoaderPrincipal,
1035
                                          &debugEqual)) && debugEqual),
1036
                     "Principals should be the same");
1037
#endif
1038
      }
1039
0
1040
0
      // Then alternate sheets
1041
0
      if (!sheet) {
1042
0
        aSheetState = eSheetPending;
1043
0
        loadData = nullptr;
1044
0
        mSheets->mPendingDatas.Get(&key, &loadData);
1045
0
        if (loadData) {
1046
0
          sheet = loadData->mSheet;
1047
0
          LOG(("  From pending: %p", sheet.get()));
1048
0
1049
#ifdef DEBUG
1050
          bool debugEqual;
1051
          NS_ASSERTION((!aLoaderPrincipal && !loadData->mLoaderPrincipal) ||
1052
                       (aLoaderPrincipal && loadData->mLoaderPrincipal &&
1053
                        NS_SUCCEEDED(aLoaderPrincipal->
1054
                                     Equals(loadData->mLoaderPrincipal,
1055
                                            &debugEqual)) && debugEqual),
1056
                       "Principals should be the same");
1057
#endif
1058
        }
1059
0
      }
1060
0
    }
1061
0
1062
0
    if (sheet) {
1063
0
      // The sheet we have now should be either incomplete or without
1064
0
      // a forced unique inner.
1065
0
      NS_ASSERTION(!sheet->HasForcedUniqueInner() ||
1066
0
                   !sheet->IsComplete(),
1067
0
                   "Unexpected complete sheet with forced unique inner");
1068
0
      NS_ASSERTION(sheet->IsComplete() ||
1069
0
                   aSheetState != eSheetComplete,
1070
0
                   "Sheet thinks it's not complete while we think it is");
1071
0
1072
0
      RefPtr<StyleSheet> clonedSheet =
1073
0
        sheet->Clone(nullptr, nullptr, nullptr, nullptr);
1074
0
      *aSheet = std::move(clonedSheet);
1075
0
      if (*aSheet && fromCompleteSheets &&
1076
0
          !sheet->GetOwnerNode() &&
1077
0
          !sheet->GetParentSheet()) {
1078
0
        // The sheet we're cloning isn't actually referenced by
1079
0
        // anyone.  Replace it in the cache, so that if our CSSOM is
1080
0
        // later modified we don't end up with two copies of our inner
1081
0
        // hanging around.
1082
0
        URIPrincipalReferrerPolicyAndCORSModeHashKey key(aURI, aLoaderPrincipal, aCORSMode, aReferrerPolicy);
1083
0
        NS_ASSERTION((*aSheet)->IsComplete(),
1084
0
                     "Should only be caching complete sheets");
1085
0
        mSheets->mCompleteSheets.Put(&key, *aSheet);
1086
0
      }
1087
0
    }
1088
0
  }
1089
0
1090
0
  if (!*aSheet) {
1091
0
    aSheetState = eSheetNeedsParser;
1092
0
    nsIURI *sheetURI;
1093
0
    nsCOMPtr<nsIURI> baseURI;
1094
0
    nsIURI* originalURI;
1095
0
    if (!aURI) {
1096
0
      // Inline style.  Use the document's base URL so that @import in
1097
0
      // the inline sheet picks up the right base.
1098
0
      NS_ASSERTION(aLinkingContent, "Inline stylesheet without linking content?");
1099
0
      baseURI = aLinkingContent->GetBaseURI();
1100
0
      sheetURI = aLinkingContent->OwnerDoc()->GetDocumentURI();
1101
0
      originalURI = nullptr;
1102
0
    } else {
1103
0
      baseURI = aURI;
1104
0
      sheetURI = aURI;
1105
0
      originalURI = aURI;
1106
0
    }
1107
0
1108
0
    SRIMetadata sriMetadata;
1109
0
    if (!aIntegrity.IsEmpty()) {
1110
0
      MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
1111
0
              ("css::Loader::CreateSheet, integrity=%s",
1112
0
               NS_ConvertUTF16toUTF8(aIntegrity).get()));
1113
0
      nsAutoCString sourceUri;
1114
0
      if (mDocument && mDocument->GetDocumentURI()) {
1115
0
        mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1116
0
      }
1117
0
      SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter,
1118
0
                                  &sriMetadata);
1119
0
    }
1120
0
1121
0
    *aSheet = new StyleSheet(aParsingMode, aCORSMode, aReferrerPolicy, sriMetadata);
1122
0
    (*aSheet)->SetURIs(sheetURI, originalURI, baseURI);
1123
0
  }
1124
0
1125
0
  NS_ASSERTION(*aSheet, "We should have a sheet by now!");
1126
0
  NS_ASSERTION(aSheetState != eSheetStateUnknown, "Have to set a state!");
1127
0
  LOG(("  State: %s", gStateStrings[aSheetState]));
1128
0
1129
0
  return NS_OK;
1130
0
}
1131
1132
static Loader::MediaMatched
1133
MediaListMatches(const MediaList* aMediaList, const nsIDocument* aDocument)
1134
0
{
1135
0
  if (!aMediaList || !aDocument) {
1136
0
    return Loader::MediaMatched::Yes;
1137
0
  }
1138
0
1139
0
  nsPresContext* pc = aDocument->GetPresContext();
1140
0
  if (!pc) {
1141
0
    // Conservatively assume a match.
1142
0
    return Loader::MediaMatched::Yes;
1143
0
  }
1144
0
1145
0
  if (aMediaList->Matches(pc)) {
1146
0
    return Loader::MediaMatched::Yes;
1147
0
  }
1148
0
1149
0
  return Loader::MediaMatched::No;
1150
0
}
1151
1152
/**
1153
 * PrepareSheet() handles setting the media and title on the sheet, as
1154
 * well as setting the enabled state based on the title and whether
1155
 * the sheet had "alternate" in its rel.
1156
 */
1157
Loader::MediaMatched
1158
Loader::PrepareSheet(StyleSheet* aSheet,
1159
                     const nsAString& aTitle,
1160
                     const nsAString& aMediaString,
1161
                     MediaList* aMediaList,
1162
                     IsAlternate aIsAlternate)
1163
0
{
1164
0
  MOZ_ASSERT(aSheet, "Must have a sheet!");
1165
0
1166
0
  RefPtr<MediaList> mediaList(aMediaList);
1167
0
1168
0
  if (!aMediaString.IsEmpty()) {
1169
0
    NS_ASSERTION(!aMediaList,
1170
0
                 "must not provide both aMediaString and aMediaList");
1171
0
    mediaList = MediaList::Create(aMediaString);
1172
0
  }
1173
0
1174
0
  aSheet->SetMedia(mediaList);
1175
0
1176
0
  aSheet->SetTitle(aTitle);
1177
0
  aSheet->SetEnabled(aIsAlternate == IsAlternate::No);
1178
0
  return MediaListMatches(mediaList, mDocument);
1179
0
}
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
1198
Loader::InsertSheetInTree(StyleSheet& aSheet, nsIContent* aLinkingContent)
1199
0
{
1200
0
  LOG(("css::Loader::InsertSheetInTree"));
1201
0
  MOZ_ASSERT(mDocument, "Must have a document to insert into");
1202
0
  MOZ_ASSERT(!aLinkingContent ||
1203
0
             aLinkingContent->IsInUncomposedDoc() ||
1204
0
             aLinkingContent->IsInShadowTree(),
1205
0
             "Why would we insert it anywhere?");
1206
0
1207
0
  nsCOMPtr<nsIStyleSheetLinkingElement> linkingElement =
1208
0
    do_QueryInterface(aLinkingContent);
1209
0
  if (linkingElement) {
1210
0
    linkingElement->SetStyleSheet(&aSheet);
1211
0
  }
1212
0
1213
0
  ShadowRoot* shadow =
1214
0
    aLinkingContent ? aLinkingContent->GetContainingShadow() : nullptr;
1215
0
1216
0
  auto& target = shadow
1217
0
    ? static_cast<DocumentOrShadowRoot&>(*shadow)
1218
0
    : static_cast<DocumentOrShadowRoot&>(*mDocument);
1219
0
1220
0
  // XXX Need to cancel pending sheet loads for this element, if any
1221
0
1222
0
  int32_t sheetCount = target.SheetCount();
1223
0
1224
0
  /*
1225
0
   * Start the walk at the _end_ of the list, since in the typical
1226
0
   * case we'll just want to append anyway.  We want to break out of
1227
0
   * the loop when insertionPoint points to just before the index we
1228
0
   * want to insert at.  In other words, when we leave the loop
1229
0
   * insertionPoint is the index of the stylesheet that immediately
1230
0
   * precedes the one we're inserting.
1231
0
   */
1232
0
  int32_t insertionPoint = sheetCount - 1;
1233
0
  for (; insertionPoint >= 0; --insertionPoint) {
1234
0
    nsINode* sheetOwner = target.SheetAt(insertionPoint)->GetOwnerNode();
1235
0
    if (sheetOwner && !aLinkingContent) {
1236
0
      // Keep moving; all sheets with a sheetOwner come after all
1237
0
      // sheets without a linkingNode
1238
0
      continue;
1239
0
    }
1240
0
1241
0
    if (!sheetOwner) {
1242
0
      // Aha!  The current sheet has no sheet owner, so we want to insert after
1243
0
      // it no matter whether we have a linking content or not.
1244
0
      break;
1245
0
    }
1246
0
1247
0
    MOZ_ASSERT(aLinkingContent != sheetOwner,
1248
0
               "Why do we still have our old sheet?");
1249
0
1250
0
    // Have to compare
1251
0
    if (nsContentUtils::PositionIsBefore(sheetOwner, aLinkingContent)) {
1252
0
      // The current sheet comes before us, and it better be the first
1253
0
      // such, because now we break
1254
0
      break;
1255
0
    }
1256
0
  }
1257
0
1258
0
  ++insertionPoint;
1259
0
1260
0
  if (shadow) {
1261
0
    shadow->InsertSheetAt(insertionPoint, aSheet);
1262
0
  } else {
1263
0
    mDocument->InsertSheetAt(insertionPoint, aSheet);
1264
0
  }
1265
0
1266
0
  LOG(("  Inserting into target (doc: %d) at position %d", target.AsNode().IsDocument(), insertionPoint));
1267
0
}
1268
1269
/**
1270
 * InsertChildSheet handles ordering of @import-ed sheet in their
1271
 * parent sheets.  Here we want to just insert based on order of the
1272
 * @import rules that imported the sheets.  In theory we can't just
1273
 * append to the end because the CSSOM can insert @import rules.  In
1274
 * practice, we get the call to load the child sheet before the CSSOM
1275
 * has finished inserting the @import rule, so we have no idea where
1276
 * to put it anyway.  So just append for now.  (In the future if we
1277
 * want to insert the sheet at the correct position, we'll need to
1278
 * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
1279
 * bug 1220506.)
1280
 */
1281
void
1282
Loader::InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet)
1283
0
{
1284
0
  LOG(("css::Loader::InsertChildSheet"));
1285
0
1286
0
  // child sheets should always start out enabled, even if they got
1287
0
  // cloned off of top-level sheets which were disabled
1288
0
  aSheet.SetEnabled(true);
1289
0
  aParentSheet.PrependStyleSheet(&aSheet);
1290
0
1291
0
  LOG(("  Inserting into parent sheet"));
1292
0
}
1293
1294
/**
1295
 * LoadSheet handles the actual load of a sheet.  If the load is
1296
 * supposed to be synchronous it just opens a channel synchronously
1297
 * using the given uri, wraps the resulting stream in a converter
1298
 * stream and calls ParseSheet.  Otherwise it tries to look for an
1299
 * existing load for this URI and piggyback on it.  Failing all that,
1300
 * a new load is kicked off asynchronously.
1301
 */
1302
nsresult
1303
Loader::LoadSheet(SheetLoadData* aLoadData,
1304
                  StyleSheetState aSheetState,
1305
                  bool aIsPreload)
1306
0
{
1307
0
  LOG(("css::Loader::LoadSheet"));
1308
0
  MOZ_ASSERT(aLoadData, "Need a load data");
1309
0
  MOZ_ASSERT(aLoadData->mURI, "Need a URI to load");
1310
0
  MOZ_ASSERT(aLoadData->mSheet, "Need a sheet to load into");
1311
0
  MOZ_ASSERT(aSheetState != eSheetComplete, "Why bother?");
1312
0
  MOZ_ASSERT(!aLoadData->mUseSystemPrincipal || aLoadData->mSyncLoad,
1313
0
             "Shouldn't use system principal for async loads");
1314
0
  NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
1315
0
1316
0
  LOG_URI("  Load from: '%s'", aLoadData->mURI);
1317
0
1318
0
  nsresult rv = NS_OK;
1319
0
1320
0
  if (!mDocument && !aLoadData->mIsNonDocumentSheet) {
1321
0
    // No point starting the load; just release all the data and such.
1322
0
    LOG_WARN(("  No document and not non-document sheet; pre-dropping load"));
1323
0
    SheetComplete(aLoadData, NS_BINDING_ABORTED);
1324
0
    return NS_BINDING_ABORTED;
1325
0
  }
1326
0
1327
0
  SRIMetadata sriMetadata;
1328
0
  aLoadData->mSheet->GetIntegrity(sriMetadata);
1329
0
1330
0
  if (aLoadData->mSyncLoad) {
1331
0
    LOG(("  Synchronous load"));
1332
0
    NS_ASSERTION(!aLoadData->mObserver, "Observer for a sync load?");
1333
0
    NS_ASSERTION(aSheetState == eSheetNeedsParser,
1334
0
                 "Sync loads can't reuse existing async loads");
1335
0
1336
0
    // Create a StreamLoader instance to which we will feed
1337
0
    // the data from the sync load.  Do this before creating the
1338
0
    // channel to make error recovery simpler.
1339
0
    nsCOMPtr<nsIStreamListener> streamLoader = new StreamLoader(aLoadData);
1340
0
1341
0
    if (mDocument) {
1342
0
      mozilla::net::PredictorLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
1343
0
                                   nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
1344
0
                                   mDocument);
1345
0
    }
1346
0
1347
0
    nsSecurityFlags securityFlags =
1348
0
      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
1349
0
      nsILoadInfo::SEC_ALLOW_CHROME;
1350
0
1351
0
    nsContentPolicyType contentPolicyType =
1352
0
      aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
1353
0
                 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
1354
0
1355
0
    // Just load it
1356
0
    nsCOMPtr<nsIChannel> channel;
1357
0
    // Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
1358
0
    // a node and a principal.
1359
0
    // This is because of a case where the node is the document being styled and
1360
0
    // the principal is the stylesheet (perhaps from a different origin) that is
1361
0
    // applying the styles.
1362
0
    if (aLoadData->mRequestingNode && aLoadData->mLoaderPrincipal) {
1363
0
      rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
1364
0
                                                aLoadData->mURI,
1365
0
                                                aLoadData->mRequestingNode,
1366
0
                                                aLoadData->mLoaderPrincipal,
1367
0
                                                securityFlags,
1368
0
                                                contentPolicyType);
1369
0
    } else {
1370
0
      // either we are loading something inside a document, in which case
1371
0
      // we should always have a requestingNode, or we are loading something
1372
0
      // outside a document, in which case the loadingPrincipal and the
1373
0
      // triggeringPrincipal should always be the systemPrincipal.
1374
0
      auto result = URLPreloader::ReadURI(aLoadData->mURI);
1375
0
      if (result.isOk()) {
1376
0
        nsCOMPtr<nsIInputStream> stream;
1377
0
        MOZ_TRY(NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap()));
1378
0
1379
0
        rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
1380
0
                                      aLoadData->mURI,
1381
0
                                      stream.forget(),
1382
0
                                      nsContentUtils::GetSystemPrincipal(),
1383
0
                                      securityFlags,
1384
0
                                      contentPolicyType);
1385
0
      } else {
1386
0
        rv = NS_NewChannel(getter_AddRefs(channel),
1387
0
                           aLoadData->mURI,
1388
0
                           nsContentUtils::GetSystemPrincipal(),
1389
0
                           securityFlags,
1390
0
                           contentPolicyType);
1391
0
      }
1392
0
    }
1393
0
    if (NS_FAILED(rv)) {
1394
0
      LOG_ERROR(("  Failed to create channel"));
1395
0
      SheetComplete(aLoadData, rv);
1396
0
      return rv;
1397
0
    }
1398
0
1399
0
    nsCOMPtr<nsIInputStream> stream;
1400
0
    rv = channel->Open2(getter_AddRefs(stream));
1401
0
1402
0
    if (NS_FAILED(rv)) {
1403
0
      LOG_ERROR(("  Failed to open URI synchronously"));
1404
0
      SheetComplete(aLoadData, rv);
1405
0
      return rv;
1406
0
    }
1407
0
1408
0
    // Force UA sheets to be UTF-8.
1409
0
    // XXX this is only necessary because the default in
1410
0
    // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
1411
0
    channel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
1412
0
1413
0
    // Manually feed the streamloader the contents of the stream.
1414
0
    // This will call back into OnStreamComplete
1415
0
    // and thence to ParseSheet.  Regardless of whether this fails,
1416
0
    // SheetComplete has been called.
1417
0
    return nsSyncLoadService::PushSyncStreamToListener(stream.forget(),
1418
0
                                                       streamLoader,
1419
0
                                                       channel);
1420
0
  }
1421
0
1422
0
  SheetLoadData* existingData = nullptr;
1423
0
1424
0
  URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData);
1425
0
1426
0
  if (aSheetState == eSheetLoading) {
1427
0
    mSheets->mLoadingDatas.Get(&key, &existingData);
1428
0
    NS_ASSERTION(existingData, "CreateSheet lied about the state");
1429
0
  } else if (aSheetState == eSheetPending) {
1430
0
    mSheets->mPendingDatas.Get(&key, &existingData);
1431
0
    NS_ASSERTION(existingData, "CreateSheet lied about the state");
1432
0
  }
1433
0
1434
0
  if (existingData) {
1435
0
    LOG(("  Glomming on to existing load"));
1436
0
    SheetLoadData* data = existingData;
1437
0
    while (data->mNext) {
1438
0
      data = data->mNext;
1439
0
    }
1440
0
    data->mNext = aLoadData; // transfer ownership
1441
0
    if (aSheetState == eSheetPending && !aLoadData->ShouldDefer()) {
1442
0
      // Kick the load off; someone cares about it right away
1443
0
1444
#ifdef DEBUG
1445
      SheetLoadData* removedData;
1446
      NS_ASSERTION(mSheets->mPendingDatas.Get(&key, &removedData) &&
1447
                   removedData == existingData,
1448
                   "Bad pending table.");
1449
#endif
1450
1451
0
      mSheets->mPendingDatas.Remove(&key);
1452
0
1453
0
      LOG(("  Forcing load of pending data"));
1454
0
      return LoadSheet(existingData, eSheetNeedsParser, aIsPreload);
1455
0
    }
1456
0
    // All done here; once the load completes we'll be marked complete
1457
0
    // automatically
1458
0
    return NS_OK;
1459
0
  }
1460
0
1461
0
  nsCOMPtr<nsILoadGroup> loadGroup;
1462
0
  if (mDocument) {
1463
0
    loadGroup = mDocument->GetDocumentLoadGroup();
1464
0
    // load for a document with no loadgrup indicates that something is
1465
0
    // completely bogus, let's bail out early.
1466
0
    if (!loadGroup) {
1467
0
      LOG_ERROR(("  Failed to query loadGroup from document"));
1468
0
      SheetComplete(aLoadData, NS_ERROR_UNEXPECTED);
1469
0
      return NS_ERROR_UNEXPECTED;
1470
0
    }
1471
0
  }
1472
#ifdef DEBUG
1473
  mSyncCallback = true;
1474
#endif
1475
1476
0
  CORSMode ourCORSMode = aLoadData->mSheet->GetCORSMode();
1477
0
  nsSecurityFlags securityFlags =
1478
0
    ourCORSMode == CORS_NONE
1479
0
      ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS
1480
0
      : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
1481
0
  if (ourCORSMode == CORS_ANONYMOUS) {
1482
0
    securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
1483
0
  } else if (ourCORSMode == CORS_USE_CREDENTIALS) {
1484
0
    securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
1485
0
  }
1486
0
  securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
1487
0
1488
0
  nsContentPolicyType contentPolicyType =
1489
0
    aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
1490
0
               : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
1491
0
1492
0
  nsCOMPtr<nsIChannel> channel;
1493
0
  // Note we are calling NS_NewChannelWithTriggeringPrincipal here with a node
1494
0
  // and a principal. This is because of a case where the node is the document
1495
0
  // being styled and the principal is the stylesheet (perhaps from a different
1496
0
  // origin)  that is applying the styles.
1497
0
  if (aLoadData->mRequestingNode && aLoadData->mLoaderPrincipal) {
1498
0
    rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
1499
0
                                              aLoadData->mURI,
1500
0
                                              aLoadData->mRequestingNode,
1501
0
                                              aLoadData->mLoaderPrincipal,
1502
0
                                              securityFlags,
1503
0
                                              contentPolicyType,
1504
0
                                              nullptr, // Performancestorage
1505
0
                                              loadGroup,
1506
0
                                              nullptr,   // aCallbacks
1507
0
                                              nsIChannel::LOAD_NORMAL |
1508
0
                                              nsIChannel::LOAD_CLASSIFY_URI);
1509
0
  }
1510
0
  else {
1511
0
    // either we are loading something inside a document, in which case
1512
0
    // we should always have a requestingNode, or we are loading something
1513
0
    // outside a document, in which case the loadingPrincipal and the
1514
0
    // triggeringPrincipal should always be the systemPrincipal.
1515
0
    rv = NS_NewChannel(getter_AddRefs(channel),
1516
0
                       aLoadData->mURI,
1517
0
                       nsContentUtils::GetSystemPrincipal(),
1518
0
                       securityFlags,
1519
0
                       contentPolicyType,
1520
0
                       nullptr, // aPerformanceStorage
1521
0
                       loadGroup,
1522
0
                       nullptr,   // aCallbacks
1523
0
                       nsIChannel::LOAD_NORMAL |
1524
0
                       nsIChannel::LOAD_CLASSIFY_URI);
1525
0
  }
1526
0
1527
0
  if (NS_FAILED(rv)) {
1528
#ifdef DEBUG
1529
    mSyncCallback = false;
1530
#endif
1531
0
    LOG_ERROR(("  Failed to create channel"));
1532
0
    SheetComplete(aLoadData, rv);
1533
0
    return rv;
1534
0
  }
1535
0
1536
0
  if (!aLoadData->ShouldDefer()) {
1537
0
    nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
1538
0
    if (cos) {
1539
0
      cos->AddClassFlags(nsIClassOfService::Leader);
1540
0
    }
1541
0
  }
1542
0
1543
0
  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
1544
0
  if (httpChannel) {
1545
0
    // Send a minimal Accept header for text/css
1546
0
    rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
1547
0
                                       NS_LITERAL_CSTRING("text/css,*/*;q=0.1"),
1548
0
                                       false);
1549
0
    NS_ENSURE_SUCCESS(rv, rv);
1550
0
1551
0
    nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
1552
0
    if (referrerURI) {
1553
0
      rv = httpChannel->SetReferrerWithPolicy(referrerURI,
1554
0
                                              aLoadData->mSheet->GetReferrerPolicy());
1555
0
      Unused << NS_WARN_IF(NS_FAILED(rv));
1556
0
    }
1557
0
1558
0
    nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
1559
0
    if (internalChannel) {
1560
0
      rv = internalChannel->SetIntegrityMetadata(sriMetadata.GetIntegrityString());
1561
0
      NS_ENSURE_SUCCESS(rv, rv);
1562
0
    }
1563
0
1564
0
    // Set the initiator type
1565
0
    nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
1566
0
    if (timedChannel) {
1567
0
      if (aLoadData->mParentData) {
1568
0
        timedChannel->SetInitiatorType(NS_LITERAL_STRING("css"));
1569
0
1570
0
        // This is a child sheet load.
1571
0
        //
1572
0
        // The resource timing of the sub-resources that a document loads
1573
0
        // should normally be reported to the document.  One exception is any
1574
0
        // sub-resources of any cross-origin resources that are loaded.  We
1575
0
        // don't mind reporting timing data for a direct child cross-origin
1576
0
        // resource since the resource that linked to it (and hence potentially
1577
0
        // anything in that parent origin) is aware that the cross-origin
1578
0
        // resources is to be loaded.  However, we do not want to report
1579
0
        // timings for any sub-resources that a cross-origin resource may load
1580
0
        // since that obviously leaks information about what the cross-origin
1581
0
        // resource loads, which is bad.
1582
0
        //
1583
0
        // In addition to checking whether we're an immediate child resource of
1584
0
        // a cross-origin resource (by checking if mIsCrossOriginNoCORS is set
1585
0
        // to true on our parent), we also check our parent to see whether it
1586
0
        // itself is a sub-resource of a cross-origin resource by checking
1587
0
        // mBlockResourceTiming.  If that is set then we too are such a
1588
0
        // sub-resource and so we set the flag on ourself too to propagate it
1589
0
        // on down.
1590
0
        //
1591
0
        if (aLoadData->mParentData->mIsCrossOriginNoCORS ||
1592
0
            aLoadData->mParentData->mBlockResourceTiming) {
1593
0
          // Set a flag so any other stylesheet triggered by this one will
1594
0
          // not be reported
1595
0
          aLoadData->mBlockResourceTiming = true;
1596
0
1597
0
          // Mark the channel so PerformanceMainThread::AddEntry will not
1598
0
          // report the resource.
1599
0
          timedChannel->SetReportResourceTiming(false);
1600
0
        }
1601
0
1602
0
      } else {
1603
0
        timedChannel->SetInitiatorType(NS_LITERAL_STRING("link"));
1604
0
      }
1605
0
    }
1606
0
  }
1607
0
1608
0
  // Now tell the channel we expect text/css data back....  We do
1609
0
  // this before opening it, so it's only treated as a hint.
1610
0
  channel->SetContentType(NS_LITERAL_CSTRING("text/css"));
1611
0
1612
0
  // We don't have to hold on to the stream loader.  The ownership
1613
0
  // model is: Necko owns the stream loader, which owns the load data,
1614
0
  // which owns us
1615
0
  nsCOMPtr<nsIStreamListener> streamLoader = new StreamLoader(aLoadData);
1616
0
1617
0
  if (mDocument) {
1618
0
    mozilla::net::PredictorLearn(aLoadData->mURI, mDocument->GetDocumentURI(),
1619
0
                                 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
1620
0
                                 mDocument);
1621
0
  }
1622
0
1623
0
  rv = channel->AsyncOpen2(streamLoader);
1624
0
1625
#ifdef DEBUG
1626
  mSyncCallback = false;
1627
#endif
1628
1629
0
  if (NS_FAILED(rv)) {
1630
0
    LOG_ERROR(("  Failed to create stream loader"));
1631
0
    SheetComplete(aLoadData, rv);
1632
0
    return rv;
1633
0
  }
1634
0
1635
0
  mSheets->mLoadingDatas.Put(&key, aLoadData);
1636
0
  aLoadData->mIsLoading = true;
1637
0
1638
0
  return NS_OK;
1639
0
}
1640
1641
/**
1642
 * ParseSheet handles parsing the data stream.
1643
 */
1644
Loader::Completed
1645
Loader::ParseSheet(const nsACString& aBytes,
1646
                   SheetLoadData* aLoadData,
1647
                   AllowAsyncParse aAllowAsync)
1648
0
{
1649
0
  LOG(("css::Loader::ParseSheet"));
1650
0
  AUTO_PROFILER_LABEL("css::Loader::ParseSheet", LAYOUT);
1651
0
  MOZ_ASSERT(aLoadData);
1652
0
  aLoadData->mIsBeingParsed = true;
1653
0
1654
0
  StyleSheet* sheet = aLoadData->mSheet;
1655
0
  MOZ_ASSERT(sheet);
1656
0
1657
0
  // Some cases, like inline style and UA stylesheets, need to be parsed
1658
0
  // synchronously. The former may trigger child loads, the latter must not.
1659
0
  if (aLoadData->mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
1660
0
    sheet->ParseSheetSync(this, aBytes, aLoadData, aLoadData->mLineNumber);
1661
0
    aLoadData->mIsBeingParsed = false;
1662
0
1663
0
    bool noPendingChildren = aLoadData->mPendingChildren == 0;
1664
0
    MOZ_ASSERT_IF(aLoadData->mSyncLoad, noPendingChildren);
1665
0
    if (noPendingChildren) {
1666
0
      SheetComplete(aLoadData, NS_OK);
1667
0
      return Completed::Yes;
1668
0
    }
1669
0
    return Completed::No;
1670
0
  }
1671
0
1672
0
  // This parse does not need to be synchronous. \o/
1673
0
  //
1674
0
  // Note that we need to block onload because there may be no network requests
1675
0
  // pending.
1676
0
  BlockOnload();
1677
0
  RefPtr<SheetLoadData> loadData = aLoadData;
1678
0
  nsCOMPtr<nsISerialEventTarget> target = DispatchTarget();
1679
0
  sheet->ParseSheet(this, aBytes, aLoadData)->Then(target, __func__,
1680
0
    [loadData = std::move(loadData)](bool aDummy) {
1681
0
      MOZ_ASSERT(NS_IsMainThread());
1682
0
      loadData->mIsBeingParsed = false;
1683
0
      loadData->mLoader->UnblockOnload(/* aFireSync = */ false);
1684
0
      // If there are no child sheets outstanding, mark us as complete.
1685
0
      // Otherwise, the children are holding strong refs to the data and
1686
0
      // will call SheetComplete() on it when they complete.
1687
0
      if (loadData->mPendingChildren == 0) {
1688
0
        loadData->mLoader->SheetComplete(loadData, NS_OK);
1689
0
      }
1690
0
    }, [] { MOZ_CRASH("rejected parse promise"); }
1691
0
  );
1692
0
  return Completed::No;
1693
0
}
1694
1695
/**
1696
 * SheetComplete is the do-it-all cleanup function.  It removes the
1697
 * load data from the "loading" hashtable, adds the sheet to the
1698
 * "completed" hashtable, massages the XUL cache, handles siblings of
1699
 * the load data (other loads for the same URI), handles unblocking
1700
 * blocked parent loads as needed, and most importantly calls
1701
 * NS_RELEASE on the load data to destroy the whole mess.
1702
 */
1703
void
1704
Loader::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus)
1705
0
{
1706
0
  LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
1707
0
1708
0
  // If aStatus is a failure we need to mark this data failed.  We also need to
1709
0
  // mark any ancestors of a failing data as failed and any sibling of a
1710
0
  // failing data as failed.  Note that SheetComplete is never called on a
1711
0
  // SheetLoadData that is the mNext of some other SheetLoadData.
1712
0
  if (NS_FAILED(aStatus)) {
1713
0
    MarkLoadTreeFailed(aLoadData);
1714
0
  }
1715
0
1716
0
  // 8 is probably big enough for all our common cases.  It's not likely that
1717
0
  // imports will nest more than 8 deep, and multiple sheets with the same URI
1718
0
  // are rare.
1719
0
  AutoTArray<RefPtr<SheetLoadData>, 8> datasToNotify;
1720
0
  DoSheetComplete(aLoadData, datasToNotify);
1721
0
1722
0
  // Now it's safe to go ahead and notify observers
1723
0
  uint32_t count = datasToNotify.Length();
1724
0
  mDatasToNotifyOn += count;
1725
0
  for (uint32_t i = 0; i < count; ++i) {
1726
0
    --mDatasToNotifyOn;
1727
0
1728
0
    SheetLoadData* data = datasToNotify[i];
1729
0
    NS_ASSERTION(data && data->mMustNotify, "How did this data get here?");
1730
0
    if (data->mObserver) {
1731
0
      LOG(("  Notifying observer %p for data %p.  wasAlternate: %d",
1732
0
           data->mObserver.get(), data, data->mWasAlternate));
1733
0
      data->mObserver->StyleSheetLoaded(data->mSheet, data->ShouldDefer(),
1734
0
                                        aStatus);
1735
0
    }
1736
0
1737
0
    nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver> >::ForwardIterator iter(mObservers);
1738
0
    nsCOMPtr<nsICSSLoaderObserver> obs;
1739
0
    while (iter.HasMore()) {
1740
0
      obs = iter.GetNext();
1741
0
      LOG(("  Notifying global observer %p for data %p.  wasAlternate: %d",
1742
0
           obs.get(), data, data->mWasAlternate));
1743
0
      obs->StyleSheetLoaded(data->mSheet, data->mWasAlternate, aStatus);
1744
0
    }
1745
0
  }
1746
0
1747
0
  if (mSheets->mLoadingDatas.Count() == 0 && mSheets->mPendingDatas.Count() > 0) {
1748
0
    LOG(("  No more loading sheets; starting deferred loads"));
1749
0
    StartDeferredLoads();
1750
0
  }
1751
0
}
1752
1753
void
1754
Loader::DoSheetComplete(SheetLoadData* aLoadData, LoadDataArray& aDatasToNotify)
1755
0
{
1756
0
  LOG(("css::Loader::DoSheetComplete"));
1757
0
  MOZ_ASSERT(aLoadData, "Must have a load data!");
1758
0
  MOZ_ASSERT(aLoadData->mSheet, "Must have a sheet");
1759
0
  NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
1760
0
1761
0
  // Twiddle the hashtables
1762
0
  if (aLoadData->mURI) {
1763
0
    LOG_URI("  Finished loading: '%s'", aLoadData->mURI);
1764
0
    // Remove the data from the list of loading datas
1765
0
    if (aLoadData->mIsLoading) {
1766
0
      URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData);
1767
#ifdef DEBUG
1768
      SheetLoadData *loadingData;
1769
      NS_ASSERTION(
1770
        mSheets->mLoadingDatas.Get(&key, &loadingData) &&
1771
        loadingData == aLoadData,
1772
        "Bad loading table");
1773
#endif
1774
1775
0
      mSheets->mLoadingDatas.Remove(&key);
1776
0
      aLoadData->mIsLoading = false;
1777
0
    }
1778
0
  }
1779
0
1780
0
  // Go through and deal with the whole linked list.
1781
0
  SheetLoadData* data = aLoadData;
1782
0
  while (data) {
1783
0
    if (!data->mSheetAlreadyComplete) {
1784
0
      // If mSheetAlreadyComplete, then the sheet could well be modified between
1785
0
      // when we posted the async call to SheetComplete and now, since the sheet
1786
0
      // was page-accessible during that whole time.
1787
0
      MOZ_ASSERT(!data->mSheet->HasForcedUniqueInner(),
1788
0
                 "should not get a forced unique inner during parsing");
1789
0
      data->mSheet->SetComplete();
1790
0
      data->ScheduleLoadEventIfNeeded();
1791
0
    }
1792
0
    if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
1793
0
      // Don't notify here so we don't trigger script.  Remember the
1794
0
      // info we need to notify, then do it later when it's safe.
1795
0
      aDatasToNotify.AppendElement(data);
1796
0
1797
0
      // On append failure, just press on.  We'll fail to notify the observer,
1798
0
      // but not much we can do about that....
1799
0
    }
1800
0
1801
0
    NS_ASSERTION(!data->mParentData ||
1802
0
                 data->mParentData->mPendingChildren != 0,
1803
0
                 "Broken pending child count on our parent");
1804
0
1805
0
    // If we have a parent, our parent is no longer being parsed, and
1806
0
    // we are the last pending child, then our load completion
1807
0
    // completes the parent too.  Note that the parent _can_ still be
1808
0
    // being parsed (eg if the child (us) failed to open the channel
1809
0
    // or some such).
1810
0
    if (data->mParentData &&
1811
0
        --(data->mParentData->mPendingChildren) == 0 &&
1812
0
        !data->mParentData->mIsBeingParsed) {
1813
0
      DoSheetComplete(data->mParentData, aDatasToNotify);
1814
0
    }
1815
0
1816
0
    data = data->mNext;
1817
0
  }
1818
0
1819
0
  // Now that it's marked complete, put the sheet in our cache.
1820
0
  // If we ever start doing this for failed loads, we'll need to
1821
0
  // adjust the PostLoadEvent code that thinks anything already
1822
0
  // complete must have loaded succesfully.
1823
0
  if (!aLoadData->mLoadFailed && aLoadData->mURI) {
1824
0
    // Pick our sheet to cache carefully.  Ideally, we want to cache
1825
0
    // one of the sheets that will be kept alive by a document or
1826
0
    // parent sheet anyway, so that if someone then accesses it via
1827
0
    // CSSOM we won't have extra clones of the inner lying around.
1828
0
    data = aLoadData;
1829
0
    StyleSheet* sheet = aLoadData->mSheet;
1830
0
    while (data) {
1831
0
      if (data->mSheet->GetParentSheet() || data->mSheet->GetOwnerNode()) {
1832
0
        sheet = data->mSheet;
1833
0
        break;
1834
0
      }
1835
0
      data = data->mNext;
1836
0
    }
1837
0
#ifdef MOZ_XUL
1838
0
    if (IsChromeURI(aLoadData->mURI)) {
1839
0
      nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1840
0
      if (cache && cache->IsEnabled()) {
1841
0
        if (!cache->GetStyleSheet(aLoadData->mURI)) {
1842
0
          LOG(("  Putting sheet in XUL prototype cache"));
1843
0
          NS_ASSERTION(sheet->IsComplete(),
1844
0
                       "Should only be caching complete sheets");
1845
0
          cache->PutStyleSheet(sheet);
1846
0
        }
1847
0
      }
1848
0
    }
1849
0
    else {
1850
0
#endif
1851
0
      URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData);
1852
0
      NS_ASSERTION(sheet->IsComplete(),
1853
0
                   "Should only be caching complete sheets");
1854
0
      mSheets->mCompleteSheets.Put(&key, sheet);
1855
0
#ifdef MOZ_XUL
1856
0
    }
1857
0
#endif
1858
0
  }
1859
0
1860
0
  NS_RELEASE(aLoadData);  // this will release parents and siblings and all that
1861
0
}
1862
1863
void
1864
Loader::MarkLoadTreeFailed(SheetLoadData* aLoadData)
1865
0
{
1866
0
  if (aLoadData->mURI) {
1867
0
    LOG_URI("  Load failed: '%s'", aLoadData->mURI);
1868
0
  }
1869
0
1870
0
  do {
1871
0
    aLoadData->mLoadFailed = true;
1872
0
1873
0
    if (aLoadData->mParentData) {
1874
0
      MarkLoadTreeFailed(aLoadData->mParentData);
1875
0
    }
1876
0
1877
0
    aLoadData = aLoadData->mNext;
1878
0
  } while (aLoadData);
1879
0
}
1880
1881
Result<Loader::LoadSheetResult, nsresult>
1882
Loader::LoadInlineStyle(const SheetInfo& aInfo,
1883
                        const nsAString& aBuffer,
1884
                        uint32_t aLineNumber,
1885
                        nsICSSLoaderObserver* aObserver)
1886
0
{
1887
0
  LOG(("css::Loader::LoadInlineStyle"));
1888
0
1889
0
  if (!mEnabled) {
1890
0
    LOG_WARN(("  Not enabled"));
1891
0
    return Err(NS_ERROR_NOT_AVAILABLE);
1892
0
  }
1893
0
1894
0
  if (!mDocument) {
1895
0
    return Err(NS_ERROR_NOT_INITIALIZED);
1896
0
  }
1897
0
1898
0
  nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(
1899
0
      do_QueryInterface(aInfo.mContent));
1900
0
  NS_ASSERTION(owningElement, "Element is not a style linking element!");
1901
0
1902
0
  // Since we're not planning to load a URI, no need to hand a principal to the
1903
0
  // load data or to CreateSheet().
1904
0
1905
0
  // Check IsAlternateSheet now, since it can mutate our document.
1906
0
  auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
1907
0
1908
0
  StyleSheetState state;
1909
0
  RefPtr<StyleSheet> sheet;
1910
0
  nsresult rv = CreateSheet(aInfo,
1911
0
                            nullptr,
1912
0
                            eAuthorSheetFeatures,
1913
0
                            false,
1914
0
                            state,
1915
0
                            &sheet);
1916
0
  if (NS_FAILED(rv)) {
1917
0
    return Err(rv);
1918
0
  }
1919
0
  NS_ASSERTION(state == eSheetNeedsParser,
1920
0
               "Inline sheets should not be cached");
1921
0
1922
0
  LOG(("  Sheet is alternate: %d", static_cast<int>(isAlternate)));
1923
0
1924
0
  auto matched =
1925
0
    PrepareSheet(sheet, aInfo.mTitle, aInfo.mMedia, nullptr, isAlternate);
1926
0
1927
0
  InsertSheetInTree(*sheet, aInfo.mContent);
1928
0
1929
0
  nsIPrincipal* principal = aInfo.mContent->NodePrincipal();
1930
0
  if (aInfo.mTriggeringPrincipal) {
1931
0
    // The triggering principal may be an expanded principal, which is safe to
1932
0
    // use for URL security checks, but not as the loader principal for a
1933
0
    // stylesheet. So treat this as principal inheritance, and downgrade if
1934
0
    // necessary.
1935
0
    principal =
1936
0
      BasePrincipal::Cast(aInfo.mTriggeringPrincipal)->PrincipalToInherit();
1937
0
  }
1938
0
1939
0
  SheetLoadData* data = new SheetLoadData(this,
1940
0
                                          aInfo.mTitle,
1941
0
                                          nullptr,
1942
0
                                          sheet,
1943
0
                                          false,
1944
0
                                          owningElement,
1945
0
                                          isAlternate,
1946
0
                                          matched,
1947
0
                                          aObserver,
1948
0
                                          nullptr,
1949
0
                                          aInfo.mContent);
1950
0
1951
0
  // We never actually load this, so just set its principal directly
1952
0
  sheet->SetPrincipal(principal);
1953
0
1954
0
  NS_ADDREF(data);
1955
0
  data->mLineNumber = aLineNumber;
1956
0
  // Parse completion releases the load data.
1957
0
  //
1958
0
  // Note that we need to parse synchronously, since the web expects that the
1959
0
  // effects of inline stylesheets are visible immediately (aside from
1960
0
  // @imports).
1961
0
  NS_ConvertUTF16toUTF8 utf8(aBuffer);
1962
0
  Completed completed = ParseSheet(utf8, data, AllowAsyncParse::No);
1963
0
1964
0
  // If the sheet is complete already, |data| may well be deleted by now.
1965
0
  if (completed == Completed::No) {
1966
0
    data->mMustNotify = true;
1967
0
  }
1968
0
  return LoadSheetResult { completed, isAlternate, matched };
1969
0
}
1970
1971
Result<Loader::LoadSheetResult, nsresult>
1972
Loader::LoadStyleLink(const SheetInfo& aInfo, nsICSSLoaderObserver* aObserver)
1973
0
{
1974
0
  MOZ_ASSERT(aInfo.mURI, "Must have URL to load");
1975
0
  LOG(("css::Loader::LoadStyleLink"));
1976
0
  LOG_URI("  Link uri: '%s'", aInfo.mURI);
1977
0
  LOG(("  Link title: '%s'", NS_ConvertUTF16toUTF8(aInfo.mTitle).get()));
1978
0
  LOG(("  Link media: '%s'", NS_ConvertUTF16toUTF8(aInfo.mMedia).get()));
1979
0
  LOG(("  Link alternate rel: %d", aInfo.mHasAlternateRel));
1980
0
1981
0
  if (!mEnabled) {
1982
0
    LOG_WARN(("  Not enabled"));
1983
0
    return Err(NS_ERROR_NOT_AVAILABLE);
1984
0
  }
1985
0
1986
0
  if (!mDocument) {
1987
0
    return Err(NS_ERROR_NOT_INITIALIZED);
1988
0
  }
1989
0
1990
0
  nsIPrincipal* loadingPrincipal = aInfo.mContent
1991
0
    ? aInfo.mContent->NodePrincipal()
1992
0
    : mDocument->NodePrincipal();
1993
0
1994
0
  nsIPrincipal* principal = aInfo.mTriggeringPrincipal
1995
0
    ? aInfo.mTriggeringPrincipal.get()
1996
0
    : loadingPrincipal;
1997
0
1998
0
  nsINode* context = aInfo.mContent;
1999
0
  if (!context) {
2000
0
    context = mDocument;
2001
0
  }
2002
0
2003
0
  bool syncLoad = aInfo.mContent &&
2004
0
                  aInfo.mContent->IsInUAWidget() &&
2005
0
                  IsChromeURI(aInfo.mURI);
2006
0
  LOG(("  Link sync load: '%s'", syncLoad ? "true" : "false"));
2007
0
  MOZ_ASSERT_IF(syncLoad, !aObserver);
2008
0
2009
0
  nsresult rv = CheckContentPolicy(loadingPrincipal, principal, aInfo.mURI, context, false);
2010
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2011
0
    // Don't fire the error event if our document is loaded as data.  We're
2012
0
    // supposed to not even try to do loads in that case... Unfortunately, we
2013
0
    // implement that via nsDataDocumentContentPolicy, which doesn't have a good
2014
0
    // way to communicate back to us that _it_ is the thing that blocked the
2015
0
    // load.
2016
0
    if (aInfo.mContent && !mDocument->IsLoadedAsData()) {
2017
0
      // Fire an async error event on it.
2018
0
      RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
2019
0
        new LoadBlockingAsyncEventDispatcher(aInfo.mContent,
2020
0
                                             NS_LITERAL_STRING("error"),
2021
0
                                             CanBubble::eNo,
2022
0
                                             ChromeOnlyDispatch::eNo);
2023
0
      loadBlockingAsyncDispatcher->PostDOMEvent();
2024
0
    }
2025
0
    return Err(rv);
2026
0
  }
2027
0
2028
0
  StyleSheetState state;
2029
0
  RefPtr<StyleSheet> sheet;
2030
0
  auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
2031
0
  rv = CreateSheet(aInfo,
2032
0
                   principal,
2033
0
                   eAuthorSheetFeatures,
2034
0
                   syncLoad,
2035
0
                   state,
2036
0
                   &sheet);
2037
0
  if (NS_FAILED(rv)) {
2038
0
    return Err(rv);
2039
0
  }
2040
0
2041
0
  LOG(("  Sheet is alternate: %d", static_cast<int>(isAlternate)));
2042
0
2043
0
  auto matched =
2044
0
    PrepareSheet(sheet, aInfo.mTitle, aInfo.mMedia, nullptr, isAlternate);
2045
0
2046
0
  InsertSheetInTree(*sheet, aInfo.mContent);
2047
0
2048
0
  nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(
2049
0
    do_QueryInterface(aInfo.mContent));
2050
0
2051
0
  if (state == eSheetComplete) {
2052
0
    LOG(("  Sheet already complete: 0x%p", sheet.get()));
2053
0
    if (aObserver || !mObservers.IsEmpty() || owningElement) {
2054
0
      rv = PostLoadEvent(aInfo.mURI,
2055
0
                         sheet,
2056
0
                         aObserver,
2057
0
                         isAlternate,
2058
0
                         matched,
2059
0
                         owningElement);
2060
0
      if (NS_FAILED(rv)) {
2061
0
        return Err(rv);
2062
0
      }
2063
0
    }
2064
0
2065
0
    // The load hasn't been completed yet, will be done in PostLoadEvent.
2066
0
    return LoadSheetResult { Completed::No, isAlternate, matched };
2067
0
  }
2068
0
2069
0
  // Now we need to actually load it.
2070
0
  SheetLoadData* data = new SheetLoadData(this,
2071
0
                                          aInfo.mTitle,
2072
0
                                          aInfo.mURI,
2073
0
                                          sheet,
2074
0
                                          syncLoad,
2075
0
                                          owningElement,
2076
0
                                          isAlternate,
2077
0
                                          matched,
2078
0
                                          aObserver,
2079
0
                                          principal,
2080
0
                                          context);
2081
0
  NS_ADDREF(data);
2082
0
2083
0
  auto result = LoadSheetResult { Completed::No, isAlternate, matched };
2084
0
2085
0
  MOZ_ASSERT(result.ShouldBlock() == !data->ShouldDefer(),
2086
0
             "These should better match!");
2087
0
2088
0
  // If we have to parse and it's a non-blocking non-inline sheet, defer it.
2089
0
  if (!syncLoad &&
2090
0
      state == eSheetNeedsParser &&
2091
0
      mSheets->mLoadingDatas.Count() != 0 &&
2092
0
      !result.ShouldBlock()) {
2093
0
    LOG(("  Deferring sheet load"));
2094
0
    URIPrincipalReferrerPolicyAndCORSModeHashKey key(data);
2095
0
    mSheets->mPendingDatas.Put(&key, data);
2096
0
2097
0
    data->mMustNotify = true;
2098
0
    return result;
2099
0
  }
2100
0
2101
0
  // Load completion will free the data
2102
0
  rv = LoadSheet(data, state, false);
2103
0
  if (NS_FAILED(rv)) {
2104
0
    return Err(rv);
2105
0
  }
2106
0
2107
0
  if (!syncLoad) {
2108
0
    data->mMustNotify = true;
2109
0
  }
2110
0
  return result;
2111
0
}
2112
2113
static bool
2114
HaveAncestorDataWithURI(SheetLoadData *aData, nsIURI *aURI)
2115
0
{
2116
0
  if (!aData->mURI) {
2117
0
    // Inline style; this won't have any ancestors
2118
0
    MOZ_ASSERT(!aData->mParentData,
2119
0
               "How does inline style have a parent?");
2120
0
    return false;
2121
0
  }
2122
0
2123
0
  bool equal;
2124
0
  if (NS_FAILED(aData->mURI->Equals(aURI, &equal)) || equal) {
2125
0
    return true;
2126
0
  }
2127
0
2128
0
  // Datas down the mNext chain have the same URI as aData, so we
2129
0
  // don't have to compare to them.  But they might have different
2130
0
  // parents, and we have to check all of those.
2131
0
  while (aData) {
2132
0
    if (aData->mParentData &&
2133
0
        HaveAncestorDataWithURI(aData->mParentData, aURI)) {
2134
0
      return true;
2135
0
    }
2136
0
2137
0
    aData = aData->mNext;
2138
0
  }
2139
0
2140
0
  return false;
2141
0
}
2142
2143
nsresult
2144
Loader::LoadChildSheet(StyleSheet* aParentSheet,
2145
                       SheetLoadData* aParentData,
2146
                       nsIURI* aURL,
2147
                       dom::MediaList* aMedia,
2148
                       LoaderReusableStyleSheets* aReusableSheets)
2149
0
{
2150
0
  LOG(("css::Loader::LoadChildSheet"));
2151
0
  MOZ_ASSERT(aURL, "Must have a URI to load");
2152
0
  MOZ_ASSERT(aParentSheet, "Must have a parent sheet");
2153
0
2154
0
  if (!mEnabled) {
2155
0
    LOG_WARN(("  Not enabled"));
2156
0
    return NS_ERROR_NOT_AVAILABLE;
2157
0
  }
2158
0
2159
0
  LOG_URI("  Child uri: '%s'", aURL);
2160
0
2161
0
  nsCOMPtr<nsINode> owningNode;
2162
0
2163
0
  // Check for an associated document or shadow root: if none, don't bother
2164
0
  // walking up the parent sheets.
2165
0
  if (aParentSheet->GetAssociatedDocumentOrShadowRoot()) {
2166
0
    StyleSheet* topSheet = aParentSheet;
2167
0
    while (StyleSheet* parent = topSheet->GetParentSheet()) {
2168
0
      topSheet = parent;
2169
0
    }
2170
0
    owningNode = topSheet->GetOwnerNode();
2171
0
  }
2172
0
2173
0
  nsINode* context = nullptr;
2174
0
  nsIPrincipal* loadingPrincipal = nullptr;
2175
0
  if (owningNode) {
2176
0
    context = owningNode;
2177
0
    loadingPrincipal = owningNode->NodePrincipal();
2178
0
  } else if (mDocument) {
2179
0
    context = mDocument;
2180
0
    loadingPrincipal = mDocument->NodePrincipal();
2181
0
  }
2182
0
2183
0
  nsIPrincipal* principal = aParentSheet->Principal();
2184
0
  nsresult rv = CheckContentPolicy(loadingPrincipal, principal, aURL, context, false);
2185
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2186
0
    if (aParentData) {
2187
0
      MarkLoadTreeFailed(aParentData);
2188
0
    }
2189
0
    return rv;
2190
0
  }
2191
0
2192
0
  nsCOMPtr<nsICSSLoaderObserver> observer;
2193
0
2194
0
  if (aParentData) {
2195
0
    LOG(("  Have a parent load"));
2196
0
    // Check for cycles
2197
0
    if (HaveAncestorDataWithURI(aParentData, aURL)) {
2198
0
      // Houston, we have a loop, blow off this child and pretend this never
2199
0
      // happened
2200
0
      LOG_ERROR(("  @import cycle detected, dropping load"));
2201
0
      return NS_OK;
2202
0
    }
2203
0
2204
0
    NS_ASSERTION(aParentData->mSheet == aParentSheet,
2205
0
                 "Unexpected call to LoadChildSheet");
2206
0
  } else {
2207
0
    LOG(("  No parent load; must be CSSOM"));
2208
0
    // No parent load data, so the sheet will need to be notified when
2209
0
    // we finish, if it can be, if we do the load asynchronously.
2210
0
    observer = aParentSheet;
2211
0
  }
2212
0
2213
0
  // Now that we know it's safe to load this (passes security check and not a
2214
0
  // loop) do so.
2215
0
  RefPtr<StyleSheet> sheet;
2216
0
  StyleSheetState state;
2217
0
  if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, sheet)) {
2218
0
    state = eSheetComplete;
2219
0
  } else {
2220
0
    const nsAString& empty = EmptyString();
2221
0
    // For now, use CORS_NONE for child sheets
2222
0
    rv = CreateSheet(aURL,
2223
0
                     nullptr,
2224
0
                     principal,
2225
0
                     aParentSheet->ParsingMode(),
2226
0
                     CORS_NONE,
2227
0
                     aParentSheet->GetReferrerPolicy(),
2228
0
                     EmptyString(), // integrity is only checked on main sheet
2229
0
                     aParentData ? aParentData->mSyncLoad : false,
2230
0
                     state,
2231
0
                     &sheet);
2232
0
    NS_ENSURE_SUCCESS(rv, rv);
2233
0
2234
0
    PrepareSheet(sheet, empty, empty, aMedia, IsAlternate::No);
2235
0
  }
2236
0
2237
0
  MOZ_ASSERT(sheet);
2238
0
  InsertChildSheet(*sheet, *aParentSheet);
2239
0
2240
0
  if (state == eSheetComplete) {
2241
0
    LOG(("  Sheet already complete"));
2242
0
    // We're completely done.  No need to notify, even, since the
2243
0
    // @import rule addition/modification will trigger the right style
2244
0
    // changes automatically.
2245
0
    return NS_OK;
2246
0
  }
2247
0
2248
0
  SheetLoadData* data = new SheetLoadData(this, aURL, sheet, aParentData,
2249
0
                                          observer, principal, context);
2250
0
2251
0
  NS_ADDREF(data);
2252
0
  bool syncLoad = data->mSyncLoad;
2253
0
2254
0
  // Load completion will release the data
2255
0
  rv = LoadSheet(data, state, false);
2256
0
  NS_ENSURE_SUCCESS(rv, rv);
2257
0
2258
0
  // If syncLoad is true, |data| will be deleted by now.
2259
0
  if (!syncLoad) {
2260
0
    data->mMustNotify = true;
2261
0
  }
2262
0
  return rv;
2263
0
}
2264
2265
nsresult
2266
Loader::LoadSheetSync(nsIURI* aURL,
2267
                      SheetParsingMode aParsingMode,
2268
                      bool aUseSystemPrincipal,
2269
                      RefPtr<StyleSheet>* aSheet)
2270
0
{
2271
0
  LOG(("css::Loader::LoadSheetSync"));
2272
0
  return InternalLoadNonDocumentSheet(aURL,
2273
0
                                      false,
2274
0
                                      aParsingMode,
2275
0
                                      aUseSystemPrincipal,
2276
0
                                      nullptr,
2277
0
                                      nullptr,
2278
0
                                      aSheet,
2279
0
                                      nullptr);
2280
0
}
2281
2282
nsresult
2283
Loader::LoadSheet(nsIURI* aURL,
2284
                  SheetParsingMode aParsingMode,
2285
                  bool aUseSystemPrincipal,
2286
                  nsICSSLoaderObserver* aObserver,
2287
                  RefPtr<StyleSheet>* aSheet)
2288
0
{
2289
0
  LOG(("css::Loader::LoadSheet(aURL, aParsingMode, aUseSystemPrincipal, aObserver, aSheet)"));
2290
0
  return InternalLoadNonDocumentSheet(aURL,
2291
0
                                      false,
2292
0
                                      aParsingMode,
2293
0
                                      aUseSystemPrincipal,
2294
0
                                      nullptr,
2295
0
                                      nullptr,
2296
0
                                      aSheet,
2297
0
                                      aObserver);
2298
0
}
2299
2300
nsresult
2301
Loader::LoadSheet(nsIURI* aURL,
2302
                  nsIPrincipal* aOriginPrincipal,
2303
                  nsICSSLoaderObserver* aObserver,
2304
                  RefPtr<StyleSheet>* aSheet)
2305
0
{
2306
0
  LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call"));
2307
0
  MOZ_ASSERT(aSheet, "aSheet is null");
2308
0
  return InternalLoadNonDocumentSheet(aURL,
2309
0
                                      false,
2310
0
                                      eAuthorSheetFeatures,
2311
0
                                      false,
2312
0
                                      aOriginPrincipal,
2313
0
                                      nullptr,
2314
0
                                      aSheet,
2315
0
                                      aObserver);
2316
0
}
2317
2318
nsresult
2319
Loader::LoadSheet(nsIURI* aURL,
2320
                  bool aIsPreload,
2321
                  nsIPrincipal* aOriginPrincipal,
2322
                  const Encoding* aPreloadEncoding,
2323
                  nsICSSLoaderObserver* aObserver,
2324
                  CORSMode aCORSMode,
2325
                  ReferrerPolicy aReferrerPolicy,
2326
                  const nsAString& aIntegrity)
2327
0
{
2328
0
  LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
2329
0
  return InternalLoadNonDocumentSheet(aURL,
2330
0
                                      aIsPreload,
2331
0
                                      eAuthorSheetFeatures,
2332
0
                                      false,
2333
0
                                      aOriginPrincipal,
2334
0
                                      aPreloadEncoding,
2335
0
                                      nullptr,
2336
0
                                      aObserver,
2337
0
                                      aCORSMode,
2338
0
                                      aReferrerPolicy,
2339
0
                                      aIntegrity);
2340
0
}
2341
2342
nsresult
2343
Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
2344
                                     bool aIsPreload,
2345
                                     SheetParsingMode aParsingMode,
2346
                                     bool aUseSystemPrincipal,
2347
                                     nsIPrincipal* aOriginPrincipal,
2348
                                     const Encoding* aPreloadEncoding,
2349
                                     RefPtr<StyleSheet>* aSheet,
2350
                                     nsICSSLoaderObserver* aObserver,
2351
                                     CORSMode aCORSMode,
2352
                                     ReferrerPolicy aReferrerPolicy,
2353
                                     const nsAString& aIntegrity)
2354
0
{
2355
0
  MOZ_ASSERT(aURL, "Must have a URI to load");
2356
0
  MOZ_ASSERT(aSheet || aObserver, "Sheet and observer can't both be null");
2357
0
  MOZ_ASSERT(!aUseSystemPrincipal || !aObserver,
2358
0
             "Shouldn't load system-principal sheets async");
2359
0
2360
0
  LOG_URI("  Non-document sheet uri: '%s'", aURL);
2361
0
2362
0
  if (aSheet) {
2363
0
    *aSheet = nullptr;
2364
0
  }
2365
0
2366
0
  if (!mEnabled) {
2367
0
    LOG_WARN(("  Not enabled"));
2368
0
    return NS_ERROR_NOT_AVAILABLE;
2369
0
  }
2370
0
2371
0
  nsCOMPtr<nsIPrincipal> loadingPrincipal = (aOriginPrincipal && mDocument
2372
0
                                             ? mDocument->NodePrincipal()
2373
0
                                             : nullptr);
2374
0
  nsresult rv = CheckContentPolicy(loadingPrincipal, aOriginPrincipal,
2375
0
                                   aURL, mDocument, aIsPreload);
2376
0
  NS_ENSURE_SUCCESS(rv, rv);
2377
0
2378
0
  StyleSheetState state;
2379
0
  RefPtr<StyleSheet> sheet;
2380
0
  bool syncLoad = (aObserver == nullptr);
2381
0
  const nsAString& empty = EmptyString();
2382
0
  rv = CreateSheet(aURL, nullptr, aOriginPrincipal, aParsingMode,
2383
0
                   aCORSMode, aReferrerPolicy, aIntegrity, syncLoad,
2384
0
                   state, &sheet);
2385
0
  NS_ENSURE_SUCCESS(rv, rv);
2386
0
2387
0
  PrepareSheet(sheet, empty, empty, nullptr, IsAlternate::No);
2388
0
2389
0
  if (state == eSheetComplete) {
2390
0
    LOG(("  Sheet already complete"));
2391
0
    if (aObserver || !mObservers.IsEmpty()) {
2392
0
      rv = PostLoadEvent(aURL,
2393
0
                         sheet,
2394
0
                         aObserver,
2395
0
                         IsAlternate::No,
2396
0
                         MediaMatched::Yes,
2397
0
                         nullptr);
2398
0
    }
2399
0
    if (aSheet) {
2400
0
      sheet.swap(*aSheet);
2401
0
    }
2402
0
    return rv;
2403
0
  }
2404
0
2405
0
  SheetLoadData* data = new SheetLoadData(this,
2406
0
                                          aURL,
2407
0
                                          sheet,
2408
0
                                          syncLoad,
2409
0
                                          aUseSystemPrincipal,
2410
0
                                          aPreloadEncoding,
2411
0
                                          aObserver,
2412
0
                                          aOriginPrincipal,
2413
0
                                          mDocument);
2414
0
2415
0
  NS_ADDREF(data);
2416
0
  rv = LoadSheet(data, state, aIsPreload);
2417
0
  NS_ENSURE_SUCCESS(rv, rv);
2418
0
2419
0
  if (aSheet) {
2420
0
    sheet.swap(*aSheet);
2421
0
  }
2422
0
  if (aObserver) {
2423
0
    data->mMustNotify = true;
2424
0
  }
2425
0
2426
0
  return rv;
2427
0
}
2428
2429
nsresult
2430
Loader::PostLoadEvent(nsIURI* aURI,
2431
                      StyleSheet* aSheet,
2432
                      nsICSSLoaderObserver* aObserver,
2433
                      IsAlternate aWasAlternate,
2434
                      MediaMatched aMediaMatched,
2435
                      nsIStyleSheetLinkingElement* aElement)
2436
0
{
2437
0
  LOG(("css::Loader::PostLoadEvent"));
2438
0
  MOZ_ASSERT(aSheet, "Must have sheet");
2439
0
  MOZ_ASSERT(aObserver || !mObservers.IsEmpty() || aElement,
2440
0
             "Must have observer or element");
2441
0
2442
0
  RefPtr<SheetLoadData> evt =
2443
0
    new SheetLoadData(this,
2444
0
                      EmptyString(), // title doesn't matter here
2445
0
                      aURI,
2446
0
                      aSheet,
2447
0
                      false,
2448
0
                      aElement,
2449
0
                      aWasAlternate,
2450
0
                      aMediaMatched,
2451
0
                      aObserver,
2452
0
                      nullptr,
2453
0
                      mDocument);
2454
0
2455
0
  if (!mPostedEvents.AppendElement(evt)) {
2456
0
    return NS_ERROR_OUT_OF_MEMORY;
2457
0
  }
2458
0
2459
0
  nsresult rv;
2460
0
  RefPtr<SheetLoadData> runnable(evt);
2461
0
  if (mDocument) {
2462
0
    rv = mDocument->Dispatch(TaskCategory::Other, runnable.forget());
2463
0
  } else if (mDocGroup) {
2464
0
    rv = mDocGroup->Dispatch(TaskCategory::Other, runnable.forget());
2465
0
  } else {
2466
0
    rv = SystemGroup::Dispatch(TaskCategory::Other, runnable.forget());
2467
0
  }
2468
0
2469
0
  if (NS_FAILED(rv)) {
2470
0
    NS_WARNING("failed to dispatch stylesheet load event");
2471
0
    mPostedEvents.RemoveElement(evt);
2472
0
  } else {
2473
0
    // We'll unblock onload when we handle the event.
2474
0
    BlockOnload();
2475
0
2476
0
    // We want to notify the observer for this data.
2477
0
    evt->mMustNotify = true;
2478
0
    evt->mSheetAlreadyComplete = true;
2479
0
2480
0
    // If we get to this code, aSheet loaded correctly at some point, so
2481
0
    // we can just schedule a load event and don't need to touch the
2482
0
    // data's mLoadFailed.  Note that we do this here and not from
2483
0
    // inside our SheetComplete so that we don't end up running the load
2484
0
    // event async.
2485
0
    MOZ_ASSERT(!evt->mLoadFailed, "Why are we marked as failed?");
2486
0
    evt->ScheduleLoadEventIfNeeded();
2487
0
  }
2488
0
2489
0
  return rv;
2490
0
}
2491
2492
void
2493
Loader::HandleLoadEvent(SheetLoadData* aEvent)
2494
0
{
2495
0
  // XXXbz can't assert this yet.... May not have an observer because
2496
0
  // we're unblocking the parser
2497
0
  // NS_ASSERTION(aEvent->mObserver, "Must have observer");
2498
0
  NS_ASSERTION(aEvent->mSheet, "Must have sheet");
2499
0
2500
0
  // Very important: this needs to come before the SheetComplete call
2501
0
  // below, so that HasPendingLoads() will test true as needed under
2502
0
  // notifications we send from that SheetComplete call.
2503
0
  mPostedEvents.RemoveElement(aEvent);
2504
0
2505
0
  if (!aEvent->mIsCancelled) {
2506
0
    // SheetComplete will call Release(), so give it a reference to do
2507
0
    // that with.
2508
0
    NS_ADDREF(aEvent);
2509
0
    SheetComplete(aEvent, NS_OK);
2510
0
  }
2511
0
2512
0
  UnblockOnload(true);
2513
0
}
2514
2515
static void
2516
StopLoadingSheets(
2517
  nsDataHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey, SheetLoadData*>& aDatas,
2518
  Loader::LoadDataArray& aArr)
2519
0
{
2520
0
  for (auto iter = aDatas.Iter(); !iter.Done(); iter.Next()) {
2521
0
    SheetLoadData* data = iter.Data();
2522
0
    MOZ_ASSERT(data, "Must have a data!");
2523
0
2524
0
    data->mIsLoading = false; // we will handle the removal right here
2525
0
    data->mIsCancelled = true;
2526
0
2527
0
    aArr.AppendElement(data);
2528
0
2529
0
    iter.Remove();
2530
0
  }
2531
0
}
2532
2533
void
2534
Loader::Stop()
2535
0
{
2536
0
  uint32_t pendingCount = mSheets ? mSheets->mPendingDatas.Count() : 0;
2537
0
  uint32_t loadingCount = mSheets ? mSheets->mLoadingDatas.Count() : 0;
2538
0
  LoadDataArray arr(pendingCount + loadingCount + mPostedEvents.Length());
2539
0
2540
0
  if (pendingCount) {
2541
0
    StopLoadingSheets(mSheets->mPendingDatas, arr);
2542
0
  }
2543
0
  if (loadingCount) {
2544
0
    StopLoadingSheets(mSheets->mLoadingDatas, arr);
2545
0
  }
2546
0
2547
0
  for (RefPtr<SheetLoadData>& data : mPostedEvents) {
2548
0
    data->mIsCancelled = true;
2549
0
    // SheetComplete() calls Release(), so give this an extra ref.
2550
0
    NS_ADDREF(data.get());
2551
0
    // Move since we're about to get rid of the array below.
2552
0
    arr.AppendElement(std::move(data));
2553
0
  }
2554
0
  mPostedEvents.Clear();
2555
0
2556
0
  mDatasToNotifyOn += arr.Length();
2557
0
  for (RefPtr<SheetLoadData>& data : arr) {
2558
0
    --mDatasToNotifyOn;
2559
0
    SheetComplete(data, NS_BINDING_ABORTED);
2560
0
  }
2561
0
}
2562
2563
bool
2564
Loader::HasPendingLoads()
2565
0
{
2566
0
  return
2567
0
    (mSheets && mSheets->mLoadingDatas.Count() != 0) ||
2568
0
    (mSheets && mSheets->mPendingDatas.Count() != 0) ||
2569
0
    mPostedEvents.Length() != 0 ||
2570
0
    mDatasToNotifyOn != 0;
2571
0
}
2572
2573
nsresult
2574
Loader::AddObserver(nsICSSLoaderObserver* aObserver)
2575
0
{
2576
0
  MOZ_ASSERT(aObserver, "Must have observer");
2577
0
  if (mObservers.AppendElementUnlessExists(aObserver)) {
2578
0
    return NS_OK;
2579
0
  }
2580
0
2581
0
  return NS_ERROR_OUT_OF_MEMORY;
2582
0
}
2583
2584
void
2585
Loader::RemoveObserver(nsICSSLoaderObserver* aObserver)
2586
0
{
2587
0
  mObservers.RemoveElement(aObserver);
2588
0
}
2589
2590
void
2591
Loader::StartDeferredLoads()
2592
0
{
2593
0
  MOZ_ASSERT(mSheets, "Don't call me!");
2594
0
  LoadDataArray arr(mSheets->mPendingDatas.Count());
2595
0
  for (auto iter = mSheets->mPendingDatas.Iter(); !iter.Done(); iter.Next()) {
2596
0
    arr.AppendElement(iter.Data());
2597
0
    iter.Remove();
2598
0
  }
2599
0
2600
0
  mDatasToNotifyOn += arr.Length();
2601
0
  for (uint32_t i = 0; i < arr.Length(); ++i) {
2602
0
    --mDatasToNotifyOn;
2603
0
    LoadSheet(arr[i], eSheetNeedsParser, false);
2604
0
  }
2605
0
}
2606
2607
NS_IMPL_CYCLE_COLLECTION_CLASS(Loader)
2608
2609
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader)
2610
0
  if (tmp->mSheets) {
2611
0
    for (auto iter = tmp->mSheets->mCompleteSheets.Iter();
2612
0
         !iter.Done();
2613
0
         iter.Next()) {
2614
0
      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "Sheet cache nsCSSLoader");
2615
0
      cb.NoteXPCOMChild(iter.UserData());
2616
0
    }
2617
0
  }
2618
0
  nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver>>::ForwardIterator
2619
0
    it(tmp->mObservers);
2620
0
  while (it.HasMore()) {
2621
0
    ImplCycleCollectionTraverse(cb, it.GetNext(),
2622
0
                                "mozilla::css::Loader.mObservers");
2623
0
  }
2624
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2625
2626
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Loader)
2627
0
  if (tmp->mSheets) {
2628
0
    tmp->mSheets->mCompleteSheets.Clear();
2629
0
  }
2630
0
  tmp->mObservers.Clear();
2631
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2632
2633
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Loader, AddRef)
2634
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Loader, Release)
2635
2636
size_t
2637
Loader::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
2638
0
{
2639
0
  size_t n = aMallocSizeOf(this);
2640
0
2641
0
  if (mSheets) {
2642
0
    n += mSheets->mCompleteSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
2643
0
    for (auto iter = mSheets->mCompleteSheets.ConstIter();
2644
0
         !iter.Done();
2645
0
         iter.Next()) {
2646
0
      // If aSheet has a parent, then its parent will report it so we don't
2647
0
      // have to worry about it here. Likewise, if aSheet has an owning node,
2648
0
      // then the document that node is in will report it.
2649
0
      const StyleSheet* sheet = iter.UserData();
2650
0
      n += (sheet->GetOwnerNode() || sheet->GetParentSheet())
2651
0
         ? 0
2652
0
         : sheet->SizeOfIncludingThis(aMallocSizeOf);
2653
0
    }
2654
0
  }
2655
0
  n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
2656
0
2657
0
  // Measurement of the following members may be added later if DMD finds it is
2658
0
  // worthwhile:
2659
0
  // - mLoadingDatas: transient, and should be small
2660
0
  // - mPendingDatas: transient, and should be small
2661
0
  // - mPostedEvents: transient, and should be small
2662
0
  //
2663
0
  // The following members aren't measured:
2664
0
  // - mDocument, because it's a weak backpointer
2665
0
2666
0
  return n;
2667
0
}
2668
2669
void
2670
Loader::BlockOnload()
2671
0
{
2672
0
  if (mDocument) {
2673
0
    mDocument->BlockOnload();
2674
0
  }
2675
0
}
2676
2677
void
2678
Loader::UnblockOnload(bool aFireSync)
2679
0
{
2680
0
  if (mDocument) {
2681
0
    mDocument->UnblockOnload(aFireSync);
2682
0
  }
2683
0
}
2684
2685
already_AddRefed<nsISerialEventTarget>
2686
Loader::DispatchTarget()
2687
0
{
2688
0
  nsCOMPtr<nsISerialEventTarget> target;
2689
0
  if (mDocument) {
2690
0
    target = mDocument->EventTargetFor(TaskCategory::Other);
2691
0
  } else if (mDocGroup) {
2692
0
    target = mDocGroup->EventTargetFor(TaskCategory::Other);
2693
0
  } else {
2694
0
    target = SystemGroup::EventTargetFor(TaskCategory::Other);
2695
0
  }
2696
0
2697
0
  return target.forget();
2698
0
}
2699
2700
} // namespace css
2701
} // namespace mozilla