Viewport.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /*
  2. * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/DOM/Range.h>
  7. #include <LibWeb/Dump.h>
  8. #include <LibWeb/Layout/Viewport.h>
  9. #include <LibWeb/Painting/PaintableBox.h>
  10. #include <LibWeb/Painting/StackingContext.h>
  11. namespace Web::Layout {
  12. Viewport::Viewport(DOM::Document& document, NonnullRefPtr<CSS::StyleProperties> style)
  13. : BlockContainer(document, &document, move(style))
  14. {
  15. }
  16. Viewport::~Viewport() = default;
  17. JS::GCPtr<Selection::Selection> Viewport::selection() const
  18. {
  19. return const_cast<DOM::Document&>(document()).get_selection();
  20. }
  21. void Viewport::build_stacking_context_tree_if_needed()
  22. {
  23. if (paintable_box()->stacking_context())
  24. return;
  25. build_stacking_context_tree();
  26. }
  27. void Viewport::build_stacking_context_tree()
  28. {
  29. const_cast<Painting::PaintableBox*>(paintable_box())->set_stacking_context(make<Painting::StackingContext>(*this, nullptr));
  30. for_each_in_subtree_of_type<Box>([&](Box& box) {
  31. if (!box.paintable_box())
  32. return IterationDecision::Continue;
  33. const_cast<Painting::PaintableBox*>(box.paintable_box())->invalidate_stacking_context();
  34. if (!box.establishes_stacking_context()) {
  35. VERIFY(!box.paintable_box()->stacking_context());
  36. return IterationDecision::Continue;
  37. }
  38. auto* parent_context = const_cast<Painting::PaintableBox*>(box.paintable_box())->enclosing_stacking_context();
  39. VERIFY(parent_context);
  40. const_cast<Painting::PaintableBox*>(box.paintable_box())->set_stacking_context(make<Painting::StackingContext>(box, parent_context));
  41. return IterationDecision::Continue;
  42. });
  43. const_cast<Painting::PaintableBox*>(paintable_box())->stacking_context()->sort();
  44. }
  45. void Viewport::paint_all_phases(PaintContext& context)
  46. {
  47. build_stacking_context_tree_if_needed();
  48. context.painter().translate(-context.device_viewport_rect().location().to_type<int>());
  49. paintable_box()->stacking_context()->paint(context);
  50. }
  51. void Viewport::recompute_selection_states()
  52. {
  53. // 1. Start by resetting the selection state of all layout nodes to None.
  54. for_each_in_inclusive_subtree([&](auto& layout_node) {
  55. layout_node.set_selection_state(SelectionState::None);
  56. return IterationDecision::Continue;
  57. });
  58. // 2. If there is no active Selection or selected Range, return.
  59. auto selection = document().get_selection();
  60. if (!selection)
  61. return;
  62. auto range = selection->range();
  63. if (!range)
  64. return;
  65. auto* start_container = range->start_container();
  66. auto* end_container = range->end_container();
  67. // 3. If the selection starts and ends in the same node:
  68. if (start_container == end_container) {
  69. // 1. If the selection starts and ends at the same offset, return.
  70. if (range->start_offset() == range->end_offset()) {
  71. // NOTE: A zero-length selection should not be visible.
  72. return;
  73. }
  74. // 2. If it's a text node, mark it as StartAndEnd and return.
  75. if (is<DOM::Text>(*start_container)) {
  76. if (auto* layout_node = start_container->layout_node()) {
  77. layout_node->set_selection_state(SelectionState::StartAndEnd);
  78. }
  79. return;
  80. }
  81. }
  82. if (start_container == end_container && is<DOM::Text>(*start_container)) {
  83. if (auto* layout_node = start_container->layout_node()) {
  84. layout_node->set_selection_state(SelectionState::StartAndEnd);
  85. }
  86. return;
  87. }
  88. // 4. Mark the selection start node as Start (if text) or Full (if anything else).
  89. if (auto* layout_node = start_container->layout_node()) {
  90. if (is<DOM::Text>(*start_container))
  91. layout_node->set_selection_state(SelectionState::Start);
  92. else
  93. layout_node->set_selection_state(SelectionState::Full);
  94. }
  95. // 5. Mark the selection end node as End (if text) or Full (if anything else).
  96. if (auto* layout_node = end_container->layout_node()) {
  97. if (is<DOM::Text>(*end_container))
  98. layout_node->set_selection_state(SelectionState::End);
  99. else
  100. layout_node->set_selection_state(SelectionState::Full);
  101. }
  102. // 6. Mark the nodes between start node and end node (in tree order) as Full.
  103. for (auto* node = start_container->next_in_pre_order(); node && node != end_container; node = node->next_in_pre_order()) {
  104. if (auto* layout_node = node->layout_node())
  105. layout_node->set_selection_state(SelectionState::Full);
  106. }
  107. }
  108. }