blob: 1a32266ede31f7af928c27a624e21778e5232097 [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/cssom/selector_tree.h"
#include <set>
#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
#include "cobalt/base/version_compatibility.h"
#endif // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
#include "cobalt/cssom/complex_selector.h"
#include "cobalt/cssom/compound_selector.h"
#include "cobalt/cssom/css_style_rule.h"
namespace cobalt {
namespace cssom {
bool SelectorTree::CompoundNodeLessThan::operator()(
const CompoundSelector* lhs, const CompoundSelector* rhs) const {
return *lhs < *rhs;
}
SelectorTree::Node::Node()
: combinator_mask_(0),
compound_selector_(NULL),
selector_mask_(0),
pseudo_class_mask_(0) {}
SelectorTree::Node::Node(CompoundSelector* compound_selector,
Specificity parent_specificity)
: combinator_mask_(0),
compound_selector_(compound_selector),
cumulative_specificity_(parent_specificity),
selector_mask_(0),
pseudo_class_mask_(0) {
cumulative_specificity_.AddFrom(compound_selector_->GetSpecificity());
}
void SelectorTree::AppendRule(CSSStyleRule* rule) {
for (Selectors::const_iterator it = rule->selectors().begin();
it != rule->selectors().end(); ++it) {
DCHECK((*it)->AsComplexSelector());
Node* node = GetOrCreateNodeForComplexSelector((*it)->AsComplexSelector());
node->rules().push_back(base::AsWeakPtr(rule));
}
}
void SelectorTree::RemoveRule(CSSStyleRule* rule) {
for (Selectors::const_iterator it = rule->selectors().begin();
it != rule->selectors().end(); ++it) {
DCHECK((*it)->AsComplexSelector());
Node* node = GetOrCreateNodeForComplexSelector((*it)->AsComplexSelector());
for (Rules::iterator remove_it = node->rules().begin();
remove_it != node->rules().end(); ++remove_it) {
if (*remove_it == rule) {
node->rules().erase(remove_it);
break;
}
}
}
}
const SelectorTree::OwnedNodes& SelectorTree::children(
const Node* node, CombinatorType combinator) {
return owned_nodes_map_[std::make_pair(node, combinator)];
}
#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
namespace {
// This uses the old CompoundSelector compare logic that had a bug where 'not'
// pseudo classes with the same prefix would be treated as matching, regardless
// of their arguments.
struct PreVersion16CompoundSelectorLessThan {
bool operator()(const CompoundSelector* lhs,
const CompoundSelector* rhs) const {
const CompoundSelector::SimpleSelectors& lhs_simple_selectors =
lhs->simple_selectors();
const CompoundSelector::SimpleSelectors& rhs_simple_selectors =
rhs->simple_selectors();
if (lhs_simple_selectors.size() < rhs_simple_selectors.size()) {
return true;
}
if (lhs_simple_selectors.size() > rhs_simple_selectors.size()) {
return false;
}
for (size_t i = 0; i < lhs_simple_selectors.size(); ++i) {
if (lhs_simple_selectors[i]->type() < rhs_simple_selectors[i]->type()) {
return true;
}
if (lhs_simple_selectors[i]->type() > rhs_simple_selectors[i]->type()) {
return false;
}
if (lhs_simple_selectors[i]->prefix() <
rhs_simple_selectors[i]->prefix()) {
return true;
}
if (lhs_simple_selectors[i]->prefix() >
rhs_simple_selectors[i]->prefix()) {
return false;
}
if (lhs_simple_selectors[i]->text() < rhs_simple_selectors[i]->text()) {
return true;
}
if (lhs_simple_selectors[i]->text() > rhs_simple_selectors[i]->text()) {
return false;
}
}
return false;
}
};
bool HasNotPseudoClassCompatibilityViolations(
const SelectorTree::OwnedNodesMap& owned_nodes_map) {
constexpr int kNotPseudoClassCobaltVersionFix = 16;
if (base::VersionCompatibility::GetInstance()->GetMinimumVersion() >=
kNotPseudoClassCobaltVersionFix) {
return false;
}
typedef std::set<CompoundSelector*, PreVersion16CompoundSelectorLessThan>
PreVersion16CompoundSelectorLessThanSet;
// Walk all of the owned nodes looking for multiple compound selectors within
// them that are treated as duplicates using the pre-version 16 compound
// selector less than logic.
PreVersion16CompoundSelectorLessThanSet unsupported_usage_set;
for (const auto& owned_nodes_iterator : owned_nodes_map) {
PreVersion16CompoundSelectorLessThanSet encountered_set;
for (const auto& node_info : owned_nodes_iterator.second) {
if (!encountered_set.insert(node_info.first).second) {
unsupported_usage_set.insert(node_info.first);
}
}
}
// Log the cases of unsupported usage.
for (const auto& unsupported_usage_iterator : unsupported_usage_set) {
const std::string kUnsupportedNotUsageString =
"Unsupported :not() pseudo class usage found. Multiple negation pseudo "
"classes with the same prefix but different arguments are not "
"supported in versions of Cobalt <= 15. Offending selectors: '";
std::string selectors_string;
const CompoundSelector::SimpleSelectors& simple_selectors =
unsupported_usage_iterator->simple_selectors();
for (const auto& simple_selector : simple_selectors) {
selectors_string += simple_selector->prefix().c_str();
if (simple_selector->type() == kPseudoClass &&
simple_selector->GetContainedCompoundSelector() != NULL) {
selectors_string += "not(...)";
} else {
selectors_string += simple_selector->text().c_str();
}
}
base::VersionCompatibility::GetInstance()->ReportViolation(
kUnsupportedNotUsageString + selectors_string);
}
return !unsupported_usage_set.empty();
}
} // namespace
bool SelectorTree::ValidateVersionCompatibility() const {
return !HasNotPseudoClassCompatibilityViolations(owned_nodes_map_);
}
#endif // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
SelectorTree::Node* SelectorTree::GetOrCreateNodeForComplexSelector(
ComplexSelector* complex_selector) {
CompoundSelector* selector = complex_selector->first_selector();
Node* node = GetOrCreateNodeForCompoundSelector(selector, &root_node_,
kDescendantCombinator);
while (selector->right_combinator()) {
Combinator* combinator = selector->right_combinator();
selector = combinator->right_selector();
node = GetOrCreateNodeForCompoundSelector(selector, node,
combinator->GetCombinatorType());
}
return node;
}
SelectorTree::Node* SelectorTree::GetOrCreateNodeForCompoundSelector(
CompoundSelector* compound_selector, Node* parent_node,
CombinatorType combinator) {
if (combinator == kNextSiblingCombinator ||
combinator == kFollowingSiblingCombinator) {
has_sibling_combinators_ = true;
}
OwnedNodes& owned_nodes =
owned_nodes_map_[std::make_pair(parent_node, combinator)];
OwnedNodes::iterator child_node_it = owned_nodes.find(compound_selector);
if (child_node_it != owned_nodes.end()) {
return child_node_it->second;
}
Node* child_node =
new Node(compound_selector, parent_node->cumulative_specificity());
parent_node->combinator_mask_ |= (1 << combinator);
owned_nodes[compound_selector] = child_node;
(*compound_selector->simple_selectors().begin())
->IndexSelectorTreeNode(parent_node, child_node, combinator);
return child_node;
}
} // namespace cssom
} // namespace cobalt