Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / SelectorChecker.cpp
index f9f11f3..cc023da 100644 (file)
 #include "core/css/SelectorChecker.h"
 
 #include "HTMLNames.h"
-#include "core/css/CSSSelector.h"
 #include "core/css/CSSSelectorList.h"
 #include "core/css/SiblingTraversalStrategies.h"
 #include "core/dom/Document.h"
-#include "core/dom/Element.h"
+#include "core/dom/ElementTraversal.h"
 #include "core/dom/FullscreenElementStack.h"
 #include "core/dom/NodeRenderStyle.h"
 #include "core/dom/Text.h"
 #include "core/dom/shadow/InsertionPoint.h"
 #include "core/dom/shadow/ShadowRoot.h"
 #include "core/editing/FrameSelection.h"
-#include "core/html/HTMLAnchorElement.h"
+#include "core/frame/LocalFrame.h"
 #include "core/html/HTMLDocument.h"
 #include "core/html/HTMLFrameElementBase.h"
 #include "core/html/HTMLInputElement.h"
-#include "core/html/HTMLOptGroupElement.h"
 #include "core/html/HTMLOptionElement.h"
 #include "core/html/parser/HTMLParserIdioms.h"
+#include "core/html/track/vtt/VTTElement.h"
 #include "core/inspector/InspectorInstrumentation.h"
 #include "core/page/FocusController.h"
-#include "core/frame/Frame.h"
-#include "core/platform/ScrollableArea.h"
-#include "core/platform/ScrollbarTheme.h"
 #include "core/rendering/RenderObject.h"
 #include "core/rendering/RenderScrollbar.h"
 #include "core/rendering/style/RenderStyle.h"
-
-#include "core/html/track/WebVTTElement.h"
+#include "platform/scroll/ScrollableArea.h"
+#include "platform/scroll/ScrollbarTheme.h"
 
 namespace WebCore {
 
@@ -69,40 +65,39 @@ SelectorChecker::SelectorChecker(Document& document, Mode mode)
 {
 }
 
-static bool matchesCustomPseudoElement(const Element* element, const CSSSelector* selector)
+static bool matchesCustomPseudoElement(const Element* element, const CSSSelector& selector)
 {
     ShadowRoot* root = element->containingShadowRoot();
-    if (!root)
+    if (!root || root->type() != ShadowRoot::UserAgentShadowRoot)
         return false;
 
-    if (selector->pseudoType() != CSSSelector::PseudoPart) {
-        const AtomicString& pseudoId = selector->pseudoType() == CSSSelector::PseudoWebKitCustomElement ? element->shadowPseudoId() : element->pseudo();
-        if (pseudoId != selector->value())
-            return false;
-        if (selector->pseudoType() == CSSSelector::PseudoWebKitCustomElement && root->type() != ShadowRoot::UserAgentShadowRoot)
-            return false;
-        return true;
-    }
-
-    if (element->part() != selector->argument())
-        return false;
-    if (selector->isMatchUserAgentOnly() && root->type() != ShadowRoot::UserAgentShadowRoot)
+    if (element->shadowPseudoId() != selector.value())
         return false;
+
     return true;
 }
 
-Element* SelectorChecker::parentElement(const SelectorCheckingContext& context) const
+Element* SelectorChecker::parentElement(const SelectorCheckingContext& context, bool allowToCrossBoundary) const
 {
     // CrossesBoundary means we don't care any context.scope. So we can walk up from a shadow root to its shadow host.
-    if ((context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) == SelectorChecker::CrossesBoundary)
+    if (allowToCrossBoundary)
         return context.element->parentOrShadowHostElement();
 
-    // If context.scope is not a shadow host, we cannot walk up from a shadow root to its shadow host.
-    if (!(context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost))
+    // If context.scope is a shadow host, we should walk up from a shadow root to its shadow host.
+    if ((context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost) && context.scope == context.element->shadowHost())
+        return context.element->parentOrShadowHostElement();
+
+    if ((context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope)
         return context.element->parentElement();
 
-    // If behaviorAtBoundary is StaysWithInTreeScope, we cannot walk up from a shadow root to its shadow host.
-    return (context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope ? context.element->parentOrShadowHostElement() : context.element->parentElement();
+    // If context.scope is some element in some shadow tree and querySelector initialized the context,
+    // e.g. shadowRoot.querySelector(':host *'),
+    // (a) context.element has the same treescope as context.scope, need to walk up to its shadow host.
+    // (b) Otherwise, should not walk up from a shadow root to a shadow host.
+    if (context.scope && context.scope->treeScope() == context.element->treeScope())
+        return context.element->parentOrShadowHostElement();
+
+    return context.element->parentElement();
 }
 
 bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingContext& context) const
@@ -116,7 +111,7 @@ bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingCont
         return context.scope->contains(context.element);
 
     // If a given element is scope, i.e. shadow host, matches.
-    if (context.element == context.scope)
+    if (context.element == context.scope && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element)))
         return true;
 
     ShadowRoot* root = context.element->containingShadowRoot();
@@ -127,6 +122,16 @@ bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingCont
     return root->host() == context.scope;
 }
 
