Backed out 2 changesets (bug 1420762) for merge conflicts r=merge a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Fri, 01 Dec 2017 00:20:10 +0200
changeset 394459 8decab233d7d0ee5b8ab3881ddafb8d298e4a0cd
parent 394332 b43b77c2578007fd56ceec67209d0e1aa6109802
child 394460 fb3b97df05bd2d59a1e932ed1d07400f056832ef
push id97880
push usernbeleuzu@mozilla.com
push dateThu, 30 Nov 2017 22:47:54 +0000
treeherdermozilla-inbound@1c0622aa18cd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
bugs1420762
milestone59.0a1
backs outba350c82d8233199e3c822f0836f4ad1e588f518
00287b88254bc15ea553023a3ccad88094c37778
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
Backed out 2 changesets (bug 1420762) for merge conflicts r=merge a=merge Backed out changeset ba350c82d823 (bug 1420762) Backed out changeset 00287b88254b (bug 1420762)
dom/base/nsDocument.cpp
dom/base/nsIDocumentObserver.h
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/inspector/ServoStyleRuleMap.cpp
layout/inspector/ServoStyleRuleMap.h
layout/style/CSSStyleSheet.cpp
layout/style/CSSStyleSheet.h
layout/style/GroupRule.cpp
layout/style/MediaList.cpp
layout/style/ServoKeyframeRule.cpp
layout/style/ServoKeyframesRule.cpp
layout/style/ServoStyleRule.cpp
layout/style/ServoStyleSet.cpp
layout/style/ServoStyleSet.h
layout/style/ServoStyleSheet.cpp
layout/style/StyleRule.cpp
layout/style/StyleSetHandle.h
layout/style/StyleSetHandleInlines.h
layout/style/StyleSheet.cpp
layout/style/StyleSheet.h
layout/style/nsCSSCounterStyleRule.cpp
layout/style/nsCSSRules.cpp
layout/style/nsStyleSet.cpp
layout/style/nsStyleSet.h
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4728,18 +4728,16 @@ nsDocument::InsertStyleSheetAt(StyleShee
 
 void
 nsDocument::SetStyleSheetApplicableState(StyleSheet* aSheet,
                                          bool aApplicable)
 {
   NS_PRECONDITION(aSheet, "null arg");
 
   // If we're actually in the document style sheet list
-  //
-  // FIXME(emilio): Shadow DOM.
   MOZ_DIAGNOSTIC_ASSERT(aSheet->IsServo() == IsStyledByServo());
   if (mStyleSheets.IndexOf(aSheet) != mStyleSheets.NoIndex) {
     if (aApplicable) {
       AddStyleSheetToStyleSets(aSheet);
     } else {
       RemoveStyleSheetFromStyleSets(aSheet);
     }
   }
@@ -5752,52 +5750,55 @@ nsDocument::DocumentStatesChanged(EventS
   // Invalidate our cached state.
   mGotDocumentState &= ~aStateMask;
   mDocumentState &= ~aStateMask;
 
   NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
 }
 
 void
-nsDocument::StyleRuleChanged(StyleSheet* aSheet, css::Rule* aStyleRule)
-{
-  if (!StyleSheetChangeEventsEnabled()) {
-    return;
-  }
-
-  DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
-                             "StyleRuleChanged",
-                             mRule,
-                             aStyleRule);
-}
-
-void
-nsDocument::StyleRuleAdded(StyleSheet* aSheet, css::Rule* aStyleRule)
-{
-  if (!StyleSheetChangeEventsEnabled()) {
-    return;
-  }
-
-  DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
-                             "StyleRuleAdded",
-                             mRule,
-                             aStyleRule);
-}
-
-void
-nsDocument::StyleRuleRemoved(StyleSheet* aSheet, css::Rule* aStyleRule)
-{
-  if (!StyleSheetChangeEventsEnabled()) {
-    return;
-  }
-
-  DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
-                             "StyleRuleRemoved",
-                             mRule,
-                             aStyleRule);
+nsDocument::StyleRuleChanged(StyleSheet* aSheet,
+                             css::Rule* aStyleRule)
+{
+  NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, (aSheet, aStyleRule));
+
+  if (StyleSheetChangeEventsEnabled()) {
+    DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
+                               "StyleRuleChanged",
+                               mRule,
+                               aStyleRule);
+  }
+}
+
+void
+nsDocument::StyleRuleAdded(StyleSheet* aSheet,
+                           css::Rule* aStyleRule)
+{
+  NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded, (aSheet, aStyleRule));
+
+  if (StyleSheetChangeEventsEnabled()) {
+    DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
+                               "StyleRuleAdded",
+                               mRule,
+                               aStyleRule);
+  }
+}
+
+void
+nsDocument::StyleRuleRemoved(StyleSheet* aSheet,
+                             css::Rule* aStyleRule)
+{
+  NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved, (aSheet, aStyleRule));
+
+  if (StyleSheetChangeEventsEnabled()) {
+    DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
+                               "StyleRuleRemoved",
+                               mRule,
+                               aStyleRule);
+  }
 }
 
 #undef DO_STYLESHEET_NOTIFICATION
 
 already_AddRefed<AnonymousContent>
 nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
 {
   nsIPresShell* shell = GetShell();
--- a/dom/base/nsIDocumentObserver.h
+++ b/dom/base/nsIDocumentObserver.h
@@ -9,16 +9,22 @@
 #include "mozilla/EventStates.h"
 #include "mozilla/StyleSheet.h"
 #include "nsISupports.h"
 #include "nsIMutationObserver.h"
 
 class nsIContent;
 class nsIDocument;
 
+namespace mozilla {
+namespace css {
+class Rule;
+} // namespace css
+} // namespace mozilla
+
 #define NS_IDOCUMENT_OBSERVER_IID \
 { 0x71041fa3, 0x6dd7, 0x4cde, \
   { 0xbb, 0x76, 0xae, 0xcc, 0x69, 0xe1, 0x75, 0x78 } }
 
 typedef uint32_t nsUpdateType;
 
 #define UPDATE_CONTENT_MODEL 0x00000001
 #define UPDATE_STYLE         0x00000002
@@ -115,16 +121,55 @@ public:
    * This method is called automatically when the applicable state
    * of a StyleSheet gets changed. The style sheet passes this
    * notification to the document. The notification is passed on
    * to all of the document observers.
    *
    * @param aStyleSheet the StyleSheet that has changed state
    */
   virtual void StyleSheetApplicableStateChanged(mozilla::StyleSheet* aStyleSheet) = 0;
+
+  /**
+   * A StyleRule has just been modified within a style sheet.
+   * This method is called automatically when the rule gets
+   * modified. The style sheet passes this notification to
+   * the document. The notification is passed on to all of
+   * the document observers.
+   *
+   * @param aStyleSheet the StyleSheet that contians the rule
+   * @param aStyleRule the changed rule
+   */
+  virtual void StyleRuleChanged(mozilla::StyleSheet* aStyleSheet,
+                                mozilla::css::Rule* aStyleRule) = 0;
+
+  /**
+   * A StyleRule has just been added to a style sheet.
+   * This method is called automatically when the rule gets
+   * added to the sheet. The style sheet passes this
+   * notification to the document. The notification is passed on
+   * to all of the document observers.
+   *
+   * @param aStyleSheet the StyleSheet that has been modified
+   * @param aStyleRule the added rule
+   */
+  virtual void StyleRuleAdded(mozilla::StyleSheet* aStyleSheet,
+                              mozilla::css::Rule* aStyleRule) = 0;
+
+  /**
+   * A StyleRule has just been removed from a style sheet.
+   * This method is called automatically when the rule gets
+   * removed from the sheet. The style sheet passes this
+   * notification to the document. The notification is passed on
+   * to all of the document observers.
+   *
+   * @param aStyleSheet the StyleSheet that has been modified
+   * @param aStyleRule the removed rule
+   */
+  virtual void StyleRuleRemoved(mozilla::StyleSheet* aStyleSheet,
+                                mozilla::css::Rule* aStyleRule) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE                              \
     virtual void BeginUpdate(nsIDocument* aDocument,                         \
                              nsUpdateType aUpdateType) override;
 
@@ -153,26 +198,41 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumen
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED                        \
     virtual void StyleSheetRemoved(mozilla::StyleSheet* aStyleSheet,         \
                                    bool aDocumentSheet) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED         \
     virtual void StyleSheetApplicableStateChanged(                           \
         mozilla::StyleSheet* aStyleSheet) override;
 
+#define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED                         \
+    virtual void StyleRuleChanged(mozilla::StyleSheet* aStyleSheet,          \
+                                  mozilla::css::Rule* aStyleRule) override;
+
+#define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED                           \
+    virtual void StyleRuleAdded(mozilla::StyleSheet* aStyleSheet,            \
+                                mozilla::css::Rule* aStyleRule) override;
+
+#define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED                         \
+    virtual void StyleRuleRemoved(mozilla::StyleSheet* aStyleSheet,          \
+                                  mozilla::css::Rule* aStyleRule) override;
+
 #define NS_DECL_NSIDOCUMENTOBSERVER                                          \
     NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE                                  \
     NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE                                    \
     NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD                                    \
     NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD                                      \
     NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED                          \
     NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED                        \
     NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED                              \
     NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED                            \
     NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED             \
+    NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED                             \
+    NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED                               \
+    NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED                             \
     NS_DECL_NSIMUTATIONOBSERVER
 
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(_class)                     \
 void                                                                      \
 _class::BeginUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType)     \
 {                                                                         \
 }                                                                         \
