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 29namespace WebCore { 30 class CSSSelectorList; 31 32 // this class represents a selector for a StyleRule 33 class CSSSelector { 34 WTF_MAKE_FAST_ALLOCATED; 35 public: 36 CSSSelector(); 37 CSSSelector(const CSSSelector&); 38 explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false); 39 40 ~CSSSelector(); 41 42 /** 43 * Re-create selector text from selector's data 44 */ 45 String selectorText(const String& = "") const; 46 47 // checks if the 2 selectors (including sub selectors) agree. 48 bool operator==(const CSSSelector&) const; 49 50 // tag == -1 means apply to all elements (Selector = *) 51 52 unsigned specificity() const; 53 54 /* how the attribute value has to match.... Default is Exact */ 55 enum Match { 56 Unknown = 0, 57 Tag, 58 Id, 59 Class, 60 Exact, 61 Set, 62 List, 63 Hyphen, 64 PseudoClass, 65 PseudoElement, 66 Contain, // css3: E[foo*="bar"] 67 Begin, // css3: E[foo^="bar"] 68 End, // css3: E[foo$="bar"] 69 PagePseudoClass 70 }; 71 72 enum Relation { 73 Descendant = 0, 74 Child, 75 DirectAdjacent, 76 IndirectAdjacent, 77 SubSelector, 78 ShadowDescendant, 79 }; 80 81 enum PseudoClassType { 82 PseudoClassUnknown = 0, 83 PseudoClassEmpty, 84 PseudoClassFirstChild, 85 PseudoClassFirstOfType, 86 PseudoClassLastChild, 87 PseudoClassLastOfType, 88 PseudoClassOnlyChild, 89 PseudoClassOnlyOfType, 90 PseudoClassNthChild, 91 PseudoClassNthOfType, 92 PseudoClassNthLastChild, 93 PseudoClassNthLastOfType, 94 PseudoClassLink, 95 PseudoClassVisited, 96 PseudoClassAny, 97 PseudoClassAnyLink, 98 PseudoClassAutofill, 99 PseudoClassHover, 100 PseudoClassDrag, 101 PseudoClassFocus, 102 PseudoClassActive, 103 PseudoClassChecked, 104 PseudoClassEnabled, 105 PseudoClassFullPageMedia, 106 PseudoClassDefault, 107 PseudoClassDisabled, 108 PseudoClassOptional, 109 PseudoClassRequired, 110 PseudoClassReadOnly, 111 PseudoClassReadWrite, 112 PseudoClassValid, 113 PseudoClassInvalid, 114 PseudoClassIndeterminate, 115 PseudoClassTarget, 116 PseudoClassLang, 117 PseudoClassNot, 118 PseudoClassRoot, 119 PseudoClassScope, 120 PseudoClassWindowInactive, 121 PseudoClassCornerPresent, 122 PseudoClassDecrement, 123 PseudoClassIncrement, 124 PseudoClassHorizontal, 125 PseudoClassVertical, 126 PseudoClassStart, 127 PseudoClassEnd, 128 PseudoClassDoubleButton, 129 PseudoClassSingleButton, 130 PseudoClassNoButton, 131#if ENABLE(FULLSCREEN_API) 132 PseudoClassFullScreen, 133 PseudoClassFullScreenDocument, 134 PseudoClassFullScreenAncestor, 135 PseudoClassAnimatingFullScreenTransition, 136#endif 137 PseudoClassInRange, 138 PseudoClassOutOfRange, 139#if ENABLE(VIDEO_TRACK) 140 PseudoClassFuture, 141 PseudoClassPast, 142#endif 143 }; 144 145 enum PseudoElementType { 146 PseudoElementUnknown = 0, 147 PseudoElementAfter, 148 PseudoElementBefore, 149#if ENABLE(VIDEO_TRACK) 150 PseudoElementCue, 151#endif 152 PseudoElementFirstLetter, 153 PseudoElementFirstLine, 154 PseudoElementResizer, 155 PseudoElementScrollbar, 156 PseudoElementScrollbarButton, 157 PseudoElementScrollbarCorner, 158 PseudoElementScrollbarThumb, 159 PseudoElementScrollbarTrack, 160 PseudoElementScrollbarTrackPiece, 161 PseudoElementSelection, 162 PseudoElementUserAgentCustom, 163 PseudoElementWebKitCustom, 164 }; 165 166 enum PagePseudoClassType { 167 PagePseudoClassFirst = 1, 168 PagePseudoClassLeft, 169 PagePseudoClassRight, 170 }; 171 172 enum MarginBoxType { 173 TopLeftCornerMarginBox, 174 TopLeftMarginBox, 175 TopCenterMarginBox, 176 TopRightMarginBox, 177 TopRightCornerMarginBox, 178 BottomLeftCornerMarginBox, 179 BottomLeftMarginBox, 180 BottomCenterMarginBox, 181 BottomRightMarginBox, 182 BottomRightCornerMarginBox, 183 LeftTopMarginBox, 184 LeftMiddleMarginBox, 185 LeftBottomMarginBox, 186 RightTopMarginBox, 187 RightMiddleMarginBox, 188 RightBottomMarginBox, 189 }; 190 191 static PseudoElementType parsePseudoElementType(const String&); 192 static PseudoId pseudoId(PseudoElementType); 193 194 // Selectors are kept in an array by CSSSelectorList. The next component of the selector is 195 // the next item in the array. 196 const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); } 197 198 const QualifiedName& tagQName() const; 199 const AtomicString& value() const; 200 const QualifiedName& attribute() const; 201 const AtomicString& attributeCanonicalLocalName() const; 202 const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; } 203 const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; } 204 205 void setPseudoElementType(PseudoElementType pseudoElementType) { m_pseudoType = pseudoElementType; } 206 void setPagePseudoType(PagePseudoClassType pagePseudoType) { m_pseudoType = pagePseudoType; } 207 void setValue(const AtomicString&); 208 void setAttribute(const QualifiedName&, bool isCaseInsensitive); 209 void setArgument(const AtomicString&); 210 void setSelectorList(std::unique_ptr<CSSSelectorList>); 211 212 bool parseNth() const; 213 bool matchNth(int count) const; 214 int nthA() const; 215 int nthB() const; 216 217 PseudoClassType pseudoClassType() const 218 { 219 ASSERT(m_match == PseudoClass); 220 return static_cast<PseudoClassType>(m_pseudoType); 221 } 222 223 PseudoElementType pseudoElementType() const 224 { 225 ASSERT(m_match == PseudoElement); 226 return static_cast<PseudoElementType>(m_pseudoType); 227 } 228 229 PagePseudoClassType pagePseudoClassType() const 230 { 231 ASSERT(m_match == PagePseudoClass); 232 return static_cast<PagePseudoClassType>(m_pseudoType); 233 } 234 235 bool matchesPseudoElement() const; 236 bool isUnknownPseudoElement() const; 237 bool isCustomPseudoElement() const; 238 bool isSiblingSelector() const; 239 bool isAttributeSelector() const; 240 241 Relation relation() const { return static_cast<Relation>(m_relation); } 242 243 bool isLastInSelectorList() const { return m_isLastInSelectorList; } 244 void setLastInSelectorList() { m_isLastInSelectorList = true; } 245 bool isLastInTagHistory() const { return m_isLastInTagHistory; } 246 void setNotLastInTagHistory() { m_isLastInTagHistory = false; } 247 248 bool isSimple() const; 249 250 bool isForPage() const { return m_isForPage; } 251 void setForPage() { m_isForPage = true; } 252 253 unsigned m_relation : 3; // enum Relation 254 mutable unsigned m_match : 4; // enum Match 255 mutable unsigned m_pseudoType : 8; // PseudoType 256 257 private: 258 mutable bool m_parsedNth : 1; // Used for :nth-* 259 bool m_isLastInSelectorList : 1; 260 bool m_isLastInTagHistory : 1; 261 bool m_hasRareData : 1; 262 bool m_isForPage : 1; 263 bool m_tagIsForNamespaceRule : 1; 264 265 unsigned specificityForOneSelector() const; 266 unsigned specificityForPage() const; 267 268 // Hide. 269 CSSSelector& operator=(const CSSSelector&); 270 271 struct RareData : public RefCounted<RareData> { 272 static PassRefPtr<RareData> create(PassRefPtr<AtomicStringImpl> value) { return adoptRef(new RareData(value)); } 273 ~RareData(); 274 275 bool parseNth(); 276 bool matchNth(int count); 277 278 AtomicStringImpl* 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_attributeCanonicalLocalName; 283 AtomicString m_argument; // Used for :contains, :lang and :nth-* 284 std::unique_ptr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not 285 286 private: 287 RareData(PassRefPtr<AtomicStringImpl> value); 288 }; 289 void createRareData(); 290 291 union DataUnion { 292 DataUnion() : m_value(0) { } 293 AtomicStringImpl* m_value; 294 QualifiedName::QualifiedNameImpl* m_tagQName; 295 RareData* m_rareData; 296 } m_data; 297 }; 298 299inline const QualifiedName& CSSSelector::attribute() const 300{ 301 ASSERT(isAttributeSelector()); 302 ASSERT(m_hasRareData); 303 return m_data.m_rareData->m_attribute; 304} 305 306inline const AtomicString& CSSSelector::attributeCanonicalLocalName() const 307{ 308 ASSERT(isAttributeSelector()); 309 ASSERT(m_hasRareData); 310 return m_data.m_rareData->m_attributeCanonicalLocalName; 311} 312 313inline bool CSSSelector::matchesPseudoElement() const 314{ 315 return m_match == PseudoElement; 316} 317 318inline bool CSSSelector::isUnknownPseudoElement() const 319{ 320 return m_match == PseudoElement && m_pseudoType == PseudoElementUnknown; 321} 322 323inline bool CSSSelector::isCustomPseudoElement() const 324{ 325 return m_match == PseudoElement && (m_pseudoType == PseudoElementUserAgentCustom || m_pseudoType == PseudoElementWebKitCustom); 326} 327 328static inline bool pseudoClassIsRelativeToSiblings(CSSSelector::PseudoClassType type) 329{ 330 return type == CSSSelector::PseudoClassEmpty 331 || type == CSSSelector::PseudoClassFirstChild 332 || type == CSSSelector::PseudoClassFirstOfType 333 || type == CSSSelector::PseudoClassLastChild 334 || type == CSSSelector::PseudoClassLastOfType 335 || type == CSSSelector::PseudoClassOnlyChild 336 || type == CSSSelector::PseudoClassOnlyOfType 337 || type == CSSSelector::PseudoClassNthChild 338 || type == CSSSelector::PseudoClassNthOfType 339 || type == CSSSelector::PseudoClassNthLastChild 340 || type == CSSSelector::PseudoClassNthLastOfType; 341} 342 343inline bool CSSSelector::isSiblingSelector() const 344{ 345 return m_relation == DirectAdjacent 346 || m_relation == IndirectAdjacent 347 || (m_match == CSSSelector::PseudoClass && pseudoClassIsRelativeToSiblings(pseudoClassType())); 348} 349 350inline bool CSSSelector::isAttributeSelector() const 351{ 352 return m_match == CSSSelector::Exact 353 || m_match == CSSSelector::Set 354 || m_match == CSSSelector::List 355 || m_match == CSSSelector::Hyphen 356 || m_match == CSSSelector::Contain 357 || m_match == CSSSelector::Begin 358 || m_match == CSSSelector::End; 359} 360 361inline void CSSSelector::setValue(const AtomicString& value) 362{ 363 ASSERT(m_match != Tag); 364 // Need to do ref counting manually for the union. 365 if (m_hasRareData) { 366 if (m_data.m_rareData->m_value) 367 m_data.m_rareData->m_value->deref(); 368 m_data.m_rareData->m_value = value.impl(); 369 m_data.m_rareData->m_value->ref(); 370 return; 371 } 372 if (m_data.m_value) 373 m_data.m_value->deref(); 374 m_data.m_value = value.impl(); 375 m_data.m_value->ref(); 376} 377 378inline CSSSelector::CSSSelector() 379 : m_relation(Descendant) 380 , m_match(Unknown) 381 , m_pseudoType(0) 382 , m_parsedNth(false) 383 , m_isLastInSelectorList(false) 384 , m_isLastInTagHistory(true) 385 , m_hasRareData(false) 386 , m_isForPage(false) 387 , m_tagIsForNamespaceRule(false) 388{ 389} 390 391inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule) 392 : m_relation(Descendant) 393 , m_match(Tag) 394 , m_pseudoType(0) 395 , m_parsedNth(false) 396 , m_isLastInSelectorList(false) 397 , m_isLastInTagHistory(true) 398 , m_hasRareData(false) 399 , m_isForPage(false) 400 , m_tagIsForNamespaceRule(tagIsForNamespaceRule) 401{ 402 m_data.m_tagQName = tagQName.impl(); 403 m_data.m_tagQName->ref(); 404} 405 406inline CSSSelector::CSSSelector(const CSSSelector& o) 407 : m_relation(o.m_relation) 408 , m_match(o.m_match) 409 , m_pseudoType(o.m_pseudoType) 410 , m_parsedNth(o.m_parsedNth) 411 , m_isLastInSelectorList(o.m_isLastInSelectorList) 412 , m_isLastInTagHistory(o.m_isLastInTagHistory) 413 , m_hasRareData(o.m_hasRareData) 414 , m_isForPage(o.m_isForPage) 415 , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule) 416{ 417 if (o.m_match == Tag) { 418 m_data.m_tagQName = o.m_data.m_tagQName; 419 m_data.m_tagQName->ref(); 420 } else if (o.m_hasRareData) { 421 m_data.m_rareData = o.m_data.m_rareData; 422 m_data.m_rareData->ref(); 423 } else if (o.m_data.m_value) { 424 m_data.m_value = o.m_data.m_value; 425 m_data.m_value->ref(); 426 } 427} 428 429inline CSSSelector::~CSSSelector() 430{ 431 if (m_match == Tag) 432 m_data.m_tagQName->deref(); 433 else if (m_hasRareData) 434 m_data.m_rareData->deref(); 435 else if (m_data.m_value) 436 m_data.m_value->deref(); 437} 438 439inline const QualifiedName& CSSSelector::tagQName() const 440{ 441 ASSERT(m_match == Tag); 442 return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName); 443} 444 445inline const AtomicString& CSSSelector::value() const 446{ 447 ASSERT(m_match != Tag); 448 // AtomicString is really just an AtomicStringImpl* so the cast below is safe. 449 // FIXME: Perhaps call sites could be changed to accept AtomicStringImpl? 450 return *reinterpret_cast<const AtomicString*>(m_hasRareData ? &m_data.m_rareData->m_value : &m_data.m_value); 451} 452 453 454} // namespace WebCore 455 456#endif // CSSSelector_h 457