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/css/StyleSheetList.h"
     31 #include "core/dom/ContainerNodeAlgorithms.h"
     32 #include "core/dom/ElementTraversal.h"
     33 #include "core/dom/NodeTraversal.h"
     34 #include "core/dom/shadow/ContentDistribution.h"
     35 #include "core/html/HTMLContentElement.h"
     36 #include "core/html/HTMLShadowElement.h"
     37 #include "core/inspector/InspectorInstrumentation.h"
     38 
     39 namespace WebCore {
     40 
     41 class DistributionPool FINAL {
     42     STACK_ALLOCATED();
     43 public:
     44     explicit DistributionPool(const ContainerNode&);
     45     void clear();
     46     ~DistributionPool();
     47     void distributeTo(InsertionPoint*, ElementShadow*);
     48     void populateChildren(const ContainerNode&);
     49 
     50 private:
     51     void detachNonDistributedNodes();
     52     WillBeHeapVector<RawPtrWillBeMember<Node>, 32> m_nodes;
     53     Vector<bool, 32> m_distributed;
     54 };
     55 
     56 inline DistributionPool::DistributionPool(const ContainerNode& parent)
     57 {
     58     populateChildren(parent);
     59 }
     60 
     61 inline void DistributionPool::clear()
     62 {
     63     detachNonDistributedNodes();
     64     m_nodes.clear();
     65     m_distributed.clear();
     66 }
     67 
     68 inline void DistributionPool::populateChildren(const ContainerNode& parent)
     69 {
     70     clear();
     71     for (Node* child = parent.firstChild(); child; child = child->nextSibling()) {
     72         if (isActiveInsertionPoint(*child)) {
     73             InsertionPoint* insertionPoint = toInsertionPoint(child);
     74             for (size_t i = 0; i < insertionPoint->size(); ++i)
     75                 m_nodes.append(insertionPoint->at(i));
     76         } else {
     77             m_nodes.append(child);
     78         }
     79     }
     80     m_distributed.resize(m_nodes.size());
     81     m_distributed.fill(false);
     82 }
     83 
     84 void DistributionPool::distributeTo(InsertionPoint* insertionPoint, ElementShadow* elementShadow)
     85 {
     86     ContentDistribution distribution;
     87 
     88     for (size_t i = 0; i < m_nodes.size(); ++i) {
     89         if (m_distributed[i])
     90             continue;
     91 
     92         if (isHTMLContentElement(*insertionPoint) && !toHTMLContentElement(insertionPoint)->canSelectNode(m_nodes, i))
     93             continue;
     94 
     95         Node* node = m_nodes[i];
     96         distribution.append(node);
     97         elementShadow->didDistributeNode(node, insertionPoint);
     98         m_distributed[i] = true;
     99     }
    100 
    101     // Distributes fallback elements
    102     if (insertionPoint->isContentInsertionPoint() && distribution.isEmpty()) {
    103         for (Node* fallbackNode = insertionPoint->firstChild(); fallbackNode; fallbackNode = fallbackNode->nextSibling()) {
    104             distribution.append(fallbackNode);
    105             elementShadow->didDistributeNode(fallbackNode, insertionPoint);
    106         }
    107     }
    108     insertionPoint->setDistribution(distribution);
    109 }
    110 
    111 inline DistributionPool::~DistributionPool()
    112 {
    113     detachNonDistributedNodes();
    114 }
    115 
    116 inline void DistributionPool::detachNonDistributedNodes()
    117 {
    118     for (size_t i = 0; i < m_nodes.size(); ++i) {
    119         if (m_distributed[i])
    120             continue;
    121         if (m_nodes[i]->renderer())
    122             m_nodes[i]->lazyReattachIfAttached();
    123     }
    124 }
    125 
    126 PassOwnPtrWillBeRawPtr<ElementShadow> ElementShadow::create()
    127 {
    128     return adoptPtrWillBeNoop(new ElementShadow());
    129 }
    130 
    131 ElementShadow::ElementShadow()
    132     : m_needsDistributionRecalc(false)
    133     , m_needsSelectFeatureSet(false)
    134 {
    135 }
    136 
    137 ElementShadow::~ElementShadow()
    138 {
    139 #if !ENABLE(OILPAN)
    140     removeDetachedShadowRoots();
    141 #endif
    142 }
    143 
    144 ShadowRoot& ElementShadow::addShadowRoot(Element& shadowHost, ShadowRoot::ShadowRootType type)
    145 {
    146     RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = ShadowRoot::create(shadowHost.document(), type);
    147 
    148     if (type == ShadowRoot::AuthorShadowRoot && (!youngestShadowRoot() || youngestShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot))
    149         shadowHost.willAddFirstAuthorShadowRoot();
    150 
    151     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
    152         root->lazyReattachIfAttached();
    153 
    154     shadowRoot->setParentOrShadowHostNode(&shadowHost);
    155     shadowRoot->setParentTreeScope(shadowHost.treeScope());
    156     m_shadowRoots.push(shadowRoot.get());
    157     shadowHost.notifyNodeInserted(*shadowRoot);
    158     setNeedsDistributionRecalc();
    159 
    160     InspectorInstrumentation::didPushShadowRoot(&shadowHost, shadowRoot.get());
    161 
    162     ASSERT(m_shadowRoots.head());
    163     ASSERT(shadowRoot.get() == m_shadowRoots.head());
    164     return *m_shadowRoots.head();
    165 }
    166 
    167 #if !ENABLE(OILPAN)
    168 void ElementShadow::removeDetachedShadowRoots()
    169 {
    170     // Dont protect this ref count.
    171     Element* shadowHost = host();
    172     ASSERT(shadowHost);
    173 
    174     while (RefPtrWillBeRawPtr<ShadowRoot> oldRoot = m_shadowRoots.head()) {
    175         InspectorInstrumentation::willPopShadowRoot(shadowHost, oldRoot.get());
    176         shadowHost->document().removeFocusedElementOfSubtree(oldRoot.get());
    177         m_shadowRoots.removeHead();
    178         oldRoot->setParentOrShadowHostNode(0);
    179         oldRoot->setParentTreeScope(shadowHost->document());
    180         oldRoot->setPrev(0);
    181         oldRoot->setNext(0);
    182     }
    183 }
    184 #endif
    185 
    186 void ElementShadow::attach(const Node::AttachContext& context)
    187 {
    188     Node::AttachContext childrenContext(context);
    189     childrenContext.resolvedStyle = 0;
    190 
    191     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
    192         if (root->needsAttach())
    193             root->attach(childrenContext);
    194     }
    195 }
    196 
    197 void ElementShadow::detach(const Node::AttachContext& context)
    198 {
    199     Node::AttachContext childrenContext(context);
    200     childrenContext.resolvedStyle = 0;
    201 
    202     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
    203         root->detach(childrenContext);
    204 }
    205 
    206 void ElementShadow::setNeedsDistributionRecalc()
    207 {
    208     if (m_needsDistributionRecalc)
    209         return;
    210     m_needsDistributionRecalc = true;
    211     host()->markAncestorsWithChildNeedsDistributionRecalc();
    212     clearDistribution();
    213 }
    214 
    215 bool ElementShadow::hasSameStyles(const ElementShadow* other) const
    216 {
    217     ShadowRoot* root = youngestShadowRoot();
    218     ShadowRoot* otherRoot = other->youngestShadowRoot();
    219     while (root || otherRoot) {
    220         if (!root || !otherRoot)
    221             return false;
    222 
    223         StyleSheetList* list = root->styleSheets();
    224         StyleSheetList* otherList = otherRoot->styleSheets();
    225 
    226         if (list->length() != otherList->length())
    227             return false;
    228 
    229         for (size_t i = 0; i < list->length(); i++) {
    230             if (toCSSStyleSheet(list->item(i))->contents() != toCSSStyleSheet(otherList->item(i))->contents())
    231                 return false;
    232         }
    233         root = root->olderShadowRoot();
    234         otherRoot = otherRoot->olderShadowRoot();
    235     }
    236 
    237     return true;
    238 }
    239 
    240 const InsertionPoint* ElementShadow::finalDestinationInsertionPointFor(const Node* key) const
    241 {
    242     ASSERT(key && !key->document().childNeedsDistributionRecalc());
    243     NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key);
    244     return it == m_nodeToInsertionPoints.end() ? 0: it->value.last().get();
    245 }
    246 
    247 const DestinationInsertionPoints* ElementShadow::destinationInsertionPointsFor(const Node* key) const
    248 {
    249     ASSERT(key && !key->document().childNeedsDistributionRecalc());
    250     NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key);
    251     return it == m_nodeToInsertionPoints.end() ? 0: &it->value;
    252 }
    253 
    254 void ElementShadow::distribute()
    255 {
    256     host()->setNeedsStyleRecalc(SubtreeStyleChange);
    257     Vector<HTMLShadowElement*, 32> shadowInsertionPoints;
    258     DistributionPool pool(*host());
    259 
    260     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
    261         HTMLShadowElement* shadowInsertionPoint = 0;
    262         const WillBeHeapVector<RefPtrWillBeMember<InsertionPoint> >& insertionPoints = root->descendantInsertionPoints();
    263         for (size_t i = 0; i < insertionPoints.size(); ++i) {
    264             InsertionPoint* point = insertionPoints[i].get();
    265             if (!point->isActive())
    266                 continue;
    267             if (isHTMLShadowElement(*point)) {
    268                 ASSERT(!shadowInsertionPoint);
    269                 shadowInsertionPoint = toHTMLShadowElement(point);
    270                 shadowInsertionPoints.append(shadowInsertionPoint);
    271             } else {
    272                 pool.distributeTo(point, this);
    273                 if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*point))
    274                     shadow->setNeedsDistributionRecalc();
    275             }
    276         }
    277     }
    278 
    279     for (size_t i = shadowInsertionPoints.size(); i > 0; --i) {
    280         HTMLShadowElement* shadowInsertionPoint = shadowInsertionPoints[i - 1];
    281         ShadowRoot* root = shadowInsertionPoint->containingShadowRoot();
    282         ASSERT(root);
    283         if (root->isOldest()) {
    284             pool.distributeTo(shadowInsertionPoint, this);
    285         } else if (root->olderShadowRoot()->type() == root->type()) {
    286             // Only allow reprojecting older shadow roots between the same type to
    287             // disallow reprojecting UA elements into author shadows.
    288             DistributionPool olderShadowRootPool(*root->olderShadowRoot());
    289             olderShadowRootPool.distributeTo(shadowInsertionPoint, this);
    290             root->olderShadowRoot()->setShadowInsertionPointOfYoungerShadowRoot(shadowInsertionPoint);
    291         }
    292         if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*shadowInsertionPoint))
    293             shadow->setNeedsDistributionRecalc();
    294     }
    295 }
    296 
    297 void ElementShadow::didDistributeNode(const Node* node, InsertionPoint* insertionPoint)
    298 {
    299     NodeToDestinationInsertionPoints::AddResult result = m_nodeToInsertionPoints.add(node, DestinationInsertionPoints());
    300     result.storedValue->value.append(insertionPoint);
    301 }
    302 
    303 const SelectRuleFeatureSet& ElementShadow::ensureSelectFeatureSet()
    304 {
    305     if (!m_needsSelectFeatureSet)
    306         return m_selectFeatures;
    307 
    308     m_selectFeatures.clear();
    309     for (ShadowRoot* root = oldestShadowRoot(); root; root = root->youngerShadowRoot())
    310         collectSelectFeatureSetFrom(*root);
    311     m_needsSelectFeatureSet = false;
    312     return m_selectFeatures;
    313 }
    314 
    315 void ElementShadow::collectSelectFeatureSetFrom(ShadowRoot& root)
    316 {
    317     if (!root.containsShadowRoots() && !root.containsContentElements())
    318         return;
    319 
    320     for (Element* element = ElementTraversal::firstWithin(root); element; element = ElementTraversal::next(*element, &root)) {
    321         if (ElementShadow* shadow = element->shadow())
    322             m_selectFeatures.add(shadow->ensureSelectFeatureSet());
    323         if (!isHTMLContentElement(*element))
    324             continue;
    325         const CSSSelectorList& list = toHTMLContentElement(*element).selectorList();
    326         for (const CSSSelector* selector = list.first(); selector; selector = CSSSelectorList::next(*selector)) {
    327             for (const CSSSelector* component = selector; component; component = component->tagHistory())
    328                 m_selectFeatures.collectFeaturesFromSelector(*component);
    329         }
    330     }
    331 }
    332 
    333 void ElementShadow::didAffectSelector(AffectedSelectorMask mask)
    334 {
    335     if (ensureSelectFeatureSet().hasSelectorFor(mask))
    336         setNeedsDistributionRecalc();
    337 }
    338 
    339 void ElementShadow::willAffectSelector()
    340 {
    341     for (ElementShadow* shadow = this; shadow; shadow = shadow->containingShadow()) {
    342         if (shadow->needsSelectFeatureSet())
    343             break;
    344         shadow->setNeedsSelectFeatureSet();
    345     }
    346     setNeedsDistributionRecalc();
    347 }
    348 
    349 void ElementShadow::clearDistribution()
    350 {
    351     m_nodeToInsertionPoints.clear();
    352 
    353     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
    354         root->setShadowInsertionPointOfYoungerShadowRoot(nullptr);
    355 }
    356 
    357 void ElementShadow::trace(Visitor* visitor)
    358 {
    359     visitor->trace(m_nodeToInsertionPoints);
    360     visitor->trace(m_selectFeatures);
    361     // Shadow roots are linked with previous and next pointers which are traced.
    362     // It is therefore enough to trace one of the shadow roots here and the
    363     // rest will be traced from there.
    364     visitor->trace(m_shadowRoots.head());
    365 }
    366 
    367 } // namespace
    368