WebCore/css/CSSSelector.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/css/CSSSelector.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,983 @@
+/*
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ *               1999 Waldo Bastian (bastian@kde.org)
+ *               2001 Andreas Schlapbach (schlpbch@iam.unibe.ch)
+ *               2001-2003 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2002, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 David Smith (catfish.man@gmail.com)
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "CSSSelector.h"
+
+#include "CSSOMUtils.h"
+#include "HTMLNames.h"
+#include <wtf/Assertions.h>
+#include <wtf/HashMap.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+    
+using namespace HTMLNames;
+
+// A helper class to hold CSSSelectors.
+class CSSSelectorBag : public Noncopyable {
+public:
+    ~CSSSelectorBag()
+    {
+        deleteAllValues(m_stack);
+    }
+
+    bool isEmpty() const
+    {
+        return m_stack.isEmpty();
+    }
+
+    void append(PassOwnPtr<CSSSelector> selector)
+    {
+        if (selector)
+            m_stack.append(selector.leakPtr());
+    }
+
+    PassOwnPtr<CSSSelector> takeAny()
+    {
+        ASSERT(!isEmpty());
+        OwnPtr<CSSSelector> selector = adoptPtr(m_stack.last());
+        m_stack.removeLast();
+        return selector.release();
+    }
+
+private:
+    Vector<CSSSelector*, 16> m_stack;
+};
+
+CSSSelector::~CSSSelector()
+{
+    // We should avoid a recursive destructor call, which causes stack overflow
+    // if CSS Selectors are deeply nested.
+
+    // Early exit if we have already processed the children of this selector.
+    if (m_hasRareData) {
+        if (!m_data.m_rareData)
+            return;
+    } else if (!m_data.m_tagHistory)
+        return;
+
+    CSSSelectorBag selectorsToBeDeleted;
+    if (m_hasRareData) {
+        selectorsToBeDeleted.append(m_data.m_rareData->m_tagHistory.release());
+        selectorsToBeDeleted.append(m_data.m_rareData->m_simpleSelector.release());
+        delete m_data.m_rareData;
+    } else
+        selectorsToBeDeleted.append(adoptPtr(m_data.m_tagHistory));
+
+    // Traverse the tree of CSSSelector and delete each CSSSelector iteratively.
+    while (!selectorsToBeDeleted.isEmpty()) {
+        OwnPtr<CSSSelector> selector(selectorsToBeDeleted.takeAny());
+        ASSERT(selector);
+        if (selector->m_hasRareData) {
+            ASSERT(selector->m_data.m_rareData);
+            selectorsToBeDeleted.append(selector->m_data.m_rareData->m_tagHistory.release());
+            selectorsToBeDeleted.append(selector->m_data.m_rareData->m_simpleSelector.release());
+            delete selector->m_data.m_rareData;
+            // Clear the pointer so that a destructor of the selector, which is
+            // about to be called, can know the children are already processed.
+            selector->m_data.m_rareData = 0;
+        } else {
+            selectorsToBeDeleted.append(adoptPtr(selector->m_data.m_tagHistory));
+            // Clear the pointer for the same reason.
+            selector->m_data.m_tagHistory = 0;
+        }
+    }
+}
+
+unsigned int CSSSelector::specificity()
+{
+    if (m_isForPage)
+        return specificityForPage();
+
+    // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function
+    // isn't quite correct.
+    int s = (m_tag.localName() == starAtom ? 0 : 1);
+    switch (m_match) {
+        case Id:
+            s += 0x10000;
+            break;
+        case Exact:
+        case Class:
+        case Set:
+        case List:
+        case Hyphen:
+        case PseudoClass:
+        case PseudoElement:
+        case Contain:
+        case Begin:
+        case End:
+            s += 0x100;
+        case None:
+            break;
+    }
+
+    // FIXME: Avoid recursive calls to prevent possible stack overflow.
+    if (CSSSelector* tagHistory = this->tagHistory())
+        s += tagHistory->specificity();
+
+    // make sure it doesn't overflow
+    return s & 0xffffff;
+}
+
+unsigned CSSSelector::specificityForPage()
+{
+    // See http://dev.w3.org/csswg/css3-page/#cascading-and-page-context
+    unsigned s = (m_tag.localName() == starAtom ? 0 : 4);
+
+    switch (pseudoType()) {
+    case PseudoFirstPage:
+        s += 2;
+        break;
+    case PseudoLeftPage:
+    case PseudoRightPage:
+        s += 1;
+        break;
+    case PseudoNotParsed:
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+    }
+    return s;
+}
+
+PseudoId CSSSelector::pseudoId(PseudoType type)
+{
+    switch (type) {
+    case PseudoFirstLine:
+        return FIRST_LINE;
+    case PseudoFirstLetter:
+        return FIRST_LETTER;
+    case PseudoSelection:
+        return SELECTION;
+    case PseudoBefore:
+        return BEFORE;
+    case PseudoAfter:
+        return AFTER;
+    case PseudoFileUploadButton:
+        return FILE_UPLOAD_BUTTON;
+    case PseudoInputPlaceholder:
+        return INPUT_PLACEHOLDER;
+#if ENABLE(INPUT_SPEECH)
+    case PseudoInputSpeechButton:
+        return INPUT_SPEECH_BUTTON;
+#endif
+    case PseudoSliderThumb:
+        return SLIDER_THUMB;
+    case PseudoSearchCancelButton:
+        return SEARCH_CANCEL_BUTTON;
+    case PseudoSearchDecoration:
+        return SEARCH_DECORATION;
+    case PseudoSearchResultsDecoration:
+        return SEARCH_RESULTS_DECORATION;
+    case PseudoSearchResultsButton:
+        return SEARCH_RESULTS_BUTTON;
+    case PseudoMediaControlsPanel:
+        return MEDIA_CONTROLS_PANEL;
+    case PseudoMediaControlsMuteButton:
+        return MEDIA_CONTROLS_MUTE_BUTTON;
+    case PseudoMediaControlsPlayButton:
+        return MEDIA_CONTROLS_PLAY_BUTTON;
+    case PseudoMediaControlsTimelineContainer:
+        return MEDIA_CONTROLS_TIMELINE_CONTAINER;
+    case PseudoMediaControlsVolumeSliderContainer:
+        return MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER;
+    case PseudoMediaControlsCurrentTimeDisplay:
+        return MEDIA_CONTROLS_CURRENT_TIME_DISPLAY;
+    case PseudoMediaControlsTimeRemainingDisplay:
+        return MEDIA_CONTROLS_TIME_REMAINING_DISPLAY;
+    case PseudoMediaControlsTimeline:
+        return MEDIA_CONTROLS_TIMELINE;
+    case PseudoMediaControlsVolumeSlider:
+        return MEDIA_CONTROLS_VOLUME_SLIDER;
+    case PseudoMediaControlsVolumeSliderMuteButton:
+        return MEDIA_CONTROLS_VOLUME_SLIDER_MUTE_BUTTON;
+    case PseudoMediaControlsSeekBackButton:
+        return MEDIA_CONTROLS_SEEK_BACK_BUTTON;
+    case PseudoMediaControlsSeekForwardButton:
+        return MEDIA_CONTROLS_SEEK_FORWARD_BUTTON;
+    case PseudoMediaControlsRewindButton:
+        return MEDIA_CONTROLS_REWIND_BUTTON;
+    case PseudoMediaControlsReturnToRealtimeButton:
+        return MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON;
+    case PseudoMediaControlsToggleClosedCaptions:
+        return MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON;
+    case PseudoMediaControlsStatusDisplay:
+        return MEDIA_CONTROLS_STATUS_DISPLAY;
+    case PseudoMediaControlsFullscreenButton:
+        return MEDIA_CONTROLS_FULLSCREEN_BUTTON;
+    case PseudoScrollbar:
+        return SCROLLBAR;
+    case PseudoScrollbarButton:
+        return SCROLLBAR_BUTTON;
+    case PseudoScrollbarCorner:
+        return SCROLLBAR_CORNER;
+    case PseudoScrollbarThumb:
+        return SCROLLBAR_THUMB;
+    case PseudoScrollbarTrack:
+        return SCROLLBAR_TRACK;
+    case PseudoScrollbarTrackPiece:
+        return SCROLLBAR_TRACK_PIECE;
+    case PseudoResizer:
+        return RESIZER;
+    case PseudoInnerSpinButton:
+        return INNER_SPIN_BUTTON;
+    case PseudoOuterSpinButton:
+        return OUTER_SPIN_BUTTON;
+    case PseudoProgressBarValue:
+#if ENABLE(PROGRESS_TAG)
+        return PROGRESS_BAR_VALUE;
+#else
+        ASSERT_NOT_REACHED();
+        return NOPSEUDO;
+#endif
+
+#if ENABLE(METER_TAG)
+    case PseudoMeterHorizontalBar:
+        return METER_HORIZONTAL_BAR;
+    case PseudoMeterHorizontalOptimum:
+        return METER_HORIZONTAL_OPTIMUM;
+    case PseudoMeterHorizontalSuboptimal:
+        return METER_HORIZONTAL_SUBOPTIMAL;
+    case PseudoMeterHorizontalEvenLessGood:
+        return METER_HORIZONTAL_EVEN_LESS_GOOD;
+    case PseudoMeterVerticalBar:
+        return METER_VERTICAL_BAR;
+    case PseudoMeterVerticalOptimum:
+        return METER_VERTICAL_OPTIMUM;
+    case PseudoMeterVerticalSuboptimal:
+        return METER_VERTICAL_SUBOPTIMAL;
+    case PseudoMeterVerticalEvenLessGood:
+        return METER_VERTICAL_EVEN_LESS_GOOD;
+#else
+    case PseudoMeterHorizontalBar:
+    case PseudoMeterHorizontalOptimum:
+    case PseudoMeterHorizontalSuboptimal:
+    case PseudoMeterHorizontalEvenLessGood:
+    case PseudoMeterVerticalBar:
+    case PseudoMeterVerticalOptimum:
+    case PseudoMeterVerticalSuboptimal:
+    case PseudoMeterVerticalEvenLessGood:
+        ASSERT_NOT_REACHED();
+        return NOPSEUDO;
+#endif
+
+    case PseudoInputListButton:
+#if ENABLE(DATALIST)
+        return INPUT_LIST_BUTTON;
+#endif
+    case PseudoUnknown:
+    case PseudoEmpty:
+    case PseudoFirstChild:
+    case PseudoFirstOfType:
+    case PseudoLastChild:
+    case PseudoLastOfType:
+    case PseudoOnlyChild:
+    case PseudoOnlyOfType:
+    case PseudoNthChild:
+    case PseudoNthOfType:
+    case PseudoNthLastChild:
+    case PseudoNthLastOfType:
+    case PseudoLink:
+    case PseudoVisited:
+    case PseudoAnyLink:
+    case PseudoAutofill:
+    case PseudoHover:
+    case PseudoDrag:
+    case PseudoFocus:
+    case PseudoActive:
+    case PseudoChecked:
+    case PseudoEnabled:
+    case PseudoFullPageMedia:
+    case PseudoDefault:
+    case PseudoDisabled:
+    case PseudoOptional:
+    case PseudoRequired:
+    case PseudoReadOnly:
+    case PseudoReadWrite:
+    case PseudoValid:
+    case PseudoInvalid:
+    case PseudoIndeterminate:
+    case PseudoTarget:
+    case PseudoLang:
+    case PseudoNot:
+    case PseudoRoot:
+    case PseudoScrollbarBack:
+    case PseudoScrollbarForward:
+    case PseudoWindowInactive:
+    case PseudoCornerPresent:
+    case PseudoDecrement:
+    case PseudoIncrement:
+    case PseudoHorizontal:
+    case PseudoVertical:
+    case PseudoStart:
+    case PseudoEnd:
+    case PseudoDoubleButton:
+    case PseudoSingleButton:
+    case PseudoNoButton:
+    case PseudoFirstPage:
+    case PseudoLeftPage:
+    case PseudoRightPage:
+        return NOPSEUDO;
+    case PseudoNotParsed:
+        ASSERT_NOT_REACHED();
+        return NOPSEUDO;
+    }
+
+    ASSERT_NOT_REACHED();
+    return NOPSEUDO;
+}
+
+static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoTypeMap()
+{
+    DEFINE_STATIC_LOCAL(AtomicString, active, ("active"));
+    DEFINE_STATIC_LOCAL(AtomicString, after, ("after"));
+    DEFINE_STATIC_LOCAL(AtomicString, anyLink, ("-webkit-any-link"));
+    DEFINE_STATIC_LOCAL(AtomicString, autofill, ("-webkit-autofill"));
+    DEFINE_STATIC_LOCAL(AtomicString, before, ("before"));
+    DEFINE_STATIC_LOCAL(AtomicString, checked, ("checked"));
+    DEFINE_STATIC_LOCAL(AtomicString, fileUploadButton, ("-webkit-file-upload-button"));
+#if ENABLE(INPUT_SPEECH)
+    DEFINE_STATIC_LOCAL(AtomicString, inputSpeechButton, ("-webkit-input-speech-button"));
+#endif
+    DEFINE_STATIC_LOCAL(AtomicString, defaultString, ("default"));
+    DEFINE_STATIC_LOCAL(AtomicString, disabled, ("disabled"));
+    DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only"));
+    DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write"));
+    DEFINE_STATIC_LOCAL(AtomicString, valid, ("valid"));
+    DEFINE_STATIC_LOCAL(AtomicString, invalid, ("invalid"));
+    DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag"));
+    DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag")); // was documented with this name in Apple documentation, so keep an alia
+    DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty"));
+    DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled"));
+    DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child"));
+    DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter"));
+    DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line"));
+    DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type"));
+    DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media"));
+    DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child("));
+    DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type("));
+    DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child("));
+    DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type("));
+    DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus"));
+    DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover"));
+    DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate"));
+    DEFINE_STATIC_LOCAL(AtomicString, innerSpinButton, ("-webkit-inner-spin-button"));
+#if ENABLE(DATALIST)
+    DEFINE_STATIC_LOCAL(AtomicString, inputListButton, ("-webkit-input-list-button"));
+#endif
+    DEFINE_STATIC_LOCAL(AtomicString, inputPlaceholder, ("-webkit-input-placeholder"));
+    DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child"));
+    DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type"));
+    DEFINE_STATIC_LOCAL(AtomicString, link, ("link"));
+    DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang("));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsPanel, ("-webkit-media-controls-panel"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsMuteButton, ("-webkit-media-controls-mute-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsPlayButton, ("-webkit-media-controls-play-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimeline, ("-webkit-media-controls-timeline"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsVolumeSlider, ("-webkit-media-controls-volume-slider"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsVolumeSliderMuteButton, ("-webkit-media-controls-volume-slider-mute-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsSeekBackButton, ("-webkit-media-controls-seek-back-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsSeekForwardButton, ("-webkit-media-controls-seek-forward-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsRewindButton, ("-webkit-media-controls-rewind-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsReturnToRealtimeButton, ("-webkit-media-controls-return-to-realtime-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsToggleClosedCaptionsButton, ("-webkit-media-controls-toggle-closed-captions-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsStatusDisplay, ("-webkit-media-controls-status-display"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsFullscreenButton, ("-webkit-media-controls-fullscreen-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimelineContainer, ("-webkit-media-controls-timeline-container"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsVolumeSliderContainer, ("-webkit-media-controls-volume-slider-container"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsCurrentTimeDisplay, ("-webkit-media-controls-current-time-display"));
+    DEFINE_STATIC_LOCAL(AtomicString, mediaControlsTimeRemainingDisplay, ("-webkit-media-controls-time-remaining-display"));
+    DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not("));
+    DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child"));
+    DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type"));
+    DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional"));
+    DEFINE_STATIC_LOCAL(AtomicString, outerSpinButton, ("-webkit-outer-spin-button"));
+#if ENABLE(PROGRESS_TAG)
+    DEFINE_STATIC_LOCAL(AtomicString, progressBarValue, ("-webkit-progress-bar-value"));
+#endif
+
+#if ENABLE(METER_TAG)
+    DEFINE_STATIC_LOCAL(AtomicString, meterHorizontalBar, ("-webkit-meter-horizontal-bar"));
+    DEFINE_STATIC_LOCAL(AtomicString, meterHorizontalOptimumValue, ("-webkit-meter-horizontal-optimum-value"));
+    DEFINE_STATIC_LOCAL(AtomicString, meterHorizontalSuboptimalValue, ("-webkit-meter-horizontal-suboptimal-value"));
+    DEFINE_STATIC_LOCAL(AtomicString, meterHorizontalEvenLessGoodValue, ("-webkit-meter-horizontal-even-less-good-value"));
+    DEFINE_STATIC_LOCAL(AtomicString, meterVerticalBar, ("-webkit-meter-vertical-bar"));
+    DEFINE_STATIC_LOCAL(AtomicString, meterVerticalOptimumValue, ("-webkit-meter-vertical-optimum-value"));
+    DEFINE_STATIC_LOCAL(AtomicString, meterVerticalSuboptimalValue, ("-webkit-meter-vertical-suboptimal-value"));
+    DEFINE_STATIC_LOCAL(AtomicString, meterVerticalEvenLessGoodValue, ("-webkit-meter-vertical-even-less-good-value"));
+#endif
+
+    DEFINE_STATIC_LOCAL(AtomicString, required, ("required"));
+    DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer"));
+    DEFINE_STATIC_LOCAL(AtomicString, root, ("root"));
+    DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar"));
+    DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner"));
+    DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb"));
+    DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track"));
+    DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece"));
+    DEFINE_STATIC_LOCAL(AtomicString, searchCancelButton, ("-webkit-search-cancel-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, searchDecoration, ("-webkit-search-decoration"));
+    DEFINE_STATIC_LOCAL(AtomicString, searchResultsDecoration, ("-webkit-search-results-decoration"));
+    DEFINE_STATIC_LOCAL(AtomicString, searchResultsButton, ("-webkit-search-results-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection"));
+    DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-thumb"));
+    DEFINE_STATIC_LOCAL(AtomicString, target, ("target"));
+    DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited"));
+    DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive"));
+    DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement"));
+    DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment"));
+    DEFINE_STATIC_LOCAL(AtomicString, start, ("start"));
+    DEFINE_STATIC_LOCAL(AtomicString, end, ("end"));
+    DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal"));
+    DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical"));
+    DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button"));
+    DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present"));
+    // Paged Media pseudo-classes
+    DEFINE_STATIC_LOCAL(AtomicString, firstPage, ("first"));
+    DEFINE_STATIC_LOCAL(AtomicString, leftPage, ("left"));
+    DEFINE_STATIC_LOCAL(AtomicString, rightPage, ("right"));
+
+    static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = 0;
+    if (!nameToPseudoType) {
+        nameToPseudoType = new HashMap<AtomicStringImpl*, CSSSelector::PseudoType>;
+        nameToPseudoType->set(active.impl(), CSSSelector::PseudoActive);
+        nameToPseudoType->set(after.impl(), CSSSelector::PseudoAfter);
+        nameToPseudoType->set(anyLink.impl(), CSSSelector::PseudoAnyLink);
+        nameToPseudoType->set(autofill.impl(), CSSSelector::PseudoAutofill);
+        nameToPseudoType->set(before.impl(), CSSSelector::PseudoBefore);
+        nameToPseudoType->set(checked.impl(), CSSSelector::PseudoChecked);
+        nameToPseudoType->set(fileUploadButton.impl(), CSSSelector::PseudoFileUploadButton);
+#if ENABLE(INPUT_SPEECH)
+        nameToPseudoType->set(inputSpeechButton.impl(), CSSSelector::PseudoInputSpeechButton);
+#endif
+        nameToPseudoType->set(defaultString.impl(), CSSSelector::PseudoDefault);
+        nameToPseudoType->set(disabled.impl(), CSSSelector::PseudoDisabled);
+        nameToPseudoType->set(readOnly.impl(), CSSSelector::PseudoReadOnly);
+        nameToPseudoType->set(readWrite.impl(), CSSSelector::PseudoReadWrite);
+        nameToPseudoType->set(valid.impl(), CSSSelector::PseudoValid);
+        nameToPseudoType->set(invalid.impl(), CSSSelector::PseudoInvalid);
+        nameToPseudoType->set(drag.impl(), CSSSelector::PseudoDrag);
+        nameToPseudoType->set(dragAlias.impl(), CSSSelector::PseudoDrag);
+        nameToPseudoType->set(enabled.impl(), CSSSelector::PseudoEnabled);
+        nameToPseudoType->set(empty.impl(), CSSSelector::PseudoEmpty);
+        nameToPseudoType->set(firstChild.impl(), CSSSelector::PseudoFirstChild);
+        nameToPseudoType->set(fullPageMedia.impl(), CSSSelector::PseudoFullPageMedia);
+#if ENABLE(DATALIST)
+        nameToPseudoType->set(inputListButton.impl(), CSSSelector::PseudoInputListButton);
+#endif
+        nameToPseudoType->set(inputPlaceholder.impl(), CSSSelector::PseudoInputPlaceholder);
+        nameToPseudoType->set(lastChild.impl(), CSSSelector::PseudoLastChild);
+        nameToPseudoType->set(lastOfType.impl(), CSSSelector::PseudoLastOfType);
+        nameToPseudoType->set(onlyChild.impl(), CSSSelector::PseudoOnlyChild);
+        nameToPseudoType->set(onlyOfType.impl(), CSSSelector::PseudoOnlyOfType);
+        nameToPseudoType->set(firstLetter.impl(), CSSSelector::PseudoFirstLetter);
+        nameToPseudoType->set(firstLine.impl(), CSSSelector::PseudoFirstLine);
+        nameToPseudoType->set(firstOfType.impl(), CSSSelector::PseudoFirstOfType);
+        nameToPseudoType->set(focus.impl(), CSSSelector::PseudoFocus);
+        nameToPseudoType->set(hover.impl(), CSSSelector::PseudoHover);
+        nameToPseudoType->set(indeterminate.impl(), CSSSelector::PseudoIndeterminate);
+        nameToPseudoType->set(innerSpinButton.impl(), CSSSelector::PseudoInnerSpinButton);
+        nameToPseudoType->set(link.impl(), CSSSelector::PseudoLink);
+        nameToPseudoType->set(lang.impl(), CSSSelector::PseudoLang);
+        nameToPseudoType->set(mediaControlsPanel.impl(), CSSSelector::PseudoMediaControlsPanel);
+        nameToPseudoType->set(mediaControlsMuteButton.impl(), CSSSelector::PseudoMediaControlsMuteButton);
+        nameToPseudoType->set(mediaControlsPlayButton.impl(), CSSSelector::PseudoMediaControlsPlayButton);
+        nameToPseudoType->set(mediaControlsCurrentTimeDisplay.impl(), CSSSelector::PseudoMediaControlsCurrentTimeDisplay);
+        nameToPseudoType->set(mediaControlsTimeRemainingDisplay.impl(), CSSSelector::PseudoMediaControlsTimeRemainingDisplay);
+        nameToPseudoType->set(mediaControlsTimeline.impl(), CSSSelector::PseudoMediaControlsTimeline);
+        nameToPseudoType->set(mediaControlsVolumeSlider.impl(), CSSSelector::PseudoMediaControlsVolumeSlider);
+        nameToPseudoType->set(mediaControlsVolumeSliderMuteButton.impl(), CSSSelector::PseudoMediaControlsVolumeSliderMuteButton);
+        nameToPseudoType->set(mediaControlsSeekBackButton.impl(), CSSSelector::PseudoMediaControlsSeekBackButton);
+        nameToPseudoType->set(mediaControlsSeekForwardButton.impl(), CSSSelector::PseudoMediaControlsSeekForwardButton);
+        nameToPseudoType->set(mediaControlsRewindButton.impl(), CSSSelector::PseudoMediaControlsRewindButton);
+        nameToPseudoType->set(mediaControlsReturnToRealtimeButton.impl(), CSSSelector::PseudoMediaControlsReturnToRealtimeButton);
+        nameToPseudoType->set(mediaControlsToggleClosedCaptionsButton.impl(), CSSSelector::PseudoMediaControlsToggleClosedCaptions);
+        nameToPseudoType->set(mediaControlsStatusDisplay.impl(), CSSSelector::PseudoMediaControlsStatusDisplay);
+        nameToPseudoType->set(mediaControlsFullscreenButton.impl(), CSSSelector::PseudoMediaControlsFullscreenButton);
+        nameToPseudoType->set(mediaControlsTimelineContainer.impl(), CSSSelector::PseudoMediaControlsTimelineContainer);
+        nameToPseudoType->set(mediaControlsVolumeSliderContainer.impl(), CSSSelector::PseudoMediaControlsVolumeSliderContainer);
+        nameToPseudoType->set(notStr.impl(), CSSSelector::PseudoNot);
+        nameToPseudoType->set(nthChild.impl(), CSSSelector::PseudoNthChild);
+        nameToPseudoType->set(nthOfType.impl(), CSSSelector::PseudoNthOfType);
+        nameToPseudoType->set(nthLastChild.impl(), CSSSelector::PseudoNthLastChild);
+        nameToPseudoType->set(nthLastOfType.impl(), CSSSelector::PseudoNthLastOfType);
+        nameToPseudoType->set(outerSpinButton.impl(), CSSSelector::PseudoOuterSpinButton);
+#if ENABLE(PROGRESS_TAG)
+        nameToPseudoType->set(progressBarValue.impl(), CSSSelector::PseudoProgressBarValue);
+#endif
+#if ENABLE(METER_TAG)
+        nameToPseudoType->set(meterHorizontalBar.impl(), CSSSelector::PseudoMeterHorizontalBar);
+        nameToPseudoType->set(meterHorizontalOptimumValue.impl(), CSSSelector::PseudoMeterHorizontalOptimum);
+        nameToPseudoType->set(meterHorizontalSuboptimalValue.impl(), CSSSelector::PseudoMeterHorizontalSuboptimal);
+        nameToPseudoType->set(meterHorizontalEvenLessGoodValue.impl(), CSSSelector::PseudoMeterHorizontalEvenLessGood);
+        nameToPseudoType->set(meterVerticalBar.impl(), CSSSelector::PseudoMeterVerticalBar);
+        nameToPseudoType->set(meterVerticalOptimumValue.impl(), CSSSelector::PseudoMeterVerticalOptimum);
+        nameToPseudoType->set(meterVerticalSuboptimalValue.impl(), CSSSelector::PseudoMeterVerticalSuboptimal);
+        nameToPseudoType->set(meterVerticalEvenLessGoodValue.impl(), CSSSelector::PseudoMeterVerticalEvenLessGood);
+#endif
+        nameToPseudoType->set(root.impl(), CSSSelector::PseudoRoot);
+        nameToPseudoType->set(windowInactive.impl(), CSSSelector::PseudoWindowInactive);
+        nameToPseudoType->set(decrement.impl(), CSSSelector::PseudoDecrement);
+        nameToPseudoType->set(increment.impl(), CSSSelector::PseudoIncrement);
+        nameToPseudoType->set(start.impl(), CSSSelector::PseudoStart);
+        nameToPseudoType->set(end.impl(), CSSSelector::PseudoEnd);
+        nameToPseudoType->set(horizontal.impl(), CSSSelector::PseudoHorizontal);
+        nameToPseudoType->set(vertical.impl(), CSSSelector::PseudoVertical);
+        nameToPseudoType->set(doubleButton.impl(), CSSSelector::PseudoDoubleButton);
+        nameToPseudoType->set(singleButton.impl(), CSSSelector::PseudoSingleButton);
+        nameToPseudoType->set(noButton.impl(), CSSSelector::PseudoNoButton);
+        nameToPseudoType->set(optional.impl(), CSSSelector::PseudoOptional);
+        nameToPseudoType->set(required.impl(), CSSSelector::PseudoRequired);
+        nameToPseudoType->set(resizer.impl(), CSSSelector::PseudoResizer);
+        nameToPseudoType->set(scrollbar.impl(), CSSSelector::PseudoScrollbar);
+        nameToPseudoType->set(scrollbarButton.impl(), CSSSelector::PseudoScrollbarButton);
+        nameToPseudoType->set(scrollbarCorner.impl(), CSSSelector::PseudoScrollbarCorner);
+        nameToPseudoType->set(scrollbarThumb.impl(), CSSSelector::PseudoScrollbarThumb);
+        nameToPseudoType->set(scrollbarTrack.impl(), CSSSelector::PseudoScrollbarTrack);
+        nameToPseudoType->set(scrollbarTrackPiece.impl(), CSSSelector::PseudoScrollbarTrackPiece);
+        nameToPseudoType->set(cornerPresent.impl(), CSSSelector::PseudoCornerPresent);
+        nameToPseudoType->set(searchCancelButton.impl(), CSSSelector::PseudoSearchCancelButton);
+        nameToPseudoType->set(searchDecoration.impl(), CSSSelector::PseudoSearchDecoration);
+        nameToPseudoType->set(searchResultsDecoration.impl(), CSSSelector::PseudoSearchResultsDecoration);
+        nameToPseudoType->set(searchResultsButton.impl(), CSSSelector::PseudoSearchResultsButton);
+        nameToPseudoType->set(selection.impl(), CSSSelector::PseudoSelection);
+        nameToPseudoType->set(sliderThumb.impl(), CSSSelector::PseudoSliderThumb);
+        nameToPseudoType->set(target.impl(), CSSSelector::PseudoTarget);
+        nameToPseudoType->set(visited.impl(), CSSSelector::PseudoVisited);
+        nameToPseudoType->set(firstPage.impl(), CSSSelector::PseudoFirstPage);
+        nameToPseudoType->set(leftPage.impl(), CSSSelector::PseudoLeftPage);
+        nameToPseudoType->set(rightPage.impl(), CSSSelector::PseudoRightPage);
+    }
+    return nameToPseudoType;
+}
+
+CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name)
+{
+    if (name.isNull())
+        return PseudoUnknown;
+    HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = nameToPseudoTypeMap();
+    HashMap<AtomicStringImpl*, CSSSelector::PseudoType>::iterator slot = nameToPseudoType->find(name.impl());
+    return slot == nameToPseudoType->end() ? PseudoUnknown : slot->second;
+}
+
+void CSSSelector::extractPseudoType() const
+{
+    if (m_match != PseudoClass && m_match != PseudoElement && m_match != PagePseudoClass)
+        return;
+
+    m_pseudoType = parsePseudoType(m_value);
+
+    bool element = false; // pseudo-element
+    bool compat = false; // single colon compatbility mode
+    bool isPagePseudoClass = false; // Page pseudo-class
+
+    switch (m_pseudoType) {
+    case PseudoAfter:
+    case PseudoBefore:
+    case PseudoFirstLetter:
+    case PseudoFirstLine:
+        compat = true;
+    case PseudoFileUploadButton:
+    case PseudoInputListButton:
+    case PseudoInputPlaceholder:
+#if ENABLE(INPUT_SPEECH)
+    case PseudoInputSpeechButton:
+#endif
+    case PseudoInnerSpinButton:
+    case PseudoMediaControlsPanel:
+    case PseudoMediaControlsMuteButton:
+    case PseudoMediaControlsPlayButton:
+    case PseudoMediaControlsCurrentTimeDisplay:
+    case PseudoMediaControlsTimeRemainingDisplay:
+    case PseudoMediaControlsTimeline:
+    case PseudoMediaControlsVolumeSlider:
+    case PseudoMediaControlsVolumeSliderMuteButton:
+    case PseudoMediaControlsSeekBackButton:
+    case PseudoMediaControlsSeekForwardButton:
+    case PseudoMediaControlsRewindButton:
+    case PseudoMediaControlsReturnToRealtimeButton:
+    case PseudoMediaControlsToggleClosedCaptions:
+    case PseudoMediaControlsStatusDisplay:
+    case PseudoMediaControlsFullscreenButton:
+    case PseudoMediaControlsTimelineContainer:
+    case PseudoMediaControlsVolumeSliderContainer:
+    case PseudoMeterHorizontalBar:
+    case PseudoMeterHorizontalOptimum:
+    case PseudoMeterHorizontalSuboptimal:
+    case PseudoMeterHorizontalEvenLessGood:
+    case PseudoMeterVerticalBar:
+    case PseudoMeterVerticalOptimum:
+    case PseudoMeterVerticalSuboptimal:
+    case PseudoMeterVerticalEvenLessGood:
+    case PseudoOuterSpinButton:
+    case PseudoProgressBarValue:
+    case PseudoResizer:
+    case PseudoScrollbar:
+    case PseudoScrollbarCorner:
+    case PseudoScrollbarButton:
+    case PseudoScrollbarThumb:
+    case PseudoScrollbarTrack:
+    case PseudoScrollbarTrackPiece:
+    case PseudoSearchCancelButton:
+    case PseudoSearchDecoration:
+    case PseudoSearchResultsDecoration:
+    case PseudoSearchResultsButton:
+    case PseudoSelection:
+    case PseudoSliderThumb:
+        element = true;
+        break;
+    case PseudoUnknown:
+    case PseudoEmpty:
+    case PseudoFirstChild:
+    case PseudoFirstOfType:
+    case PseudoLastChild:
+    case PseudoLastOfType:
+    case PseudoOnlyChild:
+    case PseudoOnlyOfType:
+    case PseudoNthChild:
+    case PseudoNthOfType:
+    case PseudoNthLastChild:
+    case PseudoNthLastOfType:
+    case PseudoLink:
+    case PseudoVisited:
+    case PseudoAnyLink:
+    case PseudoAutofill:
+    case PseudoHover:
+    case PseudoDrag:
+    case PseudoFocus:
+    case PseudoActive:
+    case PseudoChecked:
+    case PseudoEnabled:
+    case PseudoFullPageMedia:
+    case PseudoDefault:
+    case PseudoDisabled:
+    case PseudoOptional:
+    case PseudoRequired:
+    case PseudoReadOnly:
+    case PseudoReadWrite:
+    case PseudoValid:
+    case PseudoInvalid:
+    case PseudoIndeterminate:
+    case PseudoTarget:
+    case PseudoLang:
+    case PseudoNot:
+    case PseudoRoot:
+    case PseudoScrollbarBack:
+    case PseudoScrollbarForward:
+    case PseudoWindowInactive:
+    case PseudoCornerPresent:
+    case PseudoDecrement:
+    case PseudoIncrement:
+    case PseudoHorizontal:
+    case PseudoVertical:
+    case PseudoStart:
+    case PseudoEnd:
+    case PseudoDoubleButton:
+    case PseudoSingleButton:
+    case PseudoNoButton:
+    case PseudoNotParsed:
+        break;
+    case PseudoFirstPage:
+    case PseudoLeftPage:
+    case PseudoRightPage:
+        isPagePseudoClass = true;
+        break;
+    }
+
+    bool matchPagePseudoClass = (m_match == PagePseudoClass);
+    if (matchPagePseudoClass != isPagePseudoClass)
+        m_pseudoType = PseudoUnknown;
+    else if (m_match == PseudoClass && element) {
+        if (!compat)
+            m_pseudoType = PseudoUnknown;
+        else
+           m_match = PseudoElement;
+    } else if (m_match == PseudoElement && !element)
+        m_pseudoType = PseudoUnknown;
+}
+
+bool CSSSelector::operator==(const CSSSelector& other)
+{
+    const CSSSelector* sel1 = this;
+    const CSSSelector* sel2 = &other;
+
+    while (sel1 && sel2) {
+        if (sel1->m_tag != sel2->m_tag || sel1->attribute() != sel2->attribute() ||
+             sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match ||
+             sel1->m_value != sel2->m_value ||
+             sel1->pseudoType() != sel2->pseudoType() ||
+             sel1->argument() != sel2->argument())
+            return false;
+        sel1 = sel1->tagHistory();
+        sel2 = sel2->tagHistory();
+    }
+
+    if (sel1 || sel2)
+        return false;
+
+    return true;
+}
+
+String CSSSelector::selectorText() const
+{
+    String str = "";
+
+    const AtomicString& prefix = m_tag.prefix();
+    const AtomicString& localName = m_tag.localName();
+    if (m_match == CSSSelector::None || !prefix.isNull() || localName != starAtom) {
+        if (prefix.isNull())
+            str = localName;
+        else
+            str = prefix + "|" + localName;
+    }
+
+    const CSSSelector* cs = this;
+    while (true) {
+        if (cs->m_match == CSSSelector::Id) {
+            str += "#";
+            serializeIdentifier(cs->m_value, str);
+        } else if (cs->m_match == CSSSelector::Class) {
+            str += ".";
+            serializeIdentifier(cs->m_value, str);
+        } else if (cs->m_match == CSSSelector::PseudoClass || cs->m_match == CSSSelector::PagePseudoClass) {
+            str += ":";
+            str += cs->m_value;
+            if (cs->pseudoType() == PseudoNot) {
+                if (CSSSelector* subSel = cs->simpleSelector())
+                    str += subSel->selectorText();
+                str += ")";
+            } else if (cs->pseudoType() == PseudoLang
+                    || cs->pseudoType() == PseudoNthChild
+                    || cs->pseudoType() == PseudoNthLastChild
+                    || cs->pseudoType() == PseudoNthOfType
+                    || cs->pseudoType() == PseudoNthLastOfType) {
+                str += cs->argument();
+                str += ")";
+            }
+        } else if (cs->m_match == CSSSelector::PseudoElement) {
+            str += "::";
+            str += cs->m_value;
+        } else if (cs->hasAttribute()) {
+            str += "[";
+            const AtomicString& prefix = cs->attribute().prefix();
+            if (!prefix.isNull())
+                str += prefix + "|";
+            str += cs->attribute().localName();
+            switch (cs->m_match) {
+                case CSSSelector::Exact:
+                    str += "=";
+                    break;
+                case CSSSelector::Set:
+                    // set has no operator or value, just the attrName
+                    str += "]";
+                    break;
+                case CSSSelector::List:
+                    str += "~=";
+                    break;
+                case CSSSelector::Hyphen:
+                    str += "|=";
+                    break;
+                case CSSSelector::Begin:
+                    str += "^=";
+                    break;
+                case CSSSelector::End:
+                    str += "$=";
+                    break;
+                case CSSSelector::Contain:
+                    str += "*=";
+                    break;
+                default:
+                    break;
+            }
+            if (cs->m_match != CSSSelector::Set) {
+                serializeString(cs->m_value, str);
+                str += "]";
+            }
+        }
+        if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory())
+            break;
+        cs = cs->tagHistory();
+    }
+
+    if (CSSSelector* tagHistory = cs->tagHistory()) {
+        String tagHistoryText = tagHistory->selectorText();
+        if (cs->relation() == CSSSelector::DirectAdjacent)
+            str = tagHistoryText + " + " + str;
+        else if (cs->relation() == CSSSelector::IndirectAdjacent)
+            str = tagHistoryText + " ~ " + str;
+        else if (cs->relation() == CSSSelector::Child)
+            str = tagHistoryText + " > " + str;
+        else
+            // Descendant
+            str = tagHistoryText + " " + str;
+    }
+
+    return str;
+}
+    
+void CSSSelector::setTagHistory(CSSSelector* tagHistory) 
+{ 
+    if (m_hasRareData) 
+        m_data.m_rareData->m_tagHistory.set(tagHistory); 
+    else 
+        m_data.m_tagHistory = tagHistory; 
+}
+
+const QualifiedName& CSSSelector::attribute() const
+{ 
+    switch (m_match) {
+    case Id:
+        return idAttr;
+    case Class:
+        return classAttr;
+    default:
+        return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName();
+    }
+}
+
+void CSSSelector::setAttribute(const QualifiedName& value) 
+{ 
+    createRareData(); 
+    m_data.m_rareData->m_attribute = value; 
+}
+    
+void CSSSelector::setArgument(const AtomicString& value) 
+{ 
+    createRareData(); 
+    m_data.m_rareData->m_argument = value; 
+}
+
+void CSSSelector::setSimpleSelector(CSSSelector* value)
+{
+    createRareData(); 
+    m_data.m_rareData->m_simpleSelector.set(value); 
+}
+
+bool CSSSelector::parseNth()
+{
+    if (!m_hasRareData)
+        return false;
+    if (m_parsedNth)
+        return true;
+    m_parsedNth = m_data.m_rareData->parseNth();
+    return m_parsedNth;
+}
+
+bool CSSSelector::matchNth(int count)
+{
+    ASSERT(m_hasRareData);
+    return m_data.m_rareData->matchNth(count);
+}
+
+bool CSSSelector::isSimple() const
+{
+    if (simpleSelector() || tagHistory() || matchesPseudoElement())
+        return false;
+
+    int numConditions = 0;
+
+    // hasTag() cannot be be used here because namespace may not be nullAtom.
+    // Example:
+    //     @namespace "http://www.w3.org/2000/svg";
+    //     svg:not(:root) { ...
+    if (m_tag != starAtom)
+        numConditions++;
+
+    if (m_match == Id || m_match == Class || m_match == PseudoClass)
+        numConditions++;
+
+    if (m_hasRareData && m_data.m_rareData->m_attribute != anyQName())
+        numConditions++;
+
+    // numConditions is 0 for a universal selector.
+    // numConditions is 1 for other simple selectors.
+    return numConditions <= 1;
+}
+
+// a helper function for parsing nth-arguments
+bool CSSSelector::RareData::parseNth()
+{
+    String argument = m_argument.lower();
+    
+    if (argument.isEmpty())
+        return false;
+    
+    m_a = 0;
+    m_b = 0;
+    if (argument == "odd") {
+        m_a = 2;
+        m_b = 1;
+    } else if (argument == "even") {
+        m_a = 2;
+        m_b = 0;
+    } else {
+        int n = argument.find('n');
+        if (n != -1) {
+            if (argument[0] == '-') {
+                if (n == 1)
+                    m_a = -1; // -n == -1n
+                else
+                    m_a = argument.substring(0, n).toInt();
+            } else if (!n)
+                m_a = 1; // n == 1n
+            else
+                m_a = argument.substring(0, n).toInt();
+            
+            int p = argument.find('+', n);
+            if (p != -1)
+                m_b = argument.substring(p + 1, argument.length() - p - 1).toInt();
+            else {
+                p = argument.find('-', n);
+                if (p != -1)
+                    m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt();
+            }
+        } else
+            m_b = argument.toInt();
+    }
+    return true;
+}
+
+// a helper function for checking nth-arguments
+bool CSSSelector::RareData::matchNth(int count)
+{
+    if (!m_a)
+        return count == m_b;
+    else if (m_a > 0) {
+        if (count < m_b)
+            return false;
+        return (count - m_b) % m_a == 0;
+    } else {
+        if (count > m_b)
+            return false;
+        return (m_b - count) % (-m_a) == 0;
+    }
+}
+
+} // namespace WebCore