@@ -219,10 +279,25 @@ void                                    
 _class::StyleSheetRemoved(mozilla::StyleSheet* aStyleSheet,               \
                           bool aDocumentSheet)                            \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::StyleSheetApplicableStateChanged(mozilla::StyleSheet* aStyleSheet)\
 {                                                                         \
 }                                                                         \
+void                                                                      \
+_class::StyleRuleChanged(mozilla::StyleSheet* aStyleSheet,                \
+                         mozilla::css::Rule* aStyleRule)                  \
+{                                                                         \
+}                                                                         \
+void                                                                      \
+_class::StyleRuleAdded(mozilla::StyleSheet* aStyleSheet,                  \
+                       mozilla::css::Rule* aStyleRule)                    \
+{                                                                         \
+}                                                                         \
+void                                                                      \
+_class::StyleRuleRemoved(mozilla::StyleSheet* aStyleSheet,                \
+                         mozilla::css::Rule* aStyleRule)                  \
+{                                                                         \
+}
 
 #endif /* nsIDocumentObserver_h___ */
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4648,16 +4648,34 @@ void
 PresShell::StyleSheetApplicableStateChanged(StyleSheet* aStyleSheet)
 {
   if (aStyleSheet->HasRules()) {
     RecordStyleSheetChange(
         aStyleSheet, StyleSheet::ChangeType::ApplicableStateChanged);
   }
 }
 
+void
+PresShell::StyleRuleChanged(StyleSheet* aStyleSheet, css::Rule* aStyleRule)
+{
+  RecordStyleSheetChange(aStyleSheet, StyleSheet::ChangeType::RuleChanged);
+}
+
+void
+PresShell::StyleRuleAdded(StyleSheet* aStyleSheet, css::Rule* aStyleRule)
+{
+  RecordStyleSheetChange(aStyleSheet, StyleSheet::ChangeType::RuleAdded);
+}
+
+void
+PresShell::StyleRuleRemoved(StyleSheet* aStyleSheet, css::Rule* aStyleRule)
+{
+  RecordStyleSheetChange(aStyleSheet, StyleSheet::ChangeType::RuleRemoved);
+}
+
 nsresult
 PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags,
                           nscolor aBackgroundColor,
                           gfxContext* aThebesContext)
 {
   NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED);
 
   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -294,16 +294,19 @@ public:
   NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
   NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD
   NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD
   NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED
   NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED
   NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED
   NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED
   NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED
