StyleResolver.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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. int substart = 0;
  92. for (int i = 0; i < string.length(); ++i) {
  93. char ch = string.characters_without_null_termination()[i];
  94. if (isspace(ch)) {
  95. int sublen = i - substart;
  96. if (sublen != 0)
  97. v.append(string.substring_view(substart, sublen));
  98. substart = i + 1;
  99. }
  100. }
  101. int 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::Margin) {
  109. if (value.is_length()) {
  110. style.set_property(CSS::PropertyID::MarginTop, value);
  111. style.set_property(CSS::PropertyID::MarginRight, value);
  112. style.set_property(CSS::PropertyID::MarginBottom, value);
  113. style.set_property(CSS::PropertyID::MarginLeft, value);
  114. return;
  115. }
  116. if (value.is_string()) {
  117. auto parts = split_on_whitespace(value.to_string());
  118. if (parts.size() == 2) {
  119. auto vertical = parse_css_value(parts[0]);
  120. auto horizontal = parse_css_value(parts[1]);
  121. style.set_property(CSS::PropertyID::MarginTop, vertical);
  122. style.set_property(CSS::PropertyID::MarginBottom, vertical);
  123. style.set_property(CSS::PropertyID::MarginLeft, horizontal);
  124. style.set_property(CSS::PropertyID::MarginRight, horizontal);
  125. return;
  126. }
  127. if (parts.size() == 3) {
  128. auto top = parse_css_value(parts[0]);
  129. auto horizontal = parse_css_value(parts[1]);
  130. auto bottom = parse_css_value(parts[2]);
  131. style.set_property(CSS::PropertyID::MarginTop, top);
  132. style.set_property(CSS::PropertyID::MarginBottom, bottom);
  133. style.set_property(CSS::PropertyID::MarginLeft, horizontal);
  134. style.set_property(CSS::PropertyID::MarginRight, horizontal);
  135. return;
  136. }
  137. if (parts.size() == 4) {
  138. auto top = parse_css_value(parts[0]);
  139. auto right = parse_css_value(parts[1]);
  140. auto bottom = parse_css_value(parts[2]);
  141. auto left = parse_css_value(parts[3]);
  142. style.set_property(CSS::PropertyID::MarginTop, top);
  143. style.set_property(CSS::PropertyID::MarginBottom, bottom);
  144. style.set_property(CSS::PropertyID::MarginLeft, left);
  145. style.set_property(CSS::PropertyID::MarginRight, right);
  146. return;
  147. }
  148. dbg() << "Unsure what to do with CSS margin value '" << value.to_string() << "'";
  149. return;
  150. }
  151. return;
  152. }
  153. style.set_property(property_id, value);
  154. }
  155. NonnullRefPtr<StyleProperties> StyleResolver::resolve_style(const Element& element, const StyleProperties* parent_style) const
  156. {
  157. auto style = StyleProperties::create();
  158. if (parent_style) {
  159. parent_style->for_each_property([&](auto property_id, auto& value) {
  160. if (is_inherited_property(property_id))
  161. set_property_expanding_shorthands(style, property_id, value);
  162. });
  163. }
  164. element.apply_presentational_hints(*style);
  165. auto matching_rules = collect_matching_rules(element);
  166. for (auto& rule : matching_rules) {
  167. for (auto& property : rule.declaration().properties()) {
  168. set_property_expanding_shorthands(style, property.property_id, property.value);
  169. }
  170. }
  171. auto style_attribute = element.attribute("style");
  172. if (!style_attribute.is_null()) {
  173. if (auto declaration = parse_css_declaration(style_attribute)) {
  174. for (auto& property : declaration->properties()) {
  175. set_property_expanding_shorthands(style, property.property_id, property.value);
  176. }
  177. }
  178. }
  179. return style;
  180. }