Bug 992521 - Part 1: Remove "in document" flag on ShadowRoot and its descendants. r=smaug
authorWilliam Chen <wchen@mozilla.com>
Sat, 07 Jun 2014 01:42:53 -0700
changeset 187324 78af7d36d4a63960e93e1e82b4c9e12112799236
parent 187323 4f059440e912584e98e45775578c323d1eab2e66
child 187325 8968e7307508e5cba5e1ee7597cb507afbeb6486
push id44568
push userwchen@mozilla.com
push dateSat, 07 Jun 2014 08:43:41 +0000
treeherdermozilla-inbound@8730ce555151 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs992521
milestone32.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 992521 - Part 1: Remove "in document" flag on ShadowRoot and its descendants. r=smaug
content/base/public/nsIContent.h
content/base/public/nsINode.h
content/base/src/Element.cpp
content/base/src/ShadowRoot.cpp
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsINode.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsPresShell.cpp
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -878,20 +878,20 @@ public:
    * there is a scroll frame that contains the frame being scrolled). This
    * frame is always the first continuation.
    *
    * In the case of absolutely positioned elements and floated elements, this
    * frame is the out of flow frame, not the placeholder.
    */
   nsIFrame* GetPrimaryFrame() const
   {
-    return IsInDoc() ? mPrimaryFrame : nullptr;
+    return (IsInDoc() || HasFlag(NODE_IS_IN_SHADOW_TREE)) ? mPrimaryFrame : nullptr;
   }
   void SetPrimaryFrame(nsIFrame* aFrame) {
-    NS_ASSERTION(IsInDoc(), "This will end badly!");
+    MOZ_ASSERT(IsInDoc() || HasFlag(NODE_IS_IN_SHADOW_TREE), "This will end badly!");
     NS_PRECONDITION(!aFrame || !mPrimaryFrame || aFrame == mPrimaryFrame,
                     "Losing track of existing primary frame");
     mPrimaryFrame = aFrame;
   }
 
   nsresult LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
                                       nsAString& aNamespaceURI) const;
 
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -521,16 +521,29 @@ public:
    * @return the current document
    */
   nsIDocument *GetCurrentDoc() const
   {
     return IsInDoc() ? OwnerDoc() : nullptr;
   }
 
   /**
+   * This method gets the current doc of the node hosting this content
+   * or the current doc of this content if it is not being hosted. This
+   * method walks through ShadowRoot boundaries until it reach the host
+   * that is located in the root of the "tree of trees" (see Shadow DOM
+   * spec) and returns the current doc for that host.
+   */
+  nsIDocument* GetCrossShadowCurrentDoc() const
+  {
+    return HasFlag(NODE_IS_IN_SHADOW_TREE) ?
+      GetCrossShadowCurrentDocInternal() : GetCurrentDoc();
+  }
+
+  /**
    * The values returned by this function are the ones defined for
    * nsIDOMNode.nodeType
    */
   uint16_t NodeType() const
   {
     return mNodeInfo->NodeType();
   }
   const nsString& NodeName() const
@@ -803,39 +816,17 @@ public:
     return mParent && mParent->IsElement() ? mParent->AsElement() : nullptr;
   }
 
   /**
    * Get the root of the subtree this node belongs to.  This never returns
    * null.  It may return 'this' (e.g. for document nodes, and nodes that
    * are the roots of disconnected subtrees).
    */
