Title: [118886] trunk/Source/WebCore
Revision
118886
Author
[email protected]
Date
2012-05-29 20:33:21 -0700 (Tue, 29 May 2012)

Log Message

[Shadow DOM] Node distribution should be orthogonal from node attachment
https://bugs.webkit.org/show_bug.cgi?id=87223

Reviewed by Dimitri Glazkov.

This chagne reorganizes Shadow DOM subtree distribution implementation.

Originally, it was interleaved across attach() of several classes like
InsertionPoint and ShadowRoot. Its invalidation was also mixed as a part of
the style recalculation and detach()-es.

This change extracts these bits of code to a set of ContentDistributor methods, which are
facaded by two ElementShadow API. Following two API are the primary entry points:

- ElementShadow::ensureDistribution()
- ElementShadow::invalidateDistribution()

The actual implementations are ContentDistributor::distribute() and
ContentDistributor::invalidate() respectively.

When clients need to traverse composed tree, before attach() for
example, they should call ensureDistribution() to make sure that
the traversal data structure ("the distribution") is ready.  When
there is any DOM mutation which can result a composed tree
mutation, then clients should call invalidateDistribution() to
mark the distribution being dated.

Here are such DOM mutations:

- The children of any ShadowRoots are changed,
- The children of any InsertionPoints are changed,
- The children of any host elements are changed,
- Any insertion point is inserted to or removed from the shadow tree,
- @select attribute of <content> is modified and
- New ShadowRoot is added to the shadow tree.

Note that the validity of the distribution is tracked and
unnecessary distribution requests are ignored.

After the invalidation, that shadow subtrees are detached once and
request  their  re-attachment  through  the  style  recalculation.
Then,  on the  responding  style recalculation  and attach(),  new
distribution will be computed.

No new tests. Covered by existing tests.

* dom/Element.cpp:
(WebCore::Element::~Element):
(WebCore::Element::childrenChanged):
* dom/ElementShadow.cpp:
(WebCore::ElementShadow::~ElementShadow):
(WebCore::ElementShadow::addShadowRoot):
(WebCore::ElementShadow::removeAllShadowRoots):
(WebCore::ElementShadow::attach):
(WebCore::ElementShadow::recalcStyle):
(WebCore::ElementShadow::ensureDistribution):
(WebCore::ElementShadow::invalidateDistribution):
* dom/ElementShadow.h:
(ElementShadow):
* dom/NodeRenderingContext.cpp:
(WebCore::NodeRenderingContext::NodeRenderingContext):
(WebCore::NodeRendererFactory::createRendererIfNeeded):
* dom/NodeRenderingContext.h:
(NodeRenderingContext):
* dom/ShadowRoot.cpp:
(WebCore::ShadowRoot::setApplyAuthorStyles):
(WebCore::ShadowRoot::attach):
(WebCore::ShadowRoot::childrenChanged):
(WebCore):
* dom/ShadowRoot.h:
(ShadowRoot):
* html/HTMLFormControlElement.cpp:
* html/ValidationMessage.cpp:
(WebCore::ValidationMessage::buildBubbleTree):
* html/shadow/ContentDistributor.cpp:
(WebCore::ContentDistributor::ContentDistributor):
(WebCore::ContentDistributor::~ContentDistributor):
(WebCore::ContentDistributor::findInsertionPointFor):
(WebCore::ContentDistributor::distribute):
(WebCore::ContentDistributor::invalidate):
(WebCore::ContentDistributor::finishInivalidation):
(WebCore::ContentDistributor::distributeSelectionsTo):
(WebCore::ContentDistributor::distributeShadowChildrenTo):
(WebCore::ContentDistributor::invalidateDistributionIn):
* html/shadow/ContentDistributor.h:
(WebCore::ContentDistributor::needsInvalidation):
(ContentDistributor):
(WebCore::ContentDistributor::needsDistribution):
* html/shadow/HTMLContentElement.cpp:
(WebCore::HTMLContentElement::parseAttribute):
* html/shadow/InsertionPoint.cpp:
(WebCore::InsertionPoint::attach):
(WebCore::InsertionPoint::detach):
(WebCore::InsertionPoint::nextTo):
(WebCore::InsertionPoint::previousTo):
(WebCore::InsertionPoint::childrenChanged):
* html/shadow/InsertionPoint.h:
(WebCore::InsertionPoint::setDistribution):
(WebCore::InsertionPoint::clearDistribution):
(InsertionPoint):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (118885 => 118886)


