summaryrefslogtreecommitdiffstats
path: root/layout/style/Loader.h
blob: a9f5647ea94f3a250f775adb4224e7264c8cb814 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* loading of CSS style sheets using the network APIs */

#ifndef mozilla_css_Loader_h
#define mozilla_css_Loader_h

#include <tuple>
#include <utility>

#include "mozilla/Attributes.h"
#include "mozilla/CORSMode.h"
#include "mozilla/dom/LinkStyle.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/UniquePtr.h"
#include "nsCompatibility.h"
#include "nsCycleCollectionParticipant.h"
#include "nsStringFwd.h"
#include "nsTArray.h"
#include "nsTObserverArray.h"
#include "nsURIHashKey.h"
#include "nsRefPtrHashtable.h"

class nsICSSLoaderObserver;
class nsIConsoleReportCollector;
class nsIContent;
class nsIPrincipal;

namespace mozilla {

class PreloadHashKey;
class SharedStyleSheetCache;
class SheetLoadDataHashKey;
class StyleSheet;

namespace dom {
class DocGroup;
class Element;
}  // namespace dom

// The load data for a <link> or @import style-sheet.
//
// This must contain all the state that affects CSS parsing.
class SheetLoadDataHashKey : public PLDHashEntryHdr {
 public:
  enum class IsPreload : uint8_t {
    No,
    // This is a speculative load initiated by a <link rel=stylesheet> tag
    // scanned by the parser, or @import rules found in a <style> tag.
    FromParser,
    // This is a speculative load as well, but initiated by
    // <link rel="preload" as="style">
    FromLink,
  };

  using KeyType = const SheetLoadDataHashKey&;
  using KeyTypePointer = const SheetLoadDataHashKey*;

  explicit SheetLoadDataHashKey(const SheetLoadDataHashKey* aKey)
      : mURI(aKey->mURI),
        mPrincipal(aKey->mPrincipal),
        mLoaderPrincipal(aKey->mLoaderPrincipal),
        mPartitionPrincipal(aKey->mPartitionPrincipal),
        mEncodingGuess(aKey->mEncodingGuess),
        mCORSMode(aKey->mCORSMode),
        mParsingMode(aKey->mParsingMode),
        mCompatMode(aKey->mCompatMode),
        mSRIMetadata(aKey->mSRIMetadata),
        mIsLinkPreload(aKey->mIsLinkPreload) {
    MOZ_COUNT_CTOR(SheetLoadDataHashKey);
  }

  SheetLoadDataHashKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
                       nsIPrincipal* aLoaderPrincipal,
                       nsIPrincipal* aPartitionPrincipal,
                       NotNull<const Encoding*> aEncodingGuess,
                       CORSMode aCORSMode, css::SheetParsingMode aParsingMode,
                       nsCompatibility aCompatMode,
                       const dom::SRIMetadata& aSRIMetadata,
                       IsPreload aIsPreload)
      : mURI(aURI),
        mPrincipal(aPrincipal),
        mLoaderPrincipal(aLoaderPrincipal),
        mPartitionPrincipal(aPartitionPrincipal),
        mEncodingGuess(aEncodingGuess),
        mCORSMode(aCORSMode),
        mParsingMode(aParsingMode),
        mCompatMode(aCompatMode),
        mSRIMetadata(aSRIMetadata),
        mIsLinkPreload(aIsPreload == IsPreload::FromLink) {
    MOZ_ASSERT(aURI);
    MOZ_ASSERT(aPrincipal);
    MOZ_ASSERT(aLoaderPrincipal);
    MOZ_COUNT_CTOR(SheetLoadDataHashKey);
  }

  SheetLoadDataHashKey(SheetLoadDataHashKey&& toMove)
      : mURI(std::move(toMove.mURI)),
        mPrincipal(std::move(toMove.mPrincipal)),
        mLoaderPrincipal(std::move(toMove.mLoaderPrincipal)),
        mPartitionPrincipal(std::move(toMove.mPartitionPrincipal)),
        mEncodingGuess(std::move(toMove.mEncodingGuess)),
        mCORSMode(std::move(toMove.mCORSMode)),
        mParsingMode(std::move(toMove.mParsingMode)),
        mCompatMode(std::move(toMove.mCompatMode)),
        mSRIMetadata(std::move(toMove.mSRIMetadata)),
        mIsLinkPreload(std::move(toMove.mIsLinkPreload)) {
    MOZ_COUNT_CTOR(SheetLoadDataHashKey);
  }