-  nsINode* SubtreeRoot() const
-  {
-    // There are three cases of interest here.  nsINodes that are really:
-    // 1. nsIDocument nodes - Are always in the document.
-    // 2. nsIContent nodes - Are either in the document, or mSubtreeRoot
-    //    is updated in BindToTree/UnbindFromTree.
-    // 3. nsIAttribute nodes - Are never in the document, and mSubtreeRoot
-    //    is always 'this' (as set in nsINode's ctor).
-    nsINode* node = IsInDoc() ? OwnerDocAsNode() : mSubtreeRoot;
-    NS_ASSERTION(node, "Should always have a node here!");
-#ifdef DEBUG
-    {
-      const nsINode* slowNode = this;
-      const nsINode* iter = slowNode;
-      while ((iter = iter->GetParentNode())) {
-        slowNode = iter;
-      }
-
-      NS_ASSERTION(slowNode == node, "These should always be in sync!");
-    }
-#endif
-    return node;
-  }
+  nsINode* SubtreeRoot() const;
 
   /**
    * See nsIDOMEventTarget
    */
   NS_DECL_NSIDOMEVENTTARGET
 
   virtual mozilla::EventListenerManager*
     GetExistingListenerManager() const MOZ_OVERRIDE;
@@ -1200,16 +1191,18 @@ public:
    */
   bool Contains(const nsINode* aOther) const;
   nsresult Contains(nsIDOMNode* aOther, bool* aReturn);
 
   bool UnoptimizableCCNode() const;
 
 private:
 
