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     class CSSSelector {
     35         WTF_MAKE_FAST_ALLOCATED;
     36     public:
     37         CSSSelector();
     38         CSSSelector(const CSSSelector&);
     39         explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false);
     40 
     41         ~CSSSelector();
     42 
     43         /**
     44          * Re-create selector text from selector's data
     45          */
     46         String selectorText(const String& = "") const;
     47 
     48         // checks if the 2 selectors (including sub selectors) agree.
     49         bool operator==(const CSSSelector&) const;
     50 
     51         // tag == -1 means apply to all elements (Selector = *)
     52 
     53         unsigned specificity() const;
     54 
     55         /* how the attribute value has to match.... Default is Exact */
     56         enum Match {
     57             Unknown = 0,
     58             Tag,
     59             Id,
     60             Class,
     61             Exact,
     62             Set,
     63             List,
     64             Hyphen,
     65             PseudoClass,
     66             PseudoElement,
     67             Contain, // css3: E[foo*="bar"]
     68             Begin, // css3: E[foo^="bar"]
     69             End, // css3: E[foo$="bar"]
     70             PagePseudoClass
     71         };
     72 
     73         enum Relation {
     74             Descendant = 0,
     75             Child,
     76             DirectAdjacent,
     77             IndirectAdjacent,
     78             SubSelector,
     79             ShadowPseudo,
     80             // FIXME: rename ChildTree and DescendantTree when the spec for this is written down.
     81             ChildTree,
     82             DescendantTree
     83         };
     84 
     85         enum PseudoType {
     86             PseudoNotParsed = 0,
     87             PseudoUnknown,
     88             PseudoEmpty,
     89             PseudoFirstChild,
     90             PseudoFirstOfType,
     91             PseudoLastChild,
     92             PseudoLastOfType,
     93             PseudoOnlyChild,
     94             PseudoOnlyOfType,
     95             PseudoFirstLine,
     96             PseudoFirstLetter,
     97             PseudoNthChild,
     98             PseudoNthOfType,
     99             PseudoNthLastChild,
    100             PseudoNthLastOfType,
    101             PseudoLink,
    102             PseudoVisited,
    103             PseudoAny,
    104             PseudoAnyLink,
    105             PseudoAutofill,
    106             PseudoHover,
    107             PseudoDrag,
    108             PseudoFocus,
    109             PseudoActive,
    110             PseudoChecked,
    111             PseudoEnabled,
    112             PseudoFullPageMedia,
    113             PseudoDefault,
    114             PseudoDisabled,
    115             PseudoOptional,
    116             PseudoRequired,
    117             PseudoReadOnly,
    118             PseudoReadWrite,
    119             PseudoValid,
    120             PseudoInvalid,
    121             PseudoIndeterminate,
    122             PseudoTarget,
    123             PseudoBefore,
    124             PseudoAfter,
    125             PseudoBackdrop,
    126             PseudoLang,
    127             PseudoNot,
    128             PseudoResizer,
    129             PseudoRoot,
    130             PseudoScope,
    131             PseudoScrollbar,
    132             PseudoScrollbarBack,
    133             PseudoScrollbarButton,
    134             PseudoScrollbarCorner,
    135             PseudoScrollbarForward,
    136             PseudoScrollbarThumb,
    137             PseudoScrollbarTrack,
    138             PseudoScrollbarTrackPiece,
    139             PseudoWindowInactive,
    140             PseudoCornerPresent,
    141             PseudoDecrement,
    142             PseudoIncrement,
    143             PseudoHorizontal,
    144             PseudoVertical,
    145             PseudoStart,
    146             PseudoEnd,
    147             PseudoDoubleButton,
    148             PseudoSingleButton,
    149             PseudoNoButton,
    150             PseudoSelection,
    151             PseudoLeftPage,
    152             PseudoRightPage,
    153             PseudoFirstPage,
    154             PseudoFullScreen,
    155             PseudoFullScreenDocument,
    156             PseudoFullScreenAncestor,
    157             PseudoInRange,
    158             PseudoOutOfRange,
    159             PseudoUserAgentCustomElement,
    160             PseudoWebKitCustomElement,
    161             PseudoCue,
    162             PseudoFutureCue,
    163             PseudoPastCue,
    164             PseudoSeamlessDocument,
    165             PseudoDistributed,
    166             PseudoUnresolved,
    167             PseudoContent,
    168             PseudoHost
    169         };
    170 
    171         enum MarginBoxType {
    172             TopLeftCornerMarginBox,
    173             TopLeftMarginBox,
    174             TopCenterMarginBox,
    175             TopRightMarginBox,
    176             TopRightCornerMarginBox,
    177             BottomLeftCornerMarginBox,
    178             BottomLeftMarginBox,
    179             BottomCenterMarginBox,
    180             BottomRightMarginBox,
    181             BottomRightCornerMarginBox,
    182             LeftTopMarginBox,
    183             LeftMiddleMarginBox,
    184             LeftBottomMarginBox,
    185             RightTopMarginBox,
    186             RightMiddleMarginBox,
    187             RightBottomMarginBox,
    188         };
    189 
    190         PseudoType pseudoType() const
    191         {
    192             if (m_pseudoType == PseudoNotParsed)
    193                 extractPseudoType();
    194             return static_cast<PseudoType>(m_pseudoType);
    195         }
    196 
    197         static PseudoType parsePseudoType(const AtomicString&);
    198         static PseudoId pseudoId(PseudoType);
    199 
    200         // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
    201         // the next item in the array.
    202         const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
    203 
    204         const QualifiedName& tagQName() const;
    205         const AtomicString& value() const;
    206 
    207         // WARNING: Use of QualifiedName by attribute() is a lie.
    208         // attribute() will return a QualifiedName with prefix and namespaceURI
    209         // set to starAtom to mean "matches any namespace". Be very careful
    210         // how you use the returned QualifiedName.
    211         // http://www.w3.org/TR/css3-selectors/#attrnmsp
    212         const QualifiedName& attribute() const;
    213         const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
    214         const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
    215 
    216         void setValue(const AtomicString&);
    217         void setAttribute(const QualifiedName&);
    218         void setArgument(const AtomicString&);
    219         void setSelectorList(PassOwnPtr<CSSSelectorList>);
    220         void setMatchUserAgentOnly();
    221 
    222         bool parseNth() const;
    223         bool matchNth(int count) const;
    224 
    225         bool matchesPseudoElement() const;
    226         bool isUnknownPseudoElement() const;
    227         bool isCustomPseudoElement() const;
    228         bool isDirectAdjacentSelector() const { return m_relation == DirectAdjacent; }
    229         bool isSiblingSelector() const;
    230         bool isAttributeSelector() const;
    231         bool isDistributedPseudoElement() const;
    232         bool isContentPseudoElement() const;
    233         bool isHostPseudoClass() const;
    234 
    235         Relation relation() const { return static_cast<Relation>(m_relation); }
    236 
    237         bool isLastInSelectorList() const { return m_isLastInSelectorList; }
    238         void setLastInSelectorList() { m_isLastInSelectorList = true; }
    239         bool isLastInTagHistory() const { return m_isLastInTagHistory; }
    240         void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
    241 
    242         // http://dev.w3.org/csswg/selectors4/#compound
    243         bool isCompound() const;
    244 
    245         bool isForPage() const { return m_isForPage; }
    246         void setForPage() { m_isForPage = true; }
    247 
    248         bool relationIsAffectedByPseudoContent() const { return m_relationIsAffectedByPseudoContent; }
    249         void setRelationIsAffectedByPseudoContent() { m_relationIsAffectedByPseudoContent = true; }
    250 
    251         unsigned m_relation           : 3; // enum Relation
    252         mutable unsigned m_match      : 4; // enum Match
    253         mutable unsigned m_pseudoType : 8; // PseudoType
    254 
    255     private:
    256         mutable unsigned m_parsedNth      : 1; // Used for :nth-*
    257         unsigned m_isLastInSelectorList   : 1;
    258         unsigned m_isLastInTagHistory     : 1;
    259         unsigned m_hasRareData            : 1;
    260         unsigned m_isForPage              : 1;
    261         unsigned m_tagIsForNamespaceRule  : 1;
    262         unsigned m_relationIsAffectedByPseudoContent  : 1;
    263 
    264         unsigned specificityForOneSelector() const;
    265         unsigned specificityForPage() const;
    266         void extractPseudoType() const;
    267 
    268         // Hide.
    269         CSSSelector& operator=(const CSSSelector&);
    270 
    271         struct RareData : public RefCounted<RareData> {
    272             static PassRefPtr<RareData> create(PassRefPtr<StringImpl> value) { return adoptRef(new RareData(value)); }
    273             ~RareData();
    274 
    275             bool parseNth();
    276             bool matchNth(int count);
    277 
    278             StringImpl* m_value; // Plain pointer to keep things uniform with the union.
    279             int m_a; // Used for :nth-*
    280             int m_b; // Used for :nth-*
    281             QualifiedName m_attribute; // used for attribute selector
    282             AtomicString m_argument; // Used for :contains, :lang, :nth-*
    283             OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
    284 
    285         private:
    286             RareData(PassRefPtr<StringImpl> value);
    287         };
    288         void createRareData();
    289 
    290         union DataUnion {
    291             DataUnion() : m_value(0) { }
    292             StringImpl* m_value;
    293             QualifiedName::QualifiedNameImpl* m_tagQName;
    294             RareData* m_rareData;
    295         } m_data;
    296     };
    297 
    298 inline const QualifiedName& CSSSelector::attribute() const
    299 {
    300     ASSERT(isAttributeSelector());
    301     ASSERT(m_hasRareData);
    302     return m_data.m_rareData->m_attribute;
    303 }
    304 
    305 inline bool CSSSelector::matchesPseudoElement() const
    306 {
    307     if (m_pseudoType == PseudoUnknown)
    308         extractPseudoType();
    309     return m_match == PseudoElement;
    310 }
    311 
    312 inline bool CSSSelector::isUnknownPseudoElement() const
    313 {
    314     return m_match == PseudoElement && m_pseudoType == PseudoUnknown;
    315 }
    316 
    317 inline bool CSSSelector::isCustomPseudoElement() const
    318 {
    319     return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement);
    320 }
    321 
    322 inline bool CSSSelector::isHostPseudoClass() const
    323 {
    324     return m_match == PseudoClass && m_pseudoType == PseudoHost;
    325 }
    326 
    327 inline bool CSSSelector::isSiblingSelector() const
    328 {
    329     PseudoType type = pseudoType();
    330     return m_relation == DirectAdjacent
    331         || m_relation == IndirectAdjacent
    332         || type == PseudoEmpty
    333         || type == PseudoFirstChild
    334         || type == PseudoFirstOfType
    335         || type == PseudoLastChild
    336         || type == PseudoLastOfType
    337         || type == PseudoOnlyChild
    338         || type == PseudoOnlyOfType
    339         || type == PseudoNthChild
    340         || type == PseudoNthOfType
    341         || type == PseudoNthLastChild
    342         || type == PseudoNthLastOfType;
    343 }
    344 
    345 inline bool CSSSelector::isAttributeSelector() const
    346 {
    347     return m_match == CSSSelector::Exact
    348         || m_match ==  CSSSelector::Set
    349         || m_match == CSSSelector::List
    350         || m_match == CSSSelector::Hyphen
    351         || m_match == CSSSelector::Contain
    352         || m_match == CSSSelector::Begin
    353         || m_match == CSSSelector::End;
    354 }
    355 
    356 inline bool CSSSelector::isDistributedPseudoElement() const
    357 {
    358     return m_match == PseudoElement && pseudoType() == PseudoDistributed;
    359 }
    360 
    361 inline bool CSSSelector::isContentPseudoElement() const
    362 {
    363     return m_match == PseudoElement && pseudoType() == PseudoContent;
    364 }
    365 
    366 inline void CSSSelector::setValue(const AtomicString& value)
    367 {
    368     ASSERT(m_match != Tag);
    369     ASSERT(m_pseudoType == PseudoNotParsed);
    370     // Need to do ref counting manually for the union.
    371     if (m_hasRareData) {
    372         if (m_data.m_rareData->m_value)
    373             m_data.m_rareData->m_value->deref();
    374         m_data.m_rareData->m_value = value.impl();
    375         m_data.m_rareData->m_value->ref();
    376         return;
    377     }
    378     if (m_data.m_value)
    379         m_data.m_value->deref();
    380     m_data.m_value = value.impl();
    381     m_data.m_value->ref();
    382 }
    383 
    384 inline CSSSelector::CSSSelector()
    385     : m_relation(Descendant)
    386     , m_match(Unknown)
    387     , m_pseudoType(PseudoNotParsed)
    388     , m_parsedNth(false)
    389     , m_isLastInSelectorList(false)
    390     , m_isLastInTagHistory(true)
    391     , m_hasRareData(false)
    392     , m_isForPage(false)
    393     , m_tagIsForNamespaceRule(false)
    394     , m_relationIsAffectedByPseudoContent(false)
    395 {
    396 }
    397 
    398 inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
    399     : m_relation(Descendant)
    400     , m_match(Tag)
    401     , m_pseudoType(PseudoNotParsed)
    402     , m_parsedNth(false)
    403     , m_isLastInSelectorList(false)
    404     , m_isLastInTagHistory(true)
    405     , m_hasRareData(false)
    406     , m_isForPage(false)
    407     , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
    408     , m_relationIsAffectedByPseudoContent(false)
    409 {
    410     m_data.m_tagQName = tagQName.impl();
    411     m_data.m_tagQName->ref();
    412 }
    413 
    414 inline CSSSelector::CSSSelector(const CSSSelector& o)
    415     : m_relation(o.m_relation)
    416     , m_match(o.m_match)
    417     , m_pseudoType(o.m_pseudoType)
    418     , m_parsedNth(o.m_parsedNth)
    419     , m_isLastInSelectorList(o.m_isLastInSelectorList)
    420     , m_isLastInTagHistory(o.m_isLastInTagHistory)
    421     , m_hasRareData(o.m_hasRareData)
    422     , m_isForPage(o.m_isForPage)
    423     , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
    424     , m_relationIsAffectedByPseudoContent(o.m_relationIsAffectedByPseudoContent)
    425 {
    426     if (o.m_match == Tag) {
    427         m_data.m_tagQName = o.m_data.m_tagQName;
    428         m_data.m_tagQName->ref();
    429     } else if (o.m_hasRareData) {
    430         m_data.m_rareData = o.m_data.m_rareData;
    431         m_data.m_rareData->ref();
    432     } else if (o.m_data.m_value) {
    433         m_data.m_value = o.m_data.m_value;
    434         m_data.m_value->ref();
    435     }
    436 }
    437 
    438 inline CSSSelector::~CSSSelector()
    439 {
    440     if (m_match == Tag)
    441         m_data.m_tagQName->deref();
    442     else if (m_hasRareData)
    443         m_data.m_rareData->deref();
    444     else if (m_data.m_value)
    445         m_data.m_value->deref();
    446 }
    447 
    448 inline const QualifiedName& CSSSelector::tagQName() const
    449 {
    450     ASSERT(m_match == Tag);
    451     return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
    452 }
    453 
    454 inline const AtomicString& CSSSelector::value() const
    455 {
    456     ASSERT(m_match != Tag);
    457     // AtomicString is really just a StringImpl* so the cast below is safe.
    458     // FIXME: Perhaps call sites could be changed to accept StringImpl?
    459     return *reinterpret_cast<const AtomicString*>(m_hasRareData ? &m_data.m_rareData->m_value : &m_data.m_value);
    460 }
    461 
    462 
    463 } // namespace WebCore
    464 
    465 #endif // CSSSelector_h
    466