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