StyleResolver.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #include <LibHTML/CSS/StyleResolver.h>
  2. #include <LibHTML/CSS/StyleSheet.h>
  3. #include <LibHTML/DOM/Document.h>
  4. #include <LibHTML/DOM/Element.h>
  5. #include <LibHTML/Dump.h>
  6. #include <LibHTML/Parser/CSSParser.h>
  7. #include <stdio.h>
  8. StyleResolver::StyleResolver(Document& document)
  9. : m_document(document)
  10. {
  11. }
  12. StyleResolver::~StyleResolver()
  13. {
  14. }
  15. static bool matches(const Selector& selector, const Element& element)
  16. {
  17. // FIXME: Support compound selectors.
  18. ASSERT(selector.components().size() == 1);
  19. auto& component = selector.components().first();
  20. switch (component.type) {
  21. case Selector::Component::Type::Id:
  22. return component.value == element.attribute("id");
  23. case Selector::Component::Type::Class:
  24. return element.has_class(component.value);
  25. case Selector::Component::Type::TagName:
  26. return component.value == element.tag_name();
  27. default:
  28. ASSERT_NOT_REACHED();
  29. }
  30. }
  31. static StyleSheet& default_stylesheet()
  32. {
  33. static StyleSheet* sheet;
  34. if (!sheet) {
  35. extern const char default_stylesheet_source[];
  36. String css = default_stylesheet_source;
  37. sheet = &parse_css(css).leak_ref();
  38. }
  39. return *sheet;
  40. }
  41. template<typename Callback>
  42. void StyleResolver::for_each_stylesheet(Callback callback) const
  43. {
  44. callback(default_stylesheet());
  45. for (auto& sheet : document().stylesheets()) {
  46. callback(sheet);
  47. }
  48. }
  49. NonnullRefPtrVector<StyleRule> StyleResolver::collect_matching_rules(const Element& element) const
  50. {
  51. NonnullRefPtrVector<StyleRule> matching_rules;
  52. for_each_stylesheet([&](auto& sheet) {
  53. for (auto& rule : sheet.rules()) {
  54. for (auto& selector : rule.selectors()) {
  55. if (matches(selector, element)) {
  56. matching_rules.append(rule);
  57. break;
  58. }
  59. }
  60. }
  61. });
  62. #ifdef HTML_DEBUG
  63. dbgprintf("Rules matching Element{%p}\n", &element);
  64. for (auto& rule : matching_rules) {
  65. dump_rule(rule);
  66. }
  67. #endif
  68. return matching_rules;
  69. }
  70. bool StyleResolver::is_inherited_property(const StringView& name)
  71. {
  72. static HashTable<String> inherited_properties;
  73. if (inherited_properties.is_empty()) {
  74. inherited_properties.set("border-collapse");
  75. inherited_properties.set("border-spacing");
  76. inherited_properties.set("color");
  77. inherited_properties.set("font-family");
  78. inherited_properties.set("font-size");
  79. inherited_properties.set("font-style");
  80. inherited_properties.set("font-variant");
  81. inherited_properties.set("font-weight");
  82. inherited_properties.set("font");
  83. inherited_properties.set("letter-spacing");
  84. inherited_properties.set("line-height");
  85. inherited_properties.set("list-style-image");
  86. inherited_properties.set("list-style-position");
  87. inherited_properties.set("list-style-type");
  88. inherited_properties.set("list-style");
  89. inherited_properties.set("text-align");
  90. inherited_properties.set("text-indent");
  91. inherited_properties.set("text-transform");
  92. inherited_properties.set("visibility");
  93. inherited_properties.set("white-space");
  94. inherited_properties.set("word-spacing");
  95. // FIXME: This property is not supposed to be inherited, but we currently
  96. // rely on inheritance to propagate decorations into line boxes.
  97. inherited_properties.set("text-decoraton");
  98. }
  99. return inherited_properties.contains(name);
  100. }
  101. NonnullRefPtr<StyleProperties> StyleResolver::resolve_style(const Element& element, const StyleProperties* parent_properties) const
  102. {
  103. auto style_properties = StyleProperties::create();
  104. if (parent_properties) {
  105. parent_properties->for_each_property([&](const StringView& name, auto& value) {
  106. if (is_inherited_property(name))
  107. style_properties->set_property(name, value);
  108. });
  109. }
  110. element.apply_presentational_hints(*style_properties);
  111. auto matching_rules = collect_matching_rules(element);
  112. for (auto& rule : matching_rules) {
  113. for (auto& property : rule.declaration().properties()) {
  114. style_properties->set_property(property.name, property.value);
  115. }
  116. }
  117. auto style_attribute = element.attribute("style");
  118. if (!style_attribute.is_null()) {
  119. if (auto declaration = parse_css_declaration(style_attribute)) {
  120. for (auto& property : declaration->properties()) {
  121. style_properties->set_property(property.name, property.value);
  122. }
  123. }
  124. }
  125. return style_properties;
  126. }