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