StyleResolver.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. #include <LibHTML/CSS/SelectorEngine.h>
  2. #include <LibHTML/CSS/StyleResolver.h>
  3. #include <LibHTML/CSS/StyleSheet.h>
  4. #include <LibHTML/DOM/Document.h>
  5. #include <LibHTML/DOM/Element.h>
  6. #include <LibHTML/Dump.h>
  7. #include <LibHTML/Parser/CSSParser.h>
  8. #include <ctype.h>
  9. #include <stdio.h>
  10. StyleResolver::StyleResolver(Document& document)
  11. : m_document(document)
  12. {
  13. }
  14. StyleResolver::~StyleResolver()
  15. {
  16. }
  17. static StyleSheet& default_stylesheet()
  18. {
  19. static StyleSheet* sheet;
  20. if (!sheet) {
  21. extern const char default_stylesheet_source[];
  22. String css = default_stylesheet_source;
  23. sheet = parse_css(css).leak_ref();
  24. }
  25. return *sheet;
  26. }
  27. template<typename Callback>
  28. void StyleResolver::for_each_stylesheet(Callback callback) const
  29. {
  30. callback(default_stylesheet());
  31. for (auto& sheet : document().stylesheets()) {
  32. callback(sheet);
  33. }
  34. }
  35. NonnullRefPtrVector<StyleRule> StyleResolver::collect_matching_rules(const Element& element) const
  36. {
  37. NonnullRefPtrVector<StyleRule> matching_rules;
  38. for_each_stylesheet([&](auto& sheet) {
  39. for (auto& rule : sheet.rules()) {
  40. for (auto& selector : rule.selectors()) {
  41. if (SelectorEngine::matches(selector, element)) {
  42. matching_rules.append(rule);
  43. break;
  44. }
  45. }
  46. }
  47. });
  48. #ifdef HTML_DEBUG
  49. dbgprintf("Rules matching Element{%p}\n", &element);
  50. for (auto& rule : matching_rules) {
  51. dump_rule(rule);
  52. }
  53. #endif
  54. return matching_rules;
  55. }
  56. bool StyleResolver::is_inherited_property(CSS::PropertyID property_id)
  57. {
  58. static HashTable<CSS::PropertyID> inherited_properties;
  59. if (inherited_properties.is_empty()) {
  60. inherited_properties.set(CSS::PropertyID::BorderCollapse);
  61. inherited_properties.set(CSS::PropertyID::BorderSpacing);
  62. inherited_properties.set(CSS::PropertyID::Color);
  63. inherited_properties.set(CSS::PropertyID::FontFamily);
  64. inherited_properties.set(CSS::PropertyID::FontSize);
  65. inherited_properties.set(CSS::PropertyID::FontStyle);
  66. inherited_properties.set(CSS::PropertyID::FontVariant);
  67. inherited_properties.set(CSS::PropertyID::FontWeight);
  68. inherited_properties.set(CSS::PropertyID::LetterSpacing);
  69. inherited_properties.set(CSS::PropertyID::LineHeight);
  70. inherited_properties.set(CSS::PropertyID::ListStyle);
  71. inherited_properties.set(CSS::PropertyID::ListStyleImage);
  72. inherited_properties.set(CSS::PropertyID::ListStylePosition);
  73. inherited_properties.set(CSS::PropertyID::ListStyleType);
  74. inherited_properties.set(CSS::PropertyID::TextAlign);
  75. inherited_properties.set(CSS::PropertyID::TextIndent);
  76. inherited_properties.set(CSS::PropertyID::TextTransform);
  77. inherited_properties.set(CSS::PropertyID::Visibility);
  78. inherited_properties.set(CSS::PropertyID::WhiteSpace);
  79. inherited_properties.set(CSS::PropertyID::WordSpacing);
  80. // FIXME: This property is not supposed to be inherited, but we currently
  81. // rely on inheritance to propagate decorations into line boxes.
  82. inherited_properties.set(CSS::PropertyID::TextDecoration);
  83. }
  84. return inherited_properties.contains(property_id);
  85. }
  86. static Vector<String> split_on_whitespace(const StringView& string)
  87. {
  88. if (string.is_empty())
  89. return {};
  90. Vector<String> v;
  91. size_t substart = 0;
  92. for (size_t i = 0; i < string.length(); ++i) {
  93. char ch = string.characters_without_null_termination()[i];
  94. if (isspace(ch)) {
  95. size_t sublen = i - substart;
  96. if (sublen != 0)
  97. v.append(string.substring_view(substart, sublen));
  98. substart = i + 1;
  99. }
  100. }
  101. size_t taillen = string.length() - substart;
  102. if (taillen != 0)
  103. v.append(string.substring_view(substart, taillen));
  104. return v;
  105. }
  106. static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, const StyleValue& value)
  107. {
  108. if (property_id == CSS::PropertyID::BorderStyle) {
  109. style.set_property(CSS::PropertyID::BorderTopStyle, value);
  110. style.set_property(CSS::PropertyID::BorderRightStyle, value);
  111. style.set_property(CSS::PropertyID::BorderBottomStyle, value);
  112. style.set_property(CSS::PropertyID::BorderLeftStyle, value);
  113. return;
  114. }
  115. if (property_id == CSS::PropertyID::BorderWidth) {
  116. style.set_property(CSS::PropertyID::BorderTopWidth, value);
  117. style.set_property(CSS::PropertyID::BorderRightWidth, value);
  118. style.set_property(CSS::PropertyID::BorderBottomWidth, value);
  119. style.set_property(CSS::PropertyID::BorderLeftWidth, value);
  120. return;
  121. }
  122. if (property_id == CSS::PropertyID::BorderColor) {
  123. style.set_property(CSS::PropertyID::BorderTopColor, value);
  124. style.set_property(CSS::PropertyID::BorderRightColor, value);
  125. style.set_property(CSS::PropertyID::BorderBottomColor, value);
  126. style.set_property(CSS::PropertyID::BorderLeftColor, value);
  127. return;
  128. }
  129. if (property_id == CSS::PropertyID::Margin) {
  130. if (value.is_length()) {
  131. style.set_property(CSS::PropertyID::MarginTop, value);
  132. style.set_property(CSS::PropertyID::MarginRight, value);
  133. style.set_property(CSS::PropertyID::MarginBottom, value);
  134. style.set_property(CSS::PropertyID::MarginLeft, value);
  135. return;
  136. }
  137. if (value.is_string()) {
  138. auto parts = split_on_whitespace(value.to_string());
  139. if (parts.size() == 2) {
  140. auto vertical = parse_css_value(parts[0]);
  141. auto horizontal = parse_css_value(parts[1]);
  142. style.set_property(CSS::PropertyID::MarginTop, vertical);
  143. style.set_property(CSS::PropertyID::MarginBottom, vertical);
  144. style.set_property(CSS::PropertyID::MarginLeft, horizontal);
  145. style.set_property(CSS::PropertyID::MarginRight, horizontal);
  146. return;
  147. }
  148. if (parts.size() == 3) {
  149. auto top = parse_css_value(parts[0]);
  150. auto horizontal = parse_css_value(parts[1]);
  151. auto bottom = parse_css_value(parts[2]);
  152. style.set_property(CSS::PropertyID::MarginTop, top);
  153. style.set_property(CSS::PropertyID::MarginBottom, bottom);
  154. style.set_property(CSS::PropertyID::MarginLeft, horizontal);
  155. style.set_property(CSS::PropertyID::MarginRight, horizontal);
  156. return;
  157. }
  158. if (parts.size() == 4) {
  159. auto top = parse_css_value(parts[0]);
  160. auto right = parse_css_value(parts[1]);
  161. auto bottom = parse_css_value(parts[2]);
  162. auto left = parse_css_value(parts[3]);
  163. style.set_property(CSS::PropertyID::MarginTop, top);
  164. style.set_property(CSS::PropertyID::MarginBottom, bottom);
  165. style.set_property(CSS::PropertyID::MarginLeft, left);
  166. style.set_property(CSS::PropertyID::MarginRight, right);
  167. return;
  168. }
  169. dbg() << "Unsure what to do with CSS margin value '" << value.to_string() << "'";
  170. return;
  171. }
  172. return;
  173. }
  174. if (property_id == CSS::PropertyID::Padding) {
  175. if (value.is_length()) {
  176. style.set_property(CSS::PropertyID::PaddingTop, value);
  177. style.set_property(CSS::PropertyID::PaddingRight, value);
  178. style.set_property(CSS::PropertyID::PaddingBottom, value);
  179. style.set_property(CSS::PropertyID::PaddingLeft, value);
  180. return;
  181. }
  182. if (value.is_string()) {
  183. auto parts = split_on_whitespace(value.to_string());
  184. if (parts.size() == 2) {
  185. auto vertical = parse_css_value(parts[0]);
  186. auto horizontal = parse_css_value(parts[1]);
  187. style.set_property(CSS::PropertyID::PaddingTop, vertical);
  188. style.set_property(CSS::PropertyID::PaddingBottom, vertical);
  189. style.set_property(CSS::PropertyID::PaddingLeft, horizontal);
  190. style.set_property(CSS::PropertyID::PaddingRight, horizontal);
  191. return;
  192. }
  193. if (parts.size() == 3) {
  194. auto top = parse_css_value(parts[0]);
  195. auto horizontal = parse_css_value(parts[1]);
  196. auto bottom = parse_css_value(parts[2]);
  197. style.set_property(CSS::PropertyID::PaddingTop, top);
  198. style.set_property(CSS::PropertyID::PaddingBottom, bottom);
  199. style.set_property(CSS::PropertyID::PaddingLeft, horizontal);
  200. style.set_property(CSS::PropertyID::PaddingRight, horizontal);
  201. return;
  202. }
  203. if (parts.size() == 4) {
  204. auto top = parse_css_value(parts[0]);
  205. auto right = parse_css_value(parts[1]);
  206. auto bottom = parse_css_value(parts[2]);
  207. auto left = parse_css_value(parts[3]);
  208. style.set_property(CSS::PropertyID::PaddingTop, top);
  209. style.set_property(CSS::PropertyID::PaddingBottom, bottom);
  210. style.set_property(CSS::PropertyID::PaddingLeft, left);
  211. style.set_property(CSS::PropertyID::PaddingRight, right);
  212. return;
  213. }
  214. dbg() << "Unsure what to do with CSS padding value '" << value.to_string() << "'";
  215. return;
  216. }
  217. return;
  218. }
  219. style.set_property(property_id, value);
  220. }
  221. NonnullRefPtr<StyleProperties> StyleResolver::resolve_style(const Element& element, const StyleProperties* parent_style) const
  222. {
  223. auto style = StyleProperties::create();
  224. if (parent_style) {
  225. parent_style->for_each_property([&](auto property_id, auto& value) {
  226. if (is_inherited_property(property_id))
  227. set_property_expanding_shorthands(style, property_id, value);
  228. });
  229. }
  230. element.apply_presentational_hints(*style);
  231. auto matching_rules = collect_matching_rules(element);
  232. for (auto& rule : matching_rules) {
  233. for (auto& property : rule.declaration().properties()) {
  234. set_property_expanding_shorthands(style, property.property_id, property.value);
  235. }
  236. }
  237. auto style_attribute = element.attribute("style");
  238. if (!style_attribute.is_null()) {
  239. if (auto declaration = parse_css_declaration(style_attribute)) {
  240. for (auto& property : declaration->properties()) {
  241. set_property_expanding_shorthands(style, property.property_id, property.value);
  242. }
  243. }
  244. }
  245. return style;
  246. }