1 /* 2 * Copyright (C) 2011 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/ContentDistributor.h" 29 30 #include "core/dom/NodeTraversal.h" 31 #include "core/dom/shadow/ElementShadow.h" 32 #include "core/dom/shadow/ShadowRoot.h" 33 #include "core/html/shadow/HTMLContentElement.h" 34 #include "core/html/shadow/HTMLShadowElement.h" 35 36 37 namespace WebCore { 38 39 void ContentDistribution::swap(ContentDistribution& other) 40 { 41 m_nodes.swap(other.m_nodes); 42 m_indices.swap(other.m_indices); 43 } 44 45 void ContentDistribution::append(PassRefPtr<Node> node) 46 { 47 size_t size = m_nodes.size(); 48 m_indices.set(node.get(), size); 49 m_nodes.append(node); 50 } 51 52 size_t ContentDistribution::find(const Node* node) const 53 { 54 HashMap<const Node*, size_t>::const_iterator it = m_indices.find(node); 55 if (it == m_indices.end()) 56 return notFound; 57 58 return it.get()->value; 59 } 60 61 Node* ContentDistribution::nextTo(const Node* node) const 62 { 63 size_t index = find(node); 64 if (index == notFound || index + 1 == size()) 65 return 0; 66 return at(index + 1).get(); 67 } 68 69 Node* ContentDistribution::previousTo(const Node* node) const 70 { 71 size_t index = find(node); 72 if (index == notFound || !index) 73 return 0; 74 return at(index - 1).get(); 75 } 76 77 78 ScopeContentDistribution::ScopeContentDistribution() 79 : m_insertionPointAssignedTo(0) 80 , m_numberOfShadowElementChildren(0) 81 , m_numberOfContentElementChildren(0) 82 , m_numberOfElementShadowChildren(0) 83 , m_insertionPointListIsValid(false) 84 { 85 } 86 87 void ScopeContentDistribution::setInsertionPointAssignedTo(PassRefPtr<InsertionPoint> insertionPoint) 88 { 89 m_insertionPointAssignedTo = insertionPoint; 90 } 91 92 void ScopeContentDistribution::invalidateInsertionPointList() 93 { 94 m_insertionPointListIsValid = false; 95 m_insertionPointList.clear(); 96 } 97 98 const Vector<RefPtr<InsertionPoint> >& ScopeContentDistribution::ensureInsertionPointList(ShadowRoot* shadowRoot) 99 { 100 if (m_insertionPointListIsValid) 101 return m_insertionPointList; 102 103 m_insertionPointListIsValid = true; 104 ASSERT(m_insertionPointList.isEmpty()); 105 106 if (!shadowRoot->containsInsertionPoints()) 107 return m_insertionPointList; 108 109 for (Element* element = ElementTraversal::firstWithin(shadowRoot); element; element = ElementTraversal::next(element, shadowRoot)) { 110 if (element->isInsertionPoint()) 111 m_insertionPointList.append(toInsertionPoint(element)); 112 } 113 114 return m_insertionPointList; 115 } 116 117 void ScopeContentDistribution::registerInsertionPoint(InsertionPoint* point) 118 { 119 if (isHTMLShadowElement(point)) 120 ++m_numberOfShadowElementChildren; 121 else if (isHTMLContentElement(point)) 122 ++m_numberOfContentElementChildren; 123 else 124 ASSERT_NOT_REACHED(); 125 126 invalidateInsertionPointList(); 127 } 128 129 void ScopeContentDistribution::unregisterInsertionPoint(InsertionPoint* point) 130 { 131 if (isHTMLShadowElement(point)) 132 --m_numberOfShadowElementChildren; 133 else if (isHTMLContentElement(point)) 134 --m_numberOfContentElementChildren; 135 else 136 ASSERT_NOT_REACHED(); 137 138 ASSERT(m_numberOfContentElementChildren >= 0); 139 ASSERT(m_numberOfShadowElementChildren >= 0); 140 141 invalidateInsertionPointList(); 142 } 143 144 ContentDistributor::ContentDistributor() 145 : m_needsSelectFeatureSet(false) 146 { 147 } 148 149 ContentDistributor::~ContentDistributor() 150 { 151 } 152 153 InsertionPoint* ContentDistributor::findInsertionPointFor(const Node* key) const 154 { 155 return m_nodeToInsertionPoint.get(key); 156 } 157 158 void ContentDistributor::populate(Node* node, Vector<Node*>& pool) 159 { 160 node->lazyReattachIfAttached(); 161 162 if (!isActiveInsertionPoint(node)) { 163 pool.append(node); 164 return; 165 } 166 167 InsertionPoint* insertionPoint = toInsertionPoint(node); 168 if (insertionPoint->hasDistribution()) { 169 for (size_t i = 0; i < insertionPoint->size(); ++i) 170 populate(insertionPoint->at(i), pool); 171 } else { 172 for (Node* fallbackNode = insertionPoint->firstChild(); fallbackNode; fallbackNode = fallbackNode->nextSibling()) 173 pool.append(fallbackNode); 174 } 175 } 176 177 void ContentDistributor::distribute(Element* host) 178 { 179 Vector<Node*> pool; 180 for (Node* node = host->firstChild(); node; node = node->nextSibling()) 181 populate(node, pool); 182 183 host->setNeedsStyleRecalc(); 184 185 Vector<bool> distributed(pool.size()); 186 distributed.fill(false); 187 188 Vector<HTMLShadowElement*, 8> activeShadowInsertionPoints; 189 for (ShadowRoot* root = host->youngestShadowRoot(); root; root = root->olderShadowRoot()) { 190 HTMLShadowElement* firstActiveShadowInsertionPoint = 0; 191 192 if (ScopeContentDistribution* scope = root->scopeDistribution()) { 193 const Vector<RefPtr<InsertionPoint> >& insertionPoints = scope->ensureInsertionPointList(root); 194 for (size_t i = 0; i < insertionPoints.size(); ++i) { 195 InsertionPoint* point = insertionPoints[i].get(); 196 if (!point->isActive()) 197 continue; 198 199 if (isHTMLShadowElement(point)) { 200 if (!firstActiveShadowInsertionPoint) 201 firstActiveShadowInsertionPoint = toHTMLShadowElement(point); 202 } else { 203 distributeSelectionsTo(point, pool, distributed); 204 if (ElementShadow* shadow = shadowOfParentForDistribution(point)) 205 shadow->setNeedsDistributionRecalc(); 206 } 207 } 208 } 209 210 if (firstActiveShadowInsertionPoint) 211 activeShadowInsertionPoints.append(firstActiveShadowInsertionPoint); 212 } 213 214 for (size_t i = activeShadowInsertionPoints.size(); i > 0; --i) { 215 HTMLShadowElement* shadowElement = activeShadowInsertionPoints[i - 1]; 216 ShadowRoot* root = shadowElement->containingShadowRoot(); 217 ASSERT(root); 218 if (!shadowElement->shouldSelect()) { 219 if (root->olderShadowRoot()) 220 root->olderShadowRoot()->ensureScopeDistribution()->setInsertionPointAssignedTo(shadowElement); 221 } else if (root->olderShadowRoot()) { 222 distributeNodeChildrenTo(shadowElement, root->olderShadowRoot()); 223 root->olderShadowRoot()->ensureScopeDistribution()->setInsertionPointAssignedTo(shadowElement); 224 } else { 225 distributeSelectionsTo(shadowElement, pool, distributed); 226 } 227 if (ElementShadow* shadow = shadowOfParentForDistribution(shadowElement)) 228 shadow->setNeedsDistributionRecalc(); 229 } 230 } 231 232 void ContentDistributor::distributeSelectionsTo(InsertionPoint* insertionPoint, const Vector<Node*>& pool, Vector<bool>& distributed) 233 { 234 ContentDistribution distribution; 235 236 for (size_t i = 0; i < pool.size(); ++i) { 237 if (distributed[i]) 238 continue; 239 240 if (isHTMLContentElement(insertionPoint) && !toHTMLContentElement(insertionPoint)->canSelectNode(pool, i)) 241 continue; 242 243 Node* child = pool[i]; 244 distribution.append(child); 245 m_nodeToInsertionPoint.add(child, insertionPoint); 246 distributed[i] = true; 247 } 248 249 insertionPoint->lazyReattachIfAttached(); 250 insertionPoint->setDistribution(distribution); 251 } 252 253 void ContentDistributor::distributeNodeChildrenTo(InsertionPoint* insertionPoint, ContainerNode* containerNode) 254 { 255 ContentDistribution distribution; 256 for (Node* node = containerNode->firstChild(); node; node = node->nextSibling()) { 257 node->lazyReattachIfAttached(); 258 if (isActiveInsertionPoint(node)) { 259 InsertionPoint* innerInsertionPoint = toInsertionPoint(node); 260 if (innerInsertionPoint->hasDistribution()) { 261 for (size_t i = 0; i < innerInsertionPoint->size(); ++i) { 262 distribution.append(innerInsertionPoint->at(i)); 263 m_nodeToInsertionPoint.add(innerInsertionPoint->at(i), insertionPoint); 264 } 265 } else { 266 for (Node* child = innerInsertionPoint->firstChild(); child; child = child->nextSibling()) { 267 distribution.append(child); 268 m_nodeToInsertionPoint.add(child, insertionPoint); 269 } 270 } 271 } else { 272 distribution.append(node); 273 m_nodeToInsertionPoint.add(node, insertionPoint); 274 } 275 } 276 277 insertionPoint->lazyReattachIfAttached(); 278 insertionPoint->setDistribution(distribution); 279 } 280 281 const SelectRuleFeatureSet& ContentDistributor::ensureSelectFeatureSet(ElementShadow* shadow) 282 { 283 if (!m_needsSelectFeatureSet) 284 return m_selectFeatures; 285 286 m_selectFeatures.clear(); 287 for (ShadowRoot* root = shadow->oldestShadowRoot(); root; root = root->youngerShadowRoot()) 288 collectSelectFeatureSetFrom(root); 289 m_needsSelectFeatureSet = false; 290 return m_selectFeatures; 291 } 292 293 void ContentDistributor::collectSelectFeatureSetFrom(ShadowRoot* root) 294 { 295 if (!root->containsShadowRoots() && !root->containsContentElements()) 296 return; 297 298 for (Element* element = ElementTraversal::firstWithin(root); element; element = ElementTraversal::next(element, root)) { 299 if (ElementShadow* shadow = element->shadow()) 300 m_selectFeatures.add(shadow->ensureSelectFeatureSet()); 301 if (!isHTMLContentElement(element)) 302 continue; 303 const CSSSelectorList& list = toHTMLContentElement(element)->selectorList(); 304 for (const CSSSelector* selector = list.first(); selector; selector = CSSSelectorList::next(selector)) { 305 for (const CSSSelector* component = selector; component; component = component->tagHistory()) 306 m_selectFeatures.collectFeaturesFromSelector(component); 307 } 308 } 309 } 310 311 void ContentDistributor::didAffectSelector(Element* host, AffectedSelectorMask mask) 312 { 313 if (ensureSelectFeatureSet(host->shadow()).hasSelectorFor(mask)) 314 host->shadow()->setNeedsDistributionRecalc(); 315 } 316 317 void ContentDistributor::willAffectSelector(Element* host) 318 { 319 for (ElementShadow* shadow = host->shadow(); shadow; shadow = shadow->containingShadow()) { 320 if (shadow->distributor().needsSelectFeatureSet()) 321 break; 322 shadow->distributor().setNeedsSelectFeatureSet(); 323 } 324 host->shadow()->setNeedsDistributionRecalc(); 325 } 326 327 void ContentDistributor::clearDistribution(Element* host) 328 { 329 m_nodeToInsertionPoint.clear(); 330 331 for (ShadowRoot* root = host->youngestShadowRoot(); root; root = root->olderShadowRoot()) { 332 if (ScopeContentDistribution* scope = root->scopeDistribution()) 333 scope->setInsertionPointAssignedTo(0); 334 } 335 } 336 337 } 338