+static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context)
+{
+    if ((context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope)
+        return context.element == context.scope;
+
+    if (context.scope && context.scope->isInShadowTree())
+        return context.element == context.scope->containingShadowRoot()->host();
+    return false;
+}
+
 // Recursive check of selectors and combinators
 // It can return 4 different values:
 // * SelectorMatches          - the selector matches the element e
@@ -134,19 +139,23 @@ bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingCont
 // * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
 // * SelectorFailsCompletely  - the selector fails for e and any sibling or ancestor of e
 template<typename SiblingTraversalStrategy>
-SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, PseudoId& dynamicPseudo, const SiblingTraversalStrategy& siblingTraversalStrategy) const
+SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
 {
     // first selector has to match
-    if (!checkOne(context, siblingTraversalStrategy))
+    unsigned specificity = 0;
+    if (!checkOne(context, siblingTraversalStrategy, &specificity))
         return SelectorFailsLocally;
 
     if (context.selector->m_match == CSSSelector::PseudoElement) {
         if (context.selector->isCustomPseudoElement()) {
-            if (!matchesCustomPseudoElement(context.element, context.selector))
+            if (!matchesCustomPseudoElement(context.element, *context.selector))
                 return SelectorFailsLocally;
         } else if (context.selector->isContentPseudoElement()) {
             if (!context.element->isInShadowTree() || !context.element->isInsertionPoint())
                 return SelectorFailsLocally;
+        } else if (context.selector->isShadowPseudoElement()) {
+            if (!context.element->isInShadowTree() || !context.previousElement)
+                return SelectorFailsCompletely;
         } else {
             if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
                 return SelectorFailsLocally;
@@ -154,113 +163,183 @@ SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& con
             PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType());
             if (pseudoId == FIRST_LETTER)
                 context.element->document().styleEngine()->setUsesFirstLetterRules(true);
-            if (pseudoId != NOPSEUDO && m_mode != SharingRules)
-                dynamicPseudo = pseudoId;
+            if (pseudoId != NOPSEUDO && m_mode != SharingRules && result)
+                result->dynamicPseudo = pseudoId;
         }
     }
 
-    // The rest of the selectors has to match
-    CSSSelector::Relation relation = context.selector->relation();
-
     // Prepare next selector
     const CSSSelector* historySelector = context.selector->tagHistory();
-    if (!historySelector)
-        return scopeContainsLastMatchedElement(context) ? SelectorMatches : SelectorFailsLocally;
-
-    SelectorCheckingContext nextContext(context);
-    nextContext.selector = historySelector;
+    if (!historySelector) {
+        if (scopeContainsLastMatchedElement(context)) {
+            if (result)
+                result->specificity += specificity;
+            return SelectorMatches;
+        }
+        return SelectorFailsLocally;
+    }
 
