blob: 98f33f6ef4a28a749106bfbcf6288dbeb2e5b27f [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
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "base/basictypes.h"
#include "base/containers/small_map.h"
#include "base/hash_tables.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "cobalt/base/token.h"
#include "cobalt/cssom/combinator.h"
#include "cobalt/cssom/pseudo_class_type.h"
#include "cobalt/cssom/simple_selector_type.h"
#include "cobalt/cssom/specificity.h"
namespace cobalt {
namespace cssom {
class CompoundSelector;
class ComplexSelector;
class CSSStyleRule;
// A selector tree is a tree structure that organizes compound selectors. It is
// used to accelerate rule matching. More details can be found in the doc:
class SelectorTree {
class Node;
typedef std::vector<base::WeakPtr<CSSStyleRule>> Rules;
typedef std::vector<const Node*> Nodes;
typedef std::vector<std::pair<const Node*, const Node*>> NodePairs;
struct CompoundNodeLessThan {
bool operator()(const CompoundSelector* lhs,
const CompoundSelector* rhs) const;
// This class holds references to Nodes allocated on the heap that are owned
// by the same parent Node. It deletes all contained Nodes on destruction.
class OwnedNodes
: public base::SmallMap<
std::map<CompoundSelector*, Node*, CompoundNodeLessThan>, 2> {
~OwnedNodes() {
for (iterator iter = begin(); iter != end(); ++iter) {
delete iter->second;
typedef std::map<std::pair<const Node*, CombinatorType>, OwnedNodes>
struct PseudoClassNode {
PseudoClassType pseudo_class_type;
CombinatorType combinator_type;
Node* node;
struct SimpleSelectorNode {
SimpleSelectorType simple_selector_type;
CombinatorType combinator_type;
Node* node;
typedef std::vector<SimpleSelectorNode> SimpleSelectorNodes;
typedef std::vector<PseudoClassNode> PseudoClassNodes;
// The vast majority of SelectorTextToNodesMap objects have 4 or fewer
// selectors. However, they occasionally can number in the hundreds. Using
// a SmallMap with an array size of 4, allows both cases to be handled
// quickly.
typedef base::SmallMap<base::hash_map<base::Token, SimpleSelectorNodes>, 4>
class Node {
CompoundSelector* compound_selector() const { return compound_selector_; }
Specificity cumulative_specificity() const {
return cumulative_specificity_;
bool HasCombinator(CombinatorType type) const {
return (combinator_mask_ & (1 << type)) != 0;
Rules& rules() { return rules_; }
const Rules& rules() const { return rules_; }
const PseudoClassNodes& pseudo_class_nodes() const {
return pseudo_class_nodes_;
bool HasAnyPseudoClass() const { return pseudo_class_mask_ != 0; }
bool HasPseudoClass(PseudoClassType pseudo_class_type,
CombinatorType combinator_type) const {
return (pseudo_class_mask_ &
(1u << (pseudo_class_type * kCombinatorCount +
combinator_type))) != 0;
void AppendPseudoClassNode(PseudoClassType pseudo_class_type,
CombinatorType combinator_type, Node* node) {
PseudoClassNode child_node = {pseudo_class_type, combinator_type, node};
pseudo_class_mask_ |=
(1u << (pseudo_class_type * kCombinatorCount + combinator_type));
const SelectorTextToNodesMap* selector_nodes_map() const {
return selector_nodes_map_.get();
bool HasSimpleSelector(SimpleSelectorType simple_selector_type,
CombinatorType combinator_type) const {
return (selector_mask_ & (1u << (simple_selector_type * kCombinatorCount +
combinator_type))) != 0;
void AppendSimpleSelector(base::Token name,
SimpleSelectorType simple_selector_type,
CombinatorType combinator_type, Node* node) {
// Create the SelectorTextToNodesMap if this is the first simple selector
// being appended.
if (!selector_nodes_map_) {
selector_nodes_map_.reset(new SelectorTextToNodesMap());
SimpleSelectorNode child_node = {simple_selector_type, combinator_type,
selector_mask_ |=
(1u << (simple_selector_type * kCombinatorCount + combinator_type));
friend class SelectorTree;
Node(CompoundSelector* compound_selector, Specificity parent_specificity);
// Bit mask used to quickly reject a certain combinator type.
uint8 combinator_mask_;
COMPILE_ASSERT(sizeof(uint8) * 8 >= kCombinatorCount,
// Pointer to one of the equivalent compound selectors.
CompoundSelector* compound_selector_;
// Sum of specificity from root to this node.
Specificity cumulative_specificity_;
// Indexes for the children. This is a scoped_ptr because the majority of
// nodes do not contain any children.
scoped_ptr<SelectorTextToNodesMap> selector_nodes_map_;
// Bit mask used to quickly reject a certain selector type and combinator
// combination.
uint32 selector_mask_;
COMPILE_ASSERT(sizeof(uint32) * 8 >=
kSimpleSelectorTypeCount * kCombinatorCount,
PseudoClassNodes pseudo_class_nodes_;
// Bit mask used to quickly reject a certain pseudo class and combinator
// combination.
uint32 pseudo_class_mask_;
COMPILE_ASSERT(sizeof(uint32) * 8 >=
kPseudoClassTypeCount * kCombinatorCount,
// Rules that end with this node.
Rules rules_;
SelectorTree() : has_sibling_combinators_(false) {
const Node* root_node() const { return &root_node_; }
const Nodes& root_nodes() const { return root_nodes_; }
Nodes* scratchpad_nodes_1() { return &scratchpad_nodes_1_; }
Nodes* scratchpad_nodes_2() { return &scratchpad_nodes_2_; }
NodePairs* scratchpad_node_pairs() { return &scratchpad_node_pairs_; }
bool has_sibling_combinators() const { return has_sibling_combinators_; }
void AppendRule(CSSStyleRule* rule);
void RemoveRule(CSSStyleRule* rule);
// Used by unit tests only.
const OwnedNodes& children(const Node* node, CombinatorType combinator);
// Validates the selector tree's compatibility against pre-selected versions
// of Cobalt. Returns true if there are no version compatibility violations.
bool ValidateVersionCompatibility() const;
// Gets or creates node for complex selector, starting from root.
Node* GetOrCreateNodeForComplexSelector(ComplexSelector* selector);
// Gets or creates node for compound selector, starting from given node, using
// given combiantor as edge.
Node* GetOrCreateNodeForCompoundSelector(CompoundSelector* compound_selector,
Node* parent_node,
CombinatorType combinator);
Node root_node_;
Nodes root_nodes_;
// These member variables are available for temporary operations, so that
// Node vectors associated with the SelectorTree don't have to be repeatedly
// re-allocated. This significantly speeds up rule matching.
Nodes scratchpad_nodes_1_;
Nodes scratchpad_nodes_2_;
NodePairs scratchpad_node_pairs_;
// This variable maps from a parent Node and a combinator type to child Nodes
// under the particular parent Node corresponding to the particular combinator
// type. It is only used when modifying the SelectorTree and is not used
// during rule matching. So we store it externally to the Node to minimize
// the size of Node structure.
OwnedNodesMap owned_nodes_map_;
bool has_sibling_combinators_;
} // namespace cssom
} // namespace cobalt