blob: 938d78fb07624fcd59192c267bcb64f07e7ccd4d [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/dom/rule_matching.h"
#include <algorithm>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/debug/trace_event.h"
#include "base/string_util.h"
#include "cobalt/base/unused.h"
#include "cobalt/cssom/after_pseudo_element.h"
#include "cobalt/cssom/attribute_selector.h"
#include "cobalt/cssom/before_pseudo_element.h"
#include "cobalt/cssom/cascade_precedence.h"
#include "cobalt/cssom/child_combinator.h"
#include "cobalt/cssom/class_selector.h"
#include "cobalt/cssom/combinator.h"
#include "cobalt/cssom/combinator_visitor.h"
#include "cobalt/cssom/complex_selector.h"
#include "cobalt/cssom/compound_selector.h"
#include "cobalt/cssom/css_style_rule.h"
#include "cobalt/cssom/css_style_sheet.h"
#include "cobalt/cssom/descendant_combinator.h"
#include "cobalt/cssom/empty_pseudo_class.h"
#include "cobalt/cssom/following_sibling_combinator.h"
#include "cobalt/cssom/id_selector.h"
#include "cobalt/cssom/next_sibling_combinator.h"
#include "cobalt/cssom/not_pseudo_class.h"
#include "cobalt/cssom/property_value.h"
#include "cobalt/cssom/pseudo_element.h"
#include "cobalt/cssom/selector_tree.h"
#include "cobalt/cssom/selector_visitor.h"
#include "cobalt/cssom/simple_selector.h"
#include "cobalt/cssom/type_selector.h"
#include "cobalt/cssom/universal_selector.h"
#include "cobalt/dom/attr.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/dom_token_list.h"
#include "cobalt/dom/element.h"
#include "cobalt/dom/html_element.h"
#include "cobalt/dom/html_html_element.h"
#include "cobalt/dom/named_node_map.h"
#include "cobalt/dom/node_descendants_iterator.h"
#include "cobalt/dom/node_list.h"
#include "cobalt/dom/pseudo_element.h"
#include "cobalt/math/safe_integer_conversions.h"
namespace cobalt {
namespace dom {
namespace {
using cssom::SelectorTree;
const size_t kRuleMatchingNodeSetSize = 60;
//////////////////////////////////////////////////////////////////////////
// Helper functions
//////////////////////////////////////////////////////////////////////////
// Matches an element against a selector. If the element doesn't match, returns
// NULL, otherwise returns the result of advancing the pointer to the element
// according to the combinators.
Element* MatchSelectorAndElement(cssom::Selector* selector, Element* element,
bool matching_combinators);
//////////////////////////////////////////////////////////////////////////
// Combinator matcher
//////////////////////////////////////////////////////////////////////////
class CombinatorMatcher : public cssom::CombinatorVisitor {
public:
explicit CombinatorMatcher(Element* element) : element_(element) {
DCHECK(element);
}
// Child combinator describes a childhood relationship between two elements.
// https://www.w3.org/TR/selectors4/#child-combinators
void VisitChildCombinator(cssom::ChildCombinator* child_combinator) OVERRIDE {
element_ = MatchSelectorAndElement(child_combinator->left_selector(),
element_->parent_element(), true);
}
// Next-sibling combinator describes that the elements represented by the two
// compound selectors share the same parent in the document tree and the
// element represented by the first compound selector immediately precedes the
// element represented by the second one.
// https://www.w3.org/TR/selectors4/#adjacent-sibling-combinators
void VisitNextSiblingCombinator(
cssom::NextSiblingCombinator* next_sibling_combinator) OVERRIDE {
element_ =
MatchSelectorAndElement(next_sibling_combinator->left_selector(),
element_->previous_element_sibling(), true);
}
// Descendant combinator describes an element that is the descendant of
// another element in the document tree.
// https://www.w3.org/TR/selectors4/#descendant-combinators
void VisitDescendantCombinator(
cssom::DescendantCombinator* descendant_combinator) OVERRIDE {
do {
element_ = element_->parent_element();
Element* element = MatchSelectorAndElement(
descendant_combinator->left_selector(), element_, true);
if (element) {
element_ = element;
return;
}
} while (element_);
}
// Following-sibling combinator describes that the elements represented by the
// two compound selectors share the same parent in the document tree and the
// element represented by the first compound selector precedes (not
// necessarily immediately) the element represented by the second one.
// https://www.w3.org/TR/selectors4/#general-sibling-combinators
void VisitFollowingSiblingCombinator(
cssom::FollowingSiblingCombinator* following_sibling_combinator)
OVERRIDE {
do {
element_ = element_->previous_element_sibling();
Element* element = MatchSelectorAndElement(
following_sibling_combinator->left_selector(), element_, true);
if (element) {
element_ = element;
return;
}
} while (element_);
}
Element* element() { return element_; }
private:
Element* element_;
DISALLOW_COPY_AND_ASSIGN(CombinatorMatcher);
};
//////////////////////////////////////////////////////////////////////////
// Selector matcher
//////////////////////////////////////////////////////////////////////////
class SelectorMatcher : public cssom::SelectorVisitor {
public:
explicit SelectorMatcher(Element* element)
: element_(element), matching_combinators_(false) {
DCHECK(element);
}
SelectorMatcher(Element* element, bool matching_combinators)
: element_(element), matching_combinators_(matching_combinators) {
DCHECK(element);
}
// The universal selector represents the qualified name of any element type.
// https://www.w3.org/TR/selectors4/#universal-selector
void VisitUniversalSelector(
cssom::UniversalSelector* /* universal_selector */) OVERRIDE {}
// A type selector represents an instance of the element type in the document
// tree.
// https://www.w3.org/TR/selectors4/#type-selector
void VisitTypeSelector(cssom::TypeSelector* type_selector) OVERRIDE {
if (type_selector->element_name() != element_->local_name()) {
element_ = NULL;
}
}
// An attribute selector represents an element that has an attribute that
// matches the attribute represented by the attribute selector.
// https://www.w3.org/TR/selectors4/#attribute-selector
void VisitAttributeSelector(
cssom::AttributeSelector* attribute_selector) OVERRIDE {
if (!element_->HasAttribute(attribute_selector->attribute_name().c_str())) {
element_ = NULL;
return;
}
switch (attribute_selector->value_match_type()) {
case cssom::AttributeSelector::kNoMatch:
return;
case cssom::AttributeSelector::kEquals:
if (element_->GetAttribute(attribute_selector->attribute_name().c_str())
.value() != attribute_selector->attribute_value()) {
element_ = NULL;
}
return;
case cssom::AttributeSelector::kIncludes:
case cssom::AttributeSelector::kDashMatch:
case cssom::AttributeSelector::kBeginsWith:
case cssom::AttributeSelector::kEndsWith:
case cssom::AttributeSelector::kContains:
NOTIMPLEMENTED();
element_ = NULL;
return;
}
}
// The class selector represents an element belonging to the class identified
// by the identifier.
// https://www.w3.org/TR/selectors4/#class-selector
void VisitClassSelector(cssom::ClassSelector* class_selector) OVERRIDE {
if (!element_->class_list()->ContainsValid(class_selector->class_name())) {
element_ = NULL;
}
}
// An ID selector represents an element instance that has an identifier that
// matches the identifier in the ID selector.
// https://www.w3.org/TR/selectors4/#id-selector
void VisitIdSelector(cssom::IdSelector* id_selector) OVERRIDE {
if (id_selector->id() != element_->id()) {
element_ = NULL;
}
}
// The :active pseudo-class applies while an element is being activated by the
// user. For example, between the times the user presses the mouse button and
// releases it. On systems with more than one mouse button, :active applies
// only to the primary or primary activation button (typically the "left"
// mouse button), and any aliases thereof.
// https://www.w3.org/TR/selectors4/#active-pseudo
void VisitActivePseudoClass(cssom::ActivePseudoClass*) OVERRIDE {
NOTIMPLEMENTED();
element_ = NULL;
}
// The :empty pseudo-class represents an element that has no content children.
// https://www.w3.org/TR/selectors4/#empty-pseudo
void VisitEmptyPseudoClass(cssom::EmptyPseudoClass*) OVERRIDE {
if (!element_->IsEmpty()) {
element_ = NULL;
}
}
// The :focus pseudo-class applies while an element has the focus (accepts
// keyboard or mouse events, or other forms of input).
// https://www.w3.org/TR/selectors4/#focus-pseudo
void VisitFocusPseudoClass(cssom::FocusPseudoClass*) OVERRIDE {
if (!element_->HasFocus()) {
element_ = NULL;
}
}
// The :hover pseudo-class applies while the user designates an element with a
// pointing device, but does not necessarily activate it. For example, a
// visual user agent could apply this pseudo-class when the cursor (mouse
// pointer) hovers over a box generated by the element. Interactive user
// agents that cannot detect hovering due to hardware limitations (e.g., a pen
// device that does not detect hovering) are still conforming.
// https://www.w3.org/TR/selectors4/#hover-pseudo
void VisitHoverPseudoClass(cssom::HoverPseudoClass*) OVERRIDE {
if (!element_->AsHTMLElement() ||
!element_->AsHTMLElement()->IsDesignated()) {
element_ = NULL;
}
}
// The negation pseudo-class, :not(), is a functional pseudo-class taking a
// selector list as an argument. It represents an element that is not
// represented by its argument.
// https://www.w3.org/TR/selectors4/#negation-pseudo
void VisitNotPseudoClass(cssom::NotPseudoClass* not_pseudo_class) OVERRIDE {
if (MatchSelectorAndElement(not_pseudo_class->selector(), element_, true)) {
element_ = NULL;
}
}
// Pseudo elements doesn't affect whether the selector matches or not.
// The :after pseudo-element represents a generated element.
// https://www.w3.org/TR/CSS21/generate.html#before-after-content
void VisitAfterPseudoElement(cssom::AfterPseudoElement*) OVERRIDE {}
// The :before pseudo-element represents a generated element.
// https://www.w3.org/TR/CSS21/generate.html#before-after-content
void VisitBeforePseudoElement(cssom::BeforePseudoElement*) OVERRIDE {}
// A compound selector is a chain of simple selectors that are not separated
// by a combinator.
// https://www.w3.org/TR/selectors4/#compound
void VisitCompoundSelector(
cssom::CompoundSelector* compound_selector) OVERRIDE {
DCHECK_GT(compound_selector->simple_selectors().size(), 0U);
// Iterate through all the simple selectors. If any of the simple selectors
// doesn't match, the compound selector doesn't match.
for (cssom::CompoundSelector::SimpleSelectors::const_iterator
selector_iterator = compound_selector->simple_selectors().begin();
selector_iterator != compound_selector->simple_selectors().end();
++selector_iterator) {
element_ = MatchSelectorAndElement(*selector_iterator, element_, false);
if (!element_) {
return;
}
}
// Check combinator.
if (matching_combinators_ && compound_selector->left_combinator()) {
CombinatorMatcher combinator_matcher(element_);
compound_selector->left_combinator()->Accept(&combinator_matcher);
element_ = combinator_matcher.element();
}
}
// A complex selector is a chain of one or more compound selectors separated
// by combinators.
// https://www.w3.org/TR/selectors4/#complex
void VisitComplexSelector(cssom::ComplexSelector* complex_selector) OVERRIDE {
element_ = MatchSelectorAndElement(complex_selector->last_selector(),
element_, true);
}
Element* element() const { return element_; }
private:
Element* element_;
bool matching_combinators_;
DISALLOW_COPY_AND_ASSIGN(SelectorMatcher);
};
//////////////////////////////////////////////////////////////////////////
// Helper functions
//////////////////////////////////////////////////////////////////////////
Element* MatchSelectorAndElement(cssom::Selector* selector, Element* element,
bool matching_combinators) {
DCHECK(selector);
if (!element) {
return NULL;
}
SelectorMatcher selector_matcher(element, matching_combinators);
selector->Accept(&selector_matcher);
return selector_matcher.element();
}
void AddRulesOnNodeToElement(HTMLElement* element,
const SelectorTree::Node* node) {
cssom::PseudoElement* pseudo_element =
node->compound_selector()->pseudo_element();
// Where to add matching rules.
cssom::RulesWithCascadePrecedence* target_matching_rules;
if (!pseudo_element) {
target_matching_rules = element->matching_rules();
} else {
PseudoElementType pseudo_element_type = kNotPseudoElementType;
if (pseudo_element->AsAfterPseudoElement()) {
pseudo_element_type = kAfterPseudoElementType;
} else if (pseudo_element->AsBeforePseudoElement()) {
pseudo_element_type = kBeforePseudoElementType;
} else {
NOTREACHED();
}
// Make sure the pseudo element exists under element.
if (!element->pseudo_element(pseudo_element_type)) {
element->set_pseudo_element(
pseudo_element_type,
make_scoped_ptr(new dom::PseudoElement(element)));
}
target_matching_rules =
element->pseudo_element(pseudo_element_type)->matching_rules();
}
element->rule_matching_state()->matching_nodes.insert(node);
for (SelectorTree::Rules::const_iterator rule_iterator =
node->rules().begin();
rule_iterator != node->rules().end(); ++rule_iterator) {
cssom::CSSStyleRule* rule = *rule_iterator;
if (!rule) {
continue;
}
DCHECK(rule->parent_style_sheet());
cssom::CascadePrecedence precedence(
pseudo_element ? cssom::kNormalOverride
: rule->parent_style_sheet()->origin(),
node->cumulative_specificity(),
cssom::Appearance(rule->parent_style_sheet()->index(), rule->index()));
target_matching_rules->push_back(std::make_pair(rule, precedence));
}
}
void GatherCandidateNodesFromMap(
cssom::SimpleSelectorType simple_selector_type,
cssom::CombinatorType combinator_type,
const SelectorTree::SelectorTextToNodesMap* map, base::Token key,
SelectorTree::NodeSet<kRuleMatchingNodeSetSize>* candidate_nodes) {
SelectorTree::SelectorTextToNodesMap::const_iterator it = map->find(key);
if (it != map->end()) {
const SelectorTree::SimpleSelectorNodes& nodes = it->second;
for (SelectorTree::SimpleSelectorNodes::const_iterator nodes_iterator =
nodes.begin();
nodes_iterator != nodes.end(); ++nodes_iterator) {
if (nodes_iterator->simple_selector_type == simple_selector_type &&
nodes_iterator->combinator_type == combinator_type) {
candidate_nodes->insert(nodes_iterator->node);
}
}
}
}
void GatherCandidateNodesFromSet(
cssom::PseudoClassType pseudo_class_type,
cssom::CombinatorType combinator_type,
const SelectorTree::PseudoClassNodes& pseudo_class_nodes,
SelectorTree::NodeSet<kRuleMatchingNodeSetSize>* candidate_nodes) {
for (SelectorTree::PseudoClassNodes::const_iterator nodes_iterator =
pseudo_class_nodes.begin();
nodes_iterator != pseudo_class_nodes.end(); ++nodes_iterator) {
if (nodes_iterator->pseudo_class_type == pseudo_class_type &&
nodes_iterator->combinator_type == combinator_type) {
candidate_nodes->insert(nodes_iterator->node);
}
}
}
template <class NodeSet>
void ForEachChildOnNodes(
const NodeSet& node_set, cssom::CombinatorType combinator_type,
HTMLElement* element,
base::Callback<void(HTMLElement* element, const SelectorTree::Node*)>
callback) {
// Gathering Phase: Generate candidate nodes from the node set.
SelectorTree::NodeSet<kRuleMatchingNodeSetSize> candidate_nodes;
const std::vector<base::Token>& element_class_list =
element->class_list()->GetTokens();
// Don't retrieve the element's attributes until they are needed. Retrieving
// them typically requires the creation of a NamedNodeMap.
scoped_refptr<NamedNodeMap> element_attributes;
// Iterate through all nodes in node_set.
for (typename NodeSet::const_iterator node_iterator = node_set.begin();
node_iterator != node_set.end(); ++node_iterator) {
const SelectorTree::Node* node = *node_iterator;
if (!node->HasCombinator(combinator_type)) {
continue;
}
// Gather candidate sets in node's children under the given combinator.
const SelectorTree::SelectorTextToNodesMap* selector_nodes_map =
node->selector_nodes_map();
if (selector_nodes_map) {
// Universal selector.
if (node->HasSimpleSelector(cssom::kUniversalSelector, combinator_type)) {
GatherCandidateNodesFromMap(cssom::kUniversalSelector, combinator_type,
selector_nodes_map, base::Token(),
&candidate_nodes);
}
// Type selector.
if (node->HasSimpleSelector(cssom::kTypeSelector, combinator_type)) {
GatherCandidateNodesFromMap(cssom::kTypeSelector, combinator_type,
selector_nodes_map, element->local_name(),
&candidate_nodes);
}
// Attribute selector.
if (node->HasSimpleSelector(cssom::kAttributeSelector, combinator_type)) {
// If the element's attributes have not been retrieved yet, then
// retrieve them now.
if (!element_attributes) {
element_attributes = element->attributes();
}
for (unsigned int index = 0; index < element_attributes->length();
++index) {
GatherCandidateNodesFromMap(
cssom::kAttributeSelector, combinator_type, selector_nodes_map,
base::Token(element_attributes->Item(index)->name()),
&candidate_nodes);
}
}
// Class selector.
if (node->HasSimpleSelector(cssom::kClassSelector, combinator_type)) {
for (size_t index = 0; index < element_class_list.size(); ++index) {
GatherCandidateNodesFromMap(
cssom::kClassSelector, combinator_type, selector_nodes_map,
element_class_list[index], &candidate_nodes);
}
}
// Id selector.
if (node->HasSimpleSelector(cssom::kIdSelector, combinator_type)) {
GatherCandidateNodesFromMap(cssom::kIdSelector, combinator_type,
selector_nodes_map, element->id(),
&candidate_nodes);
}
}
if (node->HasAnyPseudoClass()) {
// Empty pseudo class.
if (node->HasPseudoClass(cssom::kEmptyPseudoClass, combinator_type) &&
element->IsEmpty()) {
GatherCandidateNodesFromSet(cssom::kEmptyPseudoClass, combinator_type,
node->pseudo_class_nodes(),
&candidate_nodes);
}
// Focus pseudo class.
if (node->HasPseudoClass(cssom::kFocusPseudoClass, combinator_type) &&
element->HasFocus()) {
GatherCandidateNodesFromSet(cssom::kFocusPseudoClass, combinator_type,
node->pseudo_class_nodes(),
&candidate_nodes);
}
// Hover pseudo class.
if (node->HasPseudoClass(cssom::kHoverPseudoClass, combinator_type) &&
element->IsDesignated()) {
GatherCandidateNodesFromSet(cssom::kHoverPseudoClass, combinator_type,
node->pseudo_class_nodes(),
&candidate_nodes);
}
// Not pseudo class.
if (node->HasPseudoClass(cssom::kNotPseudoClass, combinator_type)) {
GatherCandidateNodesFromSet(cssom::kNotPseudoClass, combinator_type,
node->pseudo_class_nodes(),
&candidate_nodes);
}
}
}
// Verifying Phase: Check all candidate nodes and run callback for matching
// nodes.
for (SelectorTree::NodeSet<kRuleMatchingNodeSetSize>::const_iterator
candidate_node_iterator = candidate_nodes.begin();
candidate_node_iterator != candidate_nodes.end();
++candidate_node_iterator) {
const SelectorTree::Node* candidate_node = *candidate_node_iterator;
// Verify that this node is a match:
// 1. If the candidate node's compound selector doesn't require a visit to
// verify the match, then the act of gathering the node as a candidate
// proved the match.
// 2. Otherwise, the node requires additional verification checks, so call
// MatchSelectorAndElement().
if (!candidate_node->compound_selector()
->requires_rule_matching_verification_visit() ||
MatchSelectorAndElement(candidate_node->compound_selector(), element,
false)) {
callback.Run(element, candidate_node);
}
}
}
bool MatchRuleAndElement(cssom::CSSStyleRule* rule, Element* element) {
for (cssom::Selectors::const_iterator selector_iterator =
rule->selectors().begin();
selector_iterator != rule->selectors().end(); ++selector_iterator) {
DCHECK(*selector_iterator);
if (MatchSelectorAndElement(*selector_iterator, element, true)) {
return true;
}
}
return false;
}
} // namespace
//////////////////////////////////////////////////////////////////////////
// Public APIs
//////////////////////////////////////////////////////////////////////////
void UpdateMatchingRules(HTMLElement* current_element) {
DCHECK(current_element);
DCHECK(current_element->node_document());
SelectorTree* selector_tree =
current_element->node_document()->selector_tree();
// Get parent and previous sibling of the current element.
HTMLElement* parent = current_element->parent_element()
? current_element->parent_element()->AsHTMLElement()
: NULL;
HTMLElement* previous_sibling =
current_element->previous_element_sibling()
? current_element->previous_element_sibling()->AsHTMLElement()
: NULL;
// Calculate current element's matching nodes.
if (parent) {
// Child combinator.
ForEachChildOnNodes(parent->rule_matching_state()->matching_nodes,
cssom::kChildCombinator, current_element,
base::Bind(&AddRulesOnNodeToElement));
// Descendant combinator.
ForEachChildOnNodes(
parent->rule_matching_state()->descendant_potential_nodes,
cssom::kDescendantCombinator, current_element,
base::Bind(&AddRulesOnNodeToElement));
} else {
// Descendant combinator from root.
ForEachChildOnNodes(selector_tree->root_set(), cssom::kDescendantCombinator,
current_element, base::Bind(&AddRulesOnNodeToElement));
}
if (selector_tree->has_sibling_combinators()) {
if (previous_sibling) {
// Next sibling combinator.
ForEachChildOnNodes(
previous_sibling->rule_matching_state()->matching_nodes,
cssom::kNextSiblingCombinator, current_element,
base::Bind(&AddRulesOnNodeToElement));
// Following sibling combinator.
ForEachChildOnNodes(previous_sibling->rule_matching_state()
->following_sibling_potential_nodes,
cssom::kFollowingSiblingCombinator, current_element,
base::Bind(&AddRulesOnNodeToElement));
}
}
// Calculate current element's descendant potential nodes.
if (parent) {
current_element->rule_matching_state()->descendant_potential_nodes.insert(
parent->rule_matching_state()->descendant_potential_nodes.begin(),
parent->rule_matching_state()->descendant_potential_nodes.end());
} else {
current_element->rule_matching_state()->descendant_potential_nodes.insert(
selector_tree->root());
}
// Walk all of the matching nodes, adding any that have a descendant
// combinator as a descendant potential node. Any missing that combinator can
// never match descendants.
for (dom::HTMLElement::MatchingNodes::const_iterator iter =
current_element->rule_matching_state()->matching_nodes.begin();
iter != current_element->rule_matching_state()->matching_nodes.end();
++iter) {
if ((*iter)->HasCombinator(cssom::kDescendantCombinator)) {
// It is possible for the two lists to contain duplicate nodes, so only
// add the node if it isn't a duplicate.
current_element->rule_matching_state()->descendant_potential_nodes.insert(
*iter, true /*check_for_duplicate*/);
}
}
// Calculate current element's following sibling potential nodes.
if (selector_tree->has_sibling_combinators()) {
if (previous_sibling) {
current_element->rule_matching_state()
->following_sibling_potential_nodes.insert(
previous_sibling->rule_matching_state()
->following_sibling_potential_nodes.begin(),
previous_sibling->rule_matching_state()
->following_sibling_potential_nodes.end());
}
// Walk all of the matching nodes, adding any that have a following sibling
// combinator as a following sibling potential node. Any missing that
// combinator can never match following siblings..
for (dom::HTMLElement::MatchingNodes::const_iterator iter =
current_element->rule_matching_state()->matching_nodes.begin();
iter != current_element->rule_matching_state()->matching_nodes.end();
++iter) {
if ((*iter)->HasCombinator(cssom::kFollowingSiblingCombinator)) {
// It is possible for the two lists to contain duplicate nodes, so only
// add the node if it isn't a duplicate.
current_element->rule_matching_state()
->following_sibling_potential_nodes.insert(
*iter, true /*check_for_duplicate*/);
}
}
}
current_element->set_matching_rules_valid();
}
scoped_refptr<Element> QuerySelector(Node* node, const std::string& selectors,
cssom::CSSParser* css_parser) {
DCHECK(css_parser);
// Generate a rule with the given selectors and no style.
scoped_refptr<cssom::CSSRule> css_rule =
css_parser->ParseRule(selectors + " {}", node->GetInlineSourceLocation());
if (!css_rule) {
return NULL;
}
scoped_refptr<cssom::CSSStyleRule> css_style_rule =
css_rule->AsCSSStyleRule();
if (!css_style_rule) {
return NULL;
}
// Iterate through the descendants of the node and find the first matching
// element if any.
NodeDescendantsIterator iterator(node);
Node* child = iterator.First();
while (child) {
if (child->IsElement()) {
scoped_refptr<Element> element = child->AsElement();
if (MatchRuleAndElement(css_style_rule, element)) {
return element;
}
}
child = iterator.Next();
}
return NULL;
}
scoped_refptr<NodeList> QuerySelectorAll(Node* node,
const std::string& selectors,
cssom::CSSParser* css_parser) {
DCHECK(css_parser);
scoped_refptr<NodeList> node_list = new NodeList();
// Generate a rule with the given selectors and no style.
scoped_refptr<cssom::CSSRule> css_rule =
css_parser->ParseRule(selectors + " {}", node->GetInlineSourceLocation());
if (!css_rule) {
return node_list;
}
scoped_refptr<cssom::CSSStyleRule> css_style_rule =
css_rule->AsCSSStyleRule();
if (!css_style_rule) {
return node_list;
}
// Iterate through the descendants of the node and find the matching elements.
NodeDescendantsIterator iterator(node);
Node* child = iterator.First();
while (child) {
if (child->IsElement()) {
scoped_refptr<Element> element = child->AsElement();
if (MatchRuleAndElement(css_style_rule, element)) {
node_list->AppendNode(element);
}
}
child = iterator.Next();
}
return node_list;
}
} // namespace dom
} // namespace cobalt