Bug 1459529: Make sheets be associated to a shadow root too potentially. r?bz draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 11 May 2018 12:57:38 +0200
changeset 794258 52c0eb9a282537cf644a05c7004c11eb645bd9ba
parent 794257 89bb02029a49c10e179cac44f566b2f98214b249
child 794259 29dfd16ea517c5b763a38fe177dc65aa3eadee9b
push id109630
push userbmo:emilio@crisal.io
push dateFri, 11 May 2018 16:44:07 +0000
reviewersbz
bugs1459529
milestone62.0a1
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/ServoCSSRuleList.cpp
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);
+}
+
+RefPtr<StyleSheet>
+DocumentOrShadowRoot::RemoveSheet(StyleSheet& aSheet)
+{
+  auto index = mStyleSheets.IndexOf(&aSheet);
+  if (index == nsTArray<RefPtr<StyleSheet>>::NoIndex) {
+    return nullptr;
+  }
+  RefPtr<StyleSheet> sheet = Move(mStyleSheets[index]);
+  mStyleSheets.RemoveElementAt(index);
+  sheet->ClearAssociatedDocumentOrShadowRoot();
+  return sheet;
+}
+
 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.
    *
@@ -135,16 +122,21 @@ public:
     FLUSH_LAYOUT = 2,
     IS_ELEMENT_FROM_POINT = 4
   };
 
   void ElementsFromPointHelper(float aX, float aY, uint32_t aFlags,
                                nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
 
 protected:
+  // Returns the reference to the sheet, if found in mStyleSheets.
+  RefPtr<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
@@ -1685,21 +1685,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) {
@@ -2426,17 +2426,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?
@@ -2445,17 +2445,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?
@@ -4332,55 +4332,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);
 
@@ -4397,39 +4395,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);
 }
 
@@ -4547,17 +4540,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)
 {
@@ -4605,17 +4599,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;
@@ -11759,17 +11753,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
@@ -3044,17 +3044,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)
 {
@@ -3062,17 +3063,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);
@@ -2158,19 +2157,17 @@ Loader::LoadChildSheet(StyleSheet* aPare
   }
 
   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()) {
+  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,44 @@ 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;
+  }
+
+  // Return the document that owns this rule, if any.
+  nsIDocument* GetOwnerDoc() const
+  {
+    return mSheet ? mSheet->GetOwnerDoc() : 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 +102,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/ServoCSSRuleList.cpp
+++ b/layout/style/ServoCSSRuleList.cpp
@@ -171,17 +171,17 @@ ServoCSSRuleList::DropReference()
 nsresult
 ServoCSSRuleList::InsertRule(const nsAString& aRule, uint32_t aIndex)
 {
   MOZ_ASSERT(mStyleSheet, "Caller must ensure that "
              "the list is not unlinked from stylesheet");
   NS_ConvertUTF16toUTF8 rule(aRule);
   bool nested = !!mParentRule;
   css::Loader* loader = nullptr;
-  if (nsIDocument* doc = mStyleSheet->GetAssociatedDocument()) {
+  if (nsIDocument* doc = mStyleSheet->GetOwnerDoc()) {
     loader = doc->CSSLoader();
   }
   uint16_t type;
   nsresult rv = Servo_CssRules_InsertRule(mRawRules, mStyleSheet->RawContents(),
                                           &rule, aIndex, nested,
                                           loader, mStyleSheet, &type);
   if (NS_FAILED(rv)) {
     return rv;
--- 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
@@ -135,17 +135,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 GetServoCSSParsingEnvironmentForRule(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::GetOwnerDoc() 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 = GetOwnerDoc()) {
+    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 = GetOwnerDoc()) {
+    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,42 @@ 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();
+  bool IsKeptAliveByDocument() const;
+
+  // Returns the document whose styles this sheet is affecting.
+  nsIDocument* GetComposedDoc() const;
+
+  // Returns the document we belong to, if any.
+  nsIDocument* GetOwnerDoc() const;
+
+  void SetAssociatedDocumentOrShadowRoot(dom::DocumentOrShadowRoot*,
+                                         AssociationMode);
+  void ClearAssociatedDocumentOrShadowRoot()
+  {
+    SetAssociatedDocumentOrShadowRoot(nullptr, NotOwnedByDocumentOrShadowRoot);
+  }
 
   nsINode* GetOwnerNode() const
   {
     return mOwningNode;
   }
 
   StyleSheet* GetParentSheet() const
   {
@@ -477,17 +489,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 +510,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
@@ -231,17 +231,17 @@ nsDOMCSSDeclaration::RemoveProperty(cons
 /* static */ nsDOMCSSDeclaration::ServoCSSParsingEnvironment
 nsDOMCSSDeclaration::GetServoCSSParsingEnvironmentForRule(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 = aRule->GetOwnerDoc()) {
     return {
       sheet->URLData(),
       document->GetCompatibilityMode(),
       document->CSSLoader(),
     };
   }
 
   return {