--- trunk/Source/WebCore/ChangeLog	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/ChangeLog	2012-05-30 03:33:21 UTC (rev 118886)
@@ -1,3 +1,106 @@
+2012-05-29  MORITA Hajime  <[email protected]>
+
+        [Shadow DOM] Node distribution should be orthogonal from node attachment
+        https://bugs.webkit.org/show_bug.cgi?id=87223
+
+        Reviewed by Dimitri Glazkov.
+
+        This chagne reorganizes Shadow DOM subtree distribution implementation.
+
+        Originally, it was interleaved across attach() of several classes like
+        InsertionPoint and ShadowRoot. Its invalidation was also mixed as a part of
+        the style recalculation and detach()-es.
+
+        This change extracts these bits of code to a set of ContentDistributor methods, which are
+        facaded by two ElementShadow API. Following two API are the primary entry points:
+
+        - ElementShadow::ensureDistribution()
+        - ElementShadow::invalidateDistribution()
+
+        The actual implementations are ContentDistributor::distribute() and
+        ContentDistributor::invalidate() respectively.
+
+        When clients need to traverse composed tree, before attach() for
+        example, they should call ensureDistribution() to make sure that
+        the traversal data structure ("the distribution") is ready.  When
+        there is any DOM mutation which can result a composed tree
+        mutation, then clients should call invalidateDistribution() to
+        mark the distribution being dated.
+
+        Here are such DOM mutations:
+
+        - The children of any ShadowRoots are changed,
+        - The children of any InsertionPoints are changed,
+        - The children of any host elements are changed,
+        - Any insertion point is inserted to or removed from the shadow tree,
+        - @select attribute of <content> is modified and
+        - New ShadowRoot is added to the shadow tree.
+
+        Note that the validity of the distribution is tracked and
+        unnecessary distribution requests are ignored.
+
+        After the invalidation, that shadow subtrees are detached once and
+        request  their  re-attachment  through  the  style  recalculation.
+        Then,  on the  responding  style recalculation  and attach(),  new
+        distribution will be computed.
+
+        No new tests. Covered by existing tests.
+
+        * dom/Element.cpp:
+        (WebCore::Element::~Element):
+        (WebCore::Element::childrenChanged):
+        * dom/ElementShadow.cpp:
+        (WebCore::ElementShadow::~ElementShadow):
+        (WebCore::ElementShadow::addShadowRoot):
+        (WebCore::ElementShadow::removeAllShadowRoots):
+        (WebCore::ElementShadow::attach):
+        (WebCore::ElementShadow::recalcStyle):
+        (WebCore::ElementShadow::ensureDistribution):
+        (WebCore::ElementShadow::invalidateDistribution):
+        * dom/ElementShadow.h:
+        (ElementShadow):
+        * dom/NodeRenderingContext.cpp:
+        (WebCore::NodeRenderingContext::NodeRenderingContext):
+        (WebCore::NodeRendererFactory::createRendererIfNeeded):
+        * dom/NodeRenderingContext.h:
+        (NodeRenderingContext):
+        * dom/ShadowRoot.cpp:
+        (WebCore::ShadowRoot::setApplyAuthorStyles):
+        (WebCore::ShadowRoot::attach):
+        (WebCore::ShadowRoot::childrenChanged):
+        (WebCore):
+        * dom/ShadowRoot.h:
+        (ShadowRoot):
+        * html/HTMLFormControlElement.cpp:
+        * html/ValidationMessage.cpp:
+        (WebCore::ValidationMessage::buildBubbleTree):
+        * html/shadow/ContentDistributor.cpp:
+        (WebCore::ContentDistributor::ContentDistributor):
+        (WebCore::ContentDistributor::~ContentDistributor):
+        (WebCore::ContentDistributor::findInsertionPointFor):
+        (WebCore::ContentDistributor::distribute):
+        (WebCore::ContentDistributor::invalidate):
+        (WebCore::ContentDistributor::finishInivalidation):
+        (WebCore::ContentDistributor::distributeSelectionsTo):
+        (WebCore::ContentDistributor::distributeShadowChildrenTo):
+        (WebCore::ContentDistributor::invalidateDistributionIn):
+        * html/shadow/ContentDistributor.h:
+        (WebCore::ContentDistributor::needsInvalidation):
+        (ContentDistributor):
+        (WebCore::ContentDistributor::needsDistribution):
+        * html/shadow/HTMLContentElement.cpp:
+        (WebCore::HTMLContentElement::parseAttribute):
+        * html/shadow/InsertionPoint.cpp:
+        (WebCore::InsertionPoint::attach):
+        (WebCore::InsertionPoint::detach):
+        (WebCore::InsertionPoint::nextTo):
+        (WebCore::InsertionPoint::previousTo):
+        (WebCore::InsertionPoint::childrenChanged):
+        * html/shadow/InsertionPoint.h:
+        (WebCore::InsertionPoint::setDistribution):
+        (WebCore::InsertionPoint::clearDistribution):
+        (InsertionPoint):
+
 2012-05-29  Luke Macpherson  <[email protected]>
 
         Implement post-landing feedback for WebKitCSSTransformValue::customCSSText().

Modified: trunk/Source/WebCore/dom/Element.cpp (118885 => 118886)


--- trunk/Source/WebCore/dom/Element.cpp	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/dom/Element.cpp	2012-05-30 03:33:21 UTC (rev 118886)
@@ -132,8 +132,10 @@
     }
 #endif
 