+  nsIDocument* GetCrossShadowCurrentDocInternal() const;
+
   nsIContent* GetNextNodeImpl(const nsINode* aRoot,
                               const bool aSkipChildren) const
   {
     // Can't use nsContentUtils::ContentIsDescendantOf here, since we
     // can't include it here.
 #ifdef DEBUG
     if (aRoot) {
       const nsINode* cur = this;
@@ -1515,17 +1508,18 @@ protected:
   { return GetBoolFlag(ElementHasWeirdParserInsertionMode); }
   bool HandlingClick() const { return GetBoolFlag(NodeHandlingClick); }
   void SetHandlingClick() { SetBoolFlag(NodeHandlingClick); }
   void ClearHandlingClick() { ClearBoolFlag(NodeHandlingClick); }
 
   void SetSubtreeRootPointer(nsINode* aSubtreeRoot)
   {
     NS_ASSERTION(aSubtreeRoot, "aSubtreeRoot can never be null!");
-    NS_ASSERTION(!(IsNodeOfType(eCONTENT) && IsInDoc()), "Shouldn't be here!");
+    NS_ASSERTION(!(IsNodeOfType(eCONTENT) && IsInDoc()) &&
+                 !HasFlag(NODE_IS_IN_SHADOW_TREE), "Shouldn't be here!");
     mSubtreeRoot = aSubtreeRoot;
   }
 
   void ClearSubtreeRootPointer()
   {
     mSubtreeRoot = nullptr;
   }
 
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -826,17 +826,17 @@ Element::CreateShadowRoot(ErrorResult& a
   nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
   shadowRoot->SetAssociatedBinding(xblBinding);
   xblBinding->SetBoundElement(this);
 
   SetXBLBinding(xblBinding);
 
   // Recreate the frame for the bound content because binding a ShadowRoot
   // changes how things are rendered.
-  nsIDocument* doc = GetCurrentDoc();
+  nsIDocument* doc = GetCrossShadowCurrentDoc();
   if (doc) {
     nsIPresShell *shell = doc->GetShell();
     if (shell) {
       shell->RecreateFramesFor(this);
     }
   }
 
   return shadowRoot.forget();
@@ -1226,16 +1226,17 @@ Element::BindToTree(nsIDocument* aDocume
   if (aParent) {
     if (aParent->IsInNativeAnonymousSubtree()) {
       SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
     }
     if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) {
       SetFlags(NODE_CHROME_ONLY_ACCESS);
     }
     if (aParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      ClearSubtreeRootPointer();
       SetFlags(NODE_IS_IN_SHADOW_TREE);
     }
     ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
     if (parentContainingShadow) {
       DOMSlots()->mContainingShadow = parentContainingShadow;
     }
   }
 
@@ -1286,18 +1287,19 @@ Element::BindToTree(nsIDocument* aDocume
     UnsetFlags(NODE_FORCE_XBL_BINDINGS |
                // And clear the lazy frame construction bits.
                NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
                // And the restyle bits
                ELEMENT_ALL_RESTYLE_FLAGS);
 
     // Propagate scoped style sheet tracking bit.
     SetIsElementInStyleScope(mParent->IsElementInStyleScope());
-  } else {
-    // If we're not in the doc, update our subtree pointer.
+  } else if (!HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    // If we're not in the doc and not in a shadow tree,
+    // update our subtree pointer.
     SetSubtreeRootPointer(aParent->SubtreeRoot());
   }
 
   // This has to be here, rather than in nsGenericHTMLElement::BindToTree,
   //  because it has to happen after updating the parent pointer, but before
   //  recursively binding the kids.
   if (IsHTML()) {
     SetDirOnBind(this, aParent);
@@ -1430,16 +1432,17 @@ Element::UnbindFromTree(bool aDeep, bool
       mParent = nullptr;
       NS_RELEASE(p);
     } else {
       mParent = nullptr;
     }
     SetParentIsContent(false);
   }
   ClearInDocument();
+  UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 
   // Begin keeping track of our subtree root.
   SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
 
   if (document) {
     // Notify XBL- & nsIAnonymousContentCreator-generated
     // anonymous content that the document is changing.
     if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
@@ -1465,17 +1468,17 @@ Element::UnbindFromTree(bool aDeep, bool
     DeleteProperty(nsGkAtoms::transitionsOfAfterProperty);
     DeleteProperty(nsGkAtoms::transitionsProperty);
     DeleteProperty(nsGkAtoms::animationsOfBeforeProperty);
     DeleteProperty(nsGkAtoms::animationsOfAfterProperty);
     DeleteProperty(nsGkAtoms::animationsProperty);
   }
 
   // Unset this since that's what the old code effectively did.
-  UnsetFlags(NODE_FORCE_XBL_BINDINGS | NODE_IS_IN_SHADOW_TREE);
+  UnsetFlags(NODE_FORCE_XBL_BINDINGS);
   
 #ifdef MOZ_XUL
   nsXULElement* xulElem = nsXULElement::FromContent(this);
   if (xulElem) {
     xulElem->SetXULBindingParent(nullptr);
   }
   else
 #endif
--- a/content/base/src/ShadowRoot.cpp
+++ b/content/base/src/ShadowRoot.cpp
@@ -67,19 +67,24 @@ NS_IMPL_RELEASE_INHERITED(ShadowRoot, Do
 ShadowRoot::ShadowRoot(nsIContent* aContent,
                        already_AddRefed<nsINodeInfo>&& aNodeInfo,
                        nsXBLPrototypeBinding* aProtoBinding)
   : DocumentFragment(aNodeInfo), mPoolHost(aContent),
     mProtoBinding(aProtoBinding), mShadowElement(nullptr),
     mInsertionPointChanged(false)
 {
   SetHost(aContent);
+
+  // Nodes in a shadow tree should never store a value
+  // in the subtree root pointer, nodes in the shadow tree
+  // track the subtree root using GetContainingShadow().
+  ClearSubtreeRootPointer();
+
   SetFlags(NODE_IS_IN_SHADOW_TREE);
-  // ShadowRoot isn't really in the document but it behaves like it is.
-  SetInDocument();
+
   DOMSlots()->mBindingParent = aContent;
   DOMSlots()->mContainingShadow = this;
 
   // Add the ShadowRoot as a mutation observer on the host to watch
   // for mutations because the insertion points in this ShadowRoot
   // may need to be updated when the host children are modified.
   mPoolHost->AddMutationObserver(this);
 }
@@ -87,17 +92,21 @@ ShadowRoot::ShadowRoot(nsIContent* aCont
 ShadowRoot::~ShadowRoot()
 {
   if (mPoolHost) {
     // mPoolHost may have been unlinked or a new ShadowRoot may have been
     // creating, making this one obsolete.
     mPoolHost->RemoveMutationObserver(this);
   }
 
-  ClearInDocument();
+  UnsetFlags(NODE_IS_IN_SHADOW_TREE);
+
+  // nsINode destructor expects mSubtreeRoot == this.
+  SetSubtreeRootPointer(this);
+
   SetHost(nullptr);
 }
 
 JSObject*
 ShadowRoot::WrapObject(JSContext* aCx)
 {
   return mozilla::dom::ShadowRootBinding::Wrap(aCx, this);
 }
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -487,16 +487,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
     DataSlots()->mBindingParent = aBindingParent; // Weak, so no addref happens.
     if (aParent->IsInNativeAnonymousSubtree()) {
       SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
     }
     if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) {
       SetFlags(NODE_CHROME_ONLY_ACCESS);
     }
     if (aParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      ClearSubtreeRootPointer();
       SetFlags(NODE_IS_IN_SHADOW_TREE);
     }
     ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
     if (parentContainingShadow) {
       DataSlots()->mContainingShadow = parentContainingShadow;
     }
   }
 
@@ -522,18 +523,19 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
 
     // XXX See the comment in Element::BindToTree
     SetInDocument();
     if (mText.IsBidi()) {
       aDocument->SetBidiEnabled();
     }
     // Clear the lazy frame construction bits.
     UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
-  } else {
-    // If we're not in the doc, update our subtree pointer.
+  } else if (!HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    // If we're not in the doc and not in a shadow tree,
+    // update our subtree pointer.
     SetSubtreeRootPointer(aParent->SubtreeRoot());
   }
 
   nsNodeUtils::ParentChainChanged(this);
 
   UpdateEditableState(false);
 
   NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document");
@@ -544,20 +546,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
   return NS_OK;
 }
 
 void
 nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // Unset frame flags; if we need them again later, they'll get set again.
   UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
-             NS_REFRAME_IF_WHITESPACE |
-             // Also unset the shadow tree flag because it can
-             // no longer be a descendant of a ShadowRoot.
-             NODE_IS_IN_SHADOW_TREE);
+             NS_REFRAME_IF_WHITESPACE);
   
   nsIDocument *document = GetCurrentDoc();
   if (document) {
     // Notify XBL- & nsIAnonymousContentCreator-generated
     // anonymous content that the document is changing.
     // This is needed to update the insertion point.
     document->BindingManager()->RemovedFromDocument(this, document);
   }
