Home | History | Annotate | Download | only in css
      1 /*
      2  * Copyright (C) 1999-2003 Lars Knoll (knoll (at) kde.org)
      3  *               1999 Waldo Bastian (bastian (at) kde.org)
      4  * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  */
     21 
     22 #ifndef CSSSelector_h
     23 #define CSSSelector_h
     24 
     25 #include "core/dom/QualifiedName.h"
     26 #include "core/rendering/style/RenderStyleConstants.h"
     27 #include "wtf/OwnPtr.h"
     28 #include "wtf/PassOwnPtr.h"
     29 
     30 namespace blink {
     31     class CSSSelectorList;
     32 
     33     // This class represents a selector for a StyleRule.
     34 
     35     // CSS selector representation is somewhat complicated and subtle. A representative list of selectors is
     36     // in CSSSelectorTest; run it in a debug build to see useful debugging output.
     37     //
     38     // ** tagHistory() and relation():
     39     //
     40     // Selectors are represented as a linked list of simple selectors (defined more or less according to
     41     // http://www.w3.org/TR/css3-selectors/#simple-selectors-dfn). The tagHistory() method returns the next
     42     // simple selector in the list. The relation() method returns the relationship of the current simple selector to
     43     // the one in tagHistory(). For example, the CSS selector .a.b #c is represented as:
     44     //
     45     // selectorText(): .a.b #c
     46     // --> (relation == Descendant)
     47     //   selectorText(): .a.b
     48     //   --> (relation == SubSelector)
     49     //     selectorText(): .b
     50     //
     51     // Note that currently a bare selector such as ".a" has a relation() of Descendant. This is a bug - instead the relation should be
     52     // "None".
     53     //
     54     // The order of tagHistory() varies depending on the situation.
     55     // * Relations using combinators (http://www.w3.org/TR/css3-selectors/#combinators), such as descendant, sibling, etc., are parsed
     56     //   right-to-left (in the example above, this is why .c is earlier in the tagHistory() chain than .a.b).
     57     // * SubSelector relations are parsed left-to-right in most cases (such as the .a.b example above); a counter-example is the
     58     //   ::content pseudo-element. Most (all?) other pseudo elements and pseudo classes are parsed left-to-right.
     59     // * ShadowPseudo relations are parsed right-to-left. Example: summary::-webkit-details-marker is parsed as:
     60     //   selectorText(): summary::-webkit-details-marker
     61     //    --> (relation == ShadowPseudo)
     62     //     selectorText(): summary
     63     //
     64     // ** match():
     65     //
     66     // The match of the current simple selector tells us the type of selector, such as class, id, tagname, or pseudo-class.
     67     // Inline comments in the Match enum give examples of when each type would occur.
     68     //
     69     // ** value(), attribute():
     70     //
     71     // value() tells you the value of the simple selector. For example, for class selectors, value() will tell you the class string,
     72     // and for id selectors it will tell you the id(). See below for the special case of attribute selectors.
     73     //
     74     // ** Attribute selectors.
     75     //
     76     // Attribute selectors return the attribute name in the attribute() method. The value() method returns the value matched against
     77     // in case of selectors like [attr="value"].
     78     //
     79     // ** isCustomPseudoElement():
     80     //
     81     // It appears this is used only for pseudo elements that appear in user-agent shadow DOM. They are not exposed to author-created
     82     // shadow DOM.
     83 
     84     class CSSSelector {
     85         WTF_MAKE_FAST_ALLOCATED;
     86     public:
     87         CSSSelector();
     88         CSSSelector(const CSSSelector&);
     89         explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false);
     90 
     91         ~CSSSelector();
     92 
     93         /**
     94          * Re-create selector text from selector's data
     95          */
     96         String selectorText(const String& = "") const;
     97 
     98         // checks if the 2 selectors (including sub selectors) agree.
     99         bool operator==(const CSSSelector&) const;
    100 
    101         // tag == -1 means apply to all elements (Selector = *)
    102 
    103         // http://www.w3.org/TR/css3-selectors/#specificity
    104         // We use 256 as the base of the specificity number system.
    105         unsigned specificity() const;
    106 
    107         /* how the attribute value has to match.... Default is Exact */
    108         enum Match {
    109             Unknown = 0,
    110             Tag, // Example: div
    111             Id, // Example: #id
    112             Class, // example: .class
    113             PseudoClass, // Example:  :nth-child(2)
    114             PseudoElement, // Example: ::first-line
    115             PagePseudoClass, // ??
    116             Exact, // Example: E[foo="bar"]
    117             Set, // Example: E[foo]
    118             Hyphen, // Example: E[foo|="bar"]
    119             List, // Example: E[foo~="bar"]
    120             Contain, // css3: E[foo*="bar"]
    121             Begin, // css3: E[foo^="bar"]
    122             End, // css3: E[foo$="bar"]
    123             FirstAttributeSelectorMatch = Exact,
    124         };
    125 
    126         enum Relation {
    127             Descendant = 0, // "Space" combinator
    128             Child, // > combinator
    129             DirectAdjacent, // + combinator
    130             IndirectAdjacent, // ~ combinator
    131             SubSelector, // "No space" combinator
    132             ShadowPseudo, // Special case of shadow DOM pseudo elements / shadow pseudo element
    133             ShadowDeep // /deep/ combinator
    134         };
    135 
    136         enum PseudoType {
    137             PseudoNotParsed = 0,
    138             PseudoUnknown,
    139             PseudoEmpty,
    140             PseudoFirstChild,
    141             PseudoFirstOfType,
    142             PseudoLastChild,
    143             PseudoLastOfType,
    144             PseudoOnlyChild,
    145             PseudoOnlyOfType,
    146             PseudoFirstLine,
    147             PseudoFirstLetter,
    148             PseudoNthChild,
    149             PseudoNthOfType,
    150             PseudoNthLastChild,
    151             PseudoNthLastOfType,
    152             PseudoLink,
    153             PseudoVisited,
    154             PseudoAny,
    155             PseudoAnyLink,
    156             PseudoAutofill,
    157             PseudoHover,
    158             PseudoDrag,
    159             PseudoFocus,
    160             PseudoActive,
    161             PseudoChecked,
    162             PseudoEnabled,
    163             PseudoFullPageMedia,
    164             PseudoDefault,
    165             PseudoDisabled,
    166             PseudoOptional,
    167             PseudoRequired,
    168             PseudoReadOnly,
    169             PseudoReadWrite,
    170             PseudoValid,
    171             PseudoInvalid,
    172             PseudoIndeterminate,
    173             PseudoTarget,
    174             PseudoBefore,
    175             PseudoAfter,
    176             PseudoBackdrop,
    177             PseudoLang,
    178             PseudoNot,
    179             PseudoResizer,
    180             PseudoRoot,
    181             PseudoScope,
    182             PseudoScrollbar,
    183             PseudoScrollbarBack,
    184             PseudoScrollbarButton,
    185             PseudoScrollbarCorner,
    186             PseudoScrollbarForward,
    187             PseudoScrollbarThumb,
    188             PseudoScrollbarTrack,
    189             PseudoScrollbarTrackPiece,
    190             PseudoWindowInactive,
    191             PseudoCornerPresent,
    192             PseudoDecrement,
    193             PseudoIncrement,
    194             PseudoHorizontal,
    195             PseudoVertical,
    196             PseudoStart,
    197             PseudoEnd,
    198             PseudoDoubleButton,
    199             PseudoSingleButton,
    200             PseudoNoButton,
    201             PseudoSelection,
    202             PseudoLeftPage,
    203             PseudoRightPage,
    204             PseudoFirstPage,
    205             PseudoFullScreen,
    206             PseudoFullScreenDocument,
    207             PseudoFullScreenAncestor,
    208             PseudoInRange,
    209             PseudoOutOfRange,
    210             PseudoUserAgentCustomElement,
    211             PseudoWebKitCustomElement,
    212             PseudoCue,
    213             PseudoFutureCue,
    214             PseudoPastCue,
    215             PseudoUnresolved,
    216             PseudoContent,
    217             PseudoHost,
    218             PseudoHostContext,
    219             PseudoShadow,
    220             PseudoSpatialNavigationFocus,
    221             PseudoListBox
    222         };
    223 
    224         enum MarginBoxType {
    225             TopLeftCornerMarginBox,
    226             TopLeftMarginBox,
    227             TopCenterMarginBox,
    228             TopRightMarginBox,
    229             TopRightCornerMarginBox,
    230             BottomLeftCornerMarginBox,
    231             BottomLeftMarginBox,
    232             BottomCenterMarginBox,
    233             BottomRightMarginBox,
    234             BottomRightCornerMarginBox,
    235             LeftTopMarginBox,
    236             LeftMiddleMarginBox,
    237             LeftBottomMarginBox,
    238             RightTopMarginBox,
    239             RightMiddleMarginBox,
    240             RightBottomMarginBox,
    241         };
    242 
    243         enum AttributeMatchType {
    244             CaseSensitive,
    245             CaseInsensitive,
    246         };
    247 
    248         PseudoType pseudoType() const
    249         {
    250             if (m_pseudoType == PseudoNotParsed)
    251                 extractPseudoType();
    252             return static_cast<PseudoType>(m_pseudoType);
    253         }
    254 
    255         static PseudoType parsePseudoType(const AtomicString&, bool hasArguments);
    256         static PseudoId pseudoId(PseudoType);
    257 
    258         // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
    259         // the next item in the array.
    260         const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
    261 
    262         const QualifiedName& tagQName() const;
    263         const AtomicString& value() const;
    264 
    265         // WARNING: Use of QualifiedName by attribute() is a lie.
    266         // attribute() will return a QualifiedName with prefix and namespaceURI
    267         // set to starAtom to mean "matches any namespace". Be very careful
    268         // how you use the returned QualifiedName.
    269         // http://www.w3.org/TR/css3-selectors/#attrnmsp
    270         const QualifiedName& attribute() const;
    271         AttributeMatchType attributeMatchType() const;
    272         // Returns the argument of a parameterized selector. For example, nth-child(2) would have an argument of 2.
    273         const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
    274         const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
    275 
    276 #ifndef NDEBUG
    277         void show() const;
    278         void show(int indent) const;
    279 #endif
    280 
    281         void setValue(const AtomicString&);
    282         void setAttribute(const QualifiedName&, AttributeMatchType);
    283         void setArgument(const AtomicString&);
    284         void setSelectorList(PassOwnPtr<CSSSelectorList>);
    285         void setMatchUserAgentOnly();
    286 
    287         bool parseNth() const;
    288         bool matchNth(int count) const;
    289 
    290         bool matchesPseudoElement() const;
    291         bool isCustomPseudoElement() const;
    292         bool isDirectAdjacentSelector() const { return m_relation == DirectAdjacent; }
    293         bool isSiblingSelector() const;
    294         bool isAttributeSelector() const;
    295         bool isContentPseudoElement() const;
    296         bool isShadowPseudoElement() const;
    297         bool isHostPseudoClass() const;
    298 
    299         // FIXME: selectors with no tagHistory() get a relation() of Descendant (and sometimes even SubSelector). It should instead be
    300         // None.
    301         Relation relation() const { return static_cast<Relation>(m_relation); }
    302         void setRelation(Relation relation)
    303         {
    304             m_relation = relation;
    305             ASSERT(static_cast<Relation>(m_relation) == relation); // using a bitfield.
    306         }
    307 
    308         Match match() const { return static_cast<Match>(m_match); }
    309         void setMatch(Match match)
    310         {
    311             m_match = match;
    312             ASSERT(static_cast<Match>(m_match) == match); // using a bitfield.
    313         }
    314 
    315         bool isLastInSelectorList() const { return m_isLastInSelectorList; }
    316         void setLastInSelectorList() { m_isLastInSelectorList = true; }
    317         bool isLastInTagHistory() const { return m_isLastInTagHistory; }
    318         void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
    319 
    320         // http://dev.w3.org/csswg/selectors4/#compound
    321         bool isCompound() const;
    322 
    323         bool isForPage() const { return m_isForPage; }
    324         void setForPage() { m_isForPage = true; }
    325 
    326         bool relationIsAffectedByPseudoContent() const { return m_relationIsAffectedByPseudoContent; }
    327         void setRelationIsAffectedByPseudoContent() { m_relationIsAffectedByPseudoContent = true; }
    328 
    329     private:
    330         unsigned m_relation           : 3; // enum Relation
    331         mutable unsigned m_match      : 4; // enum Match
    332         mutable unsigned m_pseudoType : 8; // PseudoType
    333         mutable unsigned m_parsedNth      : 1; // Used for :nth-*
    334         unsigned m_isLastInSelectorList   : 1;
    335         unsigned m_isLastInTagHistory     : 1;
    336         unsigned m_hasRareData            : 1;
    337         unsigned m_isForPage              : 1;
    338         unsigned m_tagIsForNamespaceRule  : 1;
    339         unsigned m_relationIsAffectedByPseudoContent  : 1;
    340 
    341         unsigned specificityForOneSelector() const;
    342         unsigned specificityForPage() const;
    343         void extractPseudoType() const;
    344 
    345         // Hide.
    346         CSSSelector& operator=(const CSSSelector&);
    347 
    348         struct RareData : public RefCounted<RareData> {
    349             static PassRefPtr<RareData> create(const AtomicString& value) { return adoptRef(new RareData(value)); }
    350             ~RareData();
    351 
    352             bool parseNth();
    353             bool matchNth(int count);
    354             int nthAValue() const { return m_bits.m_nth.m_a; }
    355             void setNthAValue(int nthA) { m_bits.m_nth.m_a = nthA; }
    356             int nthBValue() const { return m_bits.m_nth.m_b; }
    357             void setNthBValue(int nthB) { m_bits.m_nth.m_b = nthB; }
    358 
    359             AtomicString m_value;
    360             union {
    361                 struct {
    362                     int m_a; // Used for :nth-*
    363                     int m_b; // Used for :nth-*
    364                 } m_nth;
    365                 AttributeMatchType m_attributeMatchType; // used for attribute selector (with value)
    366             } m_bits;
    367             QualifiedName m_attribute; // used for attribute selector
    368             AtomicString m_argument; // Used for :contains, :lang, :nth-*
    369             OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
    370 
    371         private:
    372             RareData(const AtomicString& value);
    373         };
    374         void createRareData();
    375 
    376         union DataUnion {
    377             DataUnion() : m_value(0) { }
    378             StringImpl* m_value;
    379             QualifiedName::QualifiedNameImpl* m_tagQName;
    380             RareData* m_rareData;
    381         } m_data;
    382     };
    383 
    384 inline const QualifiedName& CSSSelector::attribute() const
    385 {
    386     ASSERT(isAttributeSelector());
    387     ASSERT(m_hasRareData);
    388     return m_data.m_rareData->m_attribute;
    389 }
    390 
    391 inline CSSSelector::AttributeMatchType CSSSelector::attributeMatchType() const
    392 {
    393     ASSERT(isAttributeSelector());
    394     ASSERT(m_hasRareData);
    395     return m_data.m_rareData->m_bits.m_attributeMatchType;
    396 }
    397 
    398 inline bool CSSSelector::matchesPseudoElement() const
    399 {
    400     if (m_pseudoType == PseudoUnknown)
    401         extractPseudoType();
    402     return m_match == PseudoElement;
    403 }
    404 
    405 inline bool CSSSelector::isCustomPseudoElement() const
    406 {
    407     return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement);
    408 }
    409 
    410 inline bool CSSSelector::isHostPseudoClass() const
    411 {
    412     return m_match == PseudoClass && (m_pseudoType == PseudoHost || m_pseudoType == PseudoHostContext);
    413 }
    414 
    415 inline bool CSSSelector::isSiblingSelector() const
    416 {
    417     PseudoType type = pseudoType();
    418     return m_relation == DirectAdjacent
    419         || m_relation == IndirectAdjacent
    420         || type == PseudoEmpty
    421         || type == PseudoFirstChild
    422         || type == PseudoFirstOfType
    423         || type == PseudoLastChild
    424         || type == PseudoLastOfType
    425         || type == PseudoOnlyChild
    426         || type == PseudoOnlyOfType
    427         || type == PseudoNthChild
    428         || type == PseudoNthOfType
    429         || type == PseudoNthLastChild
    430         || type == PseudoNthLastOfType;
    431 }
    432 
    433 inline bool CSSSelector::isAttributeSelector() const
    434 {
    435     return m_match >= FirstAttributeSelectorMatch;
    436 }
    437 
    438 inline bool CSSSelector::isContentPseudoElement() const
    439 {
    440     return m_match == PseudoElement && pseudoType() == PseudoContent;
    441 }
    442 
    443 inline bool CSSSelector::isShadowPseudoElement() const
    444 {
    445     return m_match == PseudoElement && pseudoType() == PseudoShadow;
    446 }
    447 
    448 inline void CSSSelector::setValue(const AtomicString& value)
    449 {
    450     ASSERT(m_match != Tag);
    451     ASSERT(m_pseudoType == PseudoNotParsed);
    452     // Need to do ref counting manually for the union.
    453     if (m_hasRareData) {
    454         m_data.m_rareData->m_value = value;
    455         return;
    456     }
    457     if (m_data.m_value)
    458         m_data.m_value->deref();
    459     m_data.m_value = value.impl();
    460     m_data.m_value->ref();
    461 }
    462 
    463 inline CSSSelector::CSSSelector()
    464     : m_relation(Descendant)
    465     , m_match(Unknown)
    466     , m_pseudoType(PseudoNotParsed)
    467     , m_parsedNth(false)
    468     , m_isLastInSelectorList(false)
    469     , m_isLastInTagHistory(true)
    470     , m_hasRareData(false)
    471     , m_isForPage(false)
    472     , m_tagIsForNamespaceRule(false)
    473     , m_relationIsAffectedByPseudoContent(false)
    474 {
    475 }
    476 
    477 inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
    478     : m_relation(Descendant)
    479     , m_match(Tag)
    480     , m_pseudoType(PseudoNotParsed)
    481     , m_parsedNth(false)
    482     , m_isLastInSelectorList(false)
    483     , m_isLastInTagHistory(true)
    484     , m_hasRareData(false)
    485     , m_isForPage(false)
    486     , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
    487     , m_relationIsAffectedByPseudoContent(false)
    488 {
    489     m_data.m_tagQName = tagQName.impl();
    490     m_data.m_tagQName->ref();
    491 }
    492 
    493 inline CSSSelector::CSSSelector(const CSSSelector& o)
    494     : m_relation(o.m_relation)
    495     , m_match(o.m_match)
    496     , m_pseudoType(o.m_pseudoType)
    497     , m_parsedNth(o.m_parsedNth)
    498     , m_isLastInSelectorList(o.m_isLastInSelectorList)
    499     , m_isLastInTagHistory(o.m_isLastInTagHistory)
    500     , m_hasRareData(o.m_hasRareData)
    501     , m_isForPage(o.m_isForPage)
    502     , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
    503     , m_relationIsAffectedByPseudoContent(o.m_relationIsAffectedByPseudoContent)
    504 {
    505     if (o.m_match == Tag) {
    506         m_data.m_tagQName = o.m_data.m_tagQName;
    507         m_data.m_tagQName->ref();
    508     } else if (o.m_hasRareData) {
    509         m_data.m_rareData = o.m_data.m_rareData;
    510         m_data.m_rareData->ref();
    511     } else if (o.m_data.m_value) {
    512         m_data.m_value = o.m_data.m_value;
    513         m_data.m_value->ref();
    514     }
    515 }
    516 
    517 inline CSSSelector::~CSSSelector()
    518 {
    519     if (m_match == Tag)
    520         m_data.m_tagQName->deref();
    521     else if (m_hasRareData)
    522         m_data.m_rareData->deref();
    523     else if (m_data.m_value)
    524         m_data.m_value->deref();
    525 }
    526 
    527 inline const QualifiedName& CSSSelector::tagQName() const
    528 {
    529     ASSERT(m_match == Tag);
    530     return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
    531 }
    532 
    533 inline const AtomicString& CSSSelector::value() const
    534 {
    535     ASSERT(m_match != Tag);
    536     if (m_hasRareData)
    537         return m_data.m_rareData->m_value;
    538     // AtomicString is really just a StringImpl* so the cast below is safe.
    539     // FIXME: Perhaps call sites could be changed to accept StringImpl?
    540     return *reinterpret_cast<const AtomicString*>(&m_data.m_value);
    541 }
    542 
    543 } // namespace blink
    544 
    545 #endif // CSSSelector_h
    546