  explicit SheetLoadDataHashKey(const css::SheetLoadData&);

  MOZ_COUNTED_DTOR(SheetLoadDataHashKey)

  const SheetLoadDataHashKey& GetKey() const { return *this; }
  const SheetLoadDataHashKey* GetKeyPointer() const { return this; }

  bool KeyEquals(const SheetLoadDataHashKey* aKey) const {
    return KeyEquals(*aKey);
  }

  bool KeyEquals(const SheetLoadDataHashKey&) const;

  static const SheetLoadDataHashKey* KeyToPointer(
      const SheetLoadDataHashKey& aKey) {
    return &aKey;
  }
  static PLDHashNumber HashKey(const SheetLoadDataHashKey* aKey) {
    return nsURIHashKey::HashKey(aKey->mURI);
  }

  nsIURI* URI() const { return mURI; }

  nsIPrincipal* Principal() const { return mPrincipal; }

  nsIPrincipal* LoaderPrincipal() const { return mLoaderPrincipal; }

  css::SheetParsingMode ParsingMode() const { return mParsingMode; }

  enum { ALLOW_MEMMOVE = true };

 protected:
  const nsCOMPtr<nsIURI> mURI;
  const nsCOMPtr<nsIPrincipal> mPrincipal;
  const nsCOMPtr<nsIPrincipal> mLoaderPrincipal;
  const nsCOMPtr<nsIPrincipal> mPartitionPrincipal;
  // The encoding guess is the encoding the sheet would get if the request
  // didn't have any encoding information like @charset or a Content-Encoding
  // header.
  const NotNull<const Encoding*> mEncodingGuess;
  const CORSMode mCORSMode;
  const css::SheetParsingMode mParsingMode;
  const nsCompatibility mCompatMode;
  dom::SRIMetadata mSRIMetadata;
  const bool mIsLinkPreload;
};

namespace css {

class SheetLoadData;
class ImportRule;

/*********************
 * Style sheet reuse *
 *********************/

class MOZ_RAII LoaderReusableStyleSheets {
 public:
  LoaderReusableStyleSheets() = default;

  /**
   * Look for a reusable sheet (see AddReusableSheet) matching the
   * given URL.  If found, set aResult, remove the reused sheet from
   * the internal list, and return true.  If not found, return false;
   * in this case, aResult is not modified.
   *
   * @param aURL the url to match
   * @param aResult [out] the style sheet which can be reused
   */
  bool FindReusableStyleSheet(nsIURI* aURL, RefPtr<StyleSheet>& aResult);

  /**
   * Indicate that a certain style sheet is available for reuse if its
   * URI matches the URI of an @import.  Sheets should be added in the
   * opposite order in which they are intended to be reused.
   *
   * @param aSheet the sheet which can be reused
   */
  void AddReusableSheet(StyleSheet* aSheet) {
    mReusableSheets.AppendElement(aSheet);
  }

 private:
  LoaderReusableStyleSheets(const LoaderReusableStyleSheets&) = delete;
  LoaderReusableStyleSheets& operator=(const LoaderReusableStyleSheets&) =
      delete;

  // The sheets that can be reused.
  nsTArray<RefPtr<StyleSheet>> mReusableSheets;
};

class Loader final {
  using ReferrerPolicy = dom::ReferrerPolicy;

 public:
  using Completed = dom::LinkStyle::Completed;
  using HasAlternateRel = dom::LinkStyle::HasAlternateRel;
  using IsAlternate = dom::LinkStyle::IsAlternate;
  using IsInline = dom::LinkStyle::IsInline;
  using IsExplicitlyEnabled = dom::LinkStyle::IsExplicitlyEnabled;
  using MediaMatched = dom::LinkStyle::MediaMatched;
  using LoadSheetResult = dom::LinkStyle::Update;
  using SheetInfo = dom::LinkStyle::SheetInfo;

