Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / style / StyleSheet.cpp
blobd3613bc6733c12cb1c1a31c4fb660e3b8a00f8ef
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/. */
7 #include "mozilla/StyleSheet.h"
8 #include "mozilla/Assertions.h"
9 #include "mozilla/BasePrincipal.h"
10 #include "mozilla/ComputedStyleInlines.h"
11 #include "mozilla/css/ErrorReporter.h"
12 #include "mozilla/css/GroupRule.h"
13 #include "mozilla/dom/CSSImportRule.h"
14 #include "mozilla/dom/CSSRuleList.h"
15 #include "mozilla/dom/Element.h"
16 #include "mozilla/dom/MediaList.h"
17 #include "mozilla/dom/Promise.h"
18 #include "mozilla/dom/ReferrerInfo.h"
19 #include "mozilla/dom/ShadowRoot.h"
20 #include "mozilla/dom/ShadowRootBinding.h"
21 #include "mozilla/NullPrincipal.h"
22 #include "mozilla/ServoBindings.h"
23 #include "mozilla/ServoCSSRuleList.h"
24 #include "mozilla/ServoStyleSet.h"
25 #include "mozilla/StaticPrefs_layout.h"
26 #include "mozilla/StyleSheetInlines.h"
27 #include "mozilla/css/SheetLoadData.h"
29 #include "mozAutoDocUpdate.h"
30 #include "SheetLoadData.h"
32 namespace mozilla {
34 using namespace dom;
36 StyleSheet::StyleSheet(css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
37 const dom::SRIMetadata& aIntegrity)
38 : mParentSheet(nullptr),
39 mRelevantGlobal(nullptr),
40 mConstructorDocument(nullptr),
41 mDocumentOrShadowRoot(nullptr),
42 mParsingMode(aParsingMode),
43 mState(static_cast<State>(0)),
44 mInner(new StyleSheetInfo(aCORSMode, aIntegrity, aParsingMode)) {
45 mInner->AddSheet(this);
48 StyleSheet::StyleSheet(const StyleSheet& aCopy, StyleSheet* aParentSheetToUse,
49 dom::DocumentOrShadowRoot* aDocOrShadowRootToUse,
50 dom::Document* aConstructorDocToUse)
51 : mParentSheet(aParentSheetToUse),
52 mRelevantGlobal(nullptr),
53 mConstructorDocument(aConstructorDocToUse),
54 mTitle(aCopy.mTitle),
55 mDocumentOrShadowRoot(aDocOrShadowRootToUse),
56 mParsingMode(aCopy.mParsingMode),
57 mState(aCopy.mState),
58 // Shallow copy, but concrete subclasses will fix up.
59 mInner(aCopy.mInner) {
60 MOZ_ASSERT(!aConstructorDocToUse || aCopy.IsConstructed());
61 MOZ_ASSERT(!aConstructorDocToUse || !aDocOrShadowRootToUse,
62 "Should never have both of these together.");
63 MOZ_ASSERT(mInner, "Should only copy StyleSheets with an mInner.");
64 mInner->AddSheet(this);
65 // CSSOM's been there, force full copy now.
66 if (HasForcedUniqueInner()) {
67 MOZ_ASSERT(IsComplete(),
68 "Why have rules been accessed on an incomplete sheet?");
69 EnsureUniqueInner();
70 // But CSSOM hasn't been on _this_ stylesheet yet, so no need to clone
71 // ourselves.
72 mState &= ~(State::ForcedUniqueInner | State::ModifiedRules |
73 State::ModifiedRulesForDevtools);
76 if (aCopy.mMedia) {
77 // XXX This is wrong; we should be keeping @import rules and
78 // sheets in sync!
79 mMedia = aCopy.mMedia->Clone();
83 /* static */
84 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-cssstylesheet
85 already_AddRefed<StyleSheet> StyleSheet::Constructor(
86 const dom::GlobalObject& aGlobal, const dom::CSSStyleSheetInit& aOptions,
87 ErrorResult& aRv) {
88 nsCOMPtr<nsPIDOMWindowInner> window =
89 do_QueryInterface(aGlobal.GetAsSupports());
91 if (!window) {
92 aRv.ThrowNotSupportedError("Not supported when there is no document");
93 return nullptr;
96 Document* constructorDocument = window->GetExtantDoc();
97 if (!constructorDocument) {
98 aRv.ThrowNotSupportedError("Not supported when there is no document");
99 return nullptr;
102 // 1. Construct a sheet and set its properties (see spec).
103 auto sheet =
104 MakeRefPtr<StyleSheet>(css::SheetParsingMode::eAuthorSheetFeatures,
105 CORSMode::CORS_NONE, dom::SRIMetadata());
107 // baseURL not yet in the spec. Implemented based on the following discussion:
108 // https://github.com/WICG/construct-stylesheets/issues/95#issuecomment-594217180
109 RefPtr<nsIURI> baseURI;
110 if (!aOptions.mBaseURL.WasPassed()) {
111 baseURI = constructorDocument->GetBaseURI();
112 } else {
113 nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aOptions.mBaseURL.Value(),
114 nullptr, constructorDocument->GetBaseURI());
115 if (NS_FAILED(rv)) {
116 aRv.ThrowNotAllowedError(
117 "Constructed style sheets must have a valid base URL");
118 return nullptr;
122 nsIURI* sheetURI = constructorDocument->GetDocumentURI();
123 nsIURI* originalURI = nullptr;
124 sheet->SetURIs(sheetURI, originalURI, baseURI);
126 sheet->SetPrincipal(constructorDocument->NodePrincipal());
127 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*constructorDocument);
128 sheet->SetReferrerInfo(referrerInfo);
129 sheet->mConstructorDocument = constructorDocument;
130 if (constructorDocument) {
131 sheet->mRelevantGlobal = constructorDocument->GetParentObject();
134 // 2. Set the sheet's media according to aOptions.
135 if (aOptions.mMedia.IsUTF8String()) {
136 sheet->SetMedia(MediaList::Create(aOptions.mMedia.GetAsUTF8String()));
137 } else {
138 sheet->SetMedia(aOptions.mMedia.GetAsMediaList()->Clone());
141 // 3. Set the sheet's disabled flag according to aOptions.
142 sheet->SetDisabled(aOptions.mDisabled);
143 sheet->SetComplete();
145 // 4. Return sheet.
146 return sheet.forget();
149 StyleSheet::~StyleSheet() {
150 MOZ_ASSERT(!mInner, "Inner should have been dropped in LastRelease");
153 bool StyleSheet::HasRules() const {
154 return Servo_StyleSheet_HasRules(Inner().mContents);
157 Document* StyleSheet::GetAssociatedDocument() const {
158 auto* associated = GetAssociatedDocumentOrShadowRoot();
159 return associated ? associated->AsNode().OwnerDoc() : nullptr;
162 dom::DocumentOrShadowRoot* StyleSheet::GetAssociatedDocumentOrShadowRoot()
163 const {
164 const StyleSheet& outer = OutermostSheet();
165 if (outer.mDocumentOrShadowRoot) {
166 return outer.mDocumentOrShadowRoot;
168 if (outer.IsConstructed()) {
169 return outer.mConstructorDocument;
171 return nullptr;
174 Document* StyleSheet::GetKeptAliveByDocument() const {
175 const StyleSheet& outer = OutermostSheet();
176 if (outer.mDocumentOrShadowRoot) {
177 return outer.mDocumentOrShadowRoot->AsNode().GetComposedDoc();
179 if (outer.IsConstructed()) {
180 for (DocumentOrShadowRoot* adopter : outer.mAdopters) {
181 MOZ_ASSERT(adopter->AsNode().OwnerDoc() == outer.mConstructorDocument);
182 if (adopter->AsNode().IsInComposedDoc()) {
183 return outer.mConstructorDocument.get();
187 return nullptr;
190 void StyleSheet::LastRelease() {
191 MOZ_DIAGNOSTIC_ASSERT(mAdopters.IsEmpty(),
192 "Should have no adopters at time of destruction.");
194 if (mInner) {
195 MOZ_ASSERT(mInner->mSheets.Contains(this), "Our mInner should include us.");
196 mInner->RemoveSheet(this);
197 mInner = nullptr;
200 DropMedia();
201 DropRuleList();
204 void StyleSheet::UnlinkInner() {
205 if (!mInner) {
206 return;
209 // We can only have a cycle through our inner if we have a unique inner,
210 // because otherwise there are no JS wrappers for anything in the inner.
211 if (mInner->mSheets.Length() != 1) {
212 mInner->RemoveSheet(this);
213 mInner = nullptr;
214 return;
217 for (StyleSheet* child : ChildSheets()) {
218 MOZ_ASSERT(child->mParentSheet == this, "We have a unique inner!");
219 child->mParentSheet = nullptr;
221 Inner().mChildren.Clear();
224 void StyleSheet::TraverseInner(nsCycleCollectionTraversalCallback& cb) {
225 if (!mInner) {
226 return;
229 for (StyleSheet* child : ChildSheets()) {
230 if (child->mParentSheet == this) {
231 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet");
232 cb.NoteXPCOMChild(child);
237 // QueryInterface implementation for StyleSheet
238 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheet)
239 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
240 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
241 NS_INTERFACE_MAP_ENTRY(nsISupports)
242 NS_INTERFACE_MAP_END
244 NS_IMPL_CYCLE_COLLECTING_ADDREF(StyleSheet)
245 // We want to disconnect from our inner as soon as our refcount drops to zero,
246 // without waiting for async deletion by the cycle collector. Otherwise we
247 // might end up cloning the inner if someone mutates another sheet that shares
248 // it with us, even though there is only one such sheet and we're about to go
249 // away. This situation arises easily with sheet preloading.
250 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(StyleSheet, LastRelease())
252 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(StyleSheet)
254 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StyleSheet)
255 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMedia)
256 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleList)
257 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelevantGlobal)
258 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConstructorDocument)
259 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReplacePromise)
260 tmp->TraverseInner(cb);
261 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
263 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StyleSheet)
264 tmp->DropMedia();
265 tmp->UnlinkInner();
266 tmp->DropRuleList();
267 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelevantGlobal)
268 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConstructorDocument)
269 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReplacePromise)
270 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
271 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
273 dom::CSSStyleSheetParsingMode StyleSheet::ParsingModeDOM() {
274 #define CHECK_MODE(X, Y) \
275 static_assert( \
276 static_cast<int>(X) == static_cast<int>(Y), \
277 "mozilla::dom::CSSStyleSheetParsingMode and " \
278 "mozilla::css::SheetParsingMode should have identical values");
280 CHECK_MODE(dom::CSSStyleSheetParsingMode::Agent, css::eAgentSheetFeatures);
281 CHECK_MODE(dom::CSSStyleSheetParsingMode::User, css::eUserSheetFeatures);
282 CHECK_MODE(dom::CSSStyleSheetParsingMode::Author, css::eAuthorSheetFeatures);
284 #undef CHECK_MODE
286 return static_cast<dom::CSSStyleSheetParsingMode>(mParsingMode);
289 void StyleSheet::SetComplete() {
290 // HasForcedUniqueInner() is okay if the sheet is constructed, because
291 // constructed sheets are always unique and they may be set to complete
292 // multiple times if their rules are replaced via Replace()
293 MOZ_ASSERT(IsConstructed() || !HasForcedUniqueInner(),
294 "Can't complete a sheet that's already been forced unique.");
295 MOZ_ASSERT(!IsComplete(), "Already complete?");
296 mState |= State::Complete;
297 if (!Disabled()) {
298 ApplicableStateChanged(true);
300 MaybeResolveReplacePromise();
303 void StyleSheet::ApplicableStateChanged(bool aApplicable) {
304 MOZ_ASSERT(aApplicable == IsApplicable());
305 Document* docToPostEvent = nullptr;
306 auto Notify = [&](DocumentOrShadowRoot& target) {
307 nsINode& node = target.AsNode();
308 if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {
309 shadow->StyleSheetApplicableStateChanged(*this);
310 MOZ_ASSERT(!docToPostEvent || !shadow->IsInComposedDoc() ||
311 docToPostEvent == shadow->GetComposedDoc());
312 if (!docToPostEvent) {
313 docToPostEvent = shadow->GetComposedDoc();
315 } else {
316 Document* doc = node.AsDocument();
317 MOZ_ASSERT(!docToPostEvent || docToPostEvent == doc);
318 doc->StyleSheetApplicableStateChanged(*this);
319 docToPostEvent = doc;
323 const StyleSheet& sheet = OutermostSheet();
324 if (sheet.mDocumentOrShadowRoot) {
325 Notify(*sheet.mDocumentOrShadowRoot);
328 if (sheet.mConstructorDocument) {
329 Notify(*sheet.mConstructorDocument);
332 for (DocumentOrShadowRoot* adopter : sheet.mAdopters) {
333 MOZ_ASSERT(adopter, "adopters should never be null");
334 if (adopter != sheet.mConstructorDocument) {
335 Notify(*adopter);
339 if (docToPostEvent) {
340 docToPostEvent->PostStyleSheetApplicableStateChangeEvent(*this);
344 void StyleSheet::SetDisabled(bool aDisabled) {
345 if (IsReadOnly()) {
346 return;
349 if (aDisabled == Disabled()) {
350 return;
353 if (aDisabled) {
354 mState |= State::Disabled;
355 } else {
356 mState &= ~State::Disabled;
359 if (IsComplete()) {
360 ApplicableStateChanged(!aDisabled);
364 void StyleSheet::SetURLExtraData() {
365 Inner().mURLData =
366 new URLExtraData(GetBaseURI(), GetReferrerInfo(), Principal());
369 nsISupports* StyleSheet::GetRelevantGlobal() const {
370 const StyleSheet& outer = OutermostSheet();
371 return outer.mRelevantGlobal;
374 StyleSheetInfo::StyleSheetInfo(CORSMode aCORSMode,
375 const SRIMetadata& aIntegrity,
376 css::SheetParsingMode aParsingMode)
377 : mPrincipal(NullPrincipal::CreateWithoutOriginAttributes()),
378 mCORSMode(aCORSMode),
379 mReferrerInfo(new ReferrerInfo(nullptr)),
380 mIntegrity(aIntegrity),
381 mContents(Servo_StyleSheet_Empty(aParsingMode).Consume()),
382 mURLData(URLExtraData::Dummy()) {
383 if (!mPrincipal) {
384 MOZ_CRASH("NullPrincipal::Init failed");
386 MOZ_COUNT_CTOR(StyleSheetInfo);
389 StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy, StyleSheet* aPrimarySheet)
390 : mSheetURI(aCopy.mSheetURI),
391 mOriginalSheetURI(aCopy.mOriginalSheetURI),
392 mBaseURI(aCopy.mBaseURI),
393 mPrincipal(aCopy.mPrincipal),
394 mCORSMode(aCopy.mCORSMode),
395 mReferrerInfo(aCopy.mReferrerInfo),
396 mIntegrity(aCopy.mIntegrity),
397 // We don't rebuild the child because we're making a copy without
398 // children.
399 mSourceMapURL(aCopy.mSourceMapURL),
400 mContents(Servo_StyleSheet_Clone(aCopy.mContents.get(), aPrimarySheet)
401 .Consume()),
402 mURLData(aCopy.mURLData)
403 #ifdef DEBUG
405 mPrincipalSet(aCopy.mPrincipalSet)
406 #endif
408 AddSheet(aPrimarySheet);
410 // Our child list is fixed up by our parent.
411 MOZ_COUNT_CTOR(StyleSheetInfo);
414 StyleSheetInfo::~StyleSheetInfo() { MOZ_COUNT_DTOR(StyleSheetInfo); }
416 StyleSheetInfo* StyleSheetInfo::CloneFor(StyleSheet* aPrimarySheet) {
417 return new StyleSheetInfo(*this, aPrimarySheet);
420 MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSheetMallocSizeOf)
421 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSheetMallocEnclosingSizeOf)
423 size_t StyleSheetInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
424 size_t n = aMallocSizeOf(this);
426 n += Servo_StyleSheet_SizeOfIncludingThis(
427 ServoStyleSheetMallocSizeOf, ServoStyleSheetMallocEnclosingSizeOf,
428 mContents);
430 return n;
433 void StyleSheetInfo::AddSheet(StyleSheet* aSheet) {
434 mSheets.AppendElement(aSheet);
437 void StyleSheetInfo::RemoveSheet(StyleSheet* aSheet) {
438 // Fix up the parent pointer in children lists.
439 StyleSheet* newParent =
440 aSheet == mSheets[0] ? mSheets.SafeElementAt(1) : mSheets[0];
441 for (StyleSheet* child : mChildren) {
442 MOZ_ASSERT(child->mParentSheet);
443 MOZ_ASSERT(child->mParentSheet->mInner == this);
444 if (child->mParentSheet == aSheet) {
445 child->mParentSheet = newParent;
449 if (1 == mSheets.Length()) {
450 NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent");
451 delete this;
452 return;
455 mSheets.RemoveElement(aSheet);
458 void StyleSheet::GetType(nsAString& aType) { aType.AssignLiteral("text/css"); }
460 void StyleSheet::GetHref(nsAString& aHref, ErrorResult& aRv) {
461 if (nsIURI* sheetURI = Inner().mOriginalSheetURI) {
462 nsAutoCString str;
463 nsresult rv = sheetURI->GetSpec(str);
464 if (NS_FAILED(rv)) {
465 aRv.Throw(rv);
466 return;
468 CopyUTF8toUTF16(str, aHref);
469 } else {
470 SetDOMStringToNull(aHref);
474 void StyleSheet::GetTitle(nsAString& aTitle) {
475 // From https://drafts.csswg.org/cssom/#dom-stylesheet-title:
477 // The title attribute must return the title or null if title is the empty
478 // string.
480 if (!mTitle.IsEmpty()) {
481 aTitle.Assign(mTitle);
482 } else {
483 SetDOMStringToNull(aTitle);
487 void StyleSheet::WillDirty() {
488 MOZ_ASSERT(!IsReadOnly());
490 if (IsComplete()) {
491 EnsureUniqueInner();
495 void StyleSheet::AddStyleSet(ServoStyleSet* aStyleSet) {
496 MOZ_DIAGNOSTIC_ASSERT(!mStyleSets.Contains(aStyleSet),
497 "style set already registered");
498 mStyleSets.AppendElement(aStyleSet);
501 void StyleSheet::DropStyleSet(ServoStyleSet* aStyleSet) {
502 bool found = mStyleSets.RemoveElement(aStyleSet);
503 MOZ_DIAGNOSTIC_ASSERT(found, "didn't find style set");
504 #ifndef MOZ_DIAGNOSTIC_ASSERT_ENABLED
505 Unused << found;
506 #endif
509 // NOTE(emilio): Composed doc and containing shadow root are set in child sheets
510 // too, so no need to do it for each ancestor.
511 #define NOTIFY(function_, args_) \
512 do { \
513 StyleSheet* current = this; \
514 do { \
515 for (ServoStyleSet * set : current->mStyleSets) { \
516 set->function_ args_; \
518 if (auto* docOrShadow = current->mDocumentOrShadowRoot) { \
519 if (auto* shadow = ShadowRoot::FromNode(docOrShadow->AsNode())) { \
520 shadow->function_ args_; \
521 } else { \
522 docOrShadow->AsNode().AsDocument()->function_ args_; \
525 for (auto* adopter : mAdopters) { \
526 if (auto* shadow = ShadowRoot::FromNode(adopter->AsNode())) { \
527 shadow->function_ args_; \
528 } else { \
529 adopter->AsNode().AsDocument()->function_ args_; \
532 current = current->mParentSheet; \
533 } while (current); \
534 } while (0)
536 void StyleSheet::EnsureUniqueInner() {
537 MOZ_ASSERT(mInner->mSheets.Length() != 0, "unexpected number of outers");
539 if (IsReadOnly()) {
540 // Sheets that can't be modified don't need a unique inner.
541 return;
544 mState |= State::ForcedUniqueInner;
546 if (HasUniqueInner()) {
547 // already unique
548 return;
551 StyleSheetInfo* clone = mInner->CloneFor(this);
552 MOZ_ASSERT(clone);
554 mInner->RemoveSheet(this);
555 mInner = clone;
557 // Fixup the child lists and parent links in the Servo sheet. This is done
558 // here instead of in StyleSheetInner::CloneFor, because it's just more
559 // convenient to do so instead.
560 FixUpAfterInnerClone();
562 // let our containing style sets know that if we call
563 // nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the
564 // document
565 NOTIFY(SheetCloned, (*this));
568 // WebIDL CSSStyleSheet API
570 dom::CSSRuleList* StyleSheet::GetCssRules(nsIPrincipal& aSubjectPrincipal,
571 ErrorResult& aRv) {
572 if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
573 return nullptr;
575 return GetCssRulesInternal();
578 void StyleSheet::GetSourceMapURL(nsACString& aSourceMapURL) {
579 if (!mInner->mSourceMapURL.IsEmpty()) {
580 aSourceMapURL = mInner->mSourceMapURL;
581 return;
583 Servo_StyleSheet_GetSourceMapURL(mInner->mContents, &aSourceMapURL);
586 void StyleSheet::SetSourceMapURL(nsCString&& aSourceMapURL) {
587 mInner->mSourceMapURL = std::move(aSourceMapURL);
590 void StyleSheet::GetSourceURL(nsACString& aSourceURL) {
591 Servo_StyleSheet_GetSourceURL(mInner->mContents, &aSourceURL);
594 css::Rule* StyleSheet::GetDOMOwnerRule() const { return GetOwnerRule(); }
596 // https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule
597 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-insertrule
598 uint32_t StyleSheet::InsertRule(const nsACString& aRule, uint32_t aIndex,
599 nsIPrincipal& aSubjectPrincipal,
600 ErrorResult& aRv) {
601 if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
602 return 0;
605 if (ModificationDisallowed()) {
606 aRv.ThrowNotAllowedError(
607 "This method can only be called on "
608 "modifiable style sheets");
609 return 0;
612 return InsertRuleInternal(aRule, aIndex, aRv);
615 // https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule
616 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-deleterule
617 void StyleSheet::DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
618 ErrorResult& aRv) {
619 if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
620 return;
623 if (ModificationDisallowed()) {
624 return aRv.ThrowNotAllowedError(
625 "This method can only be called on "
626 "modifiable style sheets");
629 return DeleteRuleInternal(aIndex, aRv);
632 int32_t StyleSheet::AddRule(const nsACString& aSelector,
633 const nsACString& aBlock,
634 const Optional<uint32_t>& aIndex,
635 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
636 if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
637 return -1;
640 nsAutoCString rule;
641 rule.Append(aSelector);
642 rule.AppendLiteral(" { ");
643 if (!aBlock.IsEmpty()) {
644 rule.Append(aBlock);
645 rule.Append(' ');
647 rule.Append('}');
649 auto index =
650 aIndex.WasPassed() ? aIndex.Value() : GetCssRulesInternal()->Length();
652 InsertRuleInternal(rule, index, aRv);
653 // Always return -1.
654 return -1;
657 void StyleSheet::MaybeResolveReplacePromise() {
658 MOZ_ASSERT(!!mReplacePromise == ModificationDisallowed());
659 if (!mReplacePromise) {
660 return;
663 SetModificationDisallowed(false);
664 mReplacePromise->MaybeResolve(this);
665 mReplacePromise = nullptr;
668 void StyleSheet::MaybeRejectReplacePromise() {
669 MOZ_ASSERT(!!mReplacePromise == ModificationDisallowed());
670 if (!mReplacePromise) {
671 return;
674 SetModificationDisallowed(false);
675 mReplacePromise->MaybeRejectWithNetworkError(
676 "@import style sheet load failed");
677 mReplacePromise = nullptr;
680 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replace
681 already_AddRefed<dom::Promise> StyleSheet::Replace(const nsACString& aText,
682 ErrorResult& aRv) {
683 nsIGlobalObject* globalObject = nullptr;
684 const StyleSheet& outer = OutermostSheet();
685 if (outer.mRelevantGlobal) {
686 globalObject = outer.mRelevantGlobal;
687 } else if (Document* doc = outer.GetAssociatedDocument()) {
688 globalObject = doc->GetScopeObject();
691 RefPtr<dom::Promise> promise = dom::Promise::Create(globalObject, aRv);
692 if (!promise) {
693 return nullptr;
696 // Step 1 and 4 are variable declarations
698 // 2.1 Check if sheet is constructed, else reject promise.
699 if (!IsConstructed()) {
700 promise->MaybeRejectWithNotAllowedError(
701 "This method can only be called on "
702 "constructed style sheets");
703 return promise.forget();
706 // 2.2 Check if sheet is modifiable, else throw.
707 if (ModificationDisallowed()) {
708 promise->MaybeRejectWithNotAllowedError(
709 "This method can only be called on "
710 "modifiable style sheets");
711 return promise.forget();
714 // 3. Disallow modifications until finished.
715 SetModificationDisallowed(true);
717 // TODO(emilio, 1642227): Should constructable stylesheets notify global
718 // observers (i.e., set mMustNotify to true)?
719 auto* loader = mConstructorDocument->CSSLoader();
720 auto loadData = MakeRefPtr<css::SheetLoadData>(
721 loader, /* aURI = */ nullptr, this, css::SyncLoad::No,
722 css::Loader::UseSystemPrincipal::No, css::StylePreloadKind::None,
723 /* aPreloadEncoding */ nullptr, /* aObserver */ nullptr,
724 mConstructorDocument->NodePrincipal(), GetReferrerInfo(),
725 /* aNonce */ u""_ns);
727 // In parallel
728 // 5.1 Parse aText into rules.
729 // 5.2 Load import rules, throw NetworkError if failed.
730 // 5.3 Set sheet's rules to new rules.
731 nsISerialEventTarget* target = GetMainThreadSerialEventTarget();
732 loadData->mIsBeingParsed = true;
733 MOZ_ASSERT(!mReplacePromise);
734 mReplacePromise = promise;
735 ParseSheet(*loader, aText, *loadData)
736 ->Then(
737 target, __func__,
738 [loadData] { loadData->SheetFinishedParsingAsync(); },
739 [] { MOZ_CRASH("This MozPromise should never be rejected."); });
741 // 6. Return the promise
742 return promise.forget();
745 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync
746 void StyleSheet::ReplaceSync(const nsACString& aText, ErrorResult& aRv) {
747 // Step 1 is a variable declaration
749 // 2.1 Check if sheet is constructed, else throw.
750 if (!IsConstructed()) {
751 return aRv.ThrowNotAllowedError(
752 "Can only be called on constructed style sheets");
755 // 2.2 Check if sheet is modifiable, else throw.
756 if (ModificationDisallowed()) {
757 return aRv.ThrowNotAllowedError(
758 "Can only be called on modifiable style sheets");
761 // 3. Parse aText into rules.
762 // 4. If rules contain @imports, skip them and continue parsing.
763 auto* loader = mConstructorDocument->CSSLoader();
764 SetURLExtraData();
765 RefPtr<const StyleStylesheetContents> rawContent =
766 Servo_StyleSheet_FromUTF8Bytes(
767 loader, this,
768 /* load_data = */ nullptr, &aText, mParsingMode, URLData(),
769 mConstructorDocument->GetCompatibilityMode(),
770 /* reusable_sheets = */ nullptr,
771 mConstructorDocument->GetStyleUseCounters(),
772 StyleAllowImportRules::No, StyleSanitizationKind::None,
773 /* sanitized_output = */ nullptr)
774 .Consume();
776 // 5. Set sheet's rules to the new rules.
777 Inner().mContents = std::move(rawContent);
778 FixUpRuleListAfterContentsChangeIfNeeded();
779 RuleChanged(nullptr, StyleRuleChangeKind::Generic);
782 nsresult StyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup,
783 uint32_t aIndex) {
784 NS_ENSURE_ARG_POINTER(aGroup);
785 NS_ASSERTION(IsComplete(), "No deleting from an incomplete sheet!");
786 RefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
787 NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
789 // check that the rule actually belongs to this sheet!
790 if (this != rule->GetStyleSheet()) {
791 return NS_ERROR_INVALID_ARG;
794 if (IsReadOnly()) {
795 return NS_OK;
798 WillDirty();
800 nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
801 NS_ENSURE_SUCCESS(result, result);
803 rule->DropReferences();
805 RuleRemoved(*rule);
806 return NS_OK;
809 void StyleSheet::RuleAdded(css::Rule& aRule) {
810 SetModifiedRules();
811 NOTIFY(RuleAdded, (*this, aRule));
814 void StyleSheet::RuleRemoved(css::Rule& aRule) {
815 SetModifiedRules();
816 NOTIFY(RuleRemoved, (*this, aRule));
819 void StyleSheet::RuleChanged(css::Rule* aRule, StyleRuleChangeKind aKind) {
820 MOZ_ASSERT(!aRule || HasUniqueInner(),
821 "Shouldn't have mutated a shared sheet");
822 SetModifiedRules();
823 NOTIFY(RuleChanged, (*this, aRule, aKind));
826 // nsICSSLoaderObserver implementation
827 NS_IMETHODIMP
828 StyleSheet::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
829 nsresult aStatus) {
830 if (!aSheet->GetParentSheet()) {
831 return NS_OK; // ignore if sheet has been detached already
833 MOZ_ASSERT(this == aSheet->GetParentSheet(),
834 "We are being notified of a sheet load for a sheet that is not "
835 "our child!");
836 if (NS_FAILED(aStatus)) {
837 return NS_OK;
840 MOZ_ASSERT(aSheet->GetOwnerRule());
841 NOTIFY(ImportRuleLoaded, (*aSheet->GetOwnerRule(), *aSheet));
842 return NS_OK;
845 #undef NOTIFY
847 nsresult StyleSheet::InsertRuleIntoGroup(const nsACString& aRule,
848 css::GroupRule* aGroup,
849 uint32_t aIndex) {
850 NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!");
851 // check that the group actually belongs to this sheet!
852 if (this != aGroup->GetStyleSheet()) {
853 return NS_ERROR_INVALID_ARG;
856 if (IsReadOnly()) {
857 return NS_OK;
860 if (ModificationDisallowed()) {
861 return NS_ERROR_DOM_NOT_ALLOWED_ERR;
864 WillDirty();
866 nsresult result = InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
867 NS_ENSURE_SUCCESS(result, result);
868 RuleAdded(*aGroup->GetStyleRuleAt(aIndex));
869 return NS_OK;
872 uint64_t StyleSheet::FindOwningWindowInnerID() const {
873 uint64_t windowID = 0;
874 if (Document* doc = GetAssociatedDocument()) {
875 windowID = doc->InnerWindowID();
878 if (windowID == 0 && mOwningNode) {
879 windowID = mOwningNode->OwnerDoc()->InnerWindowID();
882 RefPtr<css::Rule> ownerRule;
883 if (windowID == 0 && (ownerRule = GetDOMOwnerRule())) {
884 RefPtr<StyleSheet> sheet = ownerRule->GetStyleSheet();
885 if (sheet) {
886 windowID = sheet->FindOwningWindowInnerID();
890 if (windowID == 0 && mParentSheet) {
891 windowID = mParentSheet->FindOwningWindowInnerID();
894 return windowID;
897 void StyleSheet::RemoveFromParent() {
898 if (!mParentSheet) {
899 return;
902 MOZ_ASSERT(mParentSheet->ChildSheets().Contains(this));
903 mParentSheet->Inner().mChildren.RemoveElement(this);
904 mParentSheet = nullptr;
907 void StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
908 ErrorResult& aRv) {
909 StyleSheetInfo& info = Inner();
911 if (aSubjectPrincipal.Subsumes(info.mPrincipal)) {
912 return;
915 // Allow access only if CORS mode is not NONE and the security flag
916 // is not turned off.
917 if (GetCORSMode() == CORS_NONE && !nsContentUtils::BypassCSSOMOriginCheck()) {
918 aRv.ThrowSecurityError("Not allowed to access cross-origin stylesheet");
919 return;
922 // Now make sure we set the principal of our inner to the subjectPrincipal.
923 // We do this because we're in a situation where the caller would not normally
924 // be able to access the sheet, but the sheet has opted in to being read.
925 // Unfortunately, that means it's also opted in to being _edited_, and if the
926 // caller now makes edits to the sheet we want the resulting resource loads,
927 // if any, to look as if they are coming from the caller's principal, not the
928 // original sheet principal.
930 // That means we need a unique inner, of course. But we don't want to do that
931 // if we're not complete yet. Luckily, all the callers of this method throw
932 // anyway if not complete, so we can just do that here too.
933 if (!IsComplete()) {
934 aRv.ThrowInvalidAccessError(
935 "Not allowed to access still-loading stylesheet");
936 return;
939 WillDirty();
941 info.mPrincipal = &aSubjectPrincipal;
944 bool StyleSheet::AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
945 ErrorResult& aRv) {
946 // Rules are not available on incomplete sheets.
947 if (!IsComplete()) {
948 aRv.ThrowInvalidAccessError(
949 "Can't access rules of still-loading style sheet");
950 return false;
952 //-- Security check: Only scripts whose principal subsumes that of the
953 // style sheet can access rule collections.
954 SubjectSubsumesInnerPrincipal(aSubjectPrincipal, aRv);
955 if (NS_WARN_IF(aRv.Failed())) {
956 return false;
958 return true;
961 void StyleSheet::SetAssociatedDocumentOrShadowRoot(
962 DocumentOrShadowRoot* aDocOrShadowRoot) {
963 MOZ_ASSERT(!IsConstructed());
964 MOZ_ASSERT(!mParentSheet || !aDocOrShadowRoot,
965 "Shouldn't be set on child sheets");
967 // not ref counted
968 mDocumentOrShadowRoot = aDocOrShadowRoot;
970 if (Document* doc = GetAssociatedDocument()) {
971 MOZ_ASSERT(!mRelevantGlobal);
972 mRelevantGlobal = doc->GetScopeObject();
976 void StyleSheet::AppendStyleSheet(StyleSheet& aSheet) {
977 WillDirty();
978 AppendStyleSheetSilently(aSheet);
981 void StyleSheet::AppendStyleSheetSilently(StyleSheet& aSheet) {
982 MOZ_ASSERT(!IsReadOnly());
984 Inner().mChildren.AppendElement(&aSheet);
986 // This is not reference counted. Our parent tells us when
987 // it's going away.
988 aSheet.mParentSheet = this;
991 size_t StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
992 size_t n = 0;
993 n += aMallocSizeOf(this);
995 // We want to measure the inner with only one of the children, and it makes
996 // sense for it to be the latest as it is the most likely to be reachable.
997 if (Inner().mSheets.LastElement() == this) {
998 n += Inner().SizeOfIncludingThis(aMallocSizeOf);
1001 // Measurement of the following members may be added later if DMD finds it
1002 // is worthwhile:
1003 // - mTitle
1004 // - mMedia
1005 // - mStyleSets
1006 // - mRuleList
1008 return n;
1011 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
1012 void StyleSheet::List(FILE* aOut, int32_t aIndent) {
1013 for (StyleSheet* child : ChildSheets()) {
1014 child->List(aOut, aIndent);
1017 nsCString line;
1018 for (int i = 0; i < aIndent; ++i) {
1019 line.AppendLiteral(" ");
1022 line.AppendLiteral("/* ");
1024 nsCString url;
1025 GetSheetURI()->GetSpec(url);
1026 if (url.IsEmpty()) {
1027 line.AppendLiteral("(no URL)");
1028 } else {
1029 line.Append(url);
1032 line.AppendLiteral(" (");
1034 switch (GetOrigin()) {
1035 case StyleOrigin::UserAgent:
1036 line.AppendLiteral("User Agent");
1037 break;
1038 case StyleOrigin::User:
1039 line.AppendLiteral("User");
1040 break;
1041 case StyleOrigin::Author:
1042 line.AppendLiteral("Author");
1043 break;
1046 if (mMedia) {
1047 nsAutoCString buffer;
1048 mMedia->GetText(buffer);
1050 if (!buffer.IsEmpty()) {
1051 line.AppendLiteral(", ");
1052 line.Append(buffer);
1056 line.AppendLiteral(") */");
1058 fprintf_stderr(aOut, "%s\n\n", line.get());
1060 nsCString newlineIndent;
1061 newlineIndent.Append('\n');
1062 for (int i = 0; i < aIndent; ++i) {
1063 newlineIndent.AppendLiteral(" ");
1066 ServoCSSRuleList* ruleList = GetCssRulesInternal();
1067 for (uint32_t i = 0, len = ruleList->Length(); i < len; ++i) {
1068 css::Rule* rule = ruleList->GetRule(i);
1070 nsAutoCString cssText;
1071 rule->GetCssText(cssText);
1072 cssText.ReplaceSubstring("\n"_ns, newlineIndent);
1073 fprintf_stderr(aOut, "%s\n", cssText.get());
1076 if (ruleList->Length() != 0) {
1077 fprintf_stderr(aOut, "\n");
1080 #endif
1082 void StyleSheet::SetMedia(already_AddRefed<dom::MediaList> aMedia) {
1083 mMedia = aMedia;
1084 if (mMedia) {
1085 mMedia->SetStyleSheet(this);
1089 void StyleSheet::DropMedia() {
1090 if (mMedia) {
1091 mMedia->SetStyleSheet(nullptr);
1092 mMedia = nullptr;
1096 dom::MediaList* StyleSheet::Media() {
1097 if (!mMedia) {
1098 mMedia = dom::MediaList::Create(EmptyCString());
1099 mMedia->SetStyleSheet(this);
1102 return mMedia;
1105 // nsWrapperCache
1107 JSObject* StyleSheet::WrapObject(JSContext* aCx,
1108 JS::Handle<JSObject*> aGivenProto) {
1109 return dom::CSSStyleSheet_Binding::Wrap(aCx, this, aGivenProto);
1112 void StyleSheet::FixUpRuleListAfterContentsChangeIfNeeded(bool aFromClone) {
1113 if (!mRuleList) {
1114 return;
1117 RefPtr<StyleLockedCssRules> rules =
1118 Servo_StyleSheet_GetRules(Inner().mContents.get()).Consume();
1119 mRuleList->SetRawContents(std::move(rules), aFromClone);
1122 void StyleSheet::FixUpAfterInnerClone() {
1123 MOZ_ASSERT(Inner().mSheets.Length() == 1, "Should've just cloned");
1124 MOZ_ASSERT(Inner().mSheets[0] == this);
1125 MOZ_ASSERT(Inner().mChildren.IsEmpty());
1127 FixUpRuleListAfterContentsChangeIfNeeded(/* aFromClone = */ true);
1129 RefPtr<StyleLockedCssRules> rules =
1130 Servo_StyleSheet_GetRules(Inner().mContents.get()).Consume();
1131 uint32_t index = 0;
1132 while (true) {
1133 uint32_t line, column; // Actually unused.
1134 RefPtr<StyleLockedImportRule> import =
1135 Servo_CssRules_GetImportRuleAt(rules, index, &line, &column).Consume();
1136 if (!import) {
1137 // Note that only @charset rules come before @import rules, and @charset
1138 // rules are parsed but skipped, so we can stop iterating as soon as we
1139 // find something that isn't an @import rule.
1140 break;
1142 auto* sheet = const_cast<StyleSheet*>(Servo_ImportRule_GetSheet(import));
1143 MOZ_ASSERT(sheet);
1144 AppendStyleSheetSilently(*sheet);
1145 index++;
1149 already_AddRefed<StyleSheet> StyleSheet::CreateEmptyChildSheet(
1150 already_AddRefed<dom::MediaList> aMediaList) const {
1151 RefPtr<StyleSheet> child =
1152 new StyleSheet(ParsingMode(), CORSMode::CORS_NONE, SRIMetadata());
1154 child->mMedia = aMediaList;
1155 return child.forget();
1158 // We disable parallel stylesheet parsing if any of the following three
1159 // conditions hold:
1161 // (1) The pref is off.
1162 // (2) The browser is recording CSS errors (which parallel parsing can't
1163 // handle).
1164 // (3) The stylesheet is a chrome stylesheet, since those can use
1165 // -moz-bool-pref, which needs to access the pref service, which is not
1166 // threadsafe.
1167 static bool AllowParallelParse(css::Loader& aLoader, URLExtraData* aUrlData) {
1168 // If the browser is recording CSS errors, we need to use the sequential path
1169 // because the parallel path doesn't support that.
1170 Document* doc = aLoader.GetDocument();
1171 if (doc && css::ErrorReporter::ShouldReportErrors(*doc)) {
1172 return false;
1175 // If this is a chrome stylesheet, it might use -moz-bool-pref, which needs to
1176 // access the pref service, which is not thread-safe. We could probably expose
1177 // the relevant booleans as thread-safe var caches if we needed to, but
1178 // parsing chrome stylesheets in parallel is unlikely to be a win anyway.
1180 // Note that UA stylesheets can also use -moz-bool-pref, but those are always
1181 // parsed sync.
1182 if (aUrlData->ChromeRulesEnabled()) {
1183 return false;
1186 return true;
1189 RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
1190 css::Loader& aLoader, const nsACString& aBytes,
1191 css::SheetLoadData& aLoadData) {
1192 MOZ_ASSERT(mParsePromise.IsEmpty());
1193 RefPtr<StyleSheetParsePromise> p = mParsePromise.Ensure(__func__);
1194 if (!aLoadData.ShouldDefer()) {
1195 mParsePromise.SetTaskPriority(nsIRunnablePriority::PRIORITY_RENDER_BLOCKING,
1196 __func__);
1198 SetURLExtraData();
1200 // @import rules are disallowed due to this decision:
1201 // https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418
1202 // We may allow @import rules again in the future.
1203 auto allowImportRules = SelfOrAncestorIsConstructed()
1204 ? StyleAllowImportRules::No
1205 : StyleAllowImportRules::Yes;
1206 URLExtraData* urlData = URLData();
1207 const bool shouldRecordCounters =
1208 aLoader.GetDocument() && aLoader.GetDocument()->GetStyleUseCounters() &&
1209 !urlData->ChromeRulesEnabled();
1210 if (!AllowParallelParse(aLoader, urlData)) {
1211 UniquePtr<StyleUseCounters> counters;
1212 if (shouldRecordCounters) {
1213 counters.reset(Servo_UseCounters_Create());
1216 RefPtr<StyleStylesheetContents> contents =
1217 Servo_StyleSheet_FromUTF8Bytes(
1218 &aLoader, this, &aLoadData, &aBytes, mParsingMode, urlData,
1219 aLoadData.mCompatMode,
1220 /* reusable_sheets = */ nullptr, counters.get(), allowImportRules,
1221 StyleSanitizationKind::None,
1222 /* sanitized_output = */ nullptr)
1223 .Consume();
1224 FinishAsyncParse(contents.forget(), std::move(counters));
1225 } else {
1226 auto holder = MakeRefPtr<css::SheetLoadDataHolder>(__func__, &aLoadData);
1227 Servo_StyleSheet_FromUTF8BytesAsync(holder, urlData, &aBytes, mParsingMode,
1228 aLoadData.mCompatMode,
1229 shouldRecordCounters, allowImportRules);
1232 return p;
1235 void StyleSheet::FinishAsyncParse(
1236 already_AddRefed<StyleStylesheetContents> aSheetContents,
1237 UniquePtr<StyleUseCounters> aUseCounters) {
1238 MOZ_ASSERT(NS_IsMainThread());
1239 MOZ_ASSERT(!mParsePromise.IsEmpty());
1240 Inner().mContents = aSheetContents;
1241 Inner().mUseCounters = std::move(aUseCounters);
1242 mParsePromise.Resolve(true, __func__);
1245 void StyleSheet::ParseSheetSync(
1246 css::Loader* aLoader, const nsACString& aBytes,
1247 css::SheetLoadData* aLoadData,
1248 css::LoaderReusableStyleSheets* aReusableSheets) {
1249 const nsCompatibility compatMode = [&] {
1250 if (aLoadData) {
1251 return aLoadData->mCompatMode;
1253 if (aLoader) {
1254 return aLoader->CompatMode(css::StylePreloadKind::None);
1256 return eCompatibility_FullStandards;
1257 }();
1259 SetURLExtraData();
1261 URLExtraData* urlData = URLData();
1262 const StyleUseCounters* useCounters =
1263 aLoader && aLoader->GetDocument() && !urlData->ChromeRulesEnabled()
1264 ? aLoader->GetDocument()->GetStyleUseCounters()
1265 : nullptr;
1267 auto allowImportRules = SelfOrAncestorIsConstructed()
1268 ? StyleAllowImportRules::No
1269 : StyleAllowImportRules::Yes;
1271 Inner().mContents = Servo_StyleSheet_FromUTF8Bytes(
1272 aLoader, this, aLoadData, &aBytes, mParsingMode,
1273 urlData, compatMode, aReusableSheets, useCounters,
1274 allowImportRules, StyleSanitizationKind::None,
1275 /* sanitized_output = */ nullptr)
1276 .Consume();
1279 void StyleSheet::ReparseSheet(const nsACString& aInput, ErrorResult& aRv) {
1280 if (!IsComplete()) {
1281 return aRv.ThrowInvalidAccessError("Cannot reparse still-loading sheet");
1284 // Allowing to modify UA sheets is dangerous (in the sense that C++ code
1285 // relies on rules in those sheets), plus they're probably going to be shared
1286 // across processes in which case this is directly a no-go.
1287 if (IsReadOnly()) {
1288 return;
1291 // Hold strong ref to the CSSLoader in case the document update
1292 // kills the document
1293 RefPtr<css::Loader> loader;
1294 if (Document* doc = GetAssociatedDocument()) {
1295 loader = doc->CSSLoader();
1296 NS_ASSERTION(loader, "Document with no CSS loader!");
1297 } else {
1298 loader = new css::Loader;
1301 WillDirty();
1303 // cache child sheets to reuse
1304 css::LoaderReusableStyleSheets reusableSheets;
1305 for (StyleSheet* child : ChildSheets()) {
1306 if (child->GetOriginalURI()) {
1307 reusableSheets.AddReusableSheet(child);
1311 // Clean up child sheets list.
1312 for (StyleSheet* child : ChildSheets()) {
1313 child->mParentSheet = nullptr;
1315 Inner().mChildren.Clear();
1317 // Notify to the stylesets about the old rules going away.
1319 ServoCSSRuleList* ruleList = GetCssRulesInternal();
1320 MOZ_ASSERT(ruleList);
1322 uint32_t ruleCount = ruleList->Length();
1323 for (uint32_t i = 0; i < ruleCount; ++i) {
1324 css::Rule* rule = ruleList->GetRule(i);
1325 MOZ_ASSERT(rule);
1326 RuleRemoved(*rule);
1329 // We need to clear the rule list here (rather than after parsing) because
1330 // ParseSheetSync may reuse child sheets, which would cause us to end up
1331 // with a wrong mChilden array.
1332 ruleList->SetRawContents(nullptr, /* aFromClone = */ false);
1335 ParseSheetSync(loader, aInput, /* aLoadData = */ nullptr, &reusableSheets);
1337 FixUpRuleListAfterContentsChangeIfNeeded();
1339 // Notify the stylesets about the new rules.
1341 // Get the rule list (which will need to be regenerated after ParseSheet).
1342 ServoCSSRuleList* ruleList = GetCssRulesInternal();
1343 MOZ_ASSERT(ruleList);
1345 uint32_t ruleCount = ruleList->Length();
1346 for (uint32_t i = 0; i < ruleCount; ++i) {
1347 css::Rule* rule = ruleList->GetRule(i);
1348 MOZ_ASSERT(rule);
1349 RuleAdded(*rule);
1353 // Our rules are no longer considered modified for devtools.
1354 mState &= ~State::ModifiedRulesForDevtools;
1357 void StyleSheet::DropRuleList() {
1358 if (mRuleList) {
1359 mRuleList->DropReferences();
1360 mRuleList = nullptr;
1364 already_AddRefed<StyleSheet> StyleSheet::Clone(
1365 StyleSheet* aCloneParent,
1366 dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot) const {
1367 MOZ_ASSERT(!IsConstructed(),
1368 "Cannot create a non-constructed sheet from a constructed sheet");
1369 RefPtr<StyleSheet> clone =
1370 new StyleSheet(*this, aCloneParent, aCloneDocumentOrShadowRoot,
1371 /* aConstructorDocToUse */ nullptr);
1372 return clone.forget();
1375 already_AddRefed<StyleSheet> StyleSheet::CloneAdoptedSheet(
1376 Document& aConstructorDocument) const {
1377 MOZ_ASSERT(IsConstructed(),
1378 "Cannot create a constructed sheet from a non-constructed sheet");
1379 MOZ_ASSERT(aConstructorDocument.IsStaticDocument(),
1380 "Should never clone adopted sheets for a non-static document");
1381 RefPtr<StyleSheet> clone = new StyleSheet(*this,
1382 /* aParentSheetToUse */ nullptr,
1383 /* aDocOrShadowRootToUse */ nullptr,
1384 &aConstructorDocument);
1385 return clone.forget();
1388 ServoCSSRuleList* StyleSheet::GetCssRulesInternal() {
1389 if (!mRuleList) {
1390 // TODO(emilio): This should go away, but we need to fix the CC setup for
1391 // @import rules first, see bug 1719963.
1392 EnsureUniqueInner();
1394 RefPtr<StyleLockedCssRules> rawRules =
1395 Servo_StyleSheet_GetRules(Inner().mContents).Consume();
1396 MOZ_ASSERT(rawRules);
1397 mRuleList = new ServoCSSRuleList(rawRules.forget(), this, nullptr);
1399 return mRuleList;
1402 uint32_t StyleSheet::InsertRuleInternal(const nsACString& aRule,
1403 uint32_t aIndex, ErrorResult& aRv) {
1404 MOZ_ASSERT(!IsReadOnly());
1405 MOZ_ASSERT(!ModificationDisallowed());
1407 // Ensure mRuleList is constructed.
1408 GetCssRulesInternal();
1410 aRv = mRuleList->InsertRule(aRule, aIndex);
1411 if (aRv.Failed()) {
1412 return 0;
1415 // XXX We may not want to get the rule when stylesheet change event
1416 // is not enabled.
1417 css::Rule* rule = mRuleList->GetRule(aIndex);
1418 RuleAdded(*rule);
1420 return aIndex;
1423 void StyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv) {
1424 MOZ_ASSERT(!IsReadOnly());
1425 MOZ_ASSERT(!ModificationDisallowed());
1427 // Ensure mRuleList is constructed.
1428 GetCssRulesInternal();
1429 if (aIndex >= mRuleList->Length()) {
1430 aRv.ThrowIndexSizeError(
1431 nsPrintfCString("Cannot delete rule at index %u"
1432 " because the number of rules is only %u",
1433 aIndex, mRuleList->Length()));
1434 return;
1437 // Hold a strong ref to the rule so it doesn't die when we remove it
1438 // from the list. XXX We may not want to hold it if stylesheet change
1439 // event is not enabled.
1440 RefPtr<css::Rule> rule = mRuleList->GetRule(aIndex);
1441 aRv = mRuleList->DeleteRule(aIndex);
1442 if (!aRv.Failed()) {
1443 RuleRemoved(*rule);
1447 nsresult StyleSheet::InsertRuleIntoGroupInternal(const nsACString& aRule,
1448 css::GroupRule* aGroup,
1449 uint32_t aIndex) {
1450 MOZ_ASSERT(!IsReadOnly());
1452 ServoCSSRuleList* rules = aGroup->CssRules();
1453 MOZ_ASSERT(rules && rules->GetParentRule() == aGroup);
1454 return rules->InsertRule(aRule, aIndex);
1457 StyleOrigin StyleSheet::GetOrigin() const {
1458 return Servo_StyleSheet_GetOrigin(Inner().mContents);
1461 void StyleSheet::SetSharedContents(const StyleLockedCssRules* aSharedRules) {
1462 MOZ_ASSERT(!IsComplete());
1464 SetURLExtraData();
1466 Inner().mContents =
1467 Servo_StyleSheet_FromSharedData(URLData(), aSharedRules).Consume();
1470 const StyleLockedCssRules* StyleSheet::ToShared(
1471 StyleSharedMemoryBuilder* aBuilder, nsCString& aErrorMessage) {
1472 // Assert some things we assume when creating a StyleSheet using shared
1473 // memory.
1474 MOZ_ASSERT(GetReferrerInfo()->ReferrerPolicy() == ReferrerPolicy::_empty);
1475 MOZ_ASSERT(GetReferrerInfo()->GetSendReferrer());
1476 MOZ_ASSERT(!nsCOMPtr<nsIURI>(GetReferrerInfo()->GetComputedReferrer()));
1477 MOZ_ASSERT(GetCORSMode() == CORS_NONE);
1478 MOZ_ASSERT(Inner().mIntegrity.IsEmpty());
1479 MOZ_ASSERT(Principal()->IsSystemPrincipal());
1481 const StyleLockedCssRules* rules = Servo_SharedMemoryBuilder_AddStylesheet(
1482 aBuilder, Inner().mContents, &aErrorMessage);
1484 #ifdef DEBUG
1485 if (!rules) {
1486 // Print the ToShmem error message so that developers know what to fix.
1487 printf_stderr("%s\n", aErrorMessage.get());
1488 MOZ_CRASH("UA style sheet contents failed shared memory requirements");
1490 #endif
1492 return rules;
1495 bool StyleSheet::IsReadOnly() const {
1496 return IsComplete() && GetOrigin() == StyleOrigin::UserAgent;
1499 } // namespace mozilla