-    if (shadow())
+    if (ElementShadow* elementShadow = shadow()) {
+        elementShadow->removeAllShadowRoots();
         rareData()->m_shadow.clear();
+    }
 
     if (hasAttrList()) {
         ASSERT(m_attributeData);
@@ -1330,7 +1332,7 @@
         checkForSiblingStyleChanges(this, renderStyle(), false, beforeChange, afterChange, childCountDelta);
 
     if (ElementShadow * shadow = this->shadow())
-        shadow->hostChildrenChanged();
+        shadow->invalidateDistribution();
 }
 
 void Element::beginParsingChildren()

Modified: trunk/Source/WebCore/dom/ElementShadow.cpp (118885 => 118886)


--- trunk/Source/WebCore/dom/ElementShadow.cpp	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/dom/ElementShadow.cpp	2012-05-30 03:33:21 UTC (rev 118886)
@@ -44,7 +44,7 @@
 
 ElementShadow::~ElementShadow()
 {
-    removeAllShadowRoots();
+    ASSERT(m_shadowRoots.isEmpty());
 }
 
 static bool validateShadowRoot(Document* document, ShadowRoot* shadowRoot, ExceptionCode& ec)
@@ -74,15 +74,13 @@
         return;
 
     shadowRoot->setHost(shadowHost);
+    m_shadowRoots.push(shadowRoot.get());
+    invalidateDistribution(shadowHost);
     ChildNodeInsertionNotifier(shadowHost).notify(shadowRoot.get());
 
-    if (shadowHost->attached()) {
-        shadowRoot->lazyAttach();
-        detach();
-        shadowHost->detachChildren();
-    }
+    if (shadowHost->attached() && !shadowRoot->attached())
+        shadowRoot->attach();
 
-    m_shadowRoots.push(shadowRoot.get());
     InspectorInstrumentation::didPushShadowRoot(shadowHost, shadowRoot.get());
 }
 
@@ -91,13 +89,14 @@
     // Dont protect this ref count.
     Element* shadowHost = host();
 
-    while (RefPtr<ShadowRoot> oldRoot = m_shadowRoots.removeHead()) {
+    while (RefPtr<ShadowRoot> oldRoot = m_shadowRoots.head()) {
         InspectorInstrumentation::willPopShadowRoot(shadowHost, oldRoot.get());
         shadowHost->document()->removeFocusedNodeOfSubtree(oldRoot.get());
 
         if (oldRoot->attached())
             oldRoot->detach();
 
+        m_shadowRoots.removeHead();
         oldRoot->setHost(0);
         oldRoot->setPrev(0);
         oldRoot->setNext(0);
@@ -105,22 +104,16 @@
         ChildNodeRemovalNotifier(shadowHost).notify(oldRoot.get());
     }
 
-    if (shadowHost->attached())
-        shadowHost->attachChildrenLazily();
+    invalidateDistribution(shadowHost);
 }
 
 void ElementShadow::attach()
 {
-    // The pool nodes are populated lazily in
-    // ensureDistributor(), and here we just ensure that it is in clean state.
-    ASSERT(!distributor().poolIsReady());
-
-    distributor().willDistribute();
+    ensureDistribution();
     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
         if (!root->attached())
             root->attach();
     }
-    distributor().didDistribute();
 }
 
 void ElementShadow::detach()
@@ -167,63 +160,46 @@
 
 void ElementShadow::recalcStyle(Node::StyleChange change)
 {
-    ShadowRoot* youngest = youngestShadowRoot();
-    if (!youngest)
-        return;
+    for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
+        StyleResolver* styleResolver = root->document()->styleResolver();
+        styleResolver->pushParentShadowRoot(root);
 
-    if (needsRedistributing())
-        reattachHostChildrenAndShadow();
-    else {
-        StyleResolver* styleResolver = youngest->document()->styleResolver();
-
-        styleResolver->pushParentShadowRoot(youngest);
-        for (Node* n = youngest->firstChild(); n; n = n->nextSibling()) {
+        for (Node* n = root->firstChild(); n; n = n->nextSibling()) {
             if (n->isElementNode())
                 static_cast<Element*>(n)->recalcStyle(change);
             else if (n->isTextNode())
                 toText(n)->recalcTextStyle(change);
         }
-        styleResolver->popParentShadowRoot(youngest);
-    }
 
-    m_distributor.clearNeedsRedistributing();
-    for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
+        styleResolver->popParentShadowRoot(root);
         root->clearNeedsStyleRecalc();
         root->clearChildNeedsStyleRecalc();
     }
 }
 
-bool ElementShadow::needsRedistributing()
+void ElementShadow::ensureDistribution()
 {
-    return m_distributor.needsRedistributing() || (youngestShadowRoot() && youngestShadowRoot()->hasInsertionPoint());
-}
-
-void ElementShadow::hostChildrenChanged()
-{
-    ASSERT(youngestShadowRoot());
-
-    if (!youngestShadowRoot()->hasInsertionPoint())
+    if (!m_distributor.needsDistribution())
         return;
-
-    // This results in forced detaching/attaching of the shadow render tree. See ShadowRoot::recalcStyle().
-    setNeedsRedistributing();
+    m_distributor.distribute(host());
 }
 
