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