Bug 1422197 - Add fast path to get DocGroup in binding code for [CEReactions]; r=smaug
authorEdgar Chen <echen@mozilla.com>
Mon, 27 Nov 2017 16:10:27 +0800
changeset 450522 57a108d1c90a9979e2b79dbf138e055ee07ed97b
parent 450521 60c1a759af6958f35b0084adfe0c4a6ed8536f84
child 450635 8062887ff0d9382ea84177f2c21f62dc0e613d9e
child 450694 3e18c58d0320ef834bcf6b9f76dad2f8f1397315
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1422197
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1422197 - Add fast path to get DocGroup in binding code for [CEReactions]; r=smaug MozReview-Commit-ID: HgbFo9ddr0o
dom/base/CustomElementRegistry.cpp
dom/base/CustomElementRegistry.h
dom/base/Selection.cpp
dom/base/Selection.h
dom/base/nsDOMAttributeMap.cpp
dom/base/nsDOMAttributeMap.h
dom/base/nsDOMTokenList.cpp
dom/base/nsDOMTokenList.h
dom/base/nsINode.cpp
dom/base/nsINode.h
dom/base/nsRange.cpp
dom/base/nsRange.h
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/Configuration.py
dom/bindings/test/TestBindingHeader.h
dom/html/HTMLOptionsCollection.cpp
dom/html/HTMLOptionsCollection.h
dom/html/nsDOMStringMap.cpp
dom/html/nsDOMStringMap.h
dom/xslt/xslt/txMozillaXSLTProcessor.cpp
dom/xslt/xslt/txMozillaXSLTProcessor.h
layout/style/ServoKeyframeRule.cpp
layout/style/ServoPageRule.cpp
layout/style/ServoPageRule.h
layout/style/ServoStyleRule.cpp
layout/style/ServoStyleRule.h
layout/style/StyleRule.cpp
layout/style/nsCSSFontFaceRule.cpp
layout/style/nsCSSFontFaceRule.h
layout/style/nsCSSRules.cpp
layout/style/nsCSSRules.h
layout/style/nsComputedDOMStyle.h
layout/style/nsDOMCSSAttrDeclaration.cpp
layout/style/nsDOMCSSAttrDeclaration.h
layout/style/nsICSSDeclaration.h
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -487,16 +487,22 @@ CustomElementRegistry::WrapObject(JSCont
   return CustomElementRegistryBinding::Wrap(aCx, this, aGivenProto);
 }
 
 nsISupports* CustomElementRegistry::GetParentObject() const
 {
   return mWindow;
 }
 
+DocGroup*
+CustomElementRegistry::GetDocGroup() const
+{
+  return mWindow ? mWindow->GetDocGroup() : nullptr;
+}
+
 static const char* kLifeCycleCallbackNames[] = {
   "connectedCallback",
   "disconnectedCallback",
   "adoptedCallback",
   "attributeChangedCallback"
 };
 
 static void
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -24,16 +24,17 @@ class nsDocument;
 
 namespace mozilla {
 namespace dom {
 
 struct CustomElementData;
 struct ElementDefinitionOptions;
 class CallbackFunction;
 class CustomElementReaction;
+class DocGroup;
 class Function;
 class Promise;
 
 struct LifecycleCallbackArgs
 {
   nsString name;
   nsString oldValue;
   nsString newValue;
@@ -471,16 +472,18 @@ private:
 
     private:
       CustomElementRegistry* mRegistry;
   };
 
 public:
   nsISupports* GetParentObject() const;
 
+  DocGroup* GetDocGroup() const;
+
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void Define(const nsAString& aName, Function& aFunctionConstructor,
               const ElementDefinitionOptions& aOptions, ErrorResult& aRv);
 
   void Get(JSContext* cx, const nsAString& name,
            JS::MutableHandle<JS::Value> aRetVal);
 
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -773,16 +773,28 @@ Selection::GetParentObject() const
 {
   nsIPresShell* shell = GetPresShell();
   if (shell) {
     return shell->GetDocument();
   }
   return nullptr;
 }
 
+DocGroup*
+Selection::GetDocGroup() const
+{
+  nsIPresShell* shell = GetPresShell();
+  if (!shell) {
+    return nullptr;
+  }
+
+  nsIDocument* doc = shell->GetDocument();
+  return doc ? doc->GetDocGroup() : nullptr;
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(Selection)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection)
   // Unlink the selection listeners *before* we do RemoveAllRanges since
   // we don't want to notify the listeners during JS GC (they could be
   // in JS!).
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedRange)
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -32,16 +32,19 @@ struct SelectionDetails;
 struct SelectionCustomColors;
 class nsCopySupport;
 class nsHTMLCopyEncoder;
 
 namespace mozilla {
 class ErrorResult;
 class HTMLEditor;
 struct AutoPrepareFocusRange;
+namespace dom {
+class DocGroup;
+} // namespace dom
 } // namespace mozilla
 
 struct RangeData
 {
   explicit RangeData(nsRange* aRange)
     : mRange(aRange)
   {}
 
@@ -77,16 +80,17 @@ public:
   // match this up with EndbatchChanges. will stop ui updates while multiple
   // selection methods are called
   void StartBatchChanges();
 
   // match this up with StartBatchChanges
   void EndBatchChanges(int16_t aReason = nsISelectionListener::NO_REASON);
 
   nsIDocument* GetParentObject() const;
+  DocGroup* GetDocGroup() const;
 
   // utility methods for scrolling the selection into view
   nsPresContext* GetPresContext() const;
   nsIPresShell* GetPresShell() const;
   nsFrameSelection* GetFrameSelection() const { return mFrameSelection; }
   // Returns a rect containing the selection region, and frame that that
   // position is relative to. For SELECTION_ANCHOR_REGION or
   // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -516,8 +516,14 @@ nsDOMAttributeMap::SizeOfIncludingThis(M
   return n;
 }
 
 /* virtual */ JSObject*
 nsDOMAttributeMap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return NamedNodeMapBinding::Wrap(aCx, this, aGivenProto);
 }