-void ElementShadow::setNeedsRedistributing()
+void ElementShadow::invalidateDistribution()
 {
-    m_distributor.setNeedsRedistributing();
-    host()->setNeedsStyleRecalc();
+    invalidateDistribution(host());
 }
 
-void ElementShadow::reattachHostChildrenAndShadow()
+void ElementShadow::invalidateDistribution(Element* host)
 {
-    ASSERT(youngestShadowRoot());
+    if (!m_distributor.needsInvalidation())
+        return;
+    bool needsReattach = m_distributor.invalidate(host);
+    if (needsReattach && host->attached()) {
+        host->detach();
+        host->lazyAttach(Node::DoNotSetAttached);
+    }
 
-    Element* hostNode = youngestShadowRoot()->host();
-    hostNode->detachChildrenIfNeeded();
-    detach();
-    attach();
-    hostNode->attachChildrenIfNeeded();
+    m_distributor.finishInivalidation();
 }
 
 } // namespace

Modified: trunk/Source/WebCore/dom/ElementShadow.h (118885 => 118886)


--- trunk/Source/WebCore/dom/ElementShadow.h	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/dom/ElementShadow.h	2012-05-30 03:33:21 UTC (rev 118886)
@@ -52,6 +52,7 @@
     ShadowRoot* youngestShadowRoot() const;
     ShadowRoot* oldestShadowRoot() const;
 
+    void removeAllShadowRoots();
     void addShadowRoot(Element* shadowHost, PassRefPtr<ShadowRoot>, ExceptionCode&);
 
     void attach();
@@ -60,18 +61,17 @@
     bool childNeedsStyleRecalc();
     bool needsStyleRecalc();
     void recalcStyle(Node::StyleChange);
-    void setNeedsRedistributing();
-    bool needsRedistributing();
-    void hostChildrenChanged();
 
+    void ensureDistribution();
+    void invalidateDistribution();
+ 
     InsertionPoint* insertionPointFor(const Node*) const;
 
     ContentDistributor& distributor();
     const ContentDistributor& distributor() const;
 
 private:
-    void removeAllShadowRoots();
-    void reattachHostChildrenAndShadow();
+    void invalidateDistribution(Element* host);
 
     DoublyLinkedList<ShadowRoot> m_shadowRoots;
     ContentDistributor m_distributor;

Modified: trunk/Source/WebCore/dom/NodeRenderingContext.cpp (118885 => 118886)


