TreeBuilder.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * Copyright (c) 2018-2021, 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 <LibWeb/DOM/Document.h>
  27. #include <LibWeb/DOM/Element.h>
  28. #include <LibWeb/DOM/ParentNode.h>
  29. #include <LibWeb/DOM/ShadowRoot.h>
  30. #include <LibWeb/Dump.h>
  31. #include <LibWeb/Layout/InitialContainingBlockBox.h>
  32. #include <LibWeb/Layout/Node.h>
  33. #include <LibWeb/Layout/TableBox.h>
  34. #include <LibWeb/Layout/TableCellBox.h>
  35. #include <LibWeb/Layout/TableRowBox.h>
  36. #include <LibWeb/Layout/TextNode.h>
  37. #include <LibWeb/Layout/TreeBuilder.h>
  38. namespace Web::Layout {
  39. TreeBuilder::TreeBuilder()
  40. {
  41. }
  42. // The insertion_parent_for_*() functions maintain the invariant that block-level boxes must have either
  43. // only block-level children or only inline-level children.
  44. static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& layout_parent)
  45. {
  46. if (layout_parent.is_inline() && !layout_parent.is_inline_block())
  47. return layout_parent;
  48. if (!layout_parent.has_children() || layout_parent.children_are_inline())
  49. return layout_parent;
  50. // Parent has block-level children, insert into an anonymous wrapper block (and create it first if needed)
  51. if (!layout_parent.last_child()->is_anonymous() || !layout_parent.last_child()->children_are_inline()) {
  52. layout_parent.append_child(layout_parent.create_anonymous_wrapper());
  53. }
  54. return *layout_parent.last_child();
  55. }
  56. static Layout::Node& insertion_parent_for_block_node(Layout::Node& layout_parent, Layout::Node& layout_node)
  57. {
  58. if (!layout_parent.has_children()) {
  59. // Parent block has no children, insert this block into parent.
  60. return layout_parent;
  61. }
  62. if (!layout_parent.children_are_inline()) {
  63. // Parent block has block-level children, insert this block into parent.
  64. return layout_parent;
  65. }
  66. // Parent block has inline-level children (our siblings).
  67. // First move these siblings into an anonymous wrapper block.
  68. NonnullRefPtrVector<Layout::Node> children;
  69. while (RefPtr<Layout::Node> child = layout_parent.first_child()) {
  70. layout_parent.remove_child(*child);
  71. children.append(child.release_nonnull());
  72. }
  73. layout_parent.append_child(adopt(*new BlockBox(layout_node.document(), nullptr, layout_parent.computed_values().clone_inherited_values())));
  74. layout_parent.set_children_are_inline(false);
  75. for (auto& child : children) {
  76. layout_parent.last_child()->append_child(child);
  77. }
  78. layout_parent.last_child()->set_children_are_inline(true);
  79. // Then it's safe to insert this block into parent.
  80. return layout_parent;
  81. }
  82. void TreeBuilder::create_layout_tree(DOM::Node& dom_node)
  83. {
  84. // If the parent doesn't have a layout node, we don't need one either.
  85. if (dom_node.parent_or_shadow_host() && !dom_node.parent_or_shadow_host()->layout_node())
  86. return;
  87. auto layout_node = dom_node.create_layout_node();
  88. if (!layout_node)
  89. return;
  90. if (!dom_node.parent_or_shadow_host()) {
  91. m_layout_root = layout_node;
  92. } else {
  93. if (layout_node->is_inline()) {
  94. // Inlines can be inserted into the nearest ancestor.
  95. auto& insertion_point = insertion_parent_for_inline_node(*m_parent_stack.last());
  96. insertion_point.append_child(*layout_node);
  97. insertion_point.set_children_are_inline(true);
  98. } else {
  99. // Non-inlines can't be inserted into an inline parent, so find the nearest non-inline ancestor.
  100. auto& nearest_non_inline_ancestor = [&]() -> Layout::Node& {
  101. for (ssize_t i = m_parent_stack.size() - 1; i >= 0; --i) {
  102. if (!m_parent_stack[i]->is_inline() || m_parent_stack[i]->is_inline_block())
  103. return *m_parent_stack[i];
  104. }
  105. VERIFY_NOT_REACHED();
  106. }();
  107. auto& insertion_point = insertion_parent_for_block_node(nearest_non_inline_ancestor, *layout_node);
  108. insertion_point.append_child(*layout_node);
  109. insertion_point.set_children_are_inline(false);
  110. }
  111. }
  112. auto* shadow_root = is<DOM::Element>(dom_node) ? downcast<DOM::Element>(dom_node).shadow_root() : nullptr;
  113. if ((dom_node.has_children() || shadow_root) && layout_node->can_have_children()) {
  114. push_parent(downcast<NodeWithStyle>(*layout_node));
  115. if (shadow_root)
  116. create_layout_tree(*shadow_root);
  117. downcast<DOM::ParentNode>(dom_node).for_each_child([&](auto& dom_child) {
  118. create_layout_tree(dom_child);
  119. });
  120. pop_parent();
  121. }
  122. }
  123. RefPtr<Node> TreeBuilder::build(DOM::Node& dom_node)
  124. {
  125. if (dom_node.parent()) {
  126. // We're building a partial layout tree, so start by building up the stack of parent layout nodes.
  127. for (auto* ancestor = dom_node.parent()->layout_node(); ancestor; ancestor = ancestor->parent())
  128. m_parent_stack.prepend(downcast<NodeWithStyle>(ancestor));
  129. }
  130. create_layout_tree(dom_node);
  131. if (auto* root = dom_node.document().layout_node())
  132. fixup_tables(*root);
  133. return move(m_layout_root);
  134. }
  135. template<CSS::Display display, typename Callback>
  136. void TreeBuilder::for_each_in_tree_with_display(NodeWithStyle& root, Callback callback)
  137. {
  138. root.for_each_in_inclusive_subtree_of_type<Box>([&](auto& box) {
  139. if (box.computed_values().display() == display)
  140. callback(box);
  141. return IterationDecision::Continue;
  142. });
  143. }
  144. void TreeBuilder::fixup_tables(NodeWithStyle& root)
  145. {
  146. // NOTE: Even if we only do a partial build, we always do fixup from the root.
  147. remove_irrelevant_boxes(root);
  148. generate_missing_child_wrappers(root);
  149. generate_missing_parents(root);
  150. }
  151. void TreeBuilder::remove_irrelevant_boxes(NodeWithStyle& root)
  152. {
  153. // The following boxes are discarded as if they were display:none:
  154. NonnullRefPtrVector<Box> to_remove;
  155. // Children of a table-column.
  156. for_each_in_tree_with_display<CSS::Display::TableColumn>(root, [&](Box& table_column) {
  157. table_column.for_each_child([&](auto& child) {
  158. to_remove.append(child);
  159. });
  160. });
  161. // Children of a table-column-group which are not a table-column.
  162. for_each_in_tree_with_display<CSS::Display::TableColumnGroup>(root, [&](Box& table_column_group) {
  163. table_column_group.for_each_child([&](auto& child) {
  164. if (child.computed_values().display() != CSS::Display::TableColumn)
  165. to_remove.append(child);
  166. });
  167. });
  168. // FIXME:
  169. // Anonymous inline boxes which contain only white space and are between two immediate siblings each of which is a table-non-root box.
  170. // Anonymous inline boxes which meet all of the following criteria:
  171. // - they contain only white space
  172. // - they are the first and/or last child of a tabular container
  173. // - whose immediate sibling, if any, is a table-non-root box
  174. for (auto& box : to_remove)
  175. box.parent()->remove_child(box);
  176. }
  177. static bool is_table_track(CSS::Display display)
  178. {
  179. return display == CSS::Display::TableRow || display == CSS::Display::TableColumn;
  180. }
  181. static bool is_table_track_group(CSS::Display display)
  182. {
  183. return display == CSS::Display::TableRowGroup || display == CSS::Display::TableColumnGroup;
  184. }
  185. static bool is_not_proper_table_child(const Node& node)
  186. {
  187. if (!node.has_style())
  188. return true;
  189. auto display = node.computed_values().display();
  190. return !is_table_track_group(display) && !is_table_track(display) && display != CSS::Display::TableCaption;
  191. }
  192. static bool is_not_table_row(const Node& node)
  193. {
  194. if (!node.has_style())
  195. return true;
  196. auto display = node.computed_values().display();
  197. return display != CSS::Display::TableRow;
  198. }
  199. static bool is_not_table_cell(const Node& node)
  200. {
  201. if (!node.has_style())
  202. return true;
  203. auto display = node.computed_values().display();
  204. return display != CSS::Display::TableCell;
  205. }
  206. template<typename Matcher, typename Callback>
  207. static void for_each_sequence_of_consecutive_children_matching(NodeWithStyle& parent, Matcher matcher, Callback callback)
  208. {
  209. NonnullRefPtrVector<Node> sequence;
  210. Node* next_sibling = nullptr;
  211. for (auto* child = parent.first_child(); child; child = next_sibling) {
  212. next_sibling = child->next_sibling();
  213. if (matcher(*child)) {
  214. sequence.append(*child);
  215. } else {
  216. if (!sequence.is_empty()) {
  217. callback(sequence, next_sibling);
  218. sequence.clear();
  219. }
  220. }
  221. }
  222. if (!sequence.is_empty())
  223. callback(sequence, nullptr);
  224. }
  225. template<typename WrapperBoxType>
  226. static void wrap_in_anonymous(NonnullRefPtrVector<Node>& sequence, Node* nearest_sibling)
  227. {
  228. VERIFY(!sequence.is_empty());
  229. auto& parent = *sequence.first().parent();
  230. auto computed_values = parent.computed_values().clone_inherited_values();
  231. static_cast<CSS::MutableComputedValues&>(computed_values).set_display(WrapperBoxType::static_display());
  232. auto wrapper = adopt(*new WrapperBoxType(parent.document(), nullptr, move(computed_values)));
  233. for (auto& child : sequence) {
  234. parent.remove_child(child);
  235. wrapper->append_child(child);
  236. }
  237. if (nearest_sibling)
  238. parent.insert_before(move(wrapper), *nearest_sibling);
  239. else
  240. parent.append_child(move(wrapper));
  241. }
  242. void TreeBuilder::generate_missing_child_wrappers(NodeWithStyle& root)
  243. {
  244. // An anonymous table-row box must be generated around each sequence of consecutive children of a table-root box which are not proper table child boxes.
  245. for_each_in_tree_with_display<CSS::Display::Table>(root, [&](auto& parent) {
  246. for_each_sequence_of_consecutive_children_matching(parent, is_not_proper_table_child, [&](auto sequence, auto nearest_sibling) {
  247. wrap_in_anonymous<TableRowBox>(sequence, nearest_sibling);
  248. });
  249. });
  250. // An anonymous table-row box must be generated around each sequence of consecutive children of a table-row-group box which are not table-row boxes.
  251. for_each_in_tree_with_display<CSS::Display::TableRowGroup>(root, [&](auto& parent) {
  252. for_each_sequence_of_consecutive_children_matching(parent, is_not_table_row, [&](auto& sequence, auto nearest_sibling) {
  253. wrap_in_anonymous<TableRowBox>(sequence, nearest_sibling);
  254. });
  255. });
  256. // An anonymous table-cell box must be generated around each sequence of consecutive children of a table-row box which are not table-cell boxes. !Testcase
  257. for_each_in_tree_with_display<CSS::Display::TableRow>(root, [&](auto& parent) {
  258. for_each_sequence_of_consecutive_children_matching(parent, is_not_table_cell, [&](auto& sequence, auto nearest_sibling) {
  259. wrap_in_anonymous<TableCellBox>(sequence, nearest_sibling);
  260. });
  261. });
  262. }
  263. void TreeBuilder::generate_missing_parents(NodeWithStyle&)
  264. {
  265. // FIXME: Implement.
  266. }
  267. }