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         // FIXME: refactor to remove BoundaryBehavior (i.e. DoesNotCrossBoundary and StaysWithinTreeScope).
     53         StaysWithinTreeScope = 2,
     54         BoundaryBehaviorMask = 3, // 2bit for boundary behavior
     55         ScopeContainsLastMatchedElement = 4,
     56         ScopeIsShadowRoot = 8,
     57         TreatShadowHostAsNormalScope = 16,
     58 
     59         ScopeIsShadowHostInPseudoHostParameter = ScopeIsShadowRoot | TreatShadowHostAsNormalScope
     60     };
     61 
     62     struct SelectorCheckingContext {
     63         // Initial selector constructor
     64         SelectorCheckingContext(const CSSSelector& selector, Element* element, VisitedMatchType visitedMatchType)
     65             : selector(&selector)
     66             , element(element)
     67             , previousElement(0)
     68             , scope(0)
     69             , visitedMatchType(visitedMatchType)
     70             , pseudoId(NOPSEUDO)
     71             , elementStyle(0)
     72             , scrollbar(0)
     73             , scrollbarPart(NoPart)
     74             , isSubSelector(false)
     75             , hasScrollbarPseudo(false)
     76             , hasSelectionPseudo(false)
     77             , behaviorAtBoundary(DoesNotCrossBoundary)
     78         { }
     79 
     80         const CSSSelector* selector;
     81         Element* element;
     82         Element* previousElement;
     83         const ContainerNode* scope;
     84         VisitedMatchType visitedMatchType;
     85         PseudoId pseudoId;
     86         RenderStyle* elementStyle;
     87         RenderScrollbar* scrollbar;
     88         ScrollbarPart scrollbarPart;
     89         bool isSubSelector;
     90         bool hasScrollbarPseudo;
     91         bool hasSelectionPseudo;
     92         BehaviorAtBoundary behaviorAtBoundary;
     93     };
     94 
     95     struct MatchResult {
     96         MatchResult()
     97             : dynamicPseudo(NOPSEUDO)
     98             , specificity(0) { }
     99 
    100         PseudoId dynamicPseudo;
    101         unsigned specificity;
    102     };
    103 
    104     template<typename SiblingTraversalStrategy>
    105     Match match(const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult* = 0) const;
    106 
    107     template<typename SiblingTraversalStrategy>
    108     bool checkOne(const SelectorCheckingContext&, const SiblingTraversalStrategy&, unsigned* specificity = 0) const;
    109 
    110     bool strictParsing() const { return m_strictParsing; }
    111 
    112     Mode mode() const { return m_mode; }
    113 
    114     static bool tagMatches(const Element&, const QualifiedName&);
    115     static bool isCommonPseudoClassSelector(const CSSSelector&);
    116     static bool matchesFocusPseudoClass(const Element&);
    117     static bool checkExactAttribute(const Element&, const QualifiedName& selectorAttributeName, const StringImpl* value);
    118 
    119     enum LinkMatchMask { MatchLink = 1, MatchVisited = 2, MatchAll = MatchLink | MatchVisited };
    120     static unsigned determineLinkMatchType(const CSSSelector&);
    121 
    122     static bool isHostInItsShadowTree(const Element&, BehaviorAtBoundary, const ContainerNode* scope);
    123 
    124 private:
    125     template<typename SiblingTraversalStrategy>
    126     Match matchForSubSelector(const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult*) const;
    127     template<typename SiblingTraversalStrategy>
    128     Match matchForRelation(const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult*) const;
    129     template<typename SiblingTraversalStrategy>
    130     Match matchForShadowDistributed(const Element*, const SiblingTraversalStrategy&, SelectorCheckingContext& nextContext, MatchResult* = 0) const;
    131     template<typename SiblingTraversalStrategy>
    132     Match matchForPseudoShadow(const ContainerNode*, const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult*) const;
    133 
    134     bool checkScrollbarPseudoClass(const SelectorCheckingContext&, Document*, const CSSSelector&) const;
    135     Element* parentElement(const SelectorCheckingContext&, bool allowToCrossBoundary = false) const;
    136     bool scopeContainsLastMatchedElement(const SelectorCheckingContext&) const;
    137 
    138     static bool isFrameFocused(const Element&);
    139 
    140     bool m_strictParsing;
    141     bool m_documentIsHTML;
    142     Mode m_mode;
    143 };
    144 
    145 inline bool SelectorChecker::isCommonPseudoClassSelector(const CSSSelector& selector)
    146 {
    147     if (selector.match() != CSSSelector::PseudoClass)
    148         return false;
    149     CSSSelector::PseudoType pseudoType = selector.pseudoType();
    150     return pseudoType == CSSSelector::PseudoLink
    151         || pseudoType == CSSSelector::PseudoAnyLink
    152         || pseudoType == CSSSelector::PseudoVisited
    153         || pseudoType == CSSSelector::PseudoFocus;
    154 }
    155 
    156 inline bool SelectorChecker::tagMatches(const Element& element, const QualifiedName& tagQName)
    157 {
    158     if (tagQName == anyQName())
    159         return true;
    160     const AtomicString& localName = tagQName.localName();
    161     if (localName != starAtom && localName != element.localName())
    162         return false;
    163     const AtomicString& namespaceURI = tagQName.namespaceURI();
    164     return namespaceURI == starAtom || namespaceURI == element.namespaceURI();
    165 }
    166 
    167 inline bool SelectorChecker::checkExactAttribute(const Element& element, const QualifiedName& selectorAttributeName, const StringImpl* value)
    168 {
    169     if (!element.hasAttributesWithoutUpdate())
    170         return false;
    171     AttributeCollection attributes = element.attributes();
    172     AttributeCollection::const_iterator end = attributes.end();
    173     for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) {
    174         if (it->matches(selectorAttributeName) && (!value || it->value().impl() == value))
    175             return true;
    176     }
    177     return false;
    178 }
    179 
    180 inline bool SelectorChecker::isHostInItsShadowTree(const Element& element, BehaviorAtBoundary behaviorAtBoundary, const ContainerNode* scope)
    181 {
    182     return scope && scope->isInShadowTree() && scope->shadowHost() == element;
    183 }
    184 
    185 }
    186 
    187 #endif
    188