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/Document.h"
     36 #include "core/dom/ElementTraversal.h"
     37 #include "core/dom/QualifiedName.h"
     38 #include "core/dom/StaticNodeList.h"
     39 #include "core/dom/shadow/ElementShadow.h"
     40 
     41 namespace blink {
     42 
     43 using namespace HTMLNames;
     44 
     45 InsertionPoint::InsertionPoint(const QualifiedName& tagName, Document& document)
     46     : HTMLElement(tagName, document, CreateInsertionPoint)
     47     , m_registeredWithShadowRoot(false)
     48 {
     49     setHasCustomStyleCallbacks();
     50 }
     51 
     52 InsertionPoint::~InsertionPoint()
     53 {
     54 }
     55 
     56 void InsertionPoint::setDistribution(ContentDistribution& distribution)
     57 {
     58     if (shouldUseFallbackElements()) {
     59         for (Node* child = firstChild(); child; child = child->nextSibling())
     60             child->lazyReattachIfAttached();
     61     }
     62 
     63     // Attempt not to reattach nodes that would be distributed to the exact same
     64     // location by comparing the old and new distributions.
     65 
     66     size_t i = 0;
     67     size_t j = 0;
     68 
     69     for ( ; i < m_distribution.size() && j < distribution.size(); ++i, ++j) {
     70         if (m_distribution.size() < distribution.size()) {
     71             // If the new distribution is larger than the old one, reattach all nodes in
     72             // the new distribution that were inserted.
     73             for ( ; j < distribution.size() && m_distribution.at(i) != distribution.at(j); ++j)
     74                 distribution.at(j)->lazyReattachIfAttached();
     75         } else if (m_distribution.size() > distribution.size()) {
     76             // If the old distribution is larger than the new one, reattach all nodes in
     77             // the old distribution that were removed.
     78             for ( ; i < m_distribution.size() && m_distribution.at(i) != distribution.at(j); ++i)
     79                 m_distribution.at(i)->lazyReattachIfAttached();
     80         } else if (m_distribution.at(i) != distribution.at(j)) {
     81             // If both distributions are the same length reattach both old and new.
     82             m_distribution.at(i)->lazyReattachIfAttached();
     83             distribution.at(j)->lazyReattachIfAttached();
     84         }
     85     }
     86 
     87     // If we hit the end of either list above we need to reattach all remaining nodes.
     88 
     89     for ( ; i < m_distribution.size(); ++i)
     90         m_distribution.at(i)->lazyReattachIfAttached();
     91 
     92     for ( ; j < distribution.size(); ++j)
     93         distribution.at(j)->lazyReattachIfAttached();
     94 
     95     m_distribution.swap(distribution);
     96     m_distribution.shrinkToFit();
     97 }
     98 
     99 void InsertionPoint::attach(const AttachContext& context)
    100 {
    101     // We need to attach the distribution here so that they're inserted in the right order
    102     // otherwise the n^2 protection inside RenderTreeBuilder will cause them to be
    103     // inserted in the wrong place later. This also lets distributed nodes benefit from
    104     // the n^2 protection.
    105     for (size_t i = 0; i < m_distribution.size(); ++i) {
    106         if (m_distribution.at(i)->needsAttach())
    107             m_distribution.at(i)->attach(context);
    108     }
    109 
    110     HTMLElement::attach(context);
    111 }
    112 
    113 void InsertionPoint::detach(const AttachContext& context)
    114 {
    115     for (size_t i = 0; i < m_distribution.size(); ++i)
    116         m_distribution.at(i)->lazyReattachIfAttached();
    117 
    118     HTMLElement::detach(context);
    119 }
    120 
    121 void InsertionPoint::willRecalcStyle(StyleRecalcChange change)
    122 {
    123     if (change < Inherit)
    124         return;
    125     for (size_t i = 0; i < m_distribution.size(); ++i)
    126         m_distribution.at(i)->setNeedsStyleRecalc(SubtreeStyleChange);
    127 }
    128 
    129 bool InsertionPoint::shouldUseFallbackElements() const
    130 {
    131     return isActive() && !hasDistribution();
    132 }
    133 
    134 bool InsertionPoint::canBeActive() const
    135 {
    136     if (!isInShadowTree())
    137         return false;
    138     return !Traversal<InsertionPoint>::firstAncestor(*this);
    139 }
    140 
    141 bool InsertionPoint::isActive() const
    142 {
    143     if (!canBeActive())
    144         return false;
    145     ShadowRoot* shadowRoot = containingShadowRoot();
    146     if (!shadowRoot)
    147         return false;
    148     if (!isHTMLShadowElement(*this) || shadowRoot->descendantShadowElementCount() <= 1)
    149         return true;
    150 
    151     // Slow path only when there are more than one shadow elements in a shadow tree. That should be a rare case.
    152     const WillBeHeapVector<RefPtrWillBeMember<InsertionPoint> >& insertionPoints = shadowRoot->descendantInsertionPoints();
    153     for (size_t i = 0; i < insertionPoints.size(); ++i) {
    154         InsertionPoint* point = insertionPoints[i].get();
    155         if (isHTMLShadowElement(*point))
    156             return point == this;
    157     }
    158     return true;
    159 }
    160 
    161 bool InsertionPoint::isShadowInsertionPoint() const
    162 {
    163     return isHTMLShadowElement(*this) && isActive();
    164 }
    165 
    166 bool InsertionPoint::isContentInsertionPoint() const
    167 {
    168     return isHTMLContentElement(*this) && isActive();
    169 }
    170 
    171 PassRefPtrWillBeRawPtr<StaticNodeList> InsertionPoint::getDistributedNodes()
    172 {
    173     document().updateDistributionForNodeIfNeeded(this);
    174 
    175     WillBeHeapVector<RefPtrWillBeMember<Node> > nodes;
    176     nodes.reserveInitialCapacity(m_distribution.size());
    177     for (size_t i = 0; i < m_distribution.size(); ++i)
    178         nodes.uncheckedAppend(m_distribution.at(i));
    179 
    180     return StaticNodeList::adopt(nodes);
    181 }
    182 
    183 bool InsertionPoint::rendererIsNeeded(const RenderStyle& style)
    184 {
    185     return !isActive() && HTMLElement::rendererIsNeeded(style);
    186 }
    187 
    188 void InsertionPoint::childrenChanged(const ChildrenChange& change)
    189 {
    190     HTMLElement::childrenChanged(change);
    191     if (ShadowRoot* root = containingShadowRoot()) {
    192         if (ElementShadow* rootOwner = root->owner())
    193             rootOwner->setNeedsDistributionRecalc();
    194     }
    195 }
    196 
    197 Node::InsertionNotificationRequest InsertionPoint::insertedInto(ContainerNode* insertionPoint)
    198 {
    199     HTMLElement::insertedInto(insertionPoint);
    200     if (ShadowRoot* root = containingShadowRoot()) {
    201         if (ElementShadow* rootOwner = root->owner()) {
    202             rootOwner->setNeedsDistributionRecalc();
    203             if (canBeActive() && !m_registeredWithShadowRoot && insertionPoint->treeScope().rootNode() == root) {
    204                 m_registeredWithShadowRoot = true;
    205                 root->didAddInsertionPoint(this);
    206                 if (canAffectSelector())
    207                     rootOwner->willAffectSelector();
    208             }
    209         }
    210     }
    211 
    212     return InsertionDone;
    213 }
    214 
    215 void InsertionPoint::removedFrom(ContainerNode* insertionPoint)
    216 {
    217     ShadowRoot* root = containingShadowRoot();
    218     if (!root)
    219         root = insertionPoint->containingShadowRoot();
    220 
    221     if (root) {
    222         if (ElementShadow* rootOwner = root->owner())
    223             rootOwner->setNeedsDistributionRecalc();
    224     }
    225 
    226     // host can be null when removedFrom() is called from ElementShadow destructor.
    227     ElementShadow* rootOwner = root ? root->owner() : 0;
    228 
    229     // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up.
    230     clearDistribution();
    231 
    232     if (m_registeredWithShadowRoot && insertionPoint->treeScope().rootNode() == root) {
    233         ASSERT(root);
    234         m_registeredWithShadowRoot = false;
    235         root->didRemoveInsertionPoint(this);
    236         if (rootOwner) {
    237             if (canAffectSelector())
    238                 rootOwner->willAffectSelector();
    239         }
    240     }
    241 
    242     HTMLElement::removedFrom(insertionPoint);
    243 }
    244 
    245 void InsertionPoint::trace(Visitor* visitor)
    246 {
    247     visitor->trace(m_distribution);
    248     HTMLElement::trace(visitor);
    249 }
    250 
    251 const InsertionPoint* resolveReprojection(const Node* projectedNode)
    252 {
    253     ASSERT(projectedNode);
    254     const InsertionPoint* insertionPoint = 0;
    255     const Node* current = projectedNode;
    256     ElementShadow* lastElementShadow = 0;
    257     while (true) {
    258         ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*current);
    259         if (!shadow || shadow == lastElementShadow)
    260             break;
    261         lastElementShadow = shadow;
    262         const InsertionPoint* insertedTo = shadow->finalDestinationInsertionPointFor(projectedNode);
    263         if (!insertedTo)
    264             break;
    265         ASSERT(current != insertedTo);
    266         current = insertedTo;
    267         insertionPoint = insertedTo;
    268     }
    269     return insertionPoint;
    270 }
    271 
    272 void collectDestinationInsertionPoints(const Node& node, WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8>& results)
    273 {
    274     const Node* current = &node;
    275     ElementShadow* lastElementShadow = 0;
    276     while (true) {
    277         ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*current);
    278         if (!shadow || shadow == lastElementShadow)
    279             return;
    280         lastElementShadow = shadow;
    281         const DestinationInsertionPoints* insertionPoints = shadow->destinationInsertionPointsFor(&node);
    282         if (!insertionPoints)
    283             return;
    284         for (size_t i = 0; i < insertionPoints->size(); ++i)
    285             results.append(insertionPoints->at(i).get());
    286         ASSERT(current != insertionPoints->last().get());
    287         current = insertionPoints->last().get();
    288     }
    289 }
    290 
    291 } // namespace blink
    292