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