tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

StyleSheet.cpp (49184B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/StyleSheet.h"
      8 
      9 #include "mozAutoDocUpdate.h"
     10 #include "mozilla/AlreadyAddRefed.h"
     11 #include "mozilla/Assertions.h"
     12 #include "mozilla/BasePrincipal.h"
     13 #include "mozilla/ComputedStyleInlines.h"
     14 #include "mozilla/NullPrincipal.h"
     15 #include "mozilla/ServoBindings.h"
     16 #include "mozilla/ServoCSSRuleList.h"
     17 #include "mozilla/ServoStyleSet.h"
     18 #include "mozilla/StaticPrefs_layout.h"
     19 #include "mozilla/StyleSheetInlines.h"
     20 #include "mozilla/css/ErrorReporter.h"
     21 #include "mozilla/css/GroupRule.h"
     22 #include "mozilla/css/SheetLoadData.h"
     23 #include "mozilla/dom/CSSImportRule.h"
     24 #include "mozilla/dom/CSSRuleList.h"
     25 #include "mozilla/dom/Element.h"
     26 #include "mozilla/dom/FetchPriority.h"
     27 #include "mozilla/dom/MediaList.h"
     28 #include "mozilla/dom/Promise.h"
     29 #include "mozilla/dom/ReferrerInfo.h"
     30 #include "mozilla/dom/ShadowRoot.h"
     31 #include "mozilla/dom/ShadowRootBinding.h"
     32 
     33 namespace mozilla {
     34 
     35 using namespace dom;
     36 
     37 StyleSheet::StyleSheet(css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
     38                       const dom::SRIMetadata& aIntegrity)
     39    : mParentSheet(nullptr),
     40      mConstructorDocument(nullptr),
     41      mDocumentOrShadowRoot(nullptr),
     42      mURLData{URLExtraData::Dummy()},
     43      mParsingMode(aParsingMode),
     44      mState(static_cast<State>(0)),
     45      mInner(new StyleSheetInfo(aCORSMode, aIntegrity, aParsingMode)) {
     46  mInner->AddSheet(this);
     47 }
     48 
     49 StyleSheet::StyleSheet(const StyleSheet& aCopy, StyleSheet* aParentSheetToUse,
     50                       dom::DocumentOrShadowRoot* aDocOrShadowRootToUse,
     51                       dom::Document* aConstructorDocToUse)
     52    : mParentSheet(aParentSheetToUse),
     53      mConstructorDocument(aConstructorDocToUse),
     54      mTitle(aCopy.mTitle),
     55      mDocumentOrShadowRoot(aDocOrShadowRootToUse),
     56      mURLData(aCopy.mURLData),
     57      mOriginalSheetURI(aCopy.mOriginalSheetURI),
     58      mParsingMode(aCopy.mParsingMode),
     59      mState(aCopy.mState),
     60      // Shallow copy, but concrete subclasses will fix up.
     61      mInner(aCopy.mInner) {
     62  MOZ_ASSERT(!aConstructorDocToUse || aCopy.IsConstructed());
     63  MOZ_ASSERT(!aConstructorDocToUse || !aDocOrShadowRootToUse,
     64             "Should never have both of these together.");
     65  MOZ_ASSERT(mInner, "Should only copy StyleSheets with an mInner.");
     66  mInner->AddSheet(this);
     67  // CSSOM's been there, force full copy now.
     68  if (HasForcedUniqueInner()) {
     69    MOZ_ASSERT(IsComplete(),
     70               "Why have rules been accessed on an incomplete sheet?");
     71    EnsureUniqueInner();
     72    // But CSSOM hasn't been on _this_ stylesheet yet, so no need to clone
     73    // ourselves.
     74    mState &= ~(State::ForcedUniqueInner | State::ModifiedRules |
     75                State::ModifiedRulesForDevtools);
     76  }
     77 
     78  if (aCopy.mMedia) {
     79    // XXX This is wrong; we should be keeping @import rules and
     80    // sheets in sync!
     81    mMedia = aCopy.mMedia->Clone();
     82  }
     83 }
     84 
     85 /* static */
     86 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-cssstylesheet
     87 already_AddRefed<StyleSheet> StyleSheet::Constructor(
     88    const dom::GlobalObject& aGlobal, const dom::CSSStyleSheetInit& aOptions,
     89    ErrorResult& aRv) {
     90  nsCOMPtr<nsPIDOMWindowInner> window =
     91      do_QueryInterface(aGlobal.GetAsSupports());
     92 
     93  if (!window) {
     94    aRv.ThrowNotSupportedError("Not supported when there is no document");
     95    return nullptr;
     96  }
     97 
     98  Document* constructorDocument = window->GetExtantDoc();
     99  if (!constructorDocument) {
    100    aRv.ThrowNotSupportedError("Not supported when there is no document");
    101    return nullptr;
    102  }
    103 
    104  return CreateConstructedSheet(
    105      *constructorDocument, constructorDocument->GetBaseURI(), aOptions, aRv);
    106 }
    107 
    108 StyleSheet::~StyleSheet() {
    109  MOZ_ASSERT(!mInner, "Inner should have been dropped in LastRelease");
    110 }
    111 
    112 bool StyleSheet::HasRules() const {
    113  return Servo_StyleSheet_HasRules(Inner().mContents);
    114 }
    115 
    116 Document* StyleSheet::GetAssociatedDocument() const {
    117  auto* associated = GetAssociatedDocumentOrShadowRoot();
    118  return associated ? associated->AsNode().OwnerDoc() : nullptr;
    119 }
    120 
    121 dom::DocumentOrShadowRoot* StyleSheet::GetAssociatedDocumentOrShadowRoot()
    122    const {
    123  const StyleSheet& outer = OutermostSheet();
    124  if (outer.mDocumentOrShadowRoot) {
    125    return outer.mDocumentOrShadowRoot;
    126  }
    127  if (outer.IsConstructed()) {
    128    return outer.mConstructorDocument;
    129  }
    130  return nullptr;
    131 }
    132 
    133 void StyleSheet::UpdateRelevantGlobal() {
    134  if (mRelevantGlobal || !IsComplete()) {
    135    return;
    136  }
    137  if (Document* doc = GetAssociatedDocument()) {
    138    mRelevantGlobal = doc->GetScopeObject();
    139  }
    140 }
    141 
    142 Document* StyleSheet::GetKeptAliveByDocument() const {
    143  const StyleSheet& outer = OutermostSheet();
    144  if (outer.mDocumentOrShadowRoot) {
    145    return outer.mDocumentOrShadowRoot->AsNode().GetComposedDoc();
    146  }
    147  if (outer.IsConstructed()) {
    148    for (DocumentOrShadowRoot* adopter : outer.mAdopters) {
    149      MOZ_ASSERT(adopter->AsNode().OwnerDoc() == outer.mConstructorDocument);
    150      if (adopter->AsNode().IsInComposedDoc()) {
    151        return outer.mConstructorDocument.get();
    152      }
    153    }
    154  }
    155  return nullptr;
    156 }
    157 
    158 void StyleSheet::LastRelease() {
    159  MOZ_DIAGNOSTIC_ASSERT(mAdopters.IsEmpty(),
    160                        "Should have no adopters at time of destruction.");
    161 
    162  if (mInner) {
    163    MOZ_ASSERT(mInner->mSheets.Contains(this), "Our mInner should include us.");
    164    mInner->RemoveSheet(this);
    165    mInner = nullptr;
    166  }
    167 
    168  DropMedia();
    169  DropRuleList();
    170 }
    171 
    172 void StyleSheet::UnlinkInner() {
    173  if (!mInner) {
    174    return;
    175  }
    176 
    177  // We can only have a cycle through our inner if we have a unique inner,
    178  // because otherwise there are no JS wrappers for anything in the inner.
    179  if (mInner->mSheets.Length() != 1) {
    180    mInner->RemoveSheet(this);
    181    mInner = nullptr;
    182    return;
    183  }
    184 
    185  for (StyleSheet* child : ChildSheets()) {
    186    MOZ_ASSERT(child->mParentSheet == this, "We have a unique inner!");
    187    child->mParentSheet = nullptr;
    188  }
    189  Inner().mChildren.Clear();
    190 }
    191 
    192 void StyleSheet::TraverseInner(nsCycleCollectionTraversalCallback& cb) {
    193  if (!mInner) {
    194    return;
    195  }
    196 
    197  for (StyleSheet* child : ChildSheets()) {
    198    if (child->mParentSheet == this) {
    199      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet");
    200      cb.NoteXPCOMChild(child);
    201    }
    202  }
    203 }
    204 
    205 // QueryInterface implementation for StyleSheet
    206 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheet)
    207  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    208  NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
    209  NS_INTERFACE_MAP_ENTRY(nsISupports)
    210 NS_INTERFACE_MAP_END
    211 
    212 NS_IMPL_CYCLE_COLLECTING_ADDREF(StyleSheet)
    213 // We want to disconnect from our inner as soon as our refcount drops to zero,
    214 // without waiting for async deletion by the cycle collector.  Otherwise we
    215 // might end up cloning the inner if someone mutates another sheet that shares
    216 // it with us, even though there is only one such sheet and we're about to go
    217 // away.  This situation arises easily with sheet preloading.
    218 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(StyleSheet, LastRelease())
    219 
    220 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(StyleSheet)
    221 
    222 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StyleSheet)
    223  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMedia)
    224  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleList)
    225  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelevantGlobal)
    226  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConstructorDocument)
    227  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReplacePromise)
    228  tmp->TraverseInner(cb);
    229 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    230 
    231 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StyleSheet)
    232  tmp->DropMedia();
    233  tmp->UnlinkInner();
    234  tmp->DropRuleList();
    235  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelevantGlobal)
    236  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConstructorDocument)
    237  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReplacePromise)
    238  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    239 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    240 
    241 dom::CSSStyleSheetParsingMode StyleSheet::ParsingModeDOM() {
    242 #define CHECK_MODE(X, Y)                            \
    243  static_assert(                                    \
    244      static_cast<int>(X) == static_cast<int>(Y),   \
    245      "mozilla::dom::CSSStyleSheetParsingMode and " \
    246      "mozilla::css::SheetParsingMode should have identical values");
    247 
    248  CHECK_MODE(dom::CSSStyleSheetParsingMode::Agent, css::eAgentSheetFeatures);
    249  CHECK_MODE(dom::CSSStyleSheetParsingMode::User, css::eUserSheetFeatures);
    250  CHECK_MODE(dom::CSSStyleSheetParsingMode::Author, css::eAuthorSheetFeatures);
    251 
    252 #undef CHECK_MODE
    253 
    254  return static_cast<dom::CSSStyleSheetParsingMode>(mParsingMode);
    255 }
    256 
    257 void StyleSheet::SetComplete() {
    258  // HasForcedUniqueInner() is okay if the sheet is constructed, because
    259  // constructed sheets are always unique and they may be set to complete
    260  // multiple times if their rules are replaced via Replace()
    261  MOZ_ASSERT(IsConstructed() || !HasForcedUniqueInner(),
    262             "Can't complete a sheet that's already been forced unique.");
    263  MOZ_ASSERT(!IsComplete(), "Already complete?");
    264  mState |= State::Complete;
    265 
    266  UpdateRelevantGlobal();
    267 
    268  if (!Disabled()) {
    269    ApplicableStateChanged(true);
    270  }
    271  MaybeResolveReplacePromise();
    272 }
    273 
    274 void StyleSheet::ApplicableStateChanged(bool aApplicable) {
    275  MOZ_ASSERT(aApplicable == IsApplicable());
    276  Document* docToPostEvent = nullptr;
    277  auto Notify = [&](DocumentOrShadowRoot& target) {
    278    nsINode& node = target.AsNode();
    279    if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {
    280      shadow->StyleSheetApplicableStateChanged(*this);
    281      MOZ_ASSERT(!docToPostEvent || !shadow->IsInComposedDoc() ||
    282                 docToPostEvent == shadow->GetComposedDoc());
    283      if (!docToPostEvent) {
    284        docToPostEvent = shadow->GetComposedDoc();
    285      }
    286    } else {
    287      Document* doc = node.AsDocument();
    288      MOZ_ASSERT(!docToPostEvent || docToPostEvent == doc);
    289      doc->StyleSheetApplicableStateChanged(*this);
    290      docToPostEvent = doc;
    291    }
    292  };
    293 
    294  const StyleSheet& sheet = OutermostSheet();
    295  if (sheet.mDocumentOrShadowRoot) {
    296    Notify(*sheet.mDocumentOrShadowRoot);
    297  }
    298 
    299  if (sheet.mConstructorDocument) {
    300    Notify(*sheet.mConstructorDocument);
    301  }
    302 
    303  for (DocumentOrShadowRoot* adopter : sheet.mAdopters) {
    304    MOZ_ASSERT(adopter, "adopters should never be null");
    305    if (adopter != sheet.mConstructorDocument) {
    306      Notify(*adopter);
    307    }
    308  }
    309 
    310  if (docToPostEvent) {
    311    docToPostEvent->PostStyleSheetApplicableStateChangeEvent(*this);
    312  }
    313 }
    314 
    315 void StyleSheet::SetDisabled(bool aDisabled) {
    316  if (IsReadOnly()) {
    317    return;
    318  }
    319 
    320  if (aDisabled == Disabled()) {
    321    return;
    322  }
    323 
    324  if (aDisabled) {
    325    mState |= State::Disabled;
    326  } else {
    327    mState &= ~State::Disabled;
    328  }
    329 
    330  if (IsComplete()) {
    331    ApplicableStateChanged(!aDisabled);
    332  }
    333 }
    334 
    335 nsISupports* StyleSheet::GetRelevantGlobal() const {
    336  const StyleSheet& outer = OutermostSheet();
    337  return outer.mRelevantGlobal;
    338 }
    339 
    340 StyleSheetInfo::StyleSheetInfo(CORSMode aCORSMode,
    341                               const SRIMetadata& aIntegrity,
    342                               css::SheetParsingMode aParsingMode)
    343    : mCORSMode(aCORSMode),
    344      mIntegrity(aIntegrity),
    345      mContents(Servo_StyleSheet_Empty(aParsingMode).Consume()) {
    346  MOZ_COUNT_CTOR(StyleSheetInfo);
    347 }
    348 
    349 StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy, StyleSheet* aPrimarySheet)
    350    : mCORSMode(aCopy.mCORSMode),
    351      mIntegrity(aCopy.mIntegrity),
    352      // We don't rebuild the child because we're making a copy without
    353      // children.
    354      mSourceMapURL(aCopy.mSourceMapURL),
    355      mContents(Servo_StyleSheet_Clone(aCopy.mContents.get(),
    356                                       aPrimarySheet->URLData())
    357                    .Consume())
    358 #ifdef DEBUG
    359      ,
    360      mPrincipalSet(aCopy.mPrincipalSet)
    361 #endif
    362 {
    363  AddSheet(aPrimarySheet);
    364 
    365  // Our child list is fixed up by our parent.
    366  MOZ_COUNT_CTOR(StyleSheetInfo);
    367 }
    368 
    369 StyleSheetInfo::~StyleSheetInfo() { MOZ_COUNT_DTOR(StyleSheetInfo); }
    370 
    371 StyleSheetInfo* StyleSheetInfo::CloneFor(StyleSheet* aPrimarySheet) {
    372  return new StyleSheetInfo(*this, aPrimarySheet);
    373 }
    374 
    375 MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSheetMallocSizeOf)
    376 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSheetMallocEnclosingSizeOf)
    377 
    378 size_t StyleSheetInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    379  size_t n = aMallocSizeOf(this);
    380 
    381  n += Servo_StyleSheet_SizeOfIncludingThis(
    382      ServoStyleSheetMallocSizeOf, ServoStyleSheetMallocEnclosingSizeOf,
    383      mContents);
    384 
    385  return n;
    386 }
    387 
    388 void StyleSheetInfo::AddSheet(StyleSheet* aSheet) {
    389  mSheets.AppendElement(aSheet);
    390 }
    391 
    392 void StyleSheetInfo::RemoveSheet(StyleSheet* aSheet) {
    393  // Fix up the parent pointer in children lists.
    394  StyleSheet* newParent =
    395      aSheet == mSheets[0] ? mSheets.SafeElementAt(1) : mSheets[0];
    396  for (StyleSheet* child : mChildren) {
    397    MOZ_ASSERT(child->mParentSheet);
    398    MOZ_ASSERT(child->mParentSheet->mInner == this);
    399    if (child->mParentSheet == aSheet) {
    400      child->mParentSheet = newParent;
    401    }
    402  }
    403 
    404  if (1 == mSheets.Length()) {
    405    NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent");
    406    delete this;
    407    return;
    408  }
    409 
    410  mSheets.UnorderedRemoveElement(aSheet);
    411 }
    412 
    413 void StyleSheet::GetType(nsAString& aType) { aType.AssignLiteral("text/css"); }
    414 
    415 void StyleSheet::GetHref(nsAString& aHref, ErrorResult& aRv) {
    416  if (nsIURI* sheetURI = mOriginalSheetURI) {
    417    nsAutoCString str;
    418    nsresult rv = sheetURI->GetSpec(str);
    419    if (NS_FAILED(rv)) {
    420      aRv.Throw(rv);
    421      return;
    422    }
    423    CopyUTF8toUTF16(str, aHref);
    424  } else {
    425    SetDOMStringToNull(aHref);
    426  }
    427 }
    428 
    429 void StyleSheet::GetTitle(nsAString& aTitle) {
    430  // From https://drafts.csswg.org/cssom/#dom-stylesheet-title:
    431  //
    432  //    The title attribute must return the title or null if title is the empty
    433  //    string.
    434  //
    435  if (!mTitle.IsEmpty()) {
    436    aTitle.Assign(mTitle);
    437  } else {
    438    SetDOMStringToNull(aTitle);
    439  }
    440 }
    441 
    442 void StyleSheet::WillDirty() {
    443  MOZ_ASSERT(!IsReadOnly());
    444 
    445  if (IsComplete()) {
    446    EnsureUniqueInner();
    447  }
    448 }
    449 
    450 void StyleSheet::AddStyleSet(ServoStyleSet* aStyleSet) {
    451  MOZ_DIAGNOSTIC_ASSERT(!mStyleSets.Contains(aStyleSet),
    452                        "style set already registered");
    453  mStyleSets.AppendElement(aStyleSet);
    454 }
    455 
    456 void StyleSheet::DropStyleSet(ServoStyleSet* aStyleSet) {
    457  bool found = mStyleSets.UnorderedRemoveElement(aStyleSet);
    458  MOZ_DIAGNOSTIC_ASSERT(found, "didn't find style set");
    459 #ifndef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    460  (void)found;
    461 #endif
    462 }
    463 
    464 // NOTE(emilio): Composed doc and containing shadow root are set in child sheets
    465 // too, so no need to do it for each ancestor.
    466 #define NOTIFY(function_, args_)                                          \
    467  do {                                                                    \
    468    StyleSheet* current = this;                                           \
    469    do {                                                                  \
    470      for (ServoStyleSet* set : current->mStyleSets) {                    \
    471        set->function_ args_;                                             \
    472      }                                                                   \
    473      if (auto* docOrShadow = current->mDocumentOrShadowRoot) {           \
    474        if (auto* shadow = ShadowRoot::FromNode(docOrShadow->AsNode())) { \
    475          shadow->function_ args_;                                        \
    476        } else {                                                          \
    477          docOrShadow->AsNode().AsDocument()->function_ args_;            \
    478        }                                                                 \
    479      }                                                                   \
    480      for (auto* adopter : mAdopters) {                                   \
    481        if (auto* shadow = ShadowRoot::FromNode(adopter->AsNode())) {     \
    482          shadow->function_ args_;                                        \
    483        } else {                                                          \
    484          adopter->AsNode().AsDocument()->function_ args_;                \
    485        }                                                                 \
    486      }                                                                   \
    487      current = current->mParentSheet;                                    \
    488    } while (current);                                                    \
    489  } while (0)
    490 
    491 void StyleSheet::EnsureUniqueInner() {
    492  MOZ_ASSERT(mInner->mSheets.Length() != 0, "unexpected number of outers");
    493 
    494  if (IsReadOnly()) {
    495    // Sheets that can't be modified don't need a unique inner.
    496    return;
    497  }
    498 
    499  mState |= State::ForcedUniqueInner;
    500 
    501  if (HasUniqueInner()) {
    502    // already unique
    503    return;
    504  }
    505 
    506  StyleSheetInfo* clone = mInner->CloneFor(this);
    507  MOZ_ASSERT(clone);
    508 
    509  mInner->RemoveSheet(this);
    510  mInner = clone;
    511 
    512  // Fixup the child lists and parent links in the Servo sheet. This is done
    513  // here instead of in StyleSheetInner::CloneFor, because it's just more
    514  // convenient to do so instead.
    515  FixUpAfterInnerClone();
    516 
    517  // let our containing style sets know that if we call
    518  // nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the
    519  // document
    520  NOTIFY(SheetCloned, (*this));
    521 }
    522 
    523 // WebIDL CSSStyleSheet API
    524 
    525 dom::CSSRuleList* StyleSheet::GetCssRules(nsIPrincipal& aSubjectPrincipal,
    526                                          ErrorResult& aRv) {
    527  if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
    528    return nullptr;
    529  }
    530  return GetCssRulesInternal();
    531 }
    532 
    533 void StyleSheet::GetSourceMapURL(nsACString& aSourceMapURL) {
    534  if (!mInner->mSourceMapURL.IsEmpty()) {
    535    aSourceMapURL = mInner->mSourceMapURL;
    536    return;
    537  }
    538  Servo_StyleSheet_GetSourceMapURL(mInner->mContents, &aSourceMapURL);
    539 }
    540 
    541 void StyleSheet::SetSourceMapURL(nsCString&& aSourceMapURL) {
    542  mInner->mSourceMapURL = std::move(aSourceMapURL);
    543 }
    544 
    545 void StyleSheet::GetSourceURL(nsACString& aSourceURL) {
    546  Servo_StyleSheet_GetSourceURL(mInner->mContents, &aSourceURL);
    547 }
    548 
    549 css::Rule* StyleSheet::GetDOMOwnerRule() const { return GetOwnerRule(); }
    550 
    551 // https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule
    552 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-insertrule
    553 uint32_t StyleSheet::InsertRule(const nsACString& aRule, uint32_t aIndex,
    554                                nsIPrincipal& aSubjectPrincipal,
    555                                ErrorResult& aRv) {
    556  if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
    557    return 0;
    558  }
    559 
    560  if (ModificationDisallowed()) {
    561    aRv.ThrowNotAllowedError(
    562        "This method can only be called on "
    563        "modifiable style sheets");
    564    return 0;
    565  }
    566 
    567  return InsertRuleInternal(aRule, aIndex, aRv);
    568 }
    569 
    570 // https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule
    571 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-deleterule
    572 void StyleSheet::DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
    573                            ErrorResult& aRv) {
    574  if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
    575    return;
    576  }
    577 
    578  if (ModificationDisallowed()) {
    579    return aRv.ThrowNotAllowedError(
    580        "This method can only be called on "
    581        "modifiable style sheets");
    582  }
    583 
    584  return DeleteRuleInternal(aIndex, aRv);
    585 }
    586 
    587 int32_t StyleSheet::AddRule(const nsACString& aSelector,
    588                            const nsACString& aBlock,
    589                            const Optional<uint32_t>& aIndex,
    590                            nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
    591  if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
    592    return -1;
    593  }
    594 
    595  nsAutoCString rule;
    596  rule.Append(aSelector);
    597  rule.AppendLiteral(" { ");
    598  if (!aBlock.IsEmpty()) {
    599    rule.Append(aBlock);
    600    rule.Append(' ');
    601  }
    602  rule.Append('}');
    603 
    604  auto index =
    605      aIndex.WasPassed() ? aIndex.Value() : GetCssRulesInternal()->Length();
    606 
    607  InsertRuleInternal(rule, index, aRv);
    608  // Always return -1.
    609  return -1;
    610 }
    611 
    612 void StyleSheet::MaybeResolveReplacePromise() {
    613  MOZ_ASSERT(!!mReplacePromise == ModificationDisallowed());
    614  if (!mReplacePromise) {
    615    return;
    616  }
    617 
    618  SetModificationDisallowed(false);
    619  mReplacePromise->MaybeResolve(this);
    620  mReplacePromise = nullptr;
    621 }
    622 
    623 void StyleSheet::MaybeRejectReplacePromise() {
    624  MOZ_ASSERT(!!mReplacePromise == ModificationDisallowed());
    625  if (!mReplacePromise) {
    626    return;
    627  }
    628 
    629  SetModificationDisallowed(false);
    630  mReplacePromise->MaybeRejectWithNetworkError(
    631      "@import style sheet load failed");
    632  mReplacePromise = nullptr;
    633 }
    634 
    635 // https://drafts.csswg.org/cssom/#dom-cssstylesheet-replace
    636 already_AddRefed<dom::Promise> StyleSheet::Replace(const nsACString& aText,
    637                                                   ErrorResult& aRv) {
    638  nsIGlobalObject* globalObject = nullptr;
    639  const StyleSheet& outer = OutermostSheet();
    640  if (outer.mRelevantGlobal) {
    641    globalObject = outer.mRelevantGlobal;
    642  } else if (Document* doc = outer.GetAssociatedDocument()) {
    643    globalObject = doc->GetScopeObject();
    644  }
    645 
    646  RefPtr<dom::Promise> promise = dom::Promise::Create(globalObject, aRv);
    647  if (!promise) {
    648    return nullptr;
    649  }
    650 
    651  // Step 1 and 4 are variable declarations
    652 
    653  // 2.1 Check if sheet is constructed, else reject promise.
    654  if (!IsConstructed()) {
    655    promise->MaybeRejectWithNotAllowedError(
    656        "This method can only be called on "
    657        "constructed style sheets");
    658    return promise.forget();
    659  }
    660 
    661  // 2.2 Check if sheet is modifiable, else throw.
    662  if (ModificationDisallowed()) {
    663    promise->MaybeRejectWithNotAllowedError(
    664        "This method can only be called on "
    665        "modifiable style sheets");
    666    return promise.forget();
    667  }
    668 
    669  // 3. Disallow modifications until finished.
    670  SetModificationDisallowed(true);
    671 
    672  // TODO(emilio, 1642227): Should constructable stylesheets notify global
    673  // observers (i.e., set mMustNotify to true)?
    674  css::Loader& loader = mConstructorDocument->EnsureCSSLoader();
    675  auto loadData = MakeRefPtr<css::SheetLoadData>(
    676      &loader, /* aURI = */ nullptr, this, css::SyncLoad::No,
    677      css::Loader::UseSystemPrincipal::No, css::StylePreloadKind::None,
    678      /* aPreloadEncoding */ nullptr, /* aObserver */ nullptr,
    679      mConstructorDocument->NodePrincipal(), GetReferrerInfo(),
    680      /* aNonce */ u""_ns, FetchPriority::Auto, nullptr);
    681 
    682  // In parallel
    683  // 5.1 Parse aText into rules.
    684  // 5.2 Load import rules, throw NetworkError if failed.
    685  // 5.3 Set sheet's rules to new rules.
    686  nsISerialEventTarget* target = GetMainThreadSerialEventTarget();
    687  loadData->mIsBeingParsed = true;
    688  MOZ_ASSERT(!mReplacePromise);
    689  mReplacePromise = promise;
    690  auto holder = MakeRefPtr<css::SheetLoadDataHolder>(__func__, loadData, false);
    691  ParseSheet(loader, aText, holder)
    692      ->Then(
    693          target, __func__,
    694          [loadData] { loadData->SheetFinishedParsingAsync(); },
    695          [] { MOZ_CRASH("This MozPromise should never be rejected."); });
    696 
    697  // 6. Return the promise
    698  return promise.forget();
    699 }
    700 
    701 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync
    702 void StyleSheet::ReplaceSync(const nsACString& aText, ErrorResult& aRv) {
    703  // Step 1 is a variable declaration
    704 
    705  // 2.1 Check if sheet is constructed, else throw.
    706  if (!IsConstructed()) {
    707    return aRv.ThrowNotAllowedError(
    708        "Can only be called on constructed style sheets");
    709  }
    710 
    711  // 2.2 Check if sheet is modifiable, else throw.
    712  if (ModificationDisallowed()) {
    713    return aRv.ThrowNotAllowedError(
    714        "Can only be called on modifiable style sheets");
    715  }
    716 
    717  // 3. Parse aText into rules.
    718  // 4. If rules contain @imports, skip them and continue parsing.
    719  RefPtr<const StyleStylesheetContents> rawContent =
    720      Servo_StyleSheet_FromUTF8Bytes(
    721          &mConstructorDocument->EnsureCSSLoader(), this,
    722          /* load_data = */ nullptr, &aText, mParsingMode, mURLData,
    723          mConstructorDocument->GetCompatibilityMode(),
    724          /* reusable_sheets = */ nullptr, StyleAllowImportRules::No,
    725          StyleSanitizationKind::None,
    726          /* sanitized_output = */ nullptr)
    727          .Consume();
    728 
    729  // 5. Set sheet's rules to the new rules.
    730  Inner().mContents = std::move(rawContent);
    731  PropagateUseCountersTo(mConstructorDocument);
    732  FixUpRuleListAfterContentsChangeIfNeeded();
    733  RuleChanged(nullptr, StyleRuleChangeKind::Generic);
    734 }
    735 
    736 nsresult StyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup,
    737                                         uint32_t aIndex) {
    738  NS_ENSURE_ARG_POINTER(aGroup);
    739  NS_ASSERTION(IsComplete(), "No deleting from an incomplete sheet!");
    740  RefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
    741  NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
    742 
    743  // check that the rule actually belongs to this sheet!
    744  if (this != rule->GetStyleSheet()) {
    745    return NS_ERROR_INVALID_ARG;
    746  }
    747 
    748  if (IsReadOnly()) {
    749    return NS_OK;
    750  }
    751 
    752  WillDirty();
    753 
    754  nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
    755  NS_ENSURE_SUCCESS(result, result);
    756 
    757  rule->DropReferences();
    758 
    759  RuleRemoved(*rule);
    760  return NS_OK;
    761 }
    762 
    763 void StyleSheet::RuleAdded(css::Rule& aRule) {
    764  SetModifiedRules();
    765  NOTIFY(RuleAdded, (*this, aRule));
    766 }
    767 
    768 void StyleSheet::RuleRemoved(css::Rule& aRule) {
    769  SetModifiedRules();
    770  NOTIFY(RuleRemoved, (*this, aRule));
    771 }
    772 
    773 void StyleSheet::RuleChanged(css::Rule* aRule, const StyleRuleChange& aChange) {
    774  MOZ_ASSERT(!aRule || HasUniqueInner(),
    775             "Shouldn't have mutated a shared sheet");
    776  SetModifiedRules();
    777  NOTIFY(RuleChanged, (*this, aRule, aChange));
    778 }
    779 
    780 // nsICSSLoaderObserver implementation
    781 NS_IMETHODIMP
    782 StyleSheet::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
    783                             nsresult aStatus) {
    784  if (!aSheet->GetParentSheet()) {
    785    return NS_OK;  // ignore if sheet has been detached already
    786  }
    787  MOZ_DIAGNOSTIC_ASSERT(this == aSheet->GetParentSheet(),
    788                        "We are being notified of a sheet load for a sheet "
    789                        "that is not our child!");
    790  if (NS_FAILED(aStatus)) {
    791    return NS_OK;
    792  }
    793  // The assert below should hold if we stop triggering import loads for invalid
    794  // insertRule() calls, see bug 1914106.
    795  // MOZ_ASSERT(aSheet->GetOwnerRule());
    796  if (!aSheet->GetOwnerRule()) {
    797    return NS_OK;
    798  }
    799  NOTIFY(ImportRuleLoaded, (*aSheet));
    800  return NS_OK;
    801 }
    802 
    803 #undef NOTIFY
    804 
    805 nsresult StyleSheet::InsertRuleIntoGroup(const nsACString& aRule,
    806                                         css::GroupRule* aGroup,
    807                                         uint32_t aIndex) {
    808  NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!");
    809  // check that the group actually belongs to this sheet!
    810  if (this != aGroup->GetStyleSheet()) {
    811    return NS_ERROR_INVALID_ARG;
    812  }
    813 
    814  if (IsReadOnly()) {
    815    return NS_OK;
    816  }
    817 
    818  if (ModificationDisallowed()) {
    819    return NS_ERROR_DOM_NOT_ALLOWED_ERR;
    820  }
    821 
    822  WillDirty();
    823 
    824  nsresult result = InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
    825  NS_ENSURE_SUCCESS(result, result);
    826  RuleAdded(*aGroup->GetStyleRuleAt(aIndex));
    827  return NS_OK;
    828 }
    829 
    830 uint64_t StyleSheet::FindOwningWindowInnerID() const {
    831  uint64_t windowID = 0;
    832  if (Document* doc = GetAssociatedDocument()) {
    833    windowID = doc->InnerWindowID();
    834  }
    835 
    836  if (windowID == 0 && mOwningNode) {
    837    windowID = mOwningNode->OwnerDoc()->InnerWindowID();
    838  }
    839 
    840  RefPtr<css::Rule> ownerRule;
    841  if (windowID == 0 && (ownerRule = GetDOMOwnerRule())) {
    842    RefPtr<StyleSheet> sheet = ownerRule->GetStyleSheet();
    843    if (sheet) {
    844      windowID = sheet->FindOwningWindowInnerID();
    845    }
    846  }
    847 
    848  if (windowID == 0 && mParentSheet) {
    849    windowID = mParentSheet->FindOwningWindowInnerID();
    850  }
    851 
    852  return windowID;
    853 }
    854 
    855 void StyleSheet::RemoveFromParent() {
    856  if (!mParentSheet) {
    857    return;
    858  }
    859 
    860  MOZ_ASSERT(mParentSheet->ChildSheets().Contains(this));
    861  mParentSheet->Inner().mChildren.RemoveElement(this);
    862  mParentSheet = nullptr;
    863 }
    864 
    865 void StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
    866                                               ErrorResult& aRv) {
    867  if (aSubjectPrincipal.Subsumes(Principal())) {
    868    return;
    869  }
    870 
    871  // Allow access only if CORS mode is not NONE and the security flag
    872  // is not turned off.
    873  if (GetCORSMode() == CORS_NONE && !nsContentUtils::BypassCSSOMOriginCheck()) {
    874    aRv.ThrowSecurityError("Not allowed to access cross-origin stylesheet");
    875    return;
    876  }
    877 
    878  // Make sure we're complete.
    879  if (!IsComplete()) {
    880    aRv.ThrowInvalidAccessError(
    881        "Not allowed to access still-loading stylesheet");
    882    return;
    883  }
    884 }
    885 
    886 bool StyleSheet::IsDirectlyAssociatedTo(
    887    dom::DocumentOrShadowRoot& aTree) const {
    888  if (mParentSheet) {
    889    // @import is never directly associated to a tree.
    890    MOZ_ASSERT(aTree.StyleOrderIndexOfSheet(*this) ==
    891               nsTArray<RefPtr<StyleSheet>>::NoIndex);
    892    return false;
    893  }
    894  bool associated = false;
    895  if (IsConstructed()) {
    896    // Idea is that the adopted stylesheet list is likely to be smaller than
    897    // list of adopters of a single sheet, but we could reverse the check if
    898    // needed.
    899    associated = aTree.AdoptedStyleSheets().Contains(this);
    900    MOZ_ASSERT(associated == mAdopters.Contains(&aTree));
    901  } else {
    902    associated = GetAssociatedDocumentOrShadowRoot() == &aTree;
    903  }
    904  MOZ_ASSERT(associated == (aTree.StyleOrderIndexOfSheet(*this) !=
    905                            nsTArray<RefPtr<StyleSheet>>::NoIndex));
    906  return associated;
    907 }
    908 
    909 bool StyleSheet::AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
    910                                   ErrorResult& aRv) {
    911  // Rules are not available on incomplete sheets.
    912  if (!IsComplete()) {
    913    aRv.ThrowInvalidAccessError(
    914        "Can't access rules of still-loading style sheet");
    915    return false;
    916  }
    917  if (aSubjectPrincipal.IsSystemPrincipal()) {
    918    // System principal should always allow access to rules. Devtools needs this
    919    // for example.
    920    return true;
    921  }
    922  if (aSubjectPrincipal.GetIsAddonOrExpandedAddonPrincipal() &&
    923      aSubjectPrincipal.Subsumes(URLData()->Principal())) {
    924    // Extensions should be able to access their own stylesheets even if they're
    925    // not origin-clean.
    926    return true;
    927  }
    928  if (!Inner().mOriginClean && !nsContentUtils::BypassCSSOMOriginCheck()) {
    929    aRv.ThrowSecurityError("Not allowed to access cross-origin stylesheet");
    930    return false;
    931  }
    932  return true;
    933 }
    934 
    935 void StyleSheet::SetAssociatedDocumentOrShadowRoot(
    936    DocumentOrShadowRoot* aDocOrShadowRoot) {
    937  MOZ_ASSERT(!IsConstructed());
    938  MOZ_ASSERT(!mParentSheet || !aDocOrShadowRoot,
    939             "Shouldn't be set on child sheets");
    940 
    941  // not ref counted
    942  mDocumentOrShadowRoot = aDocOrShadowRoot;
    943  UpdateRelevantGlobal();
    944 }
    945 
    946 void StyleSheet::AppendStyleSheet(StyleSheet& aSheet) {
    947  WillDirty();
    948  AppendStyleSheetSilently(aSheet);
    949 }
    950 
    951 void StyleSheet::AppendStyleSheetSilently(StyleSheet& aSheet) {
    952  MOZ_ASSERT(!IsReadOnly());
    953 
    954  Inner().mChildren.AppendElement(&aSheet);
    955 
    956  // This is not reference counted. Our parent tells us when
    957  // it's going away.
    958  aSheet.mParentSheet = this;
    959 }
    960 
    961 size_t StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    962  size_t n = 0;
    963  n += aMallocSizeOf(this);
    964 
    965  // We want to measure the inner with only one of the children, and it makes
    966  // sense for it to be the latest as it is the most likely to be reachable.
    967  if (Inner().mSheets.LastElement() == this) {
    968    n += Inner().SizeOfIncludingThis(aMallocSizeOf);
    969  }
    970 
    971  // Measurement of the following members may be added later if DMD finds it
    972  // is worthwhile:
    973  // - mTitle
    974  // - mMedia
    975  // - mStyleSets
    976  // - mRuleList
    977 
    978  return n;
    979 }
    980 
    981 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
    982 void StyleSheet::List(FILE* aOut, int32_t aIndent) {
    983  for (StyleSheet* child : ChildSheets()) {
    984    child->List(aOut, aIndent);
    985  }
    986 
    987  nsCString line;
    988  for (int i = 0; i < aIndent; ++i) {
    989    line.AppendLiteral("  ");
    990  }
    991 
    992  line.AppendLiteral("/* ");
    993 
    994  nsCString url;
    995  if (auto* uri = GetOriginalURI()) {
    996    uri->GetSpec(url);
    997  }
    998  if (url.IsEmpty()) {
    999    line.AppendLiteral("(no URL)");
   1000  } else {
   1001    line.Append(url);
   1002  }
   1003 
   1004  line.AppendLiteral(" (");
   1005 
   1006  switch (GetOrigin()) {
   1007    case StyleOrigin::UserAgent:
   1008      line.AppendLiteral("User Agent");
   1009      break;
   1010    case StyleOrigin::User:
   1011      line.AppendLiteral("User");
   1012      break;
   1013    case StyleOrigin::Author:
   1014      line.AppendLiteral("Author");
   1015      break;
   1016  }
   1017 
   1018  if (mMedia) {
   1019    nsAutoCString buffer;
   1020    mMedia->GetText(buffer);
   1021 
   1022    if (!buffer.IsEmpty()) {
   1023      line.AppendLiteral(", ");
   1024      line.Append(buffer);
   1025    }
   1026  }
   1027 
   1028  line.AppendLiteral(") */");
   1029 
   1030  fprintf_stderr(aOut, "%s\n\n", line.get());
   1031 
   1032  nsCString newlineIndent;
   1033  newlineIndent.Append('\n');
   1034  for (int i = 0; i < aIndent; ++i) {
   1035    newlineIndent.AppendLiteral("  ");
   1036  }
   1037 
   1038  ServoCSSRuleList* ruleList = GetCssRulesInternal();
   1039  for (uint32_t i = 0, len = ruleList->Length(); i < len; ++i) {
   1040    css::Rule* rule = ruleList->GetRule(i);
   1041 
   1042    nsAutoCString cssText;
   1043    rule->GetCssText(cssText);
   1044    cssText.ReplaceSubstring("\n"_ns, newlineIndent);
   1045    fprintf_stderr(aOut, "%s\n", cssText.get());
   1046  }
   1047 
   1048  if (ruleList->Length() != 0) {
   1049    fprintf_stderr(aOut, "\n");
   1050  }
   1051 }
   1052 #endif
   1053 
   1054 void StyleSheet::SetMedia(already_AddRefed<dom::MediaList> aMedia) {
   1055  mMedia = aMedia;
   1056  if (mMedia) {
   1057    mMedia->SetStyleSheet(this);
   1058  }
   1059 }
   1060 
   1061 void StyleSheet::DropMedia() {
   1062  if (mMedia) {
   1063    mMedia->SetStyleSheet(nullptr);
   1064    mMedia = nullptr;
   1065  }
   1066 }
   1067 
   1068 dom::MediaList* StyleSheet::Media() {
   1069  if (!mMedia) {
   1070    mMedia = dom::MediaList::Create(EmptyCString());
   1071    mMedia->SetStyleSheet(this);
   1072  }
   1073 
   1074  return mMedia;
   1075 }
   1076 
   1077 // nsWrapperCache
   1078 
   1079 JSObject* StyleSheet::WrapObject(JSContext* aCx,
   1080                                 JS::Handle<JSObject*> aGivenProto) {
   1081  return dom::CSSStyleSheet_Binding::Wrap(aCx, this, aGivenProto);
   1082 }
   1083 
   1084 void StyleSheet::FixUpRuleListAfterContentsChangeIfNeeded(bool aFromClone) {
   1085  if (!mRuleList) {
   1086    return;
   1087  }
   1088 
   1089  RefPtr<StyleLockedCssRules> rules =
   1090      Servo_StyleSheet_GetRules(Inner().mContents.get()).Consume();
   1091  mRuleList->SetRawContents(std::move(rules), aFromClone);
   1092 }
   1093 
   1094 void StyleSheet::FixUpAfterInnerClone() {
   1095  MOZ_ASSERT(Inner().mSheets.Length() == 1, "Should've just cloned");
   1096  MOZ_ASSERT(Inner().mSheets[0] == this);
   1097  MOZ_ASSERT(Inner().mChildren.IsEmpty());
   1098 
   1099  FixUpRuleListAfterContentsChangeIfNeeded(/* aFromClone = */ true);
   1100 
   1101  RefPtr<StyleLockedCssRules> rules =
   1102      Servo_StyleSheet_GetRules(Inner().mContents.get()).Consume();
   1103  size_t len = Servo_CssRules_GetRuleCount(rules.get());
   1104  bool reachedBody = false;
   1105  for (size_t i = 0; i < len; ++i) {
   1106    switch (Servo_CssRules_GetRuleTypeAt(rules, i)) {
   1107      case StyleCssRuleType::Import: {
   1108        MOZ_ASSERT(!reachedBody);
   1109        uint32_t line, column;  // Actually unused.
   1110        RefPtr<StyleLockedImportRule> import =
   1111            Servo_CssRules_GetImportRuleAt(rules, i, &line, &column).Consume();
   1112        MOZ_ASSERT(import);
   1113        if (auto* sheet =
   1114                const_cast<StyleSheet*>(Servo_ImportRule_GetSheet(import))) {
   1115          AppendStyleSheetSilently(*sheet);
   1116        }
   1117        break;
   1118      }
   1119      case StyleCssRuleType::LayerStatement:
   1120        break;
   1121      default:
   1122        // Note that only @charset and @layer statements can come before
   1123        // @import. @charset rules are parsed but skipped, so we can stop
   1124        // iterating as soon as we find the stylesheet body.
   1125        reachedBody = true;
   1126        break;
   1127    }
   1128 #ifndef DEBUG
   1129    // Keep iterating in debug builds so that we can assert that we really have
   1130    // no more @import rules.
   1131    if (reachedBody) {
   1132      break;
   1133    }
   1134 #endif
   1135  }
   1136 }
   1137 
   1138 /* static */
   1139 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-cssstylesheet
   1140 already_AddRefed<StyleSheet> StyleSheet::CreateConstructedSheet(
   1141    dom::Document& aConstructorDocument, nsIURI* aBaseURI,
   1142    const dom::CSSStyleSheetInit& aOptions, ErrorResult& aRv) {
   1143  // 1. Construct a sheet and set its properties (see spec).
   1144  auto sheet =
   1145      MakeRefPtr<StyleSheet>(css::SheetParsingMode::eAuthorSheetFeatures,
   1146                             CORSMode::CORS_NONE, dom::SRIMetadata());
   1147 
   1148  // baseURL not yet in the spec. Implemented based on the following discussion:
   1149  // https://github.com/WICG/construct-stylesheets/issues/95#issuecomment-594217180
   1150  RefPtr<nsIURI> baseURI;
   1151  if (!aOptions.mBaseURL.WasPassed()) {
   1152    baseURI = aBaseURI;
   1153  } else {
   1154    nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aOptions.mBaseURL.Value(),
   1155                            nullptr, aConstructorDocument.GetBaseURI());
   1156    if (NS_FAILED(rv)) {
   1157      aRv.ThrowNotAllowedError(
   1158          "Constructed style sheets must have a valid base URL");
   1159      return nullptr;
   1160    }
   1161  }
   1162 
   1163  auto referrerInfo = MakeRefPtr<ReferrerInfo>(aConstructorDocument);
   1164  sheet->SetURIs(nullptr, baseURI, referrerInfo,
   1165                 aConstructorDocument.NodePrincipal());
   1166  sheet->mConstructorDocument = &aConstructorDocument;
   1167 
   1168  // 2. Set the sheet's media according to aOptions.
   1169  if (aOptions.mMedia.IsUTF8String()) {
   1170    sheet->SetMedia(MediaList::Create(aOptions.mMedia.GetAsUTF8String()));
   1171  } else {
   1172    sheet->SetMedia(aOptions.mMedia.GetAsMediaList()->Clone());
   1173  }
   1174 
   1175  // 3. Set the sheet's disabled flag according to aOptions.
   1176  sheet->SetDisabled(aOptions.mDisabled);
   1177  sheet->SetComplete();
   1178 
   1179  sheet->ReplaceSync(""_ns, aRv);
   1180  MOZ_ASSERT(!aRv.Failed());
   1181 
   1182  // 4. Return sheet.
   1183  return sheet.forget();
   1184 }
   1185 
   1186 already_AddRefed<StyleSheet> StyleSheet::CreateEmptyChildSheet(
   1187    already_AddRefed<dom::MediaList> aMediaList) const {
   1188  auto child =
   1189      MakeRefPtr<StyleSheet>(ParsingMode(), CORSMode::CORS_NONE, SRIMetadata());
   1190 
   1191  child->mMedia = aMediaList;
   1192  return child.forget();
   1193 }
   1194 
   1195 RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
   1196    css::Loader& aLoader, const nsACString& aBytes,
   1197    const RefPtr<css::SheetLoadDataHolder>& aLoadData) {
   1198  MOZ_ASSERT(mParsePromise.IsEmpty());
   1199  MOZ_ASSERT_IF(NS_IsMainThread(), mAsyncParseBlockers == 0);
   1200 
   1201  RefPtr<StyleSheetParsePromise> p = mParsePromise.Ensure(__func__);
   1202  if (!aLoadData->get()->ShouldDefer()) {
   1203    mParsePromise.SetTaskPriority(nsIRunnablePriority::PRIORITY_RENDER_BLOCKING,
   1204                                  __func__);
   1205  }
   1206  BlockParsePromise();
   1207  // @import rules are disallowed due to this decision:
   1208  // https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418
   1209  // We may allow @import rules again in the future.
   1210  auto allowImportRules = SelfOrAncestorIsConstructed()
   1211                              ? StyleAllowImportRules::No
   1212                              : StyleAllowImportRules::Yes;
   1213  if (aLoadData->get()->mRecordErrors) {
   1214    MOZ_ASSERT(NS_IsMainThread());
   1215    RefPtr<StyleStylesheetContents> contents =
   1216        Servo_StyleSheet_FromUTF8Bytes(
   1217            &aLoader, this, aLoadData->get(), &aBytes, mParsingMode, mURLData,
   1218            aLoadData->get()->mCompatMode,
   1219            /* reusable_sheets = */ nullptr, allowImportRules,
   1220            StyleSanitizationKind::None,
   1221            /* sanitized_output = */ nullptr)
   1222            .Consume();
   1223    FinishAsyncParse(contents.forget());
   1224  } else {
   1225    Servo_StyleSheet_FromUTF8BytesAsync(
   1226        aLoadData, mURLData, &aBytes, mParsingMode,
   1227        aLoadData->get()->mCompatMode, allowImportRules);
   1228  }
   1229 
   1230  return p;
   1231 }
   1232 
   1233 void StyleSheet::FinishAsyncParse(
   1234    already_AddRefed<StyleStylesheetContents> aSheetContents) {
   1235  MOZ_ASSERT(NS_IsMainThread());
   1236  MOZ_ASSERT(!mParsePromise.IsEmpty());
   1237  Inner().mContents = aSheetContents;
   1238  FixUpRuleListAfterContentsChangeIfNeeded();
   1239  UnblockParsePromise();
   1240 }
   1241 
   1242 StyleNonLocalUriDependency StyleSheet::OriginalContentsUriDependency() const {
   1243  const auto* counters = UseCounters();
   1244  if (Servo_IsCustomUseCounterRecorded(
   1245          counters, StyleCustomUseCounter::MaybeHasFullBaseUriDependency)) {
   1246    return StyleNonLocalUriDependency::Full;
   1247  }
   1248  if (Servo_IsCustomUseCounterRecorded(
   1249          counters, StyleCustomUseCounter::MaybeHasPathBaseUriDependency)) {
   1250    return StyleNonLocalUriDependency::Path;
   1251  }
   1252  if (Servo_IsCustomUseCounterRecorded(
   1253          counters, StyleCustomUseCounter::HasNonLocalUriDependency)) {
   1254    return StyleNonLocalUriDependency::Absolute;
   1255  }
   1256  return StyleNonLocalUriDependency::No;
   1257 }
   1258 
   1259 const StyleUseCounters* StyleSheet::UseCounters() const {
   1260  return Servo_StyleSheet_UseCounters(RawContents());
   1261 }
   1262 
   1263 void StyleSheet::SetURIs(nsIURI* aOriginalSheetURI, nsIURI* aBaseURI,
   1264                         nsIReferrerInfo* aReferrerInfo,
   1265                         nsIPrincipal* aPrincipal) {
   1266  MOZ_ASSERT(aBaseURI);
   1267  MOZ_ASSERT(aPrincipal);
   1268  MOZ_ASSERT(aReferrerInfo);
   1269  mURLData = MakeAndAddRef<URLExtraData>(aBaseURI, aReferrerInfo, aPrincipal);
   1270  mOriginalSheetURI = aOriginalSheetURI;
   1271 }
   1272 
   1273 nsIURI* StyleSheet::GetBaseURI() const { return URLData()->BaseURI(); }
   1274 
   1275 nsIReferrerInfo* StyleSheet::GetReferrerInfo() const {
   1276  return URLData()->ReferrerInfo();
   1277 }
   1278 
   1279 nsIPrincipal* StyleSheet::Principal() const { return URLData()->Principal(); }
   1280 
   1281 void StyleSheet::PropagateUseCountersTo(Document* aDoc) const {
   1282  if (!aDoc || URLData()->ChromeRulesEnabled()) {
   1283    return;
   1284  }
   1285  if (const auto* counters = aDoc->GetStyleUseCounters()) {
   1286    Servo_UseCounters_Merge(counters, UseCounters());
   1287  }
   1288 }
   1289 
   1290 void StyleSheet::ParseSheetSync(
   1291    css::Loader* aLoader, const nsACString& aBytes,
   1292    css::SheetLoadData* aLoadData,
   1293    css::LoaderReusableStyleSheets* aReusableSheets) {
   1294  const nsCompatibility compatMode = [&] {
   1295    if (aLoadData) {
   1296      return aLoadData->mCompatMode;
   1297    }
   1298    if (aLoader) {
   1299      return aLoader->CompatMode(css::StylePreloadKind::None);
   1300    }
   1301    return eCompatibility_FullStandards;
   1302  }();
   1303 
   1304  auto allowImportRules = SelfOrAncestorIsConstructed()
   1305                              ? StyleAllowImportRules::No
   1306                              : StyleAllowImportRules::Yes;
   1307 
   1308  Inner().mContents =
   1309      Servo_StyleSheet_FromUTF8Bytes(
   1310          aLoader, this, aLoadData, &aBytes, mParsingMode, mURLData, compatMode,
   1311          aReusableSheets, allowImportRules, StyleSanitizationKind::None,
   1312          /* sanitized_output = */ nullptr)
   1313          .Consume();
   1314  PropagateUseCountersTo(aLoader ? aLoader->GetDocument() : nullptr);
   1315 }
   1316 
   1317 void StyleSheet::ReparseSheet(const nsACString& aInput, ErrorResult& aRv) {
   1318  if (!IsComplete()) {
   1319    return aRv.ThrowInvalidAccessError("Cannot reparse still-loading sheet");
   1320  }
   1321 
   1322  // Allowing to modify UA sheets is dangerous (in the sense that C++ code
   1323  // relies on rules in those sheets), plus they're probably going to be shared
   1324  // across processes in which case this is directly a no-go.
   1325  if (IsReadOnly()) {
   1326    return;
   1327  }
   1328 
   1329  // Hold strong ref to the CSSLoader in case the document update
   1330  // kills the document
   1331  RefPtr<css::Loader> loader;
   1332  if (Document* doc = GetAssociatedDocument()) {
   1333    loader = &doc->EnsureCSSLoader();
   1334  }
   1335  if (!loader) {
   1336    loader = new css::Loader;
   1337  }
   1338 
   1339  WillDirty();
   1340 
   1341  // cache child sheets to reuse
   1342  css::LoaderReusableStyleSheets reusableSheets;
   1343  for (StyleSheet* child : ChildSheets()) {
   1344    if (child->GetOriginalURI()) {
   1345      reusableSheets.AddReusableSheet(child);
   1346    }
   1347  }
   1348 
   1349  // Clean up child sheets list.
   1350  for (StyleSheet* child : ChildSheets()) {
   1351    child->mParentSheet = nullptr;
   1352  }
   1353  Inner().mChildren.Clear();
   1354 
   1355  // Notify to the stylesets about the old rules going away.
   1356  {
   1357    ServoCSSRuleList* ruleList = GetCssRulesInternal();
   1358    MOZ_ASSERT(ruleList);
   1359 
   1360    uint32_t ruleCount = ruleList->Length();
   1361    for (uint32_t i = 0; i < ruleCount; ++i) {
   1362      css::Rule* rule = ruleList->GetRule(i);
   1363      MOZ_ASSERT(rule);
   1364      RuleRemoved(*rule);
   1365    }
   1366 
   1367    // We need to clear the rule list here (rather than after parsing) because
   1368    // ParseSheetSync may reuse child sheets, which would cause us to end up
   1369    // with a wrong mChilden array.
   1370    ruleList->SetRawContents(nullptr, /* aFromClone = */ false);
   1371  }
   1372 
   1373  ParseSheetSync(loader, aInput, /* aLoadData = */ nullptr, &reusableSheets);
   1374 
   1375  FixUpRuleListAfterContentsChangeIfNeeded();
   1376 
   1377  // Notify the stylesets about the new rules.
   1378  {
   1379    // Get the rule list (which will need to be regenerated after ParseSheet).
   1380    ServoCSSRuleList* ruleList = GetCssRulesInternal();
   1381    MOZ_ASSERT(ruleList);
   1382 
   1383    uint32_t ruleCount = ruleList->Length();
   1384    for (uint32_t i = 0; i < ruleCount; ++i) {
   1385      css::Rule* rule = ruleList->GetRule(i);
   1386      MOZ_ASSERT(rule);
   1387      RuleAdded(*rule);
   1388    }
   1389  }
   1390 
   1391  // Our rules are no longer considered modified for devtools.
   1392  mState &= ~State::ModifiedRulesForDevtools;
   1393 }
   1394 
   1395 void StyleSheet::DropRuleList() {
   1396  if (mRuleList) {
   1397    mRuleList->DropReferences();
   1398    mRuleList = nullptr;
   1399  }
   1400 }
   1401 
   1402 already_AddRefed<StyleSheet> StyleSheet::Clone(
   1403    StyleSheet* aCloneParent,
   1404    dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot) const {
   1405  MOZ_ASSERT(!IsConstructed(),
   1406             "Cannot create a non-constructed sheet from a constructed sheet");
   1407  RefPtr<StyleSheet> clone =
   1408      new StyleSheet(*this, aCloneParent, aCloneDocumentOrShadowRoot,
   1409                     /* aConstructorDocToUse */ nullptr);
   1410  return clone.forget();
   1411 }
   1412 
   1413 already_AddRefed<StyleSheet> StyleSheet::CloneAdoptedSheet(
   1414    Document& aConstructorDocument) const {
   1415  MOZ_ASSERT(IsConstructed(),
   1416             "Cannot create a constructed sheet from a non-constructed sheet");
   1417  MOZ_ASSERT(aConstructorDocument.IsStaticDocument(),
   1418             "Should never clone adopted sheets for a non-static document");
   1419  RefPtr<StyleSheet> clone = new StyleSheet(*this,
   1420                                            /* aParentSheetToUse */ nullptr,
   1421                                            /* aDocOrShadowRootToUse */ nullptr,
   1422                                            &aConstructorDocument);
   1423  return clone.forget();
   1424 }
   1425 
   1426 ServoCSSRuleList* StyleSheet::GetCssRulesInternal() {
   1427  if (!mRuleList) {
   1428    // TODO(emilio): This should go away, but we need to fix the CC setup for
   1429    // @import rules first, see bug 1719963.
   1430    EnsureUniqueInner();
   1431 
   1432    RefPtr<StyleLockedCssRules> rawRules =
   1433        Servo_StyleSheet_GetRules(Inner().mContents).Consume();
   1434    MOZ_ASSERT(rawRules);
   1435    mRuleList = new ServoCSSRuleList(rawRules.forget(), this, nullptr);
   1436  }
   1437  return mRuleList;
   1438 }
   1439 
   1440 uint32_t StyleSheet::InsertRuleInternal(const nsACString& aRule,
   1441                                        uint32_t aIndex, ErrorResult& aRv) {
   1442  MOZ_ASSERT(!IsReadOnly());
   1443  MOZ_ASSERT(!ModificationDisallowed());
   1444 
   1445  // Ensure mRuleList is constructed.
   1446  GetCssRulesInternal();
   1447 
   1448  aRv = mRuleList->InsertRule(aRule, aIndex);
   1449  if (aRv.Failed()) {
   1450    return 0;
   1451  }
   1452 
   1453  // XXX We may not want to get the rule when stylesheet change event
   1454  // is not enabled.
   1455  css::Rule* rule = mRuleList->GetRule(aIndex);
   1456  RuleAdded(*rule);
   1457 
   1458  return aIndex;
   1459 }
   1460 
   1461 void StyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv) {
   1462  MOZ_ASSERT(!IsReadOnly());
   1463  MOZ_ASSERT(!ModificationDisallowed());
   1464 
   1465  // Ensure mRuleList is constructed.
   1466  GetCssRulesInternal();
   1467  if (aIndex >= mRuleList->Length()) {
   1468    aRv.ThrowIndexSizeError(
   1469        nsPrintfCString("Cannot delete rule at index %u"
   1470                        " because the number of rules is only %u",
   1471                        aIndex, mRuleList->Length()));
   1472    return;
   1473  }
   1474 
   1475  // Hold a strong ref to the rule so it doesn't die when we remove it
   1476  // from the list. XXX We may not want to hold it if stylesheet change
   1477  // event is not enabled.
   1478  RefPtr<css::Rule> rule = mRuleList->GetRule(aIndex);
   1479  aRv = mRuleList->DeleteRule(aIndex);
   1480  if (!aRv.Failed()) {
   1481    RuleRemoved(*rule);
   1482  }
   1483 }
   1484 
   1485 nsresult StyleSheet::InsertRuleIntoGroupInternal(const nsACString& aRule,
   1486                                                 css::GroupRule* aGroup,
   1487                                                 uint32_t aIndex) {
   1488  MOZ_ASSERT(!IsReadOnly());
   1489 
   1490  ServoCSSRuleList* rules = aGroup->CssRules();
   1491  MOZ_ASSERT(rules && rules->GetParentRule() == aGroup);
   1492  return rules->InsertRule(aRule, aIndex);
   1493 }
   1494 
   1495 StyleOrigin StyleSheet::GetOrigin() const {
   1496  return Servo_StyleSheet_GetOrigin(Inner().mContents);
   1497 }
   1498 
   1499 void StyleSheet::SetSharedContents(const StyleLockedCssRules* aSharedRules) {
   1500  MOZ_ASSERT(!IsComplete());
   1501 
   1502  Inner().mContents =
   1503      Servo_StyleSheet_FromSharedData(mURLData, aSharedRules).Consume();
   1504 }
   1505 
   1506 const StyleLockedCssRules* StyleSheet::ToShared(
   1507    StyleSharedMemoryBuilder* aBuilder, nsCString& aErrorMessage) {
   1508  // Assert some things we assume when creating a StyleSheet using shared
   1509  // memory.
   1510  MOZ_ASSERT(GetReferrerInfo()->ReferrerPolicy() == ReferrerPolicy::_empty);
   1511  MOZ_ASSERT(GetReferrerInfo()->GetSendReferrer());
   1512  MOZ_ASSERT(!nsCOMPtr<nsIURI>(GetReferrerInfo()->GetComputedReferrer()));
   1513  MOZ_ASSERT(GetCORSMode() == CORS_NONE);
   1514  MOZ_ASSERT(Inner().mIntegrity.IsEmpty());
   1515  MOZ_ASSERT(Principal()->IsSystemPrincipal());
   1516 
   1517  const StyleLockedCssRules* rules = Servo_SharedMemoryBuilder_AddStylesheet(
   1518      aBuilder, Inner().mContents, &aErrorMessage);
   1519 
   1520 #ifdef DEBUG
   1521  if (!rules) {
   1522    // Print the ToShmem error message so that developers know what to fix.
   1523    printf_stderr("%s\n", aErrorMessage.get());
   1524    MOZ_CRASH("UA style sheet contents failed shared memory requirements");
   1525  }
   1526 #endif
   1527 
   1528  return rules;
   1529 }
   1530 
   1531 bool StyleSheet::IsReadOnly() const {
   1532  return IsComplete() && GetOrigin() == StyleOrigin::UserAgent;
   1533 }
   1534 
   1535 }  // namespace mozilla
   1536 //