+
+DocGroup*
+nsDOMAttributeMap::GetDocGroup() const
+{
+  return mContent ? mContent->OwnerDoc()->GetDocGroup() : nullptr;
+}
--- a/dom/base/nsDOMAttributeMap.h
+++ b/dom/base/nsDOMAttributeMap.h
@@ -17,16 +17,21 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMMozNamedAttrMap.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 class nsAtom;
 class nsIDocument;
+namespace mozilla {
+namespace dom {
+class DocGroup;
+} // namespace dom
+} // namespace mozilla
 
 /**
  * Structure used as a key for caching Attrs in nsDOMAttributeMap's mAttributeCache.
  */
 class nsAttrKey
 {
 public:
   /**
@@ -82,16 +87,17 @@ private:
 };
 
 // Helper class that implements the nsIDOMMozNamedAttrMap interface.
 class nsDOMAttributeMap final : public nsIDOMMozNamedAttrMap
                               , public nsWrapperCache
 {
 public:
   typedef mozilla::dom::Attr Attr;
+  typedef mozilla::dom::DocGroup DocGroup;
   typedef mozilla::dom::Element Element;
   typedef mozilla::ErrorResult ErrorResult;
 
   explicit nsDOMAttributeMap(Element *aContent);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsDOMAttributeMap)
 
@@ -130,16 +136,17 @@ public:
 
   static void BlastSubtreeToPieces(nsINode *aNode);
 
   Element* GetParentObject() const
   {
     return mContent;
   }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  DocGroup* GetDocGroup() const;
 
   // WebIDL
   Attr* GetNamedItem(const nsAString& aAttrName);
   Attr* NamedGetter(const nsAString& aAttrName, bool& aFound);
   already_AddRefed<Attr>
   RemoveNamedItem(mozilla::dom::NodeInfo* aNodeInfo, ErrorResult& aError);
   already_AddRefed<Attr>
   RemoveNamedItem(const nsAString& aName, ErrorResult& aError);
--- a/dom/base/nsDOMTokenList.cpp
+++ b/dom/base/nsDOMTokenList.cpp
@@ -407,14 +407,20 @@ nsDOMTokenList::Stringify(nsAString& aRe
   if (!mElement) {
     aResult.Truncate();
     return;
   }
 
   mElement->GetAttr(kNameSpaceID_None, mAttrAtom, aResult);
 }
 
+DocGroup*
+nsDOMTokenList::GetDocGroup() const
+{
+  return mElement ? mElement->OwnerDoc()->GetDocGroup() : nullptr;
+}
+
 JSObject*
 nsDOMTokenList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return DOMTokenListBinding::Wrap(cx, this, aGivenProto);
 }
 
--- a/dom/base/nsDOMTokenList.h
+++ b/dom/base/nsDOMTokenList.h
@@ -17,29 +17,32 @@
 #include "nsWhitespaceTokenizer.h"
 #include "nsWrapperCache.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/DOMTokenListSupportedTokens.h"
 
 namespace mozilla {
 class ErrorResult;
-
+namespace dom {
+class DocGroup;
+} // namespace dom
 } // namespace mozilla
 
 class nsAttrValue;
 class nsAtom;
 
 // nsISupports must be on the primary inheritance chain
 
 class nsDOMTokenList : public nsISupports,
                        public nsWrapperCache
 {
 protected:
   typedef mozilla::dom::Element Element;
+  typedef mozilla::dom::DocGroup DocGroup;
   typedef nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace>
     WhitespaceTokenizer;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMTokenList)
 
   nsDOMTokenList(Element* aElement, nsAtom* aAttrAtom,
@@ -47,16 +50,18 @@ public:
 
   virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
   Element* GetParentObject()
   {
     return mElement;
   }
 
+  DocGroup* GetDocGroup() const;
+
   void RemoveDuplicates(const nsAttrValue* aAttr);
   uint32_t Length();
   void Item(uint32_t aIndex, nsAString& aResult)
   {
     bool found;
     IndexedGetter(aIndex, found, aResult);
     if (!found) {
       SetDOMStringToNull(aResult);
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -3121,8 +3121,14 @@ nsINode::IsNodeApzAwareInternal() const
 
 #ifdef MOZ_STYLO
 bool
 nsINode::IsStyledByServo() const
 {
   return OwnerDoc()->IsStyledByServo();
 }
 #endif
+
+DocGroup*
+nsINode::GetDocGroup() const
+{
+  return OwnerDoc()->GetDocGroup();
+}
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -71,16 +71,17 @@ inline bool IsSpaceCharacter(char16_t aC
 }
 inline bool IsSpaceCharacter(char aChar) {
   return aChar == ' ' || aChar == '\t' || aChar == '\n' || aChar == '\r' ||
          aChar == '\f';
 }
 class AccessibleNode;
 struct BoxQuadOptions;
 struct ConvertCoordinateOptions;
+class DocGroup;
 class DOMPoint;
 class DOMQuad;
 class DOMRectReadOnly;
 class Element;
 class EventHandlerNonNull;
 template<typename T> class Optional;
 class OwningNodeOrString;
 template<typename> class Sequence;
@@ -287,16 +288,17 @@ private:
  * nsIContent and nsIDocument share.  An instance of this interface has a list
  * of nsIContent children and provides access to them.
  */
 class nsINode : public mozilla::dom::EventTarget
 {
 public:
   typedef mozilla::dom::BoxQuadOptions BoxQuadOptions;
   typedef mozilla::dom::ConvertCoordinateOptions ConvertCoordinateOptions;
+  typedef mozilla::dom::DocGroup DocGroup;
   typedef mozilla::dom::DOMPoint DOMPoint;
   typedef mozilla::dom::DOMPointInit DOMPointInit;
   typedef mozilla::dom::DOMQuad DOMQuad;
   typedef mozilla::dom::DOMRectReadOnly DOMRectReadOnly;
   typedef mozilla::dom::OwningNodeOrString OwningNodeOrString;
   typedef mozilla::dom::TextOrElementOrDocument TextOrElementOrDocument;
   typedef mozilla::dom::CallerType CallerType;
   typedef mozilla::ErrorResult ErrorResult;
@@ -609,16 +611,21 @@ public:
   }
 
   inline bool IsInNamespace(int32_t aNamespace) const
   {
     return mNodeInfo->NamespaceID() == aNamespace;
   }
 
   /**
+   * Returns the DocGroup of the "node document" of this node.
+   */
+  DocGroup* GetDocGroup() const;
+
+  /**
    * Print a debugger friendly descriptor of this element. This will describe
    * the position of this element in the document.
    */
   friend std::ostream& operator<<(std::ostream& aStream, const nsINode& aNode);
 
 protected:
   // These 2 methods are useful for the recursive templates IsHTMLElement,
   // IsSVGElement, etc.
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -44,16 +44,22 @@ using namespace mozilla;
 using namespace mozilla::dom;
 
 JSObject*
 nsRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return RangeBinding::Wrap(aCx, this, aGivenProto);
 }
 
