Bug 1404789: Get the insertion point right when reconstructing direct children of a shadow root. r=bz
authorEmilio Cobos Álvarez <emilio@crisal.io>
Wed, 18 Oct 2017 19:41:17 +0200
changeset 683848 e214368792a2bad363b383e8efb47fd0133e7cd5
parent 683847 fff75157a8cd8440c588d1c71f9f0bbb9305c3e9
child 683849 736a76d2816f49c63fa596de38f0b30b893b5ad4
push id85474
push userbmo:emilio@crisal.io
push dateFri, 20 Oct 2017 10:02:12 +0000
reviewersbz
bugs1404789
milestone58.0a1
Bug 1404789: Get the insertion point right when reconstructing direct children of a shadow root. r=bz MozReview-Commit-ID: 2Xlke5ujt2Q
layout/base/nsCSSFrameConstructor.cpp
layout/reftests/webcomponents/reframe-shadow-child-1.html
layout/reftests/webcomponents/reframe-shadow-child-2.html
layout/reftests/webcomponents/reframe-shadow-child-ref.html
layout/reftests/webcomponents/reftest.list
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7780,18 +7780,20 @@ nsCSSFrameConstructor::ContentAppended(n
   }
 
   // Create some new frames
   //
   // We use the provided tree match context, or create a new one on the fly
   // otherwise.
   Maybe<TreeMatchContext> matchContext;
   if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
+    // We use GetParentElementCrossingShadowRoot to handle the case where
+    // aContainer is a ShadowRoot.
     matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
-    matchContext->InitAncestors(aContainer->AsElement());
+    matchContext->InitAncestors(aFirstNewContent->GetParentElementCrossingShadowRoot());
   }
   nsFrameConstructorState state(mPresShell,
                                 matchContext.ptrOr(aProvidedTreeMatchContext),
                                 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
                                 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
                                 containingBlock);
 
   LayoutFrameType frameType = parentFrame->Type();
@@ -8121,28 +8123,32 @@ nsCSSFrameConstructor::ContentRangeInser
 
     return;
   }
 
   nsContainerFrame* parentFrame = GetContentInsertionFrameFor(aContainer);
   // The xbl:children element won't have a frame, but default content can have the children as
   // a parent. While its uncommon to change the structure of the default content itself, a label,
   // for example, can be reframed by having its value attribute set or removed.
-  if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
+  if (!parentFrame &&
+      !(aContainer->IsActiveChildrenElement() ||
+        ShadowRoot::FromNode(aContainer))) {
     // We're punting on frame construction because there's no container frame.
     // The Servo-backed style system handles this case like the lazy frame
     // construction case, except when we're already constructing frames, in
     // which case we shouldn't need to do anything else.
     if (aContainer->IsStyledByServo() &&
         aInsertionKind == InsertionKind::Async) {
       LazilyStyleNewChildRange(aStartChild, aEndChild);
     }
     return;
   }
 
+  MOZ_ASSERT_IF(ShadowRoot::FromNode(aContainer), !parentFrame);
+
   // Otherwise, we've got parent content. Find its frame.
   NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
                GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!");
 
   if (aInsertionKind == InsertionKind::Async &&
       MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
     if (aContainer->IsStyledByServo()) {
       LazilyStyleNewChildRange(aStartChild, aEndChild);
@@ -8246,18 +8252,20 @@ nsCSSFrameConstructor::ContentRangeInser
     RecreateFramesForContent(insertion.mParentFrame->GetContent(),
                              aInsertionKind);
     LAYOUT_PHASE_TEMP_REENTER();
     return;
   }
 
   Maybe<TreeMatchContext> matchContext;
   if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
+    // We use GetParentElementCrossingShadowRoot to handle the case where
+    // aContainer is a ShadowRoot.
     matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
-    matchContext->InitAncestors(aContainer ? aContainer->AsElement() : nullptr);
+    matchContext->InitAncestors(aStartChild->GetParentElementCrossingShadowRoot());
   }
   nsFrameConstructorState state(mPresShell,
                                 matchContext.ptrOr(aProvidedTreeMatchContext),
                                 GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
                                 GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
                                 GetFloatContainingBlock(insertion.mParentFrame),
                                 do_AddRef(aFrameState));
 
@@ -9531,18 +9539,21 @@ nsCSSFrameConstructor::GetInsertionPoint
     // We've got an explicit insertion child. Check to see if it's
     // anonymous.
     if (aChild->GetBindingParent() == aContainer) {
       // This child content is anonymous. Don't use the insertion
       // point, since that's only for the explicit kids.
       return InsertionPoint(GetContentInsertionFrameFor(aContainer), aContainer);
     }
 
-    if (nsContentUtils::HasDistributedChildren(aContainer)) {
-      // The container distributes nodes, use the frame of the flattened tree parent.
+    if (nsContentUtils::HasDistributedChildren(aContainer) ||
+        ShadowRoot::FromNode(aContainer)) {
+      // The container distributes nodes or is a shadow root, use the frame of
+      // the flattened tree parent.
+      //
       // It may be the case that the node is distributed but not matched to any
       // insertion points, so there is no flattened parent.
       nsIContent* flattenedParent = aChild->GetFlattenedTreeParent();
       if (flattenedParent) {
         return InsertionPoint(GetContentInsertionFrameFor(flattenedParent),
                               flattenedParent);
       }
       return InsertionPoint();
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reframe-shadow-child-1.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<template id="tmpl">
+  <div style="display: table">
+    Some text
+    <span style="display: table-cell">something</span>
+    More text
+  </div>
+</template>
+<div id="host"></div>
+<script>
+  let shadowRoot = document.getElementById("host").createShadowRoot();
+  let tmpl = document.getElementById("tmpl");
+  shadowRoot.appendChild(document.importNode(tmpl.content, true));
+  document.body.offsetTop;
+  shadowRoot.firstElementChild.querySelector("span").remove();
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reframe-shadow-child-2.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<template id="tmpl">
+  <div style="display: block">
+    Some text
+    More text
+  </div>
+</template>
+<div id="host"></div>
+<script>
+  let shadowRoot = document.getElementById("host").createShadowRoot();
+  let tmpl = document.getElementById("tmpl");
+  shadowRoot.appendChild(document.importNode(tmpl.content, true));
+  document.body.offsetTop;
+  shadowRoot.firstElementChild.style.display = "table";
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reframe-shadow-child-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<div style="display: table">
+  Some text
+  More text
+</div>
--- a/layout/reftests/webcomponents/reftest.list
+++ b/layout/reftests/webcomponents/reftest.list
@@ -8,8 +8,10 @@ pref(dom.webcomponents.enabled,true) == 
 pref(dom.webcomponents.enabled,true) == fallback-content-1.html fallback-content-1-ref.html
 pref(dom.webcomponents.enabled,true) == remove-insertion-point-1.html remove-insertion-point-1-ref.html
 pref(dom.webcomponents.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
 pref(dom.webcomponents.enabled,true) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
 pref(dom.webcomponents.enabled,true) fuzzy-if(Android,2,7) == input-transition-1.html input-transition-1-ref.html
 pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
 pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html
 pref(dom.webcomponents.enabled,true) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
+pref(dom.webcomponents.enabled,true) == reframe-shadow-child-1.html reframe-shadow-child-ref.html
+pref(dom.webcomponents.enabled,true) == reframe-shadow-child-2.html reframe-shadow-child-ref.html