Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / SelectorChecker.cpp
index 3a00ae5..62893d4 100644 (file)
 #include "config.h"
 #include "core/css/SelectorChecker.h"
 
-#include "HTMLNames.h"
+#include "core/HTMLNames.h"
 #include "core/css/CSSSelectorList.h"
 #include "core/css/SiblingTraversalStrategies.h"
 #include "core/dom/Document.h"
 #include "core/dom/ElementTraversal.h"
-#include "core/dom/FullscreenElementStack.h"
+#include "core/dom/Fullscreen.h"
 #include "core/dom/NodeRenderStyle.h"
+#include "core/dom/StyleEngine.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/frame/LocalFrame.h"
 #include "core/html/HTMLDocument.h"
 #include "core/html/HTMLFrameElementBase.h"
 #include "core/html/HTMLInputElement.h"
 #include "core/html/HTMLOptionElement.h"
+#include "core/html/HTMLSelectElement.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/page/Page.h"
 #include "core/rendering/RenderObject.h"
 #include "core/rendering/RenderScrollbar.h"
 #include "core/rendering/style/RenderStyle.h"
 #include "platform/scroll/ScrollableArea.h"
 #include "platform/scroll/ScrollbarTheme.h"
 
-namespace WebCore {
+namespace blink {
 
 using namespace HTMLNames;
 
 SelectorChecker::SelectorChecker(Document& document, Mode mode)
     : m_strictParsing(!document.inQuirksMode())
-    , m_documentIsHTML(document.isHTMLDocument())
     , m_mode(mode)
 {
 }
@@ -77,45 +79,39 @@ static bool matchesCustomPseudoElement(const Element* element, const CSSSelector
     return true;
 }
 
-Element* SelectorChecker::parentElement(const SelectorCheckingContext& context, bool allowToCrossBoundary) const
+static Element* parentElement(const SelectorChecker::SelectorCheckingContext& context)
 {
-    // CrossesBoundary means we don't care any context.scope. So we can walk up from a shadow root to its shadow host.
-    if (allowToCrossBoundary)
+    // - If context.scope is a shadow root, we should walk up to its shadow host.
+    // - 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 == context.element->containingShadowRoot() || context.scope->treeScope() == context.element->treeScope()))
         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))
-        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();
+    return context.element->parentElement();
 }
 
-bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingContext& context) const
+static bool scopeContainsLastMatchedElement(const SelectorChecker::SelectorCheckingContext& context)
 {
-    if (!(context.behaviorAtBoundary & SelectorChecker::ScopeContainsLastMatchedElement))
+    if (!(context.contextFlags & SelectorChecker::ScopeContainsLastMatchedElement))
         return true;
 
     ASSERT(context.scope);
-    // If behaviorAtBoundary is not ScopeIsShadowHost, we can use "contains".
-    if (!(context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost))
-        return context.scope->contains(context.element);
-
-    // If a given element is scope, i.e. shadow host, matches.
-    if (context.element == context.scope)
+    if (context.scope->treeScope() == context.element->treeScope())
         return true;
 
-    ShadowRoot* root = context.element->containingShadowRoot();
-    if (!root)
-        return false;
-
-    // If a host of the containing shadow root is scope, matches.
-    return root->host() == context.scope;
+    // Because Blink treats a shadow host's TreeScope as a separate one from its descendent shadow roots,
+    // if the last matched element is a shadow host, the condition above isn't met, even though it
+    // should be.
+    return context.element == context.scope->shadowHost() && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element));
 }
 
 static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context)
 {
-    return context.element == context.scope && (context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope;
+    if (context.scope && context.scope->isInShadowTree())
+        return context.element == context.scope->shadowHost();
+
+    return false;
 }
 
 // Recursive check of selectors and combinators
@@ -132,13 +128,16 @@ SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& con
     if (!checkOne(context, siblingTraversalStrategy, &specificity))
         return SelectorFailsLocally;
 
-    if (context.selector->m_match == CSSSelector::PseudoElement) {
+    if (context.selector->match() == CSSSelector::PseudoElement) {
         if (context.selector->isCustomPseudoElement()) {
             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;
@@ -152,8 +151,7 @@ SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& con
     }
 
     // Prepare next selector
-    const CSSSelector* historySelector = context.selector->tagHistory();
-    if (!historySelector) {
+    if (context.selector->isLastInTagHistory()) {
         if (scopeContainsLastMatchedElement(context)) {
             if (result)
                 result->specificity += specificity;
@@ -196,6 +194,11 @@ static inline SelectorChecker::SelectorCheckingContext prepareNextContextForRela
     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
 {
@@ -209,17 +212,31 @@ SelectorChecker::Match SelectorChecker::matchForSubSelector(const SelectorChecki
     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))
+        && !(nextContext.hasScrollbarPseudo && nextContext.selector->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();
 
@@ -240,6 +257,10 @@ SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingC
         }
         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, siblingTraversalStrategy, result);
             if (match == SelectorMatches || match == SelectorFailsCompletely)
@@ -253,17 +274,24 @@ SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingC
             if (context.selector->relationIsAffectedByPseudoContent())
                 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, 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 = ElementTraversal::previousSibling(*context.element);
@@ -274,9 +302,13 @@ SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingC
         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 = ElementTraversal::previousSibling(*context.element);
         nextContext.isSubSelector = false;
@@ -289,10 +321,9 @@ SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingC
         return SelectorFailsAllSiblings;
 
     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)
+            if (context.scope && context.scope->shadowHost() && context.scope->shadowHost()->treeScope() == context.element->treeScope())
                 return SelectorFailsCompletely;
 
             Element* shadowHost = context.element->shadowHost();
@@ -304,11 +335,11 @@ SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingC
             return this->match(nextContext, siblingTraversalStrategy, result);
         }
 
-    case CSSSelector::DescendantTree:
+    case CSSSelector::ShadowDeep:
         {
             nextContext.isSubSelector = false;
             nextContext.elementStyle = 0;
-            for (nextContext.element = parentElement(context, true); nextContext.element; nextContext.element = parentElement(nextContext, true)) {
+            for (nextContext.element = context.element->parentOrShadowHostElement(); nextContext.element; nextContext.element = nextContext.element->parentOrShadowHostElement()) {
                 Match match = this->match(nextContext, siblingTraversalStrategy, result);
                 if (match == SelectorMatches || match == SelectorFailsCompletely)
                     return match;
@@ -330,41 +361,39 @@ template<typename SiblingTraversalStrategy>
 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;
-
+    WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints;
     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 (scope == insertionPoints[i]->containingShadowRoot()->shadowHost() && !(behaviorAtBoundary & ScopeIsShadowHost))
-            nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowHost);
-        else
-            nextContext.behaviorAtBoundary = behaviorAtBoundary;
-
+        if (m_mode == SharingRules)
+            nextContext.scope = insertionPoints[i]->containingShadowRoot();
         nextContext.isSubSelector = false;
         nextContext.elementStyle = 0;
         if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches)
             return SelectorMatches;
     }
-    return SelectorFailsCompletely;
+    return SelectorFailsLocally;
 }
 
-static inline bool containsHTMLSpace(const AtomicString& string)
+template<typename CharType>
+static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned length)
 {
-    for (unsigned i = 0; i < string.length(); i++)
-        if (isHTMLSpace<UChar>(string[i]))
+    for (unsigned i = 0; i < length; ++i)
+        if (isHTMLSpace<CharType>(string[i]))
             return true;
     return false;
 }
 
-static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
+static inline bool containsHTMLSpace(const AtomicString& string)
+{
+    if (LIKELY(string.is8Bit()))
+        return containsHTMLSpaceTemplate<LChar>(string.characters8(), string.length());
+    return containsHTMLSpaceTemplate<UChar>(string.characters16(), string.length());
+}
+
+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;
 
@@ -434,27 +463,31 @@ static bool anyAttributeMatches(Element& element, CSSSelector::Match match, cons
     // 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();
+    const AtomicString& selectorValue = selector.value();
+    bool caseInsensitive = selector.attributeMatchType() == CSSSelector::CaseInsensitive;
 
-    for (size_t i = 0; i < element.attributeCount(); ++i) {
-        const Attribute* attributeItem = element.attributeItem(i);
+    AttributeCollection attributes = element.attributesWithoutUpdate();
+    AttributeCollection::iterator end = attributes.end();
+    for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
+        const Attribute& attributeItem = *it;
 
-        if (!attributeItem->matches(selectorAttr))
+        if (!attributeItem.matches(selectorAttr))
             continue;
 
-        if (attributeValueMatches(attributeItem, match, selectorValue, true))
+        if (attributeValueMatches(attributeItem, match, selectorValue, !caseInsensitive))
             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 (caseInsensitive)
+            continue;
+
+        // Legacy dictates that values of some attributes should be compared in
+        // a case-insensitive manner regardless of whether the case insensitive
+        // flag is set or not.
+        bool legacyCaseInsensitive = 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)) {
+        if (legacyCaseInsensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) {
             UseCounter::count(element.document(), UseCounter::CaseInsensitiveAttrSelectorMatch);
             return true;
         }
@@ -471,25 +504,27 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
     ASSERT(context.selector);
     const CSSSelector& selector = *context.selector;
 
-    bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.behaviorAtBoundary, context.scope);
+    bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.scope);
 
