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