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, 2013 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  *
     12  * This library is free software; you can redistribute it and/or
     13  * modify it under the terms of the GNU Library General Public
     14  * License as published by the Free Software Foundation; either
     15  * version 2 of the License, or (at your option) any later version.
     16  *
     17  * This library is distributed in the hope that it will be useful,
     18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     20  * Library General Public License for more details.
     21  *
     22  * You should have received a copy of the GNU Library General Public License
     23  * along with this library; see the file COPYING.LIB.  If not, write to
     24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     25  * Boston, MA 02110-1301, USA.
     26  */
     27 
     28 #ifndef SelectorChecker_h
     29 #define SelectorChecker_h
     30 
     31 #include "core/css/CSSSelector.h"
     32 #include "core/dom/Element.h"
     33 #include "platform/scroll/ScrollTypes.h"
     34 
     35 namespace WebCore {
     36 
     37 class CSSSelector;
     38 class ContainerNode;
     39 class Element;
     40 class RenderScrollbar;
     41 class RenderStyle;
     42 
     43 class SelectorChecker {
     44     WTF_MAKE_NONCOPYABLE(SelectorChecker);
     45 public:
     46     enum Match { SelectorMatches, SelectorFailsLocally, SelectorFailsAllSiblings, SelectorFailsCompletely };
     47     enum VisitedMatchType { VisitedMatchDisabled, VisitedMatchEnabled };
     48     enum Mode { ResolvingStyle = 0, CollectingStyleRules, CollectingCSSRules, QueryingRules, SharingRules };
     49     explicit SelectorChecker(Document&, Mode);
     50     enum BehaviorAtBoundary {
     51         DoesNotCrossBoundary = 0,
     52         CrossesBoundary = 1,
     53         StaysWithinTreeScope = 2,
     54         BoundaryBehaviorMask = 3, // 2bit for boundary behavior
     55         ScopeContainsLastMatchedElement = 4,
     56         ScopeIsShadowHost = 8,
     57         TreatShadowHostAsNormalScope = 16,
     58 
     59         ScopeIsShadowHostInPseudoHostParameter = CrossesBoundary | ScopeIsShadowHost | TreatShadowHostAsNormalScope
     60     };
     61 
     62     enum MatchingTagType {
     63         MatchingElement = 0,
     64         MatchingHostInItsShadowTree
     65     };
     66 
     67     struct SelectorCheckingContext {
     68         // Initial selector constructor
     69         SelectorCheckingContext(const CSSSelector* selector, Element* element, VisitedMatchType visitedMatchType)
     70             : selector(selector)
     71             , element(element)
     72             , scope(0)
     73             , visitedMatchType(visitedMatchType)
     74             , pseudoId(NOPSEUDO)
     75             , elementStyle(0)
     76             , scrollbar(0)
     77             , scrollbarPart(NoPart)
     78             , isSubSelector(false)
     79             , hasScrollbarPseudo(false)
     80             , hasSelectionPseudo(false)
     81             , behaviorAtBoundary(DoesNotCrossBoundary)
     82         { }
     83 
     84         const CSSSelector* selector;
     85         Element* element;
     86         const ContainerNode* scope;
     87         VisitedMatchType visitedMatchType;
     88         PseudoId pseudoId;
     89         RenderStyle* elementStyle;
     90         RenderScrollbar* scrollbar;
     91         ScrollbarPart scrollbarPart;
     92         bool isSubSelector;
     93         bool hasScrollbarPseudo;
     94         bool hasSelectionPseudo;
     95         BehaviorAtBoundary behaviorAtBoundary;
     96     };
     97 
     98     struct MatchResult {
     99         MatchResult()
    100             : dynamicPseudo(NOPSEUDO)
    101             , specificity(0) { }
    102 
    103         PseudoId dynamicPseudo;
    104         unsigned specificity;
    105     };
    106 
    107     template<typename SiblingTraversalStrategy>
    108     Match match(const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult* = 0) const;
    109 
    110     template<typename SiblingTraversalStrategy>
    111     bool checkOne(const SelectorCheckingContext&, const SiblingTraversalStrategy&, unsigned* specificity = 0) const;
    112 
    113     bool strictParsing() const { return m_strictParsing; }
    114 
    115     Mode mode() const { return m_mode; }
    116 
    117     static bool tagMatches(const Element&, const QualifiedName&, MatchingTagType = MatchingElement);
    118     static bool isCommonPseudoClassSelector(const CSSSelector*);
    119     static bool matchesFocusPseudoClass(const Element&);
    120     static bool checkExactAttribute(const Element&, const QualifiedName& selectorAttributeName, const StringImpl* value);
    121 
    122     enum LinkMatchMask { MatchLink = 1, MatchVisited = 2, MatchAll = MatchLink | MatchVisited };
    123     static unsigned determineLinkMatchType(const CSSSelector*);
    124 
    125     static bool isHostInItsShadowTree(const Element&, BehaviorAtBoundary, const ContainerNode* scope);
    126 
    127 private:
    128     template<typename SiblingTraversalStrategy>
    129     Match matchForSubSelector(const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult*) const;
    130     template<typename SiblingTraversalStrategy>
    131     Match matchForRelation(const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult*) const;
    132     template<typename SiblingTraversalStrategy>
    133     Match matchForShadowDistributed(const Element*, const SiblingTraversalStrategy&, SelectorCheckingContext& nextContext, MatchResult* = 0) const;
    134 
    135     bool checkScrollbarPseudoClass(const SelectorCheckingContext&, Document*, const CSSSelector*) const;
    136     Element* parentElement(const SelectorCheckingContext&) const;
    137     bool scopeContainsLastMatchedElement(const SelectorCheckingContext&) const;
    138 
    139     static bool isFrameFocused(const Element&);
    140 
    141     bool m_strictParsing;
    142     bool m_documentIsHTML;
    143     Mode m_mode;
    144 };
    145 
    146 inline bool SelectorChecker::isCommonPseudoClassSelector(const CSSSelector* selector)
    147 {
    148     if (selector->m_match != CSSSelector::PseudoClass)
    149         return false;
    150     CSSSelector::PseudoType pseudoType = selector->pseudoType();
    151     return pseudoType == CSSSelector::PseudoLink
    152         || pseudoType == CSSSelector::PseudoAnyLink
    153         || pseudoType == CSSSelector::PseudoVisited
    154         || pseudoType == CSSSelector::PseudoFocus;
    155 }
    156 
    157 inline bool SelectorChecker::tagMatches(const Element& element, const QualifiedName& tagQName, MatchingTagType matchingTagType)
    158 {
    159     if (tagQName == anyQName())
    160         return true;
    161     const AtomicString& localName = tagQName.localName();
    162     if (localName != starAtom && (localName != element.localName() || matchingTagType == MatchingHostInItsShadowTree))
    163         return false;
    164     const AtomicString& namespaceURI = tagQName.namespaceURI();
    165     return namespaceURI == starAtom || namespaceURI == element.namespaceURI();
    166 }
    167 
    168 inline bool SelectorChecker::checkExactAttribute(const Element& element, const QualifiedName& selectorAttributeName, const StringImpl* value)
    169 {
    170     if (!element.hasAttributesWithoutUpdate())
    171         return false;
    172     unsigned size = element.attributeCount();
    173     for (unsigned i = 0; i < size; ++i) {
    174         const Attribute* attribute = element.attributeItem(i);
    175         if (attribute->matches(selectorAttributeName) && (!value || attribute->value().impl() == value))
    176             return true;
    177     }
    178     return false;
    179 }
    180 
    181 inline bool SelectorChecker::isHostInItsShadowTree(const Element& element, BehaviorAtBoundary behaviorAtBoundary, const ContainerNode* scope)
    182 {
    183     return (behaviorAtBoundary & (ScopeIsShadowHost | TreatShadowHostAsNormalScope)) == ScopeIsShadowHost && scope == element;
    184 }
    185 
    186 }
    187 
    188 #endif
    189