+DocGroup*
+nsRange::GetDocGroup() const
+{
+  return mOwner ? mOwner->GetDocGroup() : nullptr;
+}
+
 /******************************************************
  * stack based utilty class for managing monitor
  ******************************************************/
 
 static void InvalidateAllFrames(nsINode* aNode)
 {
   NS_PRECONDITION(aNode, "bad arg");
 
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -24,30 +24,32 @@
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/RangeBoundary.h"
 
 namespace mozilla {
 class ErrorResult;
 namespace dom {
 struct ClientRectsAndTexts;
+class DocGroup;
 class DocumentFragment;
 class DOMRect;
 class DOMRectList;
 class Selection;
 } // namespace dom
 } // namespace mozilla
 
 class nsRange final : public nsIDOMRange,
                       public nsStubMutationObserver,
                       public nsWrapperCache,
                       // For linking together selection-associated ranges.
                       public mozilla::LinkedListElement<nsRange>
 {
   typedef mozilla::ErrorResult ErrorResult;
+  typedef mozilla::dom::DocGroup DocGroup;
   typedef mozilla::dom::DOMRect DOMRect;
   typedef mozilla::dom::DOMRectList DOMRectList;
   typedef mozilla::RangeBoundary RangeBoundary;
   typedef mozilla::RawRangeBoundary RawRangeBoundary;
 
   virtual ~nsRange();
 
 public:
@@ -363,16 +365,17 @@ public:
                                   mozilla::ErrorResult& aError,
                                   nsIContent* aStartContainer,
                                   uint32_t aStartOffset,
                                   nsIContent* aEndContainer,
                                   uint32_t aEndOffset);
 
   nsINode* GetParentObject() const { return mOwner; }
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override final;
+  DocGroup* GetDocGroup() const;
 
 private:
   // no copy's or assigns
   nsRange(const nsRange&);
   nsRange& operator=(const nsRange&);
 
   /**
    * Cut or delete the range's contents.
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3522,38 +3522,16 @@ GetDesiredProto(JSContext* aCx, const JS
     aDesiredProto.set(nullptr);
     return true;
   }
 
   aDesiredProto.set(&protoVal.toObject());
   return true;
 }
 
-CustomElementReactionsStack*
-GetCustomElementReactionsStack(JS::Handle<JSObject*> aObj)
-{
-  // This might not be the right object, if there are wrappers. Unwrap if we can.
-  JSObject* obj = js::CheckedUnwrap(aObj);
-  if (!obj) {
-    return nullptr;
-  }
-
-  nsGlobalWindowInner* window = xpc::WindowGlobalOrNull(obj);
-  if (!window) {
-    return nullptr;
-  }
-
-  DocGroup* docGroup = window->AsInner()->GetDocGroup();
-  if (!docGroup) {
-    return nullptr;
-  }
-
-  return docGroup->CustomElementReactionsStack();
-}
-
 // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor
 already_AddRefed<Element>
 CreateXULOrHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
                        JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv)
 {
   // Step 1.
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3379,22 +3379,16 @@ bool GetSetlikeBackingObject(JSContext* 
                              bool* aBackingObjCreated);
 
 // Get the desired prototype object for an object construction from the given
 // CallArgs.  Null is returned if the default prototype should be used.
 bool
 GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
                 JS::MutableHandle<JSObject*> aDesiredProto);
 
-// Get the CustomElementReactionsStack for the docgroup of the global
-// of the underlying object of aObj.  This can be null if aObj can't
-// be CheckUnwrapped, or if the global of the result has no docgroup
-// (e.g. because it's not a Window global).
-CustomElementReactionsStack*
-GetCustomElementReactionsStack(JS::Handle<JSObject*> aObj);
 // This function is expected to be called from the constructor function for an
 // HTML or XUL element interface; the global/callargs need to be whatever was
 // passed to that constructor function.
 already_AddRefed<Element>
 CreateXULOrHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
                        JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv);
 
 void
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7851,26 +7851,26 @@ class CGPerSignatureCall(CGThing):
                     for arg, argname in self.getArguments())
 
             cgThings.append(
                 CGIfWrapper(CGList(xraySteps),
                             "objIsXray"))
 
         if (idlNode.getExtendedAttribute('CEReactions') is not None and
             not getter):
-            cgThings.append(CGGeneric(fill(
+            cgThings.append(CGGeneric(dedent(
                 """
                 Maybe<AutoCEReaction> ceReaction;
                 if (CustomElementRegistry::IsCustomElementEnabled()) {
-                  CustomElementReactionsStack* reactionsStack = GetCustomElementReactionsStack(${obj});
-                  if (reactionsStack) {
-                    ceReaction.emplace(reactionsStack, cx);
+                  DocGroup* docGroup = self->GetDocGroup();
+                  if (docGroup) {
+                    ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx);
                   }
                 }
-                """, obj=objectName)))
+                """)))
 
         # If this is a method that was generated by a maplike/setlike
         # interface, use the maplike/setlike generator to fill in the body.
         # Otherwise, use CGCallGenerator to call the native method.
         if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
             if (idlNode.maplikeOrSetlikeOrIterable.isMaplike() or
                 idlNode.maplikeOrSetlikeOrIterable.isSetlike()):
                 cgThings.append(CGMaplikeOrSetlikeMethodGenerator(descriptor,
@@ -14159,16 +14159,18 @@ class CGForwardDeclarations(CGWrapper):
             for t in getTypesFromCallback(callback):
                 builder.forwardDeclareForType(t, config)
 
         for d in callbackInterfaces:
             builder.add(d.nativeType)
             builder.add(d.nativeType + "Atoms", isStruct=True)
             for t in getTypesFromDescriptor(d):
                 builder.forwardDeclareForType(t, config)
+            if d.hasCEReactions():
+                builder.addInMozillaDom("DocGroup")
 
         for d in dictionaries:
             if len(d.members) > 0:
                 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
             for t in getTypesFromDictionary(d):
                 builder.forwardDeclareForType(t, config)
 
         for className, isStruct in additionalDeclarations:
@@ -14230,28 +14232,25 @@ class CGBindingRoot(CGThing):
         def descriptorRequiresPreferences(desc):
             iface = desc.interface
             return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface])
 
         def descriptorDeprecated(desc):
             iface = desc.interface
             return any(m.getExtendedAttribute("Deprecated") for m in iface.members + [iface])
 
-        def descriptorHasCEReactions(desc):
-            iface = desc.interface
-            return any(m.getExtendedAttribute("CEReactions") for m in iface.members + [iface])
-
         bindingHeaders["nsIDocument.h"] = any(
             descriptorDeprecated(d) for d in descriptors)
         bindingHeaders["mozilla/Preferences.h"] = any(
             descriptorRequiresPreferences(d) for d in descriptors)
         bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
             d.concrete and d.proxy for d in descriptors)
-        bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = any(
-            descriptorHasCEReactions(d) for d in descriptors)
+        hasCEReactions = any(d.hasCEReactions() for d in descriptors)
+        bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = hasCEReactions
+        bindingHeaders["mozilla/dom/DocGroup.h"] = hasCEReactions
 
         def descriptorHasChromeOnly(desc):
             ctor = desc.interface.ctor()
 
             return (any(isChromeOnly(a) or needsContainsHack(a) or
                         needsCallerType(a)
                         for a in desc.interface.members) or
                     desc.interface.getExtendedAttribute("ChromeOnly") is not None or
@@ -15082,16 +15081,22 @@ class CGBindingImplClass(CGClass):
         else:
             wrapReturnType = "JSObject*"
         self.methodDecls.insert(0,
                                 ClassMethod(wrapMethodName, wrapReturnType,
                                             wrapArgs, virtual=descriptor.wrapperCache,
                                             breakAfterReturnDecl=" ",
                                             override=descriptor.wrapperCache,
                                             body=self.getWrapObjectBody()))
+        if descriptor.hasCEReactions():
+            self.methodDecls.insert(0,
+                                    ClassMethod("GetDocGroup", "DocGroup*", [],
+                                                const=True,
+                                                breakAfterReturnDecl=" ",
+                                                body=self.getGetDocGroupBody()))
         if wantGetParent:
             self.methodDecls.insert(0,
                                     ClassMethod("GetParentObject",
                                                 self.getGetParentObjectReturnType(),
                                                 [], const=True,
                                                 breakAfterReturnDecl=" ",
                                                 body=self.getGetParentObjectBody()))
 
@@ -15102,16 +15107,19 @@ class CGBindingImplClass(CGClass):
 
     def getGetParentObjectReturnType(self):
         return ("// TODO: return something sensible here, and change the return type\n"
                 "%s*" % self.descriptor.nativeType.split('::')[-1])
 
     def getGetParentObjectBody(self):
         return None
 
+    def getGetDocGroupBody(self):
+        return None
+
     def deps(self):
         return self._deps
 
 
 class CGExampleClass(CGBindingImplClass):
     """
     Codegen for the actual example class implementation for this descriptor
     """
@@ -15241,16 +15249,18 @@ class CGExampleRoot(CGThing):
         descriptor = config.getDescriptor(interfaceName)
 
         self.root = CGWrapper(CGExampleClass(descriptor),
                               pre="\n", post="\n")
 
         self.root = CGNamespace.build(["mozilla", "dom"], self.root)
 
         builder = ForwardDeclarationBuilder()
+        if descriptor.hasCEReactions():
+            builder.addInMozillaDom("DocGroup")
         for member in descriptor.interface.members:
             if not member.isAttr() and not member.isMethod():
                 continue
             if member.isStatic():
                 builder.addInMozillaDom("GlobalObject")
             if member.isAttr():
                 if not member.isMaplikeOrSetlikeAttr():
                     builder.forwardDeclareForType(member.type, config)
@@ -15558,17 +15568,17 @@ class CGJSImplClass(CGBindingImplClass):
         extradeclarations = fill(
             """
             public:
               $*{isupportsDecl}
               $*{ccDecl}
 
             private:
               RefPtr<${jsImplName}> mImpl;
-              nsCOMPtr<nsISupports> mParent;
+              nsCOMPtr<nsIGlobalObject> mParent;
 
             """,
             isupportsDecl=isupportsDecl,
             ccDecl=ccDecl,
             jsImplName=jsImplName(descriptor.name))
 
         if descriptor.interface.hasChildInterfaces():
             decorators = ""
@@ -15648,16 +15658,26 @@ class CGJSImplClass(CGBindingImplClass):
             name=self.descriptor.name)
 
     def getGetParentObjectReturnType(self):
         return "nsISupports*"
 
     def getGetParentObjectBody(self):
         return "return mParent;\n"
 
+    def getGetDocGroupBody(self):
+        return dedent(
+            """
+            nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
+            if (!window) {
+              return nullptr;
+            }
+            return window->GetDocGroup();
+            """)
+
     def getCreateFromExistingBody(self):
         # XXXbz we could try to get parts of this (e.g. the argument
         # conversions) auto-generated by somehow creating an IDLMethod and
         # adding it to our interface, but we'd still need to special-case the
         # implementation slightly to have it not try to forward to the JS
         # object...
         return fill(
             """
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -703,16 +703,19 @@ class Descriptor(DescriptorProvider):
         # isExposedConditionally does not necessarily imply thread checks
         # (since at least [SecureContext] is independent of them), but we're
         # only used to decide whether to include nsThreadUtils.h, so we don't
         # worry about that.
         return ((self.isExposedConditionally() and
                  not self.interface.isExposedInWindow()) or
                 self.interface.isExposedInSomeButNotAllWorkers())
 
+    def hasCEReactions(self):
+        return any(m.getExtendedAttribute("CEReactions") for m in self.interface.members)
+
     def isExposedConditionally(self):
         return (self.interface.isExposedConditionally() or
                 self.interface.isExposedInSomeButNotAllWorkers())
 
     def needsXrayResolveHooks(self):
         """
         Generally, any interface with NeedResolve needs Xray
         resolveOwnProperty and enumerateOwnProperties hooks.  But for
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -16,16 +16,17 @@
 #include "nsCOMPtr.h"
 #include "nsGenericHTMLElement.h"
 #include "nsWrapperCache.h"
 
 // Forward declare this before we include TestCodeGenBinding.h, because that header relies on including
 // this one for it, for ParentDict. Hopefully it won't begin to rely on it in more fundamental ways.
 namespace mozilla {
 namespace dom {
+class DocGroup;
 class TestExternalInterface;
 class Promise;
 } // namespace dom
 } // namespace mozilla
 
 // We don't export TestCodeGenBinding.h, but it's right in our parent dir.
 #ifdef XP_WIN
 // If we're on windows, simulate including windows.h. This step will cause
@@ -110,18 +111,19 @@ public:
 };
 
 class TestInterface : public nsISupports,
                       public nsWrapperCache
 {
 public:
   NS_DECL_ISUPPORTS
 
-  // We need a GetParentObject to make binding codegen happy
+  // We need a GetParentObject and GetDocGroup to make binding codegen happy
   virtual nsISupports* GetParentObject();
+  DocGroup* GetDocGroup() const;
 
   // And now our actual WebIDL API
   // Constructors
   static
   already_AddRefed<TestInterface>
     Constructor(const GlobalObject&, ErrorResult&);
   static
   already_AddRefed<TestInterface>
@@ -1473,18 +1475,19 @@ public:
 };
 
 class TestCEReactionsInterface : public nsISupports,
                                  public nsWrapperCache
 {
 public:
   NS_DECL_ISUPPORTS
 
-  // We need a GetParentObject to make binding codegen happy
+  // We need a GetParentObject and GetDocGroup to make binding codegen happy
   virtual nsISupports* GetParentObject();
+  DocGroup* GetDocGroup() const;
 
   int32_t Item(uint32_t);
   uint32_t Length() const;
   int32_t IndexedGetter(uint32_t, bool &);
   void IndexedSetter(uint32_t, int32_t);
   void NamedDeleter(const nsAString&, bool &);
   void NamedGetter(const nsAString&, bool &, nsString&);
   void NamedSetter(const nsAString&, const nsAString&);
--- a/dom/html/HTMLOptionsCollection.cpp
+++ b/dom/html/HTMLOptionsCollection.cpp
@@ -225,16 +225,22 @@ HTMLOptionsCollection::NamedGetter(const
 }
 
 nsINode*
 HTMLOptionsCollection::GetParentObject()
 {
   return mSelect;
 }
 
+DocGroup*
+HTMLOptionsCollection::GetDocGroup() const
+{
+  return mSelect ? mSelect->GetDocGroup() : nullptr;
+}
+
 void
 HTMLOptionsCollection::GetSupportedNames(nsTArray<nsString>& aNames)
 {
   AutoTArray<nsAtom*, 8> atoms;
   for (uint32_t i = 0; i < mElements.Length(); ++i) {
     HTMLOptionElement* content = mElements.ElementAt(i);
     if (content) {
       // Note: HasName means the names is exposed on the document,
--- a/dom/html/HTMLOptionsCollection.h
+++ b/dom/html/HTMLOptionsCollection.h
@@ -15,16 +15,17 @@
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsGenericHTMLElement.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 
+class DocGroup;
 class HTMLElementOrLong;
 class HTMLOptionElementOrHTMLOptGroupElement;
 class HTMLSelectElement;
 
 /**
  * The collection of options in the select (what you get back when you do
  * select.options in DOM)
  */
@@ -53,16 +54,17 @@ protected:
   {
     nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
   }
 public:
 
   virtual uint32_t Length() override;
   virtual Element* GetElementAt(uint32_t aIndex) override;
   virtual nsINode* GetParentObject() override;
+  DocGroup* GetDocGroup() const;
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(HTMLOptionsCollection,
                                                          nsIHTMLCollection)
 
   // Helpers for HTMLSelectElement
   /**
    * Insert an option
    * @param aOption the option to insert
--- a/dom/html/nsDOMStringMap.cpp
+++ b/dom/html/nsDOMStringMap.cpp
@@ -57,16 +57,22 @@ nsDOMStringMap::~nsDOMStringMap()
   // Check if element still exists, may have been unlinked by cycle collector.
   if (mElement) {
     // Call back to element to null out weak reference to this object.
     mElement->ClearDataset();
     mElement->RemoveMutationObserver(this);
   }
 }
 
+DocGroup*
+nsDOMStringMap::GetDocGroup() const
+{
+  return mElement ? mElement->GetDocGroup() : nullptr;
+}
+
 /* virtual */
 JSObject*
 nsDOMStringMap::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return DOMStringMapBinding::Wrap(cx, this, aGivenProto);
 }
 
 void
