Dump.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <AK/Utf8View.h>
  27. #include <LibHTML/CSS/PropertyID.h>
  28. #include <LibHTML/CSS/StyleSheet.h>
  29. #include <LibHTML/DOM/Comment.h>
  30. #include <LibHTML/DOM/Document.h>
  31. #include <LibHTML/DOM/DocumentType.h>
  32. #include <LibHTML/DOM/Element.h>
  33. #include <LibHTML/DOM/Text.h>
  34. #include <LibHTML/Dump.h>
  35. #include <LibHTML/Layout/LayoutBlock.h>
  36. #include <LibHTML/Layout/LayoutNode.h>
  37. #include <LibHTML/Layout/LayoutText.h>
  38. #include <stdio.h>
  39. void dump_tree(const Node& node)
  40. {
  41. static int indent = 0;
  42. for (int i = 0; i < indent; ++i)
  43. dbgprintf(" ");
  44. if (is<Document>(node)) {
  45. dbgprintf("*Document*\n");
  46. } else if (is<Element>(node)) {
  47. dbgprintf("<%s", to<Element>(node).tag_name().characters());
  48. to<Element>(node).for_each_attribute([](auto& name, auto& value) {
  49. dbgprintf(" %s=%s", name.characters(), value.characters());
  50. });
  51. dbgprintf(">\n");
  52. } else if (is<Text>(node)) {
  53. dbgprintf("\"%s\"\n", static_cast<const Text&>(node).data().characters());
  54. } else if (is<DocumentType>(node)) {
  55. dbgprintf("<!DOCTYPE>\n");
  56. } else if (is<Comment>(node)) {
  57. dbgprintf("<!--%s-->\n", to<Comment>(node).data().characters());
  58. }
  59. ++indent;
  60. if (is<ParentNode>(node)) {
  61. static_cast<const ParentNode&>(node).for_each_child([](auto& child) {
  62. dump_tree(child);
  63. });
  64. }
  65. --indent;
  66. }
  67. void dump_tree(const LayoutNode& layout_node)
  68. {
  69. static size_t indent = 0;
  70. for (size_t i = 0; i < indent; ++i)
  71. dbgprintf(" ");
  72. String tag_name;
  73. if (layout_node.is_anonymous())
  74. tag_name = "(anonymous)";
  75. else if (is<Text>(layout_node.node()))
  76. tag_name = "#text";
  77. else if (is<Document>(layout_node.node()))
  78. tag_name = "#document";
  79. else if (is<Element>(layout_node.node()))
  80. tag_name = to<Element>(*layout_node.node()).tag_name();
  81. else
  82. tag_name = "???";
  83. if (!layout_node.is_box()) {
  84. dbgprintf("%s {%s}\n", layout_node.class_name(), tag_name.characters());
  85. } else {
  86. auto& layout_box = to<LayoutBox>(layout_node);
  87. dbgprintf("%s {%s} at (%g,%g) size %gx%g",
  88. layout_box.class_name(),
  89. tag_name.characters(),
  90. layout_box.x(),
  91. layout_box.y(),
  92. layout_box.width(),
  93. layout_box.height());
  94. // Dump the horizontal box properties
  95. dbgprintf(" [%g+%g+%g %g %g+%g+%g]",
  96. layout_box.box_model().margin().left.to_px(),
  97. layout_box.box_model().border().left.to_px(),
  98. layout_box.box_model().padding().left.to_px(),
  99. layout_box.width(),
  100. layout_box.box_model().padding().right.to_px(),
  101. layout_box.box_model().border().right.to_px(),
  102. layout_box.box_model().margin().right.to_px());
  103. // And the vertical box properties
  104. dbgprintf(" [%g+%g+%g %g %g+%g+%g]",
  105. layout_box.box_model().margin().top.to_px(),
  106. layout_box.box_model().border().top.to_px(),
  107. layout_box.box_model().padding().top.to_px(),
  108. layout_box.height(),
  109. layout_box.box_model().padding().bottom.to_px(),
  110. layout_box.box_model().border().bottom.to_px(),
  111. layout_box.box_model().margin().bottom.to_px());
  112. dbgprintf("\n");
  113. }
  114. if (layout_node.is_block() && static_cast<const LayoutBlock&>(layout_node).children_are_inline()) {
  115. auto& block = static_cast<const LayoutBlock&>(layout_node);
  116. for (size_t i = 0; i < indent; ++i)
  117. dbgprintf(" ");
  118. dbgprintf(" Line boxes (%d):\n", block.line_boxes().size());
  119. for (size_t line_box_index = 0; line_box_index < block.line_boxes().size(); ++line_box_index) {
  120. auto& line_box = block.line_boxes()[line_box_index];
  121. for (size_t i = 0; i < indent; ++i)
  122. dbgprintf(" ");
  123. dbgprintf(" [%d] width: %g\n", line_box_index, line_box.width());
  124. for (size_t fragment_index = 0; fragment_index < line_box.fragments().size(); ++fragment_index) {
  125. auto& fragment = line_box.fragments()[fragment_index];
  126. for (size_t i = 0; i < indent; ++i)
  127. dbgprintf(" ");
  128. dbgprintf(" [%d] layout_node: %s{%p}, start: %d, length: %d, rect: %s\n",
  129. fragment_index,
  130. fragment.layout_node().class_name(),
  131. &fragment.layout_node(),
  132. fragment.start(),
  133. fragment.length(),
  134. fragment.rect().to_string().characters());
  135. if (fragment.layout_node().is_text()) {
  136. for (size_t i = 0; i < indent; ++i)
  137. dbgprintf(" ");
  138. auto& layout_text = static_cast<const LayoutText&>(fragment.layout_node());
  139. auto fragment_text = layout_text.text_for_rendering().substring(fragment.start(), fragment.length());
  140. dbgprintf(" text: \"%s\"\n", fragment_text.characters());
  141. }
  142. }
  143. }
  144. }
  145. layout_node.style().for_each_property([&](auto property_id, auto& value) {
  146. for (size_t i = 0; i < indent; ++i)
  147. dbgprintf(" ");
  148. dbgprintf(" (%s: %s)\n", CSS::string_from_property_id(property_id), value.to_string().characters());
  149. });
  150. ++indent;
  151. layout_node.for_each_child([](auto& child) {
  152. dump_tree(child);
  153. });
  154. --indent;
  155. }
  156. void dump_rule(const StyleRule& rule)
  157. {
  158. dbgprintf("Rule:\n");
  159. for (auto& selector : rule.selectors()) {
  160. dbgprintf(" Selector:\n");
  161. for (auto& complex_selector : selector.complex_selectors()) {
  162. dbgprintf(" ");
  163. const char* relation_description = "";
  164. switch (complex_selector.relation) {
  165. case Selector::ComplexSelector::Relation::None:
  166. break;
  167. case Selector::ComplexSelector::Relation::ImmediateChild:
  168. relation_description = "ImmediateChild";
  169. break;
  170. case Selector::ComplexSelector::Relation::Descendant:
  171. relation_description = "Descendant";
  172. break;
  173. case Selector::ComplexSelector::Relation::AdjacentSibling:
  174. relation_description = "AdjacentSibling";
  175. break;
  176. case Selector::ComplexSelector::Relation::GeneralSibling:
  177. relation_description = "GeneralSibling";
  178. break;
  179. }
  180. if (*relation_description)
  181. dbgprintf("{%s} ", relation_description);
  182. for (size_t i = 0; i < complex_selector.compound_selector.size(); ++i) {
  183. auto& simple_selector = complex_selector.compound_selector[i];
  184. const char* type_description = "Unknown";
  185. switch (simple_selector.type) {
  186. case Selector::SimpleSelector::Type::Invalid:
  187. type_description = "Invalid";
  188. break;
  189. case Selector::SimpleSelector::Type::Universal:
  190. type_description = "Universal";
  191. break;
  192. case Selector::SimpleSelector::Type::Id:
  193. type_description = "Id";
  194. break;
  195. case Selector::SimpleSelector::Type::Class:
  196. type_description = "Class";
  197. break;
  198. case Selector::SimpleSelector::Type::TagName:
  199. type_description = "TagName";
  200. break;
  201. }
  202. const char* attribute_match_type_description = "";
  203. switch (simple_selector.attribute_match_type) {
  204. case Selector::SimpleSelector::AttributeMatchType::None:
  205. break;
  206. case Selector::SimpleSelector::AttributeMatchType::HasAttribute:
  207. attribute_match_type_description = "HasAttribute";
  208. break;
  209. case Selector::SimpleSelector::AttributeMatchType::ExactValueMatch:
  210. attribute_match_type_description = "ExactValueMatch";
  211. break;
  212. }
  213. dbgprintf("%s:%s", type_description, simple_selector.value.characters());
  214. if (simple_selector.attribute_match_type != Selector::SimpleSelector::AttributeMatchType::None) {
  215. dbgprintf(" [%s, name='%s', value='%s']", attribute_match_type_description, simple_selector.attribute_name.characters(), simple_selector.attribute_value.characters());
  216. }
  217. if (i != complex_selector.compound_selector.size() - 1)
  218. dbgprintf(", ");
  219. }
  220. dbgprintf("\n");
  221. }
  222. }
  223. dbgprintf(" Declarations:\n");
  224. for (auto& property : rule.declaration().properties()) {
  225. dbgprintf(" %s: '%s'\n", CSS::string_from_property_id(property.property_id), property.value->to_string().characters());
  226. }
  227. }
  228. void dump_sheet(const StyleSheet& sheet)
  229. {
  230. dbgprintf("StyleSheet{%p}: %d rule(s)\n", &sheet, sheet.rules().size());
  231. for (auto& rule : sheet.rules()) {
  232. dump_rule(rule);
  233. }
  234. }