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 "core/css/CSSSelector.h" 28 29 #include "HTMLNames.h" 30 #include "RuntimeEnabledFeatures.h" 31 #include "core/css/CSSOMUtils.h" 32 #include "core/css/CSSSelectorList.h" 33 #include "wtf/Assertions.h" 34 #include "wtf/HashMap.h" 35 #include "wtf/StdLibExtras.h" 36 #include "wtf/text/StringBuilder.h" 37 #include "wtf/text/StringHash.h" 38 39 namespace WebCore { 40 41 using namespace HTMLNames; 42 43 struct SameSizeAsCSSSelector { 44 unsigned bitfields; 45 void *pointers[1]; 46 }; 47 48 COMPILE_ASSERT(sizeof(CSSSelector) == sizeof(SameSizeAsCSSSelector), CSSSelectorShouldStaySmall); 49 50 void CSSSelector::createRareData() 51 { 52 ASSERT(m_match != Tag); 53 if (m_hasRareData) 54 return; 55 // Move the value to the rare data stucture. 56 m_data.m_rareData = RareData::create(adoptRef(m_data.m_value)).leakRef(); 57 m_hasRareData = true; 58 } 59 60 unsigned CSSSelector::specificity() const 61 { 62 // make sure the result doesn't overflow 63 static const unsigned maxValueMask = 0xffffff; 64 static const unsigned idMask = 0xff0000; 65 static const unsigned classMask = 0xff00; 66 static const unsigned elementMask = 0xff; 67 68 if (isForPage()) 69 return specificityForPage() & maxValueMask; 70 71 unsigned total = 0; 72 unsigned temp = 0; 73 74 for (const CSSSelector* selector = this; selector; selector = selector->tagHistory()) { 75 temp = total + selector->specificityForOneSelector(); 76 // Clamp each component to its max in the case of overflow. 77 if ((temp & idMask) < (total & idMask)) 78 total |= idMask; 79 else if ((temp & classMask) < (total & classMask)) 80 total |= classMask; 81 else if ((temp & elementMask) < (total & elementMask)) 82 total |= elementMask; 83 else 84 total = temp; 85 } 86 return total; 87 } 88 89 inline unsigned CSSSelector::specificityForOneSelector() const 90 { 91 // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function 92 // isn't quite correct. 93 switch (m_match) { 94 case Id: 95 return 0x10000; 96 case Exact: 97 case Class: 98 case Set: 99 case List: 100 case Hyphen: 101 case PseudoClass: 102 case PseudoElement: 103 case Contain: 104 case Begin: 105 case End: 106 // FIXME: PseudoAny should base the specificity on the sub-selectors. 107 // See http://lists.w3.org/Archives/Public/www-style/2010Sep/0530.html 108 if (pseudoType() == PseudoNot) { 109 ASSERT(selectorList()); 110 return selectorList()->first()->specificityForOneSelector(); 111 } 112 return 0x100; 113 case Tag: 114 return (tagQName().localName() != starAtom) ? 1 : 0; 115 case Unknown: 116 return 0; 117 } 118 ASSERT_NOT_REACHED(); 119 return 0; 120 } 121 122 unsigned CSSSelector::specificityForPage() const 123 { 124 // See http://dev.w3.org/csswg/css3-page/#cascading-and-page-context 125 unsigned s = 0; 126 127 for (const CSSSelector* component = this; component; component = component->tagHistory()) { 128 switch (component->m_match) { 129 case Tag: 130 s += tagQName().localName() == starAtom ? 0 : 4; 131 break; 132 case PseudoClass: 133 switch (component->pseudoType()) { 134 case PseudoFirstPage: 135 s += 2; 136 break; 137 case PseudoLeftPage: 138 case PseudoRightPage: 139 s += 1; 140 break; 141 case PseudoNotParsed: 142 break; 143 default: 144 ASSERT_NOT_REACHED(); 145 } 146 break; 147 default: 148 break; 149 } 150 } 151 return s; 152 } 153 154 PseudoId CSSSelector::pseudoId(PseudoType type) 155 { 156 switch (type) { 157 case PseudoFirstLine: 158 return FIRST_LINE; 159 case PseudoFirstLetter: 160 return FIRST_LETTER; 161 case PseudoSelection: 162 return SELECTION; 163 case PseudoBefore: 164 return BEFORE; 165 case PseudoAfter: 166 return AFTER; 167 case PseudoBackdrop: 168 return BACKDROP; 169 case PseudoScrollbar: 170 return SCROLLBAR; 171 case PseudoScrollbarButton: 172 return SCROLLBAR_BUTTON; 173 case PseudoScrollbarCorner: 174 return SCROLLBAR_CORNER; 175 case PseudoScrollbarThumb: 176 return SCROLLBAR_THUMB; 177 case PseudoScrollbarTrack: 178 return SCROLLBAR_TRACK; 179 case PseudoScrollbarTrackPiece: 180 return SCROLLBAR_TRACK_PIECE; 181 case PseudoResizer: 182 return RESIZER; 183 case PseudoFullScreen: 184 return FULL_SCREEN; 185 case PseudoFullScreenDocument: 186 return FULL_SCREEN_DOCUMENT; 187 case PseudoFullScreenAncestor: 188 return FULL_SCREEN_ANCESTOR; 189 case PseudoUnknown: 190 case PseudoEmpty: 191 case PseudoFirstChild: 192 case PseudoFirstOfType: 193 case PseudoLastChild: 194 case PseudoLastOfType: 195 case PseudoOnlyChild: 196 case PseudoOnlyOfType: 197 case PseudoNthChild: 198 case PseudoNthOfType: 199 case PseudoNthLastChild: 200 case PseudoNthLastOfType: 201 case PseudoLink: 202 case PseudoVisited: 203 case PseudoAny: 204 case PseudoAnyLink: 205 case PseudoAutofill: 206 case PseudoHover: 207 case PseudoDrag: 208 case PseudoFocus: 209 case PseudoActive: 210 case PseudoChecked: 211 case PseudoEnabled: 212 case PseudoFullPageMedia: 213 case PseudoDefault: 214 case PseudoDisabled: 215 case PseudoOptional: 216 case PseudoRequired: 217 case PseudoReadOnly: 218 case PseudoReadWrite: 219 case PseudoValid: 220 case PseudoInvalid: 221 case PseudoIndeterminate: 222 case PseudoTarget: 223 case PseudoLang: 224 case PseudoNot: 225 case PseudoRoot: 226 case PseudoScope: 227 case PseudoScrollbarBack: 228 case PseudoScrollbarForward: 229 case PseudoWindowInactive: 230 case PseudoCornerPresent: 231 case PseudoDecrement: 232 case PseudoIncrement: 233 case PseudoHorizontal: 234 case PseudoVertical: 235 case PseudoStart: 236 case PseudoEnd: 237 case PseudoDoubleButton: 238 case PseudoSingleButton: 239 case PseudoNoButton: 240 case PseudoFirstPage: 241 case PseudoLeftPage: 242 case PseudoRightPage: 243 case PseudoInRange: 244 case PseudoOutOfRange: 245 case PseudoUserAgentCustomElement: 246 case PseudoWebKitCustomElement: 247 case PseudoCue: 248 case PseudoFutureCue: 249 case PseudoPastCue: 250 case PseudoSeamlessDocument: 251 case PseudoDistributed: 252 case PseudoPart: 253 case PseudoUnresolved: 254 case PseudoContent: 255 case PseudoHost: 256 return NOPSEUDO; 257 case PseudoNotParsed: 258 ASSERT_NOT_REACHED(); 259 return NOPSEUDO; 260 } 261 262 ASSERT_NOT_REACHED(); 263 return NOPSEUDO; 264 } 265 266 static HashMap<StringImpl*, CSSSelector::PseudoType>* nameToPseudoTypeMap() 267 { 268 DEFINE_STATIC_LOCAL(AtomicString, active, ("active", AtomicString::ConstructFromLiteral)); 269 DEFINE_STATIC_LOCAL(AtomicString, after, ("after", AtomicString::ConstructFromLiteral)); 270 DEFINE_STATIC_LOCAL(AtomicString, any, ("-webkit-any(", AtomicString::ConstructFromLiteral)); 271 DEFINE_STATIC_LOCAL(AtomicString, anyLink, ("-webkit-any-link", AtomicString::ConstructFromLiteral)); 272 DEFINE_STATIC_LOCAL(AtomicString, autofill, ("-webkit-autofill", AtomicString::ConstructFromLiteral)); 273 DEFINE_STATIC_LOCAL(AtomicString, backdrop, ("backdrop", AtomicString::ConstructFromLiteral)); 274 DEFINE_STATIC_LOCAL(AtomicString, before, ("before", AtomicString::ConstructFromLiteral)); 275 DEFINE_STATIC_LOCAL(AtomicString, checked, ("checked", AtomicString::ConstructFromLiteral)); 276 DEFINE_STATIC_LOCAL(AtomicString, defaultString, ("default", AtomicString::ConstructFromLiteral)); 277 DEFINE_STATIC_LOCAL(AtomicString, disabled, ("disabled", AtomicString::ConstructFromLiteral)); 278 DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only", AtomicString::ConstructFromLiteral)); 279 DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write", AtomicString::ConstructFromLiteral)); 280 DEFINE_STATIC_LOCAL(AtomicString, valid, ("valid", AtomicString::ConstructFromLiteral)); 281 DEFINE_STATIC_LOCAL(AtomicString, invalid, ("invalid", AtomicString::ConstructFromLiteral)); 282 DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag", AtomicString::ConstructFromLiteral)); 283 DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag", AtomicString::ConstructFromLiteral)); // was documented with this name in Apple documentation, so keep an alia 284 DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty", AtomicString::ConstructFromLiteral)); 285 DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled", AtomicString::ConstructFromLiteral)); 286 DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child", AtomicString::ConstructFromLiteral)); 287 DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter", AtomicString::ConstructFromLiteral)); 288 DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line", AtomicString::ConstructFromLiteral)); 289 DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type", AtomicString::ConstructFromLiteral)); 290 DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media", AtomicString::ConstructFromLiteral)); 291 DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child(", AtomicString::ConstructFromLiteral)); 292 DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type(", AtomicString::ConstructFromLiteral)); 293 DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child(", AtomicString::ConstructFromLiteral)); 294 DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type(", AtomicString::ConstructFromLiteral)); 295 DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus", AtomicString::ConstructFromLiteral)); 296 DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover", AtomicString::ConstructFromLiteral)); 297 DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate", AtomicString::ConstructFromLiteral)); 298 DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child", AtomicString::ConstructFromLiteral)); 299 DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type", AtomicString::ConstructFromLiteral)); 300 DEFINE_STATIC_LOCAL(AtomicString, link, ("link", AtomicString::ConstructFromLiteral)); 301 DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang(", AtomicString::ConstructFromLiteral)); 302 DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not(", AtomicString::ConstructFromLiteral)); 303 DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child", AtomicString::ConstructFromLiteral)); 304 DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type", AtomicString::ConstructFromLiteral)); 305 DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional", AtomicString::ConstructFromLiteral)); 306 DEFINE_STATIC_LOCAL(AtomicString, required, ("required", AtomicString::ConstructFromLiteral)); 307 DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer", AtomicString::ConstructFromLiteral)); 308 DEFINE_STATIC_LOCAL(AtomicString, root, ("root", AtomicString::ConstructFromLiteral)); 309 DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar", AtomicString::ConstructFromLiteral)); 310 DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button", AtomicString::ConstructFromLiteral)); 311 DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner", AtomicString::ConstructFromLiteral)); 312 DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb", AtomicString::ConstructFromLiteral)); 313 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track", AtomicString::ConstructFromLiteral)); 314 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece", AtomicString::ConstructFromLiteral)); 315 DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection", AtomicString::ConstructFromLiteral)); 316 DEFINE_STATIC_LOCAL(AtomicString, target, ("target", AtomicString::ConstructFromLiteral)); 317 DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited", AtomicString::ConstructFromLiteral)); 318 DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive", AtomicString::ConstructFromLiteral)); 319 DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement", AtomicString::ConstructFromLiteral)); 320 DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment", AtomicString::ConstructFromLiteral)); 321 DEFINE_STATIC_LOCAL(AtomicString, start, ("start", AtomicString::ConstructFromLiteral)); 322 DEFINE_STATIC_LOCAL(AtomicString, end, ("end", AtomicString::ConstructFromLiteral)); 323 DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal", AtomicString::ConstructFromLiteral)); 324 DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical", AtomicString::ConstructFromLiteral)); 325 DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button", AtomicString::ConstructFromLiteral)); 326 DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button", AtomicString::ConstructFromLiteral)); 327 DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button", AtomicString::ConstructFromLiteral)); 328 DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present", AtomicString::ConstructFromLiteral)); 329 // Paged Media pseudo-classes 330 DEFINE_STATIC_LOCAL(AtomicString, firstPage, ("first", AtomicString::ConstructFromLiteral)); 331 DEFINE_STATIC_LOCAL(AtomicString, leftPage, ("left", AtomicString::ConstructFromLiteral)); 332 DEFINE_STATIC_LOCAL(AtomicString, rightPage, ("right", AtomicString::ConstructFromLiteral)); 333 DEFINE_STATIC_LOCAL(AtomicString, fullScreen, ("-webkit-full-screen", AtomicString::ConstructFromLiteral)); 334 DEFINE_STATIC_LOCAL(AtomicString, fullScreenDocument, ("-webkit-full-screen-document", AtomicString::ConstructFromLiteral)); 335 DEFINE_STATIC_LOCAL(AtomicString, fullScreenAncestor, ("-webkit-full-screen-ancestor", AtomicString::ConstructFromLiteral)); 336 DEFINE_STATIC_LOCAL(AtomicString, cue, ("cue(", AtomicString::ConstructFromLiteral)); 337 DEFINE_STATIC_LOCAL(AtomicString, cueWithoutParen, ("cue", AtomicString::ConstructFromLiteral)); 338 DEFINE_STATIC_LOCAL(AtomicString, futureCue, ("future", AtomicString::ConstructFromLiteral)); 339 DEFINE_STATIC_LOCAL(AtomicString, pastCue, ("past", AtomicString::ConstructFromLiteral)); 340 DEFINE_STATIC_LOCAL(AtomicString, seamlessDocument, ("-webkit-seamless-document", AtomicString::ConstructFromLiteral)); 341 DEFINE_STATIC_LOCAL(AtomicString, distributed, ("-webkit-distributed(", AtomicString::ConstructFromLiteral)); 342 DEFINE_STATIC_LOCAL(AtomicString, inRange, ("in-range", AtomicString::ConstructFromLiteral)); 343 DEFINE_STATIC_LOCAL(AtomicString, outOfRange, ("out-of-range", AtomicString::ConstructFromLiteral)); 344 DEFINE_STATIC_LOCAL(AtomicString, scope, ("scope", AtomicString::ConstructFromLiteral)); 345 DEFINE_STATIC_LOCAL(AtomicString, part, ("part(", AtomicString::ConstructFromLiteral)); 346 DEFINE_STATIC_LOCAL(AtomicString, unresolved, ("unresolved", AtomicString::ConstructFromLiteral)); 347 DEFINE_STATIC_LOCAL(AtomicString, content, ("content", AtomicString::ConstructFromLiteral)); 348 DEFINE_STATIC_LOCAL(AtomicString, host, ("host", AtomicString::ConstructFromLiteral)); 349 DEFINE_STATIC_LOCAL(AtomicString, hostWithParams, ("host(", AtomicString::ConstructFromLiteral)); 350 351 static HashMap<StringImpl*, CSSSelector::PseudoType>* nameToPseudoType = 0; 352 if (!nameToPseudoType) { 353 nameToPseudoType = new HashMap<StringImpl*, CSSSelector::PseudoType>; 354 nameToPseudoType->set(active.impl(), CSSSelector::PseudoActive); 355 nameToPseudoType->set(after.impl(), CSSSelector::PseudoAfter); 356 nameToPseudoType->set(anyLink.impl(), CSSSelector::PseudoAnyLink); 357 nameToPseudoType->set(any.impl(), CSSSelector::PseudoAny); 358 nameToPseudoType->set(autofill.impl(), CSSSelector::PseudoAutofill); 359 nameToPseudoType->set(backdrop.impl(), CSSSelector::PseudoBackdrop); 360 nameToPseudoType->set(before.impl(), CSSSelector::PseudoBefore); 361 nameToPseudoType->set(checked.impl(), CSSSelector::PseudoChecked); 362 nameToPseudoType->set(defaultString.impl(), CSSSelector::PseudoDefault); 363 nameToPseudoType->set(disabled.impl(), CSSSelector::PseudoDisabled); 364 nameToPseudoType->set(readOnly.impl(), CSSSelector::PseudoReadOnly); 365 nameToPseudoType->set(readWrite.impl(), CSSSelector::PseudoReadWrite); 366 nameToPseudoType->set(valid.impl(), CSSSelector::PseudoValid); 367 nameToPseudoType->set(invalid.impl(), CSSSelector::PseudoInvalid); 368 nameToPseudoType->set(drag.impl(), CSSSelector::PseudoDrag); 369 nameToPseudoType->set(dragAlias.impl(), CSSSelector::PseudoDrag); 370 nameToPseudoType->set(enabled.impl(), CSSSelector::PseudoEnabled); 371 nameToPseudoType->set(empty.impl(), CSSSelector::PseudoEmpty); 372 nameToPseudoType->set(firstChild.impl(), CSSSelector::PseudoFirstChild); 373 nameToPseudoType->set(fullPageMedia.impl(), CSSSelector::PseudoFullPageMedia); 374 nameToPseudoType->set(lastChild.impl(), CSSSelector::PseudoLastChild); 375 nameToPseudoType->set(lastOfType.impl(), CSSSelector::PseudoLastOfType); 376 nameToPseudoType->set(onlyChild.impl(), CSSSelector::PseudoOnlyChild); 377 nameToPseudoType->set(onlyOfType.impl(), CSSSelector::PseudoOnlyOfType); 378 nameToPseudoType->set(firstLetter.impl(), CSSSelector::PseudoFirstLetter); 379 nameToPseudoType->set(firstLine.impl(), CSSSelector::PseudoFirstLine); 380 nameToPseudoType->set(firstOfType.impl(), CSSSelector::PseudoFirstOfType); 381 nameToPseudoType->set(focus.impl(), CSSSelector::PseudoFocus); 382 nameToPseudoType->set(hover.impl(), CSSSelector::PseudoHover); 383 nameToPseudoType->set(indeterminate.impl(), CSSSelector::PseudoIndeterminate); 384 nameToPseudoType->set(link.impl(), CSSSelector::PseudoLink); 385 nameToPseudoType->set(lang.impl(), CSSSelector::PseudoLang); 386 nameToPseudoType->set(notStr.impl(), CSSSelector::PseudoNot); 387 nameToPseudoType->set(nthChild.impl(), CSSSelector::PseudoNthChild); 388 nameToPseudoType->set(nthOfType.impl(), CSSSelector::PseudoNthOfType); 389 nameToPseudoType->set(nthLastChild.impl(), CSSSelector::PseudoNthLastChild); 390 nameToPseudoType->set(nthLastOfType.impl(), CSSSelector::PseudoNthLastOfType); 391 nameToPseudoType->set(root.impl(), CSSSelector::PseudoRoot); 392 nameToPseudoType->set(windowInactive.impl(), CSSSelector::PseudoWindowInactive); 393 nameToPseudoType->set(decrement.impl(), CSSSelector::PseudoDecrement); 394 nameToPseudoType->set(increment.impl(), CSSSelector::PseudoIncrement); 395 nameToPseudoType->set(start.impl(), CSSSelector::PseudoStart); 396 nameToPseudoType->set(end.impl(), CSSSelector::PseudoEnd); 397 nameToPseudoType->set(horizontal.impl(), CSSSelector::PseudoHorizontal); 398 nameToPseudoType->set(vertical.impl(), CSSSelector::PseudoVertical); 399 nameToPseudoType->set(doubleButton.impl(), CSSSelector::PseudoDoubleButton); 400 nameToPseudoType->set(singleButton.impl(), CSSSelector::PseudoSingleButton); 401 nameToPseudoType->set(noButton.impl(), CSSSelector::PseudoNoButton); 402 nameToPseudoType->set(optional.impl(), CSSSelector::PseudoOptional); 403 nameToPseudoType->set(required.impl(), CSSSelector::PseudoRequired); 404 nameToPseudoType->set(resizer.impl(), CSSSelector::PseudoResizer); 405 nameToPseudoType->set(scope.impl(), CSSSelector::PseudoScope); 406 nameToPseudoType->set(scrollbar.impl(), CSSSelector::PseudoScrollbar); 407 nameToPseudoType->set(scrollbarButton.impl(), CSSSelector::PseudoScrollbarButton); 408 nameToPseudoType->set(scrollbarCorner.impl(), CSSSelector::PseudoScrollbarCorner); 409 nameToPseudoType->set(scrollbarThumb.impl(), CSSSelector::PseudoScrollbarThumb); 410 nameToPseudoType->set(scrollbarTrack.impl(), CSSSelector::PseudoScrollbarTrack); 411 nameToPseudoType->set(scrollbarTrackPiece.impl(), CSSSelector::PseudoScrollbarTrackPiece); 412 nameToPseudoType->set(cornerPresent.impl(), CSSSelector::PseudoCornerPresent); 413 nameToPseudoType->set(selection.impl(), CSSSelector::PseudoSelection); 414 nameToPseudoType->set(target.impl(), CSSSelector::PseudoTarget); 415 nameToPseudoType->set(visited.impl(), CSSSelector::PseudoVisited); 416 nameToPseudoType->set(firstPage.impl(), CSSSelector::PseudoFirstPage); 417 nameToPseudoType->set(leftPage.impl(), CSSSelector::PseudoLeftPage); 418 nameToPseudoType->set(rightPage.impl(), CSSSelector::PseudoRightPage); 419 nameToPseudoType->set(fullScreen.impl(), CSSSelector::PseudoFullScreen); 420 nameToPseudoType->set(fullScreenDocument.impl(), CSSSelector::PseudoFullScreenDocument); 421 nameToPseudoType->set(fullScreenAncestor.impl(), CSSSelector::PseudoFullScreenAncestor); 422 nameToPseudoType->set(cue.impl(), CSSSelector::PseudoCue); 423 nameToPseudoType->set(cueWithoutParen.impl(), CSSSelector::PseudoWebKitCustomElement); 424 nameToPseudoType->set(futureCue.impl(), CSSSelector::PseudoFutureCue); 425 nameToPseudoType->set(pastCue.impl(), CSSSelector::PseudoPastCue); 426 nameToPseudoType->set(seamlessDocument.impl(), CSSSelector::PseudoSeamlessDocument); 427 nameToPseudoType->set(distributed.impl(), CSSSelector::PseudoDistributed); 428 nameToPseudoType->set(inRange.impl(), CSSSelector::PseudoInRange); 429 nameToPseudoType->set(outOfRange.impl(), CSSSelector::PseudoOutOfRange); 430 if (RuntimeEnabledFeatures::shadowDOMEnabled()) { 431 nameToPseudoType->set(part.impl(), CSSSelector::PseudoPart); 432 nameToPseudoType->set(host.impl(), CSSSelector::PseudoHost); 433 nameToPseudoType->set(hostWithParams.impl(), CSSSelector::PseudoHost); 434 nameToPseudoType->set(content.impl(), CSSSelector::PseudoContent); 435 } 436 if (RuntimeEnabledFeatures::customDOMElementsEnabled() || RuntimeEnabledFeatures::embedderCustomElementsEnabled()) 437 nameToPseudoType->set(unresolved.impl(), CSSSelector::PseudoUnresolved); 438 } 439 return nameToPseudoType; 440 } 441 442 CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name) 443 { 444 if (name.isNull()) 445 return PseudoUnknown; 446 HashMap<StringImpl*, CSSSelector::PseudoType>* nameToPseudoType = nameToPseudoTypeMap(); 447 HashMap<StringImpl*, CSSSelector::PseudoType>::iterator slot = nameToPseudoType->find(name.impl()); 448 449 if (slot != nameToPseudoType->end()) 450 return slot->value; 451 452 if (name.startsWith("-webkit-")) 453 return PseudoWebKitCustomElement; 454 if (name.startsWith("x-") || name.startsWith("cue")) 455 return PseudoUserAgentCustomElement; 456 457 return PseudoUnknown; 458 } 459 460 void CSSSelector::extractPseudoType() const 461 { 462 if (m_match != PseudoClass && m_match != PseudoElement && m_match != PagePseudoClass) 463 return; 464 465 m_pseudoType = parsePseudoType(value()); 466 467 bool element = false; // pseudo-element 468 bool compat = false; // single colon compatbility mode 469 bool isPagePseudoClass = false; // Page pseudo-class 470 471 switch (m_pseudoType) { 472 case PseudoAfter: 473 case PseudoBefore: 474 case PseudoCue: 475 case PseudoFirstLetter: 476 case PseudoFirstLine: 477 compat = true; 478 case PseudoBackdrop: 479 case PseudoDistributed: 480 case PseudoResizer: 481 case PseudoScrollbar: 482 case PseudoScrollbarCorner: 483 case PseudoScrollbarButton: 484 case PseudoScrollbarThumb: 485 case PseudoScrollbarTrack: 486 case PseudoScrollbarTrackPiece: 487 case PseudoSelection: 488 case PseudoUserAgentCustomElement: 489 case PseudoWebKitCustomElement: 490 case PseudoPart: 491 case PseudoContent: 492 element = true; 493 break; 494 case PseudoUnknown: 495 case PseudoEmpty: 496 case PseudoFirstChild: 497 case PseudoFirstOfType: 498 case PseudoLastChild: 499 case PseudoLastOfType: 500 case PseudoOnlyChild: 501 case PseudoOnlyOfType: 502 case PseudoNthChild: 503 case PseudoNthOfType: 504 case PseudoNthLastChild: 505 case PseudoNthLastOfType: 506 case PseudoLink: 507 case PseudoVisited: 508 case PseudoAny: 509 case PseudoAnyLink: 510 case PseudoAutofill: 511 case PseudoHover: 512 case PseudoDrag: 513 case PseudoFocus: 514 case PseudoActive: 515 case PseudoChecked: 516 case PseudoEnabled: 517 case PseudoFullPageMedia: 518 case PseudoDefault: 519 case PseudoDisabled: 520 case PseudoOptional: 521 case PseudoRequired: 522 case PseudoReadOnly: 523 case PseudoReadWrite: 524 case PseudoScope: 525 case PseudoValid: 526 case PseudoInvalid: 527 case PseudoIndeterminate: 528 case PseudoTarget: 529 case PseudoLang: 530 case PseudoNot: 531 case PseudoRoot: 532 case PseudoScrollbarBack: 533 case PseudoScrollbarForward: 534 case PseudoWindowInactive: 535 case PseudoCornerPresent: 536 case PseudoDecrement: 537 case PseudoIncrement: 538 case PseudoHorizontal: 539 case PseudoVertical: 540 case PseudoStart: 541 case PseudoEnd: 542 case PseudoDoubleButton: 543 case PseudoSingleButton: 544 case PseudoNoButton: 545 case PseudoNotParsed: 546 case PseudoFullScreen: 547 case PseudoFullScreenDocument: 548 case PseudoFullScreenAncestor: 549 case PseudoSeamlessDocument: 550 case PseudoInRange: 551 case PseudoOutOfRange: 552 case PseudoFutureCue: 553 case PseudoPastCue: 554 case PseudoHost: 555 case PseudoUnresolved: 556 break; 557 case PseudoFirstPage: 558 case PseudoLeftPage: 559 case PseudoRightPage: 560 isPagePseudoClass = true; 561 break; 562 } 563 564 bool matchPagePseudoClass = (m_match == PagePseudoClass); 565 if (matchPagePseudoClass != isPagePseudoClass) 566 m_pseudoType = PseudoUnknown; 567 else if (m_match == PseudoClass && element) { 568 if (!compat) 569 m_pseudoType = PseudoUnknown; 570 else 571 m_match = PseudoElement; 572 } else if (m_match == PseudoElement && !element) 573 m_pseudoType = PseudoUnknown; 574 } 575 576 bool CSSSelector::operator==(const CSSSelector& other) const 577 { 578 const CSSSelector* sel1 = this; 579 const CSSSelector* sel2 = &other; 580 581 while (sel1 && sel2) { 582 if (sel1->attribute() != sel2->attribute() 583 || sel1->relation() != sel2->relation() 584 || sel1->m_match != sel2->m_match 585 || sel1->value() != sel2->value() 586 || sel1->pseudoType() != sel2->pseudoType() 587 || sel1->argument() != sel2->argument()) { 588 return false; 589 } 590 if (sel1->m_match == Tag) { 591 if (sel1->tagQName() != sel2->tagQName()) 592 return false; 593 } 594 sel1 = sel1->tagHistory(); 595 sel2 = sel2->tagHistory(); 596 } 597 598 if (sel1 || sel2) 599 return false; 600 601 return true; 602 } 603 604 String CSSSelector::selectorText(const String& rightSide) const 605 { 606 StringBuilder str; 607 608 if (m_match == CSSSelector::Tag && !m_tagIsForNamespaceRule) { 609 if (tagQName().prefix().isNull()) 610 str.append(tagQName().localName()); 611 else { 612 str.append(tagQName().prefix().string()); 613 str.append('|'); 614 str.append(tagQName().localName()); 615 } 616 } 617 618 const CSSSelector* cs = this; 619 while (true) { 620 if (cs->m_match == CSSSelector::Id) { 621 str.append('#'); 622 serializeIdentifier(cs->value(), str); 623 } else if (cs->m_match == CSSSelector::Class) { 624 str.append('.'); 625 serializeIdentifier(cs->value(), str); 626 } else if (cs->m_match == CSSSelector::PseudoClass || cs->m_match == CSSSelector::PagePseudoClass) { 627 str.append(':'); 628 str.append(cs->value()); 629 630 switch (cs->pseudoType()) { 631 case PseudoNot: 632 ASSERT(cs->selectorList()); 633 str.append(cs->selectorList()->first()->selectorText()); 634 str.append(')'); 635 break; 636 case PseudoLang: 637 case PseudoNthChild: 638 case PseudoNthLastChild: 639 case PseudoNthOfType: 640 case PseudoNthLastOfType: 641 str.append(cs->argument()); 642 str.append(')'); 643 break; 644 case PseudoAny: { 645 const CSSSelector* firstSubSelector = cs->selectorList()->first(); 646 for (const CSSSelector* subSelector = firstSubSelector; subSelector; subSelector = CSSSelectorList::next(subSelector)) { 647 if (subSelector != firstSubSelector) 648 str.append(','); 649 str.append(subSelector->selectorText()); 650 } 651 str.append(')'); 652 break; 653 } 654 case PseudoHost: { 655 if (cs->selectorList()) { 656 const CSSSelector* firstSubSelector = cs->selectorList()->first(); 657 for (const CSSSelector* subSelector = firstSubSelector; subSelector; subSelector = CSSSelectorList::next(subSelector)) { 658 if (subSelector != firstSubSelector) 659 str.append(','); 660 str.append(subSelector->selectorText()); 661 } 662 str.append(')'); 663 } 664 break; 665 } 666 default: 667 break; 668 } 669 } else if (cs->m_match == CSSSelector::PseudoElement) { 670 str.appendLiteral("::"); 671 str.append(cs->value()); 672 673 switch (cs->pseudoType()) { 674 case PseudoPart: 675 str.append(cs->argument()); 676 str.append(')'); 677 break; 678 case PseudoContent: 679 if (cs->relation() == CSSSelector::SubSelector && cs->tagHistory()) 680 return cs->tagHistory()->selectorText() + str.toString() + rightSide; 681 break; 682 default: 683 break; 684 } 685 } else if (cs->isAttributeSelector()) { 686 str.append('['); 687 const AtomicString& prefix = cs->attribute().prefix(); 688 if (!prefix.isNull()) { 689 str.append(prefix); 690 str.append("|"); 691 } 692 str.append(cs->attribute().localName()); 693 switch (cs->m_match) { 694 case CSSSelector::Exact: 695 str.append('='); 696 break; 697 case CSSSelector::Set: 698 // set has no operator or value, just the attrName 699 str.append(']'); 700 break; 701 case CSSSelector::List: 702 str.appendLiteral("~="); 703 break; 704 case CSSSelector::Hyphen: 705 str.appendLiteral("|="); 706 break; 707 case CSSSelector::Begin: 708 str.appendLiteral("^="); 709 break; 710 case CSSSelector::End: 711 str.appendLiteral("$="); 712 break; 713 case CSSSelector::Contain: 714 str.appendLiteral("*="); 715 break; 716 default: 717 break; 718 } 719 if (cs->m_match != CSSSelector::Set) { 720 serializeString(cs->value(), str); 721 str.append(']'); 722 } 723 } 724 if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory()) 725 break; 726 cs = cs->tagHistory(); 727 } 728 729 if (const CSSSelector* tagHistory = cs->tagHistory()) { 730 switch (cs->relation()) { 731 case CSSSelector::Descendant: 732 if (cs->relationIsAffectedByPseudoContent() && tagHistory->pseudoType() != CSSSelector::PseudoContent) 733 return tagHistory->selectorText("::-webkit-distributed(" + str.toString() + rightSide + ")"); 734 return tagHistory->selectorText(" " + str.toString() + rightSide); 735 case CSSSelector::Child: 736 if (cs->relationIsAffectedByPseudoContent() && tagHistory->pseudoType() != CSSSelector::PseudoContent) 737 return tagHistory->selectorText("::-webkit-distributed(> " + str.toString() + rightSide + ")"); 738 return tagHistory->selectorText(" > " + str.toString() + rightSide); 739 case CSSSelector::DirectAdjacent: 740 return tagHistory->selectorText(" + " + str.toString() + rightSide); 741 case CSSSelector::IndirectAdjacent: 742 return tagHistory->selectorText(" ~ " + str.toString() + rightSide); 743 case CSSSelector::SubSelector: 744 ASSERT_NOT_REACHED(); 745 case CSSSelector::ShadowPseudo: 746 return tagHistory->selectorText(str.toString() + rightSide); 747 } 748 } 749 return str.toString() + rightSide; 750 } 751 752 void CSSSelector::setAttribute(const QualifiedName& value) 753 { 754 createRareData(); 755 m_data.m_rareData->m_attribute = value; 756 } 757 758 void CSSSelector::setArgument(const AtomicString& value) 759 { 760 createRareData(); 761 m_data.m_rareData->m_argument = value; 762 } 763 764 void CSSSelector::setSelectorList(PassOwnPtr<CSSSelectorList> selectorList) 765 { 766 createRareData(); 767 m_data.m_rareData->m_selectorList = selectorList; 768 } 769 770 void CSSSelector::setMatchUserAgentOnly() 771 { 772 createRareData(); 773 m_data.m_rareData->m_matchUserAgentOnly = true; 774 } 775 776 static bool validateSubSelector(const CSSSelector* selector) 777 { 778 switch (selector->m_match) { 779 case CSSSelector::Tag: 780 case CSSSelector::Id: 781 case CSSSelector::Class: 782 case CSSSelector::Exact: 783 case CSSSelector::Set: 784 case CSSSelector::List: 785 case CSSSelector::Hyphen: 786 case CSSSelector::Contain: 787 case CSSSelector::Begin: 788 case CSSSelector::End: 789 return true; 790 case CSSSelector::PseudoElement: 791 return false; 792 case CSSSelector::PagePseudoClass: 793 case CSSSelector::PseudoClass: 794 break; 795 } 796 797 switch (selector->pseudoType()) { 798 case CSSSelector::PseudoEmpty: 799 case CSSSelector::PseudoLink: 800 case CSSSelector::PseudoVisited: 801 case CSSSelector::PseudoTarget: 802 case CSSSelector::PseudoEnabled: 803 case CSSSelector::PseudoDisabled: 804 case CSSSelector::PseudoChecked: 805 case CSSSelector::PseudoIndeterminate: 806 case CSSSelector::PseudoNthChild: 807 case CSSSelector::PseudoNthLastChild: 808 case CSSSelector::PseudoNthOfType: 809 case CSSSelector::PseudoNthLastOfType: 810 case CSSSelector::PseudoFirstChild: 811 case CSSSelector::PseudoLastChild: 812 case CSSSelector::PseudoFirstOfType: 813 case CSSSelector::PseudoLastOfType: 814 case CSSSelector::PseudoOnlyOfType: 815 return true; 816 default: 817 return false; 818 } 819 } 820 821 bool CSSSelector::isCompound() const 822 { 823 if (!validateSubSelector(this)) 824 return false; 825 826 const CSSSelector* prevSubSelector = this; 827 const CSSSelector* subSelector = tagHistory(); 828 829 while (subSelector) { 830 if (prevSubSelector->relation() != CSSSelector::SubSelector) 831 return false; 832 if (!validateSubSelector(subSelector)) 833 return false; 834 835 prevSubSelector = subSelector; 836 subSelector = subSelector->tagHistory(); 837 } 838 839 return true; 840 } 841 842 bool CSSSelector::parseNth() const 843 { 844 if (!m_hasRareData) 845 return false; 846 if (m_parsedNth) 847 return true; 848 m_parsedNth = m_data.m_rareData->parseNth(); 849 return m_parsedNth; 850 } 851 852 bool CSSSelector::matchNth(int count) const 853 { 854 ASSERT(m_hasRareData); 855 return m_data.m_rareData->matchNth(count); 856 } 857 858 CSSSelector::RareData::RareData(PassRefPtr<StringImpl> value) 859 : m_value(value.leakRef()) 860 , m_a(0) 861 , m_b(0) 862 , m_attribute(anyQName()) 863 , m_argument(nullAtom) 864 , m_matchUserAgentOnly(0) 865 { 866 } 867 868 CSSSelector::RareData::~RareData() 869 { 870 if (m_value) 871 m_value->deref(); 872 } 873 874 // a helper function for parsing nth-arguments 875 bool CSSSelector::RareData::parseNth() 876 { 877 String argument = m_argument.lower(); 878 879 if (argument.isEmpty()) 880 return false; 881 882 m_a = 0; 883 m_b = 0; 884 if (argument == "odd") { 885 m_a = 2; 886 m_b = 1; 887 } else if (argument == "even") { 888 m_a = 2; 889 m_b = 0; 890 } else { 891 size_t n = argument.find('n'); 892 if (n != notFound) { 893 if (argument[0] == '-') { 894 if (n == 1) 895 m_a = -1; // -n == -1n 896 else 897 m_a = argument.substring(0, n).toInt(); 898 } else if (!n) 899 m_a = 1; // n == 1n 900 else 901 m_a = argument.substring(0, n).toInt(); 902 903 size_t p = argument.find('+', n); 904 if (p != notFound) 905 m_b = argument.substring(p + 1, argument.length() - p - 1).toInt(); 906 else { 907 p = argument.find('-', n); 908 if (p != notFound) 909 m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt(); 910 } 911 } else 912 m_b = argument.toInt(); 913 } 914 return true; 915 } 916 917 // a helper function for checking nth-arguments 918 bool CSSSelector::RareData::matchNth(int count) 919 { 920 if (!m_a) 921 return count == m_b; 922 else if (m_a > 0) { 923 if (count < m_b) 924 return false; 925 return (count - m_b) % m_a == 0; 926 } else { 927 if (count > m_b) 928 return false; 929 return (m_b - count) % (-m_a) == 0; 930 } 931 } 932 933 } // namespace WebCore 934