@@ -566,16 +565,17 @@ nsGenericDOMDataNode::UnbindFromTree(boo
     if (GetParent()) {
       NS_RELEASE(mParent);
     } else {
       mParent = nullptr;
     }
     SetParentIsContent(false);
   }
   ClearInDocument();
+  UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 
   // Begin keeping track of our subtree root.
   SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
 
   nsDataSlots *slots = GetExistingDataSlots();
   if (slots) {
     slots->mBindingParent = nullptr;
     slots->mContainingShadow = nullptr;
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -230,16 +230,51 @@ nsINode::GetTextEditorRootContent(nsIEdi
     nsIContent* rootContent = GetEditorRootContent(editor);
     if (aEditor)
       editor.swap(*aEditor);
     return rootContent;
   }
   return nullptr;
 }
 
+nsINode*
+nsINode::SubtreeRoot() const
+{
+  // There are four cases of interest here.  nsINodes that are really:
+  // 1. nsIDocument nodes - Are always in the document.
+  // 2.a nsIContent nodes not in a shadow tree - Are either in the document,
+  //     or mSubtreeRoot is updated in BindToTree/UnbindFromTree.
+  // 2.b nsIContent nodes in a shadow tree - Are never in the document,
+  //     ignore mSubtreeRoot and return the containing shadow root.
+  // 4. nsIAttribute nodes - Are never in the document, and mSubtreeRoot
+  //    is always 'this' (as set in nsINode's ctor).
+  nsINode* node;
+  if (IsInDoc()) {
+    node = OwnerDocAsNode();
+  } else if (IsContent()) {
+    ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
+    node = containingShadow ? containingShadow : mSubtreeRoot;
+  } else {
+    node = mSubtreeRoot;
+  }
+  NS_ASSERTION(node, "Should always have a node here!");
+#ifdef DEBUG
+  {
+    const nsINode* slowNode = this;
+    const nsINode* iter = slowNode;
+    while ((iter = iter->GetParentNode())) {
+      slowNode = iter;
+    }
+
+    NS_ASSERTION(slowNode == node, "These should always be in sync!");
+  }
+#endif
+  return node;
+}
+
 static nsIContent* GetRootForContentSubtree(nsIContent* aContent)
 {
   NS_ENSURE_TRUE(aContent, nullptr);
 
   // Special case for ShadowRoot because the ShadowRoot itself is
   // the root. This is necessary to prevent selection from crossing
   // the ShadowRoot boundary.
   ShadowRoot* containingShadow = aContent->GetContainingShadow();
@@ -263,33 +298,33 @@ nsINode::GetSelectionRootContent(nsIPres
 {
   NS_ENSURE_TRUE(aPresShell, nullptr);
 
   if (IsNodeOfType(eDOCUMENT))
     return static_cast<nsIDocument*>(this)->GetRootElement();
   if (!IsNodeOfType(eCONTENT))
     return nullptr;
 
-  if (GetCurrentDoc() != aPresShell->GetDocument()) {
+  if (GetCrossShadowCurrentDoc() != aPresShell->GetDocument()) {
     return nullptr;
   }
 
   if (static_cast<nsIContent*>(this)->HasIndependentSelection()) {
     // This node should be a descendant of input/textarea editor.
     nsIContent* content = GetTextEditorRootContent();
     if (content)
       return content;
   }
 
   nsPresContext* presContext = aPresShell->GetPresContext();
   if (presContext) {
     nsIEditor* editor = nsContentUtils::GetHTMLEditor(presContext);
     if (editor) {
       // This node is in HTML editor.
-      nsIDocument* doc = GetCurrentDoc();
+      nsIDocument* doc = GetCrossShadowCurrentDoc();
       if (!doc || doc->HasFlag(NODE_IS_EDITABLE) ||
           !HasFlag(NODE_IS_EDITABLE)) {
         nsIContent* editorRoot = GetEditorRootContent(editor);
         NS_ENSURE_TRUE(editorRoot, nullptr);
         return nsContentUtils::IsInSameAnonymousTree(this, editorRoot) ?
                  editorRoot :
                  GetRootForContentSubtree(static_cast<nsIContent*>(this));
       }
@@ -343,16 +378,27 @@ nsINode::ChildNodes()
 }
 
 void
 nsINode::GetTextContentInternal(nsAString& aTextContent)
 {
   SetDOMStringToNull(aTextContent);
 }
 
+nsIDocument*
+nsINode::GetCrossShadowCurrentDocInternal() const
+{
+  MOZ_ASSERT(HasFlag(NODE_IS_IN_SHADOW_TREE) && IsContent(),
+             "Should only be caled on nodes in the shadow tree.");
+
+  // Cross ShadowRoot boundary.
+  ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
+  return containingShadow->GetHost()->GetCrossShadowCurrentDoc();
+}
+
 #ifdef DEBUG
 void
 nsINode::CheckNotNativeAnonymous() const
 {
   if (!IsNodeOfType(eCONTENT))
     return;
   nsIContent* content = static_cast<const nsIContent *>(this)->GetBindingParent();
   while (content) {
@@ -1456,17 +1502,17 @@ nsINode::doInsertChildAt(nsIContent* aKi
   nsresult rv;
 
   // The id-handling code, and in the future possibly other code, need to
   // react to unexpected attribute changes.
   nsMutationGuard::DidMutate();
 
   // Do this before checking the child-count since this could cause mutations
   nsIDocument* doc = GetCurrentDoc();
-  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
+  mozAutoDocUpdate updateBatch(GetCrossShadowCurrentDoc(), UPDATE_CONTENT_MODEL, aNotify);
 
   if (OwnerDoc() != aKid->OwnerDoc()) {
     rv = AdoptNodeIntoOwnerDoc(this, aKid);
     NS_ENSURE_SUCCESS(rv, rv);
   } else if (OwnerDoc()->DidDocumentOpen()) {
     rv = CheckForOutdatedParent(this, aKid);
     NS_ENSURE_SUCCESS(rv, rv);
   }
@@ -1596,20 +1642,17 @@ void
 nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify,
                          nsIContent* aKid, nsAttrAndChildArray& aChildArray)
 {
   NS_PRECONDITION(aKid && aKid->GetParentNode() == this &&
                   aKid == GetChildAt(aIndex) &&
                   IndexOf(aKid) == (int32_t)aIndex, "Bogus aKid");
 
   nsMutationGuard::DidMutate();
-
-  nsIDocument* doc = GetCurrentDoc();
-
-  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
+  mozAutoDocUpdate updateBatch(GetCrossShadowCurrentDoc(), UPDATE_CONTENT_MODEL, aNotify);
 
   nsIContent* previousSibling = aKid->GetPreviousSibling();
 
   if (GetFirstChild() == aKid) {
     mFirstChild = aKid->GetNextSibling();
   }
 
   aChildArray.RemoveChildAt(aIndex);
@@ -2037,17 +2080,17 @@ nsINode::ReplaceOrInsertBefore(bool aRep
             aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
             return nullptr;
           }
         }
       }
     }
   }
 
-  mozAutoDocUpdate batch(GetCurrentDoc(), UPDATE_CONTENT_MODEL, true);
+  mozAutoDocUpdate batch(GetCrossShadowCurrentDoc(), UPDATE_CONTENT_MODEL, true);
   nsAutoMutationBatch mb;
 
   // Figure out which index we want to insert at.  Note that we use
   // nodeToInsertBefore to determine this, because it's possible that
   // aRefChild == aNewChild, in which case we just removed it from the
   // parent list.
   int32_t insPos;
   if (nodeToInsertBefore) {
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -8726,17 +8726,17 @@ nsCSSFrameConstructor::RecreateFramesFor
 {
   NS_PRECONDITION(!aAsyncInsert || aContent->IsElement(),
                   "Can only insert elements async");
   // If there is no document, we don't want to recreate frames for it.  (You
   // shouldn't generally be giving this method content without a document
   // anyway).
   // Rebuilding the frame tree can have bad effects, especially if it's the
   // frame tree for chrome (see bug 157322).
-  NS_ENSURE_TRUE(aContent->GetDocument(), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(aContent->GetCrossShadowCurrentDoc(), NS_ERROR_FAILURE);
 
   // Is the frame ib-split? If so, we need to reframe the containing
   // block *here*, rather than trying to remove and re-insert the
   // content (which would otherwise result in *two* nested reframe
   // containing block from ContentRemoved() and ContentInserted(),
   // below!).  We'd really like to optimize away one of those
   // containing block reframes, hence the code here.
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -4952,17 +4952,17 @@ PresShell::CreateRangePaintInfo(nsIDOMRa
   nsIFrame* ancestorFrame;
   nsIFrame* rootFrame = GetRootFrame();
 
   // If the start or end of the range is the document, just use the root
   // frame, otherwise get the common ancestor of the two endpoints of the
   // range.
   nsINode* startParent = range->GetStartParent();
   nsINode* endParent = range->GetEndParent();
-  nsIDocument* doc = startParent->GetCurrentDoc();
+  nsIDocument* doc = startParent->GetCrossShadowCurrentDoc();
   if (startParent == doc || endParent == doc) {
     ancestorFrame = rootFrame;
   }
   else {
     nsINode* ancestor = nsContentUtils::GetCommonAncestor(startParent, endParent);
     NS_ASSERTION(!ancestor || ancestor->IsNodeOfType(nsINode::eCONTENT),
                  "common ancestor is not content");
     if (!ancestor || !ancestor->IsNodeOfType(nsINode::eCONTENT))