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