Home | History | Annotate | Download | only in css
      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