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 * * Neither the name of Google Inc. nor the names of its 11 * contributors may be used to endorse or promote products derived from 12 * this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 #include "core/dom/shadow/ElementShadow.h" 29 30 #include "core/css/StyleSheetList.h" 31 #include "core/dom/ContainerNodeAlgorithms.h" 32 #include "core/dom/ElementTraversal.h" 33 #include "core/dom/NodeTraversal.h" 34 #include "core/dom/shadow/ContentDistribution.h" 35 #include "core/html/HTMLContentElement.h" 36 #include "core/html/HTMLShadowElement.h" 37 #include "core/inspector/InspectorInstrumentation.h" 38 39 namespace WebCore { 40 41 class DistributionPool FINAL { 42 STACK_ALLOCATED(); 43 public: 44 explicit DistributionPool(const ContainerNode&); 45 void clear(); 46 ~DistributionPool(); 47 void distributeTo(InsertionPoint*, ElementShadow*); 48 void populateChildren(const ContainerNode&); 49 50 private: 51 void detachNonDistributedNodes(); 52 WillBeHeapVector<RawPtrWillBeMember<Node>, 32> m_nodes; 53 Vector<bool, 32> m_distributed; 54 }; 55 56 inline DistributionPool::DistributionPool(const ContainerNode& parent) 57 { 58 populateChildren(parent); 59 } 60 61 inline void DistributionPool::clear() 62 { 63 detachNonDistributedNodes(); 64 m_nodes.clear(); 65 m_distributed.clear(); 66 } 67 68 inline void DistributionPool::populateChildren(const ContainerNode& parent) 69 { 70 clear(); 71 for (Node* child = parent.firstChild(); child; child = child->nextSibling()) { 72 if (isActiveInsertionPoint(*child)) { 73 InsertionPoint* insertionPoint = toInsertionPoint(child); 74 for (size_t i = 0; i < insertionPoint->size(); ++i) 75 m_nodes.append(insertionPoint->at(i)); 76 } else { 77 m_nodes.append(child); 78 } 79 } 80 m_distributed.resize(m_nodes.size()); 81 m_distributed.fill(false); 82 } 83 84 void DistributionPool::distributeTo(InsertionPoint* insertionPoint, ElementShadow* elementShadow) 85 { 86 ContentDistribution distribution; 87 88 for (size_t i = 0; i < m_nodes.size(); ++i) { 89 if (m_distributed[i]) 90 continue; 91 92 if (isHTMLContentElement(*insertionPoint) && !toHTMLContentElement(insertionPoint)->canSelectNode(m_nodes, i)) 93 continue; 94 95 Node* node = m_nodes[i]; 96 distribution.append(node); 97 elementShadow->didDistributeNode(node, insertionPoint); 98 m_distributed[i] = true; 99 } 100 101 // Distributes fallback elements 102 if (insertionPoint->isContentInsertionPoint() && distribution.isEmpty()) { 103 for (Node* fallbackNode = insertionPoint->firstChild(); fallbackNode; fallbackNode = fallbackNode->nextSibling()) { 104 distribution.append(fallbackNode); 105 elementShadow->didDistributeNode(fallbackNode, insertionPoint); 106 } 107 } 108 insertionPoint->setDistribution(distribution); 109 } 110 111 inline DistributionPool::~DistributionPool() 112 { 113 detachNonDistributedNodes(); 114 } 115 116 inline void DistributionPool::detachNonDistributedNodes() 117 { 118 for (size_t i = 0; i < m_nodes.size(); ++i) { 119 if (m_distributed[i]) 120 continue; 121 if (m_nodes[i]->renderer()) 122 m_nodes[i]->lazyReattachIfAttached(); 123 } 124 } 125 126 PassOwnPtrWillBeRawPtr<ElementShadow> ElementShadow::create() 127 { 128 return adoptPtrWillBeNoop(new ElementShadow()); 129 } 130 131 ElementShadow::ElementShadow() 132 : m_needsDistributionRecalc(false) 133 , m_needsSelectFeatureSet(false) 134 { 135 } 136 137 ElementShadow::~ElementShadow() 138 { 139 #if !ENABLE(OILPAN) 140 removeDetachedShadowRoots(); 141 #endif 142 } 143 144 ShadowRoot& ElementShadow::addShadowRoot(Element& shadowHost, ShadowRoot::ShadowRootType type) 145 { 146 RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = ShadowRoot::create(shadowHost.document(), type); 147 148 if (type == ShadowRoot::AuthorShadowRoot && (!youngestShadowRoot() || youngestShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot)) 149 shadowHost.willAddFirstAuthorShadowRoot(); 150 151 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) 152 root->lazyReattachIfAttached(); 153 154 shadowRoot->setParentOrShadowHostNode(&shadowHost); 155 shadowRoot->setParentTreeScope(shadowHost.treeScope()); 156 m_shadowRoots.push(shadowRoot.get()); 157 shadowHost.notifyNodeInserted(*shadowRoot); 158 setNeedsDistributionRecalc(); 159 160 InspectorInstrumentation::didPushShadowRoot(&shadowHost, shadowRoot.get()); 161 162 ASSERT(m_shadowRoots.head()); 163 ASSERT(shadowRoot.get() == m_shadowRoots.head()); 164 return *m_shadowRoots.head(); 165 } 166 167 #if !ENABLE(OILPAN) 168 void ElementShadow::removeDetachedShadowRoots() 169 { 170 // Dont protect this ref count. 171 Element* shadowHost = host(); 172 ASSERT(shadowHost); 173 174 while (RefPtrWillBeRawPtr<ShadowRoot> oldRoot = m_shadowRoots.head()) { 175 InspectorInstrumentation::willPopShadowRoot(shadowHost, oldRoot.get()); 176 shadowHost->document().removeFocusedElementOfSubtree(oldRoot.get()); 177 m_shadowRoots.removeHead(); 178 oldRoot->setParentOrShadowHostNode(0); 179 oldRoot->setParentTreeScope(shadowHost->document()); 180 oldRoot->setPrev(0); 181 oldRoot->setNext(0); 182 } 183 } 184 #endif 185 186 void ElementShadow::attach(const Node::AttachContext& context) 187 { 188 Node::AttachContext childrenContext(context); 189 childrenContext.resolvedStyle = 0; 190 191 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) { 192 if (root->needsAttach()) 193 root->attach(childrenContext); 194 } 195 } 196 197 void ElementShadow::detach(const Node::AttachContext& context) 198 { 199 Node::AttachContext childrenContext(context); 200 childrenContext.resolvedStyle = 0; 201 202 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) 203 root->detach(childrenContext); 204 } 205 206 void ElementShadow::setNeedsDistributionRecalc() 207 { 208 if (m_needsDistributionRecalc) 209 return; 210 m_needsDistributionRecalc = true; 211 host()->markAncestorsWithChildNeedsDistributionRecalc(); 212 clearDistribution(); 213 } 214 215 bool ElementShadow::hasSameStyles(const ElementShadow* other) const 216 { 217 ShadowRoot* root = youngestShadowRoot(); 218 ShadowRoot* otherRoot = other->youngestShadowRoot(); 219 while (root || otherRoot) { 220 if (!root || !otherRoot) 221 return false; 222 223 StyleSheetList* list = root->styleSheets(); 224 StyleSheetList* otherList = otherRoot->styleSheets(); 225 226 if (list->length() != otherList->length()) 227 return false; 228 229 for (size_t i = 0; i < list->length(); i++) { 230 if (toCSSStyleSheet(list->item(i))->contents() != toCSSStyleSheet(otherList->item(i))->contents()) 231 return false; 232 } 233 root = root->olderShadowRoot(); 234 otherRoot = otherRoot->olderShadowRoot(); 235 } 236 237 return true; 238 } 239 240 const InsertionPoint* ElementShadow::finalDestinationInsertionPointFor(const Node* key) const 241 { 242 ASSERT(key && !key->document().childNeedsDistributionRecalc()); 243 NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key); 244 return it == m_nodeToInsertionPoints.end() ? 0: it->value.last().get(); 245 } 246 247 const DestinationInsertionPoints* ElementShadow::destinationInsertionPointsFor(const Node* key) const 248 { 249 ASSERT(key && !key->document().childNeedsDistributionRecalc()); 250 NodeToDestinationInsertionPoints::const_iterator it = m_nodeToInsertionPoints.find(key); 251 return it == m_nodeToInsertionPoints.end() ? 0: &it->value; 252 } 253 254 void ElementShadow::distribute() 255 { 256 host()->setNeedsStyleRecalc(SubtreeStyleChange); 257 Vector<HTMLShadowElement*, 32> shadowInsertionPoints; 258 DistributionPool pool(*host()); 259 260 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) { 261 HTMLShadowElement* shadowInsertionPoint = 0; 262 const WillBeHeapVector<RefPtrWillBeMember<InsertionPoint> >& insertionPoints = root->descendantInsertionPoints(); 263 for (size_t i = 0; i < insertionPoints.size(); ++i) { 264 InsertionPoint* point = insertionPoints[i].get(); 265 if (!point->isActive()) 266 continue; 267 if (isHTMLShadowElement(*point)) { 268 ASSERT(!shadowInsertionPoint); 269 shadowInsertionPoint = toHTMLShadowElement(point); 270 shadowInsertionPoints.append(shadowInsertionPoint); 271 } else { 272 pool.distributeTo(point, this); 273 if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*point)) 274 shadow->setNeedsDistributionRecalc(); 275 } 276 } 277 } 278 279 for (size_t i = shadowInsertionPoints.size(); i > 0; --i) { 280 HTMLShadowElement* shadowInsertionPoint = shadowInsertionPoints[i - 1]; 281 ShadowRoot* root = shadowInsertionPoint->containingShadowRoot(); 282 ASSERT(root); 283 if (root->isOldest()) { 284 pool.distributeTo(shadowInsertionPoint, this); 285 } else if (root->olderShadowRoot()->type() == root->type()) { 286 // Only allow reprojecting older shadow roots between the same type to 287 // disallow reprojecting UA elements into author shadows. 288 DistributionPool olderShadowRootPool(*root->olderShadowRoot()); 289 olderShadowRootPool.distributeTo(shadowInsertionPoint, this); 290 root->olderShadowRoot()->setShadowInsertionPointOfYoungerShadowRoot(shadowInsertionPoint); 291 } 292 if (ElementShadow* shadow = shadowWhereNodeCanBeDistributed(*shadowInsertionPoint)) 293 shadow->setNeedsDistributionRecalc(); 294 } 295 } 296 297 void ElementShadow::didDistributeNode(const Node* node, InsertionPoint* insertionPoint) 298 { 299 NodeToDestinationInsertionPoints::AddResult result = m_nodeToInsertionPoints.add(node, DestinationInsertionPoints()); 300 result.storedValue->value.append(insertionPoint); 301 } 302 303 const SelectRuleFeatureSet& ElementShadow::ensureSelectFeatureSet() 304 { 305 if (!m_needsSelectFeatureSet) 306 return m_selectFeatures; 307 308 m_selectFeatures.clear(); 309 for (ShadowRoot* root = oldestShadowRoot(); root; root = root->youngerShadowRoot()) 310 collectSelectFeatureSetFrom(*root); 311 m_needsSelectFeatureSet = false; 312 return m_selectFeatures; 313 } 314 315 void ElementShadow::collectSelectFeatureSetFrom(ShadowRoot& root) 316 { 317 if (!root.containsShadowRoots() && !root.containsContentElements()) 318 return; 319 320 for (Element* element = ElementTraversal::firstWithin(root); element; element = ElementTraversal::next(*element, &root)) { 321 if (ElementShadow* shadow = element->shadow()) 322 m_selectFeatures.add(shadow->ensureSelectFeatureSet()); 323 if (!isHTMLContentElement(*element)) 324 continue; 325 const CSSSelectorList& list = toHTMLContentElement(*element).selectorList(); 326 for (const CSSSelector* selector = list.first(); selector; selector = CSSSelectorList::next(*selector)) { 327 for (const CSSSelector* component = selector; component; component = component->tagHistory()) 328 m_selectFeatures.collectFeaturesFromSelector(*component); 329 } 330 } 331 } 332 333 void ElementShadow::didAffectSelector(AffectedSelectorMask mask) 334 { 335 if (ensureSelectFeatureSet().hasSelectorFor(mask)) 336 setNeedsDistributionRecalc(); 337 } 338 339 void ElementShadow::willAffectSelector() 340 { 341 for (ElementShadow* shadow = this; shadow; shadow = shadow->containingShadow()) { 342 if (shadow->needsSelectFeatureSet()) 343 break; 344 shadow->setNeedsSelectFeatureSet(); 345 } 346 setNeedsDistributionRecalc(); 347 } 348 349 void ElementShadow::clearDistribution() 350 { 351 m_nodeToInsertionPoints.clear(); 352 353 for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) 354 root->setShadowInsertionPointOfYoungerShadowRoot(nullptr); 355 } 356 357 void ElementShadow::trace(Visitor* visitor) 358 { 359 visitor->trace(m_nodeToInsertionPoints); 360 visitor->trace(m_selectFeatures); 361 // Shadow roots are linked with previous and next pointers which are traced. 362 // It is therefore enough to trace one of the shadow roots here and the 363 // rest will be traced from there. 364 visitor->trace(m_shadowRoots.head()); 365 } 366 367 } // namespace 368