-    PseudoId ignoreDynamicPseudo = NOPSEUDO;
-    if (relation != CSSSelector::SubSelector) {
+    Match match;
+    if (context.selector->relation() != CSSSelector::SubSelector) {
         // Abort if the next selector would exceed the scope.
-        if (context.element == context.scope && (context.behaviorAtBoundary & BoundaryBehaviorMask) != StaysWithinTreeScope)
+        if (nextSelectorExceedsScope(context))
             return SelectorFailsCompletely;
 
         // Bail-out if this selector is irrelevant for the pseudoId
-        if (context.pseudoId != NOPSEUDO && context.pseudoId != dynamicPseudo)
+        if (context.pseudoId != NOPSEUDO && (!result || context.pseudoId != result->dynamicPseudo))
             return SelectorFailsCompletely;
 
-        // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
-        if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
-            nextContext.visitedMatchType = VisitedMatchDisabled;
-
-        nextContext.pseudoId = NOPSEUDO;
+        if (result) {
+            TemporaryChange<PseudoId> dynamicPseudoScope(result->dynamicPseudo, NOPSEUDO);
+            match = matchForRelation(context, siblingTraversalStrategy, result);
+        } else {
+            return matchForRelation(context, siblingTraversalStrategy, 0);
+        }
+    } else {
+        match = matchForSubSelector(context, siblingTraversalStrategy, result);
     }
+    if (match != SelectorMatches || !result)
+        return match;
+
+    result->specificity += specificity;
+    return SelectorMatches;
+}
+
+static inline SelectorChecker::SelectorCheckingContext prepareNextContextForRelation(const SelectorChecker::SelectorCheckingContext& context)
+{
+    SelectorChecker::SelectorCheckingContext nextContext(context);
+    ASSERT(context.selector->tagHistory());
+    nextContext.selector = context.selector->tagHistory();
+    return nextContext;
+}
+
+static inline bool isAuthorShadowRoot(const Node* node)
+{
+    return node && node->isShadowRoot() && toShadowRoot(node)->type() == ShadowRoot::AuthorShadowRoot;
+}
+
+template<typename SiblingTraversalStrategy>
+SelectorChecker::Match SelectorChecker::matchForSubSelector(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
+{
+    SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
+
+    PseudoId dynamicPseudo = result ? result->dynamicPseudo : NOPSEUDO;
+    // a selector is invalid if something follows a pseudo-element
+    // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
+    // to follow the pseudo elements.
+    nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
+    nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
+    if ((context.elementStyle || m_mode == CollectingCSSRules || m_mode == CollectingStyleRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
+        && !nextContext.hasSelectionPseudo
+        && !(nextContext.hasScrollbarPseudo && nextContext.selector->m_match == CSSSelector::PseudoClass))
+        return SelectorFailsCompletely;
+
+    nextContext.isSubSelector = true;
+    return match(nextContext, siblingTraversalStrategy, result);
+}
+
+static bool selectorMatchesShadowRoot(const CSSSelector* selector)
+{
+    return selector && selector->isShadowPseudoElement();
+}
+
+template<typename SiblingTraversalStrategy>
+SelectorChecker::Match SelectorChecker::matchForPseudoShadow(const ContainerNode* node, const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
+{
+    if (!isAuthorShadowRoot(node))
+        return SelectorFailsCompletely;
+    return match(context, siblingTraversalStrategy, result);
+}
+
+template<typename SiblingTraversalStrategy>
+SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
+{
+    SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
+    nextContext.previousElement = context.element;
+
+    CSSSelector::Relation relation = context.selector->relation();
+
+    // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
+    if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
+        nextContext.visitedMatchType = VisitedMatchDisabled;
+
+    nextContext.pseudoId = NOPSEUDO;
 
     switch (relation) {
     case CSSSelector::Descendant:
         if (context.selector->relationIsAffectedByPseudoContent()) {
             for (Element* element = context.element; element; element = element->parentElement()) {
-                if (matchForShadowDistributed(element, siblingTraversalStrategy, ignoreDynamicPseudo, nextContext) == SelectorMatches)
+                if (matchForShadowDistributed(element, siblingTraversalStrategy, nextContext, result) == SelectorMatches)
                     return SelectorMatches;
             }
             return SelectorFailsCompletely;
         }
         nextContext.isSubSelector = false;
         nextContext.elementStyle = 0;
+
+        if (selectorMatchesShadowRoot(nextContext.selector))
+            return matchForPseudoShadow(context.element->containingShadowRoot(), nextContext, siblingTraversalStrategy, result);
+
         for (nextContext.element = parentElement(context); nextContext.element; nextContext.element = parentElement(nextContext)) {
-            Match match = this->match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
+            Match match = this->match(nextContext, siblingTraversalStrategy, result);
             if (match == SelectorMatches || match == SelectorFailsCompletely)
                 return match;
-            if (nextContext.element == nextContext.scope && (nextContext.behaviorAtBoundary & BoundaryBehaviorMask) != StaysWithinTreeScope)
+            if (nextSelectorExceedsScope(nextContext))
                 return SelectorFailsCompletely;
         }
         return SelectorFailsCompletely;
     case CSSSelector::Child:
         {
             if (context.selector->relationIsAffectedByPseudoContent())
-                return matchForShadowDistributed(context.element, siblingTraversalStrategy, ignoreDynamicPseudo, nextContext);
+                return matchForShadowDistributed(context.element, siblingTraversalStrategy, nextContext, result);
+
+            nextContext.isSubSelector = false;
+            nextContext.elementStyle = 0;
+
+            if (selectorMatchesShadowRoot(nextContext.selector))
+                return matchForPseudoShadow(context.element->parentNode(), nextContext, siblingTraversalStrategy, result);
 
             nextContext.element = parentElement(context);
             if (!nextContext.element)
                 return SelectorFailsCompletely;
-
-            nextContext.isSubSelector = false;
-            nextContext.elementStyle = 0;
-            return match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
+            return match(nextContext, siblingTraversalStrategy, result);
         }
     case CSSSelector::DirectAdjacent:
+        // Shadow roots can't have sibling elements
+        if (selectorMatchesShadowRoot(nextContext.selector))
+            return SelectorFailsCompletely;
+
         if (m_mode == ResolvingStyle) {
-            if (Element* parent = parentElement(context))
+            if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
                 parent->setChildrenAffectedByDirectAdjacentRules();
         }
-        nextContext.element = context.element->previousElementSibling();
+        nextContext.element = ElementTraversal::previousSibling(*context.element);
         if (!nextContext.element)
             return SelectorFailsAllSiblings;
         nextContext.isSubSelector = false;
         nextContext.elementStyle = 0;
-        return match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
+        return match(nextContext, siblingTraversalStrategy, result);
 
     case CSSSelector::IndirectAdjacent:
+        // Shadow roots can't have sibling elements
+        if (selectorMatchesShadowRoot(nextContext.selector))
+            return SelectorFailsCompletely;
+
         if (m_mode == ResolvingStyle) {
-            if (Element* parent = parentElement(context))
-                parent->setChildrenAffectedByForwardPositionalRules();
+            if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
+                parent->setChildrenAffectedByIndirectAdjacentRules();
         }
-        nextContext.element = context.element->previousElementSibling();
+        nextContext.element = ElementTraversal::previousSibling(*context.element);
         nextContext.isSubSelector = false;
         nextContext.elementStyle = 0;
-        for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) {
-            Match match = this->match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
+        for (; nextContext.element; nextContext.element = ElementTraversal::previousSibling(*nextContext.element)) {
+            Match match = this->match(nextContext, siblingTraversalStrategy, result);
             if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
                 return match;
         };
         return SelectorFailsAllSiblings;
 
-    case CSSSelector::SubSelector:
-        // a selector is invalid if something follows a pseudo-element
-        // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
-        // to follow the pseudo elements.
-        nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
-        nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
-        if ((context.elementStyle || m_mode == CollectingCSSRules || m_mode == CollectingStyleRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
-            && !nextContext.hasSelectionPseudo
-            && !(nextContext.hasScrollbarPseudo && nextContext.selector->m_match == CSSSelector::PseudoClass))
-            return SelectorFailsCompletely;
-        nextContext.isSubSelector = true;
-        return match(nextContext, dynamicPseudo, siblingTraversalStrategy);
-
     case CSSSelector::ShadowPseudo:
-    case CSSSelector::ChildTree:
         {
             // If we're in the same tree-scope as the scoping element, then following a shadow descendant combinator would escape that and thus the scope.
             if (context.scope && context.scope->treeScope() == context.element->treeScope() && (context.behaviorAtBoundary & BoundaryBehaviorMask) != StaysWithinTreeScope)
@@ -272,22 +351,25 @@ SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& con
             nextContext.element = shadowHost;
             nextContext.isSubSelector = false;
             nextContext.elementStyle = 0;
-            return this->match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
+            return this->match(nextContext, siblingTraversalStrategy, result);
         }
 
-    case CSSSelector::DescendantTree:
+    case CSSSelector::ShadowDeep:
         {
             nextContext.isSubSelector = false;
             nextContext.elementStyle = 0;
-            for (nextContext.element = parentElement(context); nextContext.element; nextContext.element = parentElement(nextContext)) {
-                Match match = this->match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
+            for (nextContext.element = parentElement(context, true); nextContext.element; nextContext.element = parentElement(nextContext, true)) {
+                Match match = this->match(nextContext, siblingTraversalStrategy, result);
                 if (match == SelectorMatches || match == SelectorFailsCompletely)
                     return match;
-                if (nextContext.element == nextContext.scope && (nextContext.behaviorAtBoundary & BoundaryBehaviorMask) != StaysWithinTreeScope)
+                if (nextSelectorExceedsScope(nextContext))
                     return SelectorFailsCompletely;
             }
             return SelectorFailsCompletely;
         }
+
+    case CSSSelector::SubSelector:
+        ASSERT_NOT_REACHED();
     }
 
     ASSERT_NOT_REACHED();
@@ -295,19 +377,34 @@ SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& con
 }
 
 template<typename SiblingTraversalStrategy>
-SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* element, const SiblingTraversalStrategy& siblingTraversalStrategy, PseudoId& dynamicPseudo, SelectorCheckingContext& nextContext) const
+SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* element, const SiblingTraversalStrategy& siblingTraversalStrategy, SelectorCheckingContext& nextContext, MatchResult* result) const
 {
     ASSERT(element);
     Vector<InsertionPoint*, 8> insertionPoints;
+
+    const ContainerNode* scope = nextContext.scope;
+    BehaviorAtBoundary behaviorAtBoundary = nextContext.behaviorAtBoundary;
+
     collectDestinationInsertionPoints(*element, insertionPoints);
     for (size_t i = 0; i < insertionPoints.size(); ++i) {
         nextContext.element = insertionPoints[i];
+
+        // If a given scope is a shadow host of an insertion point but behaviorAtBoundary doesn't have ScopeIsShadowHost,
+        // we need to update behaviorAtBoundary to make selectors like ":host > ::content" work correctly.
+        if (m_mode == SharingRules) {
+            nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowHost);
+            nextContext.scope = insertionPoints[i]->containingShadowRoot()->shadowHost();
+        } else if (scope == insertionPoints[i]->containingShadowRoot()->shadowHost() && !(behaviorAtBoundary & ScopeIsShadowHost))
+            nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowHost);
+        else
+            nextContext.behaviorAtBoundary = behaviorAtBoundary;
+
         nextContext.isSubSelector = false;
         nextContext.elementStyle = 0;
-        if (match(nextContext, dynamicPseudo, siblingTraversalStrategy) == SelectorMatches)
+        if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches)
             return SelectorMatches;
     }
-    return SelectorFailsCompletely;
+    return SelectorFailsLocally;
 }
 
 static inline bool containsHTMLSpace(const AtomicString& string)
