1/* 2 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) 3 * 1999 Waldo Bastian (bastian@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 "QualifiedName.h" 26#include "RenderStyleConstants.h" 27#include <wtf/Noncopyable.h> 28#include <wtf/OwnPtr.h> 29#include <wtf/PassOwnPtr.h> 30 31namespace WebCore { 32 class CSSSelectorList; 33 34 // this class represents a selector for a StyleRule 35 class CSSSelector { 36 WTF_MAKE_FAST_ALLOCATED; 37 public: 38 CSSSelector(); 39 CSSSelector(const CSSSelector&); 40 explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false); 41 42 ~CSSSelector(); 43 44 /** 45 * Re-create selector text from selector's data 46 */ 47 String selectorText(const String& = "") const; 48 49 // checks if the 2 selectors (including sub selectors) agree. 50 bool operator==(const CSSSelector&) const; 51 52 // tag == -1 means apply to all elements (Selector = *) 53 54 unsigned specificity() const; 55 56 /* how the attribute value has to match.... Default is Exact */ 57 enum Match { 58 Unknown = 0, 59 Tag, 60 Id, 61 Class, 62 Exact, 63 Set, 64 List, 65 Hyphen, 66 PseudoClass, 67 PseudoElement, 68 Contain, // css3: E[foo*="bar"] 69 Begin, // css3: E[foo^="bar"] 70 End, // css3: E[foo$="bar"] 71 PagePseudoClass 72 }; 73 74 enum Relation { 75 Descendant = 0, 76 Child, 77 DirectAdjacent, 78 IndirectAdjacent, 79 SubSelector, 80 ShadowDescendant, 81 }; 82 83 enum PseudoType { 84 PseudoNotParsed = 0, 85 PseudoUnknown, 86 PseudoEmpty, 87 PseudoFirstChild, 88 PseudoFirstOfType, 89 PseudoLastChild, 90 PseudoLastOfType, 91 PseudoOnlyChild, 92 PseudoOnlyOfType, 93 PseudoFirstLine, 94 PseudoFirstLetter, 95 PseudoNthChild, 96 PseudoNthOfType, 97 PseudoNthLastChild, 98 PseudoNthLastOfType, 99 PseudoLink, 100 PseudoVisited, 101 PseudoAny, 102 PseudoAnyLink, 103 PseudoAutofill, 104 PseudoHover, 105 PseudoDrag, 106 PseudoFocus, 107 PseudoActive, 108 PseudoChecked, 109 PseudoEnabled, 110 PseudoFullPageMedia, 111 PseudoDefault, 112 PseudoDisabled, 113 PseudoOptional, 114 PseudoRequired, 115 PseudoReadOnly, 116 PseudoReadWrite, 117 PseudoValid, 118 PseudoInvalid, 119 PseudoIndeterminate, 120 PseudoTarget, 121 PseudoBefore, 122 PseudoAfter, 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#if ENABLE(FULLSCREEN_API) 152 PseudoFullScreen, 153 PseudoFullScreenDocument, 154 PseudoFullScreenAncestor, 155 PseudoAnimatingFullScreenTransition, 156#endif 157 PseudoInRange, 158 PseudoOutOfRange, 159 PseudoUserAgentCustomElement, 160 PseudoWebKitCustomElement, 161#if ENABLE(VIDEO_TRACK) 162 PseudoCue, 163 PseudoFutureCue, 164 PseudoPastCue, 165#endif 166#if ENABLE(IFRAME_SEAMLESS) 167 PseudoSeamlessDocument, 168#endif 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 const QualifiedName& attribute() const; 207 const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; } 208 const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; } 209 210 void setValue(const AtomicString&); 211 void setAttribute(const QualifiedName&); 212 void setArgument(const AtomicString&); 213 void setSelectorList(PassOwnPtr<CSSSelectorList>); 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 224 Relation relation() const { return static_cast<Relation>(m_relation); } 225 226 bool isLastInSelectorList() const { return m_isLastInSelectorList; } 227 void setLastInSelectorList() { m_isLastInSelectorList = true; } 228 bool isLastInTagHistory() const { return m_isLastInTagHistory; } 229 void setNotLastInTagHistory() { m_isLastInTagHistory = false; } 230 231 bool isSimple() const; 232 233 bool isForPage() const { return m_isForPage; } 234 void setForPage() { m_isForPage = true; } 235 236 unsigned m_relation : 3; // enum Relation 237 mutable unsigned m_match : 4; // enum Match 238 mutable unsigned m_pseudoType : 8; // PseudoType 239 240 private: 241 mutable bool m_parsedNth : 1; // Used for :nth-* 242 bool m_isLastInSelectorList : 1; 243 bool m_isLastInTagHistory : 1; 244 bool m_hasRareData : 1; 245 bool m_isForPage : 1; 246 bool m_tagIsForNamespaceRule : 1; 247 248 unsigned specificityForOneSelector() const; 249 unsigned specificityForPage() const; 250 void extractPseudoType() const; 251 252 // Hide. 253 CSSSelector& operator=(const CSSSelector&); 254 255 struct RareData : public RefCounted<RareData> { 256 static PassRefPtr<RareData> create(PassRefPtr<AtomicStringImpl> value) { return adoptRef(new RareData(value)); } 257 ~RareData(); 258 259 bool parseNth(); 260 bool matchNth(int count); 261 262 AtomicStringImpl* m_value; // Plain pointer to keep things uniform with the union. 263 int m_a; // Used for :nth-* 264 int m_b; // Used for :nth-* 265 QualifiedName m_attribute; // used for attribute selector 266 AtomicString m_argument; // Used for :contains, :lang and :nth-* 267 OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not 268 269 private: 270 RareData(PassRefPtr<AtomicStringImpl> value); 271 }; 272 void createRareData(); 273 274 union DataUnion { 275 DataUnion() : m_value(0) { } 276 AtomicStringImpl* m_value; 277 QualifiedName::QualifiedNameImpl* m_tagQName; 278 RareData* m_rareData; 279 } m_data; 280 }; 281 282inline const QualifiedName& CSSSelector::attribute() const 283{ 284 ASSERT(isAttributeSelector()); 285 ASSERT(m_hasRareData); 286 return m_data.m_rareData->m_attribute; 287} 288 289inline bool CSSSelector::matchesPseudoElement() const 290{ 291 if (m_pseudoType == PseudoUnknown) 292 extractPseudoType(); 293 return m_match == PseudoElement; 294} 295 296inline bool CSSSelector::isUnknownPseudoElement() const 297{ 298 return m_match == PseudoElement && m_pseudoType == PseudoUnknown; 299} 300 301inline bool CSSSelector::isCustomPseudoElement() const 302{ 303 return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement); 304} 305 306inline bool CSSSelector::isSiblingSelector() const 307{ 308 PseudoType type = pseudoType(); 309 return m_relation == DirectAdjacent 310 || m_relation == IndirectAdjacent 311 || type == PseudoEmpty 312 || type == PseudoFirstChild 313 || type == PseudoFirstOfType 314 || type == PseudoLastChild 315 || type == PseudoLastOfType 316 || type == PseudoOnlyChild 317 || type == PseudoOnlyOfType 318 || type == PseudoNthChild 319 || type == PseudoNthOfType 320 || type == PseudoNthLastChild 321 || type == PseudoNthLastOfType; 322} 323 324inline bool CSSSelector::isAttributeSelector() const 325{ 326 return m_match == CSSSelector::Exact 327 || m_match == CSSSelector::Set 328 || m_match == CSSSelector::List 329 || m_match == CSSSelector::Hyphen 330 || m_match == CSSSelector::Contain 331 || m_match == CSSSelector::Begin 332 || m_match == CSSSelector::End; 333} 334 335inline void CSSSelector::setValue(const AtomicString& value) 336{ 337 ASSERT(m_match != Tag); 338 ASSERT(m_pseudoType == PseudoNotParsed); 339 // Need to do ref counting manually for the union. 340 if (m_hasRareData) { 341 if (m_data.m_rareData->m_value) 342 m_data.m_rareData->m_value->deref(); 343 m_data.m_rareData->m_value = value.impl(); 344 m_data.m_rareData->m_value->ref(); 345 return; 346 } 347 if (m_data.m_value) 348 m_data.m_value->deref(); 349 m_data.m_value = value.impl(); 350 m_data.m_value->ref(); 351} 352 353inline CSSSelector::CSSSelector() 354 : m_relation(Descendant) 355 , m_match(Unknown) 356 , m_pseudoType(PseudoNotParsed) 357 , m_parsedNth(false) 358 , m_isLastInSelectorList(false) 359 , m_isLastInTagHistory(true) 360 , m_hasRareData(false) 361 , m_isForPage(false) 362 , m_tagIsForNamespaceRule(false) 363{ 364} 365 366inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule) 367 : m_relation(Descendant) 368 , m_match(Tag) 369 , m_pseudoType(PseudoNotParsed) 370 , m_parsedNth(false) 371 , m_isLastInSelectorList(false) 372 , m_isLastInTagHistory(true) 373 , m_hasRareData(false) 374 , m_isForPage(false) 375 , m_tagIsForNamespaceRule(tagIsForNamespaceRule) 376{ 377 m_data.m_tagQName = tagQName.impl(); 378 m_data.m_tagQName->ref(); 379} 380 381inline CSSSelector::CSSSelector(const CSSSelector& o) 382 : m_relation(o.m_relation) 383 , m_match(o.m_match) 384 , m_pseudoType(o.m_pseudoType) 385 , m_parsedNth(o.m_parsedNth) 386 , m_isLastInSelectorList(o.m_isLastInSelectorList) 387 , m_isLastInTagHistory(o.m_isLastInTagHistory) 388 , m_hasRareData(o.m_hasRareData) 389 , m_isForPage(o.m_isForPage) 390 , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule) 391{ 392 if (o.m_match == Tag) { 393 m_data.m_tagQName = o.m_data.m_tagQName; 394 m_data.m_tagQName->ref(); 395 } else if (o.m_hasRareData) { 396 m_data.m_rareData = o.m_data.m_rareData; 397 m_data.m_rareData->ref(); 398 } else if (o.m_data.m_value) { 399 m_data.m_value = o.m_data.m_value; 400 m_data.m_value->ref(); 401 } 402} 403 404inline CSSSelector::~CSSSelector() 405{ 406 if (m_match == Tag) 407 m_data.m_tagQName->deref(); 408 else if (m_hasRareData) 409 m_data.m_rareData->deref(); 410 else if (m_data.m_value) 411 m_data.m_value->deref(); 412} 413 414inline const QualifiedName& CSSSelector::tagQName() const 415{ 416 ASSERT(m_match == Tag); 417 return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName); 418} 419 420inline const AtomicString& CSSSelector::value() const 421{ 422 ASSERT(m_match != Tag); 423 // AtomicString is really just an AtomicStringImpl* so the cast below is safe. 424 // FIXME: Perhaps call sites could be changed to accept AtomicStringImpl? 425 return *reinterpret_cast<const AtomicString*>(m_hasRareData ? &m_data.m_rareData->m_value : &m_data.m_value); 426} 427 428 429} // namespace WebCore 430 431#endif // CSSSelector_h 432