--- a/dom/html/nsDOMStringMap.h
+++ b/dom/html/nsDOMStringMap.h
@@ -11,32 +11,37 @@
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 #include "mozilla/dom/Element.h"
 #include "jsfriendapi.h" // For js::ExpandoAndGeneration
 
 namespace mozilla {
 class ErrorResult;
+namespace dom {
+class DocGroup;
+} // namespace dom
 } // namespace mozilla
 
 class nsDOMStringMap : public nsStubMutationObserver,
                        public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMStringMap)
 
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
 
   nsINode* GetParentObject()
   {
     return mElement;
   }
 
+  mozilla::dom::DocGroup* GetDocGroup() const;
+
   explicit nsDOMStringMap(mozilla::dom::Element* aElement);
 
   // WebIDL API
   virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
   void NamedGetter(const nsAString& aProp, bool& found,
                    mozilla::dom::DOMString& aResult) const;
   void NamedSetter(const nsAString& aProp, const nsAString& aValue,
                    mozilla::ErrorResult& rv);
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -1257,16 +1257,21 @@ txMozillaXSLTProcessor::ContentRemoved(n
 }
 
 /* virtual */ JSObject*
 txMozillaXSLTProcessor::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
     return XSLTProcessorBinding::Wrap(aCx, this, aGivenProto);
 }
 
