summaryrefslogtreecommitdiff
path: root/Source/WebCore/css/CSSSelector.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
commit32761a6cee1d0dee366b885b7b9c777e67885688 (patch)
treed6bec92bebfb216f4126356e55518842c2f476a1 /Source/WebCore/css/CSSSelector.cpp
parenta4e969f4965059196ca948db781e52f7cfebf19e (diff)
downloadWebKitGtk-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.cpp1009
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;
}