Title: [106465] trunk
Revision
106465
Author
[email protected]
Date
2012-02-01 05:50:25 -0800 (Wed, 01 Feb 2012)

Log Message

Content element should be able to be dynamically added/removed/replaced in a shadow tree.
https://bugs.webkit.org/show_bug.cgi?id=76611

Patch by Shinya Kawanaka <[email protected]> on 2012-02-01
Reviewed by Hajime Morita

Source/WebCore:

When a content element is added/removed/replaced in a shadow tree, we have to recreate
the shadow tree to recalculate inclusions of content elements. Currently we didn't recalculate it
when content element is removed. (When added, it is recalculated.)
This patch enables us to recalcurate the shadow tree when content element is removed.

Test: fast/dom/shadow/content-element-move.html

* dom/Element.cpp:
(WebCore::Element::attach):
  If a shadow root exists, attaches shadow tree before attaching child elements.
* dom/ShadowRoot.cpp:
  Added a flag to recalculate shadow tree.
(WebCore::ShadowRoot::ShadowRoot):
(WebCore::ShadowRoot::recalcShadowTreeStyle):
  Recalculates light children and shadow tree.
(WebCore::ShadowRoot::setNeedsReattachHostChildrenAndShadow):
(WebCore::ShadowRoot::reattachHostChildrenAndShadow):
  Detaches shadow tree and host light children, and attaches them again.
* dom/ShadowRoot.h:
(WebCore::ShadowRoot::clearNeedsReattachHostChildrenAndShadow):
(WebCore::ShadowRoot::needsReattachHostChildrenAndShadow):
* html/shadow/HTMLContentElement.cpp:
(WebCore::HTMLContentElement::attach):
  Does not need to detach included elements, because they are not attached in ContainerNode anymore.
(WebCore::HTMLContentElement::detach):
  When a content element detached, reattaches a shadow tree.

LayoutTests:

Test cases for appending/removing/replacing content element in a shadow tree.

* fast/dom/shadow/content-element-move-expected.txt: Added.
* fast/dom/shadow/content-element-move.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (106464 => 106465)


--- trunk/LayoutTests/ChangeLog	2012-02-01 13:31:11 UTC (rev 106464)
+++ trunk/LayoutTests/ChangeLog	2012-02-01 13:50:25 UTC (rev 106465)
@@ -1,3 +1,15 @@
+2012-02-01  Shinya Kawanaka  <[email protected]>
+
+        Content element should be able to be dynamically added/removed/replaced in a shadow tree.
+        https://bugs.webkit.org/show_bug.cgi?id=76611
+
+        Reviewed by Hajime Morita
+
+        Test cases for appending/removing/replacing content element in a shadow tree.
+
+        * fast/dom/shadow/content-element-move-expected.txt: Added.
+        * fast/dom/shadow/content-element-move.html: Added.
+
 2012-02-01  Peter Beverloo  <[email protected]>
 
         getIntersectionList causes transforms to be recalculated in SVG

Added: trunk/LayoutTests/fast/dom/shadow/content-element-move-expected.txt (0 => 106465)


--- trunk/LayoutTests/fast/dom/shadow/content-element-move-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/shadow/content-element-move-expected.txt	2012-02-01 13:50:25 UTC (rev 106465)
@@ -0,0 +1,24 @@
+testRemoveContent
+PASS
+testRemoveContentToRecalc1
+PASS
+testRemoveContentToRecalc2
+PASS
+testRemoveContentAndRemoveLightChildren
+PASS
+testRemoveContentAndRemoveShadowRoot1
+PASS
+testRemoveContentAndRemoveShadowRoot2
+PASS
+testAppendContent1
+PASS
+testAppendContent2
+PASS
+testAppendContent3
+PASS
+testChangeOrderOfContent
+PASS
+testMoveLightChildOut
+PASS
+TEST COMPLETED
+

Added: trunk/LayoutTests/fast/dom/shadow/content-element-move.html (0 => 106465)