@@ -318,9 +415,9 @@ static inline bool containsHTMLSpace(const AtomicString& string)
     return false;
 }
 
-static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
+static bool attributeValueMatches(const Attribute& attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
 {
-    const AtomicString& value = attributeItem->value();
+    const AtomicString& value = attributeItem.value();
     if (value.isNull())
         return false;
 
@@ -381,59 +478,80 @@ static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::M
     return true;
 }
 
-static bool anyAttributeMatches(Element& element, CSSSelector::Match match, const QualifiedName& selectorAttr, const AtomicString& selectorValue, bool caseSensitive)
+static bool anyAttributeMatches(Element& element, CSSSelector::Match match, const CSSSelector& selector)
 {
-    ASSERT(element.hasAttributesWithoutUpdate());
-    for (size_t i = 0; i < element.attributeCount(); ++i) {
-        const Attribute* attributeItem = element.attributeItem(i);
+    const QualifiedName& selectorAttr = selector.attribute();
+    ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar.
+
+    // Synchronize the attribute in case it is lazy-computed.
+    // Currently all lazy properties have a null namespace, so only pass localName().
+    element.synchronizeAttribute(selectorAttr.localName());
+
+    if (!element.hasAttributesWithoutUpdate())
+        return false;
+
+    const AtomicString& selectorValue =  selector.value();
+
+    unsigned attributeCount = element.attributeCount();
+    for (size_t i = 0; i < attributeCount; ++i) {
+        const Attribute& attributeItem = element.attributeItem(i);
 
-        if (!attributeItem->matches(selectorAttr))
+        if (!attributeItem.matches(selectorAttr))
             continue;
 
-        if (attributeValueMatches(attributeItem, match, selectorValue, caseSensitive))
+        if (attributeValueMatches(attributeItem, match, selectorValue, true))
             return true;
+
+        // Case sensitivity for attribute matching is looser than hasAttribute or
+        // Element::shouldIgnoreAttributeCase() for now. Unclear if that's correct.
+        bool caseSensitive = !element.document().isHTMLDocument() || HTMLDocument::isCaseSensitiveAttribute(selectorAttr);
+
+        // If case-insensitive, re-check, and count if result differs.
+        // See http://code.google.com/p/chromium/issues/detail?id=327060
+        if (!caseSensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) {
+            UseCounter::count(element.document(), UseCounter::CaseInsensitiveAttrSelectorMatch);
+            return true;
+        }
     }
 
     return false;
 }
 
 template<typename SiblingTraversalStrategy>
-bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy) const
+bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, unsigned* specificity) const
 {
     ASSERT(context.element);
     Element& element = *context.element;
-    const CSSSelector* const & selector = context.selector;
-    ASSERT(selector);
-    bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.behaviorAtBoundary, context.scope);
-
-    if (selector->m_match == CSSSelector::Tag)
-        return SelectorChecker::tagMatches(element, selector->tagQName(), elementIsHostInItsShadowTree ? MatchingHostInItsShadowTree : MatchingElement);
+    ASSERT(context.selector);
+    const CSSSelector& selector = *context.selector;
 
