Bug 1459529: Make sheets be associated to a shadow root too potentially. r=bz
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 11 May 2018 12:57:38 +0200
changeset 418351 699f1d745e76e50106be19aa30116b0a31453471
parent 418350 69e6247fd71006908a8f37fae9f445f715e3e360
child 418352 f04194f1a3d07c4f838101417eccf770fb354df4
push id64004
push userecoal95@gmail.com
push dateTue, 15 May 2018 16:44:48 +0000
treeherderautoland@a40accc5c91c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1459529
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1459529: Make sheets be associated to a shadow root too potentially. r=bz MozReview-Commit-ID: Cd8xJuLRY5w
dom/base/DocumentOrShadowRoot.cpp
dom/base/DocumentOrShadowRoot.h
dom/base/ShadowRoot.cpp
dom/base/ShadowRoot.h
dom/base/nsDOMWindowUtils.cpp
dom/base/nsDocument.cpp
editor/libeditor/HTMLEditor.cpp
layout/style/ErrorReporter.cpp
layout/style/Loader.cpp
layout/style/MediaList.cpp
layout/style/Rule.cpp
layout/style/Rule.h
layout/style/ServoCounterStyleRule.cpp
layout/style/ServoFontFaceRule.cpp
layout/style/ServoKeyframeRule.cpp
layout/style/ServoKeyframesRule.cpp
layout/style/ServoPageRule.cpp
layout/style/ServoStyleRule.cpp
layout/style/StyleSheet.cpp
layout/style/StyleSheet.h
layout/style/nsDOMCSSDeclaration.cpp
--- a/dom/base/DocumentOrShadowRoot.cpp
+++ b/dom/base/DocumentOrShadowRoot.cpp
@@ -29,16 +29,45 @@ StyleSheetList&
 DocumentOrShadowRoot::EnsureDOMStyleSheets()
 {
   if (!mDOMStyleSheets) {
     mDOMStyleSheets = new StyleSheetList(*this);
   }
   return *mDOMStyleSheets;
 }
 
+void
+DocumentOrShadowRoot::AppendSheet(StyleSheet& aSheet)
+{
+  aSheet.SetAssociatedDocumentOrShadowRoot(
+    this, StyleSheet::OwnedByDocumentOrShadowRoot);
+  mStyleSheets.AppendElement(&aSheet);
+}
+
+void
+DocumentOrShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet)
+{
+  aSheet.SetAssociatedDocumentOrShadowRoot(
+    this, StyleSheet::OwnedByDocumentOrShadowRoot);
+  mStyleSheets.InsertElementAt(aIndex, &aSheet);
+}
+
+already_AddRefed<StyleSheet>
+DocumentOrShadowRoot::RemoveSheet(StyleSheet& aSheet)
+{
+  auto index = mStyleSheets.IndexOf(&aSheet);
+  if (index == mStyleSheets.NoIndex) {
+    return nullptr;
+  }
+  RefPtr<StyleSheet> sheet = Move(mStyleSheets[index]);
+  mStyleSheets.RemoveElementAt(index);
+  sheet->ClearAssociatedDocumentOrShadowRoot();
+  return sheet.forget();
+}
+
 Element*
 DocumentOrShadowRoot::GetElementById(const nsAString& aElementId)
 {
   if (MOZ_UNLIKELY(aElementId.IsEmpty())) {
     nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
     return nullptr;
   }
 
--- a/dom/base/DocumentOrShadowRoot.h
+++ b/dom/base/DocumentOrShadowRoot.h
@@ -2,16 +2,18 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_DocumentOrShadowRoot_h__
 #define mozilla_dom_DocumentOrShadowRoot_h__
 
+#include "mozilla/dom/NameSpaceConstants.h"
+#include "nsContentListDeclarations.h"
 #include "nsTArray.h"
 #include "nsIdentifierMapEntry.h"
 
 class nsContentList;
 class nsINode;
 
 namespace mozilla {
 class StyleSheet;
@@ -60,31 +62,16 @@ public:
     return mStyleSheets.Length();
   }
 
   int32_t IndexOfSheet(const StyleSheet& aSheet) const
   {
     return mStyleSheets.IndexOf(&aSheet);
   }
 
-  void InsertSheetAt(size_t aIndex, StyleSheet& aSheet)
-  {
-    mStyleSheets.InsertElementAt(aIndex, &aSheet);
-  }
-
-  void RemoveSheet(StyleSheet& aSheet)
-  {
-    mStyleSheets.RemoveElement(&aSheet);
-  }
-
-  void AppendStyleSheet(StyleSheet& aSheet)
-  {
-    mStyleSheets.AppendElement(&aSheet);
-  }
-
   StyleSheetList& EnsureDOMStyleSheets();
 
   Element* GetElementById(const nsAString& aElementId);
 
   /**
    * This method returns _all_ the elements in this scope which have id
    * aElementId, if there are any.  Otherwise it returns null.
    *
@@ -183,16 +170,21 @@ public:
       return false;
     }
     return true;
   }
 
   void ReportEmptyGetElementByIdArg();
 
 protected:
+  // Returns the reference to the sheet, if found in mStyleSheets.
+  already_AddRefed<StyleSheet> RemoveSheet(StyleSheet& aSheet);
+  void AppendSheet(StyleSheet& aSheet);
+  void InsertSheetAt(size_t aIndex, StyleSheet& aSheet);
+
   nsIContent* Retarget(nsIContent* aContent) const;
 
   /**
    * If focused element's subtree root is this document or shadow root, return
    * focused element, otherwise, get the shadow host recursively until the
    * shadow host's subtree root is this document or shadow root.
    */
   Element* GetRetargetedFocusedElement();
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -127,17 +127,17 @@ ShadowRoot::WrapObject(JSContext* aCx, J
 void
 ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther)
 {
   size_t sheetCount = aOther->SheetCount();
   for (size_t i = 0; i < sheetCount; ++i) {
     StyleSheet* sheet = aOther->SheetAt(i);
     if (sheet->IsApplicable()) {
       RefPtr<StyleSheet> clonedSheet =
-        sheet->Clone(nullptr, nullptr, nullptr, nullptr);
+        sheet->Clone(nullptr, nullptr, this, nullptr);
       if (clonedSheet) {
         AppendStyleSheet(*clonedSheet.get());
       }
     }
   }
 }
 
 void
@@ -321,17 +321,17 @@ ShadowRoot::InsertSheetAt(size_t aIndex,
   if (aSheet.IsApplicable()) {
     InsertSheetIntoAuthorData(aIndex, aSheet);
   }
 }
 
 void
 ShadowRoot::AppendStyleSheet(StyleSheet& aSheet)
 {
-  DocumentOrShadowRoot::AppendStyleSheet(aSheet);
+  DocumentOrShadowRoot::AppendSheet(aSheet);
   if (aSheet.IsApplicable()) {
     Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
     if (mStyleRuleMap) {
       mStyleRuleMap->SheetAdded(aSheet);
     }
     ApplicableRulesChanged();
   }
 }
@@ -386,16 +386,18 @@ ShadowRoot::InsertSheetIntoAuthorData(si
     ApplicableRulesChanged();
     return;
   }
 
   Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
   ApplicableRulesChanged();
 }
 
