• 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, 2009, 2010 Apple Inc. All rights reserved.
7  * Copyright (C) 2008 David Smith (catfish.man@gmail.com)
8  * Copyright (C) 2010 Google Inc. All rights reserved.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25 
26 #include "config.h"
27 #include "CSSSelector.h"
28 
29 #include "CSSOMUtils.h"
30 #include "CSSSelectorList.h"
31 #include "HTMLNames.h"
32 #include <wtf/Assertions.h>
33 #include <wtf/HashMap.h>
34 #include <wtf/StdLibExtras.h>
35 #include <wtf/Vector.h>
36 
37 namespace WebCore {
38 
39 using namespace HTMLNames;
40 
createRareData()41 void CSSSelector::createRareData()
42 {
43     if (m_hasRareData)
44         return;
45     // Move the value to the rare data stucture.
46     m_data.m_rareData = new RareData(adoptRef(m_data.m_value));
47     m_hasRareData = true;
48 }
49 
specificity() const50 unsigned CSSSelector::specificity() const
51 {
52     // make sure the result doesn't overflow
53     static const unsigned maxValueMask = 0xffffff;
54     unsigned total = 0;
55     for (const CSSSelector* selector = this; selector; selector = selector->tagHistory()) {
56         if (selector->m_isForPage)
57             return (total + selector->specificityForPage()) & maxValueMask;
58         total = (total + selector->specificityForOneSelector()) & maxValueMask;
59     }
60     return total;
61 }
62 
specificityForOneSelector() const63 inline unsigned CSSSelector::specificityForOneSelector() const
64 {
65     // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function
66     // isn't quite correct.
67     unsigned s = (m_tag.localName() == starAtom ? 0 : 1);
68     switch (m_match) {
69     case Id:
70         s += 0x10000;
71         break;
72     case Exact:
73     case Class:
74     case Set:
75     case List:
76     case Hyphen:
77     case PseudoClass:
78     case PseudoElement:
79     case Contain:
80     case Begin:
81     case End:
82         // FIXME: PsuedoAny should base the specificity on the sub-selectors.
83         // See http://lists.w3.org/Archives/Public/www-style/2010Sep/0530.html
84         if (pseudoType() == PseudoNot) {
85             ASSERT(selectorList());
86             s += selectorList()->first()->specificityForOneSelector();
87         } else
88             s += 0x100;
89     case None:
90         break;
91     }
92     return s;
93 }
94 
specificityForPage() const95 unsigned CSSSelector::specificityForPage() const
96 {
97     // See http://dev.w3.org/csswg/css3-page/#cascading-and-page-context
98     unsigned s = (m_tag.localName() == starAtom ? 0 : 4);
99 
100     switch (pseudoType()) {
101     case PseudoFirstPage:
102         s += 2;
103         break;
104     case PseudoLeftPage:
105     case PseudoRightPage:
106         s += 1;
107         break;
108     case PseudoNotParsed:
109         break;
110     default:
111         ASSERT_NOT_REACHED();
112     }
113     return s;
114 }
115 
pseudoId(PseudoType type)116 PseudoId CSSSelector::pseudoId(PseudoType type)
117 {
118     switch (type) {
119     case PseudoFirstLine:
120         return FIRST_LINE;
121     case PseudoFirstLetter:
122         return FIRST_LETTER;
123     case PseudoSelection:
124         return SELECTION;
125     case PseudoBefore:
126         return BEFORE;
127     case PseudoAfter:
128         return AFTER;
129     case PseudoFileUploadButton:
130         return FILE_UPLOAD_BUTTON;
131     case PseudoInputPlaceholder:
132         return INPUT_PLACEHOLDER;
133 #if ENABLE(INPUT_SPEECH)
134     case PseudoInputSpeechButton:
135         return INPUT_SPEECH_BUTTON;
136 #endif
137     case PseudoSearchCancelButton:
138         return SEARCH_CANCEL_BUTTON;
139     case PseudoSearchDecoration:
140         return SEARCH_DECORATION;
141     case PseudoSearchResultsDecoration:
142         return SEARCH_RESULTS_DECORATION;
143     case PseudoSearchResultsButton:
144         return SEARCH_RESULTS_BUTTON;
145     case PseudoScrollbar:
146         return SCROLLBAR;
147     case PseudoScrollbarButton:
148         return SCROLLBAR_BUTTON;
149     case PseudoScrollbarCorner:
150         return SCROLLBAR_CORNER;
151     case PseudoScrollbarThumb:
152         return SCROLLBAR_THUMB;
153     case PseudoScrollbarTrack:
154         return SCROLLBAR_TRACK;
155     case PseudoScrollbarTrackPiece:
156         return SCROLLBAR_TRACK_PIECE;
157     case PseudoResizer:
158         return RESIZER;
159     case PseudoInnerSpinButton:
160         return INNER_SPIN_BUTTON;
161     case PseudoOuterSpinButton:
162         return OUTER_SPIN_BUTTON;
163 #if ENABLE(FULLSCREEN_API)
164     case PseudoFullScreen:
165         return FULL_SCREEN;
166     case PseudoFullScreenDocument:
167         return FULL_SCREEN_DOCUMENT;
168 #endif
169 
170     case PseudoInputListButton:
171 #if ENABLE(DATALIST)
172         return INPUT_LIST_BUTTON;
173 #endif
174     case PseudoUnknown:
175     case PseudoEmpty:
176     case PseudoFirstChild:
177     case PseudoFirstOfType:
178     case PseudoLastChild:
179     case PseudoLastOfType:
180     case PseudoOnlyChild:
181     case PseudoOnlyOfType:
182     case PseudoNthChild:
183     case PseudoNthOfType:
184     case PseudoNthLastChild:
185     case PseudoNthLastOfType:
186     case PseudoLink:
187     case PseudoVisited:
188     case PseudoAny:
189     case PseudoAnyLink:
190     case PseudoAutofill:
191     case PseudoHover:
192     case PseudoDrag:
193     case PseudoFocus:
194     case PseudoActive:
195     case PseudoChecked:
196     case PseudoEnabled:
197     case PseudoFullPageMedia:
198     case PseudoDefault:
199     case PseudoDisabled:
200     case PseudoOptional:
201     case PseudoRequired:
202     case PseudoReadOnly:
203     case PseudoReadWrite:
204     case PseudoValid:
205     case PseudoInvalid:
206     case PseudoIndeterminate:
207     case PseudoTarget:
208     case PseudoLang:
209     case PseudoNot:
210     case PseudoRoot:
211     case PseudoScrollbarBack:
212     case PseudoScrollbarForward:
213     case PseudoWindowInactive:
214     case PseudoCornerPresent:
215     case PseudoDecrement:
216     case PseudoIncrement:
217     case PseudoHorizontal:
218     case PseudoVertical:
219     case PseudoStart:
220     case PseudoEnd:
221     case PseudoDoubleButton:
222     case PseudoSingleButton:
223     case PseudoNoButton:
224     case PseudoFirstPage:
225     case PseudoLeftPage:
226     case PseudoRightPage:
227     case PseudoInRange:
228     case PseudoOutOfRange:
229         return NOPSEUDO;
230     case PseudoNotParsed:
231         ASSERT_NOT_REACHED();
232         return NOPSEUDO;
233     }
234 
235     ASSERT_NOT_REACHED();
236     return NOPSEUDO;
237 }
238 
nameToPseudoTypeMap()239 static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoTypeMap()
240 {
241     DEFINE_STATIC_LOCAL(AtomicString, active, ("active"));
242     DEFINE_STATIC_LOCAL(AtomicString, after, ("after"));
243     DEFINE_STATIC_LOCAL(AtomicString, any, ("-webkit-any("));
244     DEFINE_STATIC_LOCAL(AtomicString, anyLink, ("-webkit-any-link"));
245     DEFINE_STATIC_LOCAL(AtomicString, autofill, ("-webkit-autofill"));
246     DEFINE_STATIC_LOCAL(AtomicString, before, ("before"));
247     DEFINE_STATIC_LOCAL(AtomicString, checked, ("checked"));
248     DEFINE_STATIC_LOCAL(AtomicString, fileUploadButton, ("-webkit-file-upload-button"));
249 #if ENABLE(INPUT_SPEECH)
250     DEFINE_STATIC_LOCAL(AtomicString, inputSpeechButton, ("-webkit-input-speech-button"));
251 #endif
252     DEFINE_STATIC_LOCAL(AtomicString, defaultString, ("default"));
253     DEFINE_STATIC_LOCAL(AtomicString, disabled, ("disabled"));
254     DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only"));
255     DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write"));
256     DEFINE_STATIC_LOCAL(AtomicString, valid, ("valid"));
257     DEFINE_STATIC_LOCAL(AtomicString, invalid, ("invalid"));
258     DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag"));
259     DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag")); // was documented with this name in Apple documentation, so keep an alia
260     DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty"));
261     DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled"));
262     DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child"));
263     DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter"));
264     DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line"));
265     DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type"));
266     DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media"));
267     DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child("));
268     DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type("));
269     DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child("));
270     DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type("));
271     DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus"));
272     DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover"));
273     DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate"));
274     DEFINE_STATIC_LOCAL(AtomicString, innerSpinButton, ("-webkit-inner-spin-button"));
275 #if ENABLE(DATALIST)
276     DEFINE_STATIC_LOCAL(AtomicString, inputListButton, ("-webkit-input-list-button"));
277 #endif
278     DEFINE_STATIC_LOCAL(AtomicString, inputPlaceholder, ("-webkit-input-placeholder"));
279     DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child"));
280     DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type"));
281     DEFINE_STATIC_LOCAL(AtomicString, link, ("link"));
282     DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang("));
283     DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not("));
284     DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child"));
285     DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type"));
286     DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional"));
287     DEFINE_STATIC_LOCAL(AtomicString, outerSpinButton, ("-webkit-outer-spin-button"));
288     DEFINE_STATIC_LOCAL(AtomicString, required, ("required"));
289     DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer"));
290     DEFINE_STATIC_LOCAL(AtomicString, root, ("root"));
291     DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar"));
292     DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button"));
293     DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner"));
294     DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb"));
295     DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track"));
296     DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece"));
297     DEFINE_STATIC_LOCAL(AtomicString, searchCancelButton, ("-webkit-search-cancel-button"));
298     DEFINE_STATIC_LOCAL(AtomicString, searchDecoration, ("-webkit-search-decoration"));
299     DEFINE_STATIC_LOCAL(AtomicString, searchResultsDecoration, ("-webkit-search-results-decoration"));
300     DEFINE_STATIC_LOCAL(AtomicString, searchResultsButton, ("-webkit-search-results-button"));
301     DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection"));
302     DEFINE_STATIC_LOCAL(AtomicString, target, ("target"));
303     DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited"));
304     DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive"));
305     DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement"));
306     DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment"));
307     DEFINE_STATIC_LOCAL(AtomicString, start, ("start"));
308     DEFINE_STATIC_LOCAL(AtomicString, end, ("end"));
309     DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal"));
310     DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical"));
311     DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button"));
312     DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button"));
313     DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button"));
314     DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present"));
315     // Paged Media pseudo-classes
316     DEFINE_STATIC_LOCAL(AtomicString, firstPage, ("first"));
317     DEFINE_STATIC_LOCAL(AtomicString, leftPage, ("left"));
318     DEFINE_STATIC_LOCAL(AtomicString, rightPage, ("right"));
319 #if ENABLE(FULLSCREEN_API)
320     DEFINE_STATIC_LOCAL(AtomicString, fullScreen, ("-webkit-full-screen"));
321     DEFINE_STATIC_LOCAL(AtomicString, fullScreenDocument, ("-webkit-full-screen-document"));
322 #endif
323     DEFINE_STATIC_LOCAL(AtomicString, inRange, ("in-range"));
324     DEFINE_STATIC_LOCAL(AtomicString, outOfRange, ("out-of-range"));
325 
326     static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = 0;
327     if (!nameToPseudoType) {
328         nameToPseudoType = new HashMap<AtomicStringImpl*, CSSSelector::PseudoType>;
329         nameToPseudoType->set(active.impl(), CSSSelector::PseudoActive);
330         nameToPseudoType->set(after.impl(), CSSSelector::PseudoAfter);
331         nameToPseudoType->set(anyLink.impl(), CSSSelector::PseudoAnyLink);
332         nameToPseudoType->set(any.impl(), CSSSelector::PseudoAny);
333         nameToPseudoType->set(autofill.impl(), CSSSelector::PseudoAutofill);
334         nameToPseudoType->set(before.impl(), CSSSelector::PseudoBefore);
335         nameToPseudoType->set(checked.impl(), CSSSelector::PseudoChecked);
336         nameToPseudoType->set(fileUploadButton.impl(), CSSSelector::PseudoFileUploadButton);
337 #if ENABLE(INPUT_SPEECH)
338         nameToPseudoType->set(inputSpeechButton.impl(), CSSSelector::PseudoInputSpeechButton);
339 #endif
340         nameToPseudoType->set(defaultString.impl(), CSSSelector::PseudoDefault);
341         nameToPseudoType->set(disabled.impl(), CSSSelector::PseudoDisabled);
342         nameToPseudoType->set(readOnly.impl(), CSSSelector::PseudoReadOnly);
343         nameToPseudoType->set(readWrite.impl(), CSSSelector::PseudoReadWrite);
344         nameToPseudoType->set(valid.impl(), CSSSelector::PseudoValid);
345         nameToPseudoType->set(invalid.impl(), CSSSelector::PseudoInvalid);
346         nameToPseudoType->set(drag.impl(), CSSSelector::PseudoDrag);
347         nameToPseudoType->set(dragAlias.impl(), CSSSelector::PseudoDrag);
348         nameToPseudoType->set(enabled.impl(), CSSSelector::PseudoEnabled);
349         nameToPseudoType->set(empty.impl(), CSSSelector::PseudoEmpty);
350         nameToPseudoType->set(firstChild.impl(), CSSSelector::PseudoFirstChild);
351         nameToPseudoType->set(fullPageMedia.impl(), CSSSelector::PseudoFullPageMedia);
352 #if ENABLE(DATALIST)
353         nameToPseudoType->set(inputListButton.impl(), CSSSelector::PseudoInputListButton);
354 #endif
355         nameToPseudoType->set(inputPlaceholder.impl(), CSSSelector::PseudoInputPlaceholder);
356         nameToPseudoType->set(lastChild.impl(), CSSSelector::PseudoLastChild);
357         nameToPseudoType->set(lastOfType.impl(), CSSSelector::PseudoLastOfType);
358         nameToPseudoType->set(onlyChild.impl(), CSSSelector::PseudoOnlyChild);
359         nameToPseudoType->set(onlyOfType.impl(), CSSSelector::PseudoOnlyOfType);
360         nameToPseudoType->set(firstLetter.impl(), CSSSelector::PseudoFirstLetter);
361         nameToPseudoType->set(firstLine.impl(), CSSSelector::PseudoFirstLine);
362         nameToPseudoType->set(firstOfType.impl(), CSSSelector::PseudoFirstOfType);
363         nameToPseudoType->set(focus.impl(), CSSSelector::PseudoFocus);
364         nameToPseudoType->set(hover.impl(), CSSSelector::PseudoHover);
365         nameToPseudoType->set(indeterminate.impl(), CSSSelector::PseudoIndeterminate);
366         nameToPseudoType->set(innerSpinButton.impl(), CSSSelector::PseudoInnerSpinButton);
367         nameToPseudoType->set(link.impl(), CSSSelector::PseudoLink);
368         nameToPseudoType->set(lang.impl(), CSSSelector::PseudoLang);
369         nameToPseudoType->set(notStr.impl(), CSSSelector::PseudoNot);
370         nameToPseudoType->set(nthChild.impl(), CSSSelector::PseudoNthChild);
371         nameToPseudoType->set(nthOfType.impl(), CSSSelector::PseudoNthOfType);
372         nameToPseudoType->set(nthLastChild.impl(), CSSSelector::PseudoNthLastChild);
373         nameToPseudoType->set(nthLastOfType.impl(), CSSSelector::PseudoNthLastOfType);
374         nameToPseudoType->set(outerSpinButton.impl(), CSSSelector::PseudoOuterSpinButton);
375         nameToPseudoType->set(root.impl(), CSSSelector::PseudoRoot);
376         nameToPseudoType->set(windowInactive.impl(), CSSSelector::PseudoWindowInactive);
377         nameToPseudoType->set(decrement.impl(), CSSSelector::PseudoDecrement);
378         nameToPseudoType->set(increment.impl(), CSSSelector::PseudoIncrement);
379         nameToPseudoType->set(start.impl(), CSSSelector::PseudoStart);
380         nameToPseudoType->set(end.impl(), CSSSelector::PseudoEnd);
381         nameToPseudoType->set(horizontal.impl(), CSSSelector::PseudoHorizontal);
382         nameToPseudoType->set(vertical.impl(), CSSSelector::PseudoVertical);
383         nameToPseudoType->set(doubleButton.impl(), CSSSelector::PseudoDoubleButton);
384         nameToPseudoType->set(singleButton.impl(), CSSSelector::PseudoSingleButton);
385         nameToPseudoType->set(noButton.impl(), CSSSelector::PseudoNoButton);
386         nameToPseudoType->set(optional.impl(), CSSSelector::PseudoOptional);
387         nameToPseudoType->set(required.impl(), CSSSelector::PseudoRequired);
388         nameToPseudoType->set(resizer.impl(), CSSSelector::PseudoResizer);
389         nameToPseudoType->set(scrollbar.impl(), CSSSelector::PseudoScrollbar);
390         nameToPseudoType->set(scrollbarButton.impl(), CSSSelector::PseudoScrollbarButton);
391         nameToPseudoType->set(scrollbarCorner.impl(), CSSSelector::PseudoScrollbarCorner);
392         nameToPseudoType->set(scrollbarThumb.impl(), CSSSelector::PseudoScrollbarThumb);
393         nameToPseudoType->set(scrollbarTrack.impl(), CSSSelector::PseudoScrollbarTrack);
394         nameToPseudoType->set(scrollbarTrackPiece.impl(), CSSSelector::PseudoScrollbarTrackPiece);
395         nameToPseudoType->set(cornerPresent.impl(), CSSSelector::PseudoCornerPresent);
396         nameToPseudoType->set(searchCancelButton.impl(), CSSSelector::PseudoSearchCancelButton);
397         nameToPseudoType->set(searchDecoration.impl(), CSSSelector::PseudoSearchDecoration);
398         nameToPseudoType->set(searchResultsDecoration.impl(), CSSSelector::PseudoSearchResultsDecoration);
399         nameToPseudoType->set(searchResultsButton.impl(), CSSSelector::PseudoSearchResultsButton);
400         nameToPseudoType->set(selection.impl(), CSSSelector::PseudoSelection);
401         nameToPseudoType->set(target.impl(), CSSSelector::PseudoTarget);
402         nameToPseudoType->set(visited.impl(), CSSSelector::PseudoVisited);
403         nameToPseudoType->set(firstPage.impl(), CSSSelector::PseudoFirstPage);
404         nameToPseudoType->set(leftPage.impl(), CSSSelector::PseudoLeftPage);
405         nameToPseudoType->set(rightPage.impl(), CSSSelector::PseudoRightPage);
406 #if ENABLE(FULLSCREEN_API)
407         nameToPseudoType->set(fullScreen.impl(), CSSSelector::PseudoFullScreen);
408         nameToPseudoType->set(fullScreenDocument.impl(), CSSSelector::PseudoFullScreenDocument);
409 #endif
410         nameToPseudoType->set(inRange.impl(), CSSSelector::PseudoInRange);
411         nameToPseudoType->set(outOfRange.impl(), CSSSelector::PseudoOutOfRange);
412     }
413     return nameToPseudoType;
414 }
415 
parsePseudoType(const AtomicString & name)416 CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name)
417 {
418     if (name.isNull())
419         return PseudoUnknown;
420     HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = nameToPseudoTypeMap();
421     HashMap<AtomicStringImpl*, CSSSelector::PseudoType>::iterator slot = nameToPseudoType->find(name.impl());
422     return slot == nameToPseudoType->end() ? PseudoUnknown : slot->second;
423 }
424 
extractPseudoType() const425 void CSSSelector::extractPseudoType() const
426 {
427     if (m_match != PseudoClass && m_match != PseudoElement && m_match != PagePseudoClass)
428         return;
429 
430     m_pseudoType = parsePseudoType(value());
431 
432     bool element = false; // pseudo-element
433     bool compat = false; // single colon compatbility mode
434     bool isPagePseudoClass = false; // Page pseudo-class
435 
436     switch (m_pseudoType) {
437     case PseudoAfter:
438     case PseudoBefore:
439     case PseudoFirstLetter:
440     case PseudoFirstLine:
441         compat = true;
442     case PseudoFileUploadButton:
443     case PseudoInputListButton:
444     case PseudoInputPlaceholder:
445 #if ENABLE(INPUT_SPEECH)
446     case PseudoInputSpeechButton:
447 #endif
448     case PseudoInnerSpinButton:
449     case PseudoOuterSpinButton:
450     case PseudoResizer:
451     case PseudoScrollbar:
452     case PseudoScrollbarCorner:
453     case PseudoScrollbarButton:
454     case PseudoScrollbarThumb:
455     case PseudoScrollbarTrack:
456     case PseudoScrollbarTrackPiece:
457     case PseudoSearchCancelButton:
458     case PseudoSearchDecoration:
459     case PseudoSearchResultsDecoration:
460     case PseudoSearchResultsButton:
461     case PseudoSelection:
462         element = true;
463         break;
464     case PseudoUnknown:
465     case PseudoEmpty:
466     case PseudoFirstChild:
467     case PseudoFirstOfType:
468     case PseudoLastChild:
469     case PseudoLastOfType:
470     case PseudoOnlyChild:
471     case PseudoOnlyOfType:
472     case PseudoNthChild:
473     case PseudoNthOfType:
474     case PseudoNthLastChild:
475     case PseudoNthLastOfType:
476     case PseudoLink:
477     case PseudoVisited:
478     case PseudoAny:
479     case PseudoAnyLink:
480     case PseudoAutofill:
481     case PseudoHover:
482     case PseudoDrag:
483     case PseudoFocus:
484     case PseudoActive:
485     case PseudoChecked:
486     case PseudoEnabled:
487     case PseudoFullPageMedia:
488     case PseudoDefault:
489     case PseudoDisabled:
490     case PseudoOptional:
491     case PseudoRequired:
492     case PseudoReadOnly:
493     case PseudoReadWrite:
494     case PseudoValid:
495     case PseudoInvalid:
496     case PseudoIndeterminate:
497     case PseudoTarget:
498     case PseudoLang:
499     case PseudoNot:
500     case PseudoRoot:
501     case PseudoScrollbarBack:
502     case PseudoScrollbarForward:
503     case PseudoWindowInactive:
504     case PseudoCornerPresent:
505     case PseudoDecrement:
506     case PseudoIncrement:
507     case PseudoHorizontal:
508     case PseudoVertical:
509     case PseudoStart:
510     case PseudoEnd:
511     case PseudoDoubleButton:
512     case PseudoSingleButton:
513     case PseudoNoButton:
514     case PseudoNotParsed:
515 #if ENABLE(FULLSCREEN_API)
516     case PseudoFullScreen:
517     case PseudoFullScreenDocument:
518 #endif
519     case PseudoInRange:
520     case PseudoOutOfRange:
521         break;
522     case PseudoFirstPage:
523     case PseudoLeftPage:
524     case PseudoRightPage:
525         isPagePseudoClass = true;
526         break;
527     }
528 
529     bool matchPagePseudoClass = (m_match == PagePseudoClass);
530     if (matchPagePseudoClass != isPagePseudoClass)
531         m_pseudoType = PseudoUnknown;
532     else if (m_match == PseudoClass && element) {
533         if (!compat)
534             m_pseudoType = PseudoUnknown;
535         else
536            m_match = PseudoElement;
537     } else if (m_match == PseudoElement && !element)
538         m_pseudoType = PseudoUnknown;
539 }
540 
operator ==(const CSSSelector & other)541 bool CSSSelector::operator==(const CSSSelector& other)
542 {
543     const CSSSelector* sel1 = this;
544     const CSSSelector* sel2 = &other;
545 
546     while (sel1 && sel2) {
547         if (sel1->m_tag != sel2->m_tag || sel1->attribute() != sel2->attribute() ||
548              sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match ||
549              sel1->value() != sel2->value() ||
550              sel1->pseudoType() != sel2->pseudoType() ||
551              sel1->argument() != sel2->argument())
552             return false;
553         sel1 = sel1->tagHistory();
554         sel2 = sel2->tagHistory();
555     }
556 
557     if (sel1 || sel2)
558         return false;
559 
560     return true;
561 }
562 
selectorText() const563 String CSSSelector::selectorText() const
564 {
565     String str = "";
566 
567     const AtomicString& prefix = m_tag.prefix();
568     const AtomicString& localName = m_tag.localName();
569     if (m_match == CSSSelector::None || !prefix.isNull() || localName != starAtom) {
570         if (prefix.isNull())
571             str = localName;
572         else {
573             str = prefix.string();
574             str.append("|");
575             str.append(localName);
576         }
577     }
578 
579     const CSSSelector* cs = this;
580     while (true) {
581         if (cs->m_match == CSSSelector::Id) {
582             str += "#";
583             serializeIdentifier(cs->value(), str);
584         } else if (cs->m_match == CSSSelector::Class) {
585             str += ".";
586             serializeIdentifier(cs->value(), str);
587         } else if (cs->m_match == CSSSelector::PseudoClass || cs->m_match == CSSSelector::PagePseudoClass) {
588             str += ":";
589             str += cs->value();
590 
591             switch (cs->pseudoType()) {
592             case PseudoNot:
593                 ASSERT(cs->selectorList());
594                 str += cs->selectorList()->first()->selectorText();
595                 str += ")";
596                 break;
597             case PseudoLang:
598             case PseudoNthChild:
599             case PseudoNthLastChild:
600             case PseudoNthOfType:
601             case PseudoNthLastOfType:
602                 str += cs->argument();
603                 str += ")";
604                 break;
605             case PseudoAny: {
606                 CSSSelector* firstSubSelector = cs->selectorList()->first();
607                 for (CSSSelector* subSelector = firstSubSelector; subSelector; subSelector = CSSSelectorList::next(subSelector)) {
608                     if (subSelector != firstSubSelector)
609                         str += ",";
610                     str += subSelector->selectorText();
611                 }
612                 str += ")";
613                 break;
614             }
615             default:
616                 break;
617             }
618         } else if (cs->m_match == CSSSelector::PseudoElement) {
619             str += "::";
620             str += cs->value();
621         } else if (cs->hasAttribute()) {
622             str += "[";
623             const AtomicString& prefix = cs->attribute().prefix();
624             if (!prefix.isNull()) {
625                 str.append(prefix);
626                 str.append("|");
627             }
628             str += cs->attribute().localName();
629             switch (cs->m_match) {
630                 case CSSSelector::Exact:
631                     str += "=";
632                     break;
633                 case CSSSelector::Set:
634                     // set has no operator or value, just the attrName
635                     str += "]";
636                     break;
637                 case CSSSelector::List:
638                     str += "~=";
639                     break;
640                 case CSSSelector::Hyphen:
641                     str += "|=";
642                     break;
643                 case CSSSelector::Begin:
644                     str += "^=";
645                     break;
646                 case CSSSelector::End:
647                     str += "$=";
648                     break;
649                 case CSSSelector::Contain:
650                     str += "*=";
651                     break;
652                 default:
653                     break;
654             }
655             if (cs->m_match != CSSSelector::Set) {
656                 serializeString(cs->value(), str);
657                 str += "]";
658             }
659         }
660         if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory())
661             break;
662         cs = cs->tagHistory();
663     }
664 
665     if (CSSSelector* tagHistory = cs->tagHistory()) {
666         String tagHistoryText = tagHistory->selectorText();
667         if (cs->relation() == CSSSelector::DirectAdjacent)
668             str = tagHistoryText + " + " + str;
669         else if (cs->relation() == CSSSelector::IndirectAdjacent)
670             str = tagHistoryText + " ~ " + str;
671         else if (cs->relation() == CSSSelector::Child)
672             str = tagHistoryText + " > " + str;
673         else
674             // Descendant
675             str = tagHistoryText + " " + str;
676     }
677 
678     return str;
679 }
680 
attribute() const681 const QualifiedName& CSSSelector::attribute() const
682 {
683     switch (m_match) {
684     case Id:
685         return idAttr;
686     case Class:
687         return classAttr;
688     default:
689         return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName();
690     }
691 }
692 
setAttribute(const QualifiedName & value)693 void CSSSelector::setAttribute(const QualifiedName& value)
694 {
695     createRareData();
696     m_data.m_rareData->m_attribute = value;
697 }
698 
setArgument(const AtomicString & value)699 void CSSSelector::setArgument(const AtomicString& value)
700 {
701     createRareData();
702     m_data.m_rareData->m_argument = value;
703 }
704 
setSelectorList(PassOwnPtr<CSSSelectorList> selectorList)705 void CSSSelector::setSelectorList(PassOwnPtr<CSSSelectorList> selectorList)
706 {
707     createRareData();
708     m_data.m_rareData->m_selectorList = selectorList;
709 }
710 
parseNth()711 bool CSSSelector::parseNth()
712 {
713     if (!m_hasRareData)
714         return false;
715     if (m_parsedNth)
716         return true;
717     m_parsedNth = m_data.m_rareData->parseNth();
718     return m_parsedNth;
719 }
720 
matchNth(int count)721 bool CSSSelector::matchNth(int count)
722 {
723     ASSERT(m_hasRareData);
724     return m_data.m_rareData->matchNth(count);
725 }
726 
isSimple() const727 bool CSSSelector::isSimple() const
728 {
729     if (selectorList() || tagHistory() || matchesPseudoElement())
730         return false;
731 
732     int numConditions = 0;
733 
734     // hasTag() cannot be be used here because namespace may not be nullAtom.
735     // Example:
736     //     @namespace "http://www.w3.org/2000/svg";
737     //     svg:not(:root) { ...
738     if (m_tag != starAtom)
739         numConditions++;
740 
741     if (m_match == Id || m_match == Class || m_match == PseudoClass)
742         numConditions++;
743 
744     if (m_hasRareData && m_data.m_rareData->m_attribute != anyQName())
745         numConditions++;
746 
747     // numConditions is 0 for a universal selector.
748     // numConditions is 1 for other simple selectors.
749     return numConditions <= 1;
750 }
751 
RareData(PassRefPtr<AtomicStringImpl> value)752 CSSSelector::RareData::RareData(PassRefPtr<AtomicStringImpl> value)
753     : m_value(value.leakRef())
754     , m_a(0)
755     , m_b(0)
756     , m_attribute(anyQName())
757     , m_argument(nullAtom)
758 {
759 }
760 
~RareData()761 CSSSelector::RareData::~RareData()
762 {
763     if (m_value)
764         m_value->deref();
765 }
766 
767 // a helper function for parsing nth-arguments
parseNth()768 bool CSSSelector::RareData::parseNth()
769 {
770     String argument = m_argument.lower();
771 
772     if (argument.isEmpty())
773         return false;
774 
775     m_a = 0;
776     m_b = 0;
777     if (argument == "odd") {
778         m_a = 2;
779         m_b = 1;
780     } else if (argument == "even") {
781         m_a = 2;
782         m_b = 0;
783     } else {
784         size_t n = argument.find('n');
785         if (n != notFound) {
786             if (argument[0] == '-') {
787                 if (n == 1)
788                     m_a = -1; // -n == -1n
789                 else
790                     m_a = argument.substring(0, n).toInt();
791             } else if (!n)
792                 m_a = 1; // n == 1n
793             else
794                 m_a = argument.substring(0, n).toInt();
795 
796             size_t p = argument.find('+', n);
797             if (p != notFound)
798                 m_b = argument.substring(p + 1, argument.length() - p - 1).toInt();
799             else {
800                 p = argument.find('-', n);
801                 if (p != notFound)
802                     m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt();
803             }
804         } else
805             m_b = argument.toInt();
806     }
807     return true;
808 }
809 
810 // a helper function for checking nth-arguments
matchNth(int count)811 bool CSSSelector::RareData::matchNth(int count)
812 {
813     if (!m_a)
814         return count == m_b;
815     else if (m_a > 0) {
816         if (count < m_b)
817             return false;
818         return (count - m_b) % m_a == 0;
819     } else {
820         if (count > m_b)
821             return false;
822         return (m_b - count) % (-m_a) == 0;
823     }
824 }
825 
826 } // namespace WebCore
827