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     case PseudoFullScreenMediaDocument:
169         return FULL_SCREEN_MEDIA_DOCUMENT;
170 #endif
171 
172     case PseudoInputListButton:
173 #if ENABLE(DATALIST)
174         return INPUT_LIST_BUTTON;
175 #endif
176     case PseudoUnknown:
177     case PseudoEmpty:
178     case PseudoFirstChild:
179     case PseudoFirstOfType:
180     case PseudoLastChild:
181     case PseudoLastOfType:
182     case PseudoOnlyChild:
183     case PseudoOnlyOfType:
184     case PseudoNthChild:
185     case PseudoNthOfType:
186     case PseudoNthLastChild:
187     case PseudoNthLastOfType:
188     case PseudoLink:
189     case PseudoVisited:
190     case PseudoAny:
191     case PseudoAnyLink:
192     case PseudoAutofill:
193     case PseudoHover:
194     case PseudoDrag:
195     case PseudoFocus:
196     case PseudoActive:
197     case PseudoChecked:
198     case PseudoEnabled:
199     case PseudoFullPageMedia:
200     case PseudoDefault:
201     case PseudoDisabled:
202     case PseudoOptional:
203     case PseudoRequired:
204     case PseudoReadOnly:
205     case PseudoReadWrite:
206     case PseudoValid:
207     case PseudoInvalid:
208     case PseudoIndeterminate:
209     case PseudoTarget:
210     case PseudoLang:
211     case PseudoNot:
212     case PseudoRoot:
213     case PseudoScrollbarBack:
214     case PseudoScrollbarForward:
215     case PseudoWindowInactive:
216     case PseudoCornerPresent:
217     case PseudoDecrement:
218     case PseudoIncrement:
219     case PseudoHorizontal:
220     case PseudoVertical:
221     case PseudoStart:
222     case PseudoEnd:
223     case PseudoDoubleButton:
224     case PseudoSingleButton:
225     case PseudoNoButton:
226     case PseudoFirstPage:
227     case PseudoLeftPage:
228     case PseudoRightPage:
229     case PseudoInRange:
230     case PseudoOutOfRange:
231         return NOPSEUDO;
232     case PseudoNotParsed:
233         ASSERT_NOT_REACHED();
234         return NOPSEUDO;
235     }
236 
237     ASSERT_NOT_REACHED();
238     return NOPSEUDO;
239 }
240 
nameToPseudoTypeMap()241 static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoTypeMap()
242 {
243     DEFINE_STATIC_LOCAL(AtomicString, active, ("active"));
244     DEFINE_STATIC_LOCAL(AtomicString, after, ("after"));
245     DEFINE_STATIC_LOCAL(AtomicString, any, ("-webkit-any("));
246     DEFINE_STATIC_LOCAL(AtomicString, anyLink, ("-webkit-any-link"));
247     DEFINE_STATIC_LOCAL(AtomicString, autofill, ("-webkit-autofill"));
248     DEFINE_STATIC_LOCAL(AtomicString, before, ("before"));
249     DEFINE_STATIC_LOCAL(AtomicString, checked, ("checked"));
250     DEFINE_STATIC_LOCAL(AtomicString, fileUploadButton, ("-webkit-file-upload-button"));
251 #if ENABLE(INPUT_SPEECH)
252     DEFINE_STATIC_LOCAL(AtomicString, inputSpeechButton, ("-webkit-input-speech-button"));
253 #endif
254     DEFINE_STATIC_LOCAL(AtomicString, defaultString, ("default"));
255     DEFINE_STATIC_LOCAL(AtomicString, disabled, ("disabled"));
256     DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only"));
257     DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write"));
258     DEFINE_STATIC_LOCAL(AtomicString, valid, ("valid"));
259     DEFINE_STATIC_LOCAL(AtomicString, invalid, ("invalid"));
260     DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag"));
261     DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag")); // was documented with this name in Apple documentation, so keep an alia
262     DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty"));
263     DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled"));
264     DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child"));
265     DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter"));
266     DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line"));
267     DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type"));
268     DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media"));
269     DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child("));
270     DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type("));
271     DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child("));
272     DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type("));
273     DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus"));
274     DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover"));
275     DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate"));
276     DEFINE_STATIC_LOCAL(AtomicString, innerSpinButton, ("-webkit-inner-spin-button"));
277 #if ENABLE(DATALIST)
278     DEFINE_STATIC_LOCAL(AtomicString, inputListButton, ("-webkit-input-list-button"));
279 #endif
280     DEFINE_STATIC_LOCAL(AtomicString, inputPlaceholder, ("-webkit-input-placeholder"));
281     DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child"));
282     DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type"));
283     DEFINE_STATIC_LOCAL(AtomicString, link, ("link"));
284     DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang("));
285     DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not("));
286     DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child"));
287     DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type"));
288     DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional"));
289     DEFINE_STATIC_LOCAL(AtomicString, outerSpinButton, ("-webkit-outer-spin-button"));
290     DEFINE_STATIC_LOCAL(AtomicString, required, ("required"));
291     DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer"));
292     DEFINE_STATIC_LOCAL(AtomicString, root, ("root"));
293     DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar"));
294     DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button"));
295     DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner"));
296     DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb"));
297     DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track"));
298     DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece"));
299     DEFINE_STATIC_LOCAL(AtomicString, searchCancelButton, ("-webkit-search-cancel-button"));
300     DEFINE_STATIC_LOCAL(AtomicString, searchDecoration, ("-webkit-search-decoration"));
301     DEFINE_STATIC_LOCAL(AtomicString, searchResultsDecoration, ("-webkit-search-results-decoration"));
302     DEFINE_STATIC_LOCAL(AtomicString, searchResultsButton, ("-webkit-search-results-button"));
303     DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection"));
304     DEFINE_STATIC_LOCAL(AtomicString, target, ("target"));
305     DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited"));
306     DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive"));
307     DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement"));
308     DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment"));
309     DEFINE_STATIC_LOCAL(AtomicString, start, ("start"));
310     DEFINE_STATIC_LOCAL(AtomicString, end, ("end"));
311     DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal"));
312     DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical"));
313     DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button"));
314     DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button"));
315     DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button"));
316     DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present"));
317     // Paged Media pseudo-classes
318     DEFINE_STATIC_LOCAL(AtomicString, firstPage, ("first"));
319     DEFINE_STATIC_LOCAL(AtomicString, leftPage, ("left"));
320     DEFINE_STATIC_LOCAL(AtomicString, rightPage, ("right"));
321 #if ENABLE(FULLSCREEN_API)
322     DEFINE_STATIC_LOCAL(AtomicString, fullScreen, ("-webkit-full-screen"));
323     DEFINE_STATIC_LOCAL(AtomicString, fullScreenDocument, ("-webkit-full-screen-document"));
324     DEFINE_STATIC_LOCAL(AtomicString, fullScreenMediaDocument, ("-webkit-full-screen-media-document"));
325 #endif
326     DEFINE_STATIC_LOCAL(AtomicString, inRange, ("in-range"));
327     DEFINE_STATIC_LOCAL(AtomicString, outOfRange, ("out-of-range"));
328 
329     static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = 0;
330     if (!nameToPseudoType) {
331         nameToPseudoType = new HashMap<AtomicStringImpl*, CSSSelector::PseudoType>;
332         nameToPseudoType->set(active.impl(), CSSSelector::PseudoActive);
333         nameToPseudoType->set(after.impl(), CSSSelector::PseudoAfter);
334         nameToPseudoType->set(anyLink.impl(), CSSSelector::PseudoAnyLink);
335         nameToPseudoType->set(any.impl(), CSSSelector::PseudoAny);
336         nameToPseudoType->set(autofill.impl(), CSSSelector::PseudoAutofill);
337         nameToPseudoType->set(before.impl(), CSSSelector::PseudoBefore);
338         nameToPseudoType->set(checked.impl(), CSSSelector::PseudoChecked);
339         nameToPseudoType->set(fileUploadButton.impl(), CSSSelector::PseudoFileUploadButton);
340 #if ENABLE(INPUT_SPEECH)
341         nameToPseudoType->set(inputSpeechButton.impl(), CSSSelector::PseudoInputSpeechButton);
342 #endif
343         nameToPseudoType->set(defaultString.impl(), CSSSelector::PseudoDefault);
344         nameToPseudoType->set(disabled.impl(), CSSSelector::PseudoDisabled);
345         nameToPseudoType->set(readOnly.impl(), CSSSelector::PseudoReadOnly);
346         nameToPseudoType->set(readWrite.impl(), CSSSelector::PseudoReadWrite);
347         nameToPseudoType->set(valid.impl(), CSSSelector::PseudoValid);
348         nameToPseudoType->set(invalid.impl(), CSSSelector::PseudoInvalid);
349         nameToPseudoType->set(drag.impl(), CSSSelector::PseudoDrag);
350         nameToPseudoType->set(dragAlias.impl(), CSSSelector::PseudoDrag);
351         nameToPseudoType->set(enabled.impl(), CSSSelector::PseudoEnabled);
352         nameToPseudoType->set(empty.impl(), CSSSelector::PseudoEmpty);
353         nameToPseudoType->set(firstChild.impl(), CSSSelector::PseudoFirstChild);
354         nameToPseudoType->set(fullPageMedia.impl(), CSSSelector::PseudoFullPageMedia);
355 #if ENABLE(DATALIST)
356         nameToPseudoType->set(inputListButton.impl(), CSSSelector::PseudoInputListButton);
357 #endif
358         nameToPseudoType->set(inputPlaceholder.impl(), CSSSelector::PseudoInputPlaceholder);
359         nameToPseudoType->set(lastChild.impl(), CSSSelector::PseudoLastChild);
360         nameToPseudoType->set(lastOfType.impl(), CSSSelector::PseudoLastOfType);
361         nameToPseudoType->set(onlyChild.impl(), CSSSelector::PseudoOnlyChild);
362         nameToPseudoType->set(onlyOfType.impl(), CSSSelector::PseudoOnlyOfType);
363         nameToPseudoType->set(firstLetter.impl(), CSSSelector::PseudoFirstLetter);
364         nameToPseudoType->set(firstLine.impl(), CSSSelector::PseudoFirstLine);
365         nameToPseudoType->set(firstOfType.impl(), CSSSelector::PseudoFirstOfType);
366         nameToPseudoType->set(focus.impl(), CSSSelector::PseudoFocus);
367         nameToPseudoType->set(hover.impl(), CSSSelector::PseudoHover);
368         nameToPseudoType->set(indeterminate.impl(), CSSSelector::PseudoIndeterminate);
369         nameToPseudoType->set(innerSpinButton.impl(), CSSSelector::PseudoInnerSpinButton);
370         nameToPseudoType->set(link.impl(), CSSSelector::PseudoLink);
371         nameToPseudoType->set(lang.impl(), CSSSelector::PseudoLang);
372         nameToPseudoType->set(notStr.impl(), CSSSelector::PseudoNot);
373         nameToPseudoType->set(nthChild.impl(), CSSSelector::PseudoNthChild);
374         nameToPseudoType->set(nthOfType.impl(), CSSSelector::PseudoNthOfType);
375         nameToPseudoType->set(nthLastChild.impl(), CSSSelector::PseudoNthLastChild);
376         nameToPseudoType->set(nthLastOfType.impl(), CSSSelector::PseudoNthLastOfType);
377         nameToPseudoType->set(outerSpinButton.impl(), CSSSelector::PseudoOuterSpinButton);
378         nameToPseudoType->set(root.impl(), CSSSelector::PseudoRoot);
379         nameToPseudoType->set(windowInactive.impl(), CSSSelector::PseudoWindowInactive);
380         nameToPseudoType->set(decrement.impl(), CSSSelector::PseudoDecrement);
381         nameToPseudoType->set(increment.impl(), CSSSelector::PseudoIncrement);
382         nameToPseudoType->set(start.impl(), CSSSelector::PseudoStart);
383         nameToPseudoType->set(end.impl(), CSSSelector::PseudoEnd);
384         nameToPseudoType->set(horizontal.impl(), CSSSelector::PseudoHorizontal);
385         nameToPseudoType->set(vertical.impl(), CSSSelector::PseudoVertical);
386         nameToPseudoType->set(doubleButton.impl(), CSSSelector::PseudoDoubleButton);
387         nameToPseudoType->set(singleButton.impl(), CSSSelector::PseudoSingleButton);
388         nameToPseudoType->set(noButton.impl(), CSSSelector::PseudoNoButton);
389         nameToPseudoType->set(optional.impl(), CSSSelector::PseudoOptional);
390         nameToPseudoType->set(required.impl(), CSSSelector::PseudoRequired);
391         nameToPseudoType->set(resizer.impl(), CSSSelector::PseudoResizer);
392         nameToPseudoType->set(scrollbar.impl(), CSSSelector::PseudoScrollbar);
393         nameToPseudoType->set(scrollbarButton.impl(), CSSSelector::PseudoScrollbarButton);
394         nameToPseudoType->set(scrollbarCorner.impl(), CSSSelector::PseudoScrollbarCorner);
395         nameToPseudoType->set(scrollbarThumb.impl(), CSSSelector::PseudoScrollbarThumb);
396         nameToPseudoType->set(scrollbarTrack.impl(), CSSSelector::PseudoScrollbarTrack);
397         nameToPseudoType->set(scrollbarTrackPiece.impl(), CSSSelector::PseudoScrollbarTrackPiece);
398         nameToPseudoType->set(cornerPresent.impl(), CSSSelector::PseudoCornerPresent);
399         nameToPseudoType->set(searchCancelButton.impl(), CSSSelector::PseudoSearchCancelButton);
400         nameToPseudoType->set(searchDecoration.impl(), CSSSelector::PseudoSearchDecoration);
401         nameToPseudoType->set(searchResultsDecoration.impl(), CSSSelector::PseudoSearchResultsDecoration);
402         nameToPseudoType->set(searchResultsButton.impl(), CSSSelector::PseudoSearchResultsButton);
403         nameToPseudoType->set(selection.impl(), CSSSelector::PseudoSelection);
404         nameToPseudoType->set(target.impl(), CSSSelector::PseudoTarget);
405         nameToPseudoType->set(visited.impl(), CSSSelector::PseudoVisited);
406         nameToPseudoType->set(firstPage.impl(), CSSSelector::PseudoFirstPage);
407         nameToPseudoType->set(leftPage.impl(), CSSSelector::PseudoLeftPage);
408         nameToPseudoType->set(rightPage.impl(), CSSSelector::PseudoRightPage);
409 #if ENABLE(FULLSCREEN_API)
410         nameToPseudoType->set(fullScreen.impl(), CSSSelector::PseudoFullScreen);
411         nameToPseudoType->set(fullScreenDocument.impl(), CSSSelector::PseudoFullScreenDocument);
412         nameToPseudoType->set(fullScreenMediaDocument.impl(), CSSSelector::PseudoFullScreenMediaDocument);
413 #endif
414         nameToPseudoType->set(inRange.impl(), CSSSelector::PseudoInRange);
415         nameToPseudoType->set(outOfRange.impl(), CSSSelector::PseudoOutOfRange);
416     }
417     return nameToPseudoType;
418 }
419 
parsePseudoType(const AtomicString & name)420 CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name)
421 {
422     if (name.isNull())
423         return PseudoUnknown;
424     HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = nameToPseudoTypeMap();
425     HashMap<AtomicStringImpl*, CSSSelector::PseudoType>::iterator slot = nameToPseudoType->find(name.impl());
426     return slot == nameToPseudoType->end() ? PseudoUnknown : slot->second;
427 }
428 
extractPseudoType() const429 void CSSSelector::extractPseudoType() const
430 {
431     if (m_match != PseudoClass && m_match != PseudoElement && m_match != PagePseudoClass)
432         return;
433 
434     m_pseudoType = parsePseudoType(value());
435 
436     bool element = false; // pseudo-element
437     bool compat = false; // single colon compatbility mode
438     bool isPagePseudoClass = false; // Page pseudo-class
439 
440     switch (m_pseudoType) {
441     case PseudoAfter:
442     case PseudoBefore:
443     case PseudoFirstLetter:
444     case PseudoFirstLine:
445         compat = true;
446     case PseudoFileUploadButton:
447     case PseudoInputListButton:
448     case PseudoInputPlaceholder:
449 #if ENABLE(INPUT_SPEECH)
450     case PseudoInputSpeechButton:
451 #endif
452     case PseudoInnerSpinButton:
453     case PseudoOuterSpinButton:
454     case PseudoResizer:
455     case PseudoScrollbar:
456     case PseudoScrollbarCorner:
457     case PseudoScrollbarButton:
458     case PseudoScrollbarThumb:
459     case PseudoScrollbarTrack:
460     case PseudoScrollbarTrackPiece:
461     case PseudoSearchCancelButton:
462     case PseudoSearchDecoration:
463     case PseudoSearchResultsDecoration:
464     case PseudoSearchResultsButton:
465     case PseudoSelection:
466         element = true;
467         break;
468     case PseudoUnknown:
469     case PseudoEmpty:
470     case PseudoFirstChild:
471     case PseudoFirstOfType:
472     case PseudoLastChild:
473     case PseudoLastOfType:
474     case PseudoOnlyChild:
475     case PseudoOnlyOfType:
476     case PseudoNthChild:
477     case PseudoNthOfType:
478     case PseudoNthLastChild:
479     case PseudoNthLastOfType:
480     case PseudoLink:
481     case PseudoVisited:
482     case PseudoAny:
483     case PseudoAnyLink:
484     case PseudoAutofill:
485     case PseudoHover:
486     case PseudoDrag:
487     case PseudoFocus:
488     case PseudoActive:
489     case PseudoChecked:
490     case PseudoEnabled:
491     case PseudoFullPageMedia:
492     case PseudoDefault:
493     case PseudoDisabled:
494     case PseudoOptional:
495     case PseudoRequired:
496     case PseudoReadOnly:
497     case PseudoReadWrite:
498     case PseudoValid:
499     case PseudoInvalid:
500     case PseudoIndeterminate:
501     case PseudoTarget:
502     case PseudoLang:
503     case PseudoNot:
504     case PseudoRoot:
505     case PseudoScrollbarBack:
506     case PseudoScrollbarForward:
507     case PseudoWindowInactive:
508     case PseudoCornerPresent:
509     case PseudoDecrement:
510     case PseudoIncrement:
511     case PseudoHorizontal:
512     case PseudoVertical:
513     case PseudoStart:
514     case PseudoEnd:
515     case PseudoDoubleButton:
516     case PseudoSingleButton:
517     case PseudoNoButton:
518     case PseudoNotParsed:
519 #if ENABLE(FULLSCREEN_API)
520     case PseudoFullScreen:
521     case PseudoFullScreenDocument:
522     case PseudoFullScreenMediaDocument:
523 #endif
524     case PseudoInRange:
525     case PseudoOutOfRange:
526         break;
527     case PseudoFirstPage:
528     case PseudoLeftPage:
529     case PseudoRightPage:
530         isPagePseudoClass = true;
531         break;
532     }
533 
534     bool matchPagePseudoClass = (m_match == PagePseudoClass);
535     if (matchPagePseudoClass != isPagePseudoClass)
536         m_pseudoType = PseudoUnknown;
537     else if (m_match == PseudoClass && element) {
538         if (!compat)
539             m_pseudoType = PseudoUnknown;
540         else
541            m_match = PseudoElement;
542     } else if (m_match == PseudoElement && !element)
543         m_pseudoType = PseudoUnknown;
544 }
545 
operator ==(const CSSSelector & other)546 bool CSSSelector::operator==(const CSSSelector& other)
547 {
548     const CSSSelector* sel1 = this;
549     const CSSSelector* sel2 = &other;
550 
551     while (sel1 && sel2) {
552         if (sel1->m_tag != sel2->m_tag || sel1->attribute() != sel2->attribute() ||
553              sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match ||
554              sel1->value() != sel2->value() ||
555              sel1->pseudoType() != sel2->pseudoType() ||
556              sel1->argument() != sel2->argument())
557             return false;
558         sel1 = sel1->tagHistory();
559         sel2 = sel2->tagHistory();
560     }
561 
562     if (sel1 || sel2)
563         return false;
564 
565     return true;
566 }
567 
selectorText() const568 String CSSSelector::selectorText() const
569 {
570     String str = "";
571 
572     const AtomicString& prefix = m_tag.prefix();
573     const AtomicString& localName = m_tag.localName();
574     if (m_match == CSSSelector::None || !prefix.isNull() || localName != starAtom) {
575         if (prefix.isNull())
576             str = localName;
577         else {
578             str = prefix.string();
579             str.append("|");
580             str.append(localName);
581         }
582     }
583 
584     const CSSSelector* cs = this;
585     while (true) {
586         if (cs->m_match == CSSSelector::Id) {
587             str += "#";
588             serializeIdentifier(cs->value(), str);
589         } else if (cs->m_match == CSSSelector::Class) {
590             str += ".";
591             serializeIdentifier(cs->value(), str);
592         } else if (cs->m_match == CSSSelector::PseudoClass || cs->m_match == CSSSelector::PagePseudoClass) {
593             str += ":";
594             str += cs->value();
595 
596             switch (cs->pseudoType()) {
597             case PseudoNot:
598                 ASSERT(cs->selectorList());
599                 str += cs->selectorList()->first()->selectorText();
600                 str += ")";
601                 break;
602             case PseudoLang:
603             case PseudoNthChild:
604             case PseudoNthLastChild:
605             case PseudoNthOfType:
606             case PseudoNthLastOfType:
607                 str += cs->argument();
608                 str += ")";
609                 break;
610             case PseudoAny: {
611                 CSSSelector* firstSubSelector = cs->selectorList()->first();
612                 for (CSSSelector* subSelector = firstSubSelector; subSelector; subSelector = CSSSelectorList::next(subSelector)) {
613                     if (subSelector != firstSubSelector)
614                         str += ",";
615                     str += subSelector->selectorText();
616                 }
617                 str += ")";
618                 break;
619             }
620             default:
621                 break;
622             }
623         } else if (cs->m_match == CSSSelector::PseudoElement) {
624             str += "::";
625             str += cs->value();
626         } else if (cs->hasAttribute()) {
627             str += "[";
628             const AtomicString& prefix = cs->attribute().prefix();
629             if (!prefix.isNull()) {
630                 str.append(prefix);
631                 str.append("|");
632             }
633             str += cs->attribute().localName();
634             switch (cs->m_match) {
635                 case CSSSelector::Exact:
636                     str += "=";
637                     break;
638                 case CSSSelector::Set:
639                     // set has no operator or value, just the attrName
640                     str += "]";
641                     break;
642                 case CSSSelector::List:
643                     str += "~=";
644                     break;
645                 case CSSSelector::Hyphen:
646                     str += "|=";
647                     break;
648                 case CSSSelector::Begin:
649                     str += "^=";
650                     break;
651                 case CSSSelector::End:
652                     str += "$=";
653                     break;
654                 case CSSSelector::Contain:
655                     str += "*=";
656                     break;
657                 default:
658                     break;
659             }
660             if (cs->m_match != CSSSelector::Set) {
661                 serializeString(cs->value(), str);
662                 str += "]";
663             }
664         }
665         if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory())
666             break;
667         cs = cs->tagHistory();
668     }
669 
670     if (CSSSelector* tagHistory = cs->tagHistory()) {
671         String tagHistoryText = tagHistory->selectorText();
672         if (cs->relation() == CSSSelector::DirectAdjacent)
673             str = tagHistoryText + " + " + str;
674         else if (cs->relation() == CSSSelector::IndirectAdjacent)
675             str = tagHistoryText + " ~ " + str;
676         else if (cs->relation() == CSSSelector::Child)
677             str = tagHistoryText + " > " + str;
678         else
679             // Descendant
680             str = tagHistoryText + " " + str;
681     }
682 
683     return str;
684 }
685 
attribute() const686 const QualifiedName& CSSSelector::attribute() const
687 {
688     switch (m_match) {
689     case Id:
690         return idAttr;
691     case Class:
692         return classAttr;
693     default:
694         return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName();
695     }
696 }
697 
setAttribute(const QualifiedName & value)698 void CSSSelector::setAttribute(const QualifiedName& value)
699 {
700     createRareData();
701     m_data.m_rareData->m_attribute = value;
702 }
703 
setArgument(const AtomicString & value)704 void CSSSelector::setArgument(const AtomicString& value)
705 {
706     createRareData();
707     m_data.m_rareData->m_argument = value;
708 }
709 
setSelectorList(PassOwnPtr<CSSSelectorList> selectorList)710 void CSSSelector::setSelectorList(PassOwnPtr<CSSSelectorList> selectorList)
711 {
712     createRareData();
713     m_data.m_rareData->m_selectorList = selectorList;
714 }
715 
parseNth()716 bool CSSSelector::parseNth()
717 {
718     if (!m_hasRareData)
719         return false;
720     if (m_parsedNth)
721         return true;
722     m_parsedNth = m_data.m_rareData->parseNth();
723     return m_parsedNth;
724 }
725 
matchNth(int count)726 bool CSSSelector::matchNth(int count)
727 {
728     ASSERT(m_hasRareData);
729     return m_data.m_rareData->matchNth(count);
730 }
731 
isSimple() const732 bool CSSSelector::isSimple() const
733 {
734     if (selectorList() || tagHistory() || matchesPseudoElement())
735         return false;
736 
737     int numConditions = 0;
738 
739     // hasTag() cannot be be used here because namespace may not be nullAtom.
740     // Example:
741     //     @namespace "http://www.w3.org/2000/svg";
742     //     svg:not(:root) { ...
743     if (m_tag != starAtom)
744         numConditions++;
745 
746     if (m_match == Id || m_match == Class || m_match == PseudoClass)
747         numConditions++;
748 
749     if (m_hasRareData && m_data.m_rareData->m_attribute != anyQName())
750         numConditions++;
751 
752     // numConditions is 0 for a universal selector.
753     // numConditions is 1 for other simple selectors.
754     return numConditions <= 1;
755 }
756 
RareData(PassRefPtr<AtomicStringImpl> value)757 CSSSelector::RareData::RareData(PassRefPtr<AtomicStringImpl> value)
758     : m_value(value.leakRef())
759     , m_a(0)
760     , m_b(0)
761     , m_attribute(anyQName())
762     , m_argument(nullAtom)
763 {
764 }
765 
~RareData()766 CSSSelector::RareData::~RareData()
767 {
768     if (m_value)
769         m_value->deref();
770 }
771 
772 // a helper function for parsing nth-arguments
parseNth()773 bool CSSSelector::RareData::parseNth()
774 {
775     String argument = m_argument.lower();
776 
777     if (argument.isEmpty())
778         return false;
779 
780     m_a = 0;
781     m_b = 0;
782     if (argument == "odd") {
783         m_a = 2;
784         m_b = 1;
785     } else if (argument == "even") {
786         m_a = 2;
787         m_b = 0;
788     } else {
789         size_t n = argument.find('n');
790         if (n != notFound) {
791             if (argument[0] == '-') {
792                 if (n == 1)
793                     m_a = -1; // -n == -1n
794                 else
795                     m_a = argument.substring(0, n).toInt();
796             } else if (!n)
797                 m_a = 1; // n == 1n
798             else
799                 m_a = argument.substring(0, n).toInt();
800 
801             size_t p = argument.find('+', n);
802             if (p != notFound)
803                 m_b = argument.substring(p + 1, argument.length() - p - 1).toInt();
804             else {
805                 p = argument.find('-', n);
806                 if (p != notFound)
807                     m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt();
808             }
809         } else
810             m_b = argument.toInt();
811     }
812     return true;
813 }
814 
815 // a helper function for checking nth-arguments
matchNth(int count)816 bool CSSSelector::RareData::matchNth(int count)
817 {
818     if (!m_a)
819         return count == m_b;
820     else if (m_a > 0) {
821         if (count < m_b)
822             return false;
823         return (count - m_b) % m_a == 0;
824     } else {
825         if (count > m_b)
826             return false;
827         return (m_b - count) % (-m_a) == 0;
828     }
829 }
830 
831 } // namespace WebCore
832