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 Apple Inc. All rights reserved.
7 * Copyright (C) 2008 David Smith (catfish.man@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
specificity()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
extractPseudoType() const68 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
operator ==(const CSSSelector & other)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
selectorText() const414 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
setTagHistory(CSSSelector * tagHistory)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
attribute() const520 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
setAttribute(const QualifiedName & value)532 void CSSSelector::setAttribute(const QualifiedName& value)
533 {
534 createRareData();
535 m_data.m_rareData->m_attribute = value;
536 }
537
setArgument(const AtomicString & value)538 void CSSSelector::setArgument(const AtomicString& value)
539 {
540 createRareData();
541 m_data.m_rareData->m_argument = value;
542 }
543
setSimpleSelector(CSSSelector * value)544 void CSSSelector::setSimpleSelector(CSSSelector* value)
545 {
546 createRareData();
547 m_data.m_rareData->m_simpleSelector.set(value);
548 }
549
parseNth()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
matchNth(int count)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
parseNth()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
matchNth(int count)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