Bug 1422197 - Add fast path to get DocGroup in binding code for [CEReactions]; draft
authorEdgar Chen <echen@mozilla.com>
Mon, 27 Nov 2017 16:10:27 +0800
changeset 711592 a60883c49ed404100c7e33c6f6f66f6e8ec4679f
parent 710308 a186d83c0941f89addea1d949d9ad3b1a8247cef
child 743827 6ae0424a89dab05e3b575a8ca449d5786eff1aeb
push id93090
push userechen@mozilla.com
push dateThu, 14 Dec 2017 04:03:30 +0000
bugs1422197
milestone59.0a1
Bug 1422197 - Add fast path to get DocGroup in binding code for [CEReactions]; 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
@@ -3116,8 +3116,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:
@@ -339,16 +341,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
@@ -7854,26 +7854,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,
@@ -14162,16 +14162,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:
@@ -14233,28 +14235,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
@@ -15085,16 +15084,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()))
 
@@ -15105,16 +15110,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
     """
@@ -15244,16 +15252,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)
@@ -15561,17 +15571,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 = ""
@@ -15651,16 +15661,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
@@ -686,16 +686,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
@@ -1256,16 +1256,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;