• 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, 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