+DocGroup*
+txMozillaXSLTProcessor::GetDocGroup() const
+{
+    return mStylesheetDocument ? mStylesheetDocument->GetDocGroup() : nullptr;
+}
 
 /* static */ already_AddRefed<txMozillaXSLTProcessor>
 txMozillaXSLTProcessor::Constructor(const GlobalObject& aGlobal,
                                     mozilla::ErrorResult& aRv)
 {
     RefPtr<txMozillaXSLTProcessor> processor =
         new txMozillaXSLTProcessor(aGlobal.GetAsSupports());
     return processor.forget();
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.h
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.h
@@ -25,16 +25,17 @@ class nsIDOMNode;
 class nsIURI;
 class txStylesheet;
 class txResultRecycler;
 class txIGlobalParameter;
 
 namespace mozilla {
 namespace dom {
 
+class DocGroup;
 class Document;
 class DocumentFragment;
 class GlobalObject;
 
 } // namespace dom
 } // namespace mozilla
 
 /* bacd8ad0-552f-11d3-a9f7-000064657374 */
@@ -99,16 +100,18 @@ public:
 
     // WebIDL
     nsISupports*
     GetParentObject() const
     {
         return mOwner;
     }
 
+    mozilla::dom::DocGroup* GetDocGroup() const;
+
     static already_AddRefed<txMozillaXSLTProcessor>
     Constructor(const mozilla::dom::GlobalObject& aGlobal,
                 mozilla::ErrorResult& aRv);
 
     void ImportStylesheet(nsINode& stylesheet,
                           mozilla::ErrorResult& aRv);
     already_AddRefed<mozilla::dom::DocumentFragment>
     TransformToFragment(nsINode& source, nsIDocument& docVal, mozilla::ErrorResult& aRv);
--- a/layout/style/ServoKeyframeRule.cpp
+++ b/layout/style/ServoKeyframeRule.cpp
@@ -73,16 +73,26 @@ public:
   }
   nsIDocument* DocToUpdate() final { return nullptr; }
 
   nsINode* GetParentObject() final
   {
     return mRule ? mRule->GetDocument() : nullptr;
   }
 
