| // Copyright 2015 The Cobalt Authors. 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 <memory> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "base/basictypes.h" | |
| #include "base/containers/hash_tables.h" | |
| #include "base/containers/small_map.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: | |
| 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::small_map< | |
| std::map<CompoundSelector*, Node*, CompoundNodeLessThan>, 2> { | |
| public: | |
| ~OwnedNodes() { | |
| for (iterator iter = begin(); iter != end(); ++iter) { | |
| delete iter->second; | |
| } | |
| } | |
| }; | |
| typedef std::map<std::pair<const Node*, CombinatorType>, OwnedNodes> | |
| OwnedNodesMap; | |
| 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::small_map<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 std::unique_ptr because the majority | |
| // of nodes do not contain any children. | |
| std::unique_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_nodes_.push_back(&root_node_); | |
| } | |
| 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); | |
| #if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS) | |
| // Validates the selector tree's compatibility against pre-selected versions | |
| // of Cobalt. Returns true if there are no version compatibility violations. | |
| bool ValidateVersionCompatibility() const; | |
| #endif // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS) | |
| 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_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_; | |
| DISALLOW_COPY_AND_ASSIGN(SelectorTree); | |
| }; | |
| } // namespace cssom | |
| } // namespace cobalt | |
| #endif // COBALT_CSSOM_SELECTOR_TREE_H_ |