1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde (at) carewolf.com) 4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit (at) nickshanks.com) 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. 6 * Copyright (C) 2007 Alexey Proskuryakov <ap (at) webkit.org> 7 * Copyright (C) 2007, 2008 Eric Seidel <eric (at) webkit.org> 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. 11 * Copyright (C) 2012 Google Inc. All rights reserved. 12 * 13 * This library is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU Library General Public 15 * License as published by the Free Software Foundation; either 16 * version 2 of the License, or (at your option) any later version. 17 * 18 * This library is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 * Library General Public License for more details. 22 * 23 * You should have received a copy of the GNU Library General Public License 24 * along with this library; see the file COPYING.LIB. If not, write to 25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 26 * Boston, MA 02110-1301, USA. 27 */ 28 29 #include "config.h" 30 #include "core/css/RuleSet.h" 31 32 #include "HTMLNames.h" 33 #include "RuntimeEnabledFeatures.h" 34 #include "core/css/CSSFontSelector.h" 35 #include "core/css/CSSKeyframesRule.h" 36 #include "core/css/CSSSelector.h" 37 #include "core/css/CSSSelectorList.h" 38 #include "core/css/MediaQueryEvaluator.h" 39 #include "core/css/SelectorChecker.h" 40 #include "core/css/SelectorCheckerFastPath.h" 41 #include "core/css/SelectorFilter.h" 42 #include "core/css/StyleRule.h" 43 #include "core/css/StyleRuleImport.h" 44 #include "core/css/StyleSheetContents.h" 45 #include "core/css/resolver/StyleResolver.h" 46 #include "core/html/track/TextTrackCue.h" 47 #include "weborigin/SecurityOrigin.h" 48 49 namespace WebCore { 50 51 using namespace HTMLNames; 52 53 // ----------------------------------------------------------------- 54 55 static inline bool isDocumentScope(const ContainerNode* scope) 56 { 57 return !scope || scope->isDocumentNode(); 58 } 59 60 static inline bool isScopingNodeInShadowTree(const ContainerNode* scopingNode) 61 { 62 return scopingNode && scopingNode->isInShadowTree(); 63 } 64 65 static inline bool isSelectorMatchingHTMLBasedOnRuleHash(const CSSSelector* selector) 66 { 67 ASSERT(selector); 68 if (selector->m_match == CSSSelector::Tag) { 69 const AtomicString& selectorNamespace = selector->tagQName().namespaceURI(); 70 if (selectorNamespace != starAtom && selectorNamespace != xhtmlNamespaceURI) 71 return false; 72 if (selector->relation() == CSSSelector::SubSelector) 73 return isSelectorMatchingHTMLBasedOnRuleHash(selector->tagHistory()); 74 return true; 75 } 76 if (SelectorChecker::isCommonPseudoClassSelector(selector)) 77 return true; 78 return selector->m_match == CSSSelector::Id || selector->m_match == CSSSelector::Class; 79 } 80 81 static inline bool selectorListContainsUncommonAttributeSelector(const CSSSelector* selector) 82 { 83 const CSSSelectorList* selectorList = selector->selectorList(); 84 if (!selectorList) 85 return false; 86 for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(selector)) { 87 for (const CSSSelector* component = selector; component; component = component->tagHistory()) { 88 if (component->isAttributeSelector()) 89 return true; 90 } 91 } 92 return false; 93 } 94 95 static inline bool isCommonAttributeSelectorAttribute(const QualifiedName& attribute) 96 { 97 // These are explicitly tested for equality in canShareStyleWithElement. 98 return attribute == typeAttr || attribute == readonlyAttr; 99 } 100 101 static inline bool containsUncommonAttributeSelector(const CSSSelector* selector) 102 { 103 for (; selector; selector = selector->tagHistory()) { 104 // Allow certain common attributes (used in the default style) in the selectors that match the current element. 105 if (selector->isAttributeSelector() && !isCommonAttributeSelectorAttribute(selector->attribute())) 106 return true; 107 if (selectorListContainsUncommonAttributeSelector(selector)) 108 return true; 109 if (selector->relation() != CSSSelector::SubSelector) { 110 selector = selector->tagHistory(); 111 break; 112 } 113 } 114 115 for (; selector; selector = selector->tagHistory()) { 116 if (selector->isAttributeSelector()) 117 return true; 118 if (selectorListContainsUncommonAttributeSelector(selector)) 119 return true; 120 } 121 return false; 122 } 123 124 static inline PropertyWhitelistType determinePropertyWhitelistType(const AddRuleFlags addRuleFlags, const CSSSelector* selector) 125 { 126 if (addRuleFlags & RuleIsInRegionRule) 127 return PropertyWhitelistRegion; 128 for (const CSSSelector* component = selector; component; component = component->tagHistory()) { 129 if (component->pseudoType() == CSSSelector::PseudoCue || (component->m_match == CSSSelector::PseudoElement && component->value() == TextTrackCue::cueShadowPseudoId())) 130 return PropertyWhitelistCue; 131 } 132 return PropertyWhitelistNone; 133 } 134 135 namespace { 136 137 // FIXME: Should we move this class to WTF? 138 template<typename T> 139 class TerminatedArrayBuilder { 140 public: 141 explicit TerminatedArrayBuilder(PassOwnPtr<T> array) 142 : m_array(array) 143 , m_count(0) 144 , m_capacity(0) 145 { 146 if (!m_array) 147 return; 148 for (T* item = m_array.get(); !item->isLastInArray(); ++item) 149 ++m_count; 150 ++m_count; // To count the last item itself. 151 m_capacity = m_count; 152 } 153 154 void grow(size_t count) 155 { 156 ASSERT(count); 157 if (!m_array) { 158 ASSERT(!m_count); 159 ASSERT(!m_capacity); 160 m_capacity = count; 161 m_array = adoptPtr(static_cast<T*>(fastMalloc(m_capacity * sizeof(T)))); 162 return; 163 } 164 m_capacity += count; 165 m_array = adoptPtr(static_cast<T*>(fastRealloc(m_array.leakPtr(), m_capacity * sizeof(T)))); 166 m_array.get()[m_count - 1].setLastInArray(false); 167 } 168 169 void append(const T& item) 170 { 171 RELEASE_ASSERT(m_count < m_capacity); 172 ASSERT(!item.isLastInArray()); 173 m_array.get()[m_count++] = item; 174 } 175 176 PassOwnPtr<T> release() 177 { 178 RELEASE_ASSERT(m_count == m_capacity); 179 if (m_array) 180 m_array.get()[m_count - 1].setLastInArray(true); 181 assertValid(); 182 return m_array.release(); 183 } 184 185 private: 186 #ifndef NDEBUG 187 void assertValid() 188 { 189 for (size_t i = 0; i < m_count; ++i) { 190 bool isLastInArray = (i + 1 == m_count); 191 ASSERT(m_array.get()[i].isLastInArray() == isLastInArray); 192 } 193 } 194 #else 195 void assertValid() { } 196 #endif 197 198 OwnPtr<T> m_array; 199 size_t m_count; 200 size_t m_capacity; 201 }; 202 203 } 204 205 RuleData::RuleData(StyleRule* rule, unsigned selectorIndex, unsigned position, AddRuleFlags addRuleFlags) 206 : m_rule(rule) 207 , m_selectorIndex(selectorIndex) 208 , m_isLastInArray(false) 209 , m_position(position) 210 , m_hasFastCheckableSelector((addRuleFlags & RuleCanUseFastCheckSelector) && SelectorCheckerFastPath::canUse(selector())) 211 , m_specificity(selector()->specificity()) 212 , m_hasMultipartSelector(!!selector()->tagHistory()) 213 , m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash(isSelectorMatchingHTMLBasedOnRuleHash(selector())) 214 , m_containsUncommonAttributeSelector(WebCore::containsUncommonAttributeSelector(selector())) 215 , m_linkMatchType(SelectorChecker::determineLinkMatchType(selector())) 216 , m_hasDocumentSecurityOrigin(addRuleFlags & RuleHasDocumentSecurityOrigin) 217 , m_propertyWhitelistType(determinePropertyWhitelistType(addRuleFlags, selector())) 218 { 219 ASSERT(m_position == position); 220 ASSERT(m_selectorIndex == selectorIndex); 221 SelectorFilter::collectIdentifierHashes(selector(), m_descendantSelectorIdentifierHashes, maximumIdentifierCount); 222 } 223 224 static void collectFeaturesFromRuleData(RuleFeatureSet& features, const RuleData& ruleData) 225 { 226 bool foundSiblingSelector = false; 227 for (const CSSSelector* selector = ruleData.selector(); selector; selector = selector->tagHistory()) { 228 features.collectFeaturesFromSelector(selector); 229 230 if (const CSSSelectorList* selectorList = selector->selectorList()) { 231 for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) { 232 if (!foundSiblingSelector && selector->isSiblingSelector()) 233 foundSiblingSelector = true; 234 features.collectFeaturesFromSelector(subSelector); 235 } 236 } else if (!foundSiblingSelector && selector->isSiblingSelector()) 237 foundSiblingSelector = true; 238 } 239 if (foundSiblingSelector) 240 features.siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin())); 241 if (ruleData.containsUncommonAttributeSelector()) 242 features.uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin())); 243 } 244 245 void RuleSet::addToRuleSet(StringImpl* key, PendingRuleMap& map, const RuleData& ruleData) 246 { 247 if (!key) 248 return; 249 OwnPtr<LinkedStack<RuleData> >& rules = map.add(key, nullptr).iterator->value; 250 if (!rules) 251 rules = adoptPtr(new LinkedStack<RuleData>); 252 rules->push(ruleData); 253 } 254 255 bool RuleSet::findBestRuleSetAndAdd(const CSSSelector* component, RuleData& ruleData) 256 { 257 if (component->m_match == CSSSelector::Id) { 258 addToRuleSet(component->value().impl(), ensurePendingRules()->idRules, ruleData); 259 return true; 260 } 261 if (component->m_match == CSSSelector::Class) { 262 addToRuleSet(component->value().impl(), ensurePendingRules()->classRules, ruleData); 263 return true; 264 } 265 if (component->isCustomPseudoElement()) { 266 StringImpl* pseudoValue = component->pseudoType() == CSSSelector::PseudoPart ? component->argument().impl() : component->value().impl(); 267 addToRuleSet(pseudoValue, ensurePendingRules()->shadowPseudoElementRules, ruleData); 268 return true; 269 } 270 if (component->pseudoType() == CSSSelector::PseudoCue) { 271 m_cuePseudoRules.append(ruleData); 272 return true; 273 } 274 if (SelectorChecker::isCommonPseudoClassSelector(component)) { 275 switch (component->pseudoType()) { 276 case CSSSelector::PseudoLink: 277 case CSSSelector::PseudoVisited: 278 case CSSSelector::PseudoAnyLink: 279 m_linkPseudoClassRules.append(ruleData); 280 return true; 281 case CSSSelector::PseudoFocus: 282 m_focusPseudoClassRules.append(ruleData); 283 return true; 284 default: 285 ASSERT_NOT_REACHED(); 286 return true; 287 } 288 } 289 290 if (component->m_match == CSSSelector::Tag) { 291 if (component->tagQName().localName() != starAtom) { 292 // If this is part of a subselector chain, recurse ahead to find a narrower set (ID/class.) 293 if (component->relation() == CSSSelector::SubSelector 294 && (component->tagHistory()->m_match == CSSSelector::Class || component->tagHistory()->m_match == CSSSelector::Id || SelectorChecker::isCommonPseudoClassSelector(component->tagHistory())) 295 && findBestRuleSetAndAdd(component->tagHistory(), ruleData)) 296 return true; 297 298 addToRuleSet(component->tagQName().localName().impl(), ensurePendingRules()->tagRules, ruleData); 299 return true; 300 } 301 } 302 return false; 303 } 304 305 void RuleSet::addRule(StyleRule* rule, unsigned selectorIndex, AddRuleFlags addRuleFlags) 306 { 307 RuleData ruleData(rule, selectorIndex, m_ruleCount++, addRuleFlags); 308 collectFeaturesFromRuleData(m_features, ruleData); 309 310 if (!findBestRuleSetAndAdd(ruleData.selector(), ruleData)) { 311 // If we didn't find a specialized map to stick it in, file under universal rules. 312 m_universalRules.append(ruleData); 313 } 314 } 315 316 void RuleSet::addPageRule(StyleRulePage* rule) 317 { 318 ensurePendingRules(); // So that m_pageRules.shrinkToFit() gets called. 319 m_pageRules.append(rule); 320 } 321 322 void RuleSet::addViewportRule(StyleRuleViewport* rule) 323 { 324 ensurePendingRules(); // So that m_viewportRules.shrinkToFit() gets called. 325 m_viewportRules.append(rule); 326 } 327 328 void RuleSet::addRegionRule(StyleRuleRegion* regionRule, bool hasDocumentSecurityOrigin) 329 { 330 ensurePendingRules(); // So that m_regionSelectorsAndRuleSets.shrinkToFit() gets called. 331 OwnPtr<RuleSet> regionRuleSet = RuleSet::create(); 332 // The region rule set should take into account the position inside the parent rule set. 333 // Otherwise, the rules inside region block might be incorrectly positioned before other similar rules from 334 // the stylesheet that contains the region block. 335 regionRuleSet->m_ruleCount = m_ruleCount; 336 337 // Collect the region rules into a rule set 338 // FIXME: Should this add other types of rules? (i.e. use addChildRules() directly?) 339 const Vector<RefPtr<StyleRuleBase> >& childRules = regionRule->childRules(); 340 AddRuleFlags addRuleFlags = hasDocumentSecurityOrigin ? RuleHasDocumentSecurityOrigin : RuleHasNoSpecialState; 341 addRuleFlags = static_cast<AddRuleFlags>(addRuleFlags | RuleCanUseFastCheckSelector | RuleIsInRegionRule); 342 for (unsigned i = 0; i < childRules.size(); ++i) { 343 StyleRuleBase* regionStylingRule = childRules[i].get(); 344 if (regionStylingRule->isStyleRule()) 345 regionRuleSet->addStyleRule(static_cast<StyleRule*>(regionStylingRule), addRuleFlags); 346 } 347 // Update the "global" rule count so that proper order is maintained 348 m_ruleCount = regionRuleSet->m_ruleCount; 349 350 m_regionSelectorsAndRuleSets.append(RuleSetSelectorPair(regionRule->selectorList().first(), regionRuleSet.release())); 351 } 352 353 void RuleSet::addChildRules(const Vector<RefPtr<StyleRuleBase> >& rules, const MediaQueryEvaluator& medium, StyleResolver* resolver, const ContainerNode* scope, bool hasDocumentSecurityOrigin, AddRuleFlags addRuleFlags) 354 { 355 for (unsigned i = 0; i < rules.size(); ++i) { 356 StyleRuleBase* rule = rules[i].get(); 357 358 if (rule->isStyleRule()) { 359 StyleRule* styleRule = static_cast<StyleRule*>(rule); 360 361 const CSSSelectorList& selectorList = styleRule->selectorList(); 362 for (size_t selectorIndex = 0; selectorIndex != notFound; selectorIndex = selectorList.indexOfNextSelectorAfter(selectorIndex)) { 363 if (selectorList.hasShadowDistributedAt(selectorIndex)) { 364 if (isDocumentScope(scope)) 365 continue; 366 resolver->ruleSets().shadowDistributedRules().addRule(styleRule, selectorIndex, const_cast<ContainerNode*>(scope), addRuleFlags); 367 } else 368 addRule(styleRule, selectorIndex, addRuleFlags); 369 } 370 } else if (rule->isPageRule()) 371 addPageRule(static_cast<StyleRulePage*>(rule)); 372 else if (rule->isMediaRule()) { 373 StyleRuleMedia* mediaRule = static_cast<StyleRuleMedia*>(rule); 374 if ((!mediaRule->mediaQueries() || medium.eval(mediaRule->mediaQueries(), resolver))) 375 addChildRules(mediaRule->childRules(), medium, resolver, scope, hasDocumentSecurityOrigin, addRuleFlags); 376 } else if (rule->isFontFaceRule() && resolver) { 377 // Add this font face to our set. 378 // FIXME(BUG 72461): We don't add @font-face rules of scoped style sheets for the moment. 379 if (!isDocumentScope(scope)) 380 continue; 381 const StyleRuleFontFace* fontFaceRule = static_cast<StyleRuleFontFace*>(rule); 382 resolver->fontSelector()->addFontFaceRule(fontFaceRule); 383 resolver->invalidateMatchedPropertiesCache(); 384 } else if (rule->isKeyframesRule() && resolver) { 385 resolver->ensureScopedStyleResolver(scope)->addKeyframeStyle(static_cast<StyleRuleKeyframes*>(rule)); 386 } else if (rule->isRegionRule() && resolver) { 387 // FIXME (BUG 72472): We don't add @-webkit-region rules of scoped style sheets for the moment. 388 addRegionRule(static_cast<StyleRuleRegion*>(rule), hasDocumentSecurityOrigin); 389 } else if (rule->isHostRule() && resolver) { 390 if (!isScopingNodeInShadowTree(scope)) 391 continue; 392 bool enabled = resolver->buildScopedStyleTreeInDocumentOrder(); 393 resolver->setBuildScopedStyleTreeInDocumentOrder(false); 394 resolver->ensureScopedStyleResolver(scope->shadowHost())->addHostRule(static_cast<StyleRuleHost*>(rule), hasDocumentSecurityOrigin, scope); 395 resolver->setBuildScopedStyleTreeInDocumentOrder(enabled); 396 } else if (RuntimeEnabledFeatures::cssViewportEnabled() && rule->isViewportRule()) { 397 // @viewport should not be scoped. 398 if (!isDocumentScope(scope)) 399 continue; 400 addViewportRule(static_cast<StyleRuleViewport*>(rule)); 401 } 402 else if (rule->isSupportsRule() && static_cast<StyleRuleSupports*>(rule)->conditionIsSupported()) 403 addChildRules(static_cast<StyleRuleSupports*>(rule)->childRules(), medium, resolver, scope, hasDocumentSecurityOrigin, addRuleFlags); 404 } 405 } 406 407 void RuleSet::addRulesFromSheet(StyleSheetContents* sheet, const MediaQueryEvaluator& medium, StyleResolver* resolver, const ContainerNode* scope) 408 { 409 ASSERT(sheet); 410 411 const Vector<RefPtr<StyleRuleImport> >& importRules = sheet->importRules(); 412 for (unsigned i = 0; i < importRules.size(); ++i) { 413 StyleRuleImport* importRule = importRules[i].get(); 414 if (importRule->styleSheet() && (!importRule->mediaQueries() || medium.eval(importRule->mediaQueries(), resolver))) 415 addRulesFromSheet(importRule->styleSheet(), medium, resolver, scope); 416 } 417 418 bool hasDocumentSecurityOrigin = resolver && resolver->document()->securityOrigin()->canRequest(sheet->baseURL()); 419 AddRuleFlags addRuleFlags = static_cast<AddRuleFlags>((hasDocumentSecurityOrigin ? RuleHasDocumentSecurityOrigin : 0) | (!scope ? RuleCanUseFastCheckSelector : 0)); 420 421 addChildRules(sheet->childRules(), medium, resolver, scope, hasDocumentSecurityOrigin, addRuleFlags); 422 } 423 424 void RuleSet::addStyleRule(StyleRule* rule, AddRuleFlags addRuleFlags) 425 { 426 for (size_t selectorIndex = 0; selectorIndex != notFound; selectorIndex = rule->selectorList().indexOfNextSelectorAfter(selectorIndex)) 427 addRule(rule, selectorIndex, addRuleFlags); 428 } 429 430 void RuleSet::compactPendingRules(PendingRuleMap& pendingMap, CompactRuleMap& compactMap) 431 { 432 PendingRuleMap::iterator end = pendingMap.end(); 433 for (PendingRuleMap::iterator it = pendingMap.begin(); it != end; ++it) { 434 OwnPtr<LinkedStack<RuleData> > pendingRules = it->value.release(); 435 CompactRuleMap::iterator compactRules = compactMap.add(it->key, nullptr).iterator; 436 437 TerminatedArrayBuilder<RuleData> builder(compactRules->value.release()); 438 builder.grow(pendingRules->size()); 439 while (!pendingRules->isEmpty()) { 440 builder.append(pendingRules->peek()); 441 pendingRules->pop(); 442 } 443 444 compactRules->value = builder.release(); 445 } 446 } 447 448 void RuleSet::compactRules() 449 { 450 ASSERT(m_pendingRules); 451 OwnPtr<PendingRuleMaps> pendingRules = m_pendingRules.release(); 452 compactPendingRules(pendingRules->idRules, m_idRules); 453 compactPendingRules(pendingRules->classRules, m_classRules); 454 compactPendingRules(pendingRules->tagRules, m_tagRules); 455 compactPendingRules(pendingRules->shadowPseudoElementRules, m_shadowPseudoElementRules); 456 m_linkPseudoClassRules.shrinkToFit(); 457 m_cuePseudoRules.shrinkToFit(); 458 m_focusPseudoClassRules.shrinkToFit(); 459 m_universalRules.shrinkToFit(); 460 m_pageRules.shrinkToFit(); 461 m_viewportRules.shrinkToFit(); 462 } 463 464 } // namespace WebCore 465