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