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