InlineLevelIterator.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /*
  2. * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/Layout/BreakNode.h>
  7. #include <LibWeb/Layout/InlineLevelIterator.h>
  8. #include <LibWeb/Layout/InlineNode.h>
  9. #include <LibWeb/Layout/ListItemMarkerBox.h>
  10. #include <LibWeb/Layout/ReplacedBox.h>
  11. namespace Web::Layout {
  12. // This is similar to Layout::Node::next_in_pre_order() but will not descend into inline-block nodes.
  13. static Layout::Node* next_inline_node_in_pre_order(Layout::Node& current, Layout::Node const* stay_within)
  14. {
  15. if (current.first_child() && current.first_child()->is_inline() && !current.is_inline_block())
  16. return current.first_child();
  17. Layout::Node* node = &current;
  18. Layout::Node* next = nullptr;
  19. while (!(next = node->next_sibling())) {
  20. node = node->parent();
  21. if (!node || node == stay_within)
  22. return nullptr;
  23. }
  24. return next;
  25. }
  26. void InlineLevelIterator::skip_to_next()
  27. {
  28. VERIFY(m_current_node);
  29. do {
  30. m_current_node = next_inline_node_in_pre_order(*m_current_node, &m_container);
  31. } while (m_current_node && !m_current_node->is_inline());
  32. }
  33. Optional<InlineLevelIterator::Item> InlineLevelIterator::next(float available_width)
  34. {
  35. if (!m_current_node)
  36. return {};
  37. if (is<Layout::TextNode>(*m_current_node)) {
  38. auto& text_node = static_cast<Layout::TextNode&>(*m_current_node);
  39. if (!m_text_node_context.has_value()) {
  40. bool previous_is_empty_or_ends_in_whitespace = m_container.line_boxes().is_empty() || m_container.line_boxes().last().is_empty_or_ends_in_whitespace();
  41. enter_text_node(text_node, previous_is_empty_or_ends_in_whitespace);
  42. }
  43. auto chunk_opt = m_text_node_context->chunk_iterator.next();
  44. if (!chunk_opt.has_value()) {
  45. m_text_node_context = {};
  46. skip_to_next();
  47. return next(available_width);
  48. }
  49. auto& chunk = chunk_opt.value();
  50. float chunk_width = text_node.font().width(chunk.view) + text_node.font().glyph_spacing();
  51. Item item {
  52. .type = Item::Type::Text,
  53. .node = &text_node,
  54. .offset_in_node = chunk.start,
  55. .length_in_node = chunk.length,
  56. .width = chunk_width,
  57. .should_force_break = m_text_node_context->do_respect_linebreaks && chunk.has_breaking_newline,
  58. .is_collapsible_whitespace = m_text_node_context->do_collapse && chunk.is_all_whitespace,
  59. };
  60. return item;
  61. }
  62. if (is<Layout::BreakNode>(*m_current_node)) {
  63. skip_to_next();
  64. return Item {
  65. .type = Item::Type::ForcedBreak,
  66. };
  67. }
  68. if (is<Layout::ListItemMarkerBox>(*m_current_node)) {
  69. skip_to_next();
  70. return next(available_width);
  71. }
  72. if (!is<Layout::Box>(*m_current_node)) {
  73. skip_to_next();
  74. return next(available_width);
  75. }
  76. if (is<Layout::ReplacedBox>(*m_current_node)) {
  77. auto& replaced_box = static_cast<Layout::ReplacedBox&>(*m_current_node);
  78. replaced_box.prepare_for_replaced_layout();
  79. }
  80. auto& box = verify_cast<Layout::Box>(*m_current_node);
  81. skip_to_next();
  82. return Item {
  83. .type = Item::Type::Element,
  84. .node = &box,
  85. .offset_in_node = 0,
  86. .length_in_node = 0,
  87. .width = box.width(),
  88. };
  89. }
  90. void InlineLevelIterator::enter_text_node(Layout::TextNode& text_node, bool previous_is_empty_or_ends_in_whitespace)
  91. {
  92. bool do_collapse = true;
  93. bool do_wrap_lines = true;
  94. bool do_respect_linebreaks = false;
  95. if (text_node.computed_values().white_space() == CSS::WhiteSpace::Nowrap) {
  96. do_collapse = true;
  97. do_wrap_lines = false;
  98. do_respect_linebreaks = false;
  99. } else if (text_node.computed_values().white_space() == CSS::WhiteSpace::Pre) {
  100. do_collapse = false;
  101. do_wrap_lines = false;
  102. do_respect_linebreaks = true;
  103. } else if (text_node.computed_values().white_space() == CSS::WhiteSpace::PreLine) {
  104. do_collapse = true;
  105. do_wrap_lines = true;
  106. do_respect_linebreaks = true;
  107. } else if (text_node.computed_values().white_space() == CSS::WhiteSpace::PreWrap) {
  108. do_collapse = false;
  109. do_wrap_lines = true;
  110. do_respect_linebreaks = true;
  111. }
  112. text_node.compute_text_for_rendering(do_collapse, previous_is_empty_or_ends_in_whitespace);
  113. m_text_node_context = TextNodeContext {
  114. .do_collapse = do_collapse,
  115. .do_wrap_lines = do_wrap_lines,
  116. .do_respect_linebreaks = do_respect_linebreaks,
  117. .chunk_iterator = TextNode::ChunkIterator { text_node.text_for_rendering(), m_layout_mode, do_wrap_lines, do_respect_linebreaks },
  118. };
  119. }
  120. }