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