-    if (selector.m_match == CSSSelector::Tag)
-        return SelectorChecker::tagMatches(element, selector.tagQName(), elementIsHostInItsShadowTree ? MatchingHostInItsShadowTree : MatchingElement);
+    // Only :host and :host-context() should match the host: http://drafts.csswg.org/css-scoping/#host-element
+    if (elementIsHostInItsShadowTree && (!selector.isHostPseudoClass()
+        && !(context.contextFlags & TreatShadowHostAsNormalScope)
+        && selector.match() != CSSSelector::PseudoElement))
+            return false;
 
-    if (selector.m_match == CSSSelector::Class)
-        return element.hasClass() && element.classNames().contains(selector.value()) && !elementIsHostInItsShadowTree;
+    if (selector.match() == CSSSelector::Tag)
+        return SelectorChecker::tagMatches(element, selector.tagQName());
 
-    if (selector.m_match == CSSSelector::Id)
-        return element.hasID() && element.idForStyleResolution() == selector.value() && !elementIsHostInItsShadowTree;
+    if (selector.match() == CSSSelector::Class)
+        return element.hasClass() && element.classNames().contains(selector.value());
 
-    if (selector.isAttributeSelector()) {
-        if (elementIsHostInItsShadowTree)
-            return false;
-        if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector.m_match), selector))
-            return false;
-    }
+    if (selector.match() == CSSSelector::Id)
+        return element.hasID() && element.idForStyleResolution() == selector.value();
 
