| // 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. |
| |
| #ifndef COBALT_CSSOM_SELECTOR_TREE_H_ |
| #define COBALT_CSSOM_SELECTOR_TREE_H_ |
| |
| #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: |
| // https://docs.google.com/document/d/1LTbSenGsGR94JTGg6DfZDXYB3MBBCp4C8dRC4rckt_8/ |
| class SelectorTree { |
| public: |
| typedef std::vector<base::WeakPtr<CSSStyleRule> > Rules; |
| |
| class Node; |
| |
| // This class can be used to store Nodes. It stores the Nodes in its |
| // internal buffer whose size can be configured via template parameter. |
| // After the internal buffer is used up the extra Nodes will be stored inside |
| // the contained std::vector. |
| // TODO: Move this off to its own file if this can also be used by |
| // other code. |
| template <size_t InternalCacheSize> |
| class NodeSet { |
| public: |
| // Minimum interface for iterator. |
| class const_iterator { |
| public: |
| const_iterator(const NodeSet* set, size_t index) |
| : set_(set), index_(index) {} |
| void operator++() { ++index_; } |
| const Node* operator*() const { return set_->GetNode(index_); } |
| bool operator!=(const const_iterator& that) const { |
| return set_ != that.set_ || index_ != that.index_; |
| } |
| |
| private: |
| const NodeSet* set_; |
| size_t index_; |
| }; |
| |
| NodeSet() : size_(0) {} |
| void insert(const Node* node, bool check_for_duplicate = false) { |
| // If |check_for_duplicate| is true, then check if the node is already |
| // contained. In nearly all cases, this check is unnecessary because it is |
| // already known that the node is not a duplicate. As a result, the caller |
| // must explicitly request the check when needed. |
| if (check_for_duplicate) { |
| for (size_t i = 0; i < size_; ++i) { |
| if (GetNode(i) == node) { |
| return; |
| } |
| } |
| } |
| |
| if (size_ < InternalCacheSize) { |
| nodes_[size_] = node; |
| } else { |
| nodes_vector_.push_back(node); |
| } |
| ++size_; |
| } |
| template <class ConstIterator> |
| void insert(ConstIterator begin, ConstIterator end, |
| bool check_for_duplicate = false) { |
| while (begin != end) { |
| insert(*begin, check_for_duplicate); |
| ++begin; |
| } |
| } |
| |
| const_iterator begin() const { return const_iterator(this, 0); } |
| const_iterator end() const { return const_iterator(this, size_); } |
| size_t size() const { return size_; } |
| void clear() { |
| size_ = 0; |
| nodes_vector_.clear(); |
| } |
| const Node* GetNode(size_t index) const { |
| if (index < InternalCacheSize) { |
| return nodes_[index]; |
| } |
| return nodes_vector_[index - InternalCacheSize]; |
| } |
| |
| private: |
| size_t size_; |
| const Node* nodes_[InternalCacheSize]; |
| std::vector<const Node*> nodes_vector_; |
| }; |
| |
| 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> { |
| public: |
| ~OwnedNodes() { |
| for (iterator iter = begin(); iter != end(); ++iter) { |
| delete iter->second; |
| } |
| } |
| }; |
| |
| 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> |
| SelectorTextToNodesMap; |
| |
| class Node { |
| public: |
| 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_nodes_.push_back(child_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, |
| node}; |
| (*selector_nodes_map_)[name].push_back(child_node); |
| selector_mask_ |= |
| (1u << (simple_selector_type * kCombinatorCount + combinator_type)); |
| } |
| |
| private: |
| friend class SelectorTree; |
| |
| Node(); |
| 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, |
| combinator_mask_does_not_have_enough_bits); |
| |
| // 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, |
| selector_mask_does_not_have_enough_bits); |
| 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, |
| pseudo_class_mask_does_not_have_enough_bits); |
| |
| // Rules that end with this node. |
| Rules rules_; |
| }; |
| |
| SelectorTree() : has_sibling_combinators_(false) { root_set_.insert(&root_); } |
| |
| const Node* root() const { return &root_; } |
| const NodeSet<1>& root_set() const { return root_set_; } |
| |
| 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); |
| |
| private: |
| // 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_; |
| NodeSet<1> root_set_; |
| |
| // 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. |
| std::map<std::pair<const Node*, CombinatorType>, OwnedNodes> owned_nodes_map_; |
| |
| bool has_sibling_combinators_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SelectorTree); |
| }; |
| |
| } // namespace cssom |
| } // namespace cobalt |
| |
| #endif // COBALT_CSSOM_SELECTOR_TREE_H_ |