--- trunk/Source/WebCore/dom/NodeRenderingContext.cpp	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/dom/NodeRenderingContext.cpp	2012-05-30 03:33:21 UTC (rev 118886)
@@ -77,6 +77,8 @@
             m_visualParentShadow = toShadowRoot(parent)->owner();
 
         if (m_visualParentShadow) {
+            m_visualParentShadow->ensureDistribution();
+
             if ((m_insertionPoint = m_visualParentShadow->insertionPointFor(m_node))) {
                 if (m_insertionPoint->shadowRoot()->isUsedForRendering()) {
                     m_phase = AttachingDistributed;
@@ -91,7 +93,10 @@
         }
 
         if (isShadowBoundary(parent)) {
-            if (!parent->shadowRoot()->isUsedForRendering()) {
+            ShadowRoot* parentShadowRoot = parent->shadowRoot();
+            parentShadowRoot->owner()->ensureDistribution();
+
+            if (!parentShadowRoot->isUsedForRendering()) {
                 m_phase = AttachingNotDistributed;
                 m_parentNodeForRenderingAndStyle = parent;
                 return;
@@ -285,12 +290,6 @@
     return m_parentNodeForRenderingAndStyle ? m_parentNodeForRenderingAndStyle->renderer() : 0;
 }
 
-void NodeRenderingContext::hostChildrenChanged()
-{
-    if (m_phase == AttachingNotDistributed && m_visualParentShadow)
-        m_visualParentShadow->hostChildrenChanged();
-}
-
 bool NodeRenderingContext::shouldCreateRenderer() const
 {
     ASSERT(m_phase != Calculating);
@@ -366,9 +365,6 @@
     ASSERT(!node->renderer());
     ASSERT(document->shouldCreateRenderers());
 
-    // FIXME: This side effect should be visible from attach() code.
-    m_context.hostChildrenChanged();
-
     if (!m_context.shouldCreateRenderer())
         return;
 

Modified: trunk/Source/WebCore/dom/NodeRenderingContext.h (118885 => 118886)


--- trunk/Source/WebCore/dom/NodeRenderingContext.h	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/dom/NodeRenderingContext.h	2012-05-30 03:33:21 UTC (rev 118886)
@@ -60,8 +60,6 @@
 
     bool shouldCreateRenderer() const;
 
-    void hostChildrenChanged();
-
     bool isOnUpperEncapsulationBoundary() const;
     bool isOnEncapsulationBoundary() const;
     bool hasFlowThreadParent() const { return m_parentFlowRenderer; }

Modified: trunk/Source/WebCore/dom/ShadowRoot.cpp (118885 => 118886)


--- trunk/Source/WebCore/dom/ShadowRoot.cpp	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/dom/ShadowRoot.cpp	2012-05-30 03:33:21 UTC (rev 118886)
@@ -189,8 +189,7 @@
 {
     if (m_applyAuthorStyles != value) {
         m_applyAuthorStyles = value;
-        if (attached() && owner())
-            owner()->setNeedsRedistributing();
+        host()->setNeedsStyleRecalc();
     }
 }
 
@@ -198,8 +197,15 @@
 {
     StyleResolver* styleResolver = document()->styleResolver();
     styleResolver->pushParentShadowRoot(this);
-    DocumentFragment::attach();
+    attachChildrenIfNeeded();
+    attachAsNode();
     styleResolver->popParentShadowRoot(this);
 }
 
+void ShadowRoot::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
+{
+    ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
+    owner()->invalidateDistribution();
 }
+
+}

Modified: trunk/Source/WebCore/dom/ShadowRoot.h (118885 => 118886)


--- trunk/Source/WebCore/dom/ShadowRoot.h	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/dom/ShadowRoot.h	2012-05-30 03:33:21 UTC (rev 118886)
@@ -60,7 +60,6 @@
     void recalcShadowTreeStyle(StyleChange);
 
     InsertionPoint* insertionPointFor(Node*) const;
-    void hostChildrenChanged();
 
     virtual bool applyAuthorStyles() const OVERRIDE;
     void setApplyAuthorStyles(bool);
@@ -91,10 +90,10 @@
 private:
     ShadowRoot(Document*);
     virtual ~ShadowRoot();
-
     virtual String nodeName() const;
     virtual PassRefPtr<Node> cloneNode(bool deep);
     virtual bool childTypeAllowed(NodeType) const;
+    virtual void childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) OVERRIDE;
 
     ShadowRoot* m_prev;
     ShadowRoot* m_next;

Modified: trunk/Source/WebCore/html/HTMLFormControlElement.cpp (118885 => 118886)


--- trunk/Source/WebCore/html/HTMLFormControlElement.cpp	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.cpp	2012-05-30 03:33:21 UTC (rev 118886)
@@ -26,6 +26,7 @@
 #include "HTMLFormControlElement.h"
 
 #include "Attribute.h"
+#include "ElementShadow.h"
 #include "Event.h"
 #include "EventHandler.h"
 #include "EventNames.h"

Modified: trunk/Source/WebCore/html/ValidationMessage.cpp (118885 => 118886)


--- trunk/Source/WebCore/html/ValidationMessage.cpp	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/html/ValidationMessage.cpp	2012-05-30 03:33:21 UTC (rev 118886)
@@ -132,6 +132,7 @@
 void ValidationMessage::buildBubbleTree(Timer<ValidationMessage>*)
 {
     HTMLElement* host = toHTMLElement(m_element);
+
     Document* doc = host->document();
     m_bubble = HTMLDivElement::create(doc);
     m_bubble->setShadowPseudoId("-webkit-validation-bubble");
@@ -141,6 +142,7 @@
     ExceptionCode ec = 0;
     host->ensureShadowRoot()->appendChild(m_bubble.get(), ec);
     ASSERT(!ec);
+    host->document()->updateLayout();
     adjustBubblePosition(host->getRect(), m_bubble.get());
 
     RefPtr<HTMLDivElement> clipper = HTMLDivElement::create(doc);

Modified: trunk/Source/WebCore/html/shadow/ContentDistributor.cpp (118885 => 118886)


--- trunk/Source/WebCore/html/shadow/ContentDistributor.cpp	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/html/shadow/ContentDistributor.cpp	2012-05-30 03:33:21 UTC (rev 118886)
@@ -28,6 +28,7 @@
 #include "ContentDistributor.h"
 
 #include "ContentSelectorQuery.h"
+#include "ElementShadow.h"
 #include "HTMLContentElement.h"
 #include "ShadowRoot.h"
 
@@ -35,72 +36,113 @@
 namespace WebCore {
 
 ContentDistributor::ContentDistributor()
-    : m_phase(Prevented)
-    , m_needsRedistributing(false)
+    : m_validity(Undetermined)
 {
 }
 
 ContentDistributor::~ContentDistributor()
 {
-    ASSERT(m_pool.isEmpty());
 }
 
-void ContentDistributor::distribute(InsertionPoint* insertionPoint, ContentDistribution* distribution)
+InsertionPoint* ContentDistributor::findInsertionPointFor(const Node* key) const
 {
-    ASSERT(m_phase == Prepared);
-    ASSERT(distribution->isEmpty());
+    return m_nodeToInsertionPoint.get(key);
+}
 
-    ContentSelectorQuery query(insertionPoint);
 
-    for (size_t i = 0; i < m_pool.size(); ++i) {
-        Node* child = m_pool[i].get();
-        if (!child)
-            continue;
-        if (!query.matches(child))
-            continue;
+void ContentDistributor::distribute(Element* host)
+{
+    ASSERT(needsDistribution());
+    ASSERT(m_nodeToInsertionPoint.isEmpty());
 
-        distribution->append(child);
-        m_nodeToInsertionPoint.add(child, insertionPoint);
-        m_pool[i] = 0;
+    m_validity = Valid;
+
+    ContentDistribution pool;
+    for (Node* node = host->firstChild(); node; node = node->nextSibling())
+        pool.append(node);
+
+    for (ShadowRoot* root = host->youngestShadowRoot(); root; root = root->olderShadowRoot()) {
+        for (Node* node = root; node; node = node->traverseNextNode(root)) {
+            if (!isInsertionPoint(node))
+                continue;
+            InsertionPoint* point = toInsertionPoint(node);
+            if (!point->isActive())
+                continue;
+            ShadowRoot* older = root->olderShadowRoot();
+            if (point->doesSelectFromHostChildren())
+                distributeSelectionsTo(point, pool);
+            else if (older && !older->assignedTo()) {
+                distributeShadowChildrenTo(point, older);
+                older->setAssignedTo(point);
+            }
+        }
     }
 }
 
-void ContentDistributor::clearDistribution(ContentDistribution* list)
+bool ContentDistributor::invalidate(Element* host)
 {
-    for (size_t i = 0; i < list->size(); ++i)
-        m_nodeToInsertionPoint.remove(list->at(i).get());
-    list->clear();
+    ASSERT(needsInvalidation());
+    bool needsReattach = (m_validity == Undetermined) || !m_nodeToInsertionPoint.isEmpty();
+
+    for (ShadowRoot* root = host->youngestShadowRoot(); root; root = root->olderShadowRoot()) {
+        root->setAssignedTo(0);
+
+        for (Node* node = root; node; node = node->traverseNextNode(root)) {
+            if (!isInsertionPoint(node))
+                continue;
+            needsReattach = needsReattach || true;
+            InsertionPoint* point = toInsertionPoint(node);
+            point->clearDistribution();
+        }
+    }
+
+    m_validity = Invalidating;
+    m_nodeToInsertionPoint.clear();
+    return needsReattach;
 }
 
-InsertionPoint* ContentDistributor::findInsertionPointFor(const Node* key) const
+void ContentDistributor::finishInivalidation()
 {
-    return m_nodeToInsertionPoint.get(key);
+    ASSERT(m_validity == Invalidating);
+    m_validity = Invalidated;
 }
 
-void ContentDistributor::willDistribute()
+void ContentDistributor::distributeSelectionsTo(InsertionPoint* insertionPoint, ContentDistribution& pool)
 {
-    m_phase = Started;
+    ContentDistribution distribution;
+    ContentSelectorQuery query(insertionPoint);
+
+    for (size_t i = 0; i < pool.size(); ++i) {
+        Node* child = pool[i].get();
+        if (!child)
+            continue;
+        if (!query.matches(child))
+            continue;
+
+        distribution.append(child);
+        m_nodeToInsertionPoint.add(child, insertionPoint);
+        pool[i] = 0;
+    }
+
+    insertionPoint->setDistribution(distribution);
 }
 
-void ContentDistributor::didDistribute()
+void ContentDistributor::distributeShadowChildrenTo(InsertionPoint* insertionPoint, ShadowRoot* root)
 {
-    ASSERT(m_phase != Prevented);
-    m_phase = Prevented;
-    m_pool.clear();
+    ContentDistribution distribution;
+    for (Node* node = root->firstChild(); node; node = node->nextSibling()) {
+        distribution.append(node);
+        m_nodeToInsertionPoint.add(node, insertionPoint);
+    }
+
+    insertionPoint->setDistribution(distribution);
 }
 
-void ContentDistributor::preparePoolFor(Element* shadowHost)
+void ContentDistributor::invalidateDistributionIn(ContentDistribution* list)
 {
-    if (poolIsReady())
-        return;
-
-    ASSERT(m_pool.isEmpty());
-    ASSERT(shadowHost);
-    ASSERT(m_phase == Started);
-
-    m_phase = Prepared;
-    for (Node* node = shadowHost->firstChild(); node; node = node->nextSibling())
-        m_pool.append(node);
+    for (size_t i = 0; i < list->size(); ++i)
+        m_nodeToInsertionPoint.remove(list->at(i).get());
+    list->clear();
 }
 
 }

Modified: trunk/Source/WebCore/html/shadow/ContentDistributor.h (118885 => 118886)


--- trunk/Source/WebCore/html/shadow/ContentDistributor.h	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/html/shadow/ContentDistributor.h	2012-05-30 03:33:21 UTC (rev 118886)
@@ -48,45 +48,39 @@
 class ContentDistributor {
     WTF_MAKE_NONCOPYABLE(ContentDistributor);
 public:
+    enum Validity {
+        Valid = 0,
+        Invalidated = 1,
+        Invalidating = 2,
+        Undetermined = 3
+    };
+
     ContentDistributor();
     ~ContentDistributor();
 
-    void distribute(InsertionPoint*, ContentDistribution*);
-    void clearDistribution(ContentDistribution*);
     InsertionPoint* findInsertionPointFor(const Node* key) const;
 
-    void willDistribute();
-    bool inDistribution() const;
-    void didDistribute();
+    void distribute(Element* host);
+    bool invalidate(Element* host);
+    void finishInivalidation();
+    bool needsDistribution() const;
+    bool needsInvalidation() const { return m_validity != Invalidated; }
 
-    void preparePoolFor(Element* shadowHost);
-    bool poolIsReady() const;
-    bool needsRedistributing() const { return m_needsRedistributing; }
-    void setNeedsRedistributing() { m_needsRedistributing = true; }
-    void clearNeedsRedistributing() { m_needsRedistributing = false; }
-private:
-    enum DistributionPhase {
-        Prevented,
-        Started,
-        Prepared,
-    };
+    void distributeSelectionsTo(InsertionPoint*, ContentDistribution& pool);
+    void distributeShadowChildrenTo(InsertionPoint*, ShadowRoot*);
+    void invalidateDistributionIn(ContentDistribution*);
 
-    Vector<RefPtr<Node> > m_pool;
-    DistributionPhase m_phase;
+private:
     HashMap<const Node*, InsertionPoint*> m_nodeToInsertionPoint;
-    bool m_needsRedistributing : 1;
+    unsigned m_validity : 2;
 };
 
-inline bool ContentDistributor::inDistribution() const
+inline bool ContentDistributor::needsDistribution() const
 {
-    return m_phase != Prevented;
+    // During the invalidation, re-distribution should be supressed.
+    return m_validity != Valid && m_validity != Invalidating;
 }
 
-inline bool ContentDistributor::poolIsReady() const
-{
-    return m_phase == Prepared;
 }
 
-}
-
 #endif

Modified: trunk/Source/WebCore/html/shadow/HTMLContentElement.cpp (118885 => 118886)


--- trunk/Source/WebCore/html/shadow/HTMLContentElement.cpp	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/html/shadow/HTMLContentElement.cpp	2012-05-30 03:33:21 UTC (rev 118886)
@@ -91,7 +91,7 @@
 {
     if (attribute.name() == selectAttr) {
         if (ShadowRoot* root = shadowRoot())
-            root->owner()->setNeedsRedistributing();
+            root->owner()->invalidateDistribution();
     } else
         InsertionPoint::parseAttribute(attribute);
 }

Modified: trunk/Source/WebCore/html/shadow/InsertionPoint.cpp (118885 => 118886)


--- trunk/Source/WebCore/html/shadow/InsertionPoint.cpp	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/html/shadow/InsertionPoint.cpp	2012-05-30 03:33:21 UTC (rev 118886)
@@ -47,53 +47,22 @@
 
 void InsertionPoint::attach()
 {
-    if (isShadowBoundary()) {
-        ShadowRoot* root = toShadowRoot(treeScope()->rootNode());
-        if (doesSelectFromHostChildren()) {
-            distributeHostChildren(root->owner());
-            attachDistributedNode();
-        } else if (!root->olderShadowRoot()->assignedTo()) {
-            ASSERT(!root->olderShadowRoot()->attached());
-            assignShadowRoot(root->olderShadowRoot());
-            root->olderShadowRoot()->attach();
-        }
-    }
-
+    if (ShadowRoot* root = shadowRoot())
+        root->owner()->ensureDistribution();
+    for (size_t i = 0; i < m_distribution.size(); ++i)
+        m_distribution.at(i)->attach();
     HTMLElement::attach();
 }
 
 void InsertionPoint::detach()
 {
-    ShadowRoot* root = shadowRoot();
-    if (root && isActive()) {
-        ElementShadow* shadow = root->owner();
-
-        if (doesSelectFromHostChildren())
-            clearDistribution(shadow);
-        else if (ShadowRoot* assignedShadowRoot = assignedFrom())
-            clearAssignment(assignedShadowRoot);
-
-        // When shadow element is detached, shadow tree should be recreated to re-calculate selector for
-        // other insertion points.
-        shadow->setNeedsRedistributing();
-    }
-
-    ASSERT(m_distribution.isEmpty());
+    if (ShadowRoot* root = shadowRoot())
+        root->owner()->ensureDistribution();
+    for (size_t i = 0; i < m_distribution.size(); ++i)
+        m_distribution.at(i)->detach();
     HTMLElement::detach();
 }
 
-ShadowRoot* InsertionPoint::assignedFrom() const
-{
-    Node* treeScopeRoot = treeScope()->rootNode();
-    if (!treeScopeRoot->isShadowRoot())
-        return 0;
-
-    ShadowRoot* olderShadowRoot = toShadowRoot(treeScopeRoot)->olderShadowRoot();
-    if (olderShadowRoot && olderShadowRoot->assignedTo() == this)
-        return olderShadowRoot;
-    return 0;
-}
-
 bool InsertionPoint::isShadowBoundary() const
 {
     return treeScope()->rootNode()->isShadowRoot() && isActive();
@@ -118,59 +87,57 @@
     return !isShadowBoundary() && HTMLElement::rendererIsNeeded(context);
 }
 
-inline void InsertionPoint::distributeHostChildren(ElementShadow* shadow)
+Node* InsertionPoint::nextTo(const Node* node) const
 {
-    if (!shadow->distributor().inDistribution()) {
-        // If ContentDistributor is not int selecting phase, it means InsertionPoint is attached from
-        // non-ElementShadow node. To run distribute algorithm, we have to reattach ElementShadow.
-        shadow->setNeedsRedistributing();
-        return;
-    }
-
-    shadow->distributor().preparePoolFor(shadow->host());
-    shadow->distributor().clearDistribution(&m_distribution);
-    shadow->distributor().distribute(this, &m_distribution);
+    size_t index = m_distribution.find(node);
+    if (index == notFound || index + 1 == m_distribution.size())
+        return 0;
+    return m_distribution.at(index + 1).get();
 }
 
-inline void InsertionPoint::clearDistribution(ElementShadow* shadow)
+Node* InsertionPoint::previousTo(const Node* node) const
 {
-    shadow->distributor().clearDistribution(&m_distribution);
+    size_t index = m_distribution.find(node);
+    if (index == notFound || !index)
+        return 0;
+    return m_distribution.at(index - 1).get();
 }
 
-inline void InsertionPoint::attachDistributedNode()
+void InsertionPoint::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
 {
-    for (size_t i = 0; i < m_distribution.size(); ++i)
-        m_distribution.at(i)->attach();
+    HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
+    if (ShadowRoot* root = shadowRoot())
+        root->owner()->invalidateDistribution();
 }
 
-inline void InsertionPoint::assignShadowRoot(ShadowRoot* shadowRoot)
+Node::InsertionNotificationRequest InsertionPoint::insertedInto(ContainerNode* insertionPoint)
 {
-    shadowRoot->setAssignedTo(this);
-    m_distribution.clear();
-    for (Node* node = shadowRoot->firstChild(); node; node = node->nextSibling())
-        m_distribution.append(node);
-}
+    HTMLElement::insertedInto(insertionPoint);
+    if (insertionPoint->inDocument()) {
+        if (ShadowRoot* root = shadowRoot())
+            root->owner()->invalidateDistribution();
+    }
 
-inline void InsertionPoint::clearAssignment(ShadowRoot* shadowRoot)
-{
-    shadowRoot->setAssignedTo(0);
-    m_distribution.clear();
+    return InsertionDone;
 }
 
-Node* InsertionPoint::nextTo(const Node* node) const
+void InsertionPoint::removedFrom(ContainerNode* insertionPoint)
 {
-    size_t index = m_distribution.find(node);
-    if (index == notFound || index + 1 == m_distribution.size())
-        return 0;
-    return m_distribution.at(index + 1).get();
-}
+    if (insertionPoint->inDocument()) {
+        Node* parent = parentNode();
+        if (!parent)
+            parent = insertionPoint;
+        if (ShadowRoot* root = parent->shadowRoot()) {
+            // host can be null when removedFrom() is called from ElementShadow destructor.
+            if (root->host())
+                root->owner()->invalidateDistribution();
+        }
 
-Node* InsertionPoint::previousTo(const Node* node) const
-{
-    size_t index = m_distribution.find(node);
-    if (index == notFound || !index)
-        return 0;
-    return m_distribution.at(index - 1).get();
+        // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up.
+        clearDistribution();
+    }
+
+    HTMLElement::removedFrom(insertionPoint);
 }
 
 

Modified: trunk/Source/WebCore/html/shadow/InsertionPoint.h (118885 => 118886)


--- trunk/Source/WebCore/html/shadow/InsertionPoint.h	2012-05-30 03:30:23 UTC (rev 118885)
+++ trunk/Source/WebCore/html/shadow/InsertionPoint.h	2012-05-30 03:33:21 UTC (rev 118886)
@@ -43,6 +43,8 @@
     virtual ~InsertionPoint();
 
     bool hasDistribution() const { return !m_distribution.isEmpty(); }
+    void setDistribution(ContentDistribution& distribution) { m_distribution.swap(distribution); }
+    void clearDistribution() { m_distribution.clear(); }
     bool isShadowBoundary() const;
     bool isActive() const;
 
@@ -52,9 +54,7 @@
 
     virtual void attach();
     virtual void detach();
-
     virtual bool isInsertionPoint() const OVERRIDE { return true; }
-    ShadowRoot* assignedFrom() const;
 
     size_t indexOf(Node* node) const { return m_distribution.find(node); }
     size_t size() const { return m_distribution.size(); }
@@ -67,15 +67,11 @@
 protected:
     InsertionPoint(const QualifiedName&, Document*);
     virtual bool rendererIsNeeded(const NodeRenderingContext&) OVERRIDE;
+    virtual void childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) OVERRIDE;
+    virtual InsertionNotificationRequest insertedInto(ContainerNode*) OVERRIDE;
+    virtual void removedFrom(ContainerNode*) OVERRIDE;
 
 private:
-    void distributeHostChildren(ElementShadow*);
-    void clearDistribution(ElementShadow*);
-    void attachDistributedNode();
-
-    void assignShadowRoot(ShadowRoot*);
-    void clearAssignment(ShadowRoot*);
-
     ContentDistribution m_distribution;
 };
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to