• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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