StyleSheetSelector.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. #include "StyleSheetSelector.h"
  2. #include "../../Include/RmlUi/Core/Element.h"
  3. #include "StyleSheetNode.h"
  4. #include <tuple>
  5. namespace Rml {
  6. static inline bool IsTextElement(const Element* element)
  7. {
  8. return element->GetTagName() == "#text";
  9. }
  10. // Returns true if a positive integer can be found for n in the equation an + b = count.
  11. static bool IsNth(int a, int b, int count)
  12. {
  13. int x = count;
  14. x -= b;
  15. if (a != 0)
  16. x /= a;
  17. return (x >= 0 && x * a + b == count);
  18. }
  19. bool operator==(const AttributeSelector& a, const AttributeSelector& b)
  20. {
  21. return a.type == b.type && a.name == b.name && a.value == b.value;
  22. }
  23. bool operator<(const AttributeSelector& a, const AttributeSelector& b)
  24. {
  25. return std::tie(a.type, a.name, a.value) < std::tie(b.type, b.name, b.value);
  26. }
  27. bool operator==(const StructuralSelector& a, const StructuralSelector& b)
  28. {
  29. // Currently sub-selectors (selector_tree) are only superficially compared. This mainly has the consequence that selectors with a sub-selector
  30. // which are instantiated separately will never compare equal, even if they have the exact same sub-selector expression. This further results in
  31. // such selectors not being de-duplicated. This should not lead to any functional differences but leads to potentially missed memory/performance
  32. // optimizations. E.g. 'div a, div b' will combine the two div nodes, while ':not(div) a, :not(div) b' will not combine the two not-div nodes.
  33. return a.type == b.type && a.a == b.a && a.b == b.b && a.selector_tree == b.selector_tree;
  34. }
  35. bool operator<(const StructuralSelector& a, const StructuralSelector& b)
  36. {
  37. return std::tie(a.type, a.a, a.b, a.selector_tree) < std::tie(b.type, b.a, b.b, b.selector_tree);
  38. }
  39. bool operator==(const CompoundSelector& a, const CompoundSelector& b)
  40. {
  41. if (a.tag != b.tag)
  42. return false;
  43. if (a.id != b.id)
  44. return false;
  45. if (a.class_names != b.class_names)
  46. return false;
  47. if (a.pseudo_class_names != b.pseudo_class_names)
  48. return false;
  49. if (a.attributes != b.attributes)
  50. return false;
  51. if (a.structural_selectors != b.structural_selectors)
  52. return false;
  53. if (a.combinator != b.combinator)
  54. return false;
  55. return true;
  56. }
  57. bool IsSelectorApplicable(const Element* element, const StructuralSelector& selector, const Element* scope)
  58. {
  59. RMLUI_ASSERT(element);
  60. switch (selector.type)
  61. {
  62. case StructuralSelectorType::Nth_Child:
  63. {
  64. Element* parent = element->GetParentNode();
  65. if (!parent)
  66. return false;
  67. // Start counting elements until we find this one.
  68. int element_index = 1;
  69. for (int i = 0; i < parent->GetNumChildren(); i++)
  70. {
  71. Element* child = parent->GetChild(i);
  72. // Skip text nodes.
  73. if (IsTextElement(child))
  74. continue;
  75. // If we've found our element, then break; the current index is our element's index.
  76. if (child == element)
  77. break;
  78. element_index++;
  79. }
  80. return IsNth(selector.a, selector.b, element_index);
  81. }
  82. break;
  83. case StructuralSelectorType::Nth_Last_Child:
  84. {
  85. Element* parent = element->GetParentNode();
  86. if (!parent)
  87. return false;
  88. // Start counting elements until we find this one.
  89. int element_index = 1;
  90. for (int i = parent->GetNumChildren() - 1; i >= 0; --i)
  91. {
  92. Element* child = parent->GetChild(i);
  93. // Skip text nodes.
  94. if (IsTextElement(child))
  95. continue;
  96. // If we've found our element, then break; the current index is our element's index.
  97. if (child == element)
  98. break;
  99. element_index++;
  100. }
  101. return IsNth(selector.a, selector.b, element_index);
  102. }
  103. break;
  104. case StructuralSelectorType::Nth_Of_Type:
  105. {
  106. Element* parent = element->GetParentNode();
  107. if (!parent)
  108. return false;
  109. // Start counting elements until we find this one.
  110. int element_index = 1;
  111. const int num_children = parent->GetNumChildren();
  112. for (int i = 0; i < num_children; i++)
  113. {
  114. Element* child = parent->GetChild(i);
  115. // If we've found our element, then break; the current index is our element's index.
  116. if (child == element)
  117. break;
  118. // Skip nodes that don't share our tag.
  119. if (child->GetTagName() != element->GetTagName())
  120. continue;
  121. element_index++;
  122. }
  123. return IsNth(selector.a, selector.b, element_index);
  124. }
  125. break;
  126. case StructuralSelectorType::Nth_Last_Of_Type:
  127. {
  128. Element* parent = element->GetParentNode();
  129. if (!parent)
  130. return false;
  131. // Start counting elements until we find this one.
  132. int element_index = 1;
  133. for (int i = parent->GetNumChildren() - 1; i >= 0; --i)
  134. {
  135. Element* child = parent->GetChild(i);
  136. // If we've found our element, then break; the current index is our element's index.
  137. if (child == element)
  138. break;
  139. // Skip nodes that don't share our tag.
  140. if (child->GetTagName() != element->GetTagName())
  141. continue;
  142. element_index++;
  143. }
  144. return IsNth(selector.a, selector.b, element_index);
  145. }
  146. break;
  147. case StructuralSelectorType::First_Child:
  148. {
  149. Element* parent = element->GetParentNode();
  150. if (!parent)
  151. return false;
  152. int child_index = 0;
  153. while (child_index < parent->GetNumChildren())
  154. {
  155. // If this child (the first non-text child) is our element, then the selector succeeds.
  156. Element* child = parent->GetChild(child_index);
  157. if (child == element)
  158. return true;
  159. // If this child is not a text element, then the selector fails; this element is non-trivial.
  160. if (!IsTextElement(child))
  161. return false;
  162. // Otherwise, skip over the text element to find the last non-trivial element.
  163. child_index++;
  164. }
  165. return false;
  166. }
  167. break;
  168. case StructuralSelectorType::Last_Child:
  169. {
  170. Element* parent = element->GetParentNode();
  171. if (!parent)
  172. return false;
  173. int child_index = parent->GetNumChildren() - 1;
  174. while (child_index >= 0)
  175. {
  176. // If this child (the last non-text child) is our element, then the selector succeeds.
  177. Element* child = parent->GetChild(child_index);
  178. if (child == element)
  179. return true;
  180. // If this child is not a text element, then the selector fails; this element is non-trivial.
  181. if (!IsTextElement(child))
  182. return false;
  183. // Otherwise, skip over the text element to find the last non-trivial element.
  184. child_index--;
  185. }
  186. return false;
  187. }
  188. break;
  189. case StructuralSelectorType::First_Of_Type:
  190. {
  191. Element* parent = element->GetParentNode();
  192. if (!parent)
  193. return false;
  194. int child_index = 0;
  195. while (child_index < parent->GetNumChildren())
  196. {
  197. // If this child is our element, then it's the first one we've found with our tag; the selector succeeds.
  198. Element* child = parent->GetChild(child_index);
  199. if (child == element)
  200. return true;
  201. // Otherwise, if this child shares our element's tag, then our element is not the first tagged child; the selector fails.
  202. if (child->GetTagName() == element->GetTagName())
  203. return false;
  204. child_index++;
  205. }
  206. return false;
  207. }
  208. break;
  209. case StructuralSelectorType::Last_Of_Type:
  210. {
  211. Element* parent = element->GetParentNode();
  212. if (!parent)
  213. return false;
  214. int child_index = parent->GetNumChildren() - 1;
  215. while (child_index >= 0)
  216. {
  217. // If this child is our element, then it's the first one we've found with our tag; the selector succeeds.
  218. Element* child = parent->GetChild(child_index);
  219. if (child == element)
  220. return true;
  221. // Otherwise, if this child shares our element's tag, then our element is not the first tagged child; the selector fails.
  222. if (child->GetTagName() == element->GetTagName())
  223. return false;
  224. child_index--;
  225. }
  226. return false;
  227. }
  228. break;
  229. case StructuralSelectorType::Only_Child:
  230. {
  231. Element* parent = element->GetParentNode();
  232. if (!parent)
  233. return false;
  234. const int num_children = parent->GetNumChildren();
  235. for (int i = 0; i < num_children; i++)
  236. {
  237. Element* child = parent->GetChild(i);
  238. // Skip the child if it is our element.
  239. if (child == element)
  240. continue;
  241. // Skip the child if it is trivial.
  242. if (IsTextElement(child))
  243. continue;
  244. return false;
  245. }
  246. return true;
  247. }
  248. break;
  249. case StructuralSelectorType::Only_Of_Type:
  250. {
  251. Element* parent = element->GetParentNode();
  252. if (!parent)
  253. return false;
  254. const int num_children = parent->GetNumChildren();
  255. for (int i = 0; i < num_children; i++)
  256. {
  257. Element* child = parent->GetChild(i);
  258. // Skip the child if it is our element.
  259. if (child == element)
  260. continue;
  261. // Skip the child if it does not share our tag.
  262. if (child->GetTagName() != element->GetTagName())
  263. continue;
  264. // We've found a similarly-tagged child to our element; selector fails.
  265. return false;
  266. }
  267. return true;
  268. }
  269. break;
  270. case StructuralSelectorType::Empty:
  271. {
  272. return element->GetNumChildren() == 0;
  273. }
  274. break;
  275. case StructuralSelectorType::Not:
  276. {
  277. if (!selector.selector_tree)
  278. {
  279. RMLUI_ERROR;
  280. return false;
  281. }
  282. bool inner_selector_matches = false;
  283. for (const StyleSheetNode* node : selector.selector_tree->leafs)
  284. {
  285. if (node->IsApplicable(element, scope))
  286. {
  287. inner_selector_matches = true;
  288. break;
  289. }
  290. }
  291. return !inner_selector_matches;
  292. }
  293. break;
  294. case StructuralSelectorType::Scope:
  295. {
  296. return scope && element == scope;
  297. }
  298. break;
  299. case StructuralSelectorType::Invalid:
  300. {
  301. RMLUI_ERROR;
  302. }
  303. break;
  304. }
  305. return false;
  306. }
  307. } // namespace Rml