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