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  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "core/dom/shadow/InsertionPoint.h"
     33 
     34 #include "core/HTMLNames.h"
     35 #include "core/dom/ElementTraversal.h"
     36 #include "core/dom/QualifiedName.h"
     37 #include "core/dom/StaticNodeList.h"
     38 #include "core/dom/shadow/ElementShadow.h"
     39 
     40 namespace WebCore {
     41 
     42 using namespace HTMLNames;
     43 
     44 InsertionPoint::InsertionPoint(const QualifiedName& tagName, Document& document)
     45     : HTMLElement(tagName, document, CreateInsertionPoint)
     46     , m_registeredWithShadowRoot(false)
     47 {
     48     setHasCustomStyleCallbacks();
     49 }
     50 
     51 InsertionPoint::~InsertionPoint()
     52 {
     53 }
     54 
     55 void InsertionPoint::setDistribution(ContentDistribution& distribution)
     56 {
     57     if (shouldUseFallbackElements()) {
     58         for (Node* child = firstChild(); child; child = child->nextSibling())
     59             child->lazyReattachIfAttached();
     60     }
     61 
     62     // Attempt not to reattach nodes that would be distributed to the exact same
     63     // location by comparing the old and new distributions.
     64 
     65     size_t i = 0;
     66     size_t j = 0;
     67 
     68     for ( ; i < m_distribution.size() && j < distribution.size(); ++i, ++j) {
     69         if (m_distribution.size() < distribution.size()) {
     70             // If the new distribution is larger than the old one, reattach all nodes in
     71             // the new distribution that were inserted.
     72             for ( ; j < distribution.size() && m_distribution.at(i) != distribution.at(j); ++j)
     73                 distribution.at(j)->lazyReattachIfAttached();
     74         } else if (m_distribution.size() > distribution.size()) {
     75             // If the old distribution is larger than the new one, reattach all nodes in
     76             // the old distribution that were removed.
     77             for ( ; i < m_distribution.size() && m_distribution.at(i) != distribution.at(j); ++i)
     78                 m_distribution.at(i)->lazyReattachIfAttached();
     79         } else if (m_distribution.at(i) != distribution.at(j)) {
     80             // If both distributions are the same length reattach both old and new.
     81             m_distribution.at(i)->lazyReattachIfAttached();
     82             distribution.at(j)->lazyReattachIfAttached();
     83         }
     84     }
     85 
     86     // If we hit the end of either list above we need to reattach all remaining nodes.
     87 
     88     for ( ; i < m_distribution.size(); ++i)
     89         m_distribution.at(i)->lazyReattachIfAttached();
     90 
     91     for ( ; j < distribution.size(); ++j)
     92         distribution.at(j)->lazyReattachIfAttached();
     93 
     94     m_distribution.swap(distribution);
     95     m_distribution.shrinkToFit();
     96 }
     97 
     98 void InsertionPoint::attach(const AttachContext& context)
     99 {
    100     // We need to attach the distribution here so that they're inserted in the right order
    101     // otherwise the n^2 protection inside RenderTreeBuilder will cause them to be
    102     // inserted in the wrong place later. This also lets distributed nodes benefit from
    103     // the n^2 protection.
    104     for (size_t i = 0; i < m_distribution.size(); ++i) {
    105         if (m_distribution.at(i)->needsAttach())
    106             m_distribution.at(i)->attach(context);
    107     }
    108 
    109     HTMLElement::attach(context);
    110 }
    111 
    112 void InsertionPoint::detach(const AttachContext& context)
    113 {
    114     for (size_t i = 0; i < m_distribution.size(); ++i)
    115         m_distribution.at(i)->lazyReattachIfAttached();
    116 
    117     HTMLElement::detach(context);
    118 }
    119 
    120 void InsertionPoint::willRecalcStyle(StyleRecalcChange change)
    121 {
    122     if (change < Inherit)
    123         return;
    124     for (size_t i = 0; i < m_distribution.size(); ++i)
    125         m_distribution.at(i)->setNeedsStyleRecalc(SubtreeStyleChange);
    126 }
    127 
    128 bool InsertionPoint::shouldUseFallbackElements() const
    129 {
    130     return isActive() && !hasDistribution();
    131 }
    132 
    133 bool InsertionPoint::canBeActive() const
    134 {
    135     if (!isInShadowTree())
    136         return false;
    137     for (Node* node = parentNode(); node; node = node->parentNode()) {
    138         if (node->isInsertionPoint())
    139             return false;
    140     }
    141     return true;
    142 }
    143 
    144 bool InsertionPoint::isActive() const
    145 {
    146     if (!canBeActive())
    147         return false;
    148     ShadowRoot* shadowRoot = containingShadowRoot();
    149     if (!shadowRoot)
    150         return false;
    151     if (!isHTMLShadowElement(*this) || shadowRoot->descendantShadowElementCount() <= 1)
    152         return true;
    153 
    154     // Slow path only when there are more than one shadow elements in a shadow tree. That should be a rare case.
    155     const WillBeHeapVector<RefPtrWillBeMember<InsertionPoint> >& insertionPoints = shadowRoot->descendantInsertionPoints();
    156     for (size_t i = 0; i < insertionPoints.size(); ++i) {
    157         InsertionPoint* point = insertionPoints[i].get();
    158         if (isHTMLShadowElement(*point))
    159             return point == this;
    160     }
    161     return true;
    162 }
    163 
    164 bool InsertionPoint::isShadowInsertionPoint() const
    165 {
    166     return isHTMLShadowElement(*this) && isActive();
    167 }
    168 
    169 bool InsertionPoint::isContentInsertionPoint() const
    170 {
    171     return isHTMLContentElement(*this) && isActive();
    172 }
    173 
    174 PassRefPtrWillBeRawPtr<StaticNodeList> InsertionPoint::getDistributedNodes()
    175 {
    176     document().updateDistributionForNodeIfNeeded(this);
    177 
    178     WillBeHeapVector<RefPtrWillBeMember<Node> > nodes;
    179     nodes.reserveInitialCapacity(m_distribution.size());
    180     for (size_t i = 0; i < m_distribution.size(); ++i)
    181         nodes.uncheckedAppend(m_distribution.at(i));
    182 
    183     return StaticNodeList::adopt(nodes);
    184 }
    185 
    186 bool InsertionPoint::rendererIsNeeded(const RenderStyle& style)
    187 {
    188     return !isActive() && HTMLElement::rendererIsNeeded(style);
    189 }
    190 
    191 void InsertionPoint::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
    192 {
    193     HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
    194     if (ShadowRoot* root = containingShadowRoot()) {
    195         if (ElementShadow* rootOwner = root->owner())
    196             rootOwner->setNeedsDistributionRecalc();
    197     }
    198 }
    199 
    200 Node::InsertionNotificationRequest InsertionPoint::insertedInto(ContainerNode* insertionPoint)
    201 {
    202     HTMLElement::insertedInto(insertionPoint);
    203     if (ShadowRoot* root = containingShadowRoot()) {
    204         if (ElementShadow* rootOwner = root->owner()) {
    205             rootOwner->setNeedsDistributionRecalc();
    206             if (canBeActive() && !m_registeredWithShadowRoot && insertionPoint->treeScope().rootNode() == root) {
    207                 m_registeredWithShadowRoot = true;
    208                 root->didAddInsertionPoint(this);
    209                 if (canAffectSelector())
    210                     rootOwner->willAffectSelector();
    211             }
    212         }
    213     }
    214 
    215     return InsertionDone;
    216 }
    217 
    218 void InsertionPoint::removedFrom(ContainerNode* insertionPoint)
    219 {
    220     ShadowRoot* root = containingShadowRoot();
    221     if (!root)
    222         root = insertionPoint->containingShadowRoot();
    223 
    224     if (root) {
    225         if (ElementShadow* rootOwner = root->owner())
    226             rootOwner->setNeedsDistributionRecalc();
    227     }
    228 
    229     // host can be null when removedFrom() is called from ElementShadow destructor.
    230     ElementShadow* rootOwner = root ? root->owner() : 0;
    231 
    232     // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up.
    233     clearDistribution();
    234 
    235     if (m_registeredWithShadowRoot && insertionPoint->treeScope().rootNode() == root) {
    236         ASSERT(root);
    237         m_registeredWithShadowRoot = false;
    238         root->didRemoveInsertionPoint(this);
    239         if (rootOwner) {
    240             if (canAffectSelector())
    241                 rootOwner->willAffectSelector();
    242         }
    243     }
    244 
    245     HTMLElement::removedFrom(insertionPoint);
    246 }
    247 
    248 void InsertionPoint::trace(Visitor* visitor)
    249 {
    250     visitor->trace(m_distribution);
    251     HTMLElement::trace(visitor);
    252 }
    253 
    254 const InsertionPoint* resolveReprojection(const Node* projectedNode)
    255 {
    256     ASSERT(projectedNode);
    257     const InsertionPoint* insertionPoint = 0;
    258     const Node* current = projectedNode;
    259     ElementShadow* lastElementShadow = 0;
    260     while (true) {
    261         ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*current);
    262         if (!shadow || shadow == lastElementShadow)
    263             break;
    264         lastElementShadow = shadow;
    265         const InsertionPoint* insertedTo = shadow->finalDestinationInsertionPointFor(projectedNode);
    266         if (!insertedTo)
    267             break;
    268         ASSERT(current != insertedTo);
    269         current = insertedTo;
    270         insertionPoint = insertedTo;
    271     }
    272     return insertionPoint;
    273 }
    274 
    275 void collectDestinationInsertionPoints(const Node& node, WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8>& results)
    276 {
    277     const Node* current = &node;
    278     ElementShadow* lastElementShadow = 0;
    279     while (true) {
    280         ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*current);
    281         if (!shadow || shadow == lastElementShadow)
    282             return;
    283         lastElementShadow = shadow;
    284         const DestinationInsertionPoints* insertionPoints = shadow->destinationInsertionPointsFor(&node);
    285         if (!insertionPoints)
    286             return;
    287         for (size_t i = 0; i < insertionPoints->size(); ++i)
    288             results.append(insertionPoints->at(i).get());
    289         ASSERT(current != insertionPoints->last().get());
    290         current = insertionPoints->last().get();
    291     }
    292 }
    293 
    294 } // namespace WebCore
    295