-    if (selector.m_match == CSSSelector::PseudoClass) {
+    if (selector.isAttributeSelector())
+        return anyAttributeMatches(element, selector.match(), selector);
+
+    if (selector.match() == CSSSelector::PseudoClass) {
         // Handle :not up front.
         if (selector.pseudoType() == CSSSelector::PseudoNot) {
             SelectorCheckingContext subContext(context);
@@ -503,6 +538,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;
             }
@@ -547,7 +586,7 @@ 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()) {
+            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
                 bool result = siblingTraversalStrategy.isFirstChild(element);
                 if (m_mode == ResolvingStyle) {
                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
@@ -560,7 +599,7 @@ 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()) {
+            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
                 bool result = siblingTraversalStrategy.isFirstOfType(element, element.tagQName());
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByForwardPositionalRules();
@@ -569,7 +608,7 @@ 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()) {
+            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
                 bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
                 if (m_mode == ResolvingStyle) {
                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
@@ -582,7 +621,7 @@ 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.parentElementOrDocumentFragment()) {
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByBackwardPositionalRules();
                 if (!parent->isFinishedParsingChildren())
@@ -591,7 +630,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             }
             break;
         case CSSSelector::PseudoOnlyChild:
-            if (Element* parent = element.parentElement()) {
+            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
                 bool firstChild = siblingTraversalStrategy.isFirstChild(element);
                 bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
                 if (m_mode == ResolvingStyle) {
@@ -608,7 +647,7 @@ 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.parentElementOrDocumentFragment()) {
                 if (m_mode == ResolvingStyle) {
                     parent->setChildrenAffectedByForwardPositionalRules();
                     parent->setChildrenAffectedByBackwardPositionalRules();
@@ -621,11 +660,10 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
         case CSSSelector::PseudoNthChild:
             if (!selector.parseNth())
                 break;
-            if (Element* parent = element.parentElement()) {
+            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
                 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();
@@ -638,7 +676,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
         case CSSSelector::PseudoNthOfType:
             if (!selector.parseNth())
                 break;
-            if (Element* parent = element.parentElement()) {
+            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
                 int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element.tagQName());
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByForwardPositionalRules();
@@ -650,7 +688,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
         case CSSSelector::PseudoNthLastChild:
             if (!selector.parseNth())
                 break;
-            if (Element* parent = element.parentElement()) {
+            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByBackwardPositionalRules();
                 if (!parent->isFinishedParsingChildren())
@@ -663,7 +701,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
         case CSSSelector::PseudoNthLastOfType:
             if (!selector.parseNth())
                 break;
-            if (Element* parent = element.parentElement()) {
+            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
                 if (m_mode == ResolvingStyle)
                     parent->setChildrenAffectedByBackwardPositionalRules();
                 if (!parent->isFinishedParsingChildren())
@@ -705,7 +743,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
                 if (context.elementStyle)
                     context.elementStyle->setAffectedByDrag();
                 else
-                    element.setChildrenAffectedByDrag();
+                    element.setChildrenOrSiblingsAffectedByDrag();
             }
             if (element.renderer() && element.renderer()->isDragging())
                 return true;
@@ -715,18 +753,18 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
                 if (context.elementStyle)
                     context.elementStyle->setAffectedByFocus();
                 else
-                    element.setChildrenAffectedByFocus();
+                    element.setChildrenOrSiblingsAffectedByFocus();
             }
             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() && !element.hasTagName(aTag)) || element.isLink()) {
+            if (m_strictParsing || context.isSubSelector || element.isLink()) {
                 if (m_mode == ResolvingStyle) {
                     if (context.elementStyle)
                         context.elementStyle->setAffectedByHover();
                     else
-                        element.setChildrenAffectedByHover();
+                        element.setChildrenOrSiblingsAffectedByHover();
                 }
                 if (element.hovered() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoHover))
                     return true;
@@ -735,20 +773,22 @@ 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() && !element.hasTagName(aTag)) || element.isLink()) {
+            if (m_strictParsing || context.isSubSelector || element.isLink()) {
                 if (m_mode == ResolvingStyle) {
                     if (context.elementStyle)
                         context.elementStyle->setAffectedByActive();
                     else
-                        element.setChildrenAffectedByActive();
+                        element.setChildrenOrSiblingsAffectedByActive();
                 }
                 if (element.active() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoActive))
                     return true;
             }
             break;
         case CSSSelector::PseudoEnabled:
-            if (element.isFormControlElement() || element.hasTagName(optionTag) || element.hasTagName(optgroupTag))
+            if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
                 return !element.isDisabledFormControl();
+            else if (isHTMLAnchorElement(element) || isHTMLAreaElement(element))
+                return element.isLink();
             break;
         case CSSSelector::PseudoFullPageMedia:
             return element.document().isMediaDocument();
@@ -756,7 +796,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
         case CSSSelector::PseudoDefault:
             return element.isDefaultButtonForForm();
         case CSSSelector::PseudoDisabled:
-            if (element.isFormControlElement() || element.hasTagName(optionTag) || element.hasTagName(optgroupTag))
+            if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
                 return element.isDisabledFormControl();
             break;
         case CSSSelector::PseudoReadOnly:
@@ -775,7 +815,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
@@ -784,7 +824,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;
             }
@@ -813,20 +853,15 @@ 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 (!fullscreen->webkitIsFullScreen())
-                    return false;
-                return element == fullscreen->webkitCurrentFullScreenElement();
-            }
-            return false;
+            return Fullscreen::isActiveFullScreenElement(element);
         case CSSSelector::PseudoFullScreenAncestor:
             return element.containsFullScreenElement();
         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 (!Fullscreen::isFullScreen(element.document()))
                 return false;
             return true;
         case CSSSelector::PseudoInRange:
@@ -842,6 +877,8 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
 
         case CSSSelector::PseudoScope:
             {
+                if (m_mode == SharingRules)
+                    return true;
                 const Node* contextualReferenceNode = !context.scope ? element.document().documentElement() : context.scope;
                 if (element == contextualReferenceNode)
                     return true;
@@ -854,10 +891,15 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
             break;
 
         case CSSSelector::PseudoHost:
-        case CSSSelector::PseudoAncestor:
+        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.scope->shadowHost();
+                if (!shadowHost || shadowHost != element)
                     return false;
                 ASSERT(element.shadow());
 
@@ -873,25 +915,27 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
 
                 // 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.contextFlags = TreatShadowHostAsNormalScope;
                     subContext.scope = context.scope;
                     // Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
                     Element* nextElement = &element;
+                    SelectorCheckingContext hostContext(subContext);
                     do {
                         MatchResult subResult;
-                        subContext.element = nextElement;
-                        if (match(subContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
+                        hostContext.element = nextElement;
+                        if (match(hostContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
                             matched = true;
                             // Consider div:host(div:host(div:host(div:host...))).
-                            maxSpecificity = std::max(maxSpecificity, subContext.selector->specificity() + subResult.specificity);
+                            maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity);
                             break;
                         }
-                        subContext.behaviorAtBoundary = DoesNotCrossBoundary;
-                        subContext.scope = 0;
+                        hostContext.contextFlags = DefaultBehavior;
+                        hostContext.scope = nullptr;
 
                         if (selector.pseudoType() == CSSSelector::PseudoHost)
                             break;
 
+                        hostContext.elementStyle = 0;
                         nextElement = NodeRenderingTraversal::parentElement(nextElement);
                     } while (nextElement);
                 }
@@ -902,6 +946,10 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
                 }
             }
             break;
+        case CSSSelector::PseudoSpatialNavigationFocus:
+            return context.isUARule && matchesSpatialNavigationFocusPseudoClass(element);
+        case CSSSelector::PseudoListBox:
+            return context.isUARule && matchesListBoxPseudoClass(element);
 
         case CSSSelector::PseudoHorizontal:
         case CSSSelector::PseudoVertical:
@@ -922,10 +970,10 @@ 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.match() == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) {
         SelectorCheckingContext subContext(context);
         subContext.isSubSelector = true;
-        subContext.behaviorAtBoundary = StaysWithinTreeScope;
+        subContext.contextFlags = DefaultBehavior;
 
         const CSSSelector* contextSelector = context.selector;
         ASSERT(contextSelector);
@@ -952,7 +1000,7 @@ bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& c
     if (!scrollbar)
         return false;
 
-    ASSERT(selector.m_match == CSSSelector::PseudoClass);
+    ASSERT(selector.match() == CSSSelector::PseudoClass);
     switch (selector.pseudoType()) {
     case CSSSelector::PseudoEnabled:
         return scrollbar->enabled();
@@ -1074,6 +1122,16 @@ bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
     return element.focused() && isFrameFocused(element);
 }
 
+bool SelectorChecker::matchesSpatialNavigationFocusPseudoClass(const Element& element)
+{
+    return isHTMLOptionElement(element) && toHTMLOptionElement(element).spatialNavigationFocused() && isFrameFocused(element);
+}
+
+bool SelectorChecker::matchesListBoxPseudoClass(const Element& element)
+{
+    return isHTMLSelectElement(element) && !toHTMLSelectElement(element).usesMenuList();
+}
+
 template
 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const;