Bug 1087460 - Part 1: Make GetComposedDoc() work in UnbindFromTree. r=smaug
authorWilliam Chen <wchen@mozilla.com>
Mon, 09 Feb 2015 10:01:23 -0800
changeset 228576 09b002eaa381c992fa255074d482286a7a935681
parent 228575 a4cf56d0e98ff7059ba78e8fdb34f8494cd01186
child 228577 7a7e30dbc96cf5e3384f72a69e93e5a705ae4440
push id28264
push usercbook@mozilla.com
push dateWed, 11 Feb 2015 13:58:35 +0000
treeherdermozilla-central@38058cb42a0e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1087460
milestone38.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 1087460 - Part 1: Make GetComposedDoc() work in UnbindFromTree. r=smaug
dom/base/Element.cpp
dom/base/ShadowRoot.cpp
dom/base/ShadowRoot.h
dom/base/nsGenericDOMDataNode.cpp
dom/base/nsINode.cpp
dom/base/nsINode.h
dom/html/HTMLShadowElement.cpp
dom/html/HTMLStyleElement.cpp
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -987,31 +987,35 @@ Element::CreateShadowRoot(ErrorResult& a
   protoBinding->SetInheritsStyle(false);
 
   // Calling SetPrototypeBinding takes ownership of protoBinding.
   docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding);
 
   nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(),
                                                    protoBinding);
 
+  shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
+
   // Replace the old ShadowRoot with the new one and let the old
   // ShadowRoot know about the younger ShadowRoot because the old
   // ShadowRoot is projected into the younger ShadowRoot's shadow
   // insertion point (if it exists).
   ShadowRoot* olderShadow = GetShadowRoot();
   SetShadowRoot(shadowRoot);
   if (olderShadow) {
     olderShadow->SetYoungerShadow(shadowRoot);
 
     // Unbind children of older shadow root because they
     // are no longer in the composed tree.
     for (nsIContent* child = olderShadow->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       child->UnbindFromTree(true, false);
     }
+
+    olderShadow->SetIsComposedDocParticipant(false);
   }
 
   // xblBinding takes ownership of docInfo.
   nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
   shadowRoot->SetAssociatedBinding(xblBinding);
   xblBinding->SetBoundElement(this);
 
   SetXBLBinding(xblBinding);