-    if (selector->m_match == CSSSelector::Class)
-        return element.hasClass() && element.classNames().contains(selector->value()) && !elementIsHostInItsShadowTree;
+    bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.behaviorAtBoundary, context.scope);
 
-    if (selector->m_match == CSSSelector::Id)
-        return element.hasID() && element.idForStyleResolution() == selector->value() && !elementIsHostInItsShadowTree;
+    // Only :host and :ancestor should match the host: http://drafts.csswg.org/css-scoping/#host-element
+    if (elementIsHostInItsShadowTree && !selector.isHostPseudoClass())
+        return false;
 
-    if (selector->isAttributeSelector()) {
-        const QualifiedName& attr = selector->attribute();
+    if (selector.m_match == CSSSelector::Tag)
+        return SelectorChecker::tagMatches(element, selector.tagQName());
 
-        if (!element.hasAttributes() || elementIsHostInItsShadowTree)
-            return false;
+    if (selector.m_match == CSSSelector::Class)
+        return element.hasClass() && element.classNames().contains(selector.value());
 
-        bool caseSensitive = !m_documentIsHTML || HTMLDocument::isCaseSensitiveAttribute(attr);
+    if (selector.m_match == CSSSelector::Id)
+        return element.hasID() && element.idForStyleResolution() == selector.value();
 
-        if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector->m_match), attr, selector->value(), caseSensitive))
+    if (selector.isAttributeSelector()) {
+        if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector.m_match), selector))
             return false;
     }
 
-    if (selector->m_match == CSSSelector::PseudoClass) {
+    if (selector.m_match == CSSSelector::PseudoClass) {
         // Handle :not up front.
-        if (selector->pseudoType() == CSSSelector::PseudoNot) {
+        if (selector.pseudoType() == CSSSelector::PseudoNot) {
             SelectorCheckingContext subContext(context);
             subContext.isSubSelector = true;
-            ASSERT(selector->selectorList());
-            for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
+            ASSERT(selector.selectorList());
+            for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
                 // :not cannot nest. I don't really know why this is a
                 // restriction in CSS3, but it is, so let's honor it.
                 // the parser enforces that this never occurs
@@ -441,6 +559,10 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
                 // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
                 if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled))
                     return true;
+                // context.scope is not available if m_mode == SharingRules.
+                // We cannot determine whether :host or :scope matches a given element or not.
+                if (m_mode == SharingRules && (subContext.selector->isHostPseudoClass() || subContext.selector->pseudoType() == CSSSelector::PseudoScope))
+                    return true;
                 if (!checkOne(subContext, DOMSiblingTraversalStrategy()))
                     return true;
             }
@@ -449,12 +571,12 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             // (since there are no elements involved).
             return checkScrollbarPseudoClass(context, &element.document(), selector);
         } else if (context.hasSelectionPseudo) {
-            if (selector->pseudoType() == CSSSelector::PseudoWindowInactive)
+            if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
                 return !element.document().page()->focusController().isActive();
         }
 
         // Normal element pseudo class checking.
