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 Apple Inc. All rights reserved. 7 * Copyright (C) 2008 David Smith (catfish.man (at) gmail.com) 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 */ 24 25 #include "config.h" 26 #include "CSSSelector.h" 27 28 #include "wtf/Assertions.h" 29 #include "HTMLNames.h" 30 31 #include <wtf/StdLibExtras.h> 32 33 namespace WebCore { 34 35 using namespace HTMLNames; 36 37 unsigned int CSSSelector::specificity() 38 { 39 // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function 40 // isn't quite correct. 41 int s = (m_tag.localName() == starAtom ? 0 : 1); 42 switch (m_match) { 43 case Id: 44 s += 0x10000; 45 break; 46 case Exact: 47 case Class: 48 case Set: 49 case List: 50 case Hyphen: 51 case PseudoClass: 52 case PseudoElement: 53 case Contain: 54 case Begin: 55 case End: 56 s += 0x100; 57 case None: 58 break; 59 } 60 61 if (CSSSelector* tagHistory = this->tagHistory()) 62 s += tagHistory->specificity(); 63 64 // make sure it doesn't overflow 65 return s & 0xffffff; 66 } 67 68 void CSSSelector::extractPseudoType() const 69 { 70 if (m_match != PseudoClass && m_match != PseudoElement) 71 return; 72 73 DEFINE_STATIC_LOCAL(AtomicString, active, ("active")); 74 DEFINE_STATIC_LOCAL(AtomicString, after, ("after")); 75 DEFINE_STATIC_LOCAL(AtomicString, anyLink, ("-webkit-any-link")); 76 DEFINE_STATIC_LOCAL(AtomicString, autofill, ("-webkit-autofill")); 77 DEFINE_STATIC_LOCAL(AtomicString, before, ("before")); 78 DEFINE_STATIC_LOCAL(AtomicString, checked, ("checked")); 79 DEFINE_STATIC_LOCAL(AtomicString, fileUploadButton, ("-webkit-file-upload-button")); 80 DEFINE_STATIC_LOCAL(AtomicString, defaultString, ("default")); 81 DEFINE_STATIC_LOCAL(AtomicString, disabled, ("disabled")); 82 DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only")); 83 DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write")); 84 DEFINE_STATIC_LOCAL(AtomicString, valid, ("valid")); 85 DEFINE_STATIC_LOCAL(AtomicString, invalid, ("invalid")); 86 DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag")); 87 DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag")); // was documented with this name in Apple documentation, so keep an alia 88 DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty")); 89 DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled")); 90 DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child")); 91 DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter")); 92 DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line")); 93 DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type")); 94 DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media")); 95 DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child(")); 96 DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type(")); 97 DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child(")); 98 DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type(")); 99 DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus")); 100 DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover")); 101 DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate")); 102 DEFINE_STATIC_LOCAL(AtomicString, innerSpinButton, ("-webkit-inner-spin-button")); 103 #if ENABLE(DATALIST) 104 DEFINE_STATIC_LOCAL(AtomicString, inputListButton, ("-webkit-input-list-button")); 105 #endif 106 DEFINE_STATIC_LOCAL(AtomicString, inputPlaceholder, ("-webkit-input-placeholder")); 107 DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child")); 108 DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type")); 109 DEFINE_STATIC_LOCAL(AtomicString, link, ("link")); 110 DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang(")); 111 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsPanel, ("-webkit-media-controls-panel")); 112 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsMuteButton, ("-webkit-media-controls-mute-button")); 113 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsPlayButton, ("-webkit-media-controls-play-button")); 114 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimeline, ("-webkit-media-controls-timeline")); 115 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsVolumeSlider, ("-webkit-media-controls-volume-slider")); 116 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsSeekBackButton, ("-webkit-media-controls-seek-back-button")); 117 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsSeekForwardButton, ("-webkit-media-controls-seek-forward-button")); 118 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsRewindButton, ("-webkit-media-controls-rewind-button")); 119 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsReturnToRealtimeButton, ("-webkit-media-controls-return-to-realtime-button")); 120 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsToggleClosedCaptionsButton, ("-webkit-media-controls-toggle-closed-captions-button")); 121 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsStatusDisplay, ("-webkit-media-controls-status-display")); 122 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsFullscreenButton, ("-webkit-media-controls-fullscreen-button")); 123 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimelineContainer, ("-webkit-media-controls-timeline-container")); 124 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsVolumeSliderContainer, ("-webkit-media-controls-volume-slider-container")); 125 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsCurrentTimeDisplay, ("-webkit-media-controls-current-time-display")); 126 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimeRemainingDisplay, ("-webkit-media-controls-time-remaining-display")); 127 DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not(")); 128 DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child")); 129 DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type")); 130 DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional")); 131 DEFINE_STATIC_LOCAL(AtomicString, outerSpinButton, ("-webkit-outer-spin-button")); 132 DEFINE_STATIC_LOCAL(AtomicString, required, ("required")); 133 DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer")); 134 DEFINE_STATIC_LOCAL(AtomicString, root, ("root")); 135 DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar")); 136 DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button")); 137 DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner")); 138 DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb")); 139 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track")); 140 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece")); 141 DEFINE_STATIC_LOCAL(AtomicString, searchCancelButton, ("-webkit-search-cancel-button")); 142 DEFINE_STATIC_LOCAL(AtomicString, searchDecoration, ("-webkit-search-decoration")); 143 DEFINE_STATIC_LOCAL(AtomicString, searchResultsDecoration, ("-webkit-search-results-decoration")); 144 DEFINE_STATIC_LOCAL(AtomicString, searchResultsButton, ("-webkit-search-results-button")); 145 DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection")); 146 DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-thumb")); 147 DEFINE_STATIC_LOCAL(AtomicString, target, ("target")); 148 DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited")); 149 DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive")); 150 DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement")); 151 DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment")); 152 DEFINE_STATIC_LOCAL(AtomicString, start, ("start")); 153 DEFINE_STATIC_LOCAL(AtomicString, end, ("end")); 154 DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal")); 155 DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical")); 156 DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button")); 157 DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button")); 158 DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button")); 159 DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present")); 160 161 bool element = false; // pseudo-element 162 bool compat = false; // single colon compatbility mode 163 164 m_pseudoType = PseudoUnknown; 165 if (m_value == active) 166 m_pseudoType = PseudoActive; 167 else if (m_value == after) { 168 m_pseudoType = PseudoAfter; 169 element = true; 170 compat = true; 171 } else if (m_value == anyLink) 172 m_pseudoType = PseudoAnyLink; 173 else if (m_value == autofill) 174 m_pseudoType = PseudoAutofill; 175 else if (m_value == before) { 176 m_pseudoType = PseudoBefore; 177 element = true; 178 compat = true; 179 } else if (m_value == checked) 180 m_pseudoType = PseudoChecked; 181 else if (m_value == fileUploadButton) { 182 m_pseudoType = PseudoFileUploadButton; 183 element = true; 184 } else if (m_value == defaultString) 185 m_pseudoType = PseudoDefault; 186 else if (m_value == disabled) 187 m_pseudoType = PseudoDisabled; 188 else if (m_value == readOnly) 189 m_pseudoType = PseudoReadOnly; 190 else if (m_value == readWrite) 191 m_pseudoType = PseudoReadWrite; 192 else if (m_value == valid) 193 m_pseudoType = PseudoValid; 194 else if (m_value == invalid) 195 m_pseudoType = PseudoInvalid; 196 else if (m_value == drag || m_value == dragAlias) 197 m_pseudoType = PseudoDrag; 198 else if (m_value == enabled) 199 m_pseudoType = PseudoEnabled; 200 else if (m_value == empty) 201 m_pseudoType = PseudoEmpty; 202 else if (m_value == firstChild) 203 m_pseudoType = PseudoFirstChild; 204 else if (m_value == fullPageMedia) 205 m_pseudoType = PseudoFullPageMedia; 206 else 207 #if ENABLE(DATALIST) 208 if (m_value == inputListButton) { 209 m_pseudoType = PseudoInputListButton; 210 element = true; 211 } else 212 #endif 213 if (m_value == inputPlaceholder) { 214 m_pseudoType = PseudoInputPlaceholder; 215 element = true; 216 } else if (m_value == lastChild) 217 m_pseudoType = PseudoLastChild; 218 else if (m_value == lastOfType) 219 m_pseudoType = PseudoLastOfType; 220 else if (m_value == onlyChild) 221 m_pseudoType = PseudoOnlyChild; 222 else if (m_value == onlyOfType) 223 m_pseudoType = PseudoOnlyOfType; 224 else if (m_value == firstLetter) { 225 m_pseudoType = PseudoFirstLetter; 226 element = true; 227 compat = true; 228 } else if (m_value == firstLine) { 229 m_pseudoType = PseudoFirstLine; 230 element = true; 231 compat = true; 232 } else if (m_value == firstOfType) 233 m_pseudoType = PseudoFirstOfType; 234 else if (m_value == focus) 235 m_pseudoType = PseudoFocus; 236 else if (m_value == hover) 237 m_pseudoType = PseudoHover; 238 else if (m_value == indeterminate) 239 m_pseudoType = PseudoIndeterminate; 240 else if (m_value == innerSpinButton) { 241 m_pseudoType = PseudoInnerSpinButton; 242 element = true; 243 } else if (m_value == link) 244 m_pseudoType = PseudoLink; 245 else if (m_value == lang) 246 m_pseudoType = PseudoLang; 247 else if (m_value == mediaControlsPanel) { 248 m_pseudoType = PseudoMediaControlsPanel; 249 element = true; 250 } else if (m_value == mediaControlsMuteButton) { 251 m_pseudoType = PseudoMediaControlsMuteButton; 252 element = true; 253 } else if (m_value == mediaControlsPlayButton) { 254 m_pseudoType = PseudoMediaControlsPlayButton; 255 element = true; 256 } else if (m_value == mediaControlsCurrentTimeDisplay) { 257 m_pseudoType = PseudoMediaControlsCurrentTimeDisplay; 258 element = true; 259 } else if (m_value == mediaControlsTimeRemainingDisplay) { 260 m_pseudoType = PseudoMediaControlsTimeRemainingDisplay; 261 element = true; 262 } else if (m_value == mediaControlsTimeline) { 263 m_pseudoType = PseudoMediaControlsTimeline; 264 element = true; 265 } else if (m_value == mediaControlsVolumeSlider) { 266 m_pseudoType = PseudoMediaControlsVolumeSlider; 267 element = true; 268 } else if (m_value == mediaControlsSeekBackButton) { 269 m_pseudoType = PseudoMediaControlsSeekBackButton; 270 element = true; 271 } else if (m_value == mediaControlsSeekForwardButton) { 272 m_pseudoType = PseudoMediaControlsSeekForwardButton; 273 element = true; 274 } else if (m_value == mediaControlsRewindButton) { 275 m_pseudoType = PseudoMediaControlsRewindButton; 276 element = true; 277 } else if (m_value == mediaControlsReturnToRealtimeButton) { 278 m_pseudoType = PseudoMediaControlsReturnToRealtimeButton; 279 element = true; 280 } else if (m_value == mediaControlsToggleClosedCaptionsButton) { 281 m_pseudoType = PseudoMediaControlsToggleClosedCaptions; 282 element = true; 283 } else if (m_value == mediaControlsStatusDisplay) { 284 m_pseudoType = PseudoMediaControlsStatusDisplay; 285 element = true; 286 } else if (m_value == mediaControlsFullscreenButton) { 287 m_pseudoType = PseudoMediaControlsFullscreenButton; 288 element = true; 289 } else if (m_value == mediaControlsTimelineContainer) { 290 m_pseudoType = PseudoMediaControlsTimelineContainer; 291 element = true; 292 } else if (m_value == mediaControlsVolumeSliderContainer) { 293 m_pseudoType = PseudoMediaControlsVolumeSliderContainer; 294 element = true; 295 } else if (m_value == notStr) 296 m_pseudoType = PseudoNot; 297 else if (m_value == nthChild) 298 m_pseudoType = PseudoNthChild; 299 else if (m_value == nthOfType) 300 m_pseudoType = PseudoNthOfType; 301 else if (m_value == nthLastChild) 302 m_pseudoType = PseudoNthLastChild; 303 else if (m_value == nthLastOfType) 304 m_pseudoType = PseudoNthLastOfType; 305 else if (m_value == outerSpinButton) { 306 m_pseudoType = PseudoOuterSpinButton; 307 element = true; 308 } else if (m_value == root) 309 m_pseudoType = PseudoRoot; 310 else if (m_value == windowInactive) 311 m_pseudoType = PseudoWindowInactive; 312 else if (m_value == decrement) 313 m_pseudoType = PseudoDecrement; 314 else if (m_value == increment) 315 m_pseudoType = PseudoIncrement; 316 else if (m_value == start) 317 m_pseudoType = PseudoStart; 318 else if (m_value == end) 319 m_pseudoType = PseudoEnd; 320 else if (m_value == horizontal) 321 m_pseudoType = PseudoHorizontal; 322 else if (m_value == vertical) 323 m_pseudoType = PseudoVertical; 324 else if (m_value == doubleButton) 325 m_pseudoType = PseudoDoubleButton; 326 else if (m_value == singleButton) 327 m_pseudoType = PseudoSingleButton; 328 else if (m_value == noButton) 329 m_pseudoType = PseudoNoButton; 330 else if (m_value == optional) 331 m_pseudoType = PseudoOptional; 332 else if (m_value == required) 333 m_pseudoType = PseudoRequired; 334 else if (m_value == scrollbarCorner) { 335 element = true; 336 m_pseudoType = PseudoScrollbarCorner; 337 } else if (m_value == resizer) { 338 element = true; 339 m_pseudoType = PseudoResizer; 340 } else if (m_value == scrollbar) { 341 element = true; 342 m_pseudoType = PseudoScrollbar; 343 } else if (m_value == scrollbarButton) { 344 element = true; 345 m_pseudoType = PseudoScrollbarButton; 346 } else if (m_value == scrollbarCorner) { 347 element = true; 348 m_pseudoType = PseudoScrollbarCorner; 349 } else if (m_value == scrollbarThumb) { 350 element = true; 351 m_pseudoType = PseudoScrollbarThumb; 352 } else if (m_value == scrollbarTrack) { 353 element = true; 354 m_pseudoType = PseudoScrollbarTrack; 355 } else if (m_value == scrollbarTrackPiece) { 356 element = true; 357 m_pseudoType = PseudoScrollbarTrackPiece; 358 } else if (m_value == cornerPresent) 359 m_pseudoType = PseudoCornerPresent; 360 else if (m_value == searchCancelButton) { 361 m_pseudoType = PseudoSearchCancelButton; 362 element = true; 363 } else if (m_value == searchDecoration) { 364 m_pseudoType = PseudoSearchDecoration; 365 element = true; 366 } else if (m_value == searchResultsDecoration) { 367 m_pseudoType = PseudoSearchResultsDecoration; 368 element = true; 369 } else if (m_value == searchResultsButton) { 370 m_pseudoType = PseudoSearchResultsButton; 371 element = true; 372 } else if (m_value == selection) { 373 m_pseudoType = PseudoSelection; 374 element = true; 375 } else if (m_value == sliderThumb) { 376 m_pseudoType = PseudoSliderThumb; 377 element = true; 378 } else if (m_value == target) 379 m_pseudoType = PseudoTarget; 380 else if (m_value == visited) 381 m_pseudoType = PseudoVisited; 382 383 if (m_match == PseudoClass && element) { 384 if (!compat) 385 m_pseudoType = PseudoUnknown; 386 else 387 m_match = PseudoElement; 388 } else if (m_match == PseudoElement && !element) 389 m_pseudoType = PseudoUnknown; 390 } 391 392 bool CSSSelector::operator==(const CSSSelector& other) 393 { 394 const CSSSelector* sel1 = this; 395 const CSSSelector* sel2 = &other; 396 397 while (sel1 && sel2) { 398 if (sel1->m_tag != sel2->m_tag || sel1->attribute() != sel2->attribute() || 399 sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match || 400 sel1->m_value != sel2->m_value || 401 sel1->pseudoType() != sel2->pseudoType() || 402 sel1->argument() != sel2->argument()) 403 return false; 404 sel1 = sel1->tagHistory(); 405 sel2 = sel2->tagHistory(); 406 } 407 408 if (sel1 || sel2) 409 return false; 410 411 return true; 412 } 413 414 String CSSSelector::selectorText() const 415 { 416 String str = ""; 417 418 const AtomicString& prefix = m_tag.prefix(); 419 const AtomicString& localName = m_tag.localName(); 420 if (m_match == CSSSelector::None || !prefix.isNull() || localName != starAtom) { 421 if (prefix.isNull()) 422 str = localName; 423 else 424 str = prefix + "|" + localName; 425 } 426 427 const CSSSelector* cs = this; 428 while (true) { 429 if (cs->m_match == CSSSelector::Id) { 430 str += "#"; 431 str += cs->m_value; 432 } else if (cs->m_match == CSSSelector::Class) { 433 str += "."; 434 str += cs->m_value; 435 } else if (cs->m_match == CSSSelector::PseudoClass) { 436 str += ":"; 437 str += cs->m_value; 438 if (cs->pseudoType() == PseudoNot) { 439 if (CSSSelector* subSel = cs->simpleSelector()) 440 str += subSel->selectorText(); 441 str += ")"; 442 } else if (cs->pseudoType() == PseudoLang 443 || cs->pseudoType() == PseudoNthChild 444 || cs->pseudoType() == PseudoNthLastChild 445 || cs->pseudoType() == PseudoNthOfType 446 || cs->pseudoType() == PseudoNthLastOfType) { 447 str += cs->argument(); 448 str += ")"; 449 } 450 } else if (cs->m_match == CSSSelector::PseudoElement) { 451 str += "::"; 452 str += cs->m_value; 453 } else if (cs->hasAttribute()) { 454 str += "["; 455 const AtomicString& prefix = cs->attribute().prefix(); 456 if (!prefix.isNull()) 457 str += prefix + "|"; 458 str += cs->attribute().localName(); 459 switch (cs->m_match) { 460 case CSSSelector::Exact: 461 str += "="; 462 break; 463 case CSSSelector::Set: 464 // set has no operator or value, just the attrName 465 str += "]"; 466 break; 467 case CSSSelector::List: 468 str += "~="; 469 break; 470 case CSSSelector::Hyphen: 471 str += "|="; 472 break; 473 case CSSSelector::Begin: 474 str += "^="; 475 break; 476 case CSSSelector::End: 477 str += "$="; 478 break; 479 case CSSSelector::Contain: 480 str += "*="; 481 break; 482 default: 483 break; 484 } 485 if (cs->m_match != CSSSelector::Set) { 486 str += "\""; 487 str += cs->m_value; 488 str += "\"]"; 489 } 490 } 491 if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory()) 492 break; 493 cs = cs->tagHistory(); 494 } 495 496 if (CSSSelector* tagHistory = cs->tagHistory()) { 497 String tagHistoryText = tagHistory->selectorText(); 498 if (cs->relation() == CSSSelector::DirectAdjacent) 499 str = tagHistoryText + " + " + str; 500 else if (cs->relation() == CSSSelector::IndirectAdjacent) 501 str = tagHistoryText + " ~ " + str; 502 else if (cs->relation() == CSSSelector::Child) 503 str = tagHistoryText + " > " + str; 504 else 505 // Descendant 506 str = tagHistoryText + " " + str; 507 } 508 509 return str; 510 } 511 512 void CSSSelector::setTagHistory(CSSSelector* tagHistory) 513 { 514 if (m_hasRareData) 515 m_data.m_rareData->m_tagHistory.set(tagHistory); 516 else 517 m_data.m_tagHistory = tagHistory; 518 } 519 520 const QualifiedName& CSSSelector::attribute() const 521 { 522 switch (m_match) { 523 case Id: 524 return idAttr; 525 case Class: 526 return classAttr; 527 default: 528 return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName(); 529 } 530 } 531 532 void CSSSelector::setAttribute(const QualifiedName& value) 533 { 534 createRareData(); 535 m_data.m_rareData->m_attribute = value; 536 } 537 538 void CSSSelector::setArgument(const AtomicString& value) 539 { 540 createRareData(); 541 m_data.m_rareData->m_argument = value; 542 } 543 544 void CSSSelector::setSimpleSelector(CSSSelector* value) 545 { 546 createRareData(); 547 m_data.m_rareData->m_simpleSelector.set(value); 548 } 549 550 bool CSSSelector::parseNth() 551 { 552 if (!m_hasRareData) 553 return false; 554 if (m_parsedNth) 555 return true; 556 m_parsedNth = m_data.m_rareData->parseNth(); 557 return m_parsedNth; 558 } 559 560 bool CSSSelector::matchNth(int count) 561 { 562 ASSERT(m_hasRareData); 563 return m_data.m_rareData->matchNth(count); 564 } 565 566 // a helper function for parsing nth-arguments 567 bool CSSSelector::RareData::parseNth() 568 { 569 const String& argument = m_argument; 570 571 if (argument.isEmpty()) 572 return false; 573 574 m_a = 0; 575 m_b = 0; 576 if (argument == "odd") { 577 m_a = 2; 578 m_b = 1; 579 } else if (argument == "even") { 580 m_a = 2; 581 m_b = 0; 582 } else { 583 int n = argument.find('n'); 584 if (n != -1) { 585 if (argument[0] == '-') { 586 if (n == 1) 587 m_a = -1; // -n == -1n 588 else 589 m_a = argument.substring(0, n).toInt(); 590 } else if (!n) 591 m_a = 1; // n == 1n 592 else 593 m_a = argument.substring(0, n).toInt(); 594 595 int p = argument.find('+', n); 596 if (p != -1) 597 m_b = argument.substring(p + 1, argument.length() - p - 1).toInt(); 598 else { 599 p = argument.find('-', n); 600 m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt(); 601 } 602 } else 603 m_b = argument.toInt(); 604 } 605 return true; 606 } 607 608 // a helper function for checking nth-arguments 609 bool CSSSelector::RareData::matchNth(int count) 610 { 611 if (!m_a) 612 return count == m_b; 613 else if (m_a > 0) { 614 if (count < m_b) 615 return false; 616 return (count - m_b) % m_a == 0; 617 } else { 618 if (count > m_b) 619 return false; 620 return (m_b - count) % (-m_a) == 0; 621 } 622 } 623 624 } // namespace WebCore 625