@@ -1573,16 +1577,17 @@ Element::BindToTree(nsIDocument* aDocume
     if (sheet) {
       mAttrsAndChildren.SetMappedAttrStyleSheet(sheet);
     }
   }
 
   // Call BindToTree on shadow root children.
   ShadowRoot* shadowRoot = GetShadowRoot();
   if (shadowRoot) {
+    shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
     for (nsIContent* child = shadowRoot->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       rv = child->BindToTree(nullptr, shadowRoot,
                              shadowRoot->GetBindingParent(),
                              aCompileEventHandlers);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
@@ -1630,18 +1635,17 @@ Element::UnbindFromTree(bool aDeep, bool
   NS_PRECONDITION(aDeep || (!GetUncomposedDoc() && !GetBindingParent()),
                   "Shallow unbind won't clear document and binding parent on "
                   "kids!");
 
   RemoveFromIdTable();
 
   // Make sure to unbind this node before doing the kids
   nsIDocument* document =
-    HasFlag(NODE_FORCE_XBL_BINDINGS) || IsInShadowTree() ?
-      OwnerDoc() : GetUncomposedDoc();
+    HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc();
 
   if (aNullParent) {
     if (IsFullScreenAncestor()) {
       // The element being removed is an ancestor of the full-screen element,
       // exit full-screen state.
       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                       NS_LITERAL_CSTRING("DOM"), OwnerDoc(),
                                       nsContentUtils::eDOM_PROPERTIES,
@@ -1751,16 +1755,18 @@ Element::UnbindFromTree(bool aDeep, bool
 
   // Unbind children of shadow root.
   ShadowRoot* shadowRoot = GetShadowRoot();
   if (shadowRoot) {
     for (nsIContent* child = shadowRoot->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       child->UnbindFromTree(true, false);
     }
+
+    shadowRoot->SetIsComposedDocParticipant(false);
   }
 }
 
 nsICSSDeclaration*
 Element::GetSMILOverrideStyle()
 {
   Element::nsDOMSlots *slots = DOMSlots();
 
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -62,17 +62,17 @@ NS_INTERFACE_MAP_END_INHERITING(Document
 NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
 NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
 
 ShadowRoot::ShadowRoot(nsIContent* aContent,
                        already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                        nsXBLPrototypeBinding* aProtoBinding)
   : DocumentFragment(aNodeInfo), mPoolHost(aContent),
     mProtoBinding(aProtoBinding), mShadowElement(nullptr),
-    mInsertionPointChanged(false)
+    mInsertionPointChanged(false), mIsComposedDocParticipant(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();
 
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -122,16 +122,22 @@ public:
                            const nsAString& aLocalName);
   already_AddRefed<nsContentList>
     GetElementsByClassName(const nsAString& aClasses);
   void GetInnerHTML(nsAString& aInnerHTML);
   void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
   Element* Host();
   ShadowRoot* GetOlderShadowRoot() { return mOlderShadow; }
   void StyleSheetChanged();
+
+  bool IsComposedDocParticipant() { return mIsComposedDocParticipant; }
+  void SetIsComposedDocParticipant(bool aIsComposedDocParticipant)
+  {
+    mIsComposedDocParticipant = aIsComposedDocParticipant;
+  }
 protected:
   virtual ~ShadowRoot();
 
   // The pool host is the parent of the nodes that will be distributed
   // into the insertion points in this ShadowRoot. See |ChangeShadowRoot|.
   nsCOMPtr<nsIContent> mPoolHost;
 
   // An array of content insertion points that are a descendant of the ShadowRoot
@@ -166,16 +172,22 @@ protected:
   // this ShadowRoot was created.
   nsRefPtr<ShadowRoot> mYoungerShadow;
 
   // A boolean that indicates that an insertion point was added or removed
   // from this ShadowRoot and that the nodes need to be redistributed into
   // the insertion points. After this flag is set, nodes will be distributed
   // on the next mutation event.
   bool mInsertionPointChanged;
+
+  // Flag to indicate whether the descendants of this shadow root are part of the
+  // composed document. Ideally, we would use a node flag on nodes to
+  // mark whether it is in the composed document, but we have run out of flags
+  // so instead we track it here.
+  bool mIsComposedDocParticipant;
 };
 
 class ShadowRootStyleSheetList : public StyleSheetList
 {
 public:
   explicit ShadowRootStyleSheetList(ShadowRoot* aShadowRoot);
 
   NS_DECL_ISUPPORTS_INHERITED
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -108,16 +108,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   if (slots) {
     slots->Traverse(cb);
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode)
   nsINode::Unlink(tmp);
 
+  // Clear flag here because unlinking slots will clear the
+  // containing shadow root pointer.
+  tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE);
+
   nsDataSlots *slots = tmp->GetExistingDataSlots();
   if (slots) {
     slots->Unlink();
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -557,18 +561,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
 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);
 
   nsIDocument* document =
-    HasFlag(NODE_FORCE_XBL_BINDINGS) || IsInShadowTree() ?
-      OwnerDoc() : GetUncomposedDoc();
+    HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc();
 
   if (aNullParent) {
     if (GetParent()) {
       NS_RELEASE(mParent);
     } else {
       mParent = nullptr;
     }
     SetParentIsContent(false);
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -385,27 +385,18 @@ nsINode::GetTextContentInternal(nsAStrin
 }
 
 nsIDocument*
 nsINode::GetComposedDocInternal() 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();
-
-  nsIContent* poolHost = containingShadow->GetPoolHost();
-  if (!poolHost) {
-    // This node is in an older shadow root that does not get projected into
-    // an insertion point, thus this node can not be in the composed document.
-    return nullptr;
-  }
-
-  return poolHost->GetComposedDoc();
+  return containingShadow->IsComposedDocParticipant() ?  OwnerDoc() : nullptr;
 }
 
 #ifdef DEBUG
 void
 nsINode::CheckNotNativeAnonymous() const
 {
   if (!IsNodeOfType(eCONTENT))
     return;
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -515,21 +515,19 @@ public:
    * @deprecated
    */
   nsIDocument *GetCurrentDoc() const
   {
     return GetUncomposedDoc();
   }
 
   /**
-   * 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.
+   * This method returns the owner doc if the node is in the
+   * composed document (as defined in the Shadow DOM spec), otherwise
+   * it returns null.
    */
   nsIDocument* GetComposedDoc() const
   {
     return IsInShadowTree() ?
       GetComposedDocInternal() : GetUncomposedDoc();
   }
 
   /**
--- a/dom/html/HTMLShadowElement.cpp
+++ b/dom/html/HTMLShadowElement.cpp
@@ -141,16 +141,18 @@ HTMLShadowElement::BindToTree(nsIDocumen
 
     containingShadow->SetInsertionPointChanged();
   }
 
   if (mIsInsertionPoint && containingShadow) {
     // Propagate BindToTree calls to projected shadow root children.
     ShadowRoot* projectedShadow = containingShadow->GetOlderShadowRoot();
     if (projectedShadow) {
+      projectedShadow->SetIsComposedDocParticipant(IsInComposedDoc());
+
       for (nsIContent* child = projectedShadow->GetFirstChild(); child;
            child = child->GetNextSibling()) {
         rv = child->BindToTree(nullptr, projectedShadow,
                                projectedShadow->GetBindingParent(),
                                aCompileEventHandlers);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
@@ -168,16 +170,18 @@ HTMLShadowElement::UnbindFromTree(bool a
     // Propagate UnbindFromTree call to previous projected shadow
     // root children.
     ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadowRoot();
     if (projectedShadow) {
       for (nsIContent* child = projectedShadow->GetFirstChild(); child;
            child = child->GetNextSibling()) {
         child->UnbindFromTree(true, false);
       }
+
+      projectedShadow->SetIsComposedDocParticipant(false);
     }
   }
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
   if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) {
     nsTArray<HTMLShadowElement*>& shadowDescendants =
       oldContainingShadow->ShadowDescendants();
--- a/dom/html/HTMLStyleElement.cpp
+++ b/dom/html/HTMLStyleElement.cpp
@@ -152,25 +152,24 @@ HTMLStyleElement::BindToTree(nsIDocument
 
   return rv;  
 }
 
 void
 HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc();
-  nsCOMPtr<nsIDocument> oldComposedDoc = GetComposedDoc();
   ShadowRoot* oldShadow = GetContainingShadow();
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
-  if (GetContainingShadow() && !oldComposedDoc) {
-    // The style is in a shadow tree and was already not
-    // in the composed document. Thus the sheet does not
-    // need to be updated.
+  if (oldShadow && GetContainingShadow()) {
+    // The style is in a shadow tree and is still in the
+    // shadow tree. Thus the sheets in the shadow DOM
+    // do not need to be updated.
     return;
   }
 
   UpdateStyleSheetInternal(oldDoc, oldShadow);
 }
 
 nsresult
 HTMLStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,