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