ElementDefinition.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. #include "../../Include/Rocket/Core/PropertyIterators.h"
  32. namespace Rocket {
  33. namespace Core {
  34. ElementDefinition::ElementDefinition()
  35. {
  36. structurally_volatile = false;
  37. }
  38. ElementDefinition::~ElementDefinition()
  39. {
  40. }
  41. // Initialises the element definition from a list of style sheet nodes.
  42. void ElementDefinition::Initialise(const std::vector< const StyleSheetNode* >& style_sheet_nodes, const PseudoClassList& volatile_pseudo_classes, bool _structurally_volatile, const StyleSheet& style_sheet)
  43. {
  44. // Set the volatile structure flag.
  45. structurally_volatile = _structurally_volatile;
  46. // Mark all the volatile pseudo-classes as structurally volatile.
  47. for (PseudoClassList::const_iterator i = volatile_pseudo_classes.begin(); i != volatile_pseudo_classes.end(); ++i)
  48. pseudo_class_volatility[*i] = STRUCTURE_VOLATILE;
  49. // Merge the default (non-pseudo-class) properties.
  50. for (size_t i = 0; i < style_sheet_nodes.size(); ++i)
  51. properties.Merge(style_sheet_nodes[i]->GetProperties());
  52. // Merge the pseudo-class properties.
  53. PseudoClassPropertyMap merged_pseudo_class_properties;
  54. for (size_t i = 0; i < style_sheet_nodes.size(); ++i)
  55. {
  56. // Merge all the pseudo-classes.
  57. PseudoClassPropertyMap node_properties;
  58. style_sheet_nodes[i]->GetPseudoClassProperties(node_properties);
  59. for (PseudoClassPropertyMap::iterator j = node_properties.begin(); j != node_properties.end(); ++j)
  60. {
  61. // Merge the property maps into one uber-map; for the decorators.
  62. PseudoClassPropertyMap::iterator k = merged_pseudo_class_properties.find((*j).first);
  63. if (k == merged_pseudo_class_properties.end())
  64. merged_pseudo_class_properties[(*j).first] = (*j).second;
  65. else
  66. (*k).second.Merge((*j).second);
  67. // Search through all entries in this dictionary; we'll insert each one into our optimised list of
  68. // pseudo-class properties.
  69. for (PropertyMap::const_iterator k = (*j).second.GetProperties().begin(); k != (*j).second.GetProperties().end(); ++k)
  70. {
  71. PropertyId property_id = (*k).first;
  72. const Property& property = (*k).second;
  73. // Skip this property if its specificity is lower than the base property's, as in
  74. // this case it will never be used.
  75. const Property* default_property = properties.GetProperty(property_id);
  76. if (default_property != NULL &&
  77. default_property->specificity >= property.specificity)
  78. continue;
  79. PseudoClassPropertyDictionary::iterator l = pseudo_class_properties.find(property_id);
  80. if (l == pseudo_class_properties.end())
  81. pseudo_class_properties[property_id] = PseudoClassPropertyList(1, PseudoClassProperty((*j).first, property));
  82. else
  83. {
  84. // Find the location to insert this entry in the map, based on property priorities.
  85. int index = 0;
  86. while (index < (int) (*l).second.size() &&
  87. (*l).second[index].second.specificity > property.specificity)
  88. index++;
  89. (*l).second.insert((*l).second.begin() + index, PseudoClassProperty((*j).first, property));
  90. }
  91. }
  92. }
  93. }
  94. // Turn the decorator properties from String to DecoratorList.
  95. // This is essentially an optimization, we could do this just as well in e.g. ComputeValues() or ElementDecoration, but when we do it here, we only need to do it once.
  96. // Note, since the user may set a new decorator through its style, we we need to do it again in ComputeValues.
  97. }
  98. // Returns a specific property from the element definition's base properties.
  99. const Property* ElementDefinition::GetProperty(PropertyId id, const PseudoClassList& pseudo_classes) const
  100. {
  101. // Find a pseudo-class override for this property.
  102. if(pseudo_class_properties.size() > 0 && pseudo_classes.size() > 0)
  103. {
  104. PseudoClassPropertyDictionary::const_iterator property_iterator = pseudo_class_properties.find(id);
  105. if (property_iterator != pseudo_class_properties.end())
  106. {
  107. const PseudoClassPropertyList& property_list = (*property_iterator).second;
  108. for (size_t i = 0; i < property_list.size(); ++i)
  109. {
  110. if (!IsPseudoClassRuleApplicable(property_list[i].first, pseudo_classes))
  111. continue;
  112. return &property_list[i].second;
  113. }
  114. }
  115. }
  116. return properties.GetProperty(id);
  117. }
  118. // Returns the list of properties this element definition defines for an element with the given set of pseudo-classes.
  119. void ElementDefinition::GetDefinedProperties(PropertyNameList& property_names, const PseudoClassList& pseudo_classes) const
  120. {
  121. for (PropertyMap::const_iterator i = properties.GetProperties().begin(); i != properties.GetProperties().end(); ++i)
  122. property_names.insert((*i).first);
  123. for (PseudoClassPropertyDictionary::const_iterator i = pseudo_class_properties.begin(); i != pseudo_class_properties.end(); ++i)
  124. {
  125. // If this property is already in the default dictionary, don't bother checking for it here.
  126. if (property_names.find((*i).first) != property_names.end())
  127. continue;
  128. const PseudoClassPropertyList& property_list = (*i).second;
  129. // Search through all the pseudo-class combinations that have a definition for this property; if the calling
  130. // element matches at least one of them, then add it to the list.
  131. bool property_defined = false;
  132. for (size_t j = 0; j < property_list.size(); ++j)
  133. {
  134. if (IsPseudoClassRuleApplicable(property_list[j].first, pseudo_classes))
  135. {
  136. property_defined = true;
  137. break;
  138. }
  139. }
  140. if (property_defined)
  141. property_names.insert((*i).first);
  142. }
  143. }
  144. // Returns the list of properties this element definition has explicit definitions for involving the given
  145. // pseudo-class.
  146. void ElementDefinition::GetDefinedProperties(PropertyNameList& property_names, const PseudoClassList& pseudo_classes, const String& pseudo_class) const
  147. {
  148. for (PseudoClassPropertyDictionary::const_iterator i = pseudo_class_properties.begin(); i != pseudo_class_properties.end(); ++i)
  149. {
  150. // If this property has already been found, don't bother checking for it again.
  151. if (property_names.find((*i).first) != property_names.end())
  152. continue;
  153. const PseudoClassPropertyList& property_list = (*i).second;
  154. bool property_defined = false;
  155. for (size_t j = 0; j < property_list.size(); ++j)
  156. {
  157. bool rule_valid = true;
  158. bool found_toggled_pseudo_class = false;
  159. const StringList& rule_pseudo_classes = property_list[j].first;
  160. for (size_t j = 0; j < rule_pseudo_classes.size(); ++j)
  161. {
  162. if (rule_pseudo_classes[j] == pseudo_class)
  163. {
  164. found_toggled_pseudo_class = true;
  165. continue;
  166. }
  167. if (std::find(pseudo_classes.begin(), pseudo_classes.end(), rule_pseudo_classes[j]) == pseudo_classes.end())
  168. {
  169. rule_valid = false;
  170. break;
  171. }
  172. }
  173. if (rule_valid &&
  174. found_toggled_pseudo_class)
  175. {
  176. property_defined = true;
  177. break;
  178. }
  179. }
  180. if (property_defined)
  181. property_names.insert((*i).first);
  182. }
  183. }
  184. // Returns the volatility of a pseudo-class.
  185. ElementDefinition::PseudoClassVolatility ElementDefinition::GetPseudoClassVolatility(const String& pseudo_class) const
  186. {
  187. PseudoClassVolatilityMap::const_iterator i = pseudo_class_volatility.find(pseudo_class);
  188. if (i == pseudo_class_volatility.end())
  189. return STABLE;
  190. else
  191. return i->second;
  192. }
  193. // Returns true if this definition is built from nodes using structural selectors.
  194. bool ElementDefinition::IsStructurallyVolatile() const
  195. {
  196. return structurally_volatile;
  197. }
  198. ElementDefinitionIterator ElementDefinition::begin(const PseudoClassList& pseudo_classes) const {
  199. return ElementDefinitionIterator(pseudo_classes, properties.GetProperties().begin(), pseudo_class_properties.begin(), properties.GetProperties().end(), pseudo_class_properties.end());
  200. }
  201. ElementDefinitionIterator ElementDefinition::end(const PseudoClassList& pseudo_classes) const {
  202. return ElementDefinitionIterator(pseudo_classes, properties.GetProperties().end(), pseudo_class_properties.end(), properties.GetProperties().end(), pseudo_class_properties.end());
  203. }
  204. // Destroys the definition.
  205. void ElementDefinition::OnReferenceDeactivate()
  206. {
  207. delete this;
  208. }
  209. // Returns true if the pseudo-class requirement of a rule is met by a list of an element's pseudo-classes.
  210. bool ElementDefinition::IsPseudoClassRuleApplicable(const StringList& rule_pseudo_classes, const PseudoClassList& element_pseudo_classes)
  211. {
  212. for (StringList::size_type i = 0; i < rule_pseudo_classes.size(); ++i)
  213. {
  214. if (std::find(element_pseudo_classes.begin(), element_pseudo_classes.end(), rule_pseudo_classes[i]) == element_pseudo_classes.end())
  215. return false;
  216. }
  217. return true;
  218. }
  219. }
  220. }