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 Apple Inc. All rights reserved.
      7  * Copyright (C) 2008 David Smith (catfish.man (at) gmail.com)
      8  *
      9  * This library is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU Library General Public
     11  * License as published by the Free Software Foundation; either
     12  * version 2 of the License, or (at your option) any later version.
     13  *
     14  * This library is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     17  * Library General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU Library General Public License
     20  * along with this library; see the file COPYING.LIB.  If not, write to
     21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     22  * Boston, MA 02110-1301, USA.
     23  */
     24 
     25 #include "config.h"
     26 #include "CSSSelector.h"
     27 
     28 #include "wtf/Assertions.h"
     29 #include "HTMLNames.h"
     30 
     31 #include <wtf/StdLibExtras.h>
     32 
     33 namespace WebCore {
     34 
     35 using namespace HTMLNames;
     36 
     37 unsigned int CSSSelector::specificity()
     38 {
     39     // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function
     40     // isn't quite correct.
     41     int s = (m_tag.localName() == starAtom ? 0 : 1);
     42     switch (m_match) {
     43         case Id:
     44             s += 0x10000;
     45             break;
     46         case Exact:
     47         case Class:
     48         case Set:
     49         case List:
     50         case Hyphen:
     51         case PseudoClass:
     52         case PseudoElement:
     53         case Contain:
     54         case Begin:
     55         case End:
     56             s += 0x100;
     57         case None:
     58             break;
     59     }
     60 
     61     if (CSSSelector* tagHistory = this->tagHistory())
     62         s += tagHistory->specificity();
     63 
     64     // make sure it doesn't overflow
     65     return s & 0xffffff;
     66 }
     67 
     68 void CSSSelector::extractPseudoType() const
     69 {
     70     if (m_match != PseudoClass && m_match != PseudoElement)
     71         return;
     72 
     73     DEFINE_STATIC_LOCAL(AtomicString, active, ("active"));
     74     DEFINE_STATIC_LOCAL(AtomicString, after, ("after"));
     75     DEFINE_STATIC_LOCAL(AtomicString, anyLink, ("-webkit-any-link"));
     76     DEFINE_STATIC_LOCAL(AtomicString, autofill, ("-webkit-autofill"));
     77     DEFINE_STATIC_LOCAL(AtomicString, before, ("before"));
     78     DEFINE_STATIC_LOCAL(AtomicString, checked, ("checked"));
     79     DEFINE_STATIC_LOCAL(AtomicString, fileUploadButton, ("-webkit-file-upload-button"));
     80     DEFINE_STATIC_LOCAL(AtomicString, defaultString, ("default"));
     81     DEFINE_STATIC_LOCAL(AtomicString, disabled, ("disabled"));
     82     DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only"));
     83     DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write"));
     84     DEFINE_STATIC_LOCAL(AtomicString, valid, ("valid"));
     85     DEFINE_STATIC_LOCAL(AtomicString, invalid, ("invalid"));
     86     DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag"));
     87     DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag")); // was documented with this name in Apple documentation, so keep an alia
     88     DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty"));
     89     DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled"));
     90     DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child"));
     91     DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter"));
     92     DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line"));
     93     DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type"));
     94     DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media"));
     95     DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child("));
     96     DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type("));
     97     DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child("));
     98     DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type("));
     99     DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus"));
    100     DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover"));
    101     DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate"));
    102     DEFINE_STATIC_LOCAL(AtomicString, innerSpinButton, ("-webkit-inner-spin-button"));
    103 #if ENABLE(DATALIST)
    104     DEFINE_STATIC_LOCAL(AtomicString, inputListButton, ("-webkit-input-list-button"));
    105 #endif
    106     DEFINE_STATIC_LOCAL(AtomicString, inputPlaceholder, ("-webkit-input-placeholder"));
    107     DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child"));
    108     DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type"));
    109     DEFINE_STATIC_LOCAL(AtomicString, link, ("link"));
    110     DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang("));
    111     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsPanel, ("-webkit-media-controls-panel"));
    112     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsMuteButton, ("-webkit-media-controls-mute-button"));
    113     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsPlayButton, ("-webkit-media-controls-play-button"));
    114     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimeline, ("-webkit-media-controls-timeline"));
    115     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsVolumeSlider, ("-webkit-media-controls-volume-slider"));
    116     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsSeekBackButton, ("-webkit-media-controls-seek-back-button"));
    117     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsSeekForwardButton, ("-webkit-media-controls-seek-forward-button"));
    118     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsRewindButton, ("-webkit-media-controls-rewind-button"));
    119     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsReturnToRealtimeButton, ("-webkit-media-controls-return-to-realtime-button"));
    120     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsToggleClosedCaptionsButton, ("-webkit-media-controls-toggle-closed-captions-button"));
    121     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsStatusDisplay, ("-webkit-media-controls-status-display"));
    122     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsFullscreenButton, ("-webkit-media-controls-fullscreen-button"));
    123     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimelineContainer, ("-webkit-media-controls-timeline-container"));
    124     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsVolumeSliderContainer, ("-webkit-media-controls-volume-slider-container"));
    125     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsCurrentTimeDisplay, ("-webkit-media-controls-current-time-display"));
    126     DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimeRemainingDisplay, ("-webkit-media-controls-time-remaining-display"));
    127     DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not("));
    128     DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child"));
    129     DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type"));
    130     DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional"));
    131     DEFINE_STATIC_LOCAL(AtomicString, outerSpinButton, ("-webkit-outer-spin-button"));
    132     DEFINE_STATIC_LOCAL(AtomicString, required, ("required"));
    133     DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer"));
    134     DEFINE_STATIC_LOCAL(AtomicString, root, ("root"));
    135     DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar"));
    136     DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button"));
    137     DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner"));
    138     DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb"));
    139     DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track"));
    140     DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece"));
    141     DEFINE_STATIC_LOCAL(AtomicString, searchCancelButton, ("-webkit-search-cancel-button"));
    142     DEFINE_STATIC_LOCAL(AtomicString, searchDecoration, ("-webkit-search-decoration"));
    143     DEFINE_STATIC_LOCAL(AtomicString, searchResultsDecoration, ("-webkit-search-results-decoration"));
    144     DEFINE_STATIC_LOCAL(AtomicString, searchResultsButton, ("-webkit-search-results-button"));
    145     DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection"));
    146     DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-thumb"));
    147     DEFINE_STATIC_LOCAL(AtomicString, target, ("target"));
    148     DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited"));
    149     DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive"));
    150     DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement"));
    151     DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment"));
    152     DEFINE_STATIC_LOCAL(AtomicString, start, ("start"));
    153     DEFINE_STATIC_LOCAL(AtomicString, end, ("end"));
    154     DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal"));
    155     DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical"));
    156     DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button"));
    157     DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button"));
    158     DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button"));
    159     DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present"));
    160 
    161     bool element = false; // pseudo-element
    162     bool compat = false; // single colon compatbility mode
    163 
    164     m_pseudoType = PseudoUnknown;
    165     if (m_value == active)
    166         m_pseudoType = PseudoActive;
    167     else if (m_value == after) {
    168         m_pseudoType = PseudoAfter;
    169         element = true;
    170         compat = true;
    171     } else if (m_value == anyLink)
    172         m_pseudoType = PseudoAnyLink;
    173     else if (m_value == autofill)
    174         m_pseudoType = PseudoAutofill;
    175     else if (m_value == before) {
    176         m_pseudoType = PseudoBefore;
    177         element = true;
    178         compat = true;
    179     } else if (m_value == checked)
    180         m_pseudoType = PseudoChecked;
    181     else if (m_value == fileUploadButton) {
    182         m_pseudoType = PseudoFileUploadButton;
    183         element = true;
    184     } else if (m_value == defaultString)
    185         m_pseudoType = PseudoDefault;
    186     else if (m_value == disabled)
    187         m_pseudoType = PseudoDisabled;
    188     else if (m_value == readOnly)
    189         m_pseudoType = PseudoReadOnly;
    190     else if (m_value == readWrite)
    191         m_pseudoType = PseudoReadWrite;
    192     else if (m_value == valid)
    193         m_pseudoType = PseudoValid;
    194     else if (m_value == invalid)
    195         m_pseudoType = PseudoInvalid;
    196     else if (m_value == drag || m_value == dragAlias)
    197         m_pseudoType = PseudoDrag;
    198     else if (m_value == enabled)
    199         m_pseudoType = PseudoEnabled;
    200     else if (m_value == empty)
    201         m_pseudoType = PseudoEmpty;
    202     else if (m_value == firstChild)
    203         m_pseudoType = PseudoFirstChild;
    204     else if (m_value == fullPageMedia)
    205         m_pseudoType = PseudoFullPageMedia;
    206     else
    207 #if ENABLE(DATALIST)
    208     if (m_value == inputListButton) {
    209         m_pseudoType = PseudoInputListButton;
    210         element = true;
    211     } else
    212 #endif
    213     if (m_value == inputPlaceholder) {
    214         m_pseudoType = PseudoInputPlaceholder;
    215         element = true;
    216     } else if (m_value == lastChild)
    217         m_pseudoType = PseudoLastChild;
    218     else if (m_value == lastOfType)
    219         m_pseudoType = PseudoLastOfType;
    220     else if (m_value == onlyChild)
    221         m_pseudoType = PseudoOnlyChild;
    222     else if (m_value == onlyOfType)
    223         m_pseudoType = PseudoOnlyOfType;
    224     else if (m_value == firstLetter) {
    225         m_pseudoType = PseudoFirstLetter;
    226         element = true;
    227         compat = true;
    228     } else if (m_value == firstLine) {
    229         m_pseudoType = PseudoFirstLine;
    230         element = true;
    231         compat = true;
    232     } else if (m_value == firstOfType)
    233         m_pseudoType = PseudoFirstOfType;
    234     else if (m_value == focus)
    235         m_pseudoType = PseudoFocus;
    236     else if (m_value == hover)
    237         m_pseudoType = PseudoHover;
    238     else if (m_value == indeterminate)
    239         m_pseudoType = PseudoIndeterminate;
    240     else if (m_value == innerSpinButton) {
    241         m_pseudoType = PseudoInnerSpinButton;
    242         element = true;
    243     } else if (m_value == link)
    244         m_pseudoType = PseudoLink;
    245     else if (m_value == lang)
    246         m_pseudoType = PseudoLang;
    247     else if (m_value == mediaControlsPanel) {
    248         m_pseudoType = PseudoMediaControlsPanel;
    249         element = true;
    250     } else if (m_value == mediaControlsMuteButton) {
    251         m_pseudoType = PseudoMediaControlsMuteButton;
    252         element = true;
    253     } else if (m_value == mediaControlsPlayButton) {
    254         m_pseudoType = PseudoMediaControlsPlayButton;
    255         element = true;
    256     } else if (m_value == mediaControlsCurrentTimeDisplay) {
    257         m_pseudoType = PseudoMediaControlsCurrentTimeDisplay;
    258         element = true;
    259     } else if (m_value == mediaControlsTimeRemainingDisplay) {
    260         m_pseudoType = PseudoMediaControlsTimeRemainingDisplay;
    261         element = true;
    262     } else if (m_value == mediaControlsTimeline) {
    263         m_pseudoType = PseudoMediaControlsTimeline;
    264         element = true;
    265     } else if (m_value == mediaControlsVolumeSlider) {
    266         m_pseudoType = PseudoMediaControlsVolumeSlider;
    267         element = true;
    268     } else if (m_value == mediaControlsSeekBackButton) {
    269         m_pseudoType = PseudoMediaControlsSeekBackButton;
    270         element = true;
    271     } else if (m_value == mediaControlsSeekForwardButton) {
    272         m_pseudoType = PseudoMediaControlsSeekForwardButton;
    273         element = true;
    274     } else if (m_value == mediaControlsRewindButton) {
    275         m_pseudoType = PseudoMediaControlsRewindButton;
    276         element = true;
    277     } else if (m_value == mediaControlsReturnToRealtimeButton) {
    278         m_pseudoType = PseudoMediaControlsReturnToRealtimeButton;
    279         element = true;
    280     } else if (m_value == mediaControlsToggleClosedCaptionsButton) {
    281         m_pseudoType = PseudoMediaControlsToggleClosedCaptions;
    282         element = true;
    283     } else if (m_value == mediaControlsStatusDisplay) {
    284         m_pseudoType = PseudoMediaControlsStatusDisplay;
    285         element = true;
    286     } else if (m_value == mediaControlsFullscreenButton) {
    287         m_pseudoType = PseudoMediaControlsFullscreenButton;
    288         element = true;
    289     } else if (m_value == mediaControlsTimelineContainer) {
    290         m_pseudoType = PseudoMediaControlsTimelineContainer;
    291         element = true;
    292     } else if (m_value == mediaControlsVolumeSliderContainer) {
    293         m_pseudoType = PseudoMediaControlsVolumeSliderContainer;
    294         element = true;
    295     } else if (m_value == notStr)
    296         m_pseudoType = PseudoNot;
    297     else if (m_value == nthChild)
    298         m_pseudoType = PseudoNthChild;
    299     else if (m_value == nthOfType)
    300         m_pseudoType = PseudoNthOfType;
    301     else if (m_value == nthLastChild)
    302         m_pseudoType = PseudoNthLastChild;
    303     else if (m_value == nthLastOfType)
    304         m_pseudoType = PseudoNthLastOfType;
    305     else if (m_value == outerSpinButton) {
    306         m_pseudoType = PseudoOuterSpinButton;
    307         element = true;
    308     } else if (m_value == root)
    309         m_pseudoType = PseudoRoot;
    310     else if (m_value == windowInactive)
    311         m_pseudoType = PseudoWindowInactive;
    312     else if (m_value == decrement)
    313         m_pseudoType = PseudoDecrement;
    314     else if (m_value == increment)
    315         m_pseudoType = PseudoIncrement;
    316     else if (m_value == start)
    317         m_pseudoType = PseudoStart;
    318     else if (m_value == end)
    319         m_pseudoType = PseudoEnd;
    320     else if (m_value == horizontal)
    321         m_pseudoType = PseudoHorizontal;
    322     else if (m_value == vertical)
    323         m_pseudoType = PseudoVertical;
    324     else if (m_value == doubleButton)
    325         m_pseudoType = PseudoDoubleButton;
    326     else if (m_value == singleButton)
    327         m_pseudoType = PseudoSingleButton;
    328     else if (m_value == noButton)
    329         m_pseudoType = PseudoNoButton;
    330     else if (m_value == optional)
    331         m_pseudoType = PseudoOptional;
    332     else if (m_value == required)
    333         m_pseudoType = PseudoRequired;
    334     else if (m_value == scrollbarCorner) {
    335         element = true;
    336         m_pseudoType = PseudoScrollbarCorner;
    337     } else if (m_value == resizer) {
    338         element = true;
    339         m_pseudoType = PseudoResizer;
    340     } else if (m_value == scrollbar) {
    341         element = true;
    342         m_pseudoType = PseudoScrollbar;
    343     } else if (m_value == scrollbarButton) {
    344         element = true;
    345         m_pseudoType = PseudoScrollbarButton;
    346     } else if (m_value == scrollbarCorner) {
    347         element = true;
    348         m_pseudoType = PseudoScrollbarCorner;
    349     } else if (m_value == scrollbarThumb) {
    350         element = true;
    351         m_pseudoType = PseudoScrollbarThumb;
    352     } else if (m_value == scrollbarTrack) {
    353         element = true;
    354         m_pseudoType = PseudoScrollbarTrack;
    355     } else if (m_value == scrollbarTrackPiece) {
    356         element = true;
    357         m_pseudoType = PseudoScrollbarTrackPiece;
    358     } else if (m_value == cornerPresent)
    359          m_pseudoType = PseudoCornerPresent;
    360     else if (m_value == searchCancelButton) {
    361         m_pseudoType = PseudoSearchCancelButton;
    362         element = true;
    363     } else if (m_value == searchDecoration) {
    364         m_pseudoType = PseudoSearchDecoration;
    365         element = true;
    366     } else if (m_value == searchResultsDecoration) {
    367         m_pseudoType = PseudoSearchResultsDecoration;
    368         element = true;
    369     } else if (m_value == searchResultsButton) {
    370         m_pseudoType = PseudoSearchResultsButton;
    371         element = true;
    372     }  else if (m_value == selection) {
    373         m_pseudoType = PseudoSelection;
    374         element = true;
    375     } else if (m_value == sliderThumb) {
    376         m_pseudoType = PseudoSliderThumb;
    377         element = true;
    378     } else if (m_value == target)
    379         m_pseudoType = PseudoTarget;
    380     else if (m_value == visited)
    381         m_pseudoType = PseudoVisited;
    382 
    383     if (m_match == PseudoClass && element) {
    384         if (!compat)
    385             m_pseudoType = PseudoUnknown;
    386         else
    387            m_match = PseudoElement;
    388     } else if (m_match == PseudoElement && !element)
    389         m_pseudoType = PseudoUnknown;
    390 }
    391 
    392 bool CSSSelector::operator==(const CSSSelector& other)
    393 {
    394     const CSSSelector* sel1 = this;
    395     const CSSSelector* sel2 = &other;
    396 
    397     while (sel1 && sel2) {
    398         if (sel1->m_tag != sel2->m_tag || sel1->attribute() != sel2->attribute() ||
    399              sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match ||
    400              sel1->m_value != sel2->m_value ||
    401              sel1->pseudoType() != sel2->pseudoType() ||
    402              sel1->argument() != sel2->argument())
    403             return false;
    404         sel1 = sel1->tagHistory();
    405         sel2 = sel2->tagHistory();
    406     }
    407 
    408     if (sel1 || sel2)
    409         return false;
    410 
    411     return true;
    412 }
    413 
    414 String CSSSelector::selectorText() const
    415 {
    416     String str = "";
    417 
    418     const AtomicString& prefix = m_tag.prefix();
    419     const AtomicString& localName = m_tag.localName();
    420     if (m_match == CSSSelector::None || !prefix.isNull() || localName != starAtom) {
    421         if (prefix.isNull())
    422             str = localName;
    423         else
    424             str = prefix + "|" + localName;
    425     }
    426 
    427     const CSSSelector* cs = this;
    428     while (true) {
    429         if (cs->m_match == CSSSelector::Id) {
    430             str += "#";
    431             str += cs->m_value;
    432         } else if (cs->m_match == CSSSelector::Class) {
    433             str += ".";
    434             str += cs->m_value;
    435         } else if (cs->m_match == CSSSelector::PseudoClass) {
    436             str += ":";
    437             str += cs->m_value;
    438             if (cs->pseudoType() == PseudoNot) {
    439                 if (CSSSelector* subSel = cs->simpleSelector())
    440                     str += subSel->selectorText();
    441                 str += ")";
    442             } else if (cs->pseudoType() == PseudoLang
    443                     || cs->pseudoType() == PseudoNthChild
    444                     || cs->pseudoType() == PseudoNthLastChild
    445                     || cs->pseudoType() == PseudoNthOfType
    446                     || cs->pseudoType() == PseudoNthLastOfType) {
    447                 str += cs->argument();
    448                 str += ")";
    449             }
    450         } else if (cs->m_match == CSSSelector::PseudoElement) {
    451             str += "::";
    452             str += cs->m_value;
    453         } else if (cs->hasAttribute()) {
    454             str += "[";
    455             const AtomicString& prefix = cs->attribute().prefix();
    456             if (!prefix.isNull())
    457                 str += prefix + "|";
    458             str += cs->attribute().localName();
    459             switch (cs->m_match) {
    460                 case CSSSelector::Exact:
    461                     str += "=";
    462                     break;
    463                 case CSSSelector::Set:
    464                     // set has no operator or value, just the attrName
    465                     str += "]";
    466                     break;
    467                 case CSSSelector::List:
    468                     str += "~=";
    469                     break;
    470                 case CSSSelector::Hyphen:
    471                     str += "|=";
    472                     break;
    473                 case CSSSelector::Begin:
    474                     str += "^=";
    475                     break;
    476                 case CSSSelector::End:
    477                     str += "$=";
    478                     break;
    479                 case CSSSelector::Contain:
    480                     str += "*=";
    481                     break;
    482                 default:
    483                     break;
    484             }
    485             if (cs->m_match != CSSSelector::Set) {
    486                 str += "\"";
    487                 str += cs->m_value;
    488                 str += "\"]";
    489             }
    490         }
    491         if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory())
    492             break;
    493         cs = cs->tagHistory();
    494     }
    495 
    496     if (CSSSelector* tagHistory = cs->tagHistory()) {
    497         String tagHistoryText = tagHistory->selectorText();
    498         if (cs->relation() == CSSSelector::DirectAdjacent)
    499             str = tagHistoryText + " + " + str;
    500         else if (cs->relation() == CSSSelector::IndirectAdjacent)
    501             str = tagHistoryText + " ~ " + str;
    502         else if (cs->relation() == CSSSelector::Child)
    503             str = tagHistoryText + " > " + str;
    504         else
    505             // Descendant
    506             str = tagHistoryText + " " + str;
    507     }
    508 
    509     return str;
    510 }
    511 
    512 void CSSSelector::setTagHistory(CSSSelector* tagHistory)
    513 {
    514     if (m_hasRareData)
    515         m_data.m_rareData->m_tagHistory.set(tagHistory);
    516     else
    517         m_data.m_tagHistory = tagHistory;
    518 }
    519 
    520 const QualifiedName& CSSSelector::attribute() const
    521 {
    522     switch (m_match) {
    523     case Id:
    524         return idAttr;
    525     case Class:
    526         return classAttr;
    527     default:
    528         return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName();
    529     }
    530 }
    531 
    532 void CSSSelector::setAttribute(const QualifiedName& value)
    533 {
    534     createRareData();
    535     m_data.m_rareData->m_attribute = value;
    536 }
    537 
    538 void CSSSelector::setArgument(const AtomicString& value)
    539 {
    540     createRareData();
    541     m_data.m_rareData->m_argument = value;
    542 }
    543 
    544 void CSSSelector::setSimpleSelector(CSSSelector* value)
    545 {
    546     createRareData();
    547     m_data.m_rareData->m_simpleSelector.set(value);
    548 }
    549 
    550 bool CSSSelector::parseNth()
    551 {
    552     if (!m_hasRareData)
    553         return false;
    554     if (m_parsedNth)
    555         return true;
    556     m_parsedNth = m_data.m_rareData->parseNth();
    557     return m_parsedNth;
    558 }
    559 
    560 bool CSSSelector::matchNth(int count)
    561 {
    562     ASSERT(m_hasRareData);
    563     return m_data.m_rareData->matchNth(count);
    564 }
    565 
    566 // a helper function for parsing nth-arguments
    567 bool CSSSelector::RareData::parseNth()
    568 {
    569     const String& argument = m_argument;
    570 
    571     if (argument.isEmpty())
    572         return false;
    573 
    574     m_a = 0;
    575     m_b = 0;
    576     if (argument == "odd") {
    577         m_a = 2;
    578         m_b = 1;
    579     } else if (argument == "even") {
    580         m_a = 2;
    581         m_b = 0;
    582     } else {
    583         int n = argument.find('n');
    584         if (n != -1) {
    585             if (argument[0] == '-') {
    586                 if (n == 1)
    587                     m_a = -1; // -n == -1n
    588                 else
    589                     m_a = argument.substring(0, n).toInt();
    590             } else if (!n)
    591                 m_a = 1; // n == 1n
    592             else
    593                 m_a = argument.substring(0, n).toInt();
    594 
    595             int p = argument.find('+', n);
    596             if (p != -1)
    597                 m_b = argument.substring(p + 1, argument.length() - p - 1).toInt();
    598             else {
    599                 p = argument.find('-', n);
    600                 m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt();
    601             }
    602         } else
    603             m_b = argument.toInt();
    604     }
    605     return true;
    606 }
    607 
    608 // a helper function for checking nth-arguments
    609 bool CSSSelector::RareData::matchNth(int count)
    610 {
    611     if (!m_a)
    612         return count == m_b;
    613     else if (m_a > 0) {
    614         if (count < m_b)
    615             return false;
    616         return (count - m_b) % m_a == 0;
    617     } else {
    618         if (count > m_b)
    619             return false;
    620         return (m_b - count) % (-m_a) == 0;
    621     }
    622 }
    623 
    624 } // namespace WebCore
    625