+  DocGroup* GetDocGroup() const final
+  {
+    if (!mRule) {
+      return nullptr;
+    }
+
+    nsIDocument* document = mRule->GetDocument();
+    return document ? document->GetDocGroup() : 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;
   }
 
 private:
--- a/layout/style/ServoPageRule.cpp
+++ b/layout/style/ServoPageRule.cpp
@@ -54,16 +54,23 @@ ServoPageRuleDeclaration::GetParentRule(
 }
 
 nsINode*
 ServoPageRuleDeclaration::GetParentObject()
 {
   return Rule()->GetDocument();
 }
 
+DocGroup*
+ServoPageRuleDeclaration::GetDocGroup() const
+{
+  nsIDocument* document = Rule()->GetDocument();
+  return document ? document->GetDocGroup() : nullptr;
+}
+
 DeclarationBlock*
 ServoPageRuleDeclaration::GetCSSDeclaration(Operation aOperation)
 {
   return mDecls;
 }
 
 nsresult
 ServoPageRuleDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl)
--- a/layout/style/ServoPageRule.h
+++ b/layout/style/ServoPageRule.h
@@ -11,26 +11,31 @@
 
 #include "mozilla/dom/CSSPageRule.h"
 #include "mozilla/ServoBindingTypes.h"
 
 #include "nsDOMCSSDeclaration.h"
 
 namespace mozilla {
 
+namespace dom {
+class DocGroup;
+} // namespace dom
+
 class ServoDeclarationBlock;
 class ServoPageRule;
 
 class ServoPageRuleDeclaration final : public nsDOMCSSDeclaration
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD GetParentRule(nsIDOMCSSRule** aParent) final;
   nsINode* GetParentObject() final;
+  DocGroup* GetDocGroup() const final;
 
 protected:
   DeclarationBlock* GetCSSDeclaration(Operation aOperation) final;
   nsresult SetCSSDeclaration(DeclarationBlock* aDecl) final;
   nsIDocument* DocToUpdate() final;
   void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv,
                                 nsIPrincipal* aSubjectPrincipal) final;
   nsDOMCSSDeclaration::ServoCSSParsingEnvironment
--- a/layout/style/ServoStyleRule.cpp
+++ b/layout/style/ServoStyleRule.cpp
@@ -56,16 +56,23 @@ ServoStyleRuleDeclaration::GetParentRule
 }
 
 nsINode*
 ServoStyleRuleDeclaration::GetParentObject()
 {
   return Rule()->GetDocument();
 }
 
+DocGroup*
+ServoStyleRuleDeclaration::GetDocGroup() const
+{
+  nsIDocument* document = Rule()->GetDocument();
+  return document ? document->GetDocGroup() : nullptr;
+}
+
 DeclarationBlock*
 ServoStyleRuleDeclaration::GetCSSDeclaration(Operation aOperation)
 {
   return mDecls;
 }
 
 nsresult
 ServoStyleRuleDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl)
--- a/layout/style/ServoStyleRule.h
+++ b/layout/style/ServoStyleRule.h
@@ -15,26 +15,31 @@
 
 #include "nsICSSStyleRuleDOMWrapper.h"
 #include "nsIDOMCSSStyleRule.h"
 #include "nsICSSStyleRuleDOMWrapper.h"
 #include "nsDOMCSSDeclaration.h"
 
 namespace mozilla {
 
+namespace dom {
+class DocGroup;
+} // namespace dom
+
 class ServoDeclarationBlock;
 class ServoStyleRule;
 
 class ServoStyleRuleDeclaration final : public nsDOMCSSDeclaration
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD GetParentRule(nsIDOMCSSRule** aParent) final;
   nsINode* GetParentObject() final;
+  mozilla::dom::DocGroup* GetDocGroup() const final;
 
 protected:
   DeclarationBlock* GetCSSDeclaration(Operation aOperation) final;
   nsresult SetCSSDeclaration(DeclarationBlock* aDecl) final;
   nsIDocument* DocToUpdate() final;
   void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv,
                                 nsIPrincipal* aSubjectPrincipal) final;
   ServoCSSParsingEnvironment
