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, disabled, ("disabled"));
81 DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only"));
82 DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write"));
83 DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag"));
84 DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag")); // was documented with this name in Apple documentation, so keep an alia
85 DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty"));
86 DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled"));
87 DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child"));
88 DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter"));
89 DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line"));
90 DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type"));
91 DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media"));
92 DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child("));
93 DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type("));
94 DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child("));
95 DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type("));
96 DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus"));
97 DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover"));
98 DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate"));
99 DEFINE_STATIC_LOCAL(AtomicString, inputPlaceholder, ("-webkit-input-placeholder"));
100 DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child"));
101 DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type"));
102 DEFINE_STATIC_LOCAL(AtomicString, link, ("link"));
103 DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang("));
104 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsPanel, ("-webkit-media-controls-panel"));
105 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsMuteButton, ("-webkit-media-controls-mute-button"));
106 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsPlayButton, ("-webkit-media-controls-play-button"));
107 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimeline, ("-webkit-media-controls-timeline"));
108 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsSeekBackButton, ("-webkit-media-controls-seek-back-button"));
109 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsSeekForwardButton, ("-webkit-media-controls-seek-forward-button"));
110 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsRewindButton, ("-webkit-media-controls-rewind-button"));
111 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsReturnToRealtimeButton, ("-webkit-media-controls-return-to-realtime-button"));
112 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsStatusDisplay, ("-webkit-media-controls-status-display"));
113 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsFullscreenButton, ("-webkit-media-controls-fullscreen-button"));
114 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimelineContainer, ("-webkit-media-controls-timeline-container"));
115 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsCurrentTimeDisplay, ("-webkit-media-controls-current-time-display"));
116 DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimeRemainingDisplay, ("-webkit-media-controls-time-remaining-display"));
117 DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not("));
118 DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child"));
119 DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type"));
120 DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional"));
121 DEFINE_STATIC_LOCAL(AtomicString, required, ("required"));
122 DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer"));
123 DEFINE_STATIC_LOCAL(AtomicString, root, ("root"));
124 DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar"));
125 DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button"));
126 DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner"));
127 DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb"));
128 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track"));
129 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece"));
130 DEFINE_STATIC_LOCAL(AtomicString, searchCancelButton, ("-webkit-search-cancel-button"));
131 DEFINE_STATIC_LOCAL(AtomicString, searchDecoration, ("-webkit-search-decoration"));
132 DEFINE_STATIC_LOCAL(AtomicString, searchResultsDecoration, ("-webkit-search-results-decoration"));
133 DEFINE_STATIC_LOCAL(AtomicString, searchResultsButton, ("-webkit-search-results-button"));
134 DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection"));
135 DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-thumb"));
136 DEFINE_STATIC_LOCAL(AtomicString, target, ("target"));
137 DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited"));
138 DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive"));
139 DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement"));
140 DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment"));
141 DEFINE_STATIC_LOCAL(AtomicString, start, ("start"));
142 DEFINE_STATIC_LOCAL(AtomicString, end, ("end"));
143 DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal"));
144 DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical"));
145 DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button"));
146 DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button"));
147 DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button"));
148 DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present"));
149
150 bool element = false; // pseudo-element
151 bool compat = false; // single colon compatbility mode
152
153 m_pseudoType = PseudoUnknown;
154 if (m_value == active)
155 m_pseudoType = PseudoActive;
156 else if (m_value == after) {
157 m_pseudoType = PseudoAfter;
158 element = true;
159 compat = true;
160 } else if (m_value == anyLink)
161 m_pseudoType = PseudoAnyLink;
162 else if (m_value == autofill)
163 m_pseudoType = PseudoAutofill;
164 else if (m_value == before) {
165 m_pseudoType = PseudoBefore;
166 element = true;
167 compat = true;
168 } else if (m_value == checked)
169 m_pseudoType = PseudoChecked;
170 else if (m_value == fileUploadButton) {
171 m_pseudoType = PseudoFileUploadButton;
172 element = true;
173 } else if (m_value == disabled)
174 m_pseudoType = PseudoDisabled;
175 else if (m_value == readOnly)
176 m_pseudoType = PseudoReadOnly;
177 else if (m_value == readWrite)
178 m_pseudoType = PseudoReadWrite;
179 else if (m_value == drag || m_value == dragAlias)
180 m_pseudoType = PseudoDrag;
181 else if (m_value == enabled)
182 m_pseudoType = PseudoEnabled;
183 else if (m_value == empty)
184 m_pseudoType = PseudoEmpty;
185 else if (m_value == firstChild)
186 m_pseudoType = PseudoFirstChild;
187 else if (m_value == fullPageMedia)
188 m_pseudoType = PseudoFullPageMedia;
189 else if (m_value == inputPlaceholder) {
190 m_pseudoType = PseudoInputPlaceholder;
191 element = true;
192 } else if (m_value == lastChild)
193 m_pseudoType = PseudoLastChild;
194 else if (m_value == lastOfType)
195 m_pseudoType = PseudoLastOfType;
196 else if (m_value == onlyChild)
197 m_pseudoType = PseudoOnlyChild;
198 else if (m_value == onlyOfType)
199 m_pseudoType = PseudoOnlyOfType;
200 else if (m_value == firstLetter) {
201 m_pseudoType = PseudoFirstLetter;
202 element = true;
203 compat = true;
204 } else if (m_value == firstLine) {
205 m_pseudoType = PseudoFirstLine;
206 element = true;
207 compat = true;
208 } else if (m_value == firstOfType)
209 m_pseudoType = PseudoFirstOfType;
210 else if (m_value == focus)
211 m_pseudoType = PseudoFocus;
212 else if (m_value == hover)
213 m_pseudoType = PseudoHover;
214 else if (m_value == indeterminate)
215 m_pseudoType = PseudoIndeterminate;
216 else if (m_value == link)
217 m_pseudoType = PseudoLink;
218 else if (m_value == lang)
219 m_pseudoType = PseudoLang;
220 else if (m_value == mediaControlsPanel) {
221 m_pseudoType = PseudoMediaControlsPanel;
222 element = true;
223 } else if (m_value == mediaControlsMuteButton) {
224 m_pseudoType = PseudoMediaControlsMuteButton;
225 element = true;
226 } else if (m_value == mediaControlsPlayButton) {
227 m_pseudoType = PseudoMediaControlsPlayButton;
228 element = true;
229 } else if (m_value == mediaControlsCurrentTimeDisplay) {
230 m_pseudoType = PseudoMediaControlsCurrentTimeDisplay;
231 element = true;
232 } else if (m_value == mediaControlsTimeRemainingDisplay) {
233 m_pseudoType = PseudoMediaControlsTimeRemainingDisplay;
234 element = true;
235 } else if (m_value == mediaControlsTimeline) {
236 m_pseudoType = PseudoMediaControlsTimeline;
237 element = true;
238 } else if (m_value == mediaControlsSeekBackButton) {
239 m_pseudoType = PseudoMediaControlsSeekBackButton;
240 element = true;
241 } else if (m_value == mediaControlsSeekForwardButton) {
242 m_pseudoType = PseudoMediaControlsSeekForwardButton;
243 element = true;
244 } else if (m_value == mediaControlsRewindButton) {
245 m_pseudoType = PseudoMediaControlsRewindButton;
246 element = true;
247 } else if (m_value == mediaControlsReturnToRealtimeButton) {
248 m_pseudoType = PseudoMediaControlsReturnToRealtimeButton;
249 element = true;
250 } else if (m_value == mediaControlsStatusDisplay) {
251 m_pseudoType = PseudoMediaControlsStatusDisplay;
252 element = true;
253 } else if (m_value == mediaControlsFullscreenButton) {
254 m_pseudoType = PseudoMediaControlsFullscreenButton;
255 element = true;
256 } else if (m_value == mediaControlsTimelineContainer) {
257 m_pseudoType = PseudoMediaControlsTimelineContainer;
258 element = true;
259 } else if (m_value == notStr)
260 m_pseudoType = PseudoNot;
261 else if (m_value == nthChild)
262 m_pseudoType = PseudoNthChild;
263 else if (m_value == nthOfType)
264 m_pseudoType = PseudoNthOfType;
265 else if (m_value == nthLastChild)
266 m_pseudoType = PseudoNthLastChild;
267 else if (m_value == nthLastOfType)
268 m_pseudoType = PseudoNthLastOfType;
269 else if (m_value == root)
270 m_pseudoType = PseudoRoot;
271 else if (m_value == windowInactive)
272 m_pseudoType = PseudoWindowInactive;
273 else if (m_value == decrement)
274 m_pseudoType = PseudoDecrement;
275 else if (m_value == increment)
276 m_pseudoType = PseudoIncrement;
277 else if (m_value == start)
278 m_pseudoType = PseudoStart;
279 else if (m_value == end)
280 m_pseudoType = PseudoEnd;
281 else if (m_value == horizontal)
282 m_pseudoType = PseudoHorizontal;
283 else if (m_value == vertical)
284 m_pseudoType = PseudoVertical;
285 else if (m_value == doubleButton)
286 m_pseudoType = PseudoDoubleButton;
287 else if (m_value == singleButton)
288 m_pseudoType = PseudoSingleButton;
289 else if (m_value == noButton)
290 m_pseudoType = PseudoNoButton;
291 else if (m_value == optional)
292 m_pseudoType = PseudoOptional;
293 else if (m_value == required)
294 m_pseudoType = PseudoRequired;
295 else if (m_value == scrollbarCorner) {
296 element = true;
297 m_pseudoType = PseudoScrollbarCorner;
298 } else if (m_value == resizer) {
299 element = true;
300 m_pseudoType = PseudoResizer;
301 } else if (m_value == scrollbar) {
302 element = true;
303 m_pseudoType = PseudoScrollbar;
304 } else if (m_value == scrollbarButton) {
305 element = true;
306 m_pseudoType = PseudoScrollbarButton;
307 } else if (m_value == scrollbarCorner) {
308 element = true;
309 m_pseudoType = PseudoScrollbarCorner;
310 } else if (m_value == scrollbarThumb) {
311 element = true;
312 m_pseudoType = PseudoScrollbarThumb;
313 } else if (m_value == scrollbarTrack) {
314 element = true;
315 m_pseudoType = PseudoScrollbarTrack;
316 } else if (m_value == scrollbarTrackPiece) {
317 element = true;
318 m_pseudoType = PseudoScrollbarTrackPiece;
319 } else if (m_value == cornerPresent)
320 m_pseudoType = PseudoCornerPresent;
321 else if (m_value == searchCancelButton) {
322 m_pseudoType = PseudoSearchCancelButton;
323 element = true;
324 } else if (m_value == searchDecoration) {
325 m_pseudoType = PseudoSearchDecoration;
326 element = true;
327 } else if (m_value == searchResultsDecoration) {
328 m_pseudoType = PseudoSearchResultsDecoration;
329 element = true;
330 } else if (m_value == searchResultsButton) {
331 m_pseudoType = PseudoSearchResultsButton;
332 element = true;
333 } else if (m_value == selection) {
334 m_pseudoType = PseudoSelection;
335 element = true;
336 } else if (m_value == sliderThumb) {
337 m_pseudoType = PseudoSliderThumb;
338 element = true;
339 } else if (m_value == target)
340 m_pseudoType = PseudoTarget;
341 else if (m_value == visited)
342 m_pseudoType = PseudoVisited;
343
344 if (m_match == PseudoClass && element) {
345 if (!compat)
346 m_pseudoType = PseudoUnknown;
347 else
348 m_match = PseudoElement;
349 } else if (m_match == PseudoElement && !element)
350 m_pseudoType = PseudoUnknown;
351 }
352
operator ==(const CSSSelector & other)353 bool CSSSelector::operator==(const CSSSelector& other)
354 {
355 const CSSSelector* sel1 = this;
356 const CSSSelector* sel2 = &other;
357
358 while (sel1 && sel2) {
359 if (sel1->m_tag != sel2->m_tag || sel1->attribute() != sel2->attribute() ||
360 sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match ||
361 sel1->m_value != sel2->m_value ||
362 sel1->pseudoType() != sel2->pseudoType() ||
363 sel1->argument() != sel2->argument())
364 return false;
365 sel1 = sel1->tagHistory();
366 sel2 = sel2->tagHistory();
367 }
368
369 if (sel1 || sel2)
370 return false;
371
372 return true;
373 }
374
selectorText() const375 String CSSSelector::selectorText() const
376 {
377 String str = "";
378
379 const AtomicString& prefix = m_tag.prefix();
380 const AtomicString& localName = m_tag.localName();
381 if (m_match == CSSSelector::None || !prefix.isNull() || localName != starAtom) {
382 if (prefix.isNull())
383 str = localName;
384 else
385 str = prefix + "|" + localName;
386 }
387
388 const CSSSelector* cs = this;
389 while (true) {
390 if (cs->m_match == CSSSelector::Id) {
391 str += "#";
392 str += cs->m_value;
393 } else if (cs->m_match == CSSSelector::Class) {
394 str += ".";
395 str += cs->m_value;
396 } else if (cs->m_match == CSSSelector::PseudoClass) {
397 str += ":";
398 str += cs->m_value;
399 if (cs->pseudoType() == PseudoNot) {
400 if (CSSSelector* subSel = cs->simpleSelector())
401 str += subSel->selectorText();
402 str += ")";
403 } else if (cs->pseudoType() == PseudoLang
404 || cs->pseudoType() == PseudoNthChild
405 || cs->pseudoType() == PseudoNthLastChild
406 || cs->pseudoType() == PseudoNthOfType
407 || cs->pseudoType() == PseudoNthLastOfType) {
408 str += cs->argument();
409 str += ")";
410 }
411 } else if (cs->m_match == CSSSelector::PseudoElement) {
412 str += "::";
413 str += cs->m_value;
414 } else if (cs->hasAttribute()) {
415 str += "[";
416 const AtomicString& prefix = cs->attribute().prefix();
417 if (!prefix.isNull())
418 str += prefix + "|";
419 str += cs->attribute().localName();
420 switch (cs->m_match) {
421 case CSSSelector::Exact:
422 str += "=";
423 break;
424 case CSSSelector::Set:
425 // set has no operator or value, just the attrName
426 str += "]";
427 break;
428 case CSSSelector::List:
429 str += "~=";
430 break;
431 case CSSSelector::Hyphen:
432 str += "|=";
433 break;
434 case CSSSelector::Begin:
435 str += "^=";
436 break;
437 case CSSSelector::End:
438 str += "$=";
439 break;
440 case CSSSelector::Contain:
441 str += "*=";
442 break;
443 default:
444 break;
445 }
446 if (cs->m_match != CSSSelector::Set) {
447 str += "\"";
448 str += cs->m_value;
449 str += "\"]";
450 }
451 }
452 if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory())
453 break;
454 cs = cs->tagHistory();
455 }
456
457 if (CSSSelector* tagHistory = cs->tagHistory()) {
458 String tagHistoryText = tagHistory->selectorText();
459 if (cs->relation() == CSSSelector::DirectAdjacent)
460 str = tagHistoryText + " + " + str;
461 else if (cs->relation() == CSSSelector::IndirectAdjacent)
462 str = tagHistoryText + " ~ " + str;
463 else if (cs->relation() == CSSSelector::Child)
464 str = tagHistoryText + " > " + str;
465 else
466 // Descendant
467 str = tagHistoryText + " " + str;
468 }
469
470 return str;
471 }
472
setTagHistory(CSSSelector * tagHistory)473 void CSSSelector::setTagHistory(CSSSelector* tagHistory)
474 {
475 if (m_hasRareData)
476 m_data.m_rareData->m_tagHistory.set(tagHistory);
477 else
478 m_data.m_tagHistory = tagHistory;
479 }
480
attribute() const481 const QualifiedName& CSSSelector::attribute() const
482 {
483 switch (m_match) {
484 case Id:
485 return idAttr;
486 case Class:
487 return classAttr;
488 default:
489 return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName();
490 }
491 }
492
setAttribute(const QualifiedName & value)493 void CSSSelector::setAttribute(const QualifiedName& value)
494 {
495 createRareData();
496 m_data.m_rareData->m_attribute = value;
497 }
498
setArgument(const AtomicString & value)499 void CSSSelector::setArgument(const AtomicString& value)
500 {
501 createRareData();
502 m_data.m_rareData->m_argument = value;
503 }
504
setSimpleSelector(CSSSelector * value)505 void CSSSelector::setSimpleSelector(CSSSelector* value)
506 {
507 createRareData();
508 m_data.m_rareData->m_simpleSelector.set(value);
509 }
510
parseNth()511 bool CSSSelector::parseNth()
512 {
513 if (!m_hasRareData)
514 return false;
515 if (m_parsedNth)
516 return true;
517 m_parsedNth = m_data.m_rareData->parseNth();
518 return m_parsedNth;
519 }
520
matchNth(int count)521 bool CSSSelector::matchNth(int count)
522 {
523 ASSERT(m_hasRareData);
524 return m_data.m_rareData->matchNth(count);
525 }
526
527 // a helper function for parsing nth-arguments
parseNth()528 bool CSSSelector::RareData::parseNth()
529 {
530 const String& argument = m_argument;
531
532 if (argument.isEmpty())
533 return false;
534
535 m_a = 0;
536 m_b = 0;
537 if (argument == "odd") {
538 m_a = 2;
539 m_b = 1;
540 } else if (argument == "even") {
541 m_a = 2;
542 m_b = 0;
543 } else {
544 int n = argument.find('n');
545 if (n != -1) {
546 if (argument[0] == '-') {
547 if (n == 1)
548 m_a = -1; // -n == -1n
549 else
550 m_a = argument.substring(0, n).toInt();
551 } else if (!n)
552 m_a = 1; // n == 1n
553 else
554 m_a = argument.substring(0, n).toInt();
555
556 int p = argument.find('+', n);
557 if (p != -1)
558 m_b = argument.substring(p + 1, argument.length() - p - 1).toInt();
559 else {
560 p = argument.find('-', n);
561 m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt();
562 }
563 } else
564 m_b = argument.toInt();
565 }
566 return true;
567 }
568
569 // a helper function for checking nth-arguments
matchNth(int count)570 bool CSSSelector::RareData::matchNth(int count)
571 {
572 if (!m_a)
573 return count == m_b;
574 else if (m_a > 0) {
575 if (count < m_b)
576 return false;
577 return (count - m_b) % m_a == 0;
578 } else {
579 if (count > m_b)
580 return false;
581 return (m_b - count) % (-m_a) == 0;
582 }
583 }
584
585 } // namespace WebCore
586