--- trunk/LayoutTests/fast/dom/shadow/content-element-move.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/shadow/content-element-move.html	2012-02-01 13:50:25 UTC (rev 106465)
@@ -0,0 +1,504 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+/* relative positioning ensures underlying RenderLayer */
+.container {
+    position: relative;
+}
+
+.span {
+    display: boxed-inline;
+    margin: 2px;
+    border: solid;
+}
+</style>
+<script>
+function log(message) {
+    document.getElementById('console').innerHTML += (message + "\n");
+}
+
+function removeAllChildren(elem) {
+    while (elem.firstChild)
+        elem.removeChild(elem.firstChild);
+}
+
+function cleanUp() {
+    removeAllChildren(document.getElementById('actual-container'));
+    removeAllChildren(document.getElementById('expect-container'));
+}
+
+function removeContainerLines(text) {
+    var lines = text.split('\n');
+    lines.splice(0, 2);
+    return lines.join('\n');
+}
+
+function check() {
+    var refContainerRenderTree = internals.elementRenderTreeAsText(document.getElementById('expect-container'));
+    var refRenderTree = removeContainerLines(refContainerRenderTree);
+
+    var targetContainerRenderTree = internals.elementRenderTreeAsText(document.getElementById('actual-container'));
+    var targetRenderTree = removeContainerLines(targetContainerRenderTree);
+
+    if (targetRenderTree == refRenderTree)
+        log("PASS");
+    else {
+        log("FAIL");
+        log("Expected: ");
+        log(refRenderTree);
+        log("Actual: ");
+        log(targetRenderTree);
+    }
+}
+
+function createSpanWithText(text) {
+    var span = document.createElement('span');
+    span.appendChild(document.createTextNode(text));
+    return span;
+}
+
+function createContentWithSelect(select, fallback) {
+    var content = internals.createContentElement(document);
+    content.setAttribute('select', select);
+    if (!fallback)
+        content.appendChild(createSpanWithText("FALLBACK"));
+
+    return content;
+}
+
+function appendShadow(target, select) {
+    var root = internals.ensureShadowRoot(target);
+
+    var content = internals.createContentElement(document);
+    content.setAttribute('select', select);
+    content.appendChild(createSpanWithText("FALLBACK"));
+
+    root.appendChild(document.createTextNode("{SHADOW: "));
+    root.appendChild(content);
+    root.appendChild(document.createTextNode("}"));
+}
+
+function appendShadowDeep(target, select) {
+    var root = internals.ensureShadowRoot(target);
+
+    var child = document.createElement("span");
+    {
+        var content = internals.createContentElement(document);
+        content.setAttribute('select', select);
+        content.appendChild(createSpanWithText("FALLBACK"));
+
+        child.appendChild(document.createTextNode("{INNER: "));
+        child.appendChild(content);
+        child.appendChild(document.createTextNode("}"));
+    }
+
+    root.appendChild(document.createTextNode("{SHADOW: "));
+    root.appendChild(child);
+    root.appendChild(document.createTextNode("}"));
+}
+
+// ----------------------------------------------------------------------
+// Test Functions.
+
+function testRemoveContent(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span>LIGHT</span>";
+
+    // Create shadow tree.
+    var content = createContentWithSelect('span');
+    var sr = internals.ensureShadowRoot(root);
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(content);
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, content, callIfDone) {
+        return function() {
+            content.parentNode.removeChild(content);
+            document.getElementById('expect-container').innerHTML = "<div><span>BEFORE</span><span>AFTER</span></div>";
+            callIfDone();
+        };
+    })(root, content, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testRemoveContentToRecalc1(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span class='c1'>LIGHT 1</span><span>LIGHT 2</span>";
+
+    // Create shadow tree.
+    var content1 = createContentWithSelect('.c1');
+    var content2 = createContentWithSelect('span');
+    var sr = internals.ensureShadowRoot(root);
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(content1);
+    sr.appendChild(createSpanWithText("MID"));
+    sr.appendChild(content2);
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, content1, callIfDone) {
+        return function() {
+            content1.parentNode.removeChild(content1);
+            document.getElementById('expect-container').innerHTML = "<div><span>BEFORE</span><span>MID</span><span>LIGHT 1</span><span>LIGHT 2</span><span>AFTER</span></div>";
+            callIfDone();
+        };
+    })(root, content1, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testRemoveContentToRecalc2(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span>LIGHT 1</span><span class='c1'>LIGHT 2</span>";
+
+    // Create shadow tree.
+    var content1 = createContentWithSelect('.c1');
+    var content2 = createContentWithSelect('span');
+    var sr = internals.ensureShadowRoot(root);
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(content1);
+    sr.appendChild(createSpanWithText("MID"));
+    sr.appendChild(content2);
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, content1, callIfDone) {
+        return function() {
+            content1.parentNode.removeChild(content1);
+            document.getElementById('expect-container').innerHTML = "<div><span>BEFORE</span><span>MID</span><span>LIGHT 1</span><span>LIGHT 2</span><span>AFTER</span></div>";
+            callIfDone();
+        };
+    })(root, content1, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testRemoveContentAndRemoveLightChildren(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span>LIGHT</span>";
+
+    // Create shadow tree.
+    var content = createContentWithSelect('span');
+    var sr = internals.ensureShadowRoot(root);
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(content);
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, content, callIfDone) {
+        return function() {
+            content.parentNode.removeChild(content);
+            removeAllChildren(root);
+            document.getElementById('expect-container').innerHTML = "<div><span>BEFORE</span><span>AFTER</span></div>";
+            callIfDone();
+        };
+    })(root, content, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testRemoveShadowRoot(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span>LIGHT</span>";
+
+    // Create shadow tree.
+    var content = createContentWithSelect('span');
+    var sr = internals.ensureShadowRoot(root);
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(content);
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, content, callIfDone) {
+        return function() {
+            internals.removeShadowRoot(root);
+            document.getElementById('expect-container').innerHTML = "<div><span>LIGHT</span></div>";
+            callIfDone();
+        };
+    })(root, content, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testRemoveContentAndRemoveShadowRoot1(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span>LIGHT</span>";
+
+    // Create shadow tree.
+    var content = createContentWithSelect('span');
+    var sr = internals.ensureShadowRoot(root);
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(content);
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, content, callIfDone) {
+        return function() {
+            content.parentNode.removeChild(content);
+            internals.removeShadowRoot(root);
+            document.getElementById('expect-container').innerHTML = "<div><span>LIGHT</span></div>";
+            callIfDone();
+        };
+    })(root, content, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testRemoveContentAndRemoveShadowRoot2(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span>LIGHT 1</span><span class='c1'>LIGHT 2</span>";
+
+    // Create shadow tree.
+    var content = createContentWithSelect('.c1');
+    var sr = internals.ensureShadowRoot(root);
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(content);
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, content, callIfDone) {
+        return function() {
+            content.parentNode.removeChild(content);
+            internals.removeShadowRoot(root);
+            document.getElementById('expect-container').innerHTML = "<div><span>LIGHT 1</span><span>LIGHT 2</span></div>";
+            callIfDone();
+        };
+    })(root, content, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testAppendContent1(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span>LIGHT</span>";
+
+    // Create shadow tree.
+    var sr = internals.ensureShadowRoot(root);
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, callIfDone) {
+        return function() {
+            var content = createContentWithSelect('span');
+            var sr = internals.ensureShadowRoot(root);
+            sr.insertBefore(content, sr.lastChild);
+            document.getElementById('expect-container').innerHTML = "<div><span>BEFORE</span><SPAN>LIGHT</span><span>AFTER</span></div>";
+            callIfDone();
+        };
+    })(root, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testAppendContent2(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span>LIGHT 1</span><span class='c2'>LIGHT 2</span>";
+
+    // Create shadow tree.
+    var sr = internals.ensureShadowRoot(root);
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, callIfDone) {
+        return function() {
+            var content1 = createContentWithSelect('.c2');
+            var content2 = createContentWithSelect('span');
+            var sr = internals.ensureShadowRoot(root);
+            sr.insertBefore(content1, sr.lastChild);
+            sr.insertBefore(content2, sr.lastChild);
+            document.getElementById('expect-container').innerHTML = "<div><span>BEFORE</span><span>LIGHT 2</span><span>LIGHT 1</span><span>AFTER</span></div>";
+            callIfDone();
+        };
+    })(root, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testAppendContent3(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span>LIGHT 1</span><span class='c2'>LIGHT 2</span>";
+
+    // Create shadow tree.
+    var sr = internals.ensureShadowRoot(root);
+    var mid = createSpanWithText("MID");
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(mid);
+    sr.appendChild(createContentWithSelect('.c2'));
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, mid, callIfDone) {
+        return function() {
+            var content = createContentWithSelect('span');
+            var sr = internals.ensureShadowRoot(root);
+            sr.insertBefore(content, mid);
+            document.getElementById('expect-container').innerHTML =
+                "<div><span>BEFORE</span><span>LIGHT 1</span><span>LIGHT 2</span><span>MID</span><span>FALLBACK</span><span>AFTER</span></div>";
+            callIfDone();
+        };
+    })(root, mid, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testChangeOrderOfContent(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    root.innerHTML = "<span class='c1'>LIGHT 1</span><span>LIGHT 2</span>";
+
+    // Create shadow tree.
+    var sr = internals.ensureShadowRoot(root);
+    var content1 = createContentWithSelect('.c1');
+    var content2 = createContentWithSelect('span');
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(content1);
+    sr.appendChild(content2);
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, content1, callIfDone) {
+        return function() {
+            var sr = internals.ensureShadowRoot(root);
+            sr.removeChild(content1);
+            sr.insertBefore(content1, sr.lastChild);
+            document.getElementById('expect-container').innerHTML =
+                "<div><span>BEFORE</span><SPAN>LIGHT 1</span><span>LIGHT 2</span><span>FALLBACK</span><span>AFTER</span></div>";
+            callIfDone();
+        };
+    })(root, content1, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+function testMoveLightChildOut(callIfDone) {
+    var root = document.createElement('div');
+
+    // Create light tree.
+    var n1 = document.createElement('div');
+    n1.setAttribute('id', 'n1');
+    n1.innerHTML = "<span id='light1'>LIGHT 1</span><span>LIGHT 2</span>";
+    var n2 = document.createElement('div');
+    n2.setAttribute('id', 'n2');
+
+    root.appendChild(n1);
+    root.appendChild(n2);
+
+    // Create shadow tree in 'n1'
+    var sr = internals.ensureShadowRoot(n1);
+    var content = createContentWithSelect('span');
+    sr.appendChild(createSpanWithText("BEFORE"));
+    sr.appendChild(content);
+    sr.appendChild(createSpanWithText("AFTER"));
+
+    document.getElementById('actual-container').appendChild(root);
+
+    var f = (function(root, n2, callIfDone) {
+        return function() {
+            var e = document.getElementById('light1');
+            e.parentNode.removeChild(e);
+            n2.appendChild(e);
+
+            document.getElementById('expect-container').innerHTML =
+                "<div><div><span>BEFORE</span><span>LIGHT 2</span><span>AFTER</span></div><div><span>LIGHT 1</span></div></div>"
+            callIfDone();
+        };
+    })(root, n2, callIfDone);
+
+    setTimeout(f, 0);
+}
+
+// ----------------------------------------------------------------------
+// Test Drivers.
+
+var testFuncs = [
+    testRemoveContent,
+    testRemoveContentToRecalc1,
+    testRemoveContentToRecalc2,
+    testRemoveContentAndRemoveLightChildren,
+    testRemoveContentAndRemoveShadowRoot1,
+    testRemoveContentAndRemoveShadowRoot2,
+    testAppendContent1,
+    testAppendContent2,
+    testAppendContent3,
+    testChangeOrderOfContent,
+    testMoveLightChildOut
+];
+
+function doTestIfLeft() {
+    var test = testFuncs.shift();
+    if (test == null)
+        return doneTest();
+
+    var callIfDone = function() {
+        setTimeout(function() {
+            check();
+            cleanUp();
+            doTestIfLeft();
+        }, 0);
+    };
+
+    log(test.name);
+    test(callIfDone);
+}
+
+function doneTest() {
+    log("TEST COMPLETED");
+    layoutTestController.notifyDone();
+}
+
+function doTest() {
+    if (window.layoutTestController) {
+        layoutTestController.waitUntilDone();
+        layoutTestController.dumpAsText();
+    }
+
+    cleanUp();
+    doTestIfLeft();
+}
+</script>
+</head>
+<body _onload_="doTest()">
+
+<div id="actual-container" class="container"></div>
+<div id="expect-container" class="container"></div>
+<pre id="console"></pre>
+
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (106464 => 106465)


--- trunk/Source/WebCore/ChangeLog	2012-02-01 13:31:11 UTC (rev 106464)
+++ trunk/Source/WebCore/ChangeLog	2012-02-01 13:50:25 UTC (rev 106465)
@@ -1,3 +1,37 @@
+2012-02-01  Shinya Kawanaka  <[email protected]>
+
+        Content element should be able to be dynamically added/removed/replaced in a shadow tree.
+        https://bugs.webkit.org/show_bug.cgi?id=76611
+
+        Reviewed by Hajime Morita
+
+        When a content element is added/removed/replaced in a shadow tree, we have to recreate
+        the shadow tree to recalculate inclusions of content elements. Currently we didn't recalculate it
+        when content element is removed. (When added, it is recalculated.)
+        This patch enables us to recalcurate the shadow tree when content element is removed.
+
+        Test: fast/dom/shadow/content-element-move.html
+
+        * dom/Element.cpp:
+        (WebCore::Element::attach):
+          If a shadow root exists, attaches shadow tree before attaching child elements.
+        * dom/ShadowRoot.cpp:
+          Added a flag to recalculate shadow tree.
+        (WebCore::ShadowRoot::ShadowRoot):
+        (WebCore::ShadowRoot::recalcShadowTreeStyle):
+          Recalculates light children and shadow tree.
+        (WebCore::ShadowRoot::setNeedsReattachHostChildrenAndShadow):
+        (WebCore::ShadowRoot::reattachHostChildrenAndShadow):
+          Detaches shadow tree and host light children, and attaches them again.
+        * dom/ShadowRoot.h:
+        (WebCore::ShadowRoot::clearNeedsReattachHostChildrenAndShadow):
+        (WebCore::ShadowRoot::needsReattachHostChildrenAndShadow):
+        * html/shadow/HTMLContentElement.cpp:
+        (WebCore::HTMLContentElement::attach):
+          Does not need to detach included elements, because they are not attached in ContainerNode anymore.
+        (WebCore::HTMLContentElement::detach):
+          When a content element detached, reattaches a shadow tree.
+
 2012-02-01  Peter Beverloo  <[email protected]>
 
         getIntersectionList causes transforms to be recalculated in SVG

Modified: trunk/Source/WebCore/dom/Element.cpp (106464 => 106465)


--- trunk/Source/WebCore/dom/Element.cpp	2012-02-01 13:31:11 UTC (rev 106464)
+++ trunk/Source/WebCore/dom/Element.cpp	2012-02-01 13:50:25 UTC (rev 106465)
@@ -940,16 +940,25 @@
     RenderWidget::suspendWidgetHierarchyUpdates();
 
     createRendererIfNeeded();
-    
     StyleSelectorParentPusher parentPusher(this);
 
-    if (firstChild())
-        parentPusher.push();
-    ContainerNode::attach();
-
+    // When a shadow root exists, it does the work of attaching the children.
     if (ShadowRoot* shadow = shadowRoot()) {
         parentPusher.push();
+        Node::attach();
         shadow->attach();
+
+        // In a shadow tree, some of light children may be attached by 'content' element.
+        // However, when there is no content element or content element does not select
+        // all light children, we have to attach the rest of light children here.
+        for (Node* child = firstChild(); child; child = child->nextSibling()) {
+            if (!child->attached())
+                child->attach();
+        }
+    } else {
+        if (firstChild())
+            parentPusher.push();
+        ContainerNode::attach();
     }
 
     if (hasRareData()) {   

Modified: trunk/Source/WebCore/dom/ShadowRoot.cpp (106464 => 106465)


--- trunk/Source/WebCore/dom/ShadowRoot.cpp	2012-02-01 13:31:11 UTC (rev 106464)
+++ trunk/Source/WebCore/dom/ShadowRoot.cpp	2012-02-01 13:50:25 UTC (rev 106465)
@@ -39,6 +39,7 @@
 ShadowRoot::ShadowRoot(Document* document)
     : TreeScope(document, CreateShadowRoot)
     , m_applyAuthorSheets(false)
+    , m_needsRecalculateContent(false)
 {
     ASSERT(document);
     
@@ -99,8 +100,8 @@
 
 void ShadowRoot::recalcShadowTreeStyle(StyleChange change)
 {
-    if (hasContentElement())
-        reattach();
+    if (needsReattachHostChildrenAndShadow())
+        reattachHostChildrenAndShadow();
     else {
         for (Node* n = firstChild(); n; n = n->nextSibling()) {
             if (n->isElementNode())
@@ -110,10 +111,18 @@
         }
     }
 
+    clearNeedsReattachHostChildrenAndShadow();
     clearNeedsStyleRecalc();
     clearChildNeedsStyleRecalc();
 }
 
+void ShadowRoot::setNeedsReattachHostChildrenAndShadow()
+{
+    m_needsRecalculateContent = true;
+    if (shadowHost())
+        shadowHost()->setNeedsStyleRecalc();
+}
+
 HTMLContentElement* ShadowRoot::includerFor(Node* node) const
 {
     if (!m_inclusions)
@@ -168,6 +177,25 @@
         m_inclusions->didSelect();
 }
 
+void ShadowRoot::reattachHostChildrenAndShadow()
+{
+    Node* hostNode = host();
+    if (!hostNode)
+        return;
+
+    for (Node* child = hostNode->firstChild(); child; child = child->nextSibling()) {
+        if (child->attached())
+            child->detach();
+    }
+
+    reattach();
+
+    for (Node* child = hostNode->firstChild(); child; child = child->nextSibling()) {
+        if (!child->attached())
+            child->attach();
+    }
+}
+
 ContentInclusionSelector* ShadowRoot::inclusions() const
 {
     return m_inclusions.get();

Modified: trunk/Source/WebCore/dom/ShadowRoot.h (106464 => 106465)


--- trunk/Source/WebCore/dom/ShadowRoot.h	2012-02-01 13:31:11 UTC (rev 106464)
+++ trunk/Source/WebCore/dom/ShadowRoot.h	2012-02-01 13:50:25 UTC (rev 106465)
@@ -43,11 +43,16 @@
 
     void recalcShadowTreeStyle(StyleChange);
 
+    void setNeedsReattachHostChildrenAndShadow();
+    void clearNeedsReattachHostChildrenAndShadow();
+    bool needsReattachHostChildrenAndShadow();
+
     HTMLContentElement* includerFor(Node*) const;
     void hostChildrenChanged();
     bool isInclusionSelectorActive() const;
 
     virtual void attach();
+    void reattachHostChildrenAndShadow();
 
     virtual bool applyAuthorSheets() const;
     void setApplyAuthorSheets(bool);
@@ -68,7 +73,8 @@
 
     bool hasContentElement() const;
 
-    bool m_applyAuthorSheets;
+    bool m_applyAuthorSheets : 1;
+    bool m_needsRecalculateContent : 1;
     OwnPtr<ContentInclusionSelector> m_inclusions;
 };
 
@@ -77,6 +83,16 @@
     return adoptRef(new ShadowRoot(document));
 }
 
+inline void ShadowRoot::clearNeedsReattachHostChildrenAndShadow()
+{
+    m_needsRecalculateContent = false;
+}
+
+inline bool ShadowRoot::needsReattachHostChildrenAndShadow()
+{
+    return m_needsRecalculateContent || hasContentElement();
+}
+
 inline ShadowRoot* toShadowRoot(Node* node)
 {
     ASSERT(!node || node->nodeType() == Node::SHADOW_ROOT_NODE);

Modified: trunk/Source/WebCore/html/shadow/HTMLContentElement.cpp (106464 => 106465)


--- trunk/Source/WebCore/html/shadow/HTMLContentElement.cpp	2012-02-01 13:31:11 UTC (rev 106464)
+++ trunk/Source/WebCore/html/shadow/HTMLContentElement.cpp	2012-02-01 13:50:25 UTC (rev 106465)
@@ -83,8 +83,6 @@
 
     if (root) {
         for (ShadowInclusion* inclusion = m_inclusions->first(); inclusion; inclusion = inclusion->next())
-            inclusion->content()->detach();
-        for (ShadowInclusion* inclusion = m_inclusions->first(); inclusion; inclusion = inclusion->next())
             inclusion->content()->attach();
     }
 }
@@ -94,6 +92,10 @@
     if (ShadowRoot* root = toShadowRoot(shadowTreeRootNode())) {
         if (ContentInclusionSelector* selector = root->inclusions())
             selector->unselect(m_inclusions.get());
+
+        // When content element is detached, shadow tree should be recreated to re-calculate inclusions for
+        // other content elements.
+        root->setNeedsReattachHostChildrenAndShadow();
     }
 
     ASSERT(m_inclusions->isEmpty());
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to