summaryrefslogtreecommitdiffstats
path: root/layout/style/StyleSheet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/StyleSheet.cpp')
-rw-r--r--layout/style/StyleSheet.cpp1484
1 files changed, 1484 insertions, 0 deletions
diff --git a/layout/style/StyleSheet.cpp b/layout/style/StyleSheet.cpp
new file mode 100644
index 0000000000..494618d879
--- /dev/null
+++ b/layout/style/StyleSheet.cpp
@@ -0,0 +1,1484 @@
+/* -*- 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/. */
+
+#include "mozilla/StyleSheet.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ComputedStyleInlines.h"
+#include "mozilla/css/ErrorReporter.h"
+#include "mozilla/css/GroupRule.h"
+#include "mozilla/dom/CSSImportRule.h"
+#include "mozilla/dom/CSSRuleList.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/FetchPriority.h"
+#include "mozilla/dom/MediaList.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ReferrerInfo.h"
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/dom/ShadowRootBinding.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/ServoBindings.h"
+#include "mozilla/ServoCSSRuleList.h"
+#include "mozilla/ServoStyleSet.h"
+#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/css/SheetLoadData.h"
+
+#include "mozAutoDocUpdate.h"
+#include "SheetLoadData.h"
+
+namespace mozilla {
+
+using namespace dom;
+
+StyleSheet::StyleSheet(css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
+ const dom::SRIMetadata& aIntegrity)
+ : mParentSheet(nullptr),
+ mConstructorDocument(nullptr),
+ mDocumentOrShadowRoot(nullptr),
+ mParsingMode(aParsingMode),
+ mState(static_cast<State>(0)),
+ mInner(new StyleSheetInfo(aCORSMode, aIntegrity, aParsingMode)) {
+ mInner->AddSheet(this);
+}
+
+StyleSheet::StyleSheet(const StyleSheet& aCopy, StyleSheet* aParentSheetToUse,
+ dom::DocumentOrShadowRoot* aDocOrShadowRootToUse,
+ dom::Document* aConstructorDocToUse)
+ : mParentSheet(aParentSheetToUse),
+ mConstructorDocument(aConstructorDocToUse),
+ mTitle(aCopy.mTitle),
+ mDocumentOrShadowRoot(aDocOrShadowRootToUse),
+ mParsingMode(aCopy.mParsingMode),
+ mState(aCopy.mState),
+ // Shallow copy, but concrete subclasses will fix up.
+ mInner(aCopy.mInner) {
+ MOZ_ASSERT(!aConstructorDocToUse || aCopy.IsConstructed());
+ MOZ_ASSERT(!aConstructorDocToUse || !aDocOrShadowRootToUse,
+ "Should never have both of these together.");
+ MOZ_ASSERT(mInner, "Should only copy StyleSheets with an mInner.");
+ mInner->AddSheet(this);
+ // CSSOM's been there, force full copy now.
+ if (HasForcedUniqueInner()) {
+ MOZ_ASSERT(IsComplete(),
+ "Why have rules been accessed on an incomplete sheet?");
+ EnsureUniqueInner();
+ // But CSSOM hasn't been on _this_ stylesheet yet, so no need to clone
+ // ourselves.
+ mState &= ~(State::ForcedUniqueInner | State::ModifiedRules |
+ State::ModifiedRulesForDevtools);
+ }
+
+ if (aCopy.mMedia) {
+ // XXX This is wrong; we should be keeping @import rules and
+ // sheets in sync!
+ mMedia = aCopy.mMedia->Clone();
+ }
+}
+
+/* static */
+// https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-cssstylesheet
+already_AddRefed<StyleSheet> StyleSheet::Constructor(
+ const dom::GlobalObject& aGlobal, const dom::CSSStyleSheetInit& aOptions,
+ ErrorResult& aRv) {
+ nsCOMPtr<nsPIDOMWindowInner> window =
+ do_QueryInterface(aGlobal.GetAsSupports());
+
+ if (!window) {
+ aRv.ThrowNotSupportedError("Not supported when there is no document");
+ return nullptr;
+ }
+
+ Document* constructorDocument = window->GetExtantDoc();
+ if (!constructorDocument) {
+ aRv.ThrowNotSupportedError("Not supported when there is no document");
+ return nullptr;
+ }
+
+ // 1. Construct a sheet and set its properties (see spec).
+ auto sheet =
+ MakeRefPtr<StyleSheet>(css::SheetParsingMode::eAuthorSheetFeatures,
+ CORSMode::CORS_NONE, dom::SRIMetadata());
+
+ // baseURL not yet in the spec. Implemented based on the following discussion:
+ // https://github.com/WICG/construct-stylesheets/issues/95#issuecomment-594217180
+ RefPtr<nsIURI> baseURI;
+ if (!aOptions.mBaseURL.WasPassed()) {
+ baseURI = constructorDocument->GetBaseURI();
+ } else {
+ nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aOptions.mBaseURL.Value(),
+ nullptr, constructorDocument->GetBaseURI());
+ if (NS_FAILED(rv)) {
+ aRv.ThrowNotAllowedError(
+ "Constructed style sheets must have a valid base URL");
+ return nullptr;
+ }
+ }
+
+ nsIURI* sheetURI = constructorDocument->GetDocumentURI();
+ nsIURI* originalURI = nullptr;
+ sheet->SetURIs(sheetURI, originalURI, baseURI);
+
+ sheet->SetPrincipal(constructorDocument->NodePrincipal());
+ auto referrerInfo = MakeRefPtr<ReferrerInfo>(*constructorDocument);
+ sheet->SetReferrerInfo(referrerInfo);
+ sheet->mConstructorDocument = constructorDocument;
+
+ // 2. Set the sheet's media according to aOptions.
+ if (aOptions.mMedia.IsUTF8String()) {
+ sheet->SetMedia(MediaList::Create(aOptions.mMedia.GetAsUTF8String()));
+ } else {
+ sheet->SetMedia(aOptions.mMedia.GetAsMediaList()->Clone());
+ }
+
+ // 3. Set the sheet's disabled flag according to aOptions.
+ sheet->SetDisabled(aOptions.mDisabled);
+ sheet->SetComplete();
+
+ // 4. Return sheet.
+ return sheet.forget();
+}
+
+StyleSheet::~StyleSheet() {
+ MOZ_ASSERT(!mInner, "Inner should have been dropped in LastRelease");
+}
+
+bool StyleSheet::HasRules() const {
+ return Servo_StyleSheet_HasRules(Inner().mContents);
+}
+
+Document* StyleSheet::GetAssociatedDocument() const {
+ auto* associated = GetAssociatedDocumentOrShadowRoot();
+ return associated ? associated->AsNode().OwnerDoc() : nullptr;
+}
+
+dom::DocumentOrShadowRoot* StyleSheet::GetAssociatedDocumentOrShadowRoot()
+ const {
+ const StyleSheet& outer = OutermostSheet();
+ if (outer.mDocumentOrShadowRoot) {
+ return outer.mDocumentOrShadowRoot;
+ }
+ if (outer.IsConstructed()) {
+ return outer.mConstructorDocument;
+ }
+ return nullptr;
+}
+
+void StyleSheet::UpdateRelevantGlobal() {
+ if (mRelevantGlobal || !IsComplete()) {
+ return;
+ }
+ if (Document* doc = GetAssociatedDocument()) {
+ mRelevantGlobal = doc->GetScopeObject();
+ }
+}
+
+Document* StyleSheet::GetKeptAliveByDocument() const {
+ const StyleSheet& outer = OutermostSheet();
+ if (outer.mDocumentOrShadowRoot) {
+ return outer.mDocumentOrShadowRoot->AsNode().GetComposedDoc();
+ }
+ if (outer.IsConstructed()) {
+ for (DocumentOrShadowRoot* adopter : outer.mAdopters) {
+ MOZ_ASSERT(adopter->AsNode().OwnerDoc() == outer.mConstructorDocument);
+ if (adopter->AsNode().IsInComposedDoc()) {
+ return outer.mConstructorDocument.get();
+ }
+ }
+ }
+ return nullptr;
+}
+
+void StyleSheet::LastRelease() {
+ MOZ_DIAGNOSTIC_ASSERT(mAdopters.IsEmpty(),
+ "Should have no adopters at time of destruction.");
+
+ if (mInner) {
+ MOZ_ASSERT(mInner->mSheets.Contains(this), "Our mInner should include us.");
+ mInner->RemoveSheet(this);
+ mInner = nullptr;
+ }
+
+ DropMedia();
+ DropRuleList();
+}
+
+void StyleSheet::UnlinkInner() {
+ if (!mInner) {
+ return;
+ }
+
+ // We can only have a cycle through our inner if we have a unique inner,
+ // because otherwise there are no JS wrappers for anything in the inner.
+ if (mInner->mSheets.Length() != 1) {
+ mInner->RemoveSheet(this);
+ mInner = nullptr;
+ return;
+ }
+
+ for (StyleSheet* child : ChildSheets()) {
+ MOZ_ASSERT(child->mParentSheet == this, "We have a unique inner!");
+ child->mParentSheet = nullptr;
+ }
+ Inner().mChildren.Clear();
+}
+
+void StyleSheet::TraverseInner(nsCycleCollectionTraversalCallback& cb) {
+ if (!mInner) {
+ return;
+ }
+
+ for (StyleSheet* child : ChildSheets()) {
+ if (child->mParentSheet == this) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet");
+ cb.NoteXPCOMChild(child);
+ }
+ }
+}
+
+// QueryInterface implementation for StyleSheet
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheet)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(StyleSheet)
+// We want to disconnect from our inner as soon as our refcount drops to zero,
+// without waiting for async deletion by the cycle collector. Otherwise we
+// might end up cloning the inner if someone mutates another sheet that shares
+// it with us, even though there is only one such sheet and we're about to go
+// away. This situation arises easily with sheet preloading.
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(StyleSheet, LastRelease())
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(StyleSheet)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StyleSheet)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMedia)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleList)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelevantGlobal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConstructorDocument)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReplacePromise)
+ tmp->TraverseInner(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StyleSheet)
+ tmp->DropMedia();
+ tmp->UnlinkInner();
+ tmp->DropRuleList();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelevantGlobal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mConstructorDocument)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mReplacePromise)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+dom::CSSStyleSheetParsingMode StyleSheet::ParsingModeDOM() {
+#define CHECK_MODE(X, Y) \
+ static_assert( \
+ static_cast<int>(X) == static_cast<int>(Y), \
+ "mozilla::dom::CSSStyleSheetParsingMode and " \
+ "mozilla::css::SheetParsingMode should have identical values");
+
+ CHECK_MODE(dom::CSSStyleSheetParsingMode::Agent, css::eAgentSheetFeatures);
+ CHECK_MODE(dom::CSSStyleSheetParsingMode::User, css::eUserSheetFeatures);
+ CHECK_MODE(dom::CSSStyleSheetParsingMode::Author, css::eAuthorSheetFeatures);
+
+#undef CHECK_MODE
+
+ return static_cast<dom::CSSStyleSheetParsingMode>(mParsingMode);
+}
+
+void StyleSheet::SetComplete() {
+ // HasForcedUniqueInner() is okay if the sheet is constructed, because
+ // constructed sheets are always unique and they may be set to complete
+ // multiple times if their rules are replaced via Replace()
+ MOZ_ASSERT(IsConstructed() || !HasForcedUniqueInner(),
+ "Can't complete a sheet that's already been forced unique.");
+ MOZ_ASSERT(!IsComplete(), "Already complete?");
+ mState |= State::Complete;
+
+ UpdateRelevantGlobal();
+
+ if (!Disabled()) {
+ ApplicableStateChanged(true);
+ }
+ MaybeResolveReplacePromise();
+}
+
+void StyleSheet::ApplicableStateChanged(bool aApplicable) {
+ MOZ_ASSERT(aApplicable == IsApplicable());
+ Document* docToPostEvent = nullptr;
+ auto Notify = [&](DocumentOrShadowRoot& target) {
+ nsINode& node = target.AsNode();
+ if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {
+ shadow->StyleSheetApplicableStateChanged(*this);
+ MOZ_ASSERT(!docToPostEvent || !shadow->IsInComposedDoc() ||
+ docToPostEvent == shadow->GetComposedDoc());
+ if (!docToPostEvent) {
+ docToPostEvent = shadow->GetComposedDoc();
+ }
+ } else {
+ Document* doc = node.AsDocument();
+ MOZ_ASSERT(!docToPostEvent || docToPostEvent == doc);
+ doc->StyleSheetApplicableStateChanged(*this);
+ docToPostEvent = doc;
+ }
+ };
+
+ const StyleSheet& sheet = OutermostSheet();
+ if (sheet.mDocumentOrShadowRoot) {
+ Notify(*sheet.mDocumentOrShadowRoot);
+ }
+
+ if (sheet.mConstructorDocument) {
+ Notify(*sheet.mConstructorDocument);
+ }
+
+ for (DocumentOrShadowRoot* adopter : sheet.mAdopters) {
+ MOZ_ASSERT(adopter, "adopters should never be null");
+ if (adopter != sheet.mConstructorDocument) {
+ Notify(*adopter);
+ }
+ }
+
+ if (docToPostEvent) {
+ docToPostEvent->PostStyleSheetApplicableStateChangeEvent(*this);
+ }
+}
+
+void StyleSheet::SetDisabled(bool aDisabled) {
+ if (IsReadOnly()) {
+ return;
+ }
+
+ if (aDisabled == Disabled()) {
+ return;
+ }
+
+ if (aDisabled) {
+ mState |= State::Disabled;
+ } else {
+ mState &= ~State::Disabled;
+ }
+
+ if (IsComplete()) {
+ ApplicableStateChanged(!aDisabled);
+ }
+}
+
+void StyleSheet::SetURLExtraData() {
+ Inner().mURLData =
+ new URLExtraData(GetBaseURI(), GetReferrerInfo(), Principal());
+}
+
+nsISupports* StyleSheet::GetRelevantGlobal() const {
+ const StyleSheet& outer = OutermostSheet();
+ return outer.mRelevantGlobal;
+}
+
+StyleSheetInfo::StyleSheetInfo(CORSMode aCORSMode,
+ const SRIMetadata& aIntegrity,
+ css::SheetParsingMode aParsingMode)
+ : mPrincipal(NullPrincipal::CreateWithoutOriginAttributes()),
+ mCORSMode(aCORSMode),
+ mReferrerInfo(new ReferrerInfo(nullptr)),
+ mIntegrity(aIntegrity),
+ mContents(Servo_StyleSheet_Empty(aParsingMode).Consume()),
+ mURLData(URLExtraData::Dummy()) {
+ if (!mPrincipal) {
+ MOZ_CRASH("NullPrincipal::Init failed");
+ }
+ MOZ_COUNT_CTOR(StyleSheetInfo);
+}
+
+StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy, StyleSheet* aPrimarySheet)
+ : mSheetURI(aCopy.mSheetURI),
+ mOriginalSheetURI(aCopy.mOriginalSheetURI),
+ mBaseURI(aCopy.mBaseURI),
+ mPrincipal(aCopy.mPrincipal),
+ mCORSMode(aCopy.mCORSMode),
+ mReferrerInfo(aCopy.mReferrerInfo),
+ mIntegrity(aCopy.mIntegrity),
+ // We don't rebuild the child because we're making a copy without
+ // children.
+ mSourceMapURL(aCopy.mSourceMapURL),
+ mContents(Servo_StyleSheet_Clone(aCopy.mContents.get(), aPrimarySheet)
+ .Consume()),
+ mURLData(aCopy.mURLData)
+#ifdef DEBUG
+ ,
+ mPrincipalSet(aCopy.mPrincipalSet)
+#endif
+{
+ AddSheet(aPrimarySheet);
+
+ // Our child list is fixed up by our parent.
+ MOZ_COUNT_CTOR(StyleSheetInfo);
+}
+
+StyleSheetInfo::~StyleSheetInfo() { MOZ_COUNT_DTOR(StyleSheetInfo); }
+
+StyleSheetInfo* StyleSheetInfo::CloneFor(StyleSheet* aPrimarySheet) {
+ return new StyleSheetInfo(*this, aPrimarySheet);
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSheetMallocSizeOf)
+MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSheetMallocEnclosingSizeOf)
+
+size_t StyleSheetInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+ size_t n = aMallocSizeOf(this);
+
+ n += Servo_StyleSheet_SizeOfIncludingThis(
+ ServoStyleSheetMallocSizeOf, ServoStyleSheetMallocEnclosingSizeOf,
+ mContents);
+
+ return n;
+}
+
+void StyleSheetInfo::AddSheet(StyleSheet* aSheet) {
+ mSheets.AppendElement(aSheet);
+}
+
+void StyleSheetInfo::RemoveSheet(StyleSheet* aSheet) {
+ // Fix up the parent pointer in children lists.
+ StyleSheet* newParent =
+ aSheet == mSheets[0] ? mSheets.SafeElementAt(1) : mSheets[0];
+ for (StyleSheet* child : mChildren) {
+ MOZ_ASSERT(child->mParentSheet);
+ MOZ_ASSERT(child->mParentSheet->mInner == this);
+ if (child->mParentSheet == aSheet) {
+ child->mParentSheet = newParent;
+ }
+ }
+
+ if (1 == mSheets.Length()) {
+ NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent");
+ delete this;
+ return;
+ }
+
+ mSheets.RemoveElement(aSheet);
+}
+
+void StyleSheet::GetType(nsAString& aType) { aType.AssignLiteral("text/css"); }
+
+void StyleSheet::GetHref(nsAString& aHref, ErrorResult& aRv) {
+ if (nsIURI* sheetURI = Inner().mOriginalSheetURI) {
+ nsAutoCString str;
+ nsresult rv = sheetURI->GetSpec(str);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return;
+ }
+ CopyUTF8toUTF16(str, aHref);
+ } else {
+ SetDOMStringToNull(aHref);
+ }
+}
+
+void StyleSheet::GetTitle(nsAString& aTitle) {
+ // From https://drafts.csswg.org/cssom/#dom-stylesheet-title:
+ //
+ // The title attribute must return the title or null if title is the empty
+ // string.
+ //
+ if (!mTitle.IsEmpty()) {
+ aTitle.Assign(mTitle);
+ } else {
+ SetDOMStringToNull(aTitle);
+ }
+}
+
+void StyleSheet::WillDirty() {
+ MOZ_ASSERT(!IsReadOnly());
+
+ if (IsComplete()) {
+ EnsureUniqueInner();
+ }
+}
+
+void StyleSheet::AddStyleSet(ServoStyleSet* aStyleSet) {
+ MOZ_DIAGNOSTIC_ASSERT(!mStyleSets.Contains(aStyleSet),
+ "style set already registered");
+ mStyleSets.AppendElement(aStyleSet);
+}
+
+void StyleSheet::DropStyleSet(ServoStyleSet* aStyleSet) {
+ bool found = mStyleSets.RemoveElement(aStyleSet);
+ MOZ_DIAGNOSTIC_ASSERT(found, "didn't find style set");
+#ifndef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ Unused << found;
+#endif
+}
+
+// NOTE(emilio): Composed doc and containing shadow root are set in child sheets
+// too, so no need to do it for each ancestor.
+#define NOTIFY(function_, args_) \
+ do { \
+ StyleSheet* current = this; \
+ do { \
+ for (ServoStyleSet * set : current->mStyleSets) { \
+ set->function_ args_; \
+ } \
+ if (auto* docOrShadow = current->mDocumentOrShadowRoot) { \
+ if (auto* shadow = ShadowRoot::FromNode(docOrShadow->AsNode())) { \
+ shadow->function_ args_; \
+ } else { \
+ docOrShadow->AsNode().AsDocument()->function_ args_; \
+ } \
+ } \
+ for (auto* adopter : mAdopters) { \
+ if (auto* shadow = ShadowRoot::FromNode(adopter->AsNode())) { \
+ shadow->function_ args_; \
+ } else { \
+ adopter->AsNode().AsDocument()->function_ args_; \
+ } \
+ } \
+ current = current->mParentSheet; \
+ } while (current); \
+ } while (0)
+
+void StyleSheet::EnsureUniqueInner() {
+ MOZ_ASSERT(mInner->mSheets.Length() != 0, "unexpected number of outers");
+
+ if (IsReadOnly()) {
+ // Sheets that can't be modified don't need a unique inner.
+ return;
+ }
+
+ mState |= State::ForcedUniqueInner;
+
+ if (HasUniqueInner()) {
+ // already unique
+ return;
+ }
+
+ StyleSheetInfo* clone = mInner->CloneFor(this);
+ MOZ_ASSERT(clone);
+
+ mInner->RemoveSheet(this);
+ mInner = clone;
+
+ // Fixup the child lists and parent links in the Servo sheet. This is done
+ // here instead of in StyleSheetInner::CloneFor, because it's just more
+ // convenient to do so instead.
+ FixUpAfterInnerClone();
+
+ // let our containing style sets know that if we call
+ // nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the
+ // document
+ NOTIFY(SheetCloned, (*this));
+}
+
+// WebIDL CSSStyleSheet API
+
+dom::CSSRuleList* StyleSheet::GetCssRules(nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv) {
+ if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
+ return nullptr;
+ }
+ return GetCssRulesInternal();
+}
+
+void StyleSheet::GetSourceMapURL(nsACString& aSourceMapURL) {
+ if (!mInner->mSourceMapURL.IsEmpty()) {
+ aSourceMapURL = mInner->mSourceMapURL;
+ return;
+ }
+ Servo_StyleSheet_GetSourceMapURL(mInner->mContents, &aSourceMapURL);
+}
+
+void StyleSheet::SetSourceMapURL(nsCString&& aSourceMapURL) {
+ mInner->mSourceMapURL = std::move(aSourceMapURL);
+}
+
+void StyleSheet::GetSourceURL(nsACString& aSourceURL) {
+ Servo_StyleSheet_GetSourceURL(mInner->mContents, &aSourceURL);
+}
+
+css::Rule* StyleSheet::GetDOMOwnerRule() const { return GetOwnerRule(); }
+
+// https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule
+// https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-insertrule
+uint32_t StyleSheet::InsertRule(const nsACString& aRule, uint32_t aIndex,
+ nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv) {
+ if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
+ return 0;
+ }
+
+ if (ModificationDisallowed()) {
+ aRv.ThrowNotAllowedError(
+ "This method can only be called on "
+ "modifiable style sheets");
+ return 0;
+ }
+
+ return InsertRuleInternal(aRule, aIndex, aRv);
+}
+
+// https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule
+// https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-deleterule
+void StyleSheet::DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv) {
+ if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
+ return;
+ }
+
+ if (ModificationDisallowed()) {
+ return aRv.ThrowNotAllowedError(
+ "This method can only be called on "
+ "modifiable style sheets");
+ }
+
+ return DeleteRuleInternal(aIndex, aRv);
+}
+
+int32_t StyleSheet::AddRule(const nsACString& aSelector,
+ const nsACString& aBlock,
+ const Optional<uint32_t>& aIndex,
+ nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
+ if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
+ return -1;
+ }
+
+ nsAutoCString rule;
+ rule.Append(aSelector);
+ rule.AppendLiteral(" { ");
+ if (!aBlock.IsEmpty()) {
+ rule.Append(aBlock);
+ rule.Append(' ');
+ }
+ rule.Append('}');
+
+ auto index =
+ aIndex.WasPassed() ? aIndex.Value() : GetCssRulesInternal()->Length();
+
+ InsertRuleInternal(rule, index, aRv);
+ // Always return -1.
+ return -1;
+}
+
+void StyleSheet::MaybeResolveReplacePromise() {
+ MOZ_ASSERT(!!mReplacePromise == ModificationDisallowed());
+ if (!mReplacePromise) {
+ return;
+ }
+
+ SetModificationDisallowed(false);
+ mReplacePromise->MaybeResolve(this);
+ mReplacePromise = nullptr;
+}
+
+void StyleSheet::MaybeRejectReplacePromise() {
+ MOZ_ASSERT(!!mReplacePromise == ModificationDisallowed());
+ if (!mReplacePromise) {
+ return;
+ }
+
+ SetModificationDisallowed(false);
+ mReplacePromise->MaybeRejectWithNetworkError(
+ "@import style sheet load failed");
+ mReplacePromise = nullptr;
+}
+
+// https://drafts.csswg.org/cssom/#dom-cssstylesheet-replace
+already_AddRefed<dom::Promise> StyleSheet::Replace(const nsACString& aText,
+ ErrorResult& aRv) {
+ nsIGlobalObject* globalObject = nullptr;
+ const StyleSheet& outer = OutermostSheet();
+ if (outer.mRelevantGlobal) {
+ globalObject = outer.mRelevantGlobal;
+ } else if (Document* doc = outer.GetAssociatedDocument()) {
+ globalObject = doc->GetScopeObject();
+ }
+
+ RefPtr<dom::Promise> promise = dom::Promise::Create(globalObject, aRv);
+ if (!promise) {
+ return nullptr;
+ }
+
+ // Step 1 and 4 are variable declarations
+
+ // 2.1 Check if sheet is constructed, else reject promise.
+ if (!IsConstructed()) {
+ promise->MaybeRejectWithNotAllowedError(
+ "This method can only be called on "
+ "constructed style sheets");
+ return promise.forget();
+ }
+
+ // 2.2 Check if sheet is modifiable, else throw.
+ if (ModificationDisallowed()) {
+ promise->MaybeRejectWithNotAllowedError(
+ "This method can only be called on "
+ "modifiable style sheets");
+ return promise.forget();
+ }
+
+ // 3. Disallow modifications until finished.
+ SetModificationDisallowed(true);
+
+ // TODO(emilio, 1642227): Should constructable stylesheets notify global
+ // observers (i.e., set mMustNotify to true)?
+ auto* loader = mConstructorDocument->CSSLoader();
+ auto loadData = MakeRefPtr<css::SheetLoadData>(
+ loader, /* aURI = */ nullptr, this, css::SyncLoad::No,
+ css::Loader::UseSystemPrincipal::No, css::StylePreloadKind::None,
+ /* aPreloadEncoding */ nullptr, /* aObserver */ nullptr,
+ mConstructorDocument->NodePrincipal(), GetReferrerInfo(),
+ /* aNonce */ u""_ns, FetchPriority::Auto);
+
+ // In parallel
+ // 5.1 Parse aText into rules.
+ // 5.2 Load import rules, throw NetworkError if failed.
+ // 5.3 Set sheet's rules to new rules.
+ nsISerialEventTarget* target = GetMainThreadSerialEventTarget();
+ loadData->mIsBeingParsed = true;
+ MOZ_ASSERT(!mReplacePromise);
+ mReplacePromise = promise;
+ ParseSheet(*loader, aText, *loadData)
+ ->Then(
+ target, __func__,
+ [loadData] { loadData->SheetFinishedParsingAsync(); },
+ [] { MOZ_CRASH("This MozPromise should never be rejected."); });
+
+ // 6. Return the promise
+ return promise.forget();
+}
+
+// https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync
+void StyleSheet::ReplaceSync(const nsACString& aText, ErrorResult& aRv) {
+ // Step 1 is a variable declaration
+
+ // 2.1 Check if sheet is constructed, else throw.
+ if (!IsConstructed()) {
+ return aRv.ThrowNotAllowedError(
+ "Can only be called on constructed style sheets");
+ }
+
+ // 2.2 Check if sheet is modifiable, else throw.
+ if (ModificationDisallowed()) {
+ return aRv.ThrowNotAllowedError(
+ "Can only be called on modifiable style sheets");
+ }
+
+ // 3. Parse aText into rules.
+ // 4. If rules contain @imports, skip them and continue parsing.
+ auto* loader = mConstructorDocument->CSSLoader();
+ SetURLExtraData();
+ RefPtr<const StyleStylesheetContents> rawContent =
+ Servo_StyleSheet_FromUTF8Bytes(
+ loader, this,
+ /* load_data = */ nullptr, &aText, mParsingMode, URLData(),
+ mConstructorDocument->GetCompatibilityMode(),
+ /* reusable_sheets = */ nullptr,
+ mConstructorDocument->GetStyleUseCounters(),
+ StyleAllowImportRules::No, StyleSanitizationKind::None,
+ /* sanitized_output = */ nullptr)
+ .Consume();
+
+ // 5. Set sheet's rules to the new rules.
+ Inner().mContents = std::move(rawContent);
+ FixUpRuleListAfterContentsChangeIfNeeded();
+ RuleChanged(nullptr, StyleRuleChangeKind::Generic);
+}
+
+nsresult StyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup,
+ uint32_t aIndex) {
+ NS_ENSURE_ARG_POINTER(aGroup);
+ NS_ASSERTION(IsComplete(), "No deleting from an incomplete sheet!");
+ RefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
+ NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
+
+ // check that the rule actually belongs to this sheet!
+ if (this != rule->GetStyleSheet()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (IsReadOnly()) {
+ return NS_OK;
+ }
+
+ WillDirty();
+
+ nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
+ NS_ENSURE_SUCCESS(result, result);
+
+ rule->DropReferences();
+
+ RuleRemoved(*rule);
+ return NS_OK;
+}
+
+void StyleSheet::RuleAdded(css::Rule& aRule) {
+ SetModifiedRules();
+ NOTIFY(RuleAdded, (*this, aRule));
+}
+
+void StyleSheet::RuleRemoved(css::Rule& aRule) {
+ SetModifiedRules();
+ NOTIFY(RuleRemoved, (*this, aRule));
+}
+
+void StyleSheet::RuleChanged(css::Rule* aRule, StyleRuleChangeKind aKind) {
+ MOZ_ASSERT(!aRule || HasUniqueInner(),
+ "Shouldn't have mutated a shared sheet");
+ SetModifiedRules();
+ NOTIFY(RuleChanged, (*this, aRule, aKind));
+}
+
+// nsICSSLoaderObserver implementation
+NS_IMETHODIMP
+StyleSheet::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
+ nsresult aStatus) {
+ if (!aSheet->GetParentSheet()) {
+ return NS_OK; // ignore if sheet has been detached already
+ }
+ MOZ_ASSERT(this == aSheet->GetParentSheet(),
+ "We are being notified of a sheet load for a sheet that is not "
+ "our child!");
+ if (NS_FAILED(aStatus)) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(aSheet->GetOwnerRule());
+ NOTIFY(ImportRuleLoaded, (*aSheet->GetOwnerRule(), *aSheet));
+ return NS_OK;
+}
+
+#undef NOTIFY
+
+nsresult StyleSheet::InsertRuleIntoGroup(const nsACString& aRule,
+ css::GroupRule* aGroup,
+ uint32_t aIndex) {
+ NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!");
+ // check that the group actually belongs to this sheet!
+ if (this != aGroup->GetStyleSheet()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (IsReadOnly()) {
+ return NS_OK;
+ }
+
+ if (ModificationDisallowed()) {
+ return NS_ERROR_DOM_NOT_ALLOWED_ERR;
+ }
+
+ WillDirty();
+
+ nsresult result = InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
+ NS_ENSURE_SUCCESS(result, result);
+ RuleAdded(*aGroup->GetStyleRuleAt(aIndex));
+ return NS_OK;
+}
+
+uint64_t StyleSheet::FindOwningWindowInnerID() const {
+ uint64_t windowID = 0;
+ if (Document* doc = GetAssociatedDocument()) {
+ windowID = doc->InnerWindowID();
+ }
+
+ if (windowID == 0 && mOwningNode) {
+ windowID = mOwningNode->OwnerDoc()->InnerWindowID();
+ }
+
+ RefPtr<css::Rule> ownerRule;
+ if (windowID == 0 && (ownerRule = GetDOMOwnerRule())) {
+ RefPtr<StyleSheet> sheet = ownerRule->GetStyleSheet();
+ if (sheet) {
+ windowID = sheet->FindOwningWindowInnerID();
+ }
+ }
+
+ if (windowID == 0 && mParentSheet) {
+ windowID = mParentSheet->FindOwningWindowInnerID();
+ }
+
+ return windowID;
+}
+
+void StyleSheet::RemoveFromParent() {
+ if (!mParentSheet) {
+ return;
+ }
+
+ MOZ_ASSERT(mParentSheet->ChildSheets().Contains(this));
+ mParentSheet->Inner().mChildren.RemoveElement(this);
+ mParentSheet = nullptr;
+}
+
+void StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv) {
+ StyleSheetInfo& info = Inner();
+
+ if (aSubjectPrincipal.Subsumes(info.mPrincipal)) {
+ return;
+ }
+
+ // Allow access only if CORS mode is not NONE and the security flag
+ // is not turned off.
+ if (GetCORSMode() == CORS_NONE && !nsContentUtils::BypassCSSOMOriginCheck()) {
+ aRv.ThrowSecurityError("Not allowed to access cross-origin stylesheet");
+ return;
+ }
+
+ // Now make sure we set the principal of our inner to the subjectPrincipal.
+ // We do this because we're in a situation where the caller would not normally
+ // be able to access the sheet, but the sheet has opted in to being read.
+ // Unfortunately, that means it's also opted in to being _edited_, and if the
+ // caller now makes edits to the sheet we want the resulting resource loads,
+ // if any, to look as if they are coming from the caller's principal, not the
+ // original sheet principal.
+ //
+ // That means we need a unique inner, of course. But we don't want to do that
+ // if we're not complete yet. Luckily, all the callers of this method throw
+ // anyway if not complete, so we can just do that here too.
+ if (!IsComplete()) {
+ aRv.ThrowInvalidAccessError(
+ "Not allowed to access still-loading stylesheet");
+ return;
+ }
+
+ WillDirty();
+
+ info.mPrincipal = &aSubjectPrincipal;
+}
+
+bool StyleSheet::AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv) {
+ // Rules are not available on incomplete sheets.
+ if (!IsComplete()) {
+ aRv.ThrowInvalidAccessError(
+ "Can't access rules of still-loading style sheet");
+ return false;
+ }
+ //-- Security check: Only scripts whose principal subsumes that of the
+ // style sheet can access rule collections.
+ SubjectSubsumesInnerPrincipal(aSubjectPrincipal, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return false;
+ }
+ return true;
+}
+
+void StyleSheet::SetAssociatedDocumentOrShadowRoot(
+ DocumentOrShadowRoot* aDocOrShadowRoot) {
+ MOZ_ASSERT(!IsConstructed());
+ MOZ_ASSERT(!mParentSheet || !aDocOrShadowRoot,
+ "Shouldn't be set on child sheets");
+
+ // not ref counted
+ mDocumentOrShadowRoot = aDocOrShadowRoot;
+ UpdateRelevantGlobal();
+}
+
+void StyleSheet::AppendStyleSheet(StyleSheet& aSheet) {
+ WillDirty();
+ AppendStyleSheetSilently(aSheet);
+}
+
+void StyleSheet::AppendStyleSheetSilently(StyleSheet& aSheet) {
+ MOZ_ASSERT(!IsReadOnly());
+
+ Inner().mChildren.AppendElement(&aSheet);
+
+ // This is not reference counted. Our parent tells us when
+ // it's going away.
+ aSheet.mParentSheet = this;
+}
+
+size_t StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+ size_t n = 0;
+ n += aMallocSizeOf(this);
+
+ // We want to measure the inner with only one of the children, and it makes
+ // sense for it to be the latest as it is the most likely to be reachable.
+ if (Inner().mSheets.LastElement() == this) {
+ n += Inner().SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ // Measurement of the following members may be added later if DMD finds it
+ // is worthwhile:
+ // - mTitle
+ // - mMedia
+ // - mStyleSets
+ // - mRuleList
+
+ return n;
+}
+
+#if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
+void StyleSheet::List(FILE* aOut, int32_t aIndent) {
+ for (StyleSheet* child : ChildSheets()) {
+ child->List(aOut, aIndent);
+ }
+
+ nsCString line;
+ for (int i = 0; i < aIndent; ++i) {
+ line.AppendLiteral(" ");
+ }
+
+ line.AppendLiteral("/* ");
+
+ nsCString url;
+ GetSheetURI()->GetSpec(url);
+ if (url.IsEmpty()) {
+ line.AppendLiteral("(no URL)");
+ } else {
+ line.Append(url);
+ }
+
+ line.AppendLiteral(" (");
+
+ switch (GetOrigin()) {
+ case StyleOrigin::UserAgent:
+ line.AppendLiteral("User Agent");
+ break;
+ case StyleOrigin::User:
+ line.AppendLiteral("User");
+ break;
+ case StyleOrigin::Author:
+ line.AppendLiteral("Author");
+ break;
+ }
+
+ if (mMedia) {
+ nsAutoCString buffer;
+ mMedia->GetText(buffer);
+
+ if (!buffer.IsEmpty()) {
+ line.AppendLiteral(", ");
+ line.Append(buffer);
+ }
+ }
+
+ line.AppendLiteral(") */");
+
+ fprintf_stderr(aOut, "%s\n\n", line.get());
+
+ nsCString newlineIndent;
+ newlineIndent.Append('\n');
+ for (int i = 0; i < aIndent; ++i) {
+ newlineIndent.AppendLiteral(" ");
+ }
+
+ ServoCSSRuleList* ruleList = GetCssRulesInternal();
+ for (uint32_t i = 0, len = ruleList->Length(); i < len; ++i) {
+ css::Rule* rule = ruleList->GetRule(i);
+
+ nsAutoCString cssText;
+ rule->GetCssText(cssText);
+ cssText.ReplaceSubstring("\n"_ns, newlineIndent);
+ fprintf_stderr(aOut, "%s\n", cssText.get());
+ }
+
+ if (ruleList->Length() != 0) {
+ fprintf_stderr(aOut, "\n");
+ }
+}
+#endif
+
+void StyleSheet::SetMedia(already_AddRefed<dom::MediaList> aMedia) {
+ mMedia = aMedia;
+ if (mMedia) {
+ mMedia->SetStyleSheet(this);
+ }
+}
+
+void StyleSheet::DropMedia() {
+ if (mMedia) {
+ mMedia->SetStyleSheet(nullptr);
+ mMedia = nullptr;
+ }
+}
+
+dom::MediaList* StyleSheet::Media() {
+ if (!mMedia) {
+ mMedia = dom::MediaList::Create(EmptyCString());
+ mMedia->SetStyleSheet(this);
+ }
+
+ return mMedia;
+}
+
+// nsWrapperCache
+
+JSObject* StyleSheet::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return dom::CSSStyleSheet_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void StyleSheet::FixUpRuleListAfterContentsChangeIfNeeded(bool aFromClone) {
+ if (!mRuleList) {
+ return;
+ }
+
+ RefPtr<StyleLockedCssRules> rules =
+ Servo_StyleSheet_GetRules(Inner().mContents.get()).Consume();
+ mRuleList->SetRawContents(std::move(rules), aFromClone);
+}
+
+void StyleSheet::FixUpAfterInnerClone() {
+ MOZ_ASSERT(Inner().mSheets.Length() == 1, "Should've just cloned");
+ MOZ_ASSERT(Inner().mSheets[0] == this);
+ MOZ_ASSERT(Inner().mChildren.IsEmpty());
+
+ FixUpRuleListAfterContentsChangeIfNeeded(/* aFromClone = */ true);
+
+ RefPtr<StyleLockedCssRules> rules =
+ Servo_StyleSheet_GetRules(Inner().mContents.get()).Consume();
+ uint32_t index = 0;
+ while (true) {
+ uint32_t line, column; // Actually unused.
+ RefPtr<StyleLockedImportRule> import =
+ Servo_CssRules_GetImportRuleAt(rules, index, &line, &column).Consume();
+ if (!import) {
+ // Note that only @charset rules come before @import rules, and @charset
+ // rules are parsed but skipped, so we can stop iterating as soon as we
+ // find something that isn't an @import rule.
+ break;
+ }
+ auto* sheet = const_cast<StyleSheet*>(Servo_ImportRule_GetSheet(import));
+ MOZ_ASSERT(sheet);
+ AppendStyleSheetSilently(*sheet);
+ index++;
+ }
+}
+
+already_AddRefed<StyleSheet> StyleSheet::CreateEmptyChildSheet(
+ already_AddRefed<dom::MediaList> aMediaList) const {
+ RefPtr<StyleSheet> child =
+ new StyleSheet(ParsingMode(), CORSMode::CORS_NONE, SRIMetadata());
+
+ child->mMedia = aMediaList;
+ return child.forget();
+}
+
+// We disable parallel stylesheet parsing if the browser is recording CSS errors
+// (which parallel parsing can't handle).
+static bool AllowParallelParse(css::Loader& aLoader, URLExtraData* aUrlData) {
+ Document* doc = aLoader.GetDocument();
+ if (doc && css::ErrorReporter::ShouldReportErrors(*doc)) {
+ return false;
+ }
+ // Otherwise we can parse in parallel.
+ return true;
+}
+
+RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
+ css::Loader& aLoader, const nsACString& aBytes,
+ css::SheetLoadData& aLoadData) {
+ MOZ_ASSERT(mParsePromise.IsEmpty());
+ RefPtr<StyleSheetParsePromise> p = mParsePromise.Ensure(__func__);
+ if (!aLoadData.ShouldDefer()) {
+ mParsePromise.SetTaskPriority(nsIRunnablePriority::PRIORITY_RENDER_BLOCKING,
+ __func__);
+ }
+ SetURLExtraData();
+
+ // @import rules are disallowed due to this decision:
+ // https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418
+ // We may allow @import rules again in the future.
+ auto allowImportRules = SelfOrAncestorIsConstructed()
+ ? StyleAllowImportRules::No
+ : StyleAllowImportRules::Yes;
+ URLExtraData* urlData = URLData();
+ const bool shouldRecordCounters =
+ aLoader.GetDocument() && aLoader.GetDocument()->GetStyleUseCounters() &&
+ !urlData->ChromeRulesEnabled();
+ if (!AllowParallelParse(aLoader, urlData)) {
+ UniquePtr<StyleUseCounters> counters;
+ if (shouldRecordCounters) {
+ counters.reset(Servo_UseCounters_Create());
+ }
+
+ RefPtr<StyleStylesheetContents> contents =
+ Servo_StyleSheet_FromUTF8Bytes(
+ &aLoader, this, &aLoadData, &aBytes, mParsingMode, urlData,
+ aLoadData.mCompatMode,
+ /* reusable_sheets = */ nullptr, counters.get(), allowImportRules,
+ StyleSanitizationKind::None,
+ /* sanitized_output = */ nullptr)
+ .Consume();
+ FinishAsyncParse(contents.forget(), std::move(counters));
+ } else {
+ auto holder = MakeRefPtr<css::SheetLoadDataHolder>(__func__, &aLoadData);
+ Servo_StyleSheet_FromUTF8BytesAsync(holder, urlData, &aBytes, mParsingMode,
+ aLoadData.mCompatMode,
+ shouldRecordCounters, allowImportRules);
+ }
+
+ return p;
+}
+
+void StyleSheet::FinishAsyncParse(
+ already_AddRefed<StyleStylesheetContents> aSheetContents,
+ UniquePtr<StyleUseCounters> aUseCounters) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mParsePromise.IsEmpty());
+ Inner().mContents = aSheetContents;
+ Inner().mUseCounters = std::move(aUseCounters);
+ FixUpRuleListAfterContentsChangeIfNeeded();
+ mParsePromise.Resolve(true, __func__);
+}
+
+void StyleSheet::ParseSheetSync(
+ css::Loader* aLoader, const nsACString& aBytes,
+ css::SheetLoadData* aLoadData,
+ css::LoaderReusableStyleSheets* aReusableSheets) {
+ const nsCompatibility compatMode = [&] {
+ if (aLoadData) {
+ return aLoadData->mCompatMode;
+ }
+ if (aLoader) {
+ return aLoader->CompatMode(css::StylePreloadKind::None);
+ }
+ return eCompatibility_FullStandards;
+ }();
+
+ SetURLExtraData();
+
+ URLExtraData* urlData = URLData();
+ const StyleUseCounters* useCounters =
+ aLoader && aLoader->GetDocument() && !urlData->ChromeRulesEnabled()
+ ? aLoader->GetDocument()->GetStyleUseCounters()
+ : nullptr;
+
+ auto allowImportRules = SelfOrAncestorIsConstructed()
+ ? StyleAllowImportRules::No
+ : StyleAllowImportRules::Yes;
+
+ Inner().mContents = Servo_StyleSheet_FromUTF8Bytes(
+ aLoader, this, aLoadData, &aBytes, mParsingMode,
+ urlData, compatMode, aReusableSheets, useCounters,
+ allowImportRules, StyleSanitizationKind::None,
+ /* sanitized_output = */ nullptr)
+ .Consume();
+}
+
+void StyleSheet::ReparseSheet(const nsACString& aInput, ErrorResult& aRv) {
+ if (!IsComplete()) {
+ return aRv.ThrowInvalidAccessError("Cannot reparse still-loading sheet");
+ }
+
+ // Allowing to modify UA sheets is dangerous (in the sense that C++ code
+ // relies on rules in those sheets), plus they're probably going to be shared
+ // across processes in which case this is directly a no-go.
+ if (IsReadOnly()) {
+ return;
+ }
+
+ // Hold strong ref to the CSSLoader in case the document update
+ // kills the document
+ RefPtr<css::Loader> loader;
+ if (Document* doc = GetAssociatedDocument()) {
+ loader = doc->CSSLoader();
+ NS_ASSERTION(loader, "Document with no CSS loader!");
+ } else {
+ loader = new css::Loader;
+ }
+
+ WillDirty();
+
+ // cache child sheets to reuse
+ css::LoaderReusableStyleSheets reusableSheets;
+ for (StyleSheet* child : ChildSheets()) {
+ if (child->GetOriginalURI()) {
+ reusableSheets.AddReusableSheet(child);
+ }
+ }
+
+ // Clean up child sheets list.
+ for (StyleSheet* child : ChildSheets()) {
+ child->mParentSheet = nullptr;
+ }
+ Inner().mChildren.Clear();
+
+ // Notify to the stylesets about the old rules going away.
+ {
+ ServoCSSRuleList* ruleList = GetCssRulesInternal();
+ MOZ_ASSERT(ruleList);
+
+ uint32_t ruleCount = ruleList->Length();
+ for (uint32_t i = 0; i < ruleCount; ++i) {
+ css::Rule* rule = ruleList->GetRule(i);
+ MOZ_ASSERT(rule);
+ RuleRemoved(*rule);
+ }
+
+ // We need to clear the rule list here (rather than after parsing) because
+ // ParseSheetSync may reuse child sheets, which would cause us to end up
+ // with a wrong mChilden array.
+ ruleList->SetRawContents(nullptr, /* aFromClone = */ false);
+ }
+
+ ParseSheetSync(loader, aInput, /* aLoadData = */ nullptr, &reusableSheets);
+
+ FixUpRuleListAfterContentsChangeIfNeeded();
+
+ // Notify the stylesets about the new rules.
+ {
+ // Get the rule list (which will need to be regenerated after ParseSheet).
+ ServoCSSRuleList* ruleList = GetCssRulesInternal();
+ MOZ_ASSERT(ruleList);
+
+ uint32_t ruleCount = ruleList->Length();
+ for (uint32_t i = 0; i < ruleCount; ++i) {
+ css::Rule* rule = ruleList->GetRule(i);
+ MOZ_ASSERT(rule);
+ RuleAdded(*rule);
+ }
+ }
+
+ // Our rules are no longer considered modified for devtools.
+ mState &= ~State::ModifiedRulesForDevtools;
+}
+
+void StyleSheet::DropRuleList() {
+ if (mRuleList) {
+ mRuleList->DropReferences();
+ mRuleList = nullptr;
+ }
+}
+
+already_AddRefed<StyleSheet> StyleSheet::Clone(
+ StyleSheet* aCloneParent,
+ dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot) const {
+ MOZ_ASSERT(!IsConstructed(),
+ "Cannot create a non-constructed sheet from a constructed sheet");
+ RefPtr<StyleSheet> clone =
+ new StyleSheet(*this, aCloneParent, aCloneDocumentOrShadowRoot,
+ /* aConstructorDocToUse */ nullptr);
+ return clone.forget();
+}
+
+already_AddRefed<StyleSheet> StyleSheet::CloneAdoptedSheet(
+ Document& aConstructorDocument) const {
+ MOZ_ASSERT(IsConstructed(),
+ "Cannot create a constructed sheet from a non-constructed sheet");
+ MOZ_ASSERT(aConstructorDocument.IsStaticDocument(),
+ "Should never clone adopted sheets for a non-static document");
+ RefPtr<StyleSheet> clone = new StyleSheet(*this,
+ /* aParentSheetToUse */ nullptr,
+ /* aDocOrShadowRootToUse */ nullptr,
+ &aConstructorDocument);
+ return clone.forget();
+}
+
+ServoCSSRuleList* StyleSheet::GetCssRulesInternal() {
+ if (!mRuleList) {
+ // TODO(emilio): This should go away, but we need to fix the CC setup for
+ // @import rules first, see bug 1719963.
+ EnsureUniqueInner();
+
+ RefPtr<StyleLockedCssRules> rawRules =
+ Servo_StyleSheet_GetRules(Inner().mContents).Consume();
+ MOZ_ASSERT(rawRules);
+ mRuleList = new ServoCSSRuleList(rawRules.forget(), this, nullptr);
+ }
+ return mRuleList;
+}
+
+uint32_t StyleSheet::InsertRuleInternal(const nsACString& aRule,
+ uint32_t aIndex, ErrorResult& aRv) {
+ MOZ_ASSERT(!IsReadOnly());
+ MOZ_ASSERT(!ModificationDisallowed());
+
+ // Ensure mRuleList is constructed.
+ GetCssRulesInternal();
+
+ aRv = mRuleList->InsertRule(aRule, aIndex);
+ if (aRv.Failed()) {
+ return 0;
+ }
+
+ // XXX We may not want to get the rule when stylesheet change event
+ // is not enabled.
+ css::Rule* rule = mRuleList->GetRule(aIndex);
+ RuleAdded(*rule);
+
+ return aIndex;
+}
+
+void StyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv) {
+ MOZ_ASSERT(!IsReadOnly());
+ MOZ_ASSERT(!ModificationDisallowed());
+
+ // Ensure mRuleList is constructed.
+ GetCssRulesInternal();
+ if (aIndex >= mRuleList->Length()) {
+ aRv.ThrowIndexSizeError(
+ nsPrintfCString("Cannot delete rule at index %u"
+ " because the number of rules is only %u",
+ aIndex, mRuleList->Length()));
+ return;
+ }
+
+ // Hold a strong ref to the rule so it doesn't die when we remove it
+ // from the list. XXX We may not want to hold it if stylesheet change
+ // event is not enabled.
+ RefPtr<css::Rule> rule = mRuleList->GetRule(aIndex);
+ aRv = mRuleList->DeleteRule(aIndex);
+ if (!aRv.Failed()) {
+ RuleRemoved(*rule);
+ }
+}
+
+nsresult StyleSheet::InsertRuleIntoGroupInternal(const nsACString& aRule,
+ css::GroupRule* aGroup,
+ uint32_t aIndex) {
+ MOZ_ASSERT(!IsReadOnly());
+
+ ServoCSSRuleList* rules = aGroup->CssRules();
+ MOZ_ASSERT(rules && rules->GetParentRule() == aGroup);
+ return rules->InsertRule(aRule, aIndex);
+}
+
+StyleOrigin StyleSheet::GetOrigin() const {
+ return Servo_StyleSheet_GetOrigin(Inner().mContents);
+}
+
+void StyleSheet::SetSharedContents(const StyleLockedCssRules* aSharedRules) {
+ MOZ_ASSERT(!IsComplete());
+
+ SetURLExtraData();
+
+ Inner().mContents =
+ Servo_StyleSheet_FromSharedData(URLData(), aSharedRules).Consume();
+}
+
+const StyleLockedCssRules* StyleSheet::ToShared(
+ StyleSharedMemoryBuilder* aBuilder, nsCString& aErrorMessage) {
+ // Assert some things we assume when creating a StyleSheet using shared
+ // memory.
+ MOZ_ASSERT(GetReferrerInfo()->ReferrerPolicy() == ReferrerPolicy::_empty);
+ MOZ_ASSERT(GetReferrerInfo()->GetSendReferrer());
+ MOZ_ASSERT(!nsCOMPtr<nsIURI>(GetReferrerInfo()->GetComputedReferrer()));
+ MOZ_ASSERT(GetCORSMode() == CORS_NONE);
+ MOZ_ASSERT(Inner().mIntegrity.IsEmpty());
+ MOZ_ASSERT(Principal()->IsSystemPrincipal());
+
+ const StyleLockedCssRules* rules = Servo_SharedMemoryBuilder_AddStylesheet(
+ aBuilder, Inner().mContents, &aErrorMessage);
+
+#ifdef DEBUG
+ if (!rules) {
+ // Print the ToShmem error message so that developers know what to fix.
+ printf_stderr("%s\n", aErrorMessage.get());
+ MOZ_CRASH("UA style sheet contents failed shared memory requirements");
+ }
+#endif
+
+ return rules;
+}
+
+bool StyleSheet::IsReadOnly() const {
+ return IsComplete() && GetOrigin() == StyleOrigin::UserAgent;
+}
+
+} // namespace mozilla