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