diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/WebCore/css/CSSSelector.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/WebCore/css/CSSSelector.cpp')
-rw-r--r-- | Source/WebCore/css/CSSSelector.cpp | 1009 |
1 files changed, 446 insertions, 563 deletions
diff --git a/Source/WebCore/css/CSSSelector.cpp b/Source/WebCore/css/CSSSelector.cpp index 6cde1ac43..3e645ca16 100644 --- a/Source/WebCore/css/CSSSelector.cpp +++ b/Source/WebCore/css/CSSSelector.cpp @@ -3,7 +3,7 @@ * 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, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2002, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved. * Copyright (C) 2008 David Smith (catfish.man@gmail.com) * Copyright (C) 2010 Google Inc. All rights reserved. * @@ -29,7 +29,6 @@ #include "CSSOMUtils.h" #include "CSSSelectorList.h" #include "HTMLNames.h" -#include "SelectorPseudoTypeMap.h" #include <wtf/Assertions.h> #include <wtf/HashMap.h> #include <wtf/NeverDestroyed.h> @@ -42,224 +41,99 @@ namespace WebCore { using namespace HTMLNames; -struct SameSizeAsCSSSelector { - unsigned flags; - void* unionPointer; -}; - -static_assert(sizeof(CSSSelector) == sizeof(SameSizeAsCSSSelector), "CSSSelector should remain small."); - -CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule) - : m_relation(Descendant) - , m_match(Tag) - , m_pseudoType(0) - , m_parsedNth(false) - , m_isLastInSelectorList(false) - , m_isLastInTagHistory(true) - , m_hasRareData(false) - , m_hasNameWithCase(false) - , m_isForPage(false) - , m_tagIsForNamespaceRule(tagIsForNamespaceRule) -#if ENABLE(CSS_SELECTORS_LEVEL4) - , m_descendantDoubleChildSyntax(false) -#endif - , m_caseInsensitiveAttributeValueMatching(false) -#if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED - , m_destructorHasBeenCalled(false) -#endif -{ - const AtomicString& tagLocalName = tagQName.localName(); - const AtomicString tagLocalNameASCIILowercase = tagLocalName.convertToASCIILowercase(); - - if (tagLocalName == tagLocalNameASCIILowercase) { - m_data.m_tagQName = tagQName.impl(); - m_data.m_tagQName->ref(); - } else { - m_data.m_nameWithCase = adoptRef(new NameWithCase(tagQName, tagLocalNameASCIILowercase)).leakRef(); - m_hasNameWithCase = true; - } -} - void CSSSelector::createRareData() { - ASSERT(match() != Tag); - ASSERT(!m_hasNameWithCase); + ASSERT(m_match != Tag); if (m_hasRareData) return; // Move the value to the rare data stucture. - m_data.m_rareData = &RareData::create(adoptRef(m_data.m_value)).leakRef(); + m_data.m_rareData = RareData::create(adoptRef(m_data.m_value)).leakRef(); m_hasRareData = true; } -static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity); - -static unsigned selectorSpecificity(const CSSSelector& firstSimpleSelector, bool isComputingMaximumSpecificity) +unsigned CSSSelector::specificity() const { - unsigned total = simpleSelectorSpecificityInternal(firstSimpleSelector, isComputingMaximumSpecificity); + // make sure the result doesn't overflow + static const unsigned maxValueMask = 0xffffff; + static const unsigned idMask = 0xff0000; + static const unsigned classMask = 0xff00; + static const unsigned elementMask = 0xff; - for (const CSSSelector* selector = firstSimpleSelector.tagHistory(); selector; selector = selector->tagHistory()) - total = CSSSelector::addSpecificities(total, simpleSelectorSpecificityInternal(*selector, isComputingMaximumSpecificity)); - return total; -} + if (isForPage()) + return specificityForPage() & maxValueMask; -static unsigned maxSpecificity(const CSSSelectorList& selectorList) -{ - unsigned maxSpecificity = 0; - for (const CSSSelector* subSelector = selectorList.first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) - maxSpecificity = std::max(maxSpecificity, selectorSpecificity(*subSelector, true)); - return maxSpecificity; + unsigned total = 0; + unsigned temp = 0; + + for (const CSSSelector* selector = this; selector; selector = selector->tagHistory()) { + temp = total + selector->specificityForOneSelector(); + // Clamp each component to its max in the case of overflow. + if ((temp & idMask) < (total & idMask)) + total |= idMask; + else if ((temp & classMask) < (total & classMask)) + total |= classMask; + else if ((temp & elementMask) < (total & elementMask)) + total |= elementMask; + else + total = temp; + } + return total; } -static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity) +inline unsigned CSSSelector::specificityForOneSelector() const { - ASSERT_WITH_MESSAGE(!simpleSelector.isForPage(), "At the time of this writing, page selectors are not treated as real selectors that are matched. The value computed here only account for real selectors."); - - switch (simpleSelector.match()) { - case CSSSelector::Id: - return static_cast<unsigned>(SelectorSpecificityIncrement::ClassA); - - case CSSSelector::PagePseudoClass: - break; - case CSSSelector::PseudoClass: - if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassMatches) { - ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :matches()."); - if (!isComputingMaximumSpecificity) - return 0; - return maxSpecificity(*simpleSelector.selectorList()); - } - - if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassNot) { - ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :not()."); - return maxSpecificity(*simpleSelector.selectorList()); - } - FALLTHROUGH; - case CSSSelector::Exact: - case CSSSelector::Class: - case CSSSelector::Set: - case CSSSelector::List: - case CSSSelector::Hyphen: - case CSSSelector::Contain: - case CSSSelector::Begin: - case CSSSelector::End: - return static_cast<unsigned>(SelectorSpecificityIncrement::ClassB); - case CSSSelector::Tag: - return (simpleSelector.tagQName().localName() != starAtom) ? static_cast<unsigned>(SelectorSpecificityIncrement::ClassC) : 0; - case CSSSelector::PseudoElement: - return static_cast<unsigned>(SelectorSpecificityIncrement::ClassC); - case CSSSelector::Unknown: + // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function + // isn't quite correct. + switch (m_match) { + case Id: + return 0x10000; + case Exact: + case Class: + case Set: + case List: + case Hyphen: + case PseudoClass: + case PseudoElement: + case Contain: + case Begin: + case End: + // FIXME: PsuedoAny should base the specificity on the sub-selectors. + // See http://lists.w3.org/Archives/Public/www-style/2010Sep/0530.html + if (pseudoType() == PseudoNot && selectorList()) + return selectorList()->first()->specificityForOneSelector(); + return 0x100; + case Tag: + return (tagQName().localName() != starAtom) ? 1 : 0; + case Unknown: return 0; } ASSERT_NOT_REACHED(); return 0; } -unsigned CSSSelector::simpleSelectorSpecificity() const -{ - return simpleSelectorSpecificityInternal(*this, false); -} - -static unsigned staticSpecificityInternal(const CSSSelector& firstSimpleSelector, bool& ok); - -static unsigned simpleSelectorFunctionalPseudoClassStaticSpecificity(const CSSSelector& simpleSelector, bool& ok) -{ - if (simpleSelector.match() == CSSSelector::PseudoClass) { - CSSSelector::PseudoClassType pseudoClassType = simpleSelector.pseudoClassType(); - if (pseudoClassType == CSSSelector::PseudoClassMatches || pseudoClassType == CSSSelector::PseudoClassNthChild || pseudoClassType == CSSSelector::PseudoClassNthLastChild) { - const CSSSelectorList* selectorList = simpleSelector.selectorList(); - if (!selectorList) { - ASSERT_WITH_MESSAGE(pseudoClassType != CSSSelector::PseudoClassMatches, ":matches() should never be created without a valid selector list."); - return 0; - } - - const CSSSelector& firstSubselector = *selectorList->first(); - - unsigned initialSpecificity = staticSpecificityInternal(firstSubselector, ok); - if (!ok) - return 0; - - const CSSSelector* subselector = &firstSubselector; - while ((subselector = CSSSelectorList::next(subselector))) { - unsigned subSelectorSpecificity = staticSpecificityInternal(*subselector, ok); - if (initialSpecificity != subSelectorSpecificity) - ok = false; - if (!ok) - return 0; - } - return initialSpecificity; - } - } - return 0; -} - -static unsigned functionalPseudoClassStaticSpecificity(const CSSSelector& firstSimpleSelector, bool& ok) -{ - unsigned total = 0; - for (const CSSSelector* selector = &firstSimpleSelector; selector; selector = selector->tagHistory()) { - total = CSSSelector::addSpecificities(total, simpleSelectorFunctionalPseudoClassStaticSpecificity(*selector, ok)); - if (!ok) - return 0; - } - return total; -} - -static unsigned staticSpecificityInternal(const CSSSelector& firstSimpleSelector, bool& ok) -{ - unsigned staticSpecificity = selectorSpecificity(firstSimpleSelector, false); - return CSSSelector::addSpecificities(staticSpecificity, functionalPseudoClassStaticSpecificity(firstSimpleSelector, ok)); -} - -unsigned CSSSelector::staticSpecificity(bool &ok) const -{ - ok = true; - return staticSpecificityInternal(*this, ok); -} - -unsigned CSSSelector::addSpecificities(unsigned a, unsigned b) -{ - unsigned total = a; - - unsigned newIdValue = (b & idMask); - if (((total & idMask) + newIdValue) & ~idMask) - total |= idMask; - else - total += newIdValue; - - unsigned newClassValue = (b & classMask); - if (((total & classMask) + newClassValue) & ~classMask) - total |= classMask; - else - total += newClassValue; - - unsigned newElementValue = (b & elementMask); - if (((total & elementMask) + newElementValue) & ~elementMask) - total |= elementMask; - else - total += newElementValue; - - return total; -} - unsigned CSSSelector::specificityForPage() const { - ASSERT(isForPage()); - // See http://dev.w3.org/csswg/css3-page/#cascading-and-page-context unsigned s = 0; for (const CSSSelector* component = this; component; component = component->tagHistory()) { - switch (component->match()) { + switch (component->m_match) { case Tag: s += tagQName().localName() == starAtom ? 0 : 4; break; - case PagePseudoClass: - switch (component->pagePseudoClassType()) { - case PagePseudoClassFirst: + case PseudoClass: + switch (component->pseudoType()) { + case PseudoFirstPage: s += 2; break; - case PagePseudoClassLeft: - case PagePseudoClassRight: + case PseudoLeftPage: + case PseudoRightPage: s += 1; break; + case PseudoNotParsed: + break; + default: + ASSERT_NOT_REACHED(); } break; default: @@ -269,39 +143,112 @@ unsigned CSSSelector::specificityForPage() const return s; } -PseudoId CSSSelector::pseudoId(PseudoElementType type) +PseudoId CSSSelector::pseudoId(PseudoType type) { switch (type) { - case PseudoElementFirstLine: + case PseudoFirstLine: return FIRST_LINE; - case PseudoElementFirstLetter: + case PseudoFirstLetter: return FIRST_LETTER; - case PseudoElementSelection: + case PseudoSelection: return SELECTION; - case PseudoElementBefore: + case PseudoBefore: return BEFORE; - case PseudoElementAfter: + case PseudoAfter: return AFTER; - case PseudoElementScrollbar: + case PseudoScrollbar: return SCROLLBAR; - case PseudoElementScrollbarButton: + case PseudoScrollbarButton: return SCROLLBAR_BUTTON; - case PseudoElementScrollbarCorner: + case PseudoScrollbarCorner: return SCROLLBAR_CORNER; - case PseudoElementScrollbarThumb: + case PseudoScrollbarThumb: return SCROLLBAR_THUMB; - case PseudoElementScrollbarTrack: + case PseudoScrollbarTrack: return SCROLLBAR_TRACK; - case PseudoElementScrollbarTrackPiece: + case PseudoScrollbarTrackPiece: return SCROLLBAR_TRACK_PIECE; - case PseudoElementResizer: + case PseudoResizer: return RESIZER; +#if ENABLE(FULLSCREEN_API) + case PseudoFullScreen: + return FULL_SCREEN; + case PseudoFullScreenDocument: + return FULL_SCREEN_DOCUMENT; + case PseudoFullScreenAncestor: + return FULL_SCREEN_ANCESTOR; + case PseudoAnimatingFullScreenTransition: + return ANIMATING_FULL_SCREEN_TRANSITION; +#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 PseudoAny: + 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 PseudoScope: + 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: + case PseudoInRange: + case PseudoOutOfRange: + case PseudoUserAgentCustomElement: + case PseudoWebKitCustomElement: #if ENABLE(VIDEO_TRACK) - case PseudoElementCue: + case PseudoCue: + case PseudoFutureCue: + case PseudoPastCue: +#endif +#if ENABLE(IFRAME_SEAMLESS) + case PseudoSeamlessDocument: #endif - case PseudoElementUnknown: - case PseudoElementUserAgentCustom: - case PseudoElementWebKitCustom: + return NOPSEUDO; + case PseudoNotParsed: + ASSERT_NOT_REACHED(); return NOPSEUDO; } @@ -309,22 +256,252 @@ PseudoId CSSSelector::pseudoId(PseudoElementType type) return NOPSEUDO; } -CSSSelector::PseudoElementType CSSSelector::parsePseudoElementType(const String& name) +static NEVER_INLINE void populatePseudoTypeByNameMap(HashMap<AtomicString, CSSSelector::PseudoType>& map) +{ + struct TableEntry { + const char* name; + unsigned nameLength; + CSSSelector::PseudoType type; + }; + + // Could use strlen in this macro but not all compilers can constant-fold it. +#define TABLE_ENTRY(name, type) { name, sizeof(name) - 1, CSSSelector::type }, + + static const TableEntry table[] = { + TABLE_ENTRY("-khtml-drag", PseudoDrag) + TABLE_ENTRY("-webkit-any(", PseudoAny) + TABLE_ENTRY("-webkit-any-link", PseudoAnyLink) + TABLE_ENTRY("-webkit-autofill", PseudoAutofill) + TABLE_ENTRY("-webkit-drag", PseudoDrag) + TABLE_ENTRY("-webkit-full-page-media", PseudoFullPageMedia) + TABLE_ENTRY("-webkit-resizer", PseudoResizer) + TABLE_ENTRY("-webkit-scrollbar", PseudoScrollbar) + TABLE_ENTRY("-webkit-scrollbar-button", PseudoScrollbarButton) + TABLE_ENTRY("-webkit-scrollbar-corner", PseudoScrollbarCorner) + TABLE_ENTRY("-webkit-scrollbar-thumb", PseudoScrollbarThumb) + TABLE_ENTRY("-webkit-scrollbar-track", PseudoScrollbarTrack) + TABLE_ENTRY("-webkit-scrollbar-track-piece", PseudoScrollbarTrackPiece) + TABLE_ENTRY("active", PseudoActive) + TABLE_ENTRY("after", PseudoAfter) + TABLE_ENTRY("before", PseudoBefore) + TABLE_ENTRY("checked", PseudoChecked) + TABLE_ENTRY("corner-present", PseudoCornerPresent) + TABLE_ENTRY("decrement", PseudoDecrement) + TABLE_ENTRY("default", PseudoDefault) + TABLE_ENTRY("disabled", PseudoDisabled) + TABLE_ENTRY("double-button", PseudoDoubleButton) + TABLE_ENTRY("empty", PseudoEmpty) + TABLE_ENTRY("enabled", PseudoEnabled) + TABLE_ENTRY("end", PseudoEnd) + TABLE_ENTRY("first", PseudoFirstPage) + TABLE_ENTRY("first-child", PseudoFirstChild) + TABLE_ENTRY("first-letter", PseudoFirstLetter) + TABLE_ENTRY("first-line", PseudoFirstLine) + TABLE_ENTRY("first-of-type", PseudoFirstOfType) + TABLE_ENTRY("focus", PseudoFocus) + TABLE_ENTRY("horizontal", PseudoHorizontal) + TABLE_ENTRY("hover", PseudoHover) + TABLE_ENTRY("in-range", PseudoInRange) + TABLE_ENTRY("increment", PseudoIncrement) + TABLE_ENTRY("indeterminate", PseudoIndeterminate) + TABLE_ENTRY("invalid", PseudoInvalid) + TABLE_ENTRY("lang(", PseudoLang) + TABLE_ENTRY("last-child", PseudoLastChild) + TABLE_ENTRY("last-of-type", PseudoLastOfType) + TABLE_ENTRY("left", PseudoLeftPage) + TABLE_ENTRY("link", PseudoLink) + TABLE_ENTRY("no-button", PseudoNoButton) + TABLE_ENTRY("not(", PseudoNot) + TABLE_ENTRY("nth-child(", PseudoNthChild) + TABLE_ENTRY("nth-last-child(", PseudoNthLastChild) + TABLE_ENTRY("nth-last-of-type(", PseudoNthLastOfType) + TABLE_ENTRY("nth-of-type(", PseudoNthOfType) + TABLE_ENTRY("only-child", PseudoOnlyChild) + TABLE_ENTRY("only-of-type", PseudoOnlyOfType) + TABLE_ENTRY("optional", PseudoOptional) + TABLE_ENTRY("out-of-range", PseudoOutOfRange) + TABLE_ENTRY("read-only", PseudoReadOnly) + TABLE_ENTRY("read-write", PseudoReadWrite) + TABLE_ENTRY("required", PseudoRequired) + TABLE_ENTRY("right", PseudoRightPage) + TABLE_ENTRY("root", PseudoRoot) + TABLE_ENTRY("scope", PseudoScope) + TABLE_ENTRY("selection", PseudoSelection) + TABLE_ENTRY("single-button", PseudoSingleButton) + TABLE_ENTRY("start", PseudoStart) + TABLE_ENTRY("target", PseudoTarget) + TABLE_ENTRY("valid", PseudoValid) + TABLE_ENTRY("vertical", PseudoVertical) + TABLE_ENTRY("visited", PseudoVisited) + TABLE_ENTRY("window-inactive", PseudoWindowInactive) + +#if ENABLE(FULLSCREEN_API) + TABLE_ENTRY("-webkit-animating-full-screen-transition", PseudoAnimatingFullScreenTransition) + TABLE_ENTRY("-webkit-full-screen", PseudoFullScreen) + TABLE_ENTRY("-webkit-full-screen-ancestor", PseudoFullScreenAncestor) + TABLE_ENTRY("-webkit-full-screen-document", PseudoFullScreenDocument) +#endif + +#if ENABLE(IFRAME_SEAMLESS) + TABLE_ENTRY("-webkit-seamless-document", PseudoSeamlessDocument) +#endif + +#if ENABLE(VIDEO_TRACK) + TABLE_ENTRY("cue(", PseudoCue) + TABLE_ENTRY("future", PseudoFutureCue) + TABLE_ENTRY("past", PseudoPastCue) +#endif + }; + +#undef TABLE_ENTRY + + for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i) + map.add(AtomicString(table[i].name, table[i].nameLength, AtomicString::ConstructFromLiteral), table[i].type); +} + +CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name) { if (name.isNull()) - return PseudoElementUnknown; + return PseudoUnknown; - PseudoElementType type = parsePseudoElementString(*name.impl()); - if (type == PseudoElementUnknown) { - if (name.startsWith("-webkit-")) - type = PseudoElementWebKitCustom; + static NeverDestroyed<HashMap<AtomicString, CSSSelector::PseudoType>> types; + if (types.get().isEmpty()) + populatePseudoTypeByNameMap(types); + if (PseudoType type = types.get().get(name)) + return type; - if (name.startsWith("x-")) - type = PseudoElementUserAgentCustom; - } - return type; + if (name.startsWith("-webkit-")) + return PseudoWebKitCustomElement; + + // FIXME: This is strange. Why would all strings that start with "cue" be "user agent custom"? + if (name.startsWith("x-") || name.startsWith("cue")) + return PseudoUserAgentCustomElement; + + return PseudoUnknown; } +void CSSSelector::extractPseudoType() const +{ + if (m_match != PseudoClass && m_match != PseudoElement && m_match != PagePseudoClass) + return; + + m_pseudoType = parsePseudoType(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: +#if ENABLE(VIDEO_TRACK) + case PseudoCue: +#endif + case PseudoFirstLetter: + case PseudoFirstLine: + compat = true; + FALLTHROUGH; +#if ENABLE(SHADOW_DOM) + case PseudoDistributed: +#endif + case PseudoResizer: + case PseudoScrollbar: + case PseudoScrollbarCorner: + case PseudoScrollbarButton: + case PseudoScrollbarThumb: + case PseudoScrollbarTrack: + case PseudoScrollbarTrackPiece: + case PseudoSelection: + case PseudoUserAgentCustomElement: + case PseudoWebKitCustomElement: + 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 PseudoAny: + 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 PseudoScope: + 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: +#if ENABLE(FULLSCREEN_API) + case PseudoFullScreen: + case PseudoFullScreenDocument: + case PseudoFullScreenAncestor: + case PseudoAnimatingFullScreenTransition: +#endif +#if ENABLE(IFRAME_SEAMLESS) + case PseudoSeamlessDocument: +#endif + case PseudoInRange: + case PseudoOutOfRange: +#if ENABLE(VIDEO_TRACK) + case PseudoFutureCue: + case PseudoPastCue: +#endif + 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 { @@ -334,13 +511,13 @@ bool CSSSelector::operator==(const CSSSelector& other) const while (sel1 && sel2) { if (sel1->attribute() != sel2->attribute() || sel1->relation() != sel2->relation() - || sel1->match() != sel2->match() + || sel1->m_match != sel2->m_match || sel1->value() != sel2->value() - || sel1->m_pseudoType != sel2->m_pseudoType + || sel1->pseudoType() != sel2->pseudoType() || sel1->argument() != sel2->argument()) { return false; } - if (sel1->match() == Tag) { + if (sel1->m_match == Tag) { if (sel1->tagQName() != sel2->tagQName()) return false; } @@ -354,46 +531,11 @@ bool CSSSelector::operator==(const CSSSelector& other) const return true; } -static void appendPseudoClassFunctionTail(StringBuilder& str, const CSSSelector* selector) -{ - switch (selector->pseudoClassType()) { -#if ENABLE(CSS_SELECTORS_LEVEL4) - case CSSSelector::PseudoClassDir: -#endif - case CSSSelector::PseudoClassLang: - case CSSSelector::PseudoClassNthChild: - case CSSSelector::PseudoClassNthLastChild: - case CSSSelector::PseudoClassNthOfType: - case CSSSelector::PseudoClassNthLastOfType: -#if ENABLE(CSS_SELECTORS_LEVEL4) - case CSSSelector::PseudoClassRole: -#endif - str.append(selector->argument()); - str.append(')'); - break; - default: - break; - } - -} - -static void appendLangArgumentList(StringBuilder& str, const Vector<AtomicString>& argumentList) -{ - unsigned argumentListSize = argumentList.size(); - for (unsigned i = 0; i < argumentListSize; ++i) { - str.append('"'); - str.append(argumentList[i]); - str.append('"'); - if (i != argumentListSize - 1) - str.appendLiteral(", "); - } -} - String CSSSelector::selectorText(const String& rightSide) const { StringBuilder str; - if (match() == CSSSelector::Tag && !m_tagIsForNamespaceRule) { + if (m_match == CSSSelector::Tag && !m_tagIsForNamespaceRule) { if (tagQName().prefix().isNull()) str.append(tagQName().localName()); else { @@ -405,245 +547,44 @@ String CSSSelector::selectorText(const String& rightSide) const const CSSSelector* cs = this; while (true) { - if (cs->match() == CSSSelector::Id) { + if (cs->m_match == CSSSelector::Id) { str.append('#'); serializeIdentifier(cs->value(), str); - } else if (cs->match() == CSSSelector::Class) { + } else if (cs->m_match == CSSSelector::Class) { str.append('.'); serializeIdentifier(cs->value(), str); - } else if (cs->match() == CSSSelector::PseudoClass) { - switch (cs->pseudoClassType()) { -#if ENABLE(FULLSCREEN_API) - case CSSSelector::PseudoClassAnimatingFullScreenTransition: - str.appendLiteral(":-webkit-animating-full-screen-transition"); - break; -#endif - case CSSSelector::PseudoClassAny: { - str.appendLiteral(":-webkit-any("); - cs->selectorList()->buildSelectorsText(str); - str.append(')'); - break; - } - case CSSSelector::PseudoClassAnyLink: - str.appendLiteral(":any-link"); - break; - case CSSSelector::PseudoClassAnyLinkDeprecated: - str.appendLiteral(":-webkit-any-link"); - break; - case CSSSelector::PseudoClassAutofill: - str.appendLiteral(":-webkit-autofill"); - break; - case CSSSelector::PseudoClassDrag: - str.appendLiteral(":-webkit-drag"); - break; - case CSSSelector::PseudoClassFullPageMedia: - str.appendLiteral(":-webkit-full-page-media"); - break; -#if ENABLE(FULLSCREEN_API) - case CSSSelector::PseudoClassFullScreen: - str.appendLiteral(":-webkit-full-screen"); - break; - case CSSSelector::PseudoClassFullScreenAncestor: - str.appendLiteral(":-webkit-full-screen-ancestor"); - break; - case CSSSelector::PseudoClassFullScreenDocument: - str.appendLiteral(":-webkit-full-screen-document"); - break; -#endif - case CSSSelector::PseudoClassActive: - str.appendLiteral(":active"); - break; - case CSSSelector::PseudoClassChecked: - str.appendLiteral(":checked"); - break; - case CSSSelector::PseudoClassCornerPresent: - str.appendLiteral(":corner-present"); - break; - case CSSSelector::PseudoClassDecrement: - str.appendLiteral(":decrement"); - break; - case CSSSelector::PseudoClassDefault: - str.appendLiteral(":default"); - break; -#if ENABLE(CSS_SELECTORS_LEVEL4) - case CSSSelector::PseudoClassDir: - str.appendLiteral(":dir("); - appendPseudoClassFunctionTail(str, cs); - break; -#endif - case CSSSelector::PseudoClassDisabled: - str.appendLiteral(":disabled"); - break; - case CSSSelector::PseudoClassDoubleButton: - str.appendLiteral(":double-button"); - break; - case CSSSelector::PseudoClassEmpty: - str.appendLiteral(":empty"); - break; - case CSSSelector::PseudoClassEnabled: - str.appendLiteral(":enabled"); - break; - case CSSSelector::PseudoClassEnd: - str.appendLiteral(":end"); - break; - case CSSSelector::PseudoClassFirstChild: - str.appendLiteral(":first-child"); - break; - case CSSSelector::PseudoClassFirstOfType: - str.appendLiteral(":first-of-type"); - break; - case CSSSelector::PseudoClassFocus: - str.appendLiteral(":focus"); - break; -#if ENABLE(VIDEO_TRACK) - case CSSSelector::PseudoClassFuture: - str.appendLiteral(":future"); - break; -#endif - case CSSSelector::PseudoClassHorizontal: - str.appendLiteral(":horizontal"); - break; - case CSSSelector::PseudoClassHover: - str.appendLiteral(":hover"); - break; - case CSSSelector::PseudoClassInRange: - str.appendLiteral(":in-range"); - break; - case CSSSelector::PseudoClassIncrement: - str.appendLiteral(":increment"); - break; - case CSSSelector::PseudoClassIndeterminate: - str.appendLiteral(":indeterminate"); - break; - case CSSSelector::PseudoClassInvalid: - str.appendLiteral(":invalid"); - break; - case CSSSelector::PseudoClassLang: - str.appendLiteral(":lang("); - ASSERT_WITH_MESSAGE(cs->langArgumentList() && !cs->langArgumentList()->isEmpty(), "An empty :lang() is invalid and should never be generated by the parser."); - appendLangArgumentList(str, *cs->langArgumentList()); - str.append(')'); - break; - case CSSSelector::PseudoClassLastChild: - str.appendLiteral(":last-child"); - break; - case CSSSelector::PseudoClassLastOfType: - str.appendLiteral(":last-of-type"); - break; - case CSSSelector::PseudoClassLink: - str.appendLiteral(":link"); - break; - case CSSSelector::PseudoClassNoButton: - str.appendLiteral(":no-button"); - break; - case CSSSelector::PseudoClassNot: - str.appendLiteral(":not("); - cs->selectorList()->buildSelectorsText(str); + } else if (cs->m_match == CSSSelector::PseudoClass || cs->m_match == CSSSelector::PagePseudoClass) { + str.append(':'); + str.append(cs->value()); + + switch (cs->pseudoType()) { + case PseudoNot: + if (const CSSSelectorList* selectorList = cs->selectorList()) + str.append(selectorList->first()->selectorText()); str.append(')'); break; - case CSSSelector::PseudoClassNthChild: - str.appendLiteral(":nth-child("); + case PseudoLang: + case PseudoNthChild: + case PseudoNthLastChild: + case PseudoNthOfType: + case PseudoNthLastOfType: str.append(cs->argument()); - if (const CSSSelectorList* selectorList = cs->selectorList()) { - str.appendLiteral(" of "); - selectorList->buildSelectorsText(str); - } str.append(')'); break; - case CSSSelector::PseudoClassNthLastChild: - str.appendLiteral(":nth-last-child("); - str.append(cs->argument()); - if (const CSSSelectorList* selectorList = cs->selectorList()) { - str.appendLiteral(" of "); - selectorList->buildSelectorsText(str); + case PseudoAny: { + const CSSSelector* firstSubSelector = cs->selectorList()->first(); + for (const CSSSelector* subSelector = firstSubSelector; subSelector; subSelector = CSSSelectorList::next(subSelector)) { + if (subSelector != firstSubSelector) + str.append(','); + str.append(subSelector->selectorText()); } str.append(')'); break; - case CSSSelector::PseudoClassNthLastOfType: - str.appendLiteral(":nth-last-of-type("); - appendPseudoClassFunctionTail(str, cs); - break; - case CSSSelector::PseudoClassNthOfType: - str.appendLiteral(":nth-of-type("); - appendPseudoClassFunctionTail(str, cs); - break; - case CSSSelector::PseudoClassOnlyChild: - str.appendLiteral(":only-child"); - break; - case CSSSelector::PseudoClassOnlyOfType: - str.appendLiteral(":only-of-type"); - break; - case CSSSelector::PseudoClassOptional: - str.appendLiteral(":optional"); - break; - case CSSSelector::PseudoClassMatches: { - str.appendLiteral(":matches("); - cs->selectorList()->buildSelectorsText(str); - str.append(')'); - break; } - case CSSSelector::PseudoClassPlaceholderShown: - str.appendLiteral(":placeholder-shown"); - break; - case CSSSelector::PseudoClassOutOfRange: - str.appendLiteral(":out-of-range"); - break; -#if ENABLE(VIDEO_TRACK) - case CSSSelector::PseudoClassPast: - str.appendLiteral(":past"); - break; -#endif - case CSSSelector::PseudoClassReadOnly: - str.appendLiteral(":read-only"); - break; - case CSSSelector::PseudoClassReadWrite: - str.appendLiteral(":read-write"); - break; - case CSSSelector::PseudoClassRequired: - str.appendLiteral(":required"); - break; -#if ENABLE(CSS_SELECTORS_LEVEL4) - case CSSSelector::PseudoClassRole: - str.appendLiteral(":role("); - appendPseudoClassFunctionTail(str, cs); - break; -#endif - case CSSSelector::PseudoClassRoot: - str.appendLiteral(":root"); - break; - case CSSSelector::PseudoClassScope: - str.appendLiteral(":scope"); - break; - case CSSSelector::PseudoClassSingleButton: - str.appendLiteral(":single-button"); + default: break; - case CSSSelector::PseudoClassStart: - str.appendLiteral(":start"); - break; - case CSSSelector::PseudoClassTarget: - str.appendLiteral(":target"); - break; - case CSSSelector::PseudoClassValid: - str.appendLiteral(":valid"); - break; - case CSSSelector::PseudoClassVertical: - str.appendLiteral(":vertical"); - break; - case CSSSelector::PseudoClassVisited: - str.appendLiteral(":visited"); - break; - case CSSSelector::PseudoClassWindowInactive: - str.appendLiteral(":window-inactive"); - break; -#if ENABLE(SHADOW_DOM) - case CSSSelector::PseudoClassHost: - str.appendLiteral(":host"); - break; -#endif - case CSSSelector::PseudoClassUnknown: - ASSERT_NOT_REACHED(); } - } else if (cs->match() == CSSSelector::PseudoElement) { + } else if (cs->m_match == CSSSelector::PseudoElement) { str.appendLiteral("::"); str.append(cs->value()); } else if (cs->isAttributeSelector()) { @@ -654,7 +595,7 @@ String CSSSelector::selectorText(const String& rightSide) const str.append('|'); } str.append(cs->attribute().localName()); - switch (cs->match()) { + switch (cs->m_match) { case CSSSelector::Exact: str.append('='); break; @@ -680,27 +621,11 @@ String CSSSelector::selectorText(const String& rightSide) const default: break; } - if (cs->match() != CSSSelector::Set) { + if (cs->m_match != CSSSelector::Set) { serializeString(cs->value(), str); - if (cs->attributeValueMatchingIsCaseInsensitive()) - str.appendLiteral(" i]"); - else - str.append(']'); - } - } else if (cs->match() == CSSSelector::PagePseudoClass) { - switch (cs->pagePseudoClassType()) { - case PagePseudoClassFirst: - str.appendLiteral(":first"); - break; - case PagePseudoClassLeft: - str.appendLiteral(":left"); - break; - case PagePseudoClassRight: - str.appendLiteral(":right"); - break; + str.append(']'); } } - if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory()) break; cs = cs->tagHistory(); @@ -709,10 +634,6 @@ String CSSSelector::selectorText(const String& rightSide) const if (const CSSSelector* tagHistory = cs->tagHistory()) { switch (cs->relation()) { case CSSSelector::Descendant: -#if ENABLE(CSS_SELECTORS_LEVEL4) - if (cs->m_descendantDoubleChildSyntax) - return tagHistory->selectorText(" >> " + str.toString() + rightSide); -#endif return tagHistory->selectorText(" " + str.toString() + rightSide); case CSSSelector::Child: return tagHistory->selectorText(" > " + str.toString() + rightSide); @@ -736,7 +657,7 @@ void CSSSelector::setAttribute(const QualifiedName& value, bool isCaseInsensitiv { createRareData(); m_data.m_rareData->m_attribute = value; - m_data.m_rareData->m_attributeCanonicalLocalName = isCaseInsensitive ? value.localName().convertToASCIILowercase() : value.localName(); + m_data.m_rareData->m_attributeCanonicalLocalName = isCaseInsensitive ? value.localName().lower() : value.localName(); } void CSSSelector::setArgument(const AtomicString& value) @@ -745,16 +666,10 @@ void CSSSelector::setArgument(const AtomicString& value) m_data.m_rareData->m_argument = value; } -void CSSSelector::setLangArgumentList(std::unique_ptr<Vector<AtomicString>> argumentList) -{ - createRareData(); - m_data.m_rareData->m_langArgumentList = WTFMove(argumentList); -} - -void CSSSelector::setSelectorList(std::unique_ptr<CSSSelectorList> selectorList) +void CSSSelector::setSelectorList(PassOwnPtr<CSSSelectorList> selectorList) { createRareData(); - m_data.m_rareData->m_selectorList = WTFMove(selectorList); + m_data.m_rareData->m_selectorList = selectorList; } bool CSSSelector::parseNth() const @@ -773,20 +688,6 @@ bool CSSSelector::matchNth(int count) const return m_data.m_rareData->matchNth(count); } -int CSSSelector::nthA() const -{ - ASSERT(m_hasRareData); - ASSERT(m_parsedNth); - return m_data.m_rareData->m_a; -} - -int CSSSelector::nthB() const -{ - ASSERT(m_hasRareData); - ASSERT(m_parsedNth); - return m_data.m_rareData->m_b; -} - CSSSelector::RareData::RareData(PassRefPtr<AtomicStringImpl> value) : m_value(value.leakRef()) , m_a(0) @@ -805,60 +706,42 @@ CSSSelector::RareData::~RareData() // a helper function for parsing nth-arguments bool CSSSelector::RareData::parseNth() { - if (m_argument.isEmpty()) + String argument = m_argument.lower(); + + if (argument.isEmpty()) return false; - if (equalLettersIgnoringASCIICase(m_argument, "odd")) { + m_a = 0; + m_b = 0; + if (argument == "odd") { m_a = 2; m_b = 1; - } else if (equalLettersIgnoringASCIICase(m_argument, "even")) { + } else if (argument == "even") { m_a = 2; m_b = 0; } else { - m_a = 0; - m_b = 0; - - size_t n = std::min(m_argument.find('n'), m_argument.find('N')); + size_t n = argument.find('n'); if (n != notFound) { - if (m_argument[0] == '-') { + if (argument[0] == '-') { if (n == 1) m_a = -1; // -n == -1n - else { - bool ok; - m_a = StringView(m_argument).substring(0, n).toIntStrict(ok); - if (!ok) - return false; - } + else + m_a = argument.substring(0, n).toInt(); } else if (!n) m_a = 1; // n == 1n - else { - bool ok; - m_a = StringView(m_argument).substring(0, n).toIntStrict(ok); - if (!ok) - return false; - } + else + m_a = argument.substring(0, n).toInt(); - size_t p = m_argument.find('+', n); - if (p != notFound) { - bool ok; - m_b = StringView(m_argument).substring(p + 1).toIntStrict(ok); - if (!ok) - return false; - } else { - p = m_argument.find('-', n); - if (p != notFound) { - bool ok; - m_b = -StringView(m_argument).substring(p + 1).toIntStrict(ok); - if (!ok) - return false; - } + size_t p = argument.find('+', n); + if (p != notFound) + m_b = argument.substring(p + 1, argument.length() - p - 1).toInt(); + else { + p = argument.find('-', n); + if (p != notFound) + m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt(); } - } else { - bool ok; - m_b = m_argument.string().toIntStrict(&ok); - if (!ok) - return false; - } + } else + m_b = argument.toInt(); } return true; } |