+// FIXME(emilio): This needs to notify document observers and such,
+// presumably.
 void
 ShadowRoot::StyleSheetApplicableStateChanged(StyleSheet& aSheet, bool aApplicable)
 {
   MOZ_ASSERT(mStyleSheets.Contains(&aSheet));
   if (aApplicable) {
     int32_t index = IndexOfSheet(aSheet);
     MOZ_RELEASE_ASSERT(index >= 0);
     InsertSheetIntoAuthorData(size_t(index), aSheet);
@@ -406,22 +408,24 @@ ShadowRoot::StyleSheetApplicableStateCha
     Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
     ApplicableRulesChanged();
   }
 }
 
 void
 ShadowRoot::RemoveSheet(StyleSheet* aSheet)
 {
-  DocumentOrShadowRoot::RemoveSheet(*aSheet);
-  if (aSheet->IsApplicable()) {
+  MOZ_ASSERT(aSheet);
+  RefPtr<StyleSheet> sheet = DocumentOrShadowRoot::RemoveSheet(*aSheet);
+  MOZ_ASSERT(sheet);
+  if (sheet->IsApplicable()) {
     if (mStyleRuleMap) {
-      mStyleRuleMap->SheetRemoved(*aSheet);
+      mStyleRuleMap->SheetRemoved(*sheet);
     }
-    Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), aSheet);
+    Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), sheet);
     ApplicableRulesChanged();
   }
 }
 
 void
 ShadowRoot::AddToIdTable(Element* aElement, nsAtom* aId)
 {
   nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_shadowroot_h__
 #define mozilla_dom_shadowroot_h__
 
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DocumentOrShadowRoot.h"
+#include "mozilla/dom/NameSpaceConstants.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIdentifierMapEntry.h"
 #include "nsTHashtable.h"
 
 class nsAtom;
 class nsIContent;
 class nsXBLPrototypeBinding;
@@ -31,35 +32,17 @@ namespace dom {
 
 class Element;
 
 class ShadowRoot final : public DocumentFragment,
                          public DocumentOrShadowRoot,
                          public nsStubMutationObserver
 {
 public:
-  static ShadowRoot* FromNode(nsINode* aNode)
-  {
-    return aNode->IsShadowRoot() ? static_cast<ShadowRoot*>(aNode) : nullptr;
-  }
-
-  static const ShadowRoot* FromNode(const nsINode* aNode)
-  {
-    return aNode->IsShadowRoot() ? static_cast<const ShadowRoot*>(aNode) : nullptr;
-  }
-
-  static ShadowRoot* FromNodeOrNull(nsINode* aNode)
-  {
-    return aNode ? FromNode(aNode) : nullptr;
-  }
-
-  static const ShadowRoot* FromNodeOrNull(const nsINode* aNode)
-  {
-    return aNode ? FromNode(aNode) : nullptr;
-  }
+  NS_IMPL_FROMNODE_HELPER(ShadowRoot, IsShadowRoot());
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot,
                                            DocumentFragment)
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3464,17 +3464,17 @@ nsDOMWindowUtils::AddSheet(nsIPreloadedS
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
   StyleSheet* sheet = nullptr;
   auto preloadedSheet = static_cast<PreloadedStyleSheet*>(aSheet);
   nsresult rv = preloadedSheet->GetSheet(&sheet);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(sheet, NS_ERROR_FAILURE);
 
-  if (sheet->GetAssociatedDocument()) {
+  if (sheet->GetAssociatedDocumentOrShadowRoot()) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsIDocument::additionalSheetType type = convertSheetType(aSheetType);
   return doc->AddAdditionalStyleSheet(type, sheet);
 }
 
 NS_IMETHODIMP
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1682,21 +1682,21 @@ nsDocument::~nsDocument()
     mChildren.ChildAt(indx)->UnbindFromTree();
     mChildren.RemoveChildAt(indx);
   }
   mFirstChild = nullptr;
   mCachedRootElement = nullptr;
 
   // Let the stylesheets know we're going away
   for (StyleSheet* sheet : mStyleSheets) {
-    sheet->ClearAssociatedDocument();
+    sheet->ClearAssociatedDocumentOrShadowRoot();
   }
   for (auto& sheets : mAdditionalSheets) {
     for (StyleSheet* sheet : sheets) {
-      sheet->ClearAssociatedDocument();
+      sheet->ClearAssociatedDocumentOrShadowRoot();
     }
   }
   if (mAttrStyleSheet) {
     mAttrStyleSheet->SetOwningDocument(nullptr);
   }
   // We don't own the mOnDemandBuiltInUASheets, so we don't need to reset them.
 
   if (mListenerManager) {
@@ -2417,17 +2417,17 @@ nsIDocument::MaybeDowngradePrincipal(nsI
   return principal.forget();
 }
 
 void
 nsIDocument::RemoveDocStyleSheetsFromStyleSets()
 {
   // The stylesheets should forget us
   for (StyleSheet* sheet : Reversed(mStyleSheets)) {
-    sheet->ClearAssociatedDocument();
+    sheet->ClearAssociatedDocumentOrShadowRoot();
 
     if (sheet->IsApplicable()) {
       nsCOMPtr<nsIPresShell> shell = GetShell();
       if (shell) {
         shell->StyleSet()->RemoveDocStyleSheet(sheet);
       }
     }
     // XXX Tell observers?
@@ -2436,17 +2436,17 @@ nsIDocument::RemoveDocStyleSheetsFromSty
 
 void
 nsIDocument::RemoveStyleSheetsFromStyleSets(
     const nsTArray<RefPtr<StyleSheet>>& aSheets,
     SheetType aType)
 {
   // The stylesheets should forget us
   for (StyleSheet* sheet : Reversed(aSheets)) {
-    sheet->ClearAssociatedDocument();
+    sheet->ClearAssociatedDocumentOrShadowRoot();
 
     if (sheet->IsApplicable()) {
       nsCOMPtr<nsIPresShell> shell = GetShell();
       if (shell) {
         shell->StyleSet()->RemoveStyleSheet(aType, sheet);
       }
     }
     // XXX Tell observers?
@@ -4323,55 +4323,53 @@ nsIDocument::NotifyStyleSheetRemoved(Sty
                                aDocumentSheet);
   }
 }
 
 void
 nsIDocument::AddStyleSheet(StyleSheet* aSheet)
 {
   MOZ_ASSERT(aSheet);
-  mStyleSheets.AppendElement(aSheet);
-  aSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
+  DocumentOrShadowRoot::AppendSheet(*aSheet);
 
   if (aSheet->IsApplicable()) {
     AddStyleSheetToStyleSets(aSheet);
   }
 
   NotifyStyleSheetAdded(aSheet, true);
 }
 
 void
 nsIDocument::RemoveStyleSheetFromStyleSets(StyleSheet* aSheet)
 {
-  nsCOMPtr<nsIPresShell> shell = GetShell();
-  if (shell) {
+  if (nsIPresShell* shell = GetShell()) {
     shell->StyleSet()->RemoveDocStyleSheet(aSheet);
   }
 }
 
 void
 nsIDocument::RemoveStyleSheet(StyleSheet* aSheet)
 {
-  MOZ_ASSERT(aSheet, "null arg");
-  RefPtr<StyleSheet> sheet = aSheet; // hold ref so it won't die too soon
-
-  if (!mStyleSheets.RemoveElement(aSheet)) {
+  MOZ_ASSERT(aSheet);
+  RefPtr<StyleSheet> sheet = DocumentOrShadowRoot::RemoveSheet(*aSheet);
+
+  if (!sheet) {
     NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
     return;
   }
 
   if (!mIsGoingAway) {
-    if (aSheet->IsApplicable()) {
-      RemoveStyleSheetFromStyleSets(aSheet);
-    }
-
-    NotifyStyleSheetRemoved(aSheet, true);
-  }
-
-  aSheet->ClearAssociatedDocument();
+    if (sheet->IsApplicable()) {
+      RemoveStyleSheetFromStyleSets(sheet);
+    }
+
+    NotifyStyleSheetRemoved(sheet, true);
+  }
+
+  sheet->ClearAssociatedDocumentOrShadowRoot();
 }
 
 void
 nsIDocument::UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>>& aOldSheets,
                                nsTArray<RefPtr<StyleSheet>>& aNewSheets)
 {
   BeginUpdate(UPDATE_STYLE);
 
@@ -4388,39 +4386,34 @@ nsIDocument::UpdateStyleSheets(nsTArray<
     // First remove the old sheet.
     NS_ASSERTION(oldSheet, "None of the old sheets should be null");
     int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
     RemoveStyleSheet(oldSheet);  // This does the right notifications
 
     // Now put the new one in its place.  If it's null, just ignore it.
     StyleSheet* newSheet = aNewSheets[i];
     if (newSheet) {
-      mStyleSheets.InsertElementAt(oldIndex, newSheet);
-      newSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
+      DocumentOrShadowRoot::InsertSheetAt(oldIndex, *newSheet);
       if (newSheet->IsApplicable()) {
         AddStyleSheetToStyleSets(newSheet);
       }
 
       NotifyStyleSheetAdded(newSheet, true);
     }
   }
 
   EndUpdate(UPDATE_STYLE);
 }
 
 void
 nsIDocument::InsertStyleSheetAt(StyleSheet* aSheet, size_t aIndex)
 {
   MOZ_ASSERT(aSheet);
 
-  // FIXME(emilio): Stop touching DocumentOrShadowRoot's members directly, and use an
-  // accessor.
-  mStyleSheets.InsertElementAt(aIndex, aSheet);
-
-  aSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
+  DocumentOrShadowRoot::InsertSheetAt(aIndex, *aSheet);
 
   if (aSheet->IsApplicable()) {
     AddStyleSheetToStyleSets(aSheet);
   }
 
   NotifyStyleSheetAdded(aSheet, true);
 }
 
@@ -4538,17 +4531,18 @@ nsIDocument::LoadAdditionalStyleSheet(ad
     default:
       MOZ_CRASH("impossible value for aType");
   }
 
   RefPtr<StyleSheet> sheet;
   nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, &sheet);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  sheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
+  sheet->SetAssociatedDocumentOrShadowRoot(
+     this, StyleSheet::OwnedByDocumentOrShadowRoot);
   MOZ_ASSERT(sheet->IsApplicable());
 
   return AddAdditionalStyleSheet(aType, sheet);
 }
 
 nsresult
 nsIDocument::AddAdditionalStyleSheet(additionalSheetType aType, StyleSheet* aSheet)
 {
@@ -4596,17 +4590,17 @@ nsIDocument::RemoveAdditionalStyleSheet(
       }
     }
 
     // Passing false, so documet.styleSheets.length will not be affected by
     // these additional sheets.
     NotifyStyleSheetRemoved(sheetRef, false);
     EndUpdate(UPDATE_STYLE);
 
-    sheetRef->ClearAssociatedDocument();
+    sheetRef->ClearAssociatedDocumentOrShadowRoot();
   }
 }
 
 nsIGlobalObject*
 nsIDocument::GetScopeObject() const
 {
   nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
   return scope;
@@ -11710,17 +11704,17 @@ nsIDocument::DocAddSizeOfIncludingThis(n
 
 static size_t
 SizeOfOwnedSheetArrayExcludingThis(const nsTArray<RefPtr<StyleSheet>>& aSheets,
                                    MallocSizeOf aMallocSizeOf)
 {
   size_t n = 0;
   n += aSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
   for (StyleSheet* sheet : aSheets) {
-    if (!sheet->GetAssociatedDocument()) {
+    if (!sheet->GetAssociatedDocumentOrShadowRoot()) {
       // Avoid over-reporting shared sheets.
       continue;
     }
     n += sheet->SizeOfIncludingThis(aMallocSizeOf);
   }
   return n;
 }
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3043,17 +3043,18 @@ NS_IMETHODIMP
 HTMLEditor::EnableStyleSheet(const nsAString& aURL,
                              bool aEnable)
 {
   RefPtr<StyleSheet> sheet = GetStyleSheetForURL(aURL);
   NS_ENSURE_TRUE(sheet, NS_OK); // Don't fail if sheet not found
 
   // Ensure the style sheet is owned by our document.
   nsCOMPtr<nsIDocument> document = GetDocument();
-  sheet->SetAssociatedDocument(document, StyleSheet::NotOwnedByDocument);
+  sheet->SetAssociatedDocumentOrShadowRoot(
+    document, StyleSheet::NotOwnedByDocumentOrShadowRoot);
 
   sheet->SetDisabled(!aEnable);
   return NS_OK;
 }
 
 bool
 HTMLEditor::EnableExistingStyleSheet(const nsAString& aURL)
 {
@@ -3061,17 +3062,18 @@ HTMLEditor::EnableExistingStyleSheet(con
 
   // Enable sheet if already loaded.
   if (!sheet) {
     return false;
   }
 
   // Ensure the style sheet is owned by our document.
   nsCOMPtr<nsIDocument> document = GetDocument();
-  sheet->SetAssociatedDocument(document, StyleSheet::NotOwnedByDocument);
+  sheet->SetAssociatedDocumentOrShadowRoot(
+    document, StyleSheet::NotOwnedByDocumentOrShadowRoot);
 
   // FIXME: This used to do sheet->SetDisabled(false), figure out if we can
   // just remove all this code in bug 1449522, since it seems unused.
   return true;
 }
 
 nsresult
 HTMLEditor::AddNewStyleSheetToList(const nsAString& aURL,
--- a/layout/style/ErrorReporter.cpp
+++ b/layout/style/ErrorReporter.cpp
@@ -172,16 +172,27 @@ ErrorReporter::ShouldReportErrors(const 
     return false;
   }
 
   bool report = false;
   shell->GetCssErrorReportingEnabled(&report);
   return report;
 }
 
+static nsINode*
+SheetOwner(const StyleSheet& aSheet)
+{
+  if (nsINode* owner = aSheet.GetOwnerNode()) {
+    return owner;
+  }
+
+  auto* associated = aSheet.GetAssociatedDocumentOrShadowRoot();
+  return associated ? &associated->AsNode() : nullptr;
+}
+
 bool
 ErrorReporter::ShouldReportErrors()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   EnsureGlobalsInitialized();
   if (!sReportErrors) {
     return false;
@@ -189,20 +200,17 @@ ErrorReporter::ShouldReportErrors()
 
   if (mInnerWindowID) {
     // We already reported an error, and that has cleared mSheet and mLoader, so
     // we'd get the bogus value otherwise.
     return true;
   }
 
   if (mSheet) {
-    nsINode* owner = mSheet->GetOwnerNode()
-      ? mSheet->GetOwnerNode()
-      : mSheet->GetAssociatedDocument();
-
+    nsINode* owner = SheetOwner(*mSheet);
     if (owner && ShouldReportErrors(*owner->OwnerDoc())) {
       return true;
     }
   }
 
   if (mLoader && mLoader->GetDocument() &&
       ShouldReportErrors(*mLoader->GetDocument())) {
     return true;
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -1238,18 +1238,17 @@ Loader::InsertSheetInDoc(StyleSheet* aSh
  * practice, we get the call to load the child sheet before the CSSOM
  * has finished inserting the @import rule, so we have no idea where
  * to put it anyway.  So just append for now.  (In the future if we
  * want to insert the sheet at the correct position, we'll need to
  * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
  * bug 1220506.)
  */
 nsresult
-Loader::InsertChildSheet(StyleSheet* aSheet,
-                         StyleSheet* aParentSheet)
+Loader::InsertChildSheet(StyleSheet* aSheet, StyleSheet* aParentSheet)
 {
   LOG(("css::Loader::InsertChildSheet"));
   MOZ_ASSERT(aSheet, "Nothing to insert");
   MOZ_ASSERT(aParentSheet, "Need a parent to insert into");
 
   // child sheets should always start out enabled, even if they got
   // cloned off of top-level sheets which were disabled
   aSheet->SetEnabled(true);
@@ -2156,21 +2155,19 @@ Loader::LoadChildSheet(StyleSheet* aPare
     LOG_WARN(("  Not enabled"));
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   LOG_URI("  Child uri: '%s'", aURL);
 
   nsCOMPtr<nsINode> owningNode;
 
-  // Check for an associated document: if none, don't bother walking up the
-  // parent sheets.
-  //
-  // FIXME(emilio): This looks wrong for Shadow DOM.
-  if (aParentSheet->GetAssociatedDocument()) {
+  // Check for an associated document or shadow root: if none, don't bother
+  // walking up the parent sheets.
+  if (aParentSheet->GetAssociatedDocumentOrShadowRoot()) {
     StyleSheet* topSheet = aParentSheet;
     while (StyleSheet* parent = topSheet->GetParentSheet()) {
       topSheet = parent;
     }
     owningNode = topSheet->GetOwnerNode();
   }
 
   nsINode* context = nullptr;
--- a/layout/style/MediaList.cpp
+++ b/layout/style/MediaList.cpp
@@ -40,20 +40,18 @@ MediaList::SetStyleSheet(StyleSheet* aSh
              "Multiple style sheets competing for one media list");
   mStyleSheet = aSheet;
 }
 
 template<typename Func>
 nsresult
 MediaList::DoMediaChange(Func aCallback)
 {
-  nsCOMPtr<nsIDocument> doc;
-  if (mStyleSheet) {
-    doc = mStyleSheet->GetAssociatedDocument();
-  }
+  nsIDocument* doc =
+    mStyleSheet ? mStyleSheet->GetComposedDoc() : nullptr;
   mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true);
   if (mStyleSheet) {
     mStyleSheet->WillDirty();
   }
 
   nsresult rv = aCallback();
   if (NS_FAILED(rv)) {
     return rv;
--- a/layout/style/Rule.cpp
+++ b/layout/style/Rule.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* base class for all rule types in a CSS style sheet */
 
 #include "Rule.h"
 
 #include "mozilla/css/GroupRule.h"
+#include "mozilla/dom/DocumentOrShadowRoot.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsIDocument.h"
 #include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace mozilla {
@@ -42,22 +43,22 @@ Rule::IsKnownLive() const
     return true;
   }
 
   StyleSheet* sheet = GetStyleSheet();
   if (!sheet) {
     return false;
   }
 
-  if (!sheet->IsOwnedByDocument()) {
+  if (!sheet->IsKeptAliveByDocument()) {
     return false;
   }
 
   return nsCCUncollectableMarker::InGeneration(
-    sheet->GetAssociatedDocument()->GetMarkedCCGeneration());
+    GetComposedDoc()->GetMarkedCCGeneration());
 }
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Rule)
   return tmp->IsCCLeaf() || tmp->IsKnownLive();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Rule)
   // Please see documentation for nsCycleCollectionParticipant::CanSkip* for why
--- a/layout/style/Rule.h
+++ b/layout/style/Rule.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* base class for all rule types in a CSS style sheet */
 
 #ifndef mozilla_css_Rule_h___
 #define mozilla_css_Rule_h___
 
 #include "mozilla/dom/CSSRuleBinding.h"
+#include "mozilla/dom/DocumentOrShadowRoot.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsISupports.h"
 #include "nsWrapperCache.h"
 
 class nsIDocument;
 struct nsRuleData;
 template<class T> struct already_AddRefed;
@@ -39,37 +40,38 @@ protected:
   Rule(const Rule& aCopy)
     : mSheet(aCopy.mSheet),
       mParentRule(aCopy.mParentRule),
       mLineNumber(aCopy.mLineNumber),
       mColumnNumber(aCopy.mColumnNumber)
   {
   }
 
-  virtual ~Rule() {}
+  virtual ~Rule() = default;
 
 public:
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(Rule)
   // Return true if this rule is known to be a cycle collection leaf, in the
   // sense that it doesn't have any outgoing owning edges.
   virtual bool IsCCLeaf() const MOZ_MUST_OVERRIDE;
 
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0;
 #endif
 
   StyleSheet* GetStyleSheet() const { return mSheet; }
 
-  // Return the document the rule lives in, if any
-  nsIDocument* GetDocument() const
+  // Return the document the rule applies to, if any.
+  //
+  // Suitable for style updates, and that's about it.
+  nsIDocument* GetComposedDoc() const
   {
-    StyleSheet* sheet = GetStyleSheet();
-    return sheet ? sheet->GetAssociatedDocument() : nullptr;
+    return mSheet ? mSheet->GetComposedDoc() : nullptr;
   }
 
   virtual void SetStyleSheet(StyleSheet* aSheet);
 
   // We don't reference count this up reference. The rule will tell us
   // when it's going away or when we're detached from it.
   void SetParentRule(Rule* aRule) {
 #ifdef DEBUG
@@ -94,17 +96,24 @@ public:
     const MOZ_MUST_OVERRIDE = 0;
 
   // WebIDL interface
   virtual uint16_t Type() const = 0;
   virtual void GetCssText(nsAString& aCssText) const = 0;
   void SetCssText(const nsAString& aCssText);
   Rule* GetParentRule() const;
   StyleSheet* GetParentStyleSheet() const { return GetStyleSheet(); }
-  nsIDocument* GetParentObject() const { return GetDocument(); }
+  nsINode* GetParentObject() const
+  {
+    if (!mSheet) {
+      return nullptr;
+    }
+    auto* associated = mSheet->GetAssociatedDocumentOrShadowRoot();
+    return associated ? &associated->AsNode() : nullptr;
+  }
 
 protected:
   // True if we're known-live for cycle collection purposes.
   bool IsKnownLive() const;
 
   // This is sometimes null (e.g., for style attributes).
   StyleSheet* mSheet;
   // When the parent GroupRule is destroyed, it will call SetParentRule(nullptr)
--- a/layout/style/ServoCounterStyleRule.cpp
+++ b/layout/style/ServoCounterStyleRule.cpp
@@ -51,20 +51,19 @@ ServoCounterStyleRule::GetName(nsAString
   nsAtom* name = Servo_CounterStyleRule_GetName(mRawRule);
   nsDependentAtomString nameStr(name);
   nsStyleUtil::AppendEscapedCSSIdent(nameStr, aName);
 }
 
 void
 ServoCounterStyleRule::SetName(const nsAString& aName)
 {
-  nsIDocument* doc = GetDocument();
   NS_ConvertUTF16toUTF8 name(aName);
   if (Servo_CounterStyleRule_SetName(mRawRule, &name)) {
-    MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+    MOZ_AUTO_DOC_UPDATE(GetComposedDoc(), UPDATE_STYLE, true);
     if (StyleSheet* sheet = GetStyleSheet()) {
       sheet->RuleChanged(this);
     }
   }
 }
 
 #define CSS_COUNTER_DESC(name_, method_)                        \
   void                                                          \
@@ -75,18 +74,17 @@ ServoCounterStyleRule::SetName(const nsA
       mRawRule, eCSSCounterDesc_##method_, &aValue);            \
   }                                                             \
   void                                                          \
   ServoCounterStyleRule::Set##method_(const nsAString& aValue)  \
   {                                                             \
     NS_ConvertUTF16toUTF8 value(aValue);                        \
     if (Servo_CounterStyleRule_SetDescriptor(                   \
           mRawRule, eCSSCounterDesc_##method_, &value)) {       \
-      nsIDocument* doc = GetDocument();                         \
-      MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);             \
+      MOZ_AUTO_DOC_UPDATE(GetComposedDoc(), UPDATE_STYLE, true);\
       if (StyleSheet* sheet = GetStyleSheet()) {                \
         sheet->RuleChanged(this);                               \
       }                                                         \
     }                                                           \
   }
 #include "nsCSSCounterDescList.h"
 #undef CSS_COUNTER_DESC
 
--- a/layout/style/ServoFontFaceRule.cpp
+++ b/layout/style/ServoFontFaceRule.cpp
@@ -126,17 +126,17 @@ css::Rule*
 ServoFontFaceRuleDecl::GetParentRule()
 {
   return ContainingRule();
 }
 
 nsINode*
 ServoFontFaceRuleDecl::GetParentObject()
 {
-  return ContainingRule()->GetDocument();
+  return ContainingRule()->GetParentObject();
 }
 
 JSObject*
 ServoFontFaceRuleDecl::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return CSSStyleDeclarationBinding::Wrap(cx, this, aGivenProto);
 }
 
--- a/layout/style/ServoKeyframeRule.cpp
+++ b/layout/style/ServoKeyframeRule.cpp
@@ -61,17 +61,17 @@ public:
       nsIPrincipal* aSubjectPrincipal) const final
   {
     return GetParsingEnvironmentForRule(mRule);
   }
   nsIDocument* DocToUpdate() final { return nullptr; }
 
   nsINode* GetParentObject() final
   {
-    return mRule ? mRule->GetDocument() : nullptr;
+    return mRule ? mRule->GetParentObject() : nullptr;
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   {
     size_t n = aMallocSizeOf(this);
     // TODO we may want to add size of mDecls as well
     return n;
   }
@@ -144,18 +144,17 @@ ServoKeyframeRule::List(FILE* out, int32
   fprintf_stderr(out, "%s\n", str.get());
 }
 #endif
 
 template<typename Func>
 void
 ServoKeyframeRule::UpdateRule(Func aCallback)
 {
-  nsIDocument* doc = GetDocument();
-  MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+  MOZ_AUTO_DOC_UPDATE(GetComposedDoc(), UPDATE_STYLE, true);
 
   aCallback();
 
   if (StyleSheet* sheet = GetStyleSheet()) {
     sheet->RuleChanged(this);
   }
 }
 
--- a/layout/style/ServoKeyframesRule.cpp
+++ b/layout/style/ServoKeyframesRule.cpp
@@ -227,18 +227,17 @@ ServoKeyframesRule::FindRuleIndexForKey(
   NS_ConvertUTF16toUTF8 key(aKey);
   return Servo_KeyframesRule_FindRule(mRawRule, &key);
 }
 
 template<typename Func>
 void
 ServoKeyframesRule::UpdateRule(Func aCallback)
 {
-  nsIDocument* doc = GetDocument();
-  MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+  MOZ_AUTO_DOC_UPDATE(GetComposedDoc(), UPDATE_STYLE, true);
 
   aCallback();
 
   if (StyleSheet* sheet = GetStyleSheet()) {
     sheet->RuleChanged(this);
   }
 }
 
--- a/layout/style/ServoPageRule.cpp
+++ b/layout/style/ServoPageRule.cpp
@@ -50,17 +50,17 @@ css::Rule*
 ServoPageRuleDeclaration::GetParentRule()
 {
   return Rule();
 }
 
 nsINode*
 ServoPageRuleDeclaration::GetParentObject()
 {
-  return Rule()->GetDocument();
+  return Rule()->GetParentObject();
 }
 
 DeclarationBlock*
 ServoPageRuleDeclaration::GetCSSDeclaration(Operation aOperation)
 {
   return mDecls;
 }
 
--- a/layout/style/ServoStyleRule.cpp
+++ b/layout/style/ServoStyleRule.cpp
@@ -53,32 +53,31 @@ css::Rule*
 ServoStyleRuleDeclaration::GetParentRule()
 {
   return Rule();
 }
 
 nsINode*
 ServoStyleRuleDeclaration::GetParentObject()
 {
-  return Rule()->GetDocument();
+  return Rule()->GetParentObject();
 }
 
 DeclarationBlock*
 ServoStyleRuleDeclaration::GetCSSDeclaration(Operation aOperation)
 {
   return mDecls;
 }
 
 nsresult
 ServoStyleRuleDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl)
 {
   ServoStyleRule* rule = Rule();
   if (RefPtr<StyleSheet> sheet = rule->GetStyleSheet()) {
-    nsCOMPtr<nsIDocument> doc = sheet->GetAssociatedDocument();
-    mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true);
+    mozAutoDocUpdate updateBatch(sheet->GetComposedDoc(), UPDATE_STYLE, true);
     if (aDecl != mDecls) {
       mDecls->SetOwningRule(nullptr);
       RefPtr<ServoDeclarationBlock> decls = aDecl->AsServo();
       Servo_StyleRule_SetStyle(rule->Raw(), decls->Raw());
       mDecls = decls.forget();
       mDecls->SetOwningRule(rule);
     }
     sheet->RuleChanged(rule);
@@ -192,19 +191,17 @@ ServoStyleRule::GetSelectorText(nsAStrin
 {
   Servo_StyleRule_GetSelectorText(mRawRule, &aSelectorText);
 }
 
 void
 ServoStyleRule::SetSelectorText(const nsAString& aSelectorText)
 {
   if (RefPtr<StyleSheet> sheet = GetStyleSheet()) {
-    nsIDocument* doc = sheet->GetAssociatedDocument();
-
-    mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true);
+    mozAutoDocUpdate updateBatch(sheet->GetComposedDoc(), UPDATE_STYLE, true);
 
     // StyleRule lives inside of the Inner, it is unsafe to call WillDirty
     // if sheet does not already have a unique Inner.
     sheet->AssertHasUniqueInner();
     sheet->WillDirty();
 
     const RawServoStyleSheetContents* contents = sheet->RawContents();
     if (Servo_StyleRule_SetSelectorText(contents, mRawRule, &aSelectorText)) {
@@ -213,17 +210,16 @@ ServoStyleRule::SetSelectorText(const ns
   }
 }
 
 uint32_t
 ServoStyleRule::GetSelectorCount()
 {
   uint32_t aCount;
   Servo_StyleRule_GetSelectorCount(mRawRule, &aCount);
-
   return aCount;
 }
 
 nsresult
 ServoStyleRule::GetSelectorText(uint32_t aSelectorIndex, nsAString& aText)
 {
   Servo_StyleRule_GetSelectorTextAtIndex(mRawRule, aSelectorIndex, &aText);
   return NS_OK;
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -25,44 +25,44 @@
 
 namespace mozilla {
 
 StyleSheet::StyleSheet(css::SheetParsingMode aParsingMode,
                        CORSMode aCORSMode,
                        net::ReferrerPolicy aReferrerPolicy,
                        const dom::SRIMetadata& aIntegrity)
   : mParent(nullptr)
-  , mDocument(nullptr)
+  , mDocumentOrShadowRoot(nullptr)
   , mOwningNode(nullptr)
   , mOwnerRule(nullptr)
   , mParsingMode(aParsingMode)
   , mDisabled(false)
   , mDirtyFlags(0)
-  , mDocumentAssociationMode(NotOwnedByDocument)
+  , mAssociationMode(NotOwnedByDocumentOrShadowRoot)
   , mInner(new StyleSheetInfo(aCORSMode, aReferrerPolicy, aIntegrity, aParsingMode))
 {
   mInner->AddSheet(this);
 }
 
 StyleSheet::StyleSheet(const StyleSheet& aCopy,
                        StyleSheet* aParentToUse,
                        dom::CSSImportRule* aOwnerRuleToUse,
-                       nsIDocument* aDocumentToUse,
+                       dom::DocumentOrShadowRoot* aDocumentOrShadowRoot,
                        nsINode* aOwningNodeToUse)
   : mParent(aParentToUse)
   , mTitle(aCopy.mTitle)
-  , mDocument(aDocumentToUse)
+  , mDocumentOrShadowRoot(aDocumentOrShadowRoot)
   , mOwningNode(aOwningNodeToUse)
   , mOwnerRule(aOwnerRuleToUse)
   , mParsingMode(aCopy.mParsingMode)
   , mDisabled(aCopy.mDisabled)
   , mDirtyFlags(aCopy.mDirtyFlags)
   // We only use this constructor during cloning.  It's the cloner's
   // responsibility to notify us if we end up being owned by a document.
-  , mDocumentAssociationMode(NotOwnedByDocument)
+  , mAssociationMode(NotOwnedByDocumentOrShadowRoot)
   , mInner(aCopy.mInner) // Shallow copy, but concrete subclasses will fix up.
 {
   MOZ_ASSERT(mInner, "Should only copy StyleSheets with an mInner.");
   mInner->AddSheet(this);
 
   if (HasForcedUniqueInner()) { // CSSOM's been there, force full copy now
     NS_ASSERTION(mInner->mComplete,
                  "Why have rules been accessed on an incomplete sheet?");
@@ -83,16 +83,42 @@ StyleSheet::~StyleSheet()
 }
 
 bool
 StyleSheet::HasRules() const
 {
   return Servo_StyleSheet_HasRules(Inner().mContents);
 }
 
+nsIDocument*
+StyleSheet::GetAssociatedDocument() const
+{
+  return mDocumentOrShadowRoot
+    ? mDocumentOrShadowRoot->AsNode().OwnerDoc()
+    : nullptr;
+}
+
+nsIDocument*
+StyleSheet::GetComposedDoc() const
+{
+  return mDocumentOrShadowRoot
+    ? mDocumentOrShadowRoot->AsNode().GetComposedDoc()
+    : nullptr;
+}
+
+bool
+StyleSheet::IsKeptAliveByDocument() const
+{
+  if (mAssociationMode != OwnedByDocumentOrShadowRoot) {
+    return false;
+  }
+
+  return !!GetComposedDoc();
+}
+
 void
 StyleSheet::LastRelease()
 {
   MOZ_ASSERT(mInner, "Should have an mInner at time of destruction.");
   MOZ_ASSERT(mInner->mSheets.Contains(this), "Our mInner should include us.");
 
   UnparentChildren();
 
@@ -123,17 +149,17 @@ StyleSheet::UnlinkInner()
   while (child) {
     MOZ_ASSERT(child->mParent == this, "We have a unique inner!");
     child->mParent = nullptr;
     // We (and child) might still think we're owned by a document, because
     // unlink order is non-deterministic, so the document's unlink, which would
     // tell us it does't own us anymore, may not have happened yet.  But if
     // we're being unlinked, clearly we're not owned by a document anymore
     // conceptually!
-    child->SetAssociatedDocument(nullptr, NotOwnedByDocument);
+    child->ClearAssociatedDocumentOrShadowRoot();
 
     RefPtr<StyleSheet> next;
     // Null out child->mNext, but don't let it die yet
     next.swap(child->mNext);
     // Switch to looking at the old value of child->mNext next iteration
     child.swap(next);
     // "next" is now our previous value of child; it'll get released
     // as we loop around.
@@ -221,24 +247,28 @@ StyleSheet::SetComplete()
   if (!mDisabled) {
     ApplicableStateChanged(true);
   }
 }
 
 void
 StyleSheet::ApplicableStateChanged(bool aApplicable)
 {
-  if (mDocument) {
-    mDocument->BeginUpdate(UPDATE_STYLE);
-    mDocument->SetStyleSheetApplicableState(this, aApplicable);
-    mDocument->EndUpdate(UPDATE_STYLE);
+  if (!mDocumentOrShadowRoot) {
+    return;
   }
 
-  if (dom::ShadowRoot* shadow = GetContainingShadow()) {
+  nsINode& node = mDocumentOrShadowRoot->AsNode();
+  if (auto* shadow = ShadowRoot::FromNode(node)) {
     shadow->StyleSheetApplicableStateChanged(*this, aApplicable);
+  } else {
+    nsIDocument* doc = node.AsDocument();
+    doc->BeginUpdate(UPDATE_STYLE);
+    doc->SetStyleSheetApplicableState(this, aApplicable);
+    doc->EndUpdate(UPDATE_STYLE);
   }
 }
 
 void
 StyleSheet::SetEnabled(bool aEnabled)
 {
   // Internal method, so callers must handle BeginUpdate/EndUpdate
   bool oldDisabled = mDisabled;
@@ -342,42 +372,42 @@ StyleSheetInfo::RemoveSheet(StyleSheet* 
 
   mSheets.RemoveElement(aSheet);
 }
 
 void
 StyleSheet::ChildSheetListBuilder::SetParentLinks(StyleSheet* aSheet)
 {
   aSheet->mParent = parent;
-  aSheet->SetAssociatedDocument(parent->mDocument,
-                                parent->mDocumentAssociationMode);
+  aSheet->SetAssociatedDocumentOrShadowRoot(parent->mDocumentOrShadowRoot,
+                                            parent->mAssociationMode);
 }
 
 void
 StyleSheet::ChildSheetListBuilder::ReparentChildList(StyleSheet* aPrimarySheet,
                                                      StyleSheet* aFirstChild)
 {
   for (StyleSheet *child = aFirstChild; child; child = child->mNext) {
     child->mParent = aPrimarySheet;
-    child->SetAssociatedDocument(aPrimarySheet->mDocument,
-                                 aPrimarySheet->mDocumentAssociationMode);
+    child->SetAssociatedDocumentOrShadowRoot(aPrimarySheet->mDocumentOrShadowRoot,
+                                             aPrimarySheet->mAssociationMode);
   }
 }
 
 void
 StyleSheet::GetType(nsAString& aType)
 {
   aType.AssignLiteral("text/css");
 }
 
 void
 StyleSheet::SetDisabled(bool aDisabled)
 {
   // DOM method, so handle BeginUpdate/EndUpdate
-  MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_STYLE, true);
+  MOZ_AUTO_DOC_UPDATE(GetComposedDoc(), UPDATE_STYLE, true);
   SetEnabled(!aDisabled);
 }
 
 void
 StyleSheet::GetHref(nsAString& aHref, ErrorResult& aRv)
 {
   if (nsIURI* sheetURI = Inner().mOriginalSheetURI) {
     nsAutoCString str;
@@ -550,17 +580,17 @@ StyleSheet::DeleteRuleFromGroup(css::Gro
   RefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
   NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
 
   // check that the rule actually belongs to this sheet!
   if (this != rule->GetStyleSheet()) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
+  mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_STYLE, true);
 
   WillDirty();
 
   nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
   NS_ENSURE_SUCCESS(result, result);
 
   rule->SetStyleSheet(nullptr);
   RuleRemoved(*rule);
@@ -591,40 +621,40 @@ StyleSheet::GetContainingShadow() const
 } while (0)
 
 void
 StyleSheet::RuleAdded(css::Rule& aRule)
 {
   mDirtyFlags |= MODIFIED_RULES;
   NOTIFY(RuleAdded, (*this, aRule));
 
-  if (mDocument) {
-    mDocument->StyleRuleAdded(this, &aRule);
+  if (nsIDocument* doc = GetComposedDoc()) {
+    doc->StyleRuleAdded(this, &aRule);
   }
 }
 
 void
 StyleSheet::RuleRemoved(css::Rule& aRule)
 {
   mDirtyFlags |= MODIFIED_RULES;
   NOTIFY(RuleRemoved, (*this, aRule));
 
-  if (mDocument) {
-    mDocument->StyleRuleRemoved(this, &aRule);
+  if (nsIDocument* doc = GetComposedDoc()) {
+    doc->StyleRuleRemoved(this, &aRule);
   }
 }
 
 void
 StyleSheet::RuleChanged(css::Rule* aRule)
 {
   mDirtyFlags |= MODIFIED_RULES;
   NOTIFY(RuleChanged, (*this, aRule));
 
-  if (mDocument) {
-    mDocument->StyleRuleChanged(this, aRule);
+  if (nsIDocument* doc = GetComposedDoc()) {
+    doc->StyleRuleChanged(this, aRule);
   }
 }
 
 #undef NOTIFY
 
 nsresult
 StyleSheet::InsertRuleIntoGroup(const nsAString& aRule,
                                 css::GroupRule* aGroup,
@@ -632,33 +662,33 @@ StyleSheet::InsertRuleIntoGroup(const ns
 {
   NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!");
   // check that the group actually belongs to this sheet!
   if (this != aGroup->GetStyleSheet()) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // parse and grab the rule
-  mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
+  mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_STYLE, true);
 
   WillDirty();
 
   nsresult result = InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
   NS_ENSURE_SUCCESS(result, result);
   RuleAdded(*aGroup->GetStyleRuleAt(aIndex));
 
   return NS_OK;
 }
 
 uint64_t
 StyleSheet::FindOwningWindowInnerID() const
 {
   uint64_t windowID = 0;
-  if (mDocument) {
-    windowID = mDocument->InnerWindowID();
+  if (nsIDocument* doc = GetAssociatedDocument()) {
+    windowID = doc->InnerWindowID();
   }
 
   if (windowID == 0 && mOwningNode) {
     windowID = mOwningNode->OwnerDoc()->InnerWindowID();
   }
 
   RefPtr<css::Rule> ownerRule;
   if (windowID == 0 && (ownerRule = GetDOMOwnerRule())) {
@@ -680,20 +710,20 @@ StyleSheet::UnparentChildren()
 {
   // XXXbz this is a little bogus; see the XXX comment where we
   // declare mFirstChild in StyleSheetInfo.
   for (StyleSheet* child = GetFirstChild();
        child;
        child = child->mNext) {
     if (child->mParent == this) {
       child->mParent = nullptr;
-      MOZ_ASSERT(child->mDocumentAssociationMode == NotOwnedByDocument,
+      MOZ_ASSERT(child->mAssociationMode == NotOwnedByDocumentOrShadowRoot,
                  "How did we get to the destructor, exactly, if we're owned "
                  "by a document?");
-      child->mDocument = nullptr;
+      child->mDocumentOrShadowRoot = nullptr;
     }
   }
 }
 
 void
 StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
                                           ErrorResult& aRv)
 {
@@ -752,43 +782,38 @@ StyleSheet::AreRulesAvailable(nsIPrincip
 
 StyleSheet*
 StyleSheet::GetFirstChild() const
 {
   return Inner().mFirstChild;
 }
 
 void
-StyleSheet::SetAssociatedDocument(nsIDocument* aDocument,
-                                  DocumentAssociationMode aAssociationMode)
+StyleSheet::SetAssociatedDocumentOrShadowRoot(DocumentOrShadowRoot* aDocOrShadowRoot,
+                                              AssociationMode aAssociationMode)
 {
-  MOZ_ASSERT(aDocument || aAssociationMode == NotOwnedByDocument);
+  MOZ_ASSERT(aDocOrShadowRoot ||
+             aAssociationMode == NotOwnedByDocumentOrShadowRoot);
 
   // not ref counted
-  mDocument = aDocument;
-  mDocumentAssociationMode = aAssociationMode;
+  mDocumentOrShadowRoot = aDocOrShadowRoot;
+  mAssociationMode = aAssociationMode;
 
   // Now set the same document on all our child sheets....
   // XXXbz this is a little bogus; see the XXX comment where we
   // declare mFirstChild.
   for (StyleSheet* child = GetFirstChild();
        child; child = child->mNext) {
     if (child->mParent == this) {
-      child->SetAssociatedDocument(aDocument, aAssociationMode);
+      child->SetAssociatedDocumentOrShadowRoot(aDocOrShadowRoot, aAssociationMode);
     }
   }
 }
 
 void
-StyleSheet::ClearAssociatedDocument()
-{
-  SetAssociatedDocument(nullptr, NotOwnedByDocument);
-}
-
-void
 StyleSheet::PrependStyleSheet(StyleSheet* aSheet)
 {
   WillDirty();
   PrependStyleSheetSilently(aSheet);
 }
 
 void
 StyleSheet::PrependStyleSheetSilently(StyleSheet* aSheet)
@@ -796,17 +821,17 @@ StyleSheet::PrependStyleSheetSilently(St
   MOZ_ASSERT(aSheet);
 
   aSheet->mNext = Inner().mFirstChild;
   Inner().mFirstChild = aSheet;
 
   // This is not reference counted. Our parent tells us when
   // it's going away.
   aSheet->mParent = this;
-  aSheet->SetAssociatedDocument(mDocument, mDocumentAssociationMode);
+  aSheet->SetAssociatedDocumentOrShadowRoot(mDocumentOrShadowRoot, mAssociationMode);
 }
 
 size_t
 StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = 0;
   const StyleSheet* s = this;
   while (s) {
@@ -1088,40 +1113,40 @@ StyleSheet::ReparseSheet(const nsAString
 {
   if (!mInner->mComplete) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
   // Hold strong ref to the CSSLoader in case the document update
   // kills the document
   RefPtr<css::Loader> loader;
-  if (mDocument) {
-    loader = mDocument->CSSLoader();
+  if (nsIDocument* doc = GetAssociatedDocument()) {
+    loader = doc->CSSLoader();
     NS_ASSERTION(loader, "Document with no CSS loader!");
   } else {
     loader = new css::Loader;
   }
 
-  mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
+  mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_STYLE, true);
 
   WillDirty();
 
   // cache child sheets to reuse
   css::LoaderReusableStyleSheets reusableSheets;
   for (StyleSheet* child = GetFirstChild(); child; child = child->mNext) {
     if (child->GetOriginalURI()) {
       reusableSheets.AddReusableSheet(child);
     }
   }
 
   // clean up child sheets list
   for (StyleSheet* child = GetFirstChild(); child; ) {
     StyleSheet* next = child->mNext;
     child->mParent = nullptr;
-    child->SetAssociatedDocument(nullptr, NotOwnedByDocument);
+    child->ClearAssociatedDocumentOrShadowRoot();
     child->mNext = nullptr;
     child = next;
   }
   Inner().mFirstChild = nullptr;
 
   uint32_t lineNumber = 1;
   if (mOwningNode) {
     nsCOMPtr<nsIStyleSheetLinkingElement> link = do_QueryInterface(mOwningNode);
@@ -1188,17 +1213,17 @@ StyleSheet::StyleSheetLoaded(StyleSheet*
 {
   if (!aSheet->GetParentSheet()) {
     return NS_OK; // ignore if sheet has been detached already
   }
   NS_ASSERTION(this == aSheet->GetParentSheet(),
                "We are being notified of a sheet load for a sheet that is not our child!");
 
   if (NS_SUCCEEDED(aStatus)) {
-    mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
+    mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_STYLE, true);
     RuleAdded(*aSheet->GetOwnerRule());
   }
 
   return NS_OK;
 }
 
 void
 StyleSheet::DropRuleList()
@@ -1207,24 +1232,24 @@ StyleSheet::DropRuleList()
     mRuleList->DropReference();
     mRuleList = nullptr;
   }
 }
 
 already_AddRefed<StyleSheet>
 StyleSheet::Clone(StyleSheet* aCloneParent,
                   dom::CSSImportRule* aCloneOwnerRule,
-                  nsIDocument* aCloneDocument,
+                  dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot,
                   nsINode* aCloneOwningNode) const
 {
   RefPtr<StyleSheet> clone =
     new StyleSheet(*this,
                    aCloneParent,
                    aCloneOwnerRule,
-                   aCloneDocument,
+                   aCloneDocumentOrShadowRoot,
                    aCloneOwningNode);
   return clone.forget();
 }
 
 ServoCSSRuleList*
 StyleSheet::GetCssRulesInternal()
 {
   if (!mRuleList) {
@@ -1241,17 +1266,17 @@ StyleSheet::GetCssRulesInternal()
 uint32_t
 StyleSheet::InsertRuleInternal(const nsAString& aRule,
                                uint32_t aIndex,
                                ErrorResult& aRv)
 {
   // Ensure mRuleList is constructed.
   GetCssRulesInternal();
 
-  mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
+  mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_STYLE, true);
   aRv = mRuleList->InsertRule(aRule, aIndex);
   if (aRv.Failed()) {
     return 0;
   }
 
   // XXX We may not want to get the rule when stylesheet change event
   // is not enabled.
   css::Rule* rule = mRuleList->GetRule(aIndex);
@@ -1268,17 +1293,17 @@ StyleSheet::DeleteRuleInternal(uint32_t 
 {
   // Ensure mRuleList is constructed.
   GetCssRulesInternal();
   if (aIndex >= mRuleList->Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
-  mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
+  mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_STYLE, true);
   // Hold a strong ref to the rule so it doesn't die when we remove it
   // from the list. XXX We may not want to hold it if stylesheet change
   // event is not enabled.
   RefPtr<css::Rule> rule = mRuleList->GetRule(aIndex);
   aRv = mRuleList->DeleteRule(aIndex);
   MOZ_ASSERT(!aRv.ErrorCodeIs(NS_ERROR_DOM_INDEX_SIZE_ERR),
              "IndexSizeError should have been handled earlier");
   if (!aRv.Failed()) {
--- a/layout/style/StyleSheet.h
+++ b/layout/style/StyleSheet.h
@@ -42,28 +42,29 @@ class Loader;
 class LoaderReusableStyleSheets;
 class Rule;
 class SheetLoadData;
 }
 
 namespace dom {
 class CSSImportRule;
 class CSSRuleList;
+class DocumentOrShadowRoot;
 class MediaList;
 class ShadowRoot;
 class SRIMetadata;
 } // namespace dom
 
 class StyleSheet final : public nsICSSLoaderObserver
                        , public nsWrapperCache
 {
   StyleSheet(const StyleSheet& aCopy,
              StyleSheet* aParentToUse,
              dom::CSSImportRule* aOwnerRuleToUse,
-             nsIDocument* aDocumentToUse,
+             dom::DocumentOrShadowRoot* aDocOrShadowRootToUse,
              nsINode* aOwningNodeToUse);
 
   virtual ~StyleSheet();
 
 public:
   StyleSheet(css::SheetParsingMode aParsingMode,
              CORSMode aCORSMode,
              net::ReferrerPolicy aReferrerPolicy,
@@ -205,17 +206,17 @@ public:
    */
   bool IsApplicable() const
   {
     return !mDisabled && Inner().mComplete;
   }
 
   already_AddRefed<StyleSheet> Clone(StyleSheet* aCloneParent,
                                      dom::CSSImportRule* aCloneOwnerRule,
-                                     nsIDocument* aCloneDocument,
+                                     dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot,
                                      nsINode* aCloneOwningNode) const;
 
   bool HasForcedUniqueInner() const
   {
     return mDirtyFlags & FORCED_UNIQUE_INNER;
   }
 
   bool HasModifiedRules() const
@@ -239,31 +240,48 @@ public:
   }
 
   void EnsureUniqueInner();
 
   // Append all of this sheet's child sheets to aArray.
   void AppendAllChildSheets(nsTArray<StyleSheet*>& aArray);
 
   // style sheet owner info
-  enum DocumentAssociationMode : uint8_t {
-    // OwnedByDocument means mDocument owns us (possibly via a chain of other
-    // stylesheets).
-    OwnedByDocument,
+  enum AssociationMode : uint8_t {
+    // OwnedByDocumentOrShadowRoot means mDocumentOrShadowRoot owns us (possibly
+    // via a chain of other stylesheets).
+    OwnedByDocumentOrShadowRoot,
     // NotOwnedByDocument means we're owned by something that might have a
     // different lifetime than mDocument.
-    NotOwnedByDocument
+    NotOwnedByDocumentOrShadowRoot
   };
-  nsIDocument* GetAssociatedDocument() const { return mDocument; }
-  bool IsOwnedByDocument() const {
-    return mDocumentAssociationMode == OwnedByDocument;
+  dom::DocumentOrShadowRoot* GetAssociatedDocumentOrShadowRoot() const
+  {
+    return mDocumentOrShadowRoot;
   }
-  // aDocument must not be null.
-  void SetAssociatedDocument(nsIDocument* aDocument, DocumentAssociationMode);
-  void ClearAssociatedDocument();
+
+  // Whether this stylesheet is kept alive by the associated document or
+  // associated shadow root's document somehow, and thus at least has the same
+  // lifetime as GetAssociatedDocument().
+  bool IsKeptAliveByDocument() const;
+
+  // Returns the document whose styles this sheet is affecting.
+  nsIDocument* GetComposedDoc() const;
+
+  // Returns the document we're associated to, via mDocumentOrShadowRoot.
+  //
+  // Non-null iff GetAssociatedDocumentOrShadowRoot is non-null.
+  nsIDocument* GetAssociatedDocument() const;
+
+  void SetAssociatedDocumentOrShadowRoot(dom::DocumentOrShadowRoot*,
+                                         AssociationMode);
+  void ClearAssociatedDocumentOrShadowRoot()
+  {
+    SetAssociatedDocumentOrShadowRoot(nullptr, NotOwnedByDocumentOrShadowRoot);
+  }
 
   nsINode* GetOwnerNode() const
   {
     return mOwningNode;
   }
 
   StyleSheet* GetParentSheet() const
   {
@@ -477,17 +495,17 @@ protected:
   void TraverseInner(nsCycleCollectionTraversalCallback &);
 
   // Return whether the given @import rule has pending child sheet.
   static bool RuleHasPendingChildSheet(css::Rule* aRule);
 
   StyleSheet* mParent;    // weak ref
 
   nsString mTitle;
-  nsIDocument* mDocument; // weak ref; parents maintain this for their children
+  dom::DocumentOrShadowRoot* mDocumentOrShadowRoot; // weak ref; parents maintain this for their children
   nsINode* mOwningNode; // weak ref
   dom::CSSImportRule* mOwnerRule; // weak ref
 
   RefPtr<dom::MediaList> mMedia;
 
   RefPtr<StyleSheet> mNext;
 
   // mParsingMode controls access to nonstandard style constructs that
@@ -498,20 +516,21 @@ protected:
   bool mDisabled;
 
   enum dirtyFlagAttributes {
     FORCED_UNIQUE_INNER = 0x1,
     MODIFIED_RULES = 0x2,
   };
   uint8_t mDirtyFlags; // has been modified
 
-  // mDocumentAssociationMode determines whether mDocument directly owns us (in
-  // the sense that if it's known-live then we're known-live).  Always
-  // NotOwnedByDocument when mDocument is null.
-  DocumentAssociationMode mDocumentAssociationMode;
+  // mAssociationMode determines whether mDocumentOrShadowRoot directly owns us
+  // (in the sense that if it's known-live then we're known-live).
+  //
+  // Always NotOwnedByDocumentOrShadowRoot when mDocumentOrShadowRoot is null.
+  AssociationMode mAssociationMode;
 
   // Core information we get from parsed sheets, which are shared amongst
   // StyleSheet clones.
   //
   // FIXME(emilio): Should be NonNull.
   StyleSheetInfo* mInner;
 
   nsTArray<ServoStyleSet*> mStyleSets;
--- a/layout/style/nsDOMCSSDeclaration.cpp
+++ b/layout/style/nsDOMCSSDeclaration.cpp
@@ -223,17 +223,17 @@ nsDOMCSSDeclaration::RemoveProperty(cons
 /* static */ nsDOMCSSDeclaration::ParsingEnvironment
 nsDOMCSSDeclaration::GetParsingEnvironmentForRule(const css::Rule* aRule)
 {
   StyleSheet* sheet = aRule ? aRule->GetStyleSheet() : nullptr;
   if (!sheet) {
     return { nullptr, eCompatibility_FullStandards, nullptr };
   }
 
-  if (nsIDocument* document = aRule->GetDocument()) {
+  if (nsIDocument* document = sheet->GetAssociatedDocument()) {
     return {
       sheet->URLData(),
       document->GetCompatibilityMode(),
       document->CSSLoader(),
     };
   }
 
   return {