X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fthird_party%2FWebKit%2FSource%2Fcore%2Fcss%2FSelectorChecker.cpp;h=d95fca32c9c184873755a44150577c07a2ec9680;hb=4a1a0bdd01eef90b0826a0e761d3379d3715c10f;hp=3c6755a57fc8b3af1020bc800b63f64187843967;hpb=b1be5ca53587d23e7aeb77b26861fdc0a181ffd8;p=platform%2Fframework%2Fweb%2Fcrosswalk.git diff --git a/src/third_party/WebKit/Source/core/css/SelectorChecker.cpp b/src/third_party/WebKit/Source/core/css/SelectorChecker.cpp index 3c6755a..d95fca3 100644 --- a/src/third_party/WebKit/Source/core/css/SelectorChecker.cpp +++ b/src/third_party/WebKit/Source/core/css/SelectorChecker.cpp @@ -35,6 +35,7 @@ #include "core/dom/ElementTraversal.h" #include "core/dom/FullscreenElementStack.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" @@ -44,17 +45,19 @@ #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/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; @@ -77,32 +80,21 @@ 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 a shadow root, we should walk up to its shadow host. - if ((context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowRoot) && context.scope == context.element->containingShadowRoot()) - return context.element->parentOrShadowHostElement(); - - if ((context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope) - return 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 +static bool scopeContainsLastMatchedElement(const SelectorChecker::SelectorCheckingContext& context) { - if (!(context.behaviorAtBoundary & SelectorChecker::ScopeContainsLastMatchedElement)) + if (!(context.contextFlags & SelectorChecker::ScopeContainsLastMatchedElement)) return true; ASSERT(context.scope); @@ -332,7 +324,7 @@ SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingC case CSSSelector::ShadowPseudo: { // 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->shadowHost() && context.scope->shadowHost()->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(); @@ -348,7 +340,7 @@ SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingC { 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; @@ -371,25 +363,11 @@ SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* { ASSERT(element); WillBeHeapVector, 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 ScopeIsShadowRoot, - // we need to update behaviorAtBoundary to make selectors like ":host > ::content" work correctly. - if (m_mode == SharingRules) { - nextContext.behaviorAtBoundary = static_cast(behaviorAtBoundary | ScopeIsShadowRoot); + if (m_mode == SharingRules) nextContext.scope = insertionPoints[i]->containingShadowRoot(); - } else if (scope == insertionPoints[i]->containingShadowRoot() && !(behaviorAtBoundary & ScopeIsShadowRoot)) { - nextContext.behaviorAtBoundary = static_cast(behaviorAtBoundary | ScopeIsShadowRoot); - } else { - nextContext.behaviorAtBoundary = behaviorAtBoundary; - } - nextContext.isSubSelector = false; nextContext.elementStyle = 0; if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches) @@ -486,29 +464,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; - AttributeCollection attributes = element.attributes(); - AttributeCollection::const_iterator end = attributes.end(); - for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) { + 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)) 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; } @@ -525,11 +505,11 @@ 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); // Only :host and :ancestor should match the host: http://drafts.csswg.org/css-scoping/#host-element if (elementIsHostInItsShadowTree && !selector.isHostPseudoClass() - && !(context.behaviorAtBoundary & TreatShadowHostAsNormalScope)) + && !(context.contextFlags & TreatShadowHostAsNormalScope)) return false; if (selector.match() == CSSSelector::Tag) @@ -807,6 +787,8 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib case CSSSelector::PseudoEnabled: 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(); @@ -938,7 +920,7 @@ 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; @@ -952,8 +934,8 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity); break; } - hostContext.behaviorAtBoundary = DoesNotCrossBoundary; - hostContext.scope = 0; + hostContext.contextFlags = DefaultBehavior; + hostContext.scope = nullptr; if (selector.pseudoType() == CSSSelector::PseudoHost) break; @@ -969,6 +951,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: @@ -992,7 +978,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib } 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); @@ -1141,6 +1127,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;