-        switch (selector->pseudoType()) {
+        switch (selector.pseudoType()) {
             // Pseudo classes:
         case CSSSelector::PseudoNot:
             break; // Already handled up above.
@@ -485,8 +607,8 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             }
         case CSSSelector::PseudoFirstChild:
             // first-child matches the first child that is an element
-            if (Element* parent = element.parentElement()) {
-                bool result = siblingTraversalStrategy.isFirstChild(&element);
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
+                bool result = siblingTraversalStrategy.isFirstChild(element);
                 if (m_mode == ResolvingStyle) {
                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
                     parent->setChildrenAffectedByFirstChildRules();
@@ -498,8 +620,8 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             break;
         case CSSSelector::PseudoFirstOfType:
             // first-of-type matches the first element of its type
-            if (Element* parent = element.parentElement()) {
-                bool result = siblingTraversalStrategy.isFirstOfType(&element, element.tagQName());
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
+                bool result = siblingTraversalStrategy.isFirstOfType(element, element.tagQName());
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByForwardPositionalRules();
                 return result;
@@ -507,8 +629,8 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             break;
         case CSSSelector::PseudoLastChild:
             // last-child matches the last child that is an element
-            if (Element* parent = element.parentElement()) {
-                bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(&element);
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
+                bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
                 if (m_mode == ResolvingStyle) {
                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
                     parent->setChildrenAffectedByLastChildRules();
@@ -520,18 +642,18 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             break;
         case CSSSelector::PseudoLastOfType:
             // last-of-type matches the last element of its type
-            if (Element* parent = element.parentElement()) {
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByBackwardPositionalRules();
                 if (!parent->isFinishedParsingChildren())
                     return false;
-                return siblingTraversalStrategy.isLastOfType(&element, element.tagQName());
+                return siblingTraversalStrategy.isLastOfType(element, element.tagQName());
             }
             break;
         case CSSSelector::PseudoOnlyChild:
-            if (Element* parent = element.parentElement()) {
-                bool firstChild = siblingTraversalStrategy.isFirstChild(&element);
-                bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(&element);
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
+                bool firstChild = siblingTraversalStrategy.isFirstChild(element);
+                bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
                 if (m_mode == ResolvingStyle) {
                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
                     parent->setChildrenAffectedByFirstChildRules();
@@ -546,69 +668,68 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             break;
         case CSSSelector::PseudoOnlyOfType:
             // FIXME: This selector is very slow.
-            if (Element* parent = element.parentElement()) {
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
                 if (m_mode == ResolvingStyle) {
                     parent->setChildrenAffectedByForwardPositionalRules();
                     parent->setChildrenAffectedByBackwardPositionalRules();
                 }
                 if (!parent->isFinishedParsingChildren())
                     return false;
-                return siblingTraversalStrategy.isFirstOfType(&element, element.tagQName()) && siblingTraversalStrategy.isLastOfType(&element, element.tagQName());
+                return siblingTraversalStrategy.isFirstOfType(element, element.tagQName()) && siblingTraversalStrategy.isLastOfType(element, element.tagQName());
             }
             break;
         case CSSSelector::PseudoNthChild:
-            if (!selector->parseNth())
+            if (!selector.parseNth())
                 break;
-            if (Element* parent = element.parentElement()) {
-                int count = 1 + siblingTraversalStrategy.countElementsBefore(&element);
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
+                int count = 1 + siblingTraversalStrategy.countElementsBefore(element);
                 if (m_mode == ResolvingStyle) {
                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
-                    element.setChildIndex(count);
                     if (childStyle)
                         childStyle->setUnique();
                     parent->setChildrenAffectedByForwardPositionalRules();
                 }
 
-                if (selector->matchNth(count))
+                if (selector.matchNth(count))
                     return true;
             }
             break;
         case CSSSelector::PseudoNthOfType:
-            if (!selector->parseNth())
+            if (!selector.parseNth())
                 break;
-            if (Element* parent = element.parentElement()) {
-                int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(&element, element.tagQName());
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
+                int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element.tagQName());
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByForwardPositionalRules();
 
-                if (selector->matchNth(count))
+                if (selector.matchNth(count))
                     return true;
             }
             break;
         case CSSSelector::PseudoNthLastChild:
-            if (!selector->parseNth())
+            if (!selector.parseNth())
                 break;
-            if (Element* parent = element.parentElement()) {
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByBackwardPositionalRules();
                 if (!parent->isFinishedParsingChildren())
                     return false;
-                int count = 1 + siblingTraversalStrategy.countElementsAfter(&element);
-                if (selector->matchNth(count))
+                int count = 1 + siblingTraversalStrategy.countElementsAfter(element);
+                if (selector.matchNth(count))
                     return true;
             }
             break;
         case CSSSelector::PseudoNthLastOfType:
-            if (!selector->parseNth())
+            if (!selector.parseNth())
                 break;
-            if (Element* parent = element.parentElement()) {
+            if (ContainerNode* parent = element.parentElementOrShadowRoot()) {
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByBackwardPositionalRules();
                 if (!parent->isFinishedParsingChildren())
                     return false;
 
-                int count = 1 + siblingTraversalStrategy.countElementsOfTypeAfter(&element, element.tagQName());
-                if (selector->matchNth(count))
+                int count = 1 + siblingTraversalStrategy.countElementsOfTypeAfter(element, element.tagQName());
+                if (selector.matchNth(count))
                     return true;
             }
             break;
@@ -620,10 +741,9 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             {
                 SelectorCheckingContext subContext(context);
                 subContext.isSubSelector = true;
-                PseudoId ignoreDynamicPseudo = NOPSEUDO;
-                ASSERT(selector->selectorList());
-                for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
-                    if (match(subContext, ignoreDynamicPseudo, siblingTraversalStrategy) == SelectorMatches)
+                ASSERT(selector.selectorList());
+                for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
+                    if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
                         return true;
                 }
             }
@@ -644,22 +764,28 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
                 if (context.elementStyle)
                     context.elementStyle->setAffectedByDrag();
                 else
-                    element.setChildrenAffectedByDrag(true);
+                    element.setChildrenAffectedByDrag();
             }
             if (element.renderer() && element.renderer()->isDragging())
                 return true;
             break;
         case CSSSelector::PseudoFocus:
+            if (m_mode == ResolvingStyle) {
+                if (context.elementStyle)
+                    context.elementStyle->setAffectedByFocus();
+                else
+                    element.setChildrenAffectedByFocus();
+            }
             return matchesFocusPseudoClass(element);
         case CSSSelector::PseudoHover:
             // If we're in quirks mode, then hover should never match anchors with no
             // href and *:hover should not match anything. This is important for sites like wsj.com.
-            if (m_strictParsing || context.isSubSelector || (selector->m_match == CSSSelector::Tag && selector->tagQName() != anyQName() && !isHTMLAnchorElement(element)) || element.isLink()) {
+            if (m_strictParsing || context.isSubSelector || (selector.m_match == CSSSelector::Tag && selector.tagQName() != anyQName() && !isHTMLAnchorElement(element)) || element.isLink()) {
                 if (m_mode == ResolvingStyle) {
                     if (context.elementStyle)
                         context.elementStyle->setAffectedByHover();
                     else
-                        element.setChildrenAffectedByHover(true);
+                        element.setChildrenAffectedByHover();
                 }
                 if (element.hovered() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoHover))
                     return true;
@@ -668,19 +794,19 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
         case CSSSelector::PseudoActive:
             // If we're in quirks mode, then :active should never match anchors with no
             // href and *:active should not match anything.
-            if (m_strictParsing || context.isSubSelector || (selector->m_match == CSSSelector::Tag && selector->tagQName() != anyQName() && !isHTMLAnchorElement(element)) || element.isLink()) {
+            if (m_strictParsing || context.isSubSelector || (selector.m_match == CSSSelector::Tag && selector.tagQName() != anyQName() && !isHTMLAnchorElement(element)) || element.isLink()) {
                 if (m_mode == ResolvingStyle) {
                     if (context.elementStyle)
                         context.elementStyle->setAffectedByActive();
                     else
-                        element.setChildrenAffectedByActive(true);
+                        element.setChildrenAffectedByActive();
                 }
                 if (element.active() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoActive))
                     return true;
             }
             break;
         case CSSSelector::PseudoEnabled:
-            if (element.isFormControlElement() || element.hasTagName(optionTag) || isHTMLOptGroupElement(element))
+            if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
                 return !element.isDisabledFormControl();
             break;
         case CSSSelector::PseudoFullPageMedia:
@@ -689,7 +815,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
         case CSSSelector::PseudoDefault:
             return element.isDefaultButtonForForm();
         case CSSSelector::PseudoDisabled:
-            if (element.isFormControlElement() || element.hasTagName(optionTag) || isHTMLOptGroupElement(element))
+            if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
                 return element.isDisabledFormControl();
             break;
         case CSSSelector::PseudoReadOnly:
@@ -708,7 +834,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             return element.willValidate() && !element.isValidFormControlElement();
         case CSSSelector::PseudoChecked:
             {
-                if (element.hasTagName(inputTag)) {
+                if (isHTMLInputElement(element)) {
                     HTMLInputElement& inputElement = toHTMLInputElement(element);
                     // Even though WinIE allows checked and indeterminate to
                     // co-exist, the CSS selector spec says that you can't be
@@ -717,7 +843,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
                     // test for matching the pseudo.
                     if (inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate())
                         return true;
-                } else if (element.hasTagName(optionTag) && toHTMLOptionElement(element).selected())
+                } else if (isHTMLOptionElement(element) && toHTMLOptionElement(element).selected())
                     return true;
                 break;
             }
@@ -730,11 +856,11 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
         case CSSSelector::PseudoLang:
             {
                 AtomicString value;
-                if (element.isWebVTTElement())
-                    value = toWebVTTElement(element).language();
+                if (element.isVTTElement())
+                    value = toVTTElement(element).language();
                 else
                     value = element.computeInheritedLanguage();
-                const AtomicString& argument = selector->argument();
+                const AtomicString& argument = selector.argument();
                 if (value.isEmpty() || !value.startsWith(argument, false))
                     break;
                 if (value.length() != argument.length() && value[argument.length()] != '-')
@@ -746,9 +872,9 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             // element is an element in the document, the 'full-screen' pseudoclass applies to
             // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
             // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
-            if (element.isFrameElementBase() && element.containsFullScreenElement())
+            if (isHTMLFrameElementBase(element) && element.containsFullScreenElement())
                 return true;
-            if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(&element.document())) {
+            if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(element.document())) {
                 if (!fullscreen->webkitIsFullScreen())
                     return false;
                 return element == fullscreen->webkitCurrentFullScreenElement();
@@ -759,13 +885,9 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
         case CSSSelector::PseudoFullScreenDocument:
             // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
             // to all elements of that Document.
-            if (!FullscreenElementStack::isFullScreen(&element.document()))
+            if (!FullscreenElementStack::isFullScreen(element.document()))
                 return false;
             return true;
-        case CSSSelector::PseudoSeamlessDocument:
-            // While a document is rendered in a seamless iframe, the 'seamless-document' pseudoclass applies
-            // to all elements of that Document.
-            return element.document().shouldDisplaySeamlesslyWithParent();
         case CSSSelector::PseudoInRange:
             element.document().setContainsValidityStyleRules();
             return element.isInRange();
@@ -773,13 +895,15 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             element.document().setContainsValidityStyleRules();
             return element.isOutOfRange();
         case CSSSelector::PseudoFutureCue:
-            return (element.isWebVTTElement() && !toWebVTTElement(element).isPastNode());
+            return (element.isVTTElement() && !toVTTElement(element).isPastNode());
         case CSSSelector::PseudoPastCue:
-            return (element.isWebVTTElement() && toWebVTTElement(element).isPastNode());
+            return (element.isVTTElement() && toVTTElement(element).isPastNode());
 
         case CSSSelector::PseudoScope:
             {
-                const Node* contextualReferenceNode = !context.scope || (context.behaviorAtBoundary & BoundaryBehaviorMask) == CrossesBoundary ? element.document().documentElement() : context.scope;
+                if (m_mode == SharingRules)
+                    return true;
+                const Node* contextualReferenceNode = !context.scope ? element.document().documentElement() : context.scope;
                 if (element == contextualReferenceNode)
                     return true;
                 break;
@@ -791,29 +915,58 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             break;
 
         case CSSSelector::PseudoHost:
+        case CSSSelector::PseudoHostContext:
             {
+                if (m_mode == SharingRules)
+                    return true;
                 // :host only matches a shadow host when :host is in a shadow tree of the shadow host.
-                if (!context.scope || !(context.behaviorAtBoundary & ScopeIsShadowHost) || context.scope != element)
+                if (!context.scope)
+                    return false;
+                const ContainerNode* shadowHost = (context.behaviorAtBoundary & ScopeIsShadowHost) ? context.scope : (context.scope->isInShadowTree() ? context.scope->shadowHost() : 0);
+                if (!shadowHost || shadowHost != element)
                     return false;
                 ASSERT(element.shadow());
 
                 // For empty parameter case, i.e. just :host or :host().
-                if (!selector->selectorList())
+                if (!selector.selectorList()) // Use *'s specificity. So just 0.
                     return true;
 
                 SelectorCheckingContext subContext(context);
                 subContext.isSubSelector = true;
-                subContext.behaviorAtBoundary = CrossesBoundary;
-                subContext.scope = 0;
-                // Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
-                for (Element* nextElement = &element; nextElement; nextElement = NodeRenderingTraversal::parentElement(nextElement)) {
-                    // If one of simple selectors matches an element, returns SelectorMatches. Just "OR".
-                    for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
-                        PseudoId ignoreDynamicPseudo = NOPSEUDO;
-                        subContext.element = nextElement;
-                        if (match(subContext, ignoreDynamicPseudo, siblingTraversalStrategy) == SelectorMatches)
-                            return true;
-                    }
+
+                bool matched = false;
+                unsigned maxSpecificity = 0;
+
+                // If one of simple selectors matches an element, returns SelectorMatches. Just "OR".
+                for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
+                    subContext.behaviorAtBoundary = ScopeIsShadowHostInPseudoHostParameter;
+                    subContext.scope = shadowHost;
+                    // Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
+                    Element* nextElement = &element;
+                    SelectorCheckingContext hostContext(subContext);
+                    do {
+                        MatchResult subResult;
+                        hostContext.element = nextElement;
+                        if (match(hostContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
+                            matched = true;
+                            // Consider div:host(div:host(div:host(div:host...))).
+                            maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity);
+                            break;
+                        }
+                        hostContext.behaviorAtBoundary = DoesNotCrossBoundary;
+                        hostContext.scope = 0;
+
+                        if (selector.pseudoType() == CSSSelector::PseudoHost)
+                            break;
+
+                        hostContext.elementStyle = 0;
+                        nextElement = NodeRenderingTraversal::parentElement(nextElement);
+                    } while (nextElement);
+                }
+                if (matched) {
+                    if (specificity)
+                        *specificity = maxSpecificity;
+                    return true;
                 }
             }
             break;
@@ -837,16 +990,15 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             break;
         }
         return false;
-    }
-    else if (selector->m_match == CSSSelector::PseudoElement && selector->pseudoType() == CSSSelector::PseudoCue) {
+    } else if (selector.m_match == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) {
         SelectorCheckingContext subContext(context);
         subContext.isSubSelector = true;
         subContext.behaviorAtBoundary = StaysWithinTreeScope;
 
-        PseudoId ignoreDynamicPseudo = NOPSEUDO;
-        const CSSSelector* const & selector = context.selector;
-        for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
-            if (match(subContext, ignoreDynamicPseudo, siblingTraversalStrategy) == SelectorMatches)
+        const CSSSelector* contextSelector = context.selector;
+        ASSERT(contextSelector);
+        for (subContext.selector = contextSelector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
+            if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
                 return true;
         }
         return false;
@@ -855,21 +1007,21 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
     return true;
 }
 
-bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector* selector) const
+bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector& selector) const
 {
     RenderScrollbar* scrollbar = context.scrollbar;
     ScrollbarPart part = context.scrollbarPart;
 
     // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
     // pseudo class and just apply to everything.
-    if (selector->pseudoType() == CSSSelector::PseudoWindowInactive)
+    if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
         return !document->page()->focusController().isActive();
 
     if (!scrollbar)
         return false;
 
-    ASSERT(selector->m_match == CSSSelector::PseudoClass);
-    switch (selector->pseudoType()) {
+    ASSERT(selector.m_match == CSSSelector::PseudoClass);
+    switch (selector.pseudoType()) {
     case CSSSelector::PseudoEnabled:
         return scrollbar->enabled();
     case CSSSelector::PseudoDisabled:
@@ -936,19 +1088,19 @@ bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& c
     }
 }
 
-unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector)
+unsigned SelectorChecker::determineLinkMatchType(const CSSSelector& selector)
 {
     unsigned linkMatchType = MatchAll;
 
     // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
     // :visited never matches other elements than the innermost link element.
-    for (; selector; selector = selector->tagHistory()) {
-        switch (selector->pseudoType()) {
+    for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
+        switch (current->pseudoType()) {
         case CSSSelector::PseudoNot:
             {
                 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
-                ASSERT(selector->selectorList());
-                for (const CSSSelector* subSelector = selector->selectorList()->first(); subSelector; subSelector = subSelector->tagHistory()) {
+                ASSERT(current->selectorList());
+                for (const CSSSelector* subSelector = current->selectorList()->first(); subSelector; subSelector = subSelector->tagHistory()) {
                     CSSSelector::PseudoType subType = subSelector->pseudoType();
                     if (subType == CSSSelector::PseudoVisited)
                         linkMatchType &= ~SelectorChecker::MatchVisited;
@@ -967,7 +1119,7 @@ unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector)
             // We don't support :link and :visited inside :-webkit-any.
             break;
         }
-        CSSSelector::Relation relation = selector->relation();
+        CSSSelector::Relation relation = current->relation();
         if (relation == CSSSelector::SubSelector)
             continue;
         if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
@@ -991,9 +1143,9 @@ bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
 }
 
 template
-SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, PseudoId&, const DOMSiblingTraversalStrategy&) const;
+SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const;
 
 template
-SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, PseudoId&, const ShadowDOMSiblingTraversalStrategy&) const;
+SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const ShadowDOMSiblingTraversalStrategy&, MatchResult*) const;
 
 }