1 /* 2 * Copyright (C) 1999-2003 Lars Knoll (knoll (at) kde.org) 3 * 1999 Waldo Bastian (bastian (at) kde.org) 4 * 2001 Andreas Schlapbach (schlpbch (at) iam.unibe.ch) 5 * 2001-2003 Dirk Mueller (mueller (at) kde.org) 6 * Copyright (C) 2002, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. 7 * Copyright (C) 2008 David Smith (catfish.man (at) gmail.com) 8 * Copyright (C) 2010 Google Inc. All rights reserved. 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Library General Public 12 * License as published by the Free Software Foundation; either 13 * version 2 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Library General Public License for more details. 19 * 20 * You should have received a copy of the GNU Library General Public License 21 * along with this library; see the file COPYING.LIB. If not, write to 22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 23 * Boston, MA 02110-1301, USA. 24 */ 25 26 #include "config.h" 27 #include "CSSSelector.h" 28 29 #include "CSSOMUtils.h" 30 #include "CSSSelectorList.h" 31 #include "HTMLNames.h" 32 #include <wtf/Assertions.h> 33 #include <wtf/HashMap.h> 34 #include <wtf/StdLibExtras.h> 35 #include <wtf/Vector.h> 36 37 namespace WebCore { 38 39 using namespace HTMLNames; 40 41 void CSSSelector::createRareData() 42 { 43 if (m_hasRareData) 44 return; 45 // Move the value to the rare data stucture. 46 m_data.m_rareData = new RareData(adoptRef(m_data.m_value)); 47 m_hasRareData = true; 48 } 49 50 unsigned CSSSelector::specificity() const 51 { 52 // make sure the result doesn't overflow 53 static const unsigned maxValueMask = 0xffffff; 54 unsigned total = 0; 55 for (const CSSSelector* selector = this; selector; selector = selector->tagHistory()) { 56 if (selector->m_isForPage) 57 return (total + selector->specificityForPage()) & maxValueMask; 58 total = (total + selector->specificityForOneSelector()) & maxValueMask; 59 } 60 return total; 61 } 62 63 inline unsigned CSSSelector::specificityForOneSelector() const 64 { 65 // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function 66 // isn't quite correct. 67 unsigned s = (m_tag.localName() == starAtom ? 0 : 1); 68 switch (m_match) { 69 case Id: 70 s += 0x10000; 71 break; 72 case Exact: 73 case Class: 74 case Set: 75 case List: 76 case Hyphen: 77 case PseudoClass: 78 case PseudoElement: 79 case Contain: 80 case Begin: 81 case End: 82 // FIXME: PsuedoAny should base the specificity on the sub-selectors. 83 // See http://lists.w3.org/Archives/Public/www-style/2010Sep/0530.html 84 if (pseudoType() == PseudoNot) { 85 ASSERT(selectorList()); 86 s += selectorList()->first()->specificityForOneSelector(); 87 } else 88 s += 0x100; 89 case None: 90 break; 91 } 92 return s; 93 } 94 95 unsigned CSSSelector::specificityForPage() const 96 { 97 // See http://dev.w3.org/csswg/css3-page/#cascading-and-page-context 98 unsigned s = (m_tag.localName() == starAtom ? 0 : 4); 99 100 switch (pseudoType()) { 101 case PseudoFirstPage: 102 s += 2; 103 break; 104 case PseudoLeftPage: 105 case PseudoRightPage: 106 s += 1; 107 break; 108 case PseudoNotParsed: 109 break; 110 default: 111 ASSERT_NOT_REACHED(); 112 } 113 return s; 114 } 115 116 PseudoId CSSSelector::pseudoId(PseudoType type) 117 { 118 switch (type) { 119 case PseudoFirstLine: 120 return FIRST_LINE; 121 case PseudoFirstLetter: 122 return FIRST_LETTER; 123 case PseudoSelection: 124 return SELECTION; 125 case PseudoBefore: 126 return BEFORE; 127 case PseudoAfter: 128 return AFTER; 129 case PseudoFileUploadButton: 130 return FILE_UPLOAD_BUTTON; 131 case PseudoInputPlaceholder: 132 return INPUT_PLACEHOLDER; 133 #if ENABLE(INPUT_SPEECH) 134 case PseudoInputSpeechButton: 135 return INPUT_SPEECH_BUTTON; 136 #endif 137 case PseudoSearchCancelButton: 138 return SEARCH_CANCEL_BUTTON; 139 case PseudoSearchDecoration: 140 return SEARCH_DECORATION; 141 case PseudoSearchResultsDecoration: 142 return SEARCH_RESULTS_DECORATION; 143 case PseudoSearchResultsButton: 144 return SEARCH_RESULTS_BUTTON; 145 case PseudoScrollbar: 146 return SCROLLBAR; 147 case PseudoScrollbarButton: 148 return SCROLLBAR_BUTTON; 149 case PseudoScrollbarCorner: 150 return SCROLLBAR_CORNER; 151 case PseudoScrollbarThumb: 152 return SCROLLBAR_THUMB; 153 case PseudoScrollbarTrack: 154 return SCROLLBAR_TRACK; 155 case PseudoScrollbarTrackPiece: 156 return SCROLLBAR_TRACK_PIECE; 157 case PseudoResizer: 158 return RESIZER; 159 case PseudoInnerSpinButton: 160 return INNER_SPIN_BUTTON; 161 case PseudoOuterSpinButton: 162 return OUTER_SPIN_BUTTON; 163 #if ENABLE(FULLSCREEN_API) 164 case PseudoFullScreen: 165 return FULL_SCREEN; 166 case PseudoFullScreenDocument: 167 return FULL_SCREEN_DOCUMENT; 168 #endif 169 170 case PseudoInputListButton: 171 #if ENABLE(DATALIST) 172 return INPUT_LIST_BUTTON; 173 #endif 174 case PseudoUnknown: 175 case PseudoEmpty: 176 case PseudoFirstChild: 177 case PseudoFirstOfType: 178 case PseudoLastChild: 179 case PseudoLastOfType: 180 case PseudoOnlyChild: 181 case PseudoOnlyOfType: 182 case PseudoNthChild: 183 case PseudoNthOfType: 184 case PseudoNthLastChild: 185 case PseudoNthLastOfType: 186 case PseudoLink: 187 case PseudoVisited: 188 case PseudoAny: 189 case PseudoAnyLink: 190 case PseudoAutofill: 191 case PseudoHover: 192 case PseudoDrag: 193 case PseudoFocus: 194 case PseudoActive: 195 case PseudoChecked: 196 case PseudoEnabled: 197 case PseudoFullPageMedia: 198 case PseudoDefault: 199 case PseudoDisabled: 200 case PseudoOptional: 201 case PseudoRequired: 202 case PseudoReadOnly: 203 case PseudoReadWrite: 204 case PseudoValid: 205 case PseudoInvalid: 206 case PseudoIndeterminate: 207 case PseudoTarget: 208 case PseudoLang: 209 case PseudoNot: 210 case PseudoRoot: 211 case PseudoScrollbarBack: 212 case PseudoScrollbarForward: 213 case PseudoWindowInactive: 214 case PseudoCornerPresent: 215 case PseudoDecrement: 216 case PseudoIncrement: 217 case PseudoHorizontal: 218 case PseudoVertical: 219 case PseudoStart: 220 case PseudoEnd: 221 case PseudoDoubleButton: 222 case PseudoSingleButton: 223 case PseudoNoButton: 224 case PseudoFirstPage: 225 case PseudoLeftPage: 226 case PseudoRightPage: 227 case PseudoInRange: 228 case PseudoOutOfRange: 229 return NOPSEUDO; 230 case PseudoNotParsed: 231 ASSERT_NOT_REACHED(); 232 return NOPSEUDO; 233 } 234 235 ASSERT_NOT_REACHED(); 236 return NOPSEUDO; 237 } 238 239 static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoTypeMap() 240 { 241 DEFINE_STATIC_LOCAL(AtomicString, active, ("active")); 242 DEFINE_STATIC_LOCAL(AtomicString, after, ("after")); 243 DEFINE_STATIC_LOCAL(AtomicString, any, ("-webkit-any(")); 244 DEFINE_STATIC_LOCAL(AtomicString, anyLink, ("-webkit-any-link")); 245 DEFINE_STATIC_LOCAL(AtomicString, autofill, ("-webkit-autofill")); 246 DEFINE_STATIC_LOCAL(AtomicString, before, ("before")); 247 DEFINE_STATIC_LOCAL(AtomicString, checked, ("checked")); 248 DEFINE_STATIC_LOCAL(AtomicString, fileUploadButton, ("-webkit-file-upload-button")); 249 #if ENABLE(INPUT_SPEECH) 250 DEFINE_STATIC_LOCAL(AtomicString, inputSpeechButton, ("-webkit-input-speech-button")); 251 #endif 252 DEFINE_STATIC_LOCAL(AtomicString, defaultString, ("default")); 253 DEFINE_STATIC_LOCAL(AtomicString, disabled, ("disabled")); 254 DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only")); 255 DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write")); 256 DEFINE_STATIC_LOCAL(AtomicString, valid, ("valid")); 257 DEFINE_STATIC_LOCAL(AtomicString, invalid, ("invalid")); 258 DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag")); 259 DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag")); // was documented with this name in Apple documentation, so keep an alia 260 DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty")); 261 DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled")); 262 DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child")); 263 DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter")); 264 DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line")); 265 DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type")); 266 DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media")); 267 DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child(")); 268 DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type(")); 269 DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child(")); 270 DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type(")); 271 DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus")); 272 DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover")); 273 DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate")); 274 DEFINE_STATIC_LOCAL(AtomicString, innerSpinButton, ("-webkit-inner-spin-button")); 275 #if ENABLE(DATALIST) 276 DEFINE_STATIC_LOCAL(AtomicString, inputListButton, ("-webkit-input-list-button")); 277 #endif 278 DEFINE_STATIC_LOCAL(AtomicString, inputPlaceholder, ("-webkit-input-placeholder")); 279 DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child")); 280 DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type")); 281 DEFINE_STATIC_LOCAL(AtomicString, link, ("link")); 282 DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang(")); 283 DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not(")); 284 DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child")); 285 DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type")); 286 DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional")); 287 DEFINE_STATIC_LOCAL(AtomicString, outerSpinButton, ("-webkit-outer-spin-button")); 288 DEFINE_STATIC_LOCAL(AtomicString, required, ("required")); 289 DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer")); 290 DEFINE_STATIC_LOCAL(AtomicString, root, ("root")); 291 DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar")); 292 DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button")); 293 DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner")); 294 DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb")); 295 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track")); 296 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece")); 297 DEFINE_STATIC_LOCAL(AtomicString, searchCancelButton, ("-webkit-search-cancel-button")); 298 DEFINE_STATIC_LOCAL(AtomicString, searchDecoration, ("-webkit-search-decoration")); 299 DEFINE_STATIC_LOCAL(AtomicString, searchResultsDecoration, ("-webkit-search-results-decoration")); 300 DEFINE_STATIC_LOCAL(AtomicString, searchResultsButton, ("-webkit-search-results-button")); 301 DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection")); 302 DEFINE_STATIC_LOCAL(AtomicString, target, ("target")); 303 DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited")); 304 DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive")); 305 DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement")); 306 DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment")); 307 DEFINE_STATIC_LOCAL(AtomicString, start, ("start")); 308 DEFINE_STATIC_LOCAL(AtomicString, end, ("end")); 309 DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal")); 310 DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical")); 311 DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button")); 312 DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button")); 313 DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button")); 314 DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present")); 315 // Paged Media pseudo-classes 316 DEFINE_STATIC_LOCAL(AtomicString, firstPage, ("first")); 317 DEFINE_STATIC_LOCAL(AtomicString, leftPage, ("left")); 318 DEFINE_STATIC_LOCAL(AtomicString, rightPage, ("right")); 319 #if ENABLE(FULLSCREEN_API) 320 DEFINE_STATIC_LOCAL(AtomicString, fullScreen, ("-webkit-full-screen")); 321 DEFINE_STATIC_LOCAL(AtomicString, fullScreenDocument, ("-webkit-full-screen-document")); 322 #endif 323 DEFINE_STATIC_LOCAL(AtomicString, inRange, ("in-range")); 324 DEFINE_STATIC_LOCAL(AtomicString, outOfRange, ("out-of-range")); 325 326 static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = 0; 327 if (!nameToPseudoType) { 328 nameToPseudoType = new HashMap<AtomicStringImpl*, CSSSelector::PseudoType>; 329 nameToPseudoType->set(active.impl(), CSSSelector::PseudoActive); 330 nameToPseudoType->set(after.impl(), CSSSelector::PseudoAfter); 331 nameToPseudoType->set(anyLink.impl(), CSSSelector::PseudoAnyLink); 332 nameToPseudoType->set(any.impl(), CSSSelector::PseudoAny); 333 nameToPseudoType->set(autofill.impl(), CSSSelector::PseudoAutofill); 334 nameToPseudoType->set(before.impl(), CSSSelector::PseudoBefore); 335 nameToPseudoType->set(checked.impl(), CSSSelector::PseudoChecked); 336 nameToPseudoType->set(fileUploadButton.impl(), CSSSelector::PseudoFileUploadButton); 337 #if ENABLE(INPUT_SPEECH) 338 nameToPseudoType->set(inputSpeechButton.impl(), CSSSelector::PseudoInputSpeechButton); 339 #endif 340 nameToPseudoType->set(defaultString.impl(), CSSSelector::PseudoDefault); 341 nameToPseudoType->set(disabled.impl(), CSSSelector::PseudoDisabled); 342 nameToPseudoType->set(readOnly.impl(), CSSSelector::PseudoReadOnly); 343 nameToPseudoType->set(readWrite.impl(), CSSSelector::PseudoReadWrite); 344 nameToPseudoType->set(valid.impl(), CSSSelector::PseudoValid); 345 nameToPseudoType->set(invalid.impl(), CSSSelector::PseudoInvalid); 346 nameToPseudoType->set(drag.impl(), CSSSelector::PseudoDrag); 347 nameToPseudoType->set(dragAlias.impl(), CSSSelector::PseudoDrag); 348 nameToPseudoType->set(enabled.impl(), CSSSelector::PseudoEnabled); 349 nameToPseudoType->set(empty.impl(), CSSSelector::PseudoEmpty); 350 nameToPseudoType->set(firstChild.impl(), CSSSelector::PseudoFirstChild); 351 nameToPseudoType->set(fullPageMedia.impl(), CSSSelector::PseudoFullPageMedia); 352 #if ENABLE(DATALIST) 353 nameToPseudoType->set(inputListButton.impl(), CSSSelector::PseudoInputListButton); 354 #endif 355 nameToPseudoType->set(inputPlaceholder.impl(), CSSSelector::PseudoInputPlaceholder); 356 nameToPseudoType->set(lastChild.impl(), CSSSelector::PseudoLastChild); 357 nameToPseudoType->set(lastOfType.impl(), CSSSelector::PseudoLastOfType); 358 nameToPseudoType->set(onlyChild.impl(), CSSSelector::PseudoOnlyChild); 359 nameToPseudoType->set(onlyOfType.impl(), CSSSelector::PseudoOnlyOfType); 360 nameToPseudoType->set(firstLetter.impl(), CSSSelector::PseudoFirstLetter); 361 nameToPseudoType->set(firstLine.impl(), CSSSelector::PseudoFirstLine); 362 nameToPseudoType->set(firstOfType.impl(), CSSSelector::PseudoFirstOfType); 363 nameToPseudoType->set(focus.impl(), CSSSelector::PseudoFocus); 364 nameToPseudoType->set(hover.impl(), CSSSelector::PseudoHover); 365 nameToPseudoType->set(indeterminate.impl(), CSSSelector::PseudoIndeterminate); 366 nameToPseudoType->set(innerSpinButton.impl(), CSSSelector::PseudoInnerSpinButton); 367 nameToPseudoType->set(link.impl(), CSSSelector::PseudoLink); 368 nameToPseudoType->set(lang.impl(), CSSSelector::PseudoLang); 369 nameToPseudoType->set(notStr.impl(), CSSSelector::PseudoNot); 370 nameToPseudoType->set(nthChild.impl(), CSSSelector::PseudoNthChild); 371 nameToPseudoType->set(nthOfType.impl(), CSSSelector::PseudoNthOfType); 372 nameToPseudoType->set(nthLastChild.impl(), CSSSelector::PseudoNthLastChild); 373 nameToPseudoType->set(nthLastOfType.impl(), CSSSelector::PseudoNthLastOfType); 374 nameToPseudoType->set(outerSpinButton.impl(), CSSSelector::PseudoOuterSpinButton); 375 nameToPseudoType->set(root.impl(), CSSSelector::PseudoRoot); 376 nameToPseudoType->set(windowInactive.impl(), CSSSelector::PseudoWindowInactive); 377 nameToPseudoType->set(decrement.impl(), CSSSelector::PseudoDecrement); 378 nameToPseudoType->set(increment.impl(), CSSSelector::PseudoIncrement); 379 nameToPseudoType->set(start.impl(), CSSSelector::PseudoStart); 380 nameToPseudoType->set(end.impl(), CSSSelector::PseudoEnd); 381 nameToPseudoType->set(horizontal.impl(), CSSSelector::PseudoHorizontal); 382 nameToPseudoType->set(vertical.impl(), CSSSelector::PseudoVertical); 383 nameToPseudoType->set(doubleButton.impl(), CSSSelector::PseudoDoubleButton); 384 nameToPseudoType->set(singleButton.impl(), CSSSelector::PseudoSingleButton); 385 nameToPseudoType->set(noButton.impl(), CSSSelector::PseudoNoButton); 386 nameToPseudoType->set(optional.impl(), CSSSelector::PseudoOptional); 387 nameToPseudoType->set(required.impl(), CSSSelector::PseudoRequired); 388 nameToPseudoType->set(resizer.impl(), CSSSelector::PseudoResizer); 389 nameToPseudoType->set(scrollbar.impl(), CSSSelector::PseudoScrollbar); 390 nameToPseudoType->set(scrollbarButton.impl(), CSSSelector::PseudoScrollbarButton); 391 nameToPseudoType->set(scrollbarCorner.impl(), CSSSelector::PseudoScrollbarCorner); 392 nameToPseudoType->set(scrollbarThumb.impl(), CSSSelector::PseudoScrollbarThumb); 393 nameToPseudoType->set(scrollbarTrack.impl(), CSSSelector::PseudoScrollbarTrack); 394 nameToPseudoType->set(scrollbarTrackPiece.impl(), CSSSelector::PseudoScrollbarTrackPiece); 395 nameToPseudoType->set(cornerPresent.impl(), CSSSelector::PseudoCornerPresent); 396 nameToPseudoType->set(searchCancelButton.impl(), CSSSelector::PseudoSearchCancelButton); 397 nameToPseudoType->set(searchDecoration.impl(), CSSSelector::PseudoSearchDecoration); 398 nameToPseudoType->set(searchResultsDecoration.impl(), CSSSelector::PseudoSearchResultsDecoration); 399 nameToPseudoType->set(searchResultsButton.impl(), CSSSelector::PseudoSearchResultsButton); 400 nameToPseudoType->set(selection.impl(), CSSSelector::PseudoSelection); 401 nameToPseudoType->set(target.impl(), CSSSelector::PseudoTarget); 402 nameToPseudoType->set(visited.impl(), CSSSelector::PseudoVisited); 403 nameToPseudoType->set(firstPage.impl(), CSSSelector::PseudoFirstPage); 404 nameToPseudoType->set(leftPage.impl(), CSSSelector::PseudoLeftPage); 405 nameToPseudoType->set(rightPage.impl(), CSSSelector::PseudoRightPage); 406 #if ENABLE(FULLSCREEN_API) 407 nameToPseudoType->set(fullScreen.impl(), CSSSelector::PseudoFullScreen); 408 nameToPseudoType->set(fullScreenDocument.impl(), CSSSelector::PseudoFullScreenDocument); 409 #endif 410 nameToPseudoType->set(inRange.impl(), CSSSelector::PseudoInRange); 411 nameToPseudoType->set(outOfRange.impl(), CSSSelector::PseudoOutOfRange); 412 } 413 return nameToPseudoType; 414 } 415 416 CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name) 417 { 418 if (name.isNull()) 419 return PseudoUnknown; 420 HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = nameToPseudoTypeMap(); 421 HashMap<AtomicStringImpl*, CSSSelector::PseudoType>::iterator slot = nameToPseudoType->find(name.impl()); 422 return slot == nameToPseudoType->end() ? PseudoUnknown : slot->second; 423 } 424 425 void CSSSelector::extractPseudoType() const 426 { 427 if (m_match != PseudoClass && m_match != PseudoElement && m_match != PagePseudoClass) 428 return; 429 430 m_pseudoType = parsePseudoType(value()); 431 432 bool element = false; // pseudo-element 433 bool compat = false; // single colon compatbility mode 434 bool isPagePseudoClass = false; // Page pseudo-class 435 436 switch (m_pseudoType) { 437 case PseudoAfter: 438 case PseudoBefore: 439 case PseudoFirstLetter: 440 case PseudoFirstLine: 441 compat = true; 442 case PseudoFileUploadButton: 443 case PseudoInputListButton: 444 case PseudoInputPlaceholder: 445 #if ENABLE(INPUT_SPEECH) 446 case PseudoInputSpeechButton: 447 #endif 448 case PseudoInnerSpinButton: 449 case PseudoOuterSpinButton: 450 case PseudoResizer: 451 case PseudoScrollbar: 452 case PseudoScrollbarCorner: 453 case PseudoScrollbarButton: 454 case PseudoScrollbarThumb: 455 case PseudoScrollbarTrack: 456 case PseudoScrollbarTrackPiece: 457 case PseudoSearchCancelButton: 458 case PseudoSearchDecoration: 459 case PseudoSearchResultsDecoration: 460 case PseudoSearchResultsButton: 461 case PseudoSelection: 462 element = true; 463 break; 464 case PseudoUnknown: 465 case PseudoEmpty: 466 case PseudoFirstChild: 467 case PseudoFirstOfType: 468 case PseudoLastChild: 469 case PseudoLastOfType: 470 case PseudoOnlyChild: 471 case PseudoOnlyOfType: 472 case PseudoNthChild: 473 case PseudoNthOfType: 474 case PseudoNthLastChild: 475 case PseudoNthLastOfType: 476 case PseudoLink: 477 case PseudoVisited: 478 case PseudoAny: 479 case PseudoAnyLink: 480 case PseudoAutofill: 481 case PseudoHover: 482 case PseudoDrag: 483 case PseudoFocus: 484 case PseudoActive: 485 case PseudoChecked: 486 case PseudoEnabled: 487 case PseudoFullPageMedia: 488 case PseudoDefault: 489 case PseudoDisabled: 490 case PseudoOptional: 491 case PseudoRequired: 492 case PseudoReadOnly: 493 case PseudoReadWrite: 494 case PseudoValid: 495 case PseudoInvalid: 496 case PseudoIndeterminate: 497 case PseudoTarget: 498 case PseudoLang: 499 case PseudoNot: 500 case PseudoRoot: 501 case PseudoScrollbarBack: 502 case PseudoScrollbarForward: 503 case PseudoWindowInactive: 504 case PseudoCornerPresent: 505 case PseudoDecrement: 506 case PseudoIncrement: 507 case PseudoHorizontal: 508 case PseudoVertical: 509 case PseudoStart: 510 case PseudoEnd: 511 case PseudoDoubleButton: 512 case PseudoSingleButton: 513 case PseudoNoButton: 514 case PseudoNotParsed: 515 #if ENABLE(FULLSCREEN_API) 516 case PseudoFullScreen: 517 case PseudoFullScreenDocument: 518 #endif 519 case PseudoInRange: 520 case PseudoOutOfRange: 521 break; 522 case PseudoFirstPage: 523 case PseudoLeftPage: 524 case PseudoRightPage: 525 isPagePseudoClass = true; 526 break; 527 } 528 529 bool matchPagePseudoClass = (m_match == PagePseudoClass); 530 if (matchPagePseudoClass != isPagePseudoClass) 531 m_pseudoType = PseudoUnknown; 532 else if (m_match == PseudoClass && element) { 533 if (!compat) 534 m_pseudoType = PseudoUnknown; 535 else 536 m_match = PseudoElement; 537 } else if (m_match == PseudoElement && !element) 538 m_pseudoType = PseudoUnknown; 539 } 540 541 bool CSSSelector::operator==(const CSSSelector& other) 542 { 543 const CSSSelector* sel1 = this; 544 const CSSSelector* sel2 = &other; 545 546 while (sel1 && sel2) { 547 if (sel1->m_tag != sel2->m_tag || sel1->attribute() != sel2->attribute() || 548 sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match || 549 sel1->value() != sel2->value() || 550 sel1->pseudoType() != sel2->pseudoType() || 551 sel1->argument() != sel2->argument()) 552 return false; 553 sel1 = sel1->tagHistory(); 554 sel2 = sel2->tagHistory(); 555 } 556 557 if (sel1 || sel2) 558 return false; 559 560 return true; 561 } 562 563 String CSSSelector::selectorText() const 564 { 565 String str = ""; 566 567 const AtomicString& prefix = m_tag.prefix(); 568 const AtomicString& localName = m_tag.localName(); 569 if (m_match == CSSSelector::None || !prefix.isNull() || localName != starAtom) { 570 if (prefix.isNull()) 571 str = localName; 572 else { 573 str = prefix.string(); 574 str.append("|"); 575 str.append(localName); 576 } 577 } 578 579 const CSSSelector* cs = this; 580 while (true) { 581 if (cs->m_match == CSSSelector::Id) { 582 str += "#"; 583 serializeIdentifier(cs->value(), str); 584 } else if (cs->m_match == CSSSelector::Class) { 585 str += "."; 586 serializeIdentifier(cs->value(), str); 587 } else if (cs->m_match == CSSSelector::PseudoClass || cs->m_match == CSSSelector::PagePseudoClass) { 588 str += ":"; 589 str += cs->value(); 590 591 switch (cs->pseudoType()) { 592 case PseudoNot: 593 ASSERT(cs->selectorList()); 594 str += cs->selectorList()->first()->selectorText(); 595 str += ")"; 596 break; 597 case PseudoLang: 598 case PseudoNthChild: 599 case PseudoNthLastChild: 600 case PseudoNthOfType: 601 case PseudoNthLastOfType: 602 str += cs->argument(); 603 str += ")"; 604 break; 605 case PseudoAny: { 606 CSSSelector* firstSubSelector = cs->selectorList()->first(); 607 for (CSSSelector* subSelector = firstSubSelector; subSelector; subSelector = CSSSelectorList::next(subSelector)) { 608 if (subSelector != firstSubSelector) 609 str += ","; 610 str += subSelector->selectorText(); 611 } 612 str += ")"; 613 break; 614 } 615 default: 616 break; 617 } 618 } else if (cs->m_match == CSSSelector::PseudoElement) { 619 str += "::"; 620 str += cs->value(); 621 } else if (cs->hasAttribute()) { 622 str += "["; 623 const AtomicString& prefix = cs->attribute().prefix(); 624 if (!prefix.isNull()) { 625 str.append(prefix); 626 str.append("|"); 627 } 628 str += cs->attribute().localName(); 629 switch (cs->m_match) { 630 case CSSSelector::Exact: 631 str += "="; 632 break; 633 case CSSSelector::Set: 634 // set has no operator or value, just the attrName 635 str += "]"; 636 break; 637 case CSSSelector::List: 638 str += "~="; 639 break; 640 case CSSSelector::Hyphen: 641 str += "|="; 642 break; 643 case CSSSelector::Begin: 644 str += "^="; 645 break; 646 case CSSSelector::End: 647 str += "$="; 648 break; 649 case CSSSelector::Contain: 650 str += "*="; 651 break; 652 default: 653 break; 654 } 655 if (cs->m_match != CSSSelector::Set) { 656 serializeString(cs->value(), str); 657 str += "]"; 658 } 659 } 660 if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory()) 661 break; 662 cs = cs->tagHistory(); 663 } 664 665 if (CSSSelector* tagHistory = cs->tagHistory()) { 666 String tagHistoryText = tagHistory->selectorText(); 667 if (cs->relation() == CSSSelector::DirectAdjacent) 668 str = tagHistoryText + " + " + str; 669 else if (cs->relation() == CSSSelector::IndirectAdjacent) 670 str = tagHistoryText + " ~ " + str; 671 else if (cs->relation() == CSSSelector::Child) 672 str = tagHistoryText + " > " + str; 673 else 674 // Descendant 675 str = tagHistoryText + " " + str; 676 } 677 678 return str; 679 } 680 681 const QualifiedName& CSSSelector::attribute() const 682 { 683 switch (m_match) { 684 case Id: 685 return idAttr; 686 case Class: 687 return classAttr; 688 default: 689 return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName(); 690 } 691 } 692 693 void CSSSelector::setAttribute(const QualifiedName& value) 694 { 695 createRareData(); 696 m_data.m_rareData->m_attribute = value; 697 } 698 699 void CSSSelector::setArgument(const AtomicString& value) 700 { 701 createRareData(); 702 m_data.m_rareData->m_argument = value; 703 } 704 705 void CSSSelector::setSelectorList(PassOwnPtr<CSSSelectorList> selectorList) 706 { 707 createRareData(); 708 m_data.m_rareData->m_selectorList = selectorList; 709 } 710 711 bool CSSSelector::parseNth() 712 { 713 if (!m_hasRareData) 714 return false; 715 if (m_parsedNth) 716 return true; 717 m_parsedNth = m_data.m_rareData->parseNth(); 718 return m_parsedNth; 719 } 720 721 bool CSSSelector::matchNth(int count) 722 { 723 ASSERT(m_hasRareData); 724 return m_data.m_rareData->matchNth(count); 725 } 726 727 bool CSSSelector::isSimple() const 728 { 729 if (selectorList() || tagHistory() || matchesPseudoElement()) 730 return false; 731 732 int numConditions = 0; 733 734 // hasTag() cannot be be used here because namespace may not be nullAtom. 735 // Example: 736 // @namespace "http://www.w3.org/2000/svg"; 737 // svg:not(:root) { ... 738 if (m_tag != starAtom) 739 numConditions++; 740 741 if (m_match == Id || m_match == Class || m_match == PseudoClass) 742 numConditions++; 743 744 if (m_hasRareData && m_data.m_rareData->m_attribute != anyQName()) 745 numConditions++; 746 747 // numConditions is 0 for a universal selector. 748 // numConditions is 1 for other simple selectors. 749 return numConditions <= 1; 750 } 751 752 CSSSelector::RareData::RareData(PassRefPtr<AtomicStringImpl> value) 753 : m_value(value.leakRef()) 754 , m_a(0) 755 , m_b(0) 756 , m_attribute(anyQName()) 757 , m_argument(nullAtom) 758 { 759 } 760 761 CSSSelector::RareData::~RareData() 762 { 763 if (m_value) 764 m_value->deref(); 765 } 766 767 // a helper function for parsing nth-arguments 768 bool CSSSelector::RareData::parseNth() 769 { 770 String argument = m_argument.lower(); 771 772 if (argument.isEmpty()) 773 return false; 774 775 m_a = 0; 776 m_b = 0; 777 if (argument == "odd") { 778 m_a = 2; 779 m_b = 1; 780 } else if (argument == "even") { 781 m_a = 2; 782 m_b = 0; 783 } else { 784 size_t n = argument.find('n'); 785 if (n != notFound) { 786 if (argument[0] == '-') { 787 if (n == 1) 788 m_a = -1; // -n == -1n 789 else 790 m_a = argument.substring(0, n).toInt(); 791 } else if (!n) 792 m_a = 1; // n == 1n 793 else 794 m_a = argument.substring(0, n).toInt(); 795 796 size_t p = argument.find('+', n); 797 if (p != notFound) 798 m_b = argument.substring(p + 1, argument.length() - p - 1).toInt(); 799 else { 800 p = argument.find('-', n); 801 if (p != notFound) 802 m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt(); 803 } 804 } else 805 m_b = argument.toInt(); 806 } 807 return true; 808 } 809 810 // a helper function for checking nth-arguments 811 bool CSSSelector::RareData::matchNth(int count) 812 { 813 if (!m_a) 814 return count == m_b; 815 else if (m_a > 0) { 816 if (count < m_b) 817 return false; 818 return (count - m_b) % m_a == 0; 819 } else { 820 if (count > m_b) 821 return false; 822 return (m_b - count) % (-m_a) == 0; 823 } 824 } 825 826 } // namespace WebCore 827