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
37namespace WebCore {
38
39using namespace HTMLNames;
40
41void 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
50unsigned 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
63inline 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
95unsigned 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
116PseudoId 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
239static 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
416CSSSelector::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
425void 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
541bool 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
563String 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
681const 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
693void CSSSelector::setAttribute(const QualifiedName& value)
694{
695    createRareData();
696    m_data.m_rareData->m_attribute = value;
697}
698
699void CSSSelector::setArgument(const AtomicString& value)
700{
701    createRareData();
702    m_data.m_rareData->m_argument = value;
703}
704
705void CSSSelector::setSelectorList(PassOwnPtr<CSSSelectorList> selectorList)
706{
707    createRareData();
708    m_data.m_rareData->m_selectorList = selectorList;
709}
710
711bool 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
721bool CSSSelector::matchNth(int count)
722{
723    ASSERT(m_hasRareData);
724    return m_data.m_rareData->matchNth(count);
725}
726
727bool 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
752CSSSelector::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
761CSSSelector::RareData::~RareData()
762{
763    if (m_value)
764        m_value->deref();
765}
766
767// a helper function for parsing nth-arguments
768bool 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
811bool 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