+  NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED
+  NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED
+  NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
--- a/layout/inspector/ServoStyleRuleMap.cpp
+++ b/layout/inspector/ServoStyleRuleMap.cpp
@@ -22,70 +22,85 @@ ServoStyleRuleMap::ServoStyleRuleMap(Ser
   : mStyleSet(aStyleSet)
 {
 }
 
 ServoStyleRuleMap::~ServoStyleRuleMap()
 {
 }
 
+NS_IMPL_ISUPPORTS(ServoStyleRuleMap, nsIDocumentObserver, nsICSSLoaderObserver)
+
 void
 ServoStyleRuleMap::EnsureTable()
 {
   if (!IsEmpty()) {
     return;
   }
   mStyleSet->EnumerateStyleSheetArrays(
     [this](const nsTArray<RefPtr<ServoStyleSheet>>& aArray) {
       for (auto& sheet : aArray) {
-        FillTableFromStyleSheet(*sheet);
+        FillTableFromStyleSheet(sheet);
       }
     });
 }
 
 void
-ServoStyleRuleMap::SheetAdded(ServoStyleSheet& aStyleSheet)
+ServoStyleRuleMap::StyleSheetAdded(StyleSheet* aStyleSheet,
+                                   bool aDocumentSheet)
 {
   if (!IsEmpty()) {
-    FillTableFromStyleSheet(aStyleSheet);
+    FillTableFromStyleSheet(aStyleSheet->AsServo());
   }
 }
 
 void
-ServoStyleRuleMap::SheetRemoved(ServoStyleSheet& aStyleSheet)
+ServoStyleRuleMap::StyleSheetRemoved(StyleSheet* aStyleSheet,
+                                     bool aDocumentSheet)
 {
   // Invalidate all data inside. This isn't strictly necessary since
   // we should always get update from document before new queries come.
   // But it is probably still safer if we try to avoid having invalid
   // pointers inside. Also if the document keep adding and removing
   // stylesheets, this would also prevent us from infinitely growing
   // memory usage.
   mTable.Clear();
 }
 
 void
-ServoStyleRuleMap::RuleAdded(ServoStyleSheet& aStyleSheet, css::Rule& aStyleRule)
+ServoStyleRuleMap::StyleSheetApplicableStateChanged(StyleSheet* aStyleSheet)
+{
+  // We don't care if the stylesheet is disabled. Not removing no longer
+  // applicable stylesheets wouldn't make anything wrong.
+  if (!IsEmpty() && aStyleSheet->IsApplicable()) {
+    FillTableFromStyleSheet(aStyleSheet->AsServo());
+  }
+}
+
+void
+ServoStyleRuleMap::StyleRuleAdded(StyleSheet* aStyleSheet,
+                                  css::Rule* aStyleRule)
 {
   if (!IsEmpty()) {
     FillTableFromRule(aStyleRule);
   }
 }
 
 void
-ServoStyleRuleMap::RuleRemoved(ServoStyleSheet& aStyleSheet,
-                               css::Rule& aStyleRule)
+ServoStyleRuleMap::StyleRuleRemoved(StyleSheet* aStyleSheet,
+                                    css::Rule* aStyleRule)
 {
   if (IsEmpty()) {
     return;
   }
 
-  switch (aStyleRule.Type()) {
+  switch (aStyleRule->Type()) {
     case nsIDOMCSSRule::STYLE_RULE: {
-      auto& rule = static_cast<ServoStyleRule&>(aStyleRule);
-      mTable.Remove(rule.Raw());
+      auto rule = static_cast<ServoStyleRule*>(aStyleRule);
+      mTable.Remove(rule->Raw());
       break;
     }
     case nsIDOMCSSRule::IMPORT_RULE:
     case nsIDOMCSSRule::MEDIA_RULE:
     case nsIDOMCSSRule::SUPPORTS_RULE:
     case nsIDOMCSSRule::DOCUMENT_RULE: {
       // See the comment in StyleSheetRemoved.
       mTable.Clear();
@@ -99,59 +114,70 @@ ServoStyleRuleMap::RuleRemoved(ServoStyl
     case nsIDOMCSSRule::COUNTER_STYLE_RULE:
     case nsIDOMCSSRule::FONT_FEATURE_VALUES_RULE:
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unhandled rule");
   }
 }
 
+NS_IMETHODIMP
+ServoStyleRuleMap::StyleSheetLoaded(StyleSheet* aSheet,
+                                    bool aWasAlternate,
+                                    nsresult aStatus)
+{
+  MOZ_ASSERT(aSheet->IsServo());
+  if (!IsEmpty()) {
+    FillTableFromStyleSheet(aSheet->AsServo());
+  }
+  return NS_OK;
+}
+
 size_t
 ServoStyleRuleMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
   n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
   return n;
 }
 
 void
-ServoStyleRuleMap::FillTableFromRule(css::Rule& aRule)
+ServoStyleRuleMap::FillTableFromRule(css::Rule* aRule)
 {
-  switch (aRule.Type()) {
+  switch (aRule->Type()) {
     case nsIDOMCSSRule::STYLE_RULE: {
-      auto& rule = static_cast<ServoStyleRule&>(aRule);
-      mTable.Put(rule.Raw(), &rule);
+      auto rule = static_cast<ServoStyleRule*>(aRule);
+      mTable.Put(rule->Raw(), rule);
       break;
     }
     case nsIDOMCSSRule::MEDIA_RULE:
     case nsIDOMCSSRule::SUPPORTS_RULE:
     case nsIDOMCSSRule::DOCUMENT_RULE: {
-      auto& rule = static_cast<css::GroupRule&>(aRule);
-      auto ruleList = static_cast<ServoCSSRuleList*>(rule.CssRules());
-      FillTableFromRuleList(*ruleList);
+      auto rule = static_cast<css::GroupRule*>(aRule);
+      auto ruleList = static_cast<ServoCSSRuleList*>(rule->CssRules());
+      FillTableFromRuleList(ruleList);
       break;
     }
     case nsIDOMCSSRule::IMPORT_RULE: {
-      auto& rule = static_cast<ServoImportRule&>(aRule);
-      MOZ_ASSERT(aRule.GetStyleSheet());
-      FillTableFromStyleSheet(*rule.GetStyleSheet()->AsServo());
+      auto rule = static_cast<ServoImportRule*>(aRule);
+      FillTableFromStyleSheet(rule->GetStyleSheet()->AsServo());
       break;
     }
   }
 }
 
 void
-ServoStyleRuleMap::FillTableFromRuleList(ServoCSSRuleList& aRuleList)
+ServoStyleRuleMap::FillTableFromRuleList(ServoCSSRuleList* aRuleList)
 {
-  for (uint32_t i : IntegerRange(aRuleList.Length())) {
-    FillTableFromRule(*aRuleList.GetRule(i));
+  for (uint32_t i : IntegerRange(aRuleList->Length())) {
+    FillTableFromRule(aRuleList->GetRule(i));
   }
 }
 
 void
-ServoStyleRuleMap::FillTableFromStyleSheet(ServoStyleSheet& aSheet)
+ServoStyleRuleMap::FillTableFromStyleSheet(ServoStyleSheet* aSheet)
 {
-  if (aSheet.IsComplete()) {
-    FillTableFromRuleList(*aSheet.GetCssRulesInternal());
+  if (aSheet->IsComplete()) {
+    FillTableFromRuleList(aSheet->GetCssRulesInternal());
   }
 }
 
 } // namespace mozilla
--- a/layout/inspector/ServoStyleRuleMap.h
+++ b/layout/inspector/ServoStyleRuleMap.h
@@ -5,55 +5,65 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ServoStyleRuleMap_h
 #define mozilla_ServoStyleRuleMap_h
 
 #include "mozilla/ServoStyleRule.h"
 
 #include "nsDataHashtable.h"
+#include "nsICSSLoaderObserver.h"
+#include "nsStubDocumentObserver.h"
 
 struct RawServoStyleRule;
 
 namespace mozilla {
 class ServoCSSRuleList;
 class ServoStyleSet;
 namespace css {
 class Rule;
 } // namespace css
 
-class ServoStyleRuleMap
+class ServoStyleRuleMap final : public nsStubDocumentObserver
+                              , public nsICSSLoaderObserver
 {
 public:
+  NS_DECL_ISUPPORTS
+
   explicit ServoStyleRuleMap(ServoStyleSet* aStyleSet);
 
   void EnsureTable();
   ServoStyleRule* Lookup(const RawServoStyleRule* aRawRule) const {
     return mTable.Get(aRawRule);
   }
 
-  void SheetAdded(ServoStyleSheet& aStyleSheet);
-  void SheetRemoved(ServoStyleSheet& aStyleSheet);
+  // nsIDocumentObserver methods
+  void StyleSheetAdded(StyleSheet* aStyleSheet, bool aDocumentSheet) final;
+  void StyleSheetRemoved(StyleSheet* aStyleSheet, bool aDocumentSheet) final;
+  void StyleSheetApplicableStateChanged(StyleSheet* aStyleSheet) final;
+  void StyleRuleAdded(StyleSheet* aStyleSheet, css::Rule* aStyleRule) final;
+  void StyleRuleRemoved(StyleSheet* aStyleSheet, css::Rule* aStyleRule) final;
 
-  void RuleAdded(ServoStyleSheet& aStyleSheet, css::Rule&);
-  void RuleRemoved(ServoStyleSheet& aStyleSheet, css::Rule& aStyleRule);
+  // nsICSSLoaderObserver
+  NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet,
+                              bool aWasAlternate, nsresult aStatus) final;
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
+private:
   ~ServoStyleRuleMap();
 
-private:
   // Since we would never have a document which contains no style rule,
   // we use IsEmpty as an indication whether we need to walk through
   // all stylesheets to fill the table.
   bool IsEmpty() const { return mTable.Count() == 0; }
 
-  void FillTableFromRule(css::Rule& aRule);
-  void FillTableFromRuleList(ServoCSSRuleList& aRuleList);
-  void FillTableFromStyleSheet(ServoStyleSheet& aSheet);
+  void FillTableFromRule(css::Rule* aRule);
+  void FillTableFromRuleList(ServoCSSRuleList* aRuleList);
+  void FillTableFromStyleSheet(ServoStyleSheet* aSheet);
 
   typedef nsDataHashtable<nsPtrHashKey<const RawServoStyleRule>,
                           WeakPtr<ServoStyleRule>> Hashtable;
   Hashtable mTable;
   ServoStyleSet* mStyleSet;
 };
 
 } // namespace mozilla
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -761,18 +761,19 @@ CSSStyleSheet::InsertRuleInternal(const 
     aRv = RegisterNamespaceRule(rule);
     if (NS_WARN_IF(aRv.Failed())) {
       return 0;
     }
   }
 
   // We don't notify immediately for @import rules, but rather when
   // the sheet the rule is importing is loaded (see StyleSheetLoaded)
-  if (type != css::Rule::IMPORT_RULE || !RuleHasPendingChildSheet(rule)) {
-    RuleAdded(*rule);
+  if ((type != css::Rule::IMPORT_RULE || !RuleHasPendingChildSheet(rule)) &&
+      mDocument) {
+    mDocument->StyleRuleAdded(this, rule);
   }
 
   return aIndex;
 }
 
 void
 CSSStyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv)
 {
@@ -789,17 +790,21 @@ CSSStyleSheet::DeleteRuleInternal(uint32
   NS_ASSERTION(uint32_t(Inner()->mOrderedRules.Count()) <= INT32_MAX,
                "Too many style rules!");
 
   // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt
   RefPtr<css::Rule> rule = Inner()->mOrderedRules.ObjectAt(aIndex);
   if (rule) {
     Inner()->mOrderedRules.RemoveObjectAt(aIndex);
     rule->SetStyleSheet(nullptr);
-    RuleRemoved(*rule);
+    DidDirty();
+
+    if (mDocument) {
+      mDocument->StyleRuleRemoved(this, rule);
+    }
   }
 }
 
 nsresult
 CSSStyleSheet::InsertRuleIntoGroupInternal(const nsAString& aRule,
                                            css::GroupRule* aGroup,
                                            uint32_t aIndex)
 {
@@ -855,19 +860,22 @@ CSSStyleSheet::StyleSheetLoaded(StyleShe
   CSSStyleSheet* sheet = aSheet->AsGecko();
 
   if (sheet->GetParentSheet() == nullptr) {
     return NS_OK; // ignore if sheet has been detached already (see parseSheet)
   }
   NS_ASSERTION(this == sheet->GetParentSheet(),
                "We are being notified of a sheet load for a sheet that is not our child!");
 
-  if (NS_SUCCEEDED(aStatus)) {
+  if (mDocument && NS_SUCCEEDED(aStatus)) {
     mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
-    RuleAdded(*sheet->GetOwnerRule());
+
+    // XXXldb @import rules shouldn't even implement nsIStyleRule (but
+    // they do)!
+    mDocument->StyleRuleAdded(this, sheet->GetOwnerRule());
   }
 
   return NS_OK;
 }
 
 nsresult
 CSSStyleSheet::ReparseSheet(const nsAString& aInput)
 {
@@ -904,17 +912,19 @@ CSSStyleSheet::ReparseSheet(const nsAStr
       nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
       importRule->GetStyleSheet(getter_AddRefs(childSheet));
 
       RefPtr<CSSStyleSheet> cssSheet = do_QueryObject(childSheet);
       if (cssSheet && cssSheet->GetOriginalURI()) {
         reusableSheets.AddReusableSheet(cssSheet);
       }
     }
-    RuleRemoved(*rule);
+    if (mDocument) {
+      mDocument->StyleRuleRemoved(this, rule);
+    }
   }
 
   // nuke child sheets list and current namespace map
   for (StyleSheet* child = GetFirstChild(); child; ) {
     NS_ASSERTION(child->mParent == this, "Child sheet is not parented to this!");
     StyleSheet* next = child->mNext;
     child->mParent = nullptr;
     child->SetAssociatedDocument(nullptr, NotOwnedByDocument);
@@ -934,20 +944,22 @@ CSSStyleSheet::ReparseSheet(const nsAStr
 
   nsCSSParser parser(loader, this);
   nsresult rv = parser.ParseSheet(aInput, mInner->mSheetURI, mInner->mBaseURI,
                                   mInner->mPrincipal, lineNumber, &reusableSheets);
   DidDirty(); // we are always 'dirty' here since we always remove rules first
   NS_ENSURE_SUCCESS(rv, rv);
 
   // notify document of all new rules
-  for (int32_t index = 0; index < Inner()->mOrderedRules.Count(); ++index) {
-    RefPtr<css::Rule> rule = Inner()->mOrderedRules.ObjectAt(index);
-    if (rule->GetType() == css::Rule::IMPORT_RULE &&
-        RuleHasPendingChildSheet(rule)) {
-      continue; // notify when loaded (see StyleSheetLoaded)
+  if (mDocument) {
+    for (int32_t index = 0; index < Inner()->mOrderedRules.Count(); ++index) {
+      RefPtr<css::Rule> rule = Inner()->mOrderedRules.ObjectAt(index);
+      if (rule->GetType() == css::Rule::IMPORT_RULE &&
+          RuleHasPendingChildSheet(rule)) {
+        continue; // notify when loaded (see StyleSheetLoaded)
+      }
+      mDocument->StyleRuleAdded(this, rule);
     }
-    RuleAdded(*rule);
   }
   return NS_OK;
 }
 
 } // namespace mozilla
--- a/layout/style/CSSStyleSheet.h
+++ b/layout/style/CSSStyleSheet.h
@@ -115,16 +115,22 @@ public:
     return Inner()->mNameSpaceMap;
   }
 
   already_AddRefed<StyleSheet> Clone(StyleSheet* aCloneParent,
     dom::CSSImportRule* aCloneOwnerRule,
     nsIDocument* aCloneDocument,
     nsINode* aCloneOwningNode) const final;
 
+  void SetModifiedByChildRule() {
+    NS_ASSERTION(mDirty,
+                 "sheet must be marked dirty before handing out child rules");
+    DidDirty();
+  }
+
   nsresult AddRuleProcessor(nsCSSRuleProcessor* aProcessor);
   nsresult DropRuleProcessor(nsCSSRuleProcessor* aProcessor);
 
   // nsICSSLoaderObserver interface
   NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasAlternate,
                               nsresult aStatus) override;
 
   bool UseForPresentation(nsPresContext* aPresContext,
--- a/layout/style/GroupRule.cpp
+++ b/layout/style/GroupRule.cpp
@@ -299,17 +299,17 @@ GroupRule::SetStyleSheet(StyleSheet* aSh
 void
 GroupRule::AppendStyleRule(Rule* aRule)
 {
   GeckoRules().AppendObject(aRule);
   StyleSheet* sheet = GetStyleSheet();
   aRule->SetStyleSheet(sheet);
   aRule->SetParentRule(this);
   if (sheet) {
-    sheet->RuleChanged(this);
+    sheet->AsGecko()->SetModifiedByChildRule();
   }
 }
 
 bool
 GroupRule::EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const
 {
   for (const Rule* rule : GeckoRules()) {
     if (!aFunc(const_cast<Rule*>(rule), aData)) {
--- a/layout/style/MediaList.cpp
+++ b/layout/style/MediaList.cpp
@@ -57,22 +57,22 @@ MediaList::DoMediaChange(Func aCallback)
   }
 
   nsresult rv = aCallback();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mStyleSheet) {
-    // FIXME(emilio): We should discern between "owned by a rule" (as in @media)
-    // and "owned by a sheet" (as in <style media>), and then pass something
-    // meaningful here.
-    mStyleSheet->RuleChanged(nullptr);
+    mStyleSheet->DidDirty();
   }
-
+  /* XXXldb Pass something meaningful? */
+  if (doc) {
+    doc->StyleRuleChanged(mStyleSheet, nullptr);
+  }
   return rv;
 }
 
 /* static */ already_AddRefed<MediaList>
 MediaList::Create(
     StyleBackendType aBackendType,
     const nsAString& aMedia,
     CallerType aCallerType)
--- a/layout/style/ServoKeyframeRule.cpp
+++ b/layout/style/ServoKeyframeRule.cpp
@@ -169,17 +169,20 @@ void
 ServoKeyframeRule::UpdateRule(Func aCallback)
 {
   nsIDocument* doc = GetDocument();
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
   aCallback();
 
   if (StyleSheet* sheet = GetStyleSheet()) {
-    sheet->RuleChanged(this);
+    // FIXME sheet->AsGecko()->SetModifiedByChildRule();
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this);
+    }
   }
 }
 
 NS_IMETHODIMP
 ServoKeyframeRule::GetKeyText(nsAString& aKeyText)
 {
   Servo_Keyframe_GetKeyText(mRaw, &aKeyText);
   return NS_OK;
--- a/layout/style/ServoKeyframesRule.cpp
+++ b/layout/style/ServoKeyframesRule.cpp
@@ -247,17 +247,20 @@ void
 ServoKeyframesRule::UpdateRule(Func aCallback)
 {
   nsIDocument* doc = GetDocument();
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
   aCallback();
 
   if (StyleSheet* sheet = GetStyleSheet()) {
-    sheet->RuleChanged(this);
+    // FIXME sheet->AsGecko()->SetModifiedByChildRule();
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this);
+    }
   }
 }
 
 NS_IMETHODIMP
 ServoKeyframesRule::GetName(nsAString& aName)
 {
   nsAtom* name = Servo_KeyframesRule_GetName(mRawRule);
   aName = nsDependentAtomString(name);
--- a/layout/style/ServoStyleRule.cpp
+++ b/layout/style/ServoStyleRule.cpp
@@ -76,17 +76,19 @@ ServoStyleRuleDeclaration::SetCSSDeclara
     mozAutoDocUpdate updateBatch(doc, 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);
+    if (doc) {
+      doc->StyleRuleChanged(sheet, rule);
+    }
   }
   return NS_OK;
 }
 
 nsIDocument*
 ServoStyleRuleDeclaration::DocToUpdate()
 {
   return nullptr;
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -167,23 +167,35 @@ ServoStyleSet::Init(nsPresContext* aPres
   }
 
   // We added prefilled stylesheets into mRawSet, so the stylist is dirty.
   // The Stylist should be updated later when necessary.
   SetStylistStyleSheetsDirty();
 }
 
 void
+ServoStyleSet::BeginShutdown()
+{
+  nsIDocument* doc = mPresContext->Document();
+
+  // Remove the style rule map from document's observer and drop it.
+  if (mStyleRuleMap) {
+    doc->RemoveObserver(mStyleRuleMap);
+    doc->CSSLoader()->RemoveObserver(mStyleRuleMap);
+    mStyleRuleMap = nullptr;
+  }
+}
+
+void
 ServoStyleSet::Shutdown()
 {
   // Make sure we drop our cached style contexts before the presshell arena
   // starts going away.
   ClearNonInheritingStyleContexts();
   mRawSet = nullptr;
-  mStyleRuleMap = nullptr;
 }
 
 void
 ServoStyleSet::InvalidateStyleForCSSRuleChanges()
 {
   MOZ_ASSERT(StylistNeedsUpdate());
   mPresContext->RestyleManager()->AsServo()->PostRestyleEventForCSSRuleChanges();
 }
@@ -668,20 +680,16 @@ ServoStyleSet::AppendStyleSheet(SheetTyp
   if (mRawSet) {
     // Maintain a mirrored list of sheets on the servo side.
     // Servo will remove aSheet from its original position as part of the call
     // to Servo_StyleSet_AppendStyleSheet.
     Servo_StyleSet_AppendStyleSheet(mRawSet.get(), aSheet);
     SetStylistStyleSheetsDirty();
   }
 
-  if (mStyleRuleMap) {
-    mStyleRuleMap->SheetAdded(*aSheet);
-  }
-
   return NS_OK;
 }
 
 nsresult
 ServoStyleSet::PrependStyleSheet(SheetType aType,
                                  ServoStyleSheet* aSheet)
 {
   MOZ_ASSERT(aSheet);
@@ -696,20 +704,16 @@ ServoStyleSet::PrependStyleSheet(SheetTy
   if (mRawSet) {
     // Maintain a mirrored list of sheets on the servo side.
     // Servo will remove aSheet from its original position as part of the call
     // to Servo_StyleSet_PrependStyleSheet.
     Servo_StyleSet_PrependStyleSheet(mRawSet.get(), aSheet);
     SetStylistStyleSheetsDirty();
   }
 
-  if (mStyleRuleMap) {
-    mStyleRuleMap->SheetAdded(*aSheet);
-  }
-
   return NS_OK;
 }
 
 nsresult
 ServoStyleSet::RemoveStyleSheet(SheetType aType,
                                 ServoStyleSheet* aSheet)
 {
   MOZ_ASSERT(aSheet);
@@ -717,20 +721,16 @@ ServoStyleSet::RemoveStyleSheet(SheetTyp
 
   RemoveSheetOfType(aType, aSheet);
   if (mRawSet) {
     // Maintain a mirrored list of sheets on the servo side.
     Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), aSheet);
     SetStylistStyleSheetsDirty();
   }
 
-  if (mStyleRuleMap) {
-    mStyleRuleMap->SheetRemoved(*aSheet);
-  }
-
   return NS_OK;
 }
 
 nsresult
 ServoStyleSet::ReplaceSheets(SheetType aType,
                              const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets)
 {
   // Gecko uses a two-dimensional array keyed by sheet type, whereas Servo
@@ -753,19 +753,16 @@ ServoStyleSet::ReplaceSheets(SheetType a
   for (auto& sheet : aNewSheets) {
     AppendSheetOfType(aType, sheet);
     if (mRawSet) {
       MOZ_ASSERT(sheet->RawContents(), "Raw sheet should be in place before replacement.");
       Servo_StyleSet_AppendStyleSheet(mRawSet.get(), sheet);
     }
   }
 
-  // Just don't bother calling SheetRemoved / SheetAdded, and recreate the rule
-  // map when needed.
-  mStyleRuleMap = nullptr;
   return NS_OK;
 }
 
 nsresult
 ServoStyleSet::InsertStyleSheetBefore(SheetType aType,
                                       ServoStyleSheet* aNewSheet,
                                       ServoStyleSheet* aReferenceSheet)
 {
@@ -783,20 +780,16 @@ ServoStyleSet::InsertStyleSheetBefore(Sh
 
   if (mRawSet) {
     // Maintain a mirrored list of sheets on the servo side.
     Servo_StyleSet_InsertStyleSheetBefore(
         mRawSet.get(), aNewSheet, aReferenceSheet);
     SetStylistStyleSheetsDirty();
   }
 
-  if (mStyleRuleMap) {
-    mStyleRuleMap->SheetAdded(*aNewSheet);
-  }
-
   return NS_OK;
 }
 
 int32_t
 ServoStyleSet::SheetCount(SheetType aType) const
 {
   MOZ_ASSERT(IsCSSSheetType(aType));
   return mSheets[aType].Length();
@@ -853,20 +846,16 @@ ServoStyleSet::AddDocStyleSheet(ServoSty
 
     if (mRawSet) {
       // Maintain a mirrored list of sheets on the servo side.
       Servo_StyleSet_AppendStyleSheet(mRawSet.get(), aSheet);
       SetStylistStyleSheetsDirty();
     }
   }
 
-  if (mStyleRuleMap) {
-    mStyleRuleMap->SheetAdded(*aSheet);
-  }
-
   return NS_OK;
 }
 
 already_AddRefed<ServoStyleContext>
 ServoStyleSet::ProbePseudoElementStyle(Element* aOriginatingElement,
                                        CSSPseudoElementType aType,
                                        ServoStyleContext* aParentContext)
 {
@@ -1032,42 +1021,34 @@ ServoStyleSet::MarkOriginsDirty(OriginFl
 {
   SetStylistStyleSheetsDirty();
   Servo_StyleSet_NoteStyleSheetsChanged(mRawSet.get(),
                                         mAuthorStyleDisabled,
                                         aChangedOrigins);
 }
 
 void
-ServoStyleSet::RuleAdded(ServoStyleSheet& aSheet, css::Rule& aRule)
-{
-  if (mStyleRuleMap) {
-    mStyleRuleMap->RuleAdded(aSheet, aRule);
-  }
-
-  // FIXME(emilio): Could be more granular based on aRule.
-  MarkOriginsDirty(aSheet.GetOrigin());
-}
-
-void
-ServoStyleSet::RuleRemoved(ServoStyleSheet& aSheet, css::Rule& aRule)
+ServoStyleSet::RecordStyleSheetChange(
+    ServoStyleSheet* aSheet,
+    StyleSheet::ChangeType aChangeType)
 {
-  if (mStyleRuleMap) {
-    mStyleRuleMap->RuleRemoved(aSheet, aRule);
+  switch (aChangeType) {
+    case StyleSheet::ChangeType::RuleAdded:
+    case StyleSheet::ChangeType::RuleRemoved:
+    case StyleSheet::ChangeType::RuleChanged:
+    case StyleSheet::ChangeType::ReparsedFromInspector:
+      // FIXME(emilio): We can presumably do better in a bunch of these.
+      return MarkOriginsDirty(aSheet->GetOrigin());
+    case StyleSheet::ChangeType::ApplicableStateChanged:
+    case StyleSheet::ChangeType::Added:
+    case StyleSheet::ChangeType::Removed:
+      // Do nothing, we've already recorded the change in the
+      // Append/Remove/Replace methods, etc, and will act consequently.
+      return;
   }
-
-  // FIXME(emilio): Could be more granular based on aRule.
-  MarkOriginsDirty(aSheet.GetOrigin());
-}
-
-void
-ServoStyleSet::RuleChanged(ServoStyleSheet& aSheet, css::Rule* aRule)
-{
-  // FIXME(emilio): Could be more granular based on aRule.
-  MarkOriginsDirty(aSheet.GetOrigin());
 }
 
 #ifdef DEBUG
 void
 ServoStyleSet::AssertTreeIsClean()
 {
   DocumentStyleRootIterator iter(mPresContext->Document());
   while (Element* root = iter.GetNextStyleRoot()) {
@@ -1445,19 +1426,24 @@ ServoStyleSet::RunPostTraversalTasks()
     task.Run();
   }
 }
 
 ServoStyleRuleMap*
 ServoStyleSet::StyleRuleMap()
 {
   if (!mStyleRuleMap) {
-    mStyleRuleMap = MakeUnique<ServoStyleRuleMap>(this);
+    mStyleRuleMap = new ServoStyleRuleMap(this);
+    if (mPresContext) {
+      nsIDocument* doc = mPresContext->Document();
+      doc->AddObserver(mStyleRuleMap);
+      doc->CSSLoader()->AddObserver(mStyleRuleMap);
+    }
   }
-  return mStyleRuleMap.get();
+  return mStyleRuleMap;
 }
 
 bool
 ServoStyleSet::MightHaveAttributeDependency(const Element& aElement,
                                             nsAtom* aAttribute) const
 {
   return Servo_StyleSet_MightHaveAttributeDependency(
       mRawSet.get(), &aElement, aAttribute);
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -128,30 +128,22 @@ public:
   explicit ServoStyleSet(Kind aKind);
   ~ServoStyleSet();
 
   static UniquePtr<ServoStyleSet>
   CreateXBLServoStyleSet(nsPresContext* aPresContext,
                          const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets);
 
   void Init(nsPresContext* aPresContext, nsBindingManager* aBindingManager);
-  void BeginShutdown() {}
+  void BeginShutdown();
   void Shutdown();
 
-  // Called when a rules in a stylesheet in this set, or a child sheet of that,
-  // are mutated from CSSOM.
-  void RuleAdded(ServoStyleSheet&, css::Rule&);
-  void RuleRemoved(ServoStyleSheet&, css::Rule&);
-  void RuleChanged(ServoStyleSheet& aSheet, css::Rule* aRule);
+  void RecordStyleSheetChange(mozilla::ServoStyleSheet*, StyleSheet::ChangeType);
 
-  // All the relevant changes are handled in RuleAdded / RuleRemoved / etc, and
-  // the relevant AppendSheet / RemoveSheet...
-  void RecordStyleSheetChange(ServoStyleSheet*, StyleSheet::ChangeType) {}
-
-  void RecordShadowStyleChange(dom::ShadowRoot* aShadowRoot) {
+  void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) {
     // FIXME(emilio): When we properly support shadow dom we'll need to do
     // better.
     MarkOriginsDirty(OriginFlags::All);
   }
 
   bool StyleSheetsHaveChanged() const
   {
     return StylistNeedsUpdate();
@@ -289,17 +281,17 @@ public:
   }
 
   nsresult RemoveDocStyleSheet(ServoStyleSheet* aSheet);
   nsresult AddDocStyleSheet(ServoStyleSheet* aSheet, nsIDocument* aDocument);
 
   // check whether there is ::before/::after style for an element
   already_AddRefed<ServoStyleContext>
   ProbePseudoElementStyle(dom::Element* aOriginatingElement,
-                          CSSPseudoElementType aType,
+                          mozilla::CSSPseudoElementType aType,
                           ServoStyleContext* aParentContext);
 
   /**
    * Performs a Servo traversal to compute style for all dirty nodes in the
    * document.
    *
    * This will traverse all of the document's style roots (that is, its document
    * element, and the roots of the document-level native anonymous content).
@@ -445,16 +437,17 @@ public:
 
   // Returns true if a restyle of the document is needed due to cloning
   // sheet inners.
   bool EnsureUniqueInnerOnCSSSheets();
 
   // Called by StyleSheet::EnsureUniqueInner to let us know it cloned
   // its inner.
   void SetNeedsRestyleAfterEnsureUniqueInner() {
+    MOZ_ASSERT(!IsForXBL(), "Should not be cloning things for XBL stylesheet");
     mNeedsRestyleAfterEnsureUniqueInner = true;
   }
 
   // Returns the style rule map.
   ServoStyleRuleMap* StyleRuleMap();
 
   // Return whether this is the last PresContext which uses this XBL styleset.
   bool IsPresContextChanged(nsPresContext* aPresContext) const {
@@ -633,17 +626,17 @@ private:
   // Tasks to perform after a traversal, back on the main thread.
   //
   // These are similar to Servo's SequentialTasks, except that they are
   // posted by C++ code running on style worker threads.
   nsTArray<PostTraversalTask> mPostTraversalTasks;
 
   // Map from raw Servo style rule to Gecko's wrapper object.
   // Constructed lazily when requested by devtools.
-  UniquePtr<ServoStyleRuleMap> mStyleRuleMap;
+  RefPtr<ServoStyleRuleMap> mStyleRuleMap;
 
   // This can be null if we are used to hold XBL style sheets.
   RefPtr<nsBindingManager> mBindingManager;
 
   static ServoStyleSet* sInServoTraversal;
 };
 
 class UACacheReporter final : public nsIMemoryReporter
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -276,87 +276,113 @@ ServoStyleSheet::ReparseSheet(const nsAS
   uint32_t lineNumber = 1;
   if (mOwningNode) {
     nsCOMPtr<nsIStyleSheetLinkingElement> link = do_QueryInterface(mOwningNode);
     if (link) {
       lineNumber = link->GetLineNumber();
     }
   }
 
-  // Notify to the stylesets about the old rules going away.
-  {
+  // Notify mDocument that all our rules are removed.
+  if (mDocument) {
+    // Get the rule list.
     ServoCSSRuleList* ruleList = GetCssRulesInternal();
     MOZ_ASSERT(ruleList);
 
     uint32_t ruleCount = ruleList->Length();
     for (uint32_t i = 0; i < ruleCount; ++i) {
       css::Rule* rule = ruleList->GetRule(i);
       MOZ_ASSERT(rule);
       if (rule->GetType() == css::Rule::IMPORT_RULE &&
           RuleHasPendingChildSheet(rule)) {
         continue; // notify when loaded (see StyleSheetLoaded)
       }
-      RuleRemoved(*rule);
+      mDocument->StyleRuleRemoved(this, rule);
+
+      // Document observers could possibly detach document from this sheet.
+      if (!mDocument) {
+        // If detached, don't process any more rules.
+        break;
+      }
     }
   }
 
   DropRuleList();
 
   nsresult rv = ParseSheet(loader,
                            NS_ConvertUTF16toUTF8(aInput),
                            mInner->mSheetURI,
                            mInner->mBaseURI,
                            mInner->mPrincipal,
                            lineNumber,
                            eCompatibility_FullStandards,
                            &reusableSheets);
   DidDirty();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Notify the stylesets about the new rules.
-  {
+  // Notify mDocument that all our new rules are added.
+  if (mDocument) {
     // Get the rule list (which will need to be regenerated after ParseSheet).
     ServoCSSRuleList* ruleList = GetCssRulesInternal();
     MOZ_ASSERT(ruleList);
 
     uint32_t ruleCount = ruleList->Length();
     for (uint32_t i = 0; i < ruleCount; ++i) {
       css::Rule* rule = ruleList->GetRule(i);
       MOZ_ASSERT(rule);
       if (rule->GetType() == css::Rule::IMPORT_RULE &&
           RuleHasPendingChildSheet(rule)) {
         continue; // notify when loaded (see StyleSheetLoaded)
       }
 
-      RuleAdded(*rule);
+      mDocument->StyleRuleAdded(this, rule);
+
+      // Document observers could possibly detach document from this sheet.
+      if (!mDocument) {
+        // If detached, don't process any more rules.
+        break;
+      }
     }
   }
 
+  // FIXME(emilio): This is kind-of a hack for bug 1420713. As you may notice,
+  // there's nothing that triggers a style flush or anything similar (neither
+  // here or in the relevant Gecko path inside DidDirty).
+  //
+  // The tl;dr is: if we want to make sure scripted changes to sheets not
+  // associated with any document get properly reflected, we need to rejigger a
+  // fair amount of stuff. I'm probably doing that work as part of the shadow
+  // DOM stuff.
+  for (StyleSetHandle handle : mStyleSets) {
+    handle->AsServo()->RecordStyleSheetChange(
+      this, StyleSheet::ChangeType::ReparsedFromInspector);
+  }
+
   return NS_OK;
 }
 
 // nsICSSLoaderObserver implementation
 NS_IMETHODIMP
 ServoStyleSheet::StyleSheetLoaded(StyleSheet* aSheet,
                                   bool aWasAlternate,
                                   nsresult aStatus)
 {
   MOZ_ASSERT(aSheet->IsServo(),
              "why we were called back with a CSSStyleSheet?");
 
   ServoStyleSheet* sheet = aSheet->AsServo();
-  if (!sheet->GetParentSheet()) {
+  if (sheet->GetParentSheet() == nullptr) {
     return NS_OK; // ignore if sheet has been detached already
   }
   NS_ASSERTION(this == sheet->GetParentSheet(),
                "We are being notified of a sheet load for a sheet that is not our child!");
 
-  if (NS_SUCCEEDED(aStatus)) {
+  if (mDocument && NS_SUCCEEDED(aStatus)) {
     mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
-    RuleAdded(*sheet->GetOwnerRule());
+    mDocument->StyleRuleAdded(this, sheet->GetOwnerRule());
   }
 
   return NS_OK;
 }
 
 void
 ServoStyleSheet::DropRuleList()
 {
@@ -401,25 +427,24 @@ ServoStyleSheet::InsertRuleInternal(cons
   // Ensure mRuleList is constructed.
   GetCssRulesInternal();
 
   mozAutoDocUpdate updateBatch(mDocument, 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);
-  if (rule->GetType() != css::Rule::IMPORT_RULE ||
-      !RuleHasPendingChildSheet(rule)) {
-    RuleAdded(*rule);
+  if (mDocument) {
+    if (mRuleList->GetDOMCSSRuleType(aIndex) != nsIDOMCSSRule::IMPORT_RULE ||
+        !RuleHasPendingChildSheet(mRuleList->GetRule(aIndex))) {
+      // XXX We may not want to get the rule when stylesheet change event
+      // is not enabled.
+      mDocument->StyleRuleAdded(this, mRuleList->GetRule(aIndex));
+    }
   }
-
   return aIndex;
 }
 
 void
 ServoStyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv)
 {
   // Ensure mRuleList is constructed.
   GetCssRulesInternal();
@@ -431,18 +456,18 @@ ServoStyleSheet::DeleteRuleInternal(uint
   mozAutoDocUpdate updateBatch(mDocument, 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()) {
-    RuleRemoved(*rule);
+  if (!aRv.Failed() && mDocument) {
+    mDocument->StyleRuleRemoved(this, rule);
   }
 }
 
 nsresult
 ServoStyleSheet::InsertRuleIntoGroupInternal(const nsAString& aRule,
                                              css::GroupRule* aGroup,
                                              uint32_t aIndex)
 {
--- a/layout/style/StyleRule.cpp
+++ b/layout/style/StyleRule.cpp
@@ -1157,17 +1157,21 @@ DOMCSSDeclarationImpl::SetCSSDeclaration
     doc = sheet->GetAssociatedDocument();
   }
 
   mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true);
 
   mRule->SetDeclaration(aDecl->AsGecko());
 
   if (sheet) {
-    sheet->RuleChanged(mRule);
+    sheet->DidDirty();
+  }
+
+  if (doc) {
+    doc->StyleRuleChanged(sheet, mRule);
   }
   return NS_OK;
 }
 
 nsIDocument*
 DOMCSSDeclarationImpl::DocToUpdate()
 {
   return nullptr;
--- a/layout/style/StyleSetHandle.h
+++ b/layout/style/StyleSetHandle.h
@@ -13,41 +13,39 @@
 #include "mozilla/ServoTypes.h"
 #include "mozilla/SheetType.h"
 #include "mozilla/StyleBackendType.h"
 #include "mozilla/StyleSheet.h"
 #include "nsChangeHint.h"
 #include "nsCSSPseudoElements.h"
 #include "nsTArray.h"
 
+namespace mozilla {
+class CSSStyleSheet;
+class ServoStyleSet;
+namespace dom {
+class Element;
+class ShadowRoot;
+} // namespace dom
+} // namespace mozilla
 class nsBindingManager;
 class nsCSSCounterStyleRule;
 struct nsFontFaceRuleContainer;
 class nsAtom;
 class nsICSSAnonBoxPseudo;
 class nsIContent;
 class nsIDocument;
 class nsStyleContext;
 class nsStyleSet;
 class nsPresContext;
 class gfxFontFeatureValueSet;
 struct TreeMatchContext;
 
 namespace mozilla {
 
-class CSSStyleSheet;
-class ServoStyleSet;
-namespace dom {
-class Element;
-class ShadowRoot;
-} // namespace dom
-namespace css {
-class Rule;
-} // namespace css
-
 #define SERVO_BIT 0x1
 
 /**
  * Smart pointer class that can hold a pointer to either an nsStyleSet
  * or a ServoStyleSet.
  */
 class StyleSetHandle
 {
@@ -163,22 +161,16 @@ public:
     inline nsresult InsertStyleSheetBefore(SheetType aType,
                                     StyleSheet* aNewSheet,
                                     StyleSheet* aReferenceSheet);
     inline int32_t SheetCount(SheetType aType) const;
     inline StyleSheet* StyleSheetAt(SheetType aType, int32_t aIndex) const;
     inline void AppendAllXBLStyleSheets(nsTArray<StyleSheet*>& aArray) const;
     inline nsresult RemoveDocStyleSheet(StyleSheet* aSheet);
     inline nsresult AddDocStyleSheet(StyleSheet* aSheet, nsIDocument* aDocument);
-
-    inline void RuleRemoved(StyleSheet&, css::Rule&);
-    inline void RuleAdded(StyleSheet&, css::Rule&);
-    inline void RuleChanged(StyleSheet&, css::Rule*);
-
-    // TODO(emilio): Remove in favor of Rule* methods.
     inline void RecordStyleSheetChange(StyleSheet* aSheet, StyleSheet::ChangeType);
     inline void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
     inline bool StyleSheetsHaveChanged() const;
     inline void InvalidateStyleForCSSRuleChanges();
     inline nsRestyleHint MediumFeaturesChanged(bool aViewportChanged);
     inline already_AddRefed<nsStyleContext>
     ProbePseudoElementStyle(dom::Element* aParentElement,
                             mozilla::CSSPseudoElementType aType,
--- a/layout/style/StyleSetHandleInlines.h
+++ b/layout/style/StyleSetHandleInlines.h
@@ -245,37 +245,16 @@ nsresult
 StyleSetHandle::Ptr::AddDocStyleSheet(StyleSheet* aSheet,
                                       nsIDocument* aDocument)
 {
   FORWARD_CONCRETE(AddDocStyleSheet, (aSheet->AsGecko(), aDocument),
                                      (aSheet->AsServo(), aDocument));
 }
 
 void
-StyleSetHandle::Ptr::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule)
-{
-  FORWARD_CONCRETE(RuleRemoved, (*aSheet.AsGecko(), aRule),
-                                (*aSheet.AsServo(), aRule));
-}
-
-void
-StyleSetHandle::Ptr::RuleAdded(StyleSheet& aSheet, css::Rule& aRule)
-{
-  FORWARD_CONCRETE(RuleAdded, (*aSheet.AsGecko(), aRule),
-                              (*aSheet.AsServo(), aRule));
-}
-
-void
-StyleSetHandle::Ptr::RuleChanged(StyleSheet& aSheet, css::Rule* aRule)
-{
-  FORWARD_CONCRETE(RuleChanged, (*aSheet.AsGecko(), aRule),
-                                (*aSheet.AsServo(), aRule));
-}
-
-void
 StyleSetHandle::Ptr::RecordStyleSheetChange(StyleSheet* aSheet,
                                             StyleSheet::ChangeType aChangeType)
 {
   FORWARD_CONCRETE(RecordStyleSheetChange, (aSheet->AsGecko(), aChangeType),
                                            (aSheet->AsServo(), aChangeType));
 }
 
 void
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -449,28 +449,22 @@ StyleSheet::EnsureUniqueInner()
   MOZ_ASSERT(mInner->mSheets.Length() != 0,
              "unexpected number of outers");
   mDirty = true;
 
   if (HasUniqueInner()) {
     // already unique
     return;
   }
-
   // If this stylesheet is for XBL with Servo, don't bother cloning
   // it, as it may break ServoStyleRuleMap. XBL stylesheets are not
   // supposed to change anyway.
-  //
   // The mDocument check is used as a fast reject path because no
   // XBL stylesheets would have associated document, but in normal
   // cases, content stylesheets should usually have one.
-  //
-  // FIXME(emilio): Shadow DOM definitely modifies stylesheets! Right now all of
-  // them are unique anyway because we don't support <link>, but that needs to
-  // change.
   if (!mDocument && IsServo() &&
       mStyleSets.Length() == 1 &&
       mStyleSets[0]->AsServo()->IsForXBL()) {
     return;
   }
 
   StyleSheetInfo* clone = mInner->CloneFor(this);
   MOZ_ASSERT(clone);
@@ -604,61 +598,24 @@ StyleSheet::DeleteRuleFromGroup(css::Gro
   mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
 
   WillDirty();
 
   nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
   NS_ENSURE_SUCCESS(result, result);
 
   rule->SetStyleSheet(nullptr);
-  RuleRemoved(*rule);
-  return NS_OK;
-}
 
-#define NOTIFY_STYLE_SETS(function_, args_) do {          \
-  StyleSheet* current = this;                             \
-  do {                                                    \
-    for (StyleSetHandle handle : current->mStyleSets) {   \
-      handle->function_ args_;                            \
-    }                                                     \
-    current = current->mParent;                           \
-  } while (current);                                      \
-} while (0)
-
-void
-StyleSheet::RuleAdded(css::Rule& aRule)
-{
   DidDirty();
-  NOTIFY_STYLE_SETS(RuleAdded, (*this, aRule));
 
   if (mDocument) {
-    mDocument->StyleRuleAdded(this, &aRule);
+    mDocument->StyleRuleRemoved(this, rule);
   }
-}
-
-void
-StyleSheet::RuleRemoved(css::Rule& aRule)
-{
-  DidDirty();
-  NOTIFY_STYLE_SETS(RuleRemoved, (*this, aRule));
 
-  if (mDocument) {
-    mDocument->StyleRuleRemoved(this, &aRule);
-  }
-}
-
-void
-StyleSheet::RuleChanged(css::Rule* aRule)
-{
-  DidDirty();
-  NOTIFY_STYLE_SETS(RuleChanged, (*this, aRule));
-
-  if (mDocument) {
-    mDocument->StyleRuleChanged(this, aRule);
-  }
+  return NS_OK;
 }
 
 nsresult
 StyleSheet::InsertRuleIntoGroup(const nsAString& aRule,
                                 css::GroupRule* aGroup,
                                 uint32_t aIndex)
 {
   NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!");
@@ -674,17 +631,22 @@ StyleSheet::InsertRuleIntoGroup(const ns
 
   nsresult result;
   if (IsGecko()) {
     result = AsGecko()->InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
   } else {
     result = AsServo()->InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
   }
   NS_ENSURE_SUCCESS(result, result);
-  RuleAdded(*aGroup->GetStyleRuleAt(aIndex));
+
+  DidDirty();
+
+  if (mDocument) {
+    mDocument->StyleRuleAdded(this, aGroup->GetStyleRuleAt(aIndex));
+  }
 
   return NS_OK;
 }
 
 uint64_t
 StyleSheet::FindOwningWindowInnerID() const
 {
   uint64_t windowID = 0;
--- a/layout/style/StyleSheet.h
+++ b/layout/style/StyleSheet.h
@@ -72,16 +72,17 @@ public:
    */
   enum class ChangeType {
     Added,
     Removed,
     ApplicableStateChanged,
     RuleAdded,
     RuleRemoved,
     RuleChanged,
+    ReparsedFromInspector,
   };
 
   void SetOwningNode(nsINode* aOwningNode)
   {
     mOwningNode = aOwningNode;
   }
 
   css::SheetParsingMode ParsingMode() const { return mParsingMode; }
@@ -256,22 +257,16 @@ public:
   NS_IMETHOD DeleteRule(uint32_t aIndex) final;
 
   // Changes to sheets should be inside of a WillDirty-DidDirty pair.
   // However, the calls do not need to be matched; it's ok to call
   // WillDirty and then make no change and skip the DidDirty call.
   void WillDirty();
   virtual void DidDirty() {}
 
-  // Called when a rule changes from CSSOM.
-  //
-  // FIXME(emilio): This shouldn't allow null, but MediaList doesn't know about
-  // it's owning media rule, plus it's used for the stylesheet media itself.
-  void RuleChanged(css::Rule*);
-
   void AddStyleSet(const StyleSetHandle& aStyleSet);
   void DropStyleSet(const StyleSetHandle& aStyleSet);
 
   nsresult DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex);
   nsresult InsertRuleIntoGroup(const nsAString& aRule,
                                css::GroupRule* aGroup, uint32_t aIndex);
 
   // Find the ID of the owner inner window.
@@ -293,25 +288,16 @@ private:
   // Check if the rules are available for read and write.
   // It does the security check as well as whether the rules have been
   // completely loaded. aRv will have an exception set if this function
   // returns false.
   bool AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
                          ErrorResult& aRv);
 
 protected:
-  // Called when a rule is removed from the sheet from CSSOM.
-  void RuleAdded(css::Rule&);
-
-  // Called when a rule is added to the sheet from CSSOM.
-  void RuleRemoved(css::Rule&);
-
-  // Called from SetEnabled when the enabled state changed.
-  void EnabledStateChanged();
-
   struct ChildSheetListBuilder {
     RefPtr<StyleSheet>* sheetSlot;
     StyleSheet* parent;
 
     void SetParentLinks(StyleSheet* aSheet);
 
     static void ReparentChildList(StyleSheet* aPrimarySheet,
                                   StyleSheet* aFirstChild);
@@ -326,16 +312,19 @@ protected:
   // UniversalXPConnect or if access is allowed by CORS.  In the latter case,
   // it will set the principal of the inner to the subject principal.
   void SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aRv);
 
   // Drop our reference to mMedia
   void DropMedia();
 
+  // Called from SetEnabled when the enabled state changed.
+  void EnabledStateChanged();
+
   // Unlink our inner, if needed, for cycle collection
   virtual void UnlinkInner();
   // Traverse our inner, if needed, for cycle collection
   virtual void TraverseInner(nsCycleCollectionTraversalCallback &);
 
   // Return whether the given @import rule has pending child sheet.
   static bool RuleHasPendingChildSheet(css::Rule* aRule);
 
--- a/layout/style/nsCSSCounterStyleRule.cpp
+++ b/layout/style/nsCSSCounterStyleRule.cpp
@@ -135,17 +135,22 @@ nsCSSCounterStyleRule::SetName(const nsA
   nsCSSParser parser;
   if (RefPtr<nsAtom> name = parser.ParseCounterStyleName(aName, nullptr)) {
     nsIDocument* doc = GetDocument();
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
     mName = name;
 
     if (StyleSheet* sheet = GetStyleSheet()) {
-      sheet->RuleChanged(this);
+      if (sheet->IsGecko()) {
+        sheet->AsGecko()->SetModifiedByChildRule();
+      }
+      if (doc) {
+        doc->StyleRuleChanged(sheet, this);
+      }
     }
   }
   return NS_OK;
 }
 
 int32_t
 nsCSSCounterStyleRule::GetSystem() const
 {
@@ -177,17 +182,22 @@ nsCSSCounterStyleRule::SetDesc(nsCSSCoun
 
   nsIDocument* doc = GetDocument();
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
   mValues[aDescID] = aValue;
   mGeneration++;
 
   if (StyleSheet* sheet = GetStyleSheet()) {
-    sheet->RuleChanged(this);
+    if (sheet->IsGecko()) {
+      sheet->AsGecko()->SetModifiedByChildRule();
+    }
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this);
+    }
   }
 }
 
 NS_IMETHODIMP
 nsCSSCounterStyleRule::GetSystem(nsAString& aSystem)
 {
   const nsCSSValue& value = GetDesc(eCSSCounterDesc_System);
   if (value.GetUnit() == eCSSUnit_Null) {
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -1146,17 +1146,20 @@ nsCSSKeyframeRule::SetKeyText(const nsAS
   }
 
   nsIDocument* doc = GetDocument();
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
   newSelectors.SwapElements(mKeys);
 
   if (StyleSheet* sheet = GetStyleSheet()) {
-    sheet->RuleChanged(this);
+    sheet->AsGecko()->SetModifiedByChildRule();
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this);
+    }
   }
 
   return NS_OK;
 }
 
 nsICSSDeclaration*
 nsCSSKeyframeRule::Style()
 {
@@ -1177,17 +1180,20 @@ nsCSSKeyframeRule::ChangeDeclaration(css
 
   if (aDeclaration != mDeclaration) {
     mDeclaration->SetOwningRule(nullptr);
     mDeclaration = aDeclaration;
     mDeclaration->SetOwningRule(this);
   }
 
   if (StyleSheet* sheet = GetStyleSheet()) {
-    sheet->RuleChanged(this);
+    sheet->AsGecko()->SetModifiedByChildRule();
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this);
+    }
   }
 }
 
 /* virtual */ size_t
 nsCSSKeyframeRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this);
 
@@ -1277,17 +1283,20 @@ nsCSSKeyframesRule::SetName(const nsAStr
   }
 
   nsIDocument* doc = GetDocument();
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
   mName = NS_Atomize(aName);
 
   if (StyleSheet* sheet = GetStyleSheet()) {
-    sheet->RuleChanged(this);
+    sheet->AsGecko()->SetModifiedByChildRule();
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this);
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSSKeyframesRule::AppendRule(const nsAString& aRule)
 {
@@ -1301,17 +1310,20 @@ nsCSSKeyframesRule::AppendRule(const nsA
     parser.ParseKeyframeRule(aRule, nullptr, 0);
   if (rule) {
     nsIDocument* doc = GetDocument();
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
     AppendStyleRule(rule);
 
     if (StyleSheet* sheet = GetStyleSheet()) {
-      sheet->RuleChanged(this);
+      sheet->AsGecko()->SetModifiedByChildRule();
+      if (doc) {
+        doc->StyleRuleChanged(sheet, this);
+      }
     }
   }
 
   return NS_OK;
 }
 
 static const uint32_t RULE_NOT_FOUND = uint32_t(-1);
 
@@ -1345,17 +1357,21 @@ nsCSSKeyframesRule::DeleteRule(const nsA
   uint32_t index = FindRuleIndexForKey(aKey);
   if (index != RULE_NOT_FOUND) {
     nsIDocument* doc = GetDocument();
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
     DeleteStyleRuleAt(index);
 
     if (StyleSheet* sheet = GetStyleSheet()) {
-      sheet->RuleChanged(this);
+      sheet->AsGecko()->SetModifiedByChildRule();
+
+      if (doc) {
+        doc->StyleRuleChanged(sheet, this);
+      }
     }
   }
   return NS_OK;
 }
 
 nsCSSKeyframeRule*
 nsCSSKeyframesRule::FindRule(const nsAString& aKey)
 {
@@ -1551,20 +1567,18 @@ void
 nsCSSPageRule::ChangeDeclaration(css::Declaration* aDeclaration)
 {
   if (aDeclaration != mDeclaration) {
     mDeclaration->SetOwningRule(nullptr);
     mDeclaration = aDeclaration;
     mDeclaration->SetOwningRule(this);
   }
 
-  nsIDocument* doc = GetDocument();
-  MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
   if (StyleSheet* sheet = GetStyleSheet()) {
-    sheet->RuleChanged(this);
+    sheet->AsGecko()->SetModifiedByChildRule();
   }
 }
 
 /* virtual */ size_t
 nsCSSPageRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this);
 }
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -2349,25 +2349,26 @@ nsStyleSet::Shutdown()
   ClearNonInheritingStyleContexts();
   mRuleTree = nullptr;
   GCRuleTrees();
   MOZ_ASSERT(mUnusedRuleNodeList.isEmpty());
   MOZ_ASSERT(mUnusedRuleNodeCount == 0);
 }
 
 void
-nsStyleSet::SheetChanged(CSSStyleSheet& aStyleSheet)
+nsStyleSet::RecordStyleSheetChange(CSSStyleSheet* aStyleSheet,
+                                   StyleSheet::ChangeType)
 {
   MOZ_ASSERT(mBatching != 0, "Should be in an update");
 
   if (mStylesHaveChanged) {
     return;
   }
 
-  if (Element* scopeElement = aStyleSheet.GetScopeElement()) {
+  if (Element* scopeElement = aStyleSheet->GetScopeElement()) {
     mChangedScopeStyleRoots.AppendElement(scopeElement);
     return;
   }
 
   mStylesHaveChanged = true;
   // If we need to restyle everything, no need to restyle individual
   // scoped style roots.
   mChangedScopeStyleRoots.Clear();
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -336,39 +336,19 @@ class nsStyleSet final
 
   // Begin ignoring style context destruction, to avoid lots of unnecessary
   // work on document teardown.
   void BeginShutdown();
 
   // Free all of the data associated with this style set.
   void Shutdown();
 
-  void RuleAdded(mozilla::CSSStyleSheet& aSheet, mozilla::css::Rule&)
-  {
-    SheetChanged(aSheet);
-  }
-
-  void RuleRemoved(mozilla::CSSStyleSheet& aSheet, mozilla::css::Rule&)
-  {
-    SheetChanged(aSheet);
-  }
-
-  void RuleChanged(mozilla::CSSStyleSheet& aSheet, mozilla::css::Rule*)
-  {
-    SheetChanged(aSheet);
-  }
-
   // Notes that a style sheet has changed.
-  void RecordStyleSheetChange(mozilla::CSSStyleSheet* aSheet,
-                              mozilla::StyleSheet::ChangeType)
-  {
-    SheetChanged(*aSheet);
-  }
-
-  void SheetChanged(mozilla::CSSStyleSheet&);
+  void RecordStyleSheetChange(mozilla::CSSStyleSheet* aStyleSheet,
+                              mozilla::StyleSheet::ChangeType);
 
   // Notes that style sheets have changed in a shadow root.
   void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
 
   bool StyleSheetsHaveChanged() const
   {
     return mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty();
   }