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 WebCore {
     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         };
    221 
    222         enum MarginBoxType {
    223             TopLeftCornerMarginBox,
    224             TopLeftMarginBox,
    225             TopCenterMarginBox,
    226             TopRightMarginBox,
    227             TopRightCornerMarginBox,
    228             BottomLeftCornerMarginBox,
    229             BottomLeftMarginBox,
    230             BottomCenterMarginBox,
    231             BottomRightMarginBox,
    232             BottomRightCornerMarginBox,
    233             LeftTopMarginBox,
    234             LeftMiddleMarginBox,
    235             LeftBottomMarginBox,
    236             RightTopMarginBox,
    237             RightMiddleMarginBox,
    238             RightBottomMarginBox,
    239         };
    240 
    241         PseudoType pseudoType() const
    242         {
    243             if (m_pseudoType == PseudoNotParsed)
    244                 extractPseudoType();
    245             return static_cast<PseudoType>(m_pseudoType);
    246         }
    247 
    248         static PseudoType parsePseudoType(const AtomicString&);
    249         static PseudoId pseudoId(PseudoType);
    250 
    251         // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
    252         // the next item in the array.
    253         const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
    254 
    255         const QualifiedName& tagQName() const;
    256         const AtomicString& value() const;
    257 
    258         // WARNING: Use of QualifiedName by attribute() is a lie.
    259         // attribute() will return a QualifiedName with prefix and namespaceURI
    260         // set to starAtom to mean "matches any namespace". Be very careful
    261         // how you use the returned QualifiedName.
    262         // http://www.w3.org/TR/css3-selectors/#attrnmsp
    263         const QualifiedName& attribute() const;
    264         // Returns the argument of a parameterized selector. For example, nth-child(2) would have an argument of 2.
    265         const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
    266         const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
    267 
    268 #ifndef NDEBUG
    269         void show() const;
    270         void show(int indent) const;
    271 #endif
    272 
    273         void setValue(const AtomicString&);
    274         void setAttribute(const QualifiedName&);
    275         void setArgument(const AtomicString&);
    276         void setSelectorList(PassOwnPtr<CSSSelectorList>);
    277         void setMatchUserAgentOnly();
    278 
    279         bool parseNth() const;
    280         bool matchNth(int count) const;
    281 
    282         bool matchesPseudoElement() const;
    283         bool isUnknownPseudoElement() const;
    284         bool isCustomPseudoElement() const;
    285         bool isDirectAdjacentSelector() const { return m_relation == DirectAdjacent; }
    286         bool isSiblingSelector() const;
    287         bool isAttributeSelector() const;
    288         bool isContentPseudoElement() const;
    289         bool isShadowPseudoElement() const;
    290         bool isHostPseudoClass() const;
    291 
    292         // FIXME: selectors with no tagHistory() get a relation() of Descendant (and sometimes even SubSelector). It should instead be
    293         // None.
    294         Relation relation() const { return static_cast<Relation>(m_relation); }
    295         void setRelation(Relation relation)
    296         {
    297             m_relation = relation;
    298             ASSERT(static_cast<Relation>(m_relation) == relation); // using a bitfield.
    299         }
    300 
    301         Match match() const { return static_cast<Match>(m_match); }
    302         void setMatch(Match match)
    303         {
    304             m_match = match;
    305             ASSERT(static_cast<Match>(m_match) == match); // using a bitfield.
    306         }
    307 
    308         bool isLastInSelectorList() const { return m_isLastInSelectorList; }
    309         void setLastInSelectorList() { m_isLastInSelectorList = true; }
    310         bool isLastInTagHistory() const { return m_isLastInTagHistory; }
    311         void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
    312 
    313         // http://dev.w3.org/csswg/selectors4/#compound
    314         bool isCompound() const;
    315 
    316         bool isForPage() const { return m_isForPage; }
    317         void setForPage() { m_isForPage = true; }
    318 
    319         bool relationIsAffectedByPseudoContent() const { return m_relationIsAffectedByPseudoContent; }
    320         void setRelationIsAffectedByPseudoContent() { m_relationIsAffectedByPseudoContent = true; }
    321 
    322     private:
    323         unsigned m_relation           : 3; // enum Relation
    324         mutable unsigned m_match      : 4; // enum Match
    325         mutable unsigned m_pseudoType : 8; // PseudoType
    326         mutable unsigned m_parsedNth      : 1; // Used for :nth-*
    327         unsigned m_isLastInSelectorList   : 1;
    328         unsigned m_isLastInTagHistory     : 1;
    329         unsigned m_hasRareData            : 1;
    330         unsigned m_isForPage              : 1;
    331         unsigned m_tagIsForNamespaceRule  : 1;
    332         unsigned m_relationIsAffectedByPseudoContent  : 1;
    333 
    334         unsigned specificityForOneSelector() const;
    335         unsigned specificityForPage() const;
    336         void extractPseudoType() const;
    337 
    338         // Hide.
    339         CSSSelector& operator=(const CSSSelector&);
    340 
    341         struct RareData : public RefCounted<RareData> {
    342             static PassRefPtr<RareData> create(const AtomicString& value) { return adoptRef(new RareData(value)); }
    343             ~RareData();
    344 
    345             bool parseNth();
    346             bool matchNth(int count);
    347 
    348             AtomicString m_value;
    349             int m_a; // Used for :nth-*
    350             int m_b; // Used for :nth-*
    351             QualifiedName m_attribute; // used for attribute selector
    352             AtomicString m_argument; // Used for :contains, :lang, :nth-*
    353             OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
    354 
    355         private:
    356             RareData(const AtomicString& value);
    357         };
    358         void createRareData();
    359 
    360         union DataUnion {
    361             DataUnion() : m_value(0) { }
    362             StringImpl* m_value;
    363             QualifiedName::QualifiedNameImpl* m_tagQName;
    364             RareData* m_rareData;
    365         } m_data;
    366     };
    367 
    368 inline const QualifiedName& CSSSelector::attribute() const
    369 {
    370     ASSERT(isAttributeSelector());
    371     ASSERT(m_hasRareData);
    372     return m_data.m_rareData->m_attribute;
    373 }
    374 
    375 inline bool CSSSelector::matchesPseudoElement() const
    376 {
    377     if (m_pseudoType == PseudoUnknown)
    378         extractPseudoType();
    379     return m_match == PseudoElement;
    380 }
    381 
    382 inline bool CSSSelector::isUnknownPseudoElement() const
    383 {
    384     return m_match == PseudoElement && m_pseudoType == PseudoUnknown;
    385 }
    386 
    387 inline bool CSSSelector::isCustomPseudoElement() const
    388 {
    389     return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement);
    390 }
    391 
    392 inline bool CSSSelector::isHostPseudoClass() const
    393 {
    394     return m_match == PseudoClass && (m_pseudoType == PseudoHost || m_pseudoType == PseudoHostContext);
    395 }
    396 
    397 inline bool CSSSelector::isSiblingSelector() const
    398 {
    399     PseudoType type = pseudoType();
    400     return m_relation == DirectAdjacent
    401         || m_relation == IndirectAdjacent
    402         || type == PseudoEmpty
    403         || type == PseudoFirstChild
    404         || type == PseudoFirstOfType
    405         || type == PseudoLastChild
    406         || type == PseudoLastOfType
    407         || type == PseudoOnlyChild
    408         || type == PseudoOnlyOfType
    409         || type == PseudoNthChild
    410         || type == PseudoNthOfType
    411         || type == PseudoNthLastChild
    412         || type == PseudoNthLastOfType;
    413 }
    414 
    415 inline bool CSSSelector::isAttributeSelector() const
    416 {
    417     return m_match >= FirstAttributeSelectorMatch;
    418 }
    419 
    420 inline bool CSSSelector::isContentPseudoElement() const
    421 {
    422     return m_match == PseudoElement && pseudoType() == PseudoContent;
    423 }
    424 
    425 inline bool CSSSelector::isShadowPseudoElement() const
    426 {
    427     return m_match == PseudoElement && pseudoType() == PseudoShadow;
    428 }
    429 
    430 inline void CSSSelector::setValue(const AtomicString& value)
    431 {
    432     ASSERT(m_match != Tag);
    433     ASSERT(m_pseudoType == PseudoNotParsed);
    434     // Need to do ref counting manually for the union.
    435     if (m_hasRareData) {
    436         m_data.m_rareData->m_value = value;
    437         return;
    438     }
    439     if (m_data.m_value)
    440         m_data.m_value->deref();
    441     m_data.m_value = value.impl();
    442     m_data.m_value->ref();
    443 }
    444 
    445 inline CSSSelector::CSSSelector()
    446     : m_relation(Descendant)
    447     , m_match(Unknown)
    448     , m_pseudoType(PseudoNotParsed)
    449     , m_parsedNth(false)
    450     , m_isLastInSelectorList(false)
    451     , m_isLastInTagHistory(true)
    452     , m_hasRareData(false)
    453     , m_isForPage(false)
    454     , m_tagIsForNamespaceRule(false)
    455     , m_relationIsAffectedByPseudoContent(false)
    456 {
    457 }
    458 
    459 inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
    460     : m_relation(Descendant)
    461     , m_match(Tag)
    462     , m_pseudoType(PseudoNotParsed)
    463     , m_parsedNth(false)
    464     , m_isLastInSelectorList(false)
    465     , m_isLastInTagHistory(true)
    466     , m_hasRareData(false)
    467     , m_isForPage(false)
    468     , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
    469     , m_relationIsAffectedByPseudoContent(false)
    470 {
    471     m_data.m_tagQName = tagQName.impl();
    472     m_data.m_tagQName->ref();
    473 }
    474 
    475 inline CSSSelector::CSSSelector(const CSSSelector& o)
    476     : m_relation(o.m_relation)
    477     , m_match(o.m_match)
    478     , m_pseudoType(o.m_pseudoType)
    479     , m_parsedNth(o.m_parsedNth)
    480     , m_isLastInSelectorList(o.m_isLastInSelectorList)
    481     , m_isLastInTagHistory(o.m_isLastInTagHistory)
    482     , m_hasRareData(o.m_hasRareData)
    483     , m_isForPage(o.m_isForPage)
    484     , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
    485     , m_relationIsAffectedByPseudoContent(o.m_relationIsAffectedByPseudoContent)
    486 {
    487     if (o.m_match == Tag) {
    488         m_data.m_tagQName = o.m_data.m_tagQName;
    489         m_data.m_tagQName->ref();
    490     } else if (o.m_hasRareData) {
    491         m_data.m_rareData = o.m_data.m_rareData;
    492         m_data.m_rareData->ref();
    493     } else if (o.m_data.m_value) {
    494         m_data.m_value = o.m_data.m_value;
    495         m_data.m_value->ref();
    496     }
    497 }
    498 
    499 inline CSSSelector::~CSSSelector()
    500 {
    501     if (m_match == Tag)
    502         m_data.m_tagQName->deref();
    503     else if (m_hasRareData)
    504         m_data.m_rareData->deref();
    505     else if (m_data.m_value)
    506         m_data.m_value->deref();
    507 }
    508 
    509 inline const QualifiedName& CSSSelector::tagQName() const
    510 {
    511     ASSERT(m_match == Tag);
    512     return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
    513 }
    514 
    515 inline const AtomicString& CSSSelector::value() const
    516 {
    517     ASSERT(m_match != Tag);
    518     if (m_hasRareData)
    519         return m_data.m_rareData->m_value;
    520     // AtomicString is really just a StringImpl* so the cast below is safe.
    521     // FIXME: Perhaps call sites could be changed to accept StringImpl?
    522     return *reinterpret_cast<const AtomicString*>(&m_data.m_value);
    523 }
    524 
    525 } // namespace WebCore
    526 
    527 #endif // CSSSelector_h
    528