--- a/layout/style/StyleRule.cpp
+++ b/layout/style/StyleRule.cpp
@@ -1076,16 +1076,26 @@ public:
   // need to forward QI for cycle collection things to StyleRule.
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual nsINode *GetParentObject() override
   {
     return mRule ? mRule->GetDocument() : nullptr;
   }
 
+  virtual DocGroup* GetDocGroup() const override
+  {
+    if (!mRule) {
+      return nullptr;
+    }
+
+    nsIDocument* document = mRule->GetDocument();
+    return document ? document->GetDocGroup() : nullptr;
+  }
+
 protected:
   // This reference is not reference-counted. The rule object owns us and we go
   // away when it does.
   css::StyleRule *mRule;
 };
 
 DOMCSSDeclarationImpl::DOMCSSDeclarationImpl(css::StyleRule *aRule)
   : mRule(aRule)
--- a/layout/style/nsCSSFontFaceRule.cpp
+++ b/layout/style/nsCSSFontFaceRule.cpp
@@ -297,16 +297,23 @@ nsCSSFontFaceStyleDecl::SetPropertyValue
 }
 
 nsINode*
 nsCSSFontFaceStyleDecl::GetParentObject()
 {
   return ContainingRule()->GetDocument();
 }
 
+DocGroup*
+nsCSSFontFaceStyleDecl::GetDocGroup() const
+{
+  nsIDocument* document = ContainingRule()->GetDocument();
+  return document ? document->GetDocGroup() : nullptr;
+}
+
 JSObject*
 nsCSSFontFaceStyleDecl::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::CSSStyleDeclarationBinding::Wrap(cx, this, aGivenProto);
 }
 
 // -------------------------------------------
 // nsCSSFontFaceRule
--- a/layout/style/nsCSSFontFaceRule.h
+++ b/layout/style/nsCSSFontFaceRule.h
@@ -9,16 +9,20 @@
 
 #include "mozilla/css/Rule.h"
 #include "nsCSSValue.h"
 #include "nsICSSDeclaration.h"
 #include "nsIDOMCSSFontFaceRule.h"
 
 namespace mozilla {
 
+namespace dom {
+class DocGroup;
+} // namespace dom
+
 struct CSSFontFaceDescriptors
 {
 #define CSS_FONT_DESC(name_, method_) nsCSSValue m##method_;
 #include "nsCSSFontDescList.h"
 #undef CSS_FONT_DESC
 
   const nsCSSValue& Get(nsCSSFontDesc aFontDescID) const;
   nsCSSValue& Get(nsCSSFontDesc aFontDescID);
@@ -38,16 +42,17 @@ public:
   NS_DECL_NSIDOMCSSSTYLEDECLARATION_HELPER
   NS_DECL_NSICSSDECLARATION
   virtual already_AddRefed<mozilla::dom::CSSValue>
   GetPropertyCSSValue(const nsAString& aProp, mozilla::ErrorResult& aRv)
     override;
   using nsICSSDeclaration::GetPropertyCSSValue;
 
   virtual nsINode *GetParentObject() override;
+  virtual mozilla::dom::DocGroup* GetDocGroup() const override;
   virtual void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) override;
 
   nsresult GetPropertyValue(nsCSSFontDesc aFontDescID,
                             nsAString & aResult) const;
 
   virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
 protected:
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -904,16 +904,27 @@ nsCSSKeyframeStyleDeclaration::DocToUpda
 }
 
 nsINode*
 nsCSSKeyframeStyleDeclaration::GetParentObject()
 {
   return mRule ? mRule->GetDocument() : nullptr;
 }
 
+DocGroup*
+nsCSSKeyframeStyleDeclaration::GetDocGroup() const
+{
+  if (!mRule) {
+    return nullptr;
+  }
+
+  nsIDocument* document = mRule->GetDocument();
+  return document ? document->GetDocGroup() : nullptr;
+}
+
 // -------------------------------------------
 // nsCSSKeyframeRule
 //
 
 nsCSSKeyframeRule::nsCSSKeyframeRule(const nsCSSKeyframeRule& aCopy)
   // copy everything except our reference count and mDOMDeclaration
   : dom::CSSKeyframeRule(aCopy)
   , mKeys(aCopy.mKeys)
@@ -1336,16 +1347,27 @@ nsCSSPageStyleDeclaration::DocToUpdate()
 }
 
 nsINode*
 nsCSSPageStyleDeclaration::GetParentObject()
 {
   return mRule ? mRule->GetDocument() : nullptr;
 }
 
+DocGroup*
+nsCSSPageStyleDeclaration::GetDocGroup() const
+{
+  if (!mRule) {
+    return nullptr;
+  }
+
+  nsIDocument* document = mRule->GetDocument();
+  return document ? document->GetDocGroup() : nullptr;
+}
+
 // -------------------------------------------
 // nsCSSPageRule
 //
 
 nsCSSPageRule::nsCSSPageRule(const nsCSSPageRule& aCopy)
   // copy everything except our reference count and mDOMDeclaration
   : dom::CSSPageRule(aCopy)
   , mDeclaration(new css::Declaration(*aCopy.mDeclaration))
--- a/layout/style/nsCSSRules.h
+++ b/layout/style/nsCSSRules.h
@@ -38,16 +38,17 @@
 
 class nsMediaList;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
+class DocGroup;
 class MediaList;
 }
 
 namespace css {
 
 class MediaRule final : public dom::CSSMediaRule
 {
 public:
@@ -224,16 +225,17 @@ public:
   GetServoCSSParsingEnvironment(nsIPrincipal* aSubjectPrincipal) const final;
   virtual nsIDocument* DocToUpdate() override;
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsCSSKeyframeStyleDeclaration,
                                                          nsICSSDeclaration)
 
   virtual nsINode* GetParentObject() override;
+  virtual mozilla::dom::DocGroup* GetDocGroup() const override;
 
 protected:
   virtual ~nsCSSKeyframeStyleDeclaration();
 
   // This reference is not reference-counted. The rule object tells us
   // when it's about to go away.
   nsCSSKeyframeRule* MOZ_NON_OWNING_REF mRule;
 };
