Home | History | Annotate | Download | only in shadow
      1 /*
      2  * Copyright (C) 2012 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Neither the name of Google Inc. nor the names of its
     11  * contributors may be used to endorse or promote products derived from
     12  * this software without specific prior written permission.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "core/dom/shadow/ElementShadow.h"
     29 
     30 #include "core/dom/ContainerNodeAlgorithms.h"
     31 #include "core/dom/ElementTraversal.h"
     32 #include "core/dom/NodeTraversal.h"
     33 #include "core/dom/shadow/ContentDistribution.h"
     34 #include "core/html/shadow/HTMLContentElement.h"
     35 #include "core/html/shadow/HTMLShadowElement.h"
     36 
     37 namespace WebCore {
     38 
     39 class DistributionPool {
     40 public:
     41     explicit DistributionPool(const ContainerNode&);
     42     void clear();
     43     ~DistributionPool();
     44     void distributeTo(InsertionPoint*, ElementShadow*);
     45     void populateChildren(const ContainerNode&);
     46 
     47 private:
     48     void detachNonDistributedNodes();
     49     Vector<Node*, 32> m_nodes;
     50     Vector<bool, 32> m_distributed;
     51 };
     52 
     53 inline DistributionPool::DistributionPool(const ContainerNode& parent)
     54 {
     55     populateChildren(parent);
     56 }
     57 
     58 inline void DistributionPool::clear()
     59 {
     60     detachNonDistributedNodes();
     61     m_nodes.clear();
     62     m_distributed.clear();
     63 }
     64 
     65 inline void DistributionPool::populateChildren(const ContainerNode& parent)
     66 {
     67     clear();
     68     for (Node* child = parent.firstChild(); child; child = child->nextSibling()) {
     69         if (isActiveInsertionPoint(*child)) {
     70             InsertionPoint* insertionPoint = toInsertionPoint(child);
     71             for (size_t i = 0; i < insertionPoint->size(); ++i)
     72                 m_nodes.append(insertionPoint->at(i));
     73         } else {
     74             m_nodes.append(child);
     75         }
     76     }
     77     m_distributed.resize(m_nodes.size());
     78     m_distributed.fill(false);
     79 }
     80 
     81 void DistributionPool::distributeTo(InsertionPoint* insertionPoint, ElementShadow* elementShadow)
     82 {
     83     ContentDistribution distribution;
     84 
     85     for (size_t i = 0; i < m_nodes.size(); ++i) {
     86         if (m_distributed[i])
     87             continue;
     88 
     89         if (isHTMLContentElement(insertionPoint) && !toHTMLContentElement(insertionPoint)->canSelectNode(m_nodes, i))
     90             continue;
     91 
     92         Node* node = m_nodes[i];
     93         distribution.append(node);
     94         elementShadow->didDistributeNode(node, insertionPoint);
     95         m_distributed[i] = true;
     96     }
     97 
     98     // Distributes fallback elements
     99     if (insertionPoint->isContentInsertionPoint() && distribution.isEmpty()) {
    100         for (Node* fallbackNode = insertionPoint->firstChild(); fallbackNode; fallbackNode = fallbackNode->nextSibling()) {
    101             distribution.append(fallbackNode);
    102             elementShadow->didDistributeNode(fallbackNode, insertionPoint);
    103         }
    104     }
    105     insertionPoint->setDistribution(distribution);
    106 }
    107 
    108 inline DistributionPool::~DistributionPool()
    109 {
    110     detachNonDistributedNodes();
    111 }
    112 
    113 inline void DistributionPool::detachNonDistributedNodes()
    114 {
    115     for (size_t i = 0; i < m_nodes.size(); ++i) {
    116         if (m_distributed[i])
    117             continue;
    118         if (m_nodes[i]->renderer())
    119             m_nodes[i]->lazyReattachIfAttached();
    120     }
    121 }
    122 
    123 PassOwnPtr<ElementShadow> ElementShadow::create()
    124 {
    125     return adoptPtr(new ElementShadow());
    126 }
    127 
    128 ElementShadow::ElementShadow()
    129     : m_needsDistributionRecalc(false)
    130     , m_applyAuthorStyles(false)
    131     , m_needsSelectFeatureSet(false)
    132 {
    133 }
    134 
    135 ElementShadow::~ElementShadow()
    136 {
    137     removeAllShadowRoots();
    138 }
    139 
    140 ShadowRoot& ElementShadow::addShadowRoot(Element& shadowHost, ShadowRoot::ShadowRootType type)
    141 {
    142     RefPtr<ShadowRoot> shadowRoot = ShadowRoot::create(&shadowHost.document(), type);
    143 
    144     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
    145         root->lazyReattachIfAttached();
    146 
    147     shadowRoot->setParentOrShadowHostNode(&shadowHost);
    148     shadowRoot->setParentTreeScope(&shadowHost.treeScope());
    149     m_shadowRoots.push(shadowRoot.get());
    150     ChildNodeInsertionNotifier(shadowHost).notify(*shadowRoot);
    151     setNeedsDistributionRecalc();
    152 
    153     // addShadowRoot() affects apply-author-styles. However, we know that the youngest shadow root has not had any children yet.
    154     // The youngest shadow root's apply-author-styles is default (false). So we can just set m_applyAuthorStyles false.
    155     m_applyAuthorStyles = false;
    156 
    157     shadowHost.didAddShadowRoot(*shadowRoot);
    158     InspectorInstrumentation::didPushShadowRoot(&shadowHost, shadowRoot.get());
    159 
    160     ASSERT(m_shadowRoots.head());
    161     ASSERT(shadowRoot.get() == m_shadowRoots.head());
    162     return *m_shadowRoots.head();
    163 }
    164 
    165 void ElementShadow::removeAllShadowRoots()
    166 {
    167     // Dont protect this ref count.
    168     Element* shadowHost = host();
    169     ASSERT(shadowHost);
    170 
    171     while (RefPtr<ShadowRoot> oldRoot = m_shadowRoots.head()) {
    172         InspectorInstrumentation::willPopShadowRoot(shadowHost, oldRoot.get());
    173         shadowHost->document().removeFocusedElementOfSubtree(oldRoot.get());
    174         m_shadowRoots.removeHead();
    175         oldRoot->setParentOrShadowHostNode(0);
    176         oldRoot->setParentTreeScope(&shadowHost->document());
    177         oldRoot->setPrev(0);
    178         oldRoot->setNext(0);
    179         ChildNodeRemovalNotifier(*shadowHost).notify(*oldRoot);
    180     }
    181 }
    182 
    183 void ElementShadow::attach(const Node::AttachContext& context)
    184 {
    185     Node::AttachContext childrenContext(context);
    186     childrenContext.resolvedStyle = 0;
    187 
    188     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
    189         if (root->needsAttach())
    190             root->attach(childrenContext);
    191     }
    192 }
    193 
    194 void ElementShadow::detach(const Node::AttachContext& context)
    195 {
    196     Node::AttachContext childrenContext(context);
    197     childrenContext.resolvedStyle = 0;
    198 
    199     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
    200         root->detach(childrenContext);
    201 }
    202 
    203 void ElementShadow::removeAllEventListeners()
    204 {
    205     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
    206         for (Node* node = root; node; node = NodeTraversal::next(*node))
    207             node->removeAllEventListeners();
    208     }
    209 }
    210 
    211 void ElementShadow::setNeedsDistributionRecalc()
    212 {
    213     if (m_needsDistributionRecalc)
    214         return;
    215     m_needsDistributionRecalc = true;
    216     host()->markAncestorsWithChildNeedsDistributionRecalc();
    217     clearDistribution();
    218 }
    219 
    220 bool ElementShadow::didAffectApplyAuthorStyles()
    221 {
    222     bool applyAuthorStyles = resolveApplyAuthorStyles();
    223 
    224     if (m_applyAuthorStyles == applyAuthorStyles)
    225         return false;
    226 
    227     m_applyAuthorStyles = applyAuthorStyles;
    228     return true;
    229 }
    230 
    231 bool ElementShadow::containsActiveStyles() const
    232 {
    233     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
    234         if (root->hasScopedHTMLStyleChild())
    235             return true;
    236         if (!root->containsShadowElements())
    237             return false;
    238     }
    239     return false;
    240 }
    241 
    242 bool ElementShadow::resolveApplyAuthorStyles() const
    243 {
    244     for (const ShadowRoot* shadowRoot = youngestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->olderShadowRoot()) {
    245         if (shadowRoot->applyAuthorStyles())
    246             return true;
    247         if (!shadowRoot->containsShadowElements())
    248             break;
    249     }
    250     return false;
    251 }
    252 
    253 const InsertionPoint* ElementShadow::finalDestinationInsertionPointFor(const Node* key) const
    254 {
    255     NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key);
    256     return it == m_nodeToInsertionPoints.end() ? 0: it->value.last().get();
    257 }
    258 
    259 const DestinationInsertionPoints* ElementShadow::destinationInsertionPointsFor(const Node* key) const
    260 {
    261     NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key);
    262     return it == m_nodeToInsertionPoints.end() ? 0: &it->value;
    263 }
    264 
    265 void ElementShadow::distribute()
    266 {
    267     host()->setNeedsStyleRecalc();
    268     Vector<HTMLShadowElement*, 32> shadowInsertionPoints;
    269     DistributionPool pool(*host());
    270 
    271     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
    272         HTMLShadowElement* shadowInsertionPoint = 0;
    273         const Vector<RefPtr<InsertionPoint> >& insertionPoints = root->descendantInsertionPoints();
    274         for (size_t i = 0; i < insertionPoints.size(); ++i) {
    275             InsertionPoint* point = insertionPoints[i].get();
    276             if (!point->isActive())
    277                 continue;
    278             if (isHTMLShadowElement(point)) {
    279                 if (!shadowInsertionPoint)
    280                     shadowInsertionPoint = toHTMLShadowElement(point);
    281             } else {
    282                 pool.distributeTo(point, this);
    283                 if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*point))
    284                     shadow->setNeedsDistributionRecalc();
    285             }
    286         }
    287         if (shadowInsertionPoint) {
    288             shadowInsertionPoints.append(shadowInsertionPoint);
    289             if (shadowInsertionPoint->hasChildNodes())
    290                 pool.populateChildren(*shadowInsertionPoint);
    291         } else {
    292             pool.clear();
    293         }
    294     }
    295 
    296     for (size_t i = shadowInsertionPoints.size(); i > 0; --i) {
    297         HTMLShadowElement* shadowInsertionPoint = shadowInsertionPoints[i - 1];
    298         ShadowRoot* root = shadowInsertionPoint->containingShadowRoot();
    299         ASSERT(root);
    300         if (root->isOldest()) {
    301             pool.distributeTo(shadowInsertionPoint, this);
    302         } else if (root->olderShadowRoot()->type() == root->type()) {
    303             // Only allow reprojecting older shadow roots between the same type to
    304             // disallow reprojecting UA elements into author shadows.
    305             DistributionPool olderShadowRootPool(*root->olderShadowRoot());
    306             olderShadowRootPool.distributeTo(shadowInsertionPoint, this);
    307             root->olderShadowRoot()->setShadowInsertionPointOfYoungerShadowRoot(shadowInsertionPoint);
    308         }
    309         if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*shadowInsertionPoint))
    310             shadow->setNeedsDistributionRecalc();
    311     }
    312 }
    313 
    314 void ElementShadow::didDistributeNode(const Node* node, InsertionPoint* insertionPoint)
    315 {
    316     NodeToDestinationInsertionPoints::AddResult result = m_nodeToInsertionPoints.add(node, DestinationInsertionPoints());
    317     result.iterator->value.append(insertionPoint);
    318 }
    319 
    320 const SelectRuleFeatureSet& ElementShadow::ensureSelectFeatureSet()
    321 {
    322     if (!m_needsSelectFeatureSet)
    323         return m_selectFeatures;
    324 
    325     m_selectFeatures.clear();
    326     for (ShadowRoot* root = oldestShadowRoot(); root; root = root->youngerShadowRoot())
    327         collectSelectFeatureSetFrom(*root);
    328     m_needsSelectFeatureSet = false;
    329     return m_selectFeatures;
    330 }
    331 
    332 void ElementShadow::collectSelectFeatureSetFrom(ShadowRoot& root)
    333 {
    334     if (!root.containsShadowRoots() && !root.containsContentElements())
    335         return;
    336 
    337     for (Element* element = ElementTraversal::firstWithin(root); element; element = ElementTraversal::next(*element, &root)) {
    338         if (ElementShadow* shadow = element->shadow())
    339             m_selectFeatures.add(shadow->ensureSelectFeatureSet());
    340         if (!isHTMLContentElement(element))
    341             continue;
    342         const CSSSelectorList& list = toHTMLContentElement(element)->selectorList();
    343         for (const CSSSelector* selector = list.first(); selector; selector = CSSSelectorList::next(selector)) {
    344             for (const CSSSelector* component = selector; component; component = component->tagHistory())
    345                 m_selectFeatures.collectFeaturesFromSelector(component);
    346         }
    347     }
    348 }
    349 
    350 void ElementShadow::didAffectSelector(AffectedSelectorMask mask)
    351 {
    352     if (ensureSelectFeatureSet().hasSelectorFor(mask))
    353         setNeedsDistributionRecalc();
    354 }
    355 
    356 void ElementShadow::willAffectSelector()
    357 {
    358     for (ElementShadow* shadow = this; shadow; shadow = shadow->containingShadow()) {
    359         if (shadow->needsSelectFeatureSet())
    360             break;
    361         shadow->setNeedsSelectFeatureSet();
    362     }
    363     setNeedsDistributionRecalc();
    364 }
    365 
    366 void ElementShadow::clearDistribution()
    367 {
    368     m_nodeToInsertionPoints.clear();
    369 
    370     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
    371         root->setShadowInsertionPointOfYoungerShadowRoot(0);
    372 }
    373 
    374 } // namespace
    375