ElementDefinition.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /*
  2. * This source file is part of libRocket, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://www.librocket.com
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. *
  26. */
  27. #include "precompiled.h"
  28. #include "ElementDefinition.h"
  29. #include "StyleSheetNode.h"
  30. #include "../../Include/Rocket/Core/Log.h"
  31. namespace Rocket {
  32. namespace Core {
  33. ElementDefinition::ElementDefinition()
  34. {
  35. structurally_volatile = false;
  36. }
  37. ElementDefinition::~ElementDefinition()
  38. {
  39. }
  40. // Initialises the element definition from a list of style sheet nodes.
  41. void ElementDefinition::Initialise(const std::vector< const StyleSheetNode* >& style_sheet_nodes, const PseudoClassList& volatile_pseudo_classes, bool _structurally_volatile)
  42. {
  43. // Set the volatile structure flag.
  44. structurally_volatile = _structurally_volatile;
  45. // Mark all the volatile pseudo-classes as structurally volatile.
  46. for (const String& pseudo_name : volatile_pseudo_classes)
  47. pseudo_class_volatility[pseudo_name] = STRUCTURE_VOLATILE;
  48. // Merge the default (non-pseudo-class) properties.
  49. for (size_t i = 0; i < style_sheet_nodes.size(); ++i)
  50. properties.Merge(style_sheet_nodes[i]->GetProperties());
  51. // Merge the pseudo-class properties.
  52. for (size_t i = 0; i < style_sheet_nodes.size(); ++i)
  53. {
  54. PseudoClassPropertyMap pseudo_properties_map;
  55. style_sheet_nodes[i]->GetPseudoClassProperties(pseudo_properties_map);
  56. for (auto& pseudo_properties_pair : pseudo_properties_map)
  57. {
  58. const PseudoClassList& pseudo_classes = pseudo_properties_pair.first;
  59. PropertyDictionary& pseudo_properties = pseudo_properties_pair.second;
  60. // Search through all entries in this dictionary; we'll insert each one into our optimised list of
  61. // pseudo-class properties.
  62. for (auto& property_pair : pseudo_properties.GetProperties())
  63. {
  64. PropertyId property_id = property_pair.first;
  65. const Property& property = property_pair.second;
  66. // Skip this property if its specificity is lower than the base property's, as in
  67. // this case it will never be used.
  68. const Property* default_property = properties.GetProperty(property_id);
  69. if (default_property && (default_property->specificity >= property.specificity))
  70. continue;
  71. auto it = pseudo_class_properties.find(property_id);
  72. if (it == pseudo_class_properties.end())
  73. pseudo_class_properties[property_id] = PseudoClassPropertyList(1, PseudoClassProperty(pseudo_classes, property));
  74. else
  75. {
  76. PseudoClassPropertyList& pseudo_property_list = it->second;
  77. // Find the location to insert this entry in the map, based on property priorities.
  78. int index = 0;
  79. while (index < (int)pseudo_property_list.size() && pseudo_property_list[index].second.specificity > property.specificity)
  80. index++;
  81. pseudo_property_list.insert(pseudo_property_list.begin() + index, PseudoClassProperty(pseudo_classes, property));
  82. }
  83. }
  84. }
  85. }
  86. }
  87. // Returns a specific property from the element definition's base properties.
  88. const Property* ElementDefinition::GetProperty(PropertyId id, const PseudoClassList& pseudo_classes) const
  89. {
  90. // Find a pseudo-class override for this property.
  91. if(pseudo_class_properties.size() > 0 && pseudo_classes.size() > 0)
  92. {
  93. PseudoClassPropertyDictionary::const_iterator property_iterator = pseudo_class_properties.find(id);
  94. if (property_iterator != pseudo_class_properties.end())
  95. {
  96. const PseudoClassPropertyList& property_list = (*property_iterator).second;
  97. for (size_t i = 0; i < property_list.size(); ++i)
  98. {
  99. if (!IsPseudoClassRuleApplicable(property_list[i].first, pseudo_classes))
  100. continue;
  101. return &property_list[i].second;
  102. }
  103. }
  104. }
  105. return properties.GetProperty(id);
  106. }
  107. // Returns the list of properties this element definition defines for an element with the given set of pseudo-classes.
  108. void ElementDefinition::GetDefinedProperties(PropertyNameList& property_names, const PseudoClassList& pseudo_classes) const
  109. {
  110. for (PropertyMap::const_iterator i = properties.GetProperties().begin(); i != properties.GetProperties().end(); ++i)
  111. property_names.insert((*i).first);
  112. for (const auto& pseudo_class_properties_pair : pseudo_class_properties)
  113. {
  114. PropertyId property_id = pseudo_class_properties_pair.first;
  115. // If this property is already in the default dictionary, don't bother checking for it here.
  116. if (property_names.find(property_id) != property_names.end())
  117. continue;
  118. const PseudoClassPropertyList& property_list = pseudo_class_properties_pair.second;
  119. // Search through all the pseudo-class combinations that have a definition for this property; if the calling
  120. // element matches at least one of them, then add it to the list.
  121. bool property_defined = false;
  122. for (size_t j = 0; j < property_list.size(); ++j)
  123. {
  124. if (IsPseudoClassRuleApplicable(property_list[j].first, pseudo_classes))
  125. {
  126. property_defined = true;
  127. break;
  128. }
  129. }
  130. if (property_defined)
  131. property_names.insert(property_id);
  132. }
  133. }
  134. // Returns the list of properties this element definition has explicit definitions for involving the given
  135. // pseudo-class.
  136. void ElementDefinition::GetDefinedProperties(PropertyNameList& property_names, const PseudoClassList& pseudo_classes, const String& pseudo_class) const
  137. {
  138. for (const auto& pseudo_class_properties_pair : pseudo_class_properties)
  139. {
  140. PropertyId property_id = pseudo_class_properties_pair.first;
  141. // If this property has already been found, don't bother checking for it again.
  142. if (property_names.find(property_id) != property_names.end())
  143. continue;
  144. const PseudoClassPropertyList& property_list = pseudo_class_properties_pair.second;
  145. bool property_defined = false;
  146. for (size_t j = 0; j < property_list.size(); ++j)
  147. {
  148. bool rule_valid = true;
  149. bool found_toggled_pseudo_class = false;
  150. const PseudoClassList& rule_pseudo_classes = property_list[j].first;
  151. for (const String& rule_pseudo_class : rule_pseudo_classes)
  152. {
  153. if (rule_pseudo_class == pseudo_class)
  154. {
  155. found_toggled_pseudo_class = true;
  156. continue;
  157. }
  158. if (pseudo_classes.count(rule_pseudo_class) == 0)
  159. {
  160. rule_valid = false;
  161. break;
  162. }
  163. }
  164. if (rule_valid &&
  165. found_toggled_pseudo_class)
  166. {
  167. property_defined = true;
  168. break;
  169. }
  170. }
  171. if (property_defined)
  172. property_names.insert(property_id);
  173. }
  174. }
  175. // Returns the volatility of a pseudo-class.
  176. ElementDefinition::PseudoClassVolatility ElementDefinition::GetPseudoClassVolatility(const String& pseudo_class) const
  177. {
  178. PseudoClassVolatilityMap::const_iterator i = pseudo_class_volatility.find(pseudo_class);
  179. if (i == pseudo_class_volatility.end())
  180. return STABLE;
  181. else
  182. return i->second;
  183. }
  184. // Returns true if this definition is built from nodes using structural selectors.
  185. bool ElementDefinition::IsStructurallyVolatile() const
  186. {
  187. return structurally_volatile;
  188. }
  189. // Destroys the definition.
  190. void ElementDefinition::OnReferenceDeactivate()
  191. {
  192. delete this;
  193. }
  194. // Returns true if the pseudo-class requirement of a rule is met by a list of an element's pseudo-classes.
  195. bool ElementDefinition::IsPseudoClassRuleApplicable(const PseudoClassList& rule_pseudo_classes, const PseudoClassList& element_pseudo_classes)
  196. {
  197. // The rule pseudo classes must be a subset of the element pseudo classes
  198. // Note, requires PseudoClassList to be an @ordered container.
  199. bool result = std::includes(element_pseudo_classes.begin(), element_pseudo_classes.end(), rule_pseudo_classes.begin(), rule_pseudo_classes.end());
  200. return result;
  201. }
  202. }
  203. }