@@ -349,16 +351,17 @@ public:
   GetServoCSSParsingEnvironment(nsIPrincipal* aSubjectPrincipal) const final;
   virtual nsIDocument* DocToUpdate() override;
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsCSSPageStyleDeclaration,
                                                          nsICSSDeclaration)
 
   virtual nsINode *GetParentObject() override;
+  virtual mozilla::dom::DocGroup* GetDocGroup() const override;
 
 protected:
   virtual ~nsCSSPageStyleDeclaration();
 
   // This reference is not reference-counted. The rule object tells us
   // when it's about to go away.
   nsCSSPageRule* MOZ_NON_OWNING_REF mRule;
 };
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -24,16 +24,17 @@
 #include "nsCoord.h"
 #include "nsColor.h"
 #include "nsIContent.h"
 #include "nsStyleStruct.h"
 #include "mozilla/WritingModes.h"
 
 namespace mozilla {
 namespace dom {
+class DocGroup;
 class Element;
 } // namespace dom
 struct ComputedGridTrackInfo;
 } // namespace mozilla
 
 struct nsComputedStyleMap;
 class nsIFrame;
 class nsIPresShell;
@@ -87,16 +88,21 @@ public:
                      StyleType aStyleType,
                      AnimationFlag aFlag = eWithAnimation);
 
   virtual nsINode *GetParentObject() override
   {
     return mContent;
   }
 
+  virtual mozilla::dom::DocGroup* GetDocGroup() const override
+  {
+    return mContent ? mContent->GetDocGroup() : nullptr;
+  }
+
   static already_AddRefed<nsStyleContext>
   GetStyleContext(mozilla::dom::Element* aElement, nsAtom* aPseudo,
                   nsIPresShell* aPresShell,
                   StyleType aStyleType = eAll);
 
   static already_AddRefed<nsStyleContext>
   GetStyleContextNoFlush(mozilla::dom::Element* aElement,
                          nsAtom* aPseudo,
--- a/layout/style/nsDOMCSSAttrDeclaration.cpp
+++ b/layout/style/nsDOMCSSAttrDeclaration.cpp
@@ -198,16 +198,22 @@ nsDOMCSSAttributeDeclaration::GetParentR
 }
 
 /* virtual */ nsINode*
 nsDOMCSSAttributeDeclaration::GetParentObject()
 {
   return mElement;
 }
 
+/* virtual */ DocGroup*
+nsDOMCSSAttributeDeclaration::GetDocGroup() const
+{
+  return mElement ? mElement->OwnerDoc()->GetDocGroup() : nullptr;
+}
+
 NS_IMETHODIMP
 nsDOMCSSAttributeDeclaration::SetPropertyValue(const nsCSSPropertyID aPropID,
                                                const nsAString& aValue,
                                                nsIPrincipal* aSubjectPrincipal)
 {
   // Scripted modifications to style.opacity or style.transform
   // could immediately force us into the animated state if heuristics suggest
   // this is scripted animation.
--- a/layout/style/nsDOMCSSAttrDeclaration.h
+++ b/layout/style/nsDOMCSSAttrDeclaration.h
@@ -34,16 +34,17 @@ public:
   virtual mozilla::DeclarationBlock* GetCSSDeclaration(Operation aOperation) override;
   virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv,
                                         nsIPrincipal* aSubjectPrincipal) override;
   nsDOMCSSDeclaration::ServoCSSParsingEnvironment
   GetServoCSSParsingEnvironment(nsIPrincipal* aSubjectPrincipal) const final;
   NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override;
 
   virtual nsINode* GetParentObject() override;
+  virtual DocGroup* GetDocGroup() const override;
 
   NS_IMETHOD SetPropertyValue(const nsCSSPropertyID aPropID,
                               const nsAString& aValue,
                               nsIPrincipal* aSubjectPrincipal) override;
 
 protected:
   ~nsDOMCSSAttributeDeclaration();
 
--- a/layout/style/nsICSSDeclaration.h
+++ b/layout/style/nsICSSDeclaration.h
@@ -28,16 +28,21 @@
 #include "nsString.h"
 #include "nsIDOMCSSRule.h"
 #include "nsIDOMCSSValue.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCOMPtr.h"
 
 class nsINode;
 class nsIPrincipal;
+namespace mozilla {
+namespace dom {
+class DocGroup;
+} // namespace dom
+} // namespace mozilla
 
 // dbeabbfa-6cb3-4f5c-aec2-dd558d9d681f
 #define NS_ICSSDECLARATION_IID \
 { 0xdbeabbfa, 0x6cb3, 0x4f5c, \
  { 0xae, 0xc2, 0xdd, 0x55, 0x8d, 0x9d, 0x68, 0x1f } }
 
 class nsICSSDeclaration : public nsIDOMCSSStyleDeclaration,
                           public nsWrapperCache
@@ -57,16 +62,17 @@ public:
    * method does NOT allow setting a priority (the priority will
    * always be set to default priority).
    */
   NS_IMETHOD SetPropertyValue(const nsCSSPropertyID aPropID,
                               const nsAString& aValue,
                               nsIPrincipal* aSubjectPrincipal = nullptr) = 0;
 
   virtual nsINode *GetParentObject() = 0;
+  virtual mozilla::dom::DocGroup* GetDocGroup() const = 0;
 
   // Also have to declare all the nsIDOMCSSStyleDeclaration methods,
   // since we want to be able to call them from the WebIDL versions.
   NS_IMETHOD GetCssText(nsAString& aCssText) override = 0;
   NS_IMETHOD SetCssText(const nsAString& aCssText,
                         nsIPrincipal* aSubjectPrincipal = nullptr) override = 0;
   NS_IMETHOD GetPropertyValue(const nsAString& aPropName,
                               nsAString& aValue) override = 0;