Bug 1700051: part 48) Slightly simplify `mozInlineSpellWordUtil::FindRealWordContaini...
[gecko.git] / layout / style / StyleSheet.cpp
blobe1c6ec4eb65dd7c09bf86cce7b962f998c417fa5
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/ShadowRoot.h"
19 #include "mozilla/dom/ShadowRootBinding.h"
20 #include "mozilla/NullPrincipal.h"
21 #include "mozilla/ServoBindings.h"
22 #include "mozilla/ServoCSSRuleList.h"
23 #include "mozilla/ServoStyleSet.h"
24 #include "mozilla/StaticPrefs_layout.h"
25 #include "mozilla/StyleSheetInlines.h"
26 #include "mozilla/css/SheetLoadData.h"
28 #include "mozAutoDocUpdate.h"
29 #include "SheetLoadData.h"
31 namespace mozilla {
33 using namespace dom;
35 StyleSheet::StyleSheet(css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
36 const dom::SRIMetadata& aIntegrity)
37 : mParentSheet(nullptr),
38 mRelevantGlobal(nullptr),
39 mConstructorDocument(nullptr),
40 mDocumentOrShadowRoot(nullptr),
41 mOwningNode(nullptr),
42 mOwnerRule(nullptr),
43 mParsingMode(aParsingMode),
44 mState(static_cast<State>(0)),
45 mInner(new StyleSheetInfo(aCORSMode, aIntegrity, aParsingMode)) {
46 mInner->AddSheet(this);
49 StyleSheet::StyleSheet(const StyleSheet& aCopy, StyleSheet* aParentSheetToUse,
50 dom::CSSImportRule* aOwnerRuleToUse,
51 dom::DocumentOrShadowRoot* aDocOrShadowRootToUse,
52 dom::Document* aConstructorDocToUse,
53 nsINode* aOwningNodeToUse)
54 : mParentSheet(aParentSheetToUse),
55 mRelevantGlobal(nullptr),
56 mConstructorDocument(aConstructorDocToUse),
57 mTitle(aCopy.mTitle),
58 mDocumentOrShadowRoot(aDocOrShadowRootToUse),
59 mOwningNode(aOwningNodeToUse),
60 mOwnerRule(aOwnerRuleToUse),
61 mParsingMode(aCopy.mParsingMode),
62 mState(aCopy.mState),
63 // Shallow copy, but concrete subclasses will fix up.
64 mInner(aCopy.mInner) {
65 MOZ_ASSERT(!aConstructorDocToUse || aCopy.IsConstructed());
66 MOZ_ASSERT(!aConstructorDocToUse || !aDocOrShadowRootToUse,
67 "Should never have both of these together.");
68 MOZ_ASSERT(mInner, "Should only copy StyleSheets with an mInner.");
69 mInner->AddSheet(this);
70 // CSSOM's been there, force full copy now.
71 if (HasForcedUniqueInner()) {
72 MOZ_ASSERT(IsComplete(),
73 "Why have rules been accessed on an incomplete sheet?");
74 EnsureUniqueInner();
75 // But CSSOM hasn't been on _this_ stylesheet yet, so no need to clone
76 // ourselves.
77 mState &= ~(State::ForcedUniqueInner | State::ModifiedRules |
78 State::ModifiedRulesForDevtools);
81 if (aCopy.mMedia) {
82 // XXX This is wrong; we should be keeping @import rules and
83 // sheets in sync!
84 mMedia = aCopy.mMedia->Clone();
88 /* static */
89 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-cssstylesheet
90 already_AddRefed<StyleSheet> StyleSheet::Constructor(
91 const dom::GlobalObject& aGlobal, const dom::CSSStyleSheetInit& aOptions,
92 ErrorResult& aRv) {
93 nsCOMPtr<nsPIDOMWindowInner> window =
94 do_QueryInterface(aGlobal.GetAsSupports());
96 if (!window) {
97 aRv.ThrowNotSupportedError("Not supported when there is no document");
98 return nullptr;
101 Document* constructorDocument = window->GetExtantDoc();
102 if (!constructorDocument) {
103 aRv.ThrowNotSupportedError("Not supported when there is no document");
104 return nullptr;
107 // 1. Construct a sheet and set its properties (see spec).
108 auto sheet =
109 MakeRefPtr<StyleSheet>(css::SheetParsingMode::eAuthorSheetFeatures,
110 CORSMode::CORS_NONE, dom::SRIMetadata());
112 // baseURL not yet in the spec. Implemented based on the following discussion:
113 // https://github.com/WICG/construct-stylesheets/issues/95#issuecomment-594217180
114 RefPtr<nsIURI> baseURI;
115 if (!aOptions.mBaseURL.WasPassed()) {
116 baseURI = constructorDocument->GetBaseURI();
117 } else {
118 nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aOptions.mBaseURL.Value(),
119 nullptr, constructorDocument->GetBaseURI());
120 if (NS_FAILED(rv)) {
121 aRv.ThrowNotAllowedError(
122 "Constructed style sheets must have a valid base URL");
123 return nullptr;
127 nsIURI* sheetURI = constructorDocument->GetDocumentURI();
128 nsIURI* originalURI = nullptr;
129 sheet->SetURIs(sheetURI, originalURI, baseURI);
131 sheet->SetPrincipal(constructorDocument->NodePrincipal());
132 sheet->SetReferrerInfo(constructorDocument->GetReferrerInfo());
133 sheet->mConstructorDocument = constructorDocument;
134 if (constructorDocument) {
135 sheet->mRelevantGlobal = constructorDocument->GetParentObject();
138 // 2. Set the sheet's media according to aOptions.
139 if (aOptions.mMedia.IsUTF8String()) {
140 sheet->SetMedia(MediaList::Create(aOptions.mMedia.GetAsUTF8String()));
141 } else {
142 sheet->SetMedia(aOptions.mMedia.GetAsMediaList()->Clone());
145 // 3. Set the sheet's disabled flag according to aOptions.
146 sheet->SetDisabled(aOptions.mDisabled);
147 sheet->SetComplete();
149 // 4. Return sheet.
150 return sheet.forget();
153 StyleSheet::~StyleSheet() {
154 MOZ_ASSERT(!mInner, "Inner should have been dropped in LastRelease");
157 bool StyleSheet::HasRules() const {
158 return Servo_StyleSheet_HasRules(Inner().mContents);
161 Document* StyleSheet::GetAssociatedDocument() const {
162 auto* associated = GetAssociatedDocumentOrShadowRoot();
163 return associated ? associated->AsNode().OwnerDoc() : nullptr;
166 dom::DocumentOrShadowRoot* StyleSheet::GetAssociatedDocumentOrShadowRoot()
167 const {
168 const StyleSheet& outer = OutermostSheet();
169 if (outer.mDocumentOrShadowRoot) {
170 return outer.mDocumentOrShadowRoot;
172 if (outer.IsConstructed()) {
173 return outer.mConstructorDocument;
175 return nullptr;
178 Document* StyleSheet::GetKeptAliveByDocument() const {
179 const StyleSheet& outer = OutermostSheet();
180 if (outer.mDocumentOrShadowRoot) {
181 return outer.mDocumentOrShadowRoot->AsNode().GetComposedDoc();
183 if (outer.IsConstructed()) {
184 for (DocumentOrShadowRoot* adopter : outer.mAdopters) {
185 MOZ_ASSERT(adopter->AsNode().OwnerDoc() == outer.mConstructorDocument);
186 if (adopter->AsNode().IsInComposedDoc()) {
187 return outer.mConstructorDocument.get();
191 return nullptr;
194 void StyleSheet::LastRelease() {
195 MOZ_ASSERT(mInner, "Should have an mInner at time of destruction.");
196 MOZ_ASSERT(mInner->mSheets.Contains(this), "Our mInner should include us.");
197 MOZ_DIAGNOSTIC_ASSERT(mAdopters.IsEmpty(),
198 "Should have no adopters at time of destruction.");
200 mInner->RemoveSheet(this);
201 mInner = nullptr;
203 DropMedia();
204 DropRuleList();
207 void StyleSheet::UnlinkInner() {
208 // We can only have a cycle through our inner if we have a unique inner,
209 // because otherwise there are no JS wrappers for anything in the inner.
210 if (mInner->mSheets.Length() != 1) {
211 return;
214 for (StyleSheet* child : ChildSheets()) {
215 MOZ_ASSERT(child->mParentSheet == this, "We have a unique inner!");
216 child->mParentSheet = nullptr;
218 Inner().mChildren.Clear();
221 void StyleSheet::TraverseInner(nsCycleCollectionTraversalCallback& cb) {
222 // We can only have a cycle through our inner if we have a unique inner,
223 // because otherwise there are no JS wrappers for anything in the inner.
224 if (mInner->mSheets.Length() != 1) {
225 return;
228 for (StyleSheet* child : ChildSheets()) {
229 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet");
230 cb.NoteXPCOMChild(child);
234 // QueryInterface implementation for StyleSheet
235 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheet)
236 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
237 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
238 NS_INTERFACE_MAP_ENTRY(nsISupports)
239 NS_INTERFACE_MAP_END
241 NS_IMPL_CYCLE_COLLECTING_ADDREF(StyleSheet)
242 // We want to disconnect from our inner as soon as our refcount drops to zero,
243 // without waiting for async deletion by the cycle collector. Otherwise we
244 // might end up cloning the inner if someone mutates another sheet that shares
245 // it with us, even though there is only one such sheet and we're about to go
246 // away. This situation arises easily with sheet preloading.
247 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(StyleSheet, LastRelease())
249 NS_IMPL_CYCLE_COLLECTION_CLASS(StyleSheet)
251 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StyleSheet)
252 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMedia)
253 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleList)
254 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelevantGlobal)
255 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConstructorDocument)
256 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReplacePromise)
257 tmp->TraverseInner(cb);
258 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
260 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StyleSheet)
261 tmp->DropMedia();
262 tmp->UnlinkInner();
263 tmp->DropRuleList();
264 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelevantGlobal)
265 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConstructorDocument)
266 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReplacePromise)
267 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
268 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
270 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(StyleSheet)
272 dom::CSSStyleSheetParsingMode StyleSheet::ParsingModeDOM() {
273 #define CHECK_MODE(X, Y) \
274 static_assert( \
275 static_cast<int>(X) == static_cast<int>(Y), \
276 "mozilla::dom::CSSStyleSheetParsingMode and " \
277 "mozilla::css::SheetParsingMode should have identical values");
279 CHECK_MODE(dom::CSSStyleSheetParsingMode::Agent, css::eAgentSheetFeatures);
280 CHECK_MODE(dom::CSSStyleSheetParsingMode::User, css::eUserSheetFeatures);
281 CHECK_MODE(dom::CSSStyleSheetParsingMode::Author, css::eAuthorSheetFeatures);
283 #undef CHECK_MODE
285 return static_cast<dom::CSSStyleSheetParsingMode>(mParsingMode);
288 void StyleSheet::SetComplete() {
289 // HasForcedUniqueInner() is okay if the sheet is constructed, because
290 // constructed sheets are always unique and they may be set to complete
291 // multiple times if their rules are replaced via Replace()
292 MOZ_ASSERT(IsConstructed() || !HasForcedUniqueInner(),
293 "Can't complete a sheet that's already been forced unique.");
294 MOZ_ASSERT(!IsComplete(), "Already complete?");
295 mState |= State::Complete;
296 if (!Disabled()) {
297 ApplicableStateChanged(true);
299 MaybeResolveReplacePromise();
302 void StyleSheet::ApplicableStateChanged(bool aApplicable) {
303 MOZ_ASSERT(aApplicable == IsApplicable());
304 auto Notify = [this](DocumentOrShadowRoot& target) {
305 nsINode& node = target.AsNode();
306 if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {
307 shadow->StyleSheetApplicableStateChanged(*this);
308 } else {
309 node.AsDocument()->StyleSheetApplicableStateChanged(*this);
313 const StyleSheet& sheet = OutermostSheet();
314 if (sheet.mDocumentOrShadowRoot) {
315 Notify(*sheet.mDocumentOrShadowRoot);
318 for (DocumentOrShadowRoot* adopter : sheet.mAdopters) {
319 MOZ_ASSERT(adopter, "adopters should never be null");
320 Notify(*adopter);
324 void StyleSheet::SetDisabled(bool aDisabled) {
325 if (IsReadOnly()) {
326 return;
329 if (aDisabled == Disabled()) {
330 return;
333 if (aDisabled) {
334 mState |= State::Disabled;
335 } else {
336 mState &= ~State::Disabled;
339 if (IsComplete()) {
340 ApplicableStateChanged(!aDisabled);
344 void StyleSheet::SetURLExtraData() {
345 Inner().mURLData =
346 new URLExtraData(GetBaseURI(), GetReferrerInfo(), Principal());
349 nsISupports* StyleSheet::GetRelevantGlobal() const {
350 const StyleSheet& outer = OutermostSheet();
351 return outer.mRelevantGlobal;
354 StyleSheetInfo::StyleSheetInfo(CORSMode aCORSMode,
355 const SRIMetadata& aIntegrity,
356 css::SheetParsingMode aParsingMode)
357 : mPrincipal(NullPrincipal::CreateWithoutOriginAttributes()),
358 mCORSMode(aCORSMode),
359 mReferrerInfo(new ReferrerInfo(nullptr)),
360 mIntegrity(aIntegrity),
361 mContents(Servo_StyleSheet_Empty(aParsingMode).Consume()),
362 mURLData(URLExtraData::Dummy())
363 #ifdef DEBUG
365 mPrincipalSet(false)
366 #endif
368 if (!mPrincipal) {
369 MOZ_CRASH("NullPrincipal::Init failed");
371 MOZ_COUNT_CTOR(StyleSheetInfo);
374 StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy, StyleSheet* aPrimarySheet)
375 : mSheetURI(aCopy.mSheetURI),
376 mOriginalSheetURI(aCopy.mOriginalSheetURI),
377 mBaseURI(aCopy.mBaseURI),
378 mPrincipal(aCopy.mPrincipal),
379 mCORSMode(aCopy.mCORSMode),
380 mReferrerInfo(aCopy.mReferrerInfo),
381 mIntegrity(aCopy.mIntegrity),
382 // We don't rebuild the child because we're making a copy without
383 // children.
384 mSourceMapURL(aCopy.mSourceMapURL),
385 mSourceMapURLFromComment(aCopy.mSourceMapURLFromComment),
386 mSourceURL(aCopy.mSourceURL),
387 mContents(Servo_StyleSheet_Clone(aCopy.mContents.get(), aPrimarySheet)
388 .Consume()),
389 mURLData(aCopy.mURLData)
390 #ifdef DEBUG
392 mPrincipalSet(aCopy.mPrincipalSet)
393 #endif
395 AddSheet(aPrimarySheet);
397 // Our child list is fixed up by our parent.
398 MOZ_COUNT_CTOR(StyleSheetInfo);
401 StyleSheetInfo::~StyleSheetInfo() { MOZ_COUNT_DTOR(StyleSheetInfo); }
403 StyleSheetInfo* StyleSheetInfo::CloneFor(StyleSheet* aPrimarySheet) {
404 return new StyleSheetInfo(*this, aPrimarySheet);
407 MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSheetMallocSizeOf)
408 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSheetMallocEnclosingSizeOf)
410 size_t StyleSheetInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
411 size_t n = aMallocSizeOf(this);
413 n += Servo_StyleSheet_SizeOfIncludingThis(
414 ServoStyleSheetMallocSizeOf, ServoStyleSheetMallocEnclosingSizeOf,
415 mContents);
417 return n;
420 void StyleSheetInfo::AddSheet(StyleSheet* aSheet) {
421 mSheets.AppendElement(aSheet);
424 void StyleSheetInfo::RemoveSheet(StyleSheet* aSheet) {
425 // Fix up the parent pointer in children lists.
426 StyleSheet* newParent =
427 aSheet == mSheets[0] ? mSheets.SafeElementAt(1) : mSheets[0];
428 for (StyleSheet* child : mChildren) {
429 MOZ_ASSERT(child->mParentSheet);
430 MOZ_ASSERT(child->mParentSheet->mInner == this);
431 if (child->mParentSheet == aSheet) {
432 child->mParentSheet = newParent;
436 if (1 == mSheets.Length()) {
437 NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent");
438 delete this;
439 return;
442 mSheets.RemoveElement(aSheet);
445 void StyleSheet::GetType(nsAString& aType) { aType.AssignLiteral("text/css"); }
447 void StyleSheet::GetHref(nsAString& aHref, ErrorResult& aRv) {
448 if (nsIURI* sheetURI = Inner().mOriginalSheetURI) {
449 nsAutoCString str;
450 nsresult rv = sheetURI->GetSpec(str);
451 if (NS_FAILED(rv)) {
452 aRv.Throw(rv);
453 return;
455 CopyUTF8toUTF16(str, aHref);
456 } else {
457 SetDOMStringToNull(aHref);
461 void StyleSheet::GetTitle(nsAString& aTitle) {
462 // From https://drafts.csswg.org/cssom/#dom-stylesheet-title:
464 // The title attribute must return the title or null if title is the empty
465 // string.
467 if (!mTitle.IsEmpty()) {
468 aTitle.Assign(mTitle);
469 } else {
470 SetDOMStringToNull(aTitle);
474 void StyleSheet::WillDirty() {
475 MOZ_ASSERT(!IsReadOnly());
477 if (IsComplete()) {
478 EnsureUniqueInner();
482 void StyleSheet::AddStyleSet(ServoStyleSet* aStyleSet) {
483 MOZ_DIAGNOSTIC_ASSERT(!mStyleSets.Contains(aStyleSet),
484 "style set already registered");
485 mStyleSets.AppendElement(aStyleSet);
488 void StyleSheet::DropStyleSet(ServoStyleSet* aStyleSet) {
489 bool found = mStyleSets.RemoveElement(aStyleSet);
490 MOZ_DIAGNOSTIC_ASSERT(found, "didn't find style set");
491 #ifndef MOZ_DIAGNOSTIC_ASSERT_ENABLED
492 Unused << found;
493 #endif
496 // NOTE(emilio): Composed doc and containing shadow root are set in child sheets
497 // too, so no need to do it for each ancestor.
498 #define NOTIFY(function_, args_) \
499 do { \
500 StyleSheet* current = this; \
501 do { \
502 for (ServoStyleSet * set : current->mStyleSets) { \
503 set->function_ args_; \
505 if (auto* docOrShadow = current->mDocumentOrShadowRoot) { \
506 if (auto* shadow = ShadowRoot::FromNode(docOrShadow->AsNode())) { \
507 shadow->function_ args_; \
508 } else { \
509 docOrShadow->AsNode().AsDocument()->function_ args_; \
512 for (auto* adopter : mAdopters) { \
513 if (auto* shadow = ShadowRoot::FromNode(adopter->AsNode())) { \
514 shadow->function_ args_; \
515 } else { \
516 adopter->AsNode().AsDocument()->function_ args_; \
519 current = current->mParentSheet; \
520 } while (current); \
521 } while (0)
523 void StyleSheet::EnsureUniqueInner() {
524 MOZ_ASSERT(mInner->mSheets.Length() != 0, "unexpected number of outers");
526 if (IsReadOnly()) {
527 // Sheets that can't be modified don't need a unique inner.
528 return;
531 mState |= State::ForcedUniqueInner;
533 if (HasUniqueInner()) {
534 // already unique
535 return;
538 StyleSheetInfo* clone = mInner->CloneFor(this);
539 MOZ_ASSERT(clone);
540 mInner->RemoveSheet(this);
541 mInner = clone;
543 // Fixup the child lists and parent links in the Servo sheet. This is done
544 // here instead of in StyleSheetInner::CloneFor, because it's just more
545 // convenient to do so instead.
546 BuildChildListAfterInnerClone();
548 // let our containing style sets know that if we call
549 // nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the
550 // document
551 NOTIFY(SheetCloned, (*this));
554 // WebIDL CSSStyleSheet API
556 dom::CSSRuleList* StyleSheet::GetCssRules(nsIPrincipal& aSubjectPrincipal,
557 ErrorResult& aRv) {
558 if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
559 return nullptr;
561 return GetCssRulesInternal();
564 void StyleSheet::GetSourceMapURL(nsAString& aSourceMapURL) {
565 if (mInner->mSourceMapURL.IsEmpty()) {
566 aSourceMapURL = mInner->mSourceMapURLFromComment;
567 } else {
568 aSourceMapURL = mInner->mSourceMapURL;
572 void StyleSheet::SetSourceMapURL(const nsAString& aSourceMapURL) {
573 mInner->mSourceMapURL = aSourceMapURL;
576 void StyleSheet::SetSourceMapURLFromComment(
577 const nsAString& aSourceMapURLFromComment) {
578 mInner->mSourceMapURLFromComment = aSourceMapURLFromComment;
581 void StyleSheet::GetSourceURL(nsAString& aSourceURL) {
582 aSourceURL = mInner->mSourceURL;
585 void StyleSheet::SetSourceURL(const nsAString& aSourceURL) {
586 mInner->mSourceURL = aSourceURL;
589 css::Rule* StyleSheet::GetDOMOwnerRule() const { return mOwnerRule; }
591 // https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule
592 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-insertrule
593 uint32_t StyleSheet::InsertRule(const nsACString& aRule, uint32_t aIndex,
594 nsIPrincipal& aSubjectPrincipal,
595 ErrorResult& aRv) {
596 if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
597 return 0;
600 if (ModificationDisallowed()) {
601 aRv.ThrowNotAllowedError(
602 "This method can only be called on "
603 "modifiable style sheets");
604 return 0;
607 return InsertRuleInternal(aRule, aIndex, aRv);
610 // https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule
611 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-deleterule
612 void StyleSheet::DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
613 ErrorResult& aRv) {
614 if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
615 return;
618 if (ModificationDisallowed()) {
619 return aRv.ThrowNotAllowedError(
620 "This method can only be called on "
621 "modifiable style sheets");
624 return DeleteRuleInternal(aIndex, aRv);
627 int32_t StyleSheet::AddRule(const nsACString& aSelector,
628 const nsACString& aBlock,
629 const Optional<uint32_t>& aIndex,
630 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
631 if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
632 return -1;
635 nsAutoCString rule;
636 rule.Append(aSelector);
637 rule.AppendLiteral(" { ");
638 if (!aBlock.IsEmpty()) {
639 rule.Append(aBlock);
640 rule.Append(' ');
642 rule.Append('}');
644 auto index =
645 aIndex.WasPassed() ? aIndex.Value() : GetCssRulesInternal()->Length();
647 InsertRuleInternal(rule, index, aRv);
648 // Always return -1.
649 return -1;
652 void StyleSheet::MaybeResolveReplacePromise() {
653 MOZ_ASSERT(!!mReplacePromise == ModificationDisallowed());
654 if (!mReplacePromise) {
655 return;
658 SetModificationDisallowed(false);
659 mReplacePromise->MaybeResolve(this);
660 mReplacePromise = nullptr;
663 void StyleSheet::MaybeRejectReplacePromise() {
664 MOZ_ASSERT(!!mReplacePromise == ModificationDisallowed());
665 if (!mReplacePromise) {
666 return;
669 SetModificationDisallowed(false);
670 mReplacePromise->MaybeRejectWithNetworkError(
671 "@import style sheet load failed");
672 mReplacePromise = nullptr;
675 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replace
676 already_AddRefed<dom::Promise> StyleSheet::Replace(const nsACString& aText,
677 ErrorResult& aRv) {
678 nsIGlobalObject* globalObject = nullptr;
679 const StyleSheet& outer = OutermostSheet();
680 if (outer.mRelevantGlobal) {
681 globalObject = outer.mRelevantGlobal;
682 } else if (Document* doc = outer.GetAssociatedDocument()) {
683 globalObject = doc->GetScopeObject();
686 RefPtr<dom::Promise> promise = dom::Promise::Create(globalObject, aRv);
687 if (!promise) {
688 return nullptr;
691 // Step 1 and 4 are variable declarations
693 // 2.1 Check if sheet is constructed, else reject promise.
694 if (!IsConstructed()) {
695 promise->MaybeRejectWithNotAllowedError(
696 "This method can only be called on "
697 "constructed style sheets");
698 return promise.forget();
701 // 2.2 Check if sheet is modifiable, else throw.
702 if (ModificationDisallowed()) {
703 promise->MaybeRejectWithNotAllowedError(
704 "This method can only be called on "
705 "modifiable style sheets");
706 return promise.forget();
709 // 3. Disallow modifications until finished.
710 SetModificationDisallowed(true);
712 // TODO(emilio, 1642227): Should constructable stylesheets notify global
713 // observers (i.e., set mMustNotify to true)?
714 auto* loader = mConstructorDocument->CSSLoader();
715 auto loadData = MakeRefPtr<css::SheetLoadData>(
716 loader, nullptr, this, /* aSyncLoad */ false,
717 css::Loader::UseSystemPrincipal::No, css::StylePreloadKind::None,
718 /* aPreloadEncoding */ nullptr,
719 /* aObserver */ nullptr, mConstructorDocument->NodePrincipal(),
720 GetReferrerInfo(),
721 /* aRequestingNode */ nullptr);
723 // In parallel
724 // 5.1 Parse aText into rules.
725 // 5.2 Load import rules, throw NetworkError if failed.
726 // 5.3 Set sheet's rules to new rules.
727 nsCOMPtr<nsISerialEventTarget> target =
728 mConstructorDocument->EventTargetFor(TaskCategory::Other);
729 loadData->mIsBeingParsed = true;
730 MOZ_ASSERT(!mReplacePromise);
731 mReplacePromise = promise;
732 ParseSheet(*loader, aText, *loadData)
733 ->Then(
734 target, __func__,
735 [loadData] { loadData->SheetFinishedParsingAsync(); },
736 [] { MOZ_CRASH("This MozPromise should never be rejected."); });
738 // 6. Return the promise
739 return promise.forget();
742 // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync
743 void StyleSheet::ReplaceSync(const nsACString& aText, ErrorResult& aRv) {
744 // Step 1 is a variable declaration
746 // 2.1 Check if sheet is constructed, else throw.
747 if (!IsConstructed()) {
748 return aRv.ThrowNotAllowedError(
749 "Can only be called on constructed style sheets");
752 // 2.2 Check if sheet is modifiable, else throw.
753 if (ModificationDisallowed()) {
754 return aRv.ThrowNotAllowedError(
755 "Can only be called on modifiable style sheets");
758 // 3. Parse aText into rules.
759 // 4. If rules contain @imports, skip them and continue parsing.
760 auto* loader = mConstructorDocument->CSSLoader();
761 SetURLExtraData();
762 RefPtr<const RawServoStyleSheetContents> rawContent =
763 Servo_StyleSheet_FromUTF8Bytes(
764 loader, this,
765 /* load_data = */ nullptr, &aText, mParsingMode, Inner().mURLData,
766 /* line_number_offset = */ 0,
767 mConstructorDocument->GetCompatibilityMode(),
768 /* reusable_sheets = */ nullptr,
769 mConstructorDocument->GetStyleUseCounters(),
770 StyleAllowImportRules::No, StyleSanitizationKind::None,
771 /* sanitized_output = */ nullptr)
772 .Consume();
774 // 5. Set sheet's rules to the new rules.
775 DropRuleList();
776 Inner().mContents = std::move(rawContent);
777 FinishParse();
778 RuleChanged(nullptr, StyleRuleChangeKind::Generic);
781 nsresult StyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup,
782 uint32_t aIndex) {
783 NS_ENSURE_ARG_POINTER(aGroup);
784 NS_ASSERTION(IsComplete(), "No deleting from an incomplete sheet!");
785 RefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
786 NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
788 // check that the rule actually belongs to this sheet!
789 if (this != rule->GetStyleSheet()) {
790 return NS_ERROR_INVALID_ARG;
793 if (IsReadOnly()) {
794 return NS_OK;
797 WillDirty();
799 nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
800 NS_ENSURE_SUCCESS(result, result);
802 rule->DropReferences();
804 RuleRemoved(*rule);
805 return NS_OK;
808 void StyleSheet::RuleAdded(css::Rule& aRule) {
809 SetModifiedRules();
810 NOTIFY(RuleAdded, (*this, aRule));
813 void StyleSheet::RuleRemoved(css::Rule& aRule) {
814 SetModifiedRules();
815 NOTIFY(RuleRemoved, (*this, aRule));
818 void StyleSheet::RuleChanged(css::Rule* aRule, StyleRuleChangeKind aKind) {
819 SetModifiedRules();
820 NOTIFY(RuleChanged, (*this, aRule, aKind));
823 // nsICSSLoaderObserver implementation
824 NS_IMETHODIMP
825 StyleSheet::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
826 nsresult aStatus) {
827 if (!aSheet->GetParentSheet()) {
828 return NS_OK; // ignore if sheet has been detached already
830 MOZ_ASSERT(this == aSheet->GetParentSheet(),
831 "We are being notified of a sheet load for a sheet that is not "
832 "our child!");
833 if (NS_FAILED(aStatus)) {
834 return NS_OK;
837 MOZ_ASSERT(aSheet->GetOwnerRule());
838 NOTIFY(ImportRuleLoaded, (*aSheet->GetOwnerRule(), *aSheet));
839 return NS_OK;
842 #undef NOTIFY
844 nsresult StyleSheet::InsertRuleIntoGroup(const nsACString& aRule,
845 css::GroupRule* aGroup,
846 uint32_t aIndex) {
847 NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!");
848 // check that the group actually belongs to this sheet!
849 if (this != aGroup->GetStyleSheet()) {
850 return NS_ERROR_INVALID_ARG;
853 if (IsReadOnly()) {
854 return NS_OK;
857 if (ModificationDisallowed()) {
858 return NS_ERROR_DOM_NOT_ALLOWED_ERR;
861 WillDirty();
863 nsresult result = InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
864 NS_ENSURE_SUCCESS(result, result);
865 RuleAdded(*aGroup->GetStyleRuleAt(aIndex));
866 return NS_OK;
869 uint64_t StyleSheet::FindOwningWindowInnerID() const {
870 uint64_t windowID = 0;
871 if (Document* doc = GetAssociatedDocument()) {
872 windowID = doc->InnerWindowID();
875 if (windowID == 0 && mOwningNode) {
876 windowID = mOwningNode->OwnerDoc()->InnerWindowID();
879 RefPtr<css::Rule> ownerRule;
880 if (windowID == 0 && (ownerRule = GetDOMOwnerRule())) {
881 RefPtr<StyleSheet> sheet = ownerRule->GetStyleSheet();
882 if (sheet) {
883 windowID = sheet->FindOwningWindowInnerID();
887 if (windowID == 0 && mParentSheet) {
888 windowID = mParentSheet->FindOwningWindowInnerID();
891 return windowID;
894 void StyleSheet::RemoveFromParent() {
895 if (!mParentSheet) {
896 return;
899 MOZ_ASSERT(mParentSheet->ChildSheets().Contains(this));
900 mParentSheet->Inner().mChildren.RemoveElement(this);
901 mParentSheet = nullptr;
904 void StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
905 ErrorResult& aRv) {
906 StyleSheetInfo& info = Inner();
908 if (aSubjectPrincipal.Subsumes(info.mPrincipal)) {
909 return;
912 // Allow access only if CORS mode is not NONE and the security flag
913 // is not turned off.
914 if (GetCORSMode() == CORS_NONE && !nsContentUtils::BypassCSSOMOriginCheck()) {
915 aRv.ThrowSecurityError("Not allowed to access cross-origin stylesheet");
916 return;
919 // Now make sure we set the principal of our inner to the subjectPrincipal.
920 // We do this because we're in a situation where the caller would not normally
921 // be able to access the sheet, but the sheet has opted in to being read.
922 // Unfortunately, that means it's also opted in to being _edited_, and if the
923 // caller now makes edits to the sheet we want the resulting resource loads,
924 // if any, to look as if they are coming from the caller's principal, not the
925 // original sheet principal.
927 // That means we need a unique inner, of course. But we don't want to do that
928 // if we're not complete yet. Luckily, all the callers of this method throw
929 // anyway if not complete, so we can just do that here too.
930 if (!IsComplete()) {
931 aRv.ThrowInvalidAccessError(
932 "Not allowed to access still-loading stylesheet");
933 return;
936 WillDirty();
938 info.mPrincipal = &aSubjectPrincipal;
941 bool StyleSheet::AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
942 ErrorResult& aRv) {
943 // Rules are not available on incomplete sheets.
944 if (!IsComplete()) {
945 aRv.ThrowInvalidAccessError(
946 "Can't access rules of still-loading style sheet");
947 return false;
949 //-- Security check: Only scripts whose principal subsumes that of the
950 // style sheet can access rule collections.
951 SubjectSubsumesInnerPrincipal(aSubjectPrincipal, aRv);
952 if (NS_WARN_IF(aRv.Failed())) {
953 return false;
955 return true;
958 void StyleSheet::SetAssociatedDocumentOrShadowRoot(
959 DocumentOrShadowRoot* aDocOrShadowRoot) {
960 MOZ_ASSERT(!IsConstructed());
961 MOZ_ASSERT(!mParentSheet || !aDocOrShadowRoot,
962 "Shouldn't be set on child sheets");
964 // not ref counted
965 mDocumentOrShadowRoot = aDocOrShadowRoot;
967 if (Document* doc = GetAssociatedDocument()) {
968 MOZ_ASSERT(!mRelevantGlobal);
969 mRelevantGlobal = doc->GetScopeObject();
973 void StyleSheet::AppendStyleSheet(StyleSheet& aSheet) {
974 WillDirty();
975 AppendStyleSheetSilently(aSheet);
978 void StyleSheet::AppendStyleSheetSilently(StyleSheet& aSheet) {
979 MOZ_ASSERT(!IsReadOnly());
981 Inner().mChildren.AppendElement(&aSheet);
983 // This is not reference counted. Our parent tells us when
984 // it's going away.
985 aSheet.mParentSheet = this;
988 size_t StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
989 size_t n = 0;
990 n += aMallocSizeOf(this);
992 // We want to measure the inner with only one of the children, and it makes
993 // sense for it to be the latest as it is the most likely to be reachable.
994 if (Inner().mSheets.LastElement() == this) {
995 n += Inner().SizeOfIncludingThis(aMallocSizeOf);
998 // Measurement of the following members may be added later if DMD finds it
999 // is worthwhile:
1000 // - mTitle
1001 // - mMedia
1002 // - mStyleSets
1003 // - mRuleList
1005 return n;
1008 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
1009 void StyleSheet::List(FILE* aOut, int32_t aIndent) {
1010 for (StyleSheet* child : ChildSheets()) {
1011 child->List(aOut, aIndent);
1014 nsCString line;
1015 for (int i = 0; i < aIndent; ++i) {
1016 line.AppendLiteral(" ");
1019 line.AppendLiteral("/* ");
1021 nsCString url;
1022 GetSheetURI()->GetSpec(url);
1023 if (url.IsEmpty()) {
1024 line.AppendLiteral("(no URL)");
1025 } else {
1026 line.Append(url);
1029 line.AppendLiteral(" (");
1031 switch (GetOrigin()) {
1032 case StyleOrigin::UserAgent:
1033 line.AppendLiteral("User Agent");
1034 break;
1035 case StyleOrigin::User:
1036 line.AppendLiteral("User");
1037 break;
1038 case StyleOrigin::Author:
1039 line.AppendLiteral("Author");
1040 break;
1043 if (mMedia) {
1044 nsAutoCString buffer;
1045 mMedia->GetText(buffer);
1047 if (!buffer.IsEmpty()) {
1048 line.AppendLiteral(", ");
1049 line.Append(buffer);
1053 line.AppendLiteral(") */");
1055 fprintf_stderr(aOut, "%s\n\n", line.get());
1057 nsCString newlineIndent;
1058 newlineIndent.Append('\n');
1059 for (int i = 0; i < aIndent; ++i) {
1060 newlineIndent.AppendLiteral(" ");
1063 ServoCSSRuleList* ruleList = GetCssRulesInternal();
1064 for (uint32_t i = 0, len = ruleList->Length(); i < len; ++i) {
1065 css::Rule* rule = ruleList->GetRule(i);
1067 nsAutoCString cssText;
1068 rule->GetCssText(cssText);
1069 cssText.ReplaceSubstring("\n"_ns, newlineIndent);
1070 fprintf_stderr(aOut, "%s\n", cssText.get());
1073 if (ruleList->Length() != 0) {
1074 fprintf_stderr(aOut, "\n");
1077 #endif
1079 void StyleSheet::SetMedia(already_AddRefed<dom::MediaList> aMedia) {
1080 mMedia = aMedia;
1081 if (mMedia) {
1082 mMedia->SetStyleSheet(this);
1086 void StyleSheet::DropMedia() {
1087 if (mMedia) {
1088 mMedia->SetStyleSheet(nullptr);
1089 mMedia = nullptr;
1093 dom::MediaList* StyleSheet::Media() {
1094 if (!mMedia) {
1095 mMedia = dom::MediaList::Create(EmptyCString());
1096 mMedia->SetStyleSheet(this);
1099 return mMedia;
1102 // nsWrapperCache
1104 JSObject* StyleSheet::WrapObject(JSContext* aCx,
1105 JS::Handle<JSObject*> aGivenProto) {
1106 return dom::CSSStyleSheet_Binding::Wrap(aCx, this, aGivenProto);
1109 void StyleSheet::BuildChildListAfterInnerClone() {
1110 MOZ_ASSERT(Inner().mSheets.Length() == 1, "Should've just cloned");
1111 MOZ_ASSERT(Inner().mSheets[0] == this);
1112 MOZ_ASSERT(Inner().mChildren.IsEmpty());
1114 auto* contents = Inner().mContents.get();
1115 RefPtr<ServoCssRules> rules = Servo_StyleSheet_GetRules(contents).Consume();
1117 uint32_t index = 0;
1118 while (true) {
1119 uint32_t line, column; // Actually unused.
1120 RefPtr<RawServoImportRule> import =
1121 Servo_CssRules_GetImportRuleAt(rules, index, &line, &column).Consume();
1122 if (!import) {
1123 // Note that only @charset rules come before @import rules, and @charset
1124 // rules are parsed but skipped, so we can stop iterating as soon as we
1125 // find something that isn't an @import rule.
1126 break;
1128 auto* sheet = const_cast<StyleSheet*>(Servo_ImportRule_GetSheet(import));
1129 MOZ_ASSERT(sheet);
1130 AppendStyleSheetSilently(*sheet);
1131 index++;
1135 already_AddRefed<StyleSheet> StyleSheet::CreateEmptyChildSheet(
1136 already_AddRefed<dom::MediaList> aMediaList) const {
1137 RefPtr<StyleSheet> child =
1138 new StyleSheet(ParsingMode(), CORSMode::CORS_NONE, SRIMetadata());
1140 child->mMedia = aMediaList;
1141 return child.forget();
1144 // We disable parallel stylesheet parsing if any of the following three
1145 // conditions hold:
1147 // (1) The pref is off.
1148 // (2) The browser is recording CSS errors (which parallel parsing can't
1149 // handle).
1150 // (3) The stylesheet is a chrome stylesheet, since those can use
1151 // -moz-bool-pref, which needs to access the pref service, which is not
1152 // threadsafe.
1153 static bool AllowParallelParse(css::Loader& aLoader, URLExtraData* aUrlData) {
1154 // If the browser is recording CSS errors, we need to use the sequential path
1155 // because the parallel path doesn't support that.
1156 Document* doc = aLoader.GetDocument();
1157 if (doc && css::ErrorReporter::ShouldReportErrors(*doc)) {
1158 return false;
1161 // If this is a chrome stylesheet, it might use -moz-bool-pref, which needs to
1162 // access the pref service, which is not thread-safe. We could probably expose
1163 // the relevant booleans as thread-safe var caches if we needed to, but
1164 // parsing chrome stylesheets in parallel is unlikely to be a win anyway.
1166 // Note that UA stylesheets can also use -moz-bool-pref, but those are always
1167 // parsed sync.
1168 if (aUrlData->ChromeRulesEnabled()) {
1169 return false;
1172 return true;
1175 RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
1176 css::Loader& aLoader, const nsACString& aBytes,
1177 css::SheetLoadData& aLoadData) {
1178 MOZ_ASSERT(mParsePromise.IsEmpty());
1179 RefPtr<StyleSheetParsePromise> p = mParsePromise.Ensure(__func__);
1180 SetURLExtraData();
1182 // @import rules are disallowed due to this decision:
1183 // https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418
1184 // We may allow @import rules again in the future.
1185 auto allowImportRules = SelfOrAncestorIsConstructed()
1186 ? StyleAllowImportRules::No
1187 : StyleAllowImportRules::Yes;
1188 const bool shouldRecordCounters =
1189 aLoader.GetDocument() && aLoader.GetDocument()->GetStyleUseCounters();
1190 if (!AllowParallelParse(aLoader, Inner().mURLData)) {
1191 UniquePtr<StyleUseCounters> counters =
1192 shouldRecordCounters ? Servo_UseCounters_Create().Consume() : nullptr;
1194 RefPtr<RawServoStyleSheetContents> contents =
1195 Servo_StyleSheet_FromUTF8Bytes(
1196 &aLoader, this, &aLoadData, &aBytes, mParsingMode, Inner().mURLData,
1197 aLoadData.mLineNumber, aLoadData.mCompatMode,
1198 /* reusable_sheets = */ nullptr, counters.get(), allowImportRules,
1199 StyleSanitizationKind::None,
1200 /* sanitized_output = */ nullptr)
1201 .Consume();
1202 aLoadData.mUseCounters = std::move(counters);
1203 FinishAsyncParse(contents.forget());
1204 } else {
1205 auto holder = MakeRefPtr<css::SheetLoadDataHolder>(__func__, &aLoadData);
1206 Servo_StyleSheet_FromUTF8BytesAsync(
1207 holder, Inner().mURLData, &aBytes, mParsingMode, aLoadData.mLineNumber,
1208 aLoadData.mCompatMode, shouldRecordCounters, allowImportRules);
1211 return p;
1214 void StyleSheet::FinishAsyncParse(
1215 already_AddRefed<RawServoStyleSheetContents> aSheetContents) {
1216 MOZ_ASSERT(NS_IsMainThread());
1217 MOZ_ASSERT(!mParsePromise.IsEmpty());
1218 Inner().mContents = aSheetContents;
1219 FinishParse();
1220 mParsePromise.Resolve(true, __func__);
1223 void StyleSheet::ParseSheetSync(
1224 css::Loader* aLoader, const nsACString& aBytes,
1225 css::SheetLoadData* aLoadData, uint32_t aLineNumber,
1226 css::LoaderReusableStyleSheets* aReusableSheets) {
1227 const nsCompatibility compatMode = [&] {
1228 if (aLoadData) {
1229 return aLoadData->mCompatMode;
1231 if (aLoader) {
1232 return aLoader->CompatMode(css::StylePreloadKind::None);
1234 return eCompatibility_FullStandards;
1235 }();
1237 const StyleUseCounters* useCounters =
1238 aLoader && aLoader->GetDocument()
1239 ? aLoader->GetDocument()->GetStyleUseCounters()
1240 : nullptr;
1242 auto allowImportRules = SelfOrAncestorIsConstructed()
1243 ? StyleAllowImportRules::No
1244 : StyleAllowImportRules::Yes;
1246 SetURLExtraData();
1247 Inner().mContents =
1248 Servo_StyleSheet_FromUTF8Bytes(
1249 aLoader, this, aLoadData, &aBytes, mParsingMode, Inner().mURLData,
1250 aLineNumber, compatMode, aReusableSheets, useCounters,
1251 allowImportRules, StyleSanitizationKind::None,
1252 /* sanitized_output = */ nullptr)
1253 .Consume();
1255 FinishParse();
1258 void StyleSheet::FinishParse() {
1259 nsString sourceMapURL;
1260 Servo_StyleSheet_GetSourceMapURL(Inner().mContents, &sourceMapURL);
1261 SetSourceMapURLFromComment(sourceMapURL);
1263 nsString sourceURL;
1264 Servo_StyleSheet_GetSourceURL(Inner().mContents, &sourceURL);
1265 SetSourceURL(sourceURL);
1268 void StyleSheet::ReparseSheet(const nsACString& aInput, ErrorResult& aRv) {
1269 if (!IsComplete()) {
1270 return aRv.ThrowInvalidAccessError("Cannot reparse still-loading sheet");
1273 // Allowing to modify UA sheets is dangerous (in the sense that C++ code
1274 // relies on rules in those sheets), plus they're probably going to be shared
1275 // across processes in which case this is directly a no-go.
1276 if (IsReadOnly()) {
1277 return;
1280 // Hold strong ref to the CSSLoader in case the document update
1281 // kills the document
1282 RefPtr<css::Loader> loader;
1283 if (Document* doc = GetAssociatedDocument()) {
1284 loader = doc->CSSLoader();
1285 NS_ASSERTION(loader, "Document with no CSS loader!");
1286 } else {
1287 loader = new css::Loader;
1290 WillDirty();
1292 // cache child sheets to reuse
1293 css::LoaderReusableStyleSheets reusableSheets;
1294 for (StyleSheet* child : ChildSheets()) {
1295 if (child->GetOriginalURI()) {
1296 reusableSheets.AddReusableSheet(child);
1300 // Clean up child sheets list.
1301 for (StyleSheet* child : ChildSheets()) {
1302 child->mParentSheet = nullptr;
1304 Inner().mChildren.Clear();
1306 uint32_t lineNumber = 1;
1307 if (auto* linkStyle = LinkStyle::FromNodeOrNull(mOwningNode)) {
1308 lineNumber = linkStyle->GetLineNumber();
1311 // Notify to the stylesets about the old rules going away.
1313 ServoCSSRuleList* ruleList = GetCssRulesInternal();
1314 MOZ_ASSERT(ruleList);
1316 uint32_t ruleCount = ruleList->Length();
1317 for (uint32_t i = 0; i < ruleCount; ++i) {
1318 css::Rule* rule = ruleList->GetRule(i);
1319 MOZ_ASSERT(rule);
1320 RuleRemoved(*rule);
1324 DropRuleList();
1326 ParseSheetSync(loader, aInput, /* aLoadData = */ nullptr, lineNumber,
1327 &reusableSheets);
1329 // Notify the stylesets about the new rules.
1331 // Get the rule list (which will need to be regenerated after ParseSheet).
1332 ServoCSSRuleList* ruleList = GetCssRulesInternal();
1333 MOZ_ASSERT(ruleList);
1335 uint32_t ruleCount = ruleList->Length();
1336 for (uint32_t i = 0; i < ruleCount; ++i) {
1337 css::Rule* rule = ruleList->GetRule(i);
1338 MOZ_ASSERT(rule);
1339 RuleAdded(*rule);
1343 // Our rules are no longer considered modified for devtools.
1344 mState &= ~State::ModifiedRulesForDevtools;
1347 void StyleSheet::DropRuleList() {
1348 if (mRuleList) {
1349 mRuleList->DropReferences();
1350 mRuleList = nullptr;
1354 already_AddRefed<StyleSheet> StyleSheet::Clone(
1355 StyleSheet* aCloneParent, dom::CSSImportRule* aCloneOwnerRule,
1356 dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot,
1357 nsINode* aCloneOwningNode) const {
1358 MOZ_ASSERT(!IsConstructed(),
1359 "Cannot create a non-constructed sheet from a constructed sheet");
1360 RefPtr<StyleSheet> clone = new StyleSheet(
1361 *this, aCloneParent, aCloneOwnerRule, aCloneDocumentOrShadowRoot,
1362 /* aConstructorDocToUse */ nullptr, aCloneOwningNode);
1363 return clone.forget();
1366 already_AddRefed<StyleSheet> StyleSheet::CloneAdoptedSheet(
1367 Document& aConstructorDocument) const {
1368 MOZ_ASSERT(IsConstructed(),
1369 "Cannot create a constructed sheet from a non-constructed sheet");
1370 MOZ_ASSERT(aConstructorDocument.IsStaticDocument(),
1371 "Should never clone adopted sheets for a non-static document");
1372 RefPtr<StyleSheet> clone =
1373 new StyleSheet(*this,
1374 /* aParentSheetToUse */ nullptr,
1375 /* aOwnerRuleToUse */ nullptr,
1376 /* aDocOrShadowRootToUse */ nullptr, &aConstructorDocument,
1377 /* aOwningNodeToUse */ nullptr);
1378 return clone.forget();
1381 ServoCSSRuleList* StyleSheet::GetCssRulesInternal() {
1382 if (!mRuleList) {
1383 EnsureUniqueInner();
1385 RefPtr<ServoCssRules> rawRules =
1386 Servo_StyleSheet_GetRules(Inner().mContents).Consume();
1387 MOZ_ASSERT(rawRules);
1388 mRuleList = new ServoCSSRuleList(rawRules.forget(), this, nullptr);
1390 return mRuleList;
1393 uint32_t StyleSheet::InsertRuleInternal(const nsACString& aRule,
1394 uint32_t aIndex, ErrorResult& aRv) {
1395 MOZ_ASSERT(!IsReadOnly());
1396 MOZ_ASSERT(!ModificationDisallowed());
1398 // Ensure mRuleList is constructed.
1399 GetCssRulesInternal();
1401 aRv = mRuleList->InsertRule(aRule, aIndex);
1402 if (aRv.Failed()) {
1403 return 0;
1406 // XXX We may not want to get the rule when stylesheet change event
1407 // is not enabled.
1408 css::Rule* rule = mRuleList->GetRule(aIndex);
1409 RuleAdded(*rule);
1411 return aIndex;
1414 void StyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv) {
1415 MOZ_ASSERT(!IsReadOnly());
1416 MOZ_ASSERT(!ModificationDisallowed());
1418 // Ensure mRuleList is constructed.
1419 GetCssRulesInternal();
1420 if (aIndex >= mRuleList->Length()) {
1421 aRv.ThrowIndexSizeError(
1422 nsPrintfCString("Cannot delete rule at index %u"
1423 " because the number of rules is only %u",
1424 aIndex, mRuleList->Length()));
1425 return;
1428 // Hold a strong ref to the rule so it doesn't die when we remove it
1429 // from the list. XXX We may not want to hold it if stylesheet change
1430 // event is not enabled.
1431 RefPtr<css::Rule> rule = mRuleList->GetRule(aIndex);
1432 aRv = mRuleList->DeleteRule(aIndex);
1433 if (!aRv.Failed()) {
1434 RuleRemoved(*rule);
1438 nsresult StyleSheet::InsertRuleIntoGroupInternal(const nsACString& aRule,
1439 css::GroupRule* aGroup,
1440 uint32_t aIndex) {
1441 MOZ_ASSERT(!IsReadOnly());
1443 auto rules = static_cast<ServoCSSRuleList*>(aGroup->CssRules());
1444 MOZ_ASSERT(rules->GetParentRule() == aGroup);
1445 return rules->InsertRule(aRule, aIndex);
1448 StyleOrigin StyleSheet::GetOrigin() const {
1449 return Servo_StyleSheet_GetOrigin(Inner().mContents);
1452 void StyleSheet::SetSharedContents(const ServoCssRules* aSharedRules) {
1453 MOZ_ASSERT(!IsComplete());
1455 SetURLExtraData();
1457 Inner().mContents =
1458 Servo_StyleSheet_FromSharedData(Inner().mURLData, aSharedRules).Consume();
1460 // Don't call FinishParse(), since that tries to set source map URLs,
1461 // which we don't have.
1464 const ServoCssRules* StyleSheet::ToShared(RawServoSharedMemoryBuilder* aBuilder,
1465 nsCString& aErrorMessage) {
1466 // Assert some things we assume when creating a StyleSheet using shared
1467 // memory.
1468 MOZ_ASSERT(GetReferrerInfo()->ReferrerPolicy() == ReferrerPolicy::_empty);
1469 MOZ_ASSERT(GetReferrerInfo()->GetSendReferrer());
1470 MOZ_ASSERT(!nsCOMPtr<nsIURI>(GetReferrerInfo()->GetComputedReferrer()));
1471 MOZ_ASSERT(GetCORSMode() == CORS_NONE);
1472 MOZ_ASSERT(Inner().mIntegrity.IsEmpty());
1473 MOZ_ASSERT(Principal()->IsSystemPrincipal());
1475 const ServoCssRules* rules = Servo_SharedMemoryBuilder_AddStylesheet(
1476 aBuilder, Inner().mContents, &aErrorMessage);
1478 #ifdef DEBUG
1479 if (!rules) {
1480 // Print the ToShmem error message so that developers know what to fix.
1481 printf_stderr("%s\n", aErrorMessage.get());
1482 MOZ_CRASH("UA style sheet contents failed shared memory requirements");
1484 #endif
1486 return rules;
1489 bool StyleSheet::IsReadOnly() const {
1490 return IsComplete() && GetOrigin() == StyleOrigin::UserAgent;
1493 } // namespace mozilla