  Loader();
  // aDocGroup is used for dispatching SheetLoadData in PostLoadEvent(). It
  // can be null if you want to use this constructor, and there's no
  // document when the Loader is constructed.
  explicit Loader(dom::DocGroup*);
  explicit Loader(dom::Document*);

 private:
  // Private destructor, to discourage deletion outside of Release():
  ~Loader();

 public:
  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Loader)
  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Loader)

  void DropDocumentReference();  // notification that doc is going away

  void DeregisterFromSheetCache();
  void RegisterInSheetCache();

  void SetCompatibilityMode(nsCompatibility aCompatMode) {
    mCompatMode = aCompatMode;
  }
  nsCompatibility GetCompatibilityMode() { return mCompatMode; }

  // TODO(emilio): Is the complexity of this method and carrying the titles
  // around worth it? The alternate sheets will load anyhow eventually...
  void DocumentStyleSheetSetChanged();

  // XXXbz sort out what the deal is with events!  When should they fire?

  /**
   * Load an inline style sheet.  If a successful result is returned and
   * result.WillNotify() is true, then aObserver is guaranteed to be notified
   * asynchronously once the sheet is marked complete.  If an error is
   * returned, or if result.WillNotify() is false, aObserver will not be
   * notified.  In addition to parsing the sheet, this method will insert it
   * into the stylesheet list of this CSSLoader's document.
   * @param aObserver the observer to notify when the load completes.
   *        May be null.
   * @param aBuffer the stylesheet data
   * @param aLineNumber the line number at which the stylesheet data started.
   */
  Result<LoadSheetResult, nsresult> LoadInlineStyle(
      const SheetInfo&, const nsAString& aBuffer, uint32_t aLineNumber,
      nsICSSLoaderObserver* aObserver);

  /**
   * Load a linked (document) stylesheet.  If a successful result is returned,
   * aObserver is guaranteed to be notified asynchronously once the sheet is
   * loaded and marked complete, i.e., result.WillNotify() will always return
   * true.  If an error is returned, aObserver will not be notified.  In
   * addition to loading the sheet, this method will insert it into the
   * stylesheet list of this CSSLoader's document.
   * @param aObserver the observer to notify when the load completes.
   *                  May be null.
   */
  Result<LoadSheetResult, nsresult> LoadStyleLink(
      const SheetInfo&, nsICSSLoaderObserver* aObserver);

  /**
   * Load a child (@import-ed) style sheet.  In addition to loading the sheet,
   * this method will insert it into the child sheet list of aParentSheet.  If
   * there is no sheet currently being parsed and the child sheet is not
   * complete when this method returns, then when the child sheet becomes
   * complete aParentSheet will be QIed to nsICSSLoaderObserver and
   * asynchronously notified, just like for LoadStyleLink.  Note that if the
   * child sheet is already complete when this method returns, no
   * nsICSSLoaderObserver notification will be sent.
   *
   * @param aParentSheet the parent of this child sheet
   * @param aParentData the SheetLoadData corresponding to the load of the
   *                    parent sheet. May be null for @import rules inserted via
   *                    CSSOM.
   * @param aURL the URL of the child sheet
   * @param aMedia the already-parsed media list for the child sheet
   * @param aSavedSheets any saved style sheets which could be reused
   *              for this load
   */
  nsresult LoadChildSheet(StyleSheet& aParentSheet, SheetLoadData* aParentData,
                          nsIURI* aURL, dom::MediaList* aMedia,
                          LoaderReusableStyleSheets* aSavedSheets);

  /**
   * Called when we hit the internal memory cache with a complete stylesheet.
   */
  void DidHitCompleteSheetCache(const SheetLoadDataHashKey&,
                                const StyleUseCounters* aCounters);

  enum class UseSystemPrincipal { No, Yes };

  /**
   * Synchronously load and return the stylesheet at aURL.  Any child sheets
   * will also be loaded synchronously.  Note that synchronous loads over some
   * protocols may involve spinning up a new event loop, so use of this method
   * does NOT guarantee not receiving any events before the sheet loads.  This
   * method can be used to load sheets not associated with a document.
   *
   * @param aURL the URL of the sheet to load
   * @param aParsingMode the mode in which to parse the sheet
   *        (see comments at enum SheetParsingMode, above).
   * @param aUseSystemPrincipal if true, give the resulting sheet the system
   * principal no matter where it's being loaded from.
   *
   * NOTE: At the moment, this method assumes the sheet will be UTF-8, but
   * ideally it would allow arbitrary encodings.  Callers should NOT depend on
   * non-UTF8 sheets being treated as UTF-8 by this method.
   *
   * NOTE: A successful return from this method doesn't indicate anything about
   * whether the data could be parsed as CSS and doesn't indicate anything
   * about the status of child sheets of the returned sheet.
   */
  Result<RefPtr<StyleSheet>, nsresult> LoadSheetSync(
      nsIURI*, SheetParsingMode = eAuthorSheetFeatures,
      UseSystemPrincipal = UseSystemPrincipal::No);

  using IsPreload = SheetLoadDataHashKey::IsPreload;

  /**
   * Asynchronously load the stylesheet at aURL.  If a successful result is
   * returned, aObserver is guaranteed to be notified asynchronously once the
   * sheet is loaded and marked complete.  This method can be used to load
   * sheets not associated with a document.
   *
   * @param aURL the URL of the sheet to load
   * @param aParsingMode the mode in which to parse the sheet
   *        (see comments at enum SheetParsingMode, above).
   * @param aUseSystemPrincipal if true, give the resulting sheet the system
   * principal no matter where it's being loaded from.
   * @param aReferrerInfo referrer information of the sheet.
   * @param aObserver the observer to notify when the load completes.
   *                  Must not be null.
   * @return the sheet to load. Note that the sheet may well not be loaded by
   * the time this method returns.
   *
   * NOTE: At the moment, this method assumes the sheet will be UTF-8, but
   * ideally it would allow arbitrary encodings.  Callers should NOT depend on
   * non-UTF8 sheets being treated as UTF-8 by this method.
   */
  Result<RefPtr<StyleSheet>, nsresult> LoadSheet(
      nsIURI* aURI, IsPreload, const Encoding* aPreloadEncoding,
      nsIReferrerInfo* aReferrerInfo, nsICSSLoaderObserver* aObserver,
      CORSMode = CORS_NONE, const nsAString& aIntegrity = u""_ns);

  /**
   * As above, but without caring for a couple things.
   */
  Result<RefPtr<StyleSheet>, nsresult> LoadSheet(nsIURI*, SheetParsingMode,
                                                 UseSystemPrincipal,
                                                 nsICSSLoaderObserver*);

  /**
   * Stop loading all sheets.  All nsICSSLoaderObservers involved will be
   * notified with NS_BINDING_ABORTED as the status, possibly synchronously.
   */
  void Stop();

  /**
   * nsresult Loader::StopLoadingSheet(nsIURI* aURL), which notifies the
   * nsICSSLoaderObserver with NS_BINDING_ABORTED, was removed in Bug 556446.
   * It can be found in revision 2c44a32052ad.
   */

  /**
   * Whether the loader is enabled or not.
   * When disabled, processing of new styles is disabled and an attempt
   * to do so will fail with a return code of
   * NS_ERROR_NOT_AVAILABLE. Note that this DOES NOT disable
   * currently loading styles or already processed styles.
   */
  bool GetEnabled() { return mEnabled; }
  void SetEnabled(bool aEnabled) { mEnabled = aEnabled; }

  uint32_t ParsedSheetCount() const { return mParsedSheetCount; }

  /**
   * Get the document we live for. May return null.
   */
  dom::Document* GetDocument() const { return mDocument; }

  /**
   * Return true if this loader has pending loads (ones that would send
   * notifications to an nsICSSLoaderObserver attached to this loader).
   * If called from inside nsICSSLoaderObserver::StyleSheetLoaded, this will
   * return false if and only if that is the last StyleSheetLoaded
   * notification the CSSLoader knows it's going to send.  In other words, if
   * two sheets load at once (via load coalescing, e.g.), HasPendingLoads()
   * will return true during notification for the first one, and false
   * during notification for the second one.
   */
  bool HasPendingLoads();

  /**
   * Add an observer to this loader.  The observer will be notified
   * for all loads that would have notified their own observers (even
   * if those loads don't have observers attached to them).
   * Load-specific observers will be notified before generic
   * observers.  The loader holds a reference to the observer.
   *
   * aObserver must not be null.
   */
  void AddObserver(nsICSSLoaderObserver* aObserver);

  /**
   * Remove an observer added via AddObserver.
   */
  void RemoveObserver(nsICSSLoaderObserver* aObserver);

  // These interfaces are public only for the benefit of static functions
  // within nsCSSLoader.cpp.

  // IsAlternateSheet can change our currently selected style set if none is
  // selected and aHasAlternateRel is false.
  IsAlternate IsAlternateSheet(const nsAString& aTitle, bool aHasAlternateRel);

  typedef nsTArray<RefPtr<SheetLoadData>> LoadDataArray;

  // Measure our size.
  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;

  enum class SheetState : uint8_t {
    NeedsParser = 0,
    Pending,
    Loading,
    Complete
  };

  // The loader principal is the document's node principal, if this loader is
  // owned by a document, or the system principal otherwise.
  nsIPrincipal* LoaderPrincipal() const;

  // The partitioned principal is the document's partitioned principal, if this
  // loader is owned by a document, or the system principal otherwise.
  nsIPrincipal* PartitionedPrincipal() const;

  bool ShouldBypassCache() const;

 private:
  friend class mozilla::SharedStyleSheetCache;
  friend class SheetLoadData;
  friend class StreamLoader;

  // Helpers to conditionally block onload if mDocument is non-null.
  void IncrementOngoingLoadCount() {
    if (!mOngoingLoadCount++) {
      BlockOnload();
    }
  }

  void DecrementOngoingLoadCount() {
    MOZ_DIAGNOSTIC_ASSERT(mOngoingLoadCount);
    MOZ_DIAGNOSTIC_ASSERT(mOngoingLoadCount > mPendingLoadCount);
    if (!--mOngoingLoadCount) {
      UnblockOnload(false);
    }
  }

  void BlockOnload();
  void UnblockOnload(bool aFireSync);

  // Helper to select the correct dispatch target for asynchronous events for
  // this loader.
  already_AddRefed<nsISerialEventTarget> DispatchTarget();

  nsresult CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
                              nsIPrincipal* aTriggeringPrincipal,
                              nsIURI* aTargetURI, nsINode* aRequestingNode,
                              const nsAString& aNonce, IsPreload);

  std::tuple<RefPtr<StyleSheet>, SheetState> CreateSheet(
      const SheetInfo& aInfo, css::SheetParsingMode aParsingMode,
      bool aSyncLoad, IsPreload aIsPreload) {
    nsIPrincipal* triggeringPrincipal = aInfo.mTriggeringPrincipal
                                            ? aInfo.mTriggeringPrincipal.get()
                                            : LoaderPrincipal();
    return CreateSheet(aInfo.mURI, aInfo.mContent, triggeringPrincipal,
                       aParsingMode, aInfo.mCORSMode,
                       /* aPreloadOrParentDataEncoding = */ nullptr,
                       aInfo.mIntegrity, aSyncLoad, aIsPreload);
  }

  // For inline style, the aURI param is null, but the aLinkingContent
  // must be non-null then.  The loader principal must never be null
  // if aURI is not null.
  std::tuple<RefPtr<StyleSheet>, SheetState> CreateSheet(
      nsIURI* aURI, nsIContent* aLinkingContent,
      nsIPrincipal* aTriggeringPrincipal, css::SheetParsingMode, CORSMode,
      const Encoding* aPreloadOrParentDataEncoding, const nsAString& aIntegrity,
      bool aSyncLoad, IsPreload aIsPreload);

  // Pass in either a media string or the MediaList from the CSSParser.  Don't
  // pass both.
  //
  // This method will set the sheet's enabled state based on IsAlternate and co.
  MediaMatched PrepareSheet(StyleSheet&, const nsAString& aTitle,
                            const nsAString& aMediaString, dom::MediaList*,
                            IsAlternate, IsExplicitlyEnabled);

  // Inserts a style sheet in a document or a ShadowRoot.
  void InsertSheetInTree(StyleSheet& aSheet, nsINode* aOwningNode);
  // Inserts a style sheet into a parent style sheet.
  void InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet);

  Result<RefPtr<StyleSheet>, nsresult> InternalLoadNonDocumentSheet(
      nsIURI* aURL, IsPreload, SheetParsingMode aParsingMode,
      UseSystemPrincipal, const Encoding* aPreloadEncoding,
      nsIReferrerInfo* aReferrerInfo, nsICSSLoaderObserver* aObserver,
      CORSMode aCORSMode, const nsAString& aIntegrity);

  RefPtr<StyleSheet> LookupInlineSheetInCache(const nsAString&);

  // Post a load event for aObserver to be notified about aSheet.  The
  // notification will be sent with status NS_OK unless the load event is
  // canceled at some point (in which case it will be sent with
  // NS_BINDING_ABORTED).
  nsresult PostLoadEvent(RefPtr<SheetLoadData>);

  // Start the loads of all the sheets in mPendingDatas
  void StartDeferredLoads();

  void HandleLoadEvent(SheetLoadData&);

  // Note: LoadSheet is responsible for setting the sheet to complete on
  // failure.
  enum class PendingLoad { No, Yes };
  nsresult LoadSheet(SheetLoadData&, SheetState, PendingLoad = PendingLoad::No);

  enum class AllowAsyncParse {
    Yes,
    No,
  };

  // Parse the stylesheet in the load data.
  //
  // Returns whether the parse finished. It may not finish e.g. if the sheet had
  // an @import.
  //
  // If this function returns Completed::Yes, then ParseSheet also called
  // SheetComplete on aLoadData.
  Completed ParseSheet(const nsACString&, SheetLoadData&, AllowAsyncParse);

  // The load of the sheet in the load data is done, one way or another.
  // Do final cleanup.
  void SheetComplete(SheetLoadData&, nsresult);

  // Notify observers on an individual data. This is different from
  // SheetComplete for loads that are shared.
  void NotifyObservers(SheetLoadData&, nsresult);

  // Mark the given SheetLoadData, as well as any of its siblings, parents, etc
  // transitively, as failed.  The idea is to mark as failed any load that was
  // directly or indirectly @importing the sheet this SheetLoadData represents.
  //
  // if aOnlyForLoader is non-null, then only loads for a given loader will be
  // marked as failing. This is useful to only cancel loads associated to a
  // given loader, in case they were marked as canceled.
  static void MarkLoadTreeFailed(SheetLoadData&,
                                 Loader* aOnlyForLoader = nullptr);

  // A shorthand to mark a possible link preload as used to supress "unused"
  // warning in the console.
  void MaybeNotifyPreloadUsed(SheetLoadData&);

  nsRefPtrHashtable<nsStringHashKey, StyleSheet> mInlineSheets;

  // A set with all the different loads we've done in a given document, for the
  // purpose of not posting duplicate performance entries for them.
  nsTHashtable<const SheetLoadDataHashKey> mLoadsPerformed;

  RefPtr<SharedStyleSheetCache> mSheets;

  // The array of posted stylesheet loaded events (SheetLoadDatas) we have.
  // Note that these are rare.
  LoadDataArray mPostedEvents;

  // Our array of "global" observers
  nsTObserverArray<nsCOMPtr<nsICSSLoaderObserver>> mObservers;

  // This reference is nulled by the Document in it's destructor through
  // DropDocumentReference().
  dom::Document* MOZ_NON_OWNING_REF mDocument;  // the document we live for

  // For dispatching events via DocGroup::Dispatch() when mDocument is nullptr.
  RefPtr<dom::DocGroup> mDocGroup;

  nsCompatibility mCompatMode;

  nsCOMPtr<nsIConsoleReportCollector> mReporter;

  // Number of datas for asynchronous sheet loads still waiting to be notified.
  // This includes pending stylesheets whose load hasn't started yet but which
  // we need to, but not inline or constructable stylesheets, though the
  // constructable stylesheets bit may change, see bug 1642227.
  uint32_t mOngoingLoadCount = 0;

  // The number of sheets that have been deferred / are in a pending state.
  uint32_t mPendingLoadCount = 0;

  // The number of stylesheets that we have parsed, for testing purposes.
  uint32_t mParsedSheetCount = 0;

  bool mEnabled = true;

#ifdef DEBUG
  // Whether we're in a necko callback atm.
  bool mSyncCallback = false;
#endif
};

}  // namespace css
}  // namespace mozilla

#endif /* mozilla_css_Loader_h */