Paintable.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Copyright (c) 2022-2023, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/DOM/Document.h>
  7. #include <LibWeb/Layout/BlockContainer.h>
  8. #include <LibWeb/Painting/Paintable.h>
  9. #include <LibWeb/Painting/PaintableBox.h>
  10. #include <LibWeb/Painting/StackingContext.h>
  11. namespace Web::Painting {
  12. Paintable::Paintable(Layout::Node const& layout_node)
  13. : m_layout_node(layout_node)
  14. , m_browsing_context(const_cast<HTML::BrowsingContext&>(layout_node.browsing_context()))
  15. {
  16. auto& computed_values = layout_node.computed_values();
  17. if (layout_node.is_grid_item() && computed_values.z_index().has_value()) {
  18. // https://www.w3.org/TR/css-grid-2/#z-order
  19. // grid items with z_index should behave as if position were "relative"
  20. m_positioned = true;
  21. } else {
  22. m_positioned = computed_values.position() != CSS::Positioning::Static;
  23. }
  24. m_fixed_position = computed_values.position() == CSS::Positioning::Fixed;
  25. m_sticky_position = computed_values.position() == CSS::Positioning::Sticky;
  26. m_absolutely_positioned = computed_values.position() == CSS::Positioning::Absolute;
  27. m_floating = layout_node.is_floating();
  28. m_inline = layout_node.is_inline();
  29. }
  30. Paintable::~Paintable()
  31. {
  32. }
  33. void Paintable::visit_edges(Cell::Visitor& visitor)
  34. {
  35. Base::visit_edges(visitor);
  36. TreeNode::visit_edges(visitor);
  37. visitor.visit(m_dom_node);
  38. visitor.visit(m_layout_node);
  39. visitor.visit(m_browsing_context);
  40. if (m_containing_block.has_value())
  41. visitor.visit(m_containing_block.value());
  42. }
  43. bool Paintable::is_visible() const
  44. {
  45. auto const& computed_values = this->computed_values();
  46. return computed_values.visibility() == CSS::Visibility::Visible && computed_values.opacity() != 0;
  47. }
  48. void Paintable::set_dom_node(JS::GCPtr<DOM::Node> dom_node)
  49. {
  50. m_dom_node = dom_node;
  51. }
  52. JS::GCPtr<DOM::Node> Paintable::dom_node()
  53. {
  54. return m_dom_node;
  55. }
  56. JS::GCPtr<DOM::Node const> Paintable::dom_node() const
  57. {
  58. return m_dom_node;
  59. }
  60. HTML::BrowsingContext const& Paintable::browsing_context() const
  61. {
  62. return m_browsing_context;
  63. }
  64. HTML::BrowsingContext& Paintable::browsing_context()
  65. {
  66. return m_browsing_context;
  67. }
  68. JS::GCPtr<HTML::Navigable> Paintable::navigable() const
  69. {
  70. return document().navigable();
  71. }
  72. Paintable::DispatchEventOfSameName Paintable::handle_mousedown(Badge<EventHandler>, CSSPixelPoint, unsigned, unsigned)
  73. {
  74. return DispatchEventOfSameName::Yes;
  75. }
  76. Paintable::DispatchEventOfSameName Paintable::handle_mouseup(Badge<EventHandler>, CSSPixelPoint, unsigned, unsigned)
  77. {
  78. return DispatchEventOfSameName::Yes;
  79. }
  80. Paintable::DispatchEventOfSameName Paintable::handle_mousemove(Badge<EventHandler>, CSSPixelPoint, unsigned, unsigned)
  81. {
  82. return DispatchEventOfSameName::Yes;
  83. }
  84. bool Paintable::handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned, unsigned, int, int)
  85. {
  86. return false;
  87. }
  88. TraversalDecision Paintable::hit_test(CSSPixelPoint, HitTestType, Function<TraversalDecision(HitTestResult)> const&) const
  89. {
  90. return TraversalDecision::Continue;
  91. }
  92. StackingContext* Paintable::enclosing_stacking_context()
  93. {
  94. for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
  95. if (auto* stacking_context = ancestor->stacking_context())
  96. return const_cast<StackingContext*>(stacking_context);
  97. }
  98. // We should always reach the viewport's stacking context.
  99. VERIFY_NOT_REACHED();
  100. }
  101. void Paintable::set_stacking_context(NonnullOwnPtr<StackingContext> stacking_context)
  102. {
  103. m_stacking_context = move(stacking_context);
  104. }
  105. void Paintable::invalidate_stacking_context()
  106. {
  107. m_stacking_context = nullptr;
  108. }
  109. void Paintable::set_needs_display(InvalidateDisplayList should_invalidate_display_list)
  110. {
  111. auto& document = const_cast<DOM::Document&>(this->document());
  112. if (should_invalidate_display_list == InvalidateDisplayList::Yes)
  113. document.invalidate_display_list();
  114. auto* containing_block = this->containing_block();
  115. if (!containing_block)
  116. return;
  117. if (is<Painting::InlinePaintable>(*this)) {
  118. auto const& fragments = static_cast<Painting::InlinePaintable const*>(this)->fragments();
  119. for (auto const& fragment : fragments) {
  120. document.set_needs_display(fragment.absolute_rect(), InvalidateDisplayList::No);
  121. }
  122. }
  123. if (!is<Painting::PaintableWithLines>(*containing_block))
  124. return;
  125. static_cast<Painting::PaintableWithLines const&>(*containing_block).for_each_fragment([&](auto& fragment) {
  126. document.set_needs_display(fragment.absolute_rect(), InvalidateDisplayList::No);
  127. return IterationDecision::Continue;
  128. });
  129. }
  130. CSSPixelPoint Paintable::box_type_agnostic_position() const
  131. {
  132. if (is_paintable_box())
  133. return static_cast<PaintableBox const*>(this)->absolute_position();
  134. VERIFY(is_inline());
  135. if (is_inline_paintable()) {
  136. auto const& inline_paintable = static_cast<Painting::InlinePaintable const&>(*this);
  137. if (!inline_paintable.fragments().is_empty())
  138. return inline_paintable.fragments().first().absolute_rect().location();
  139. return inline_paintable.bounding_rect().location();
  140. }
  141. CSSPixelPoint position;
  142. if (auto const* block = containing_block(); block && is<Painting::PaintableWithLines>(*block)) {
  143. static_cast<Painting::PaintableWithLines const&>(*block).for_each_fragment([&](auto& fragment) {
  144. position = fragment.absolute_rect().location();
  145. return IterationDecision::Break;
  146. });
  147. }
  148. return position;
  149. }
  150. Gfx::AffineTransform Paintable::compute_combined_css_transform() const
  151. {
  152. Gfx::AffineTransform combined_transform;
  153. if (is_paintable_box()) {
  154. auto const& paintable_box = static_cast<PaintableBox const&>(*this);
  155. auto affine_transform = Gfx::extract_2d_affine_transform(paintable_box.transform());
  156. combined_transform = combined_transform.multiply(affine_transform);
  157. }
  158. for (auto const* ancestor = this->containing_block(); ancestor; ancestor = ancestor->containing_block()) {
  159. auto affine_transform = Gfx::extract_2d_affine_transform(ancestor->transform());
  160. combined_transform = combined_transform.multiply(affine_transform);
  161. }
  162. return combined_transform;
  163. }
  164. Painting::BorderRadiiData normalize_border_radii_data(Layout::Node const& node, CSSPixelRect const& rect, CSS::BorderRadiusData const& top_left_radius, CSS::BorderRadiusData const& top_right_radius, CSS::BorderRadiusData const& bottom_right_radius, CSS::BorderRadiusData const& bottom_left_radius)
  165. {
  166. Painting::BorderRadiusData bottom_left_radius_px {};
  167. Painting::BorderRadiusData bottom_right_radius_px {};
  168. Painting::BorderRadiusData top_left_radius_px {};
  169. Painting::BorderRadiusData top_right_radius_px {};
  170. bottom_left_radius_px.horizontal_radius = bottom_left_radius.horizontal_radius.to_px(node, rect.width());
  171. bottom_right_radius_px.horizontal_radius = bottom_right_radius.horizontal_radius.to_px(node, rect.width());
  172. top_left_radius_px.horizontal_radius = top_left_radius.horizontal_radius.to_px(node, rect.width());
  173. top_right_radius_px.horizontal_radius = top_right_radius.horizontal_radius.to_px(node, rect.width());
  174. bottom_left_radius_px.vertical_radius = bottom_left_radius.vertical_radius.to_px(node, rect.height());
  175. bottom_right_radius_px.vertical_radius = bottom_right_radius.vertical_radius.to_px(node, rect.height());
  176. top_left_radius_px.vertical_radius = top_left_radius.vertical_radius.to_px(node, rect.height());
  177. top_right_radius_px.vertical_radius = top_right_radius.vertical_radius.to_px(node, rect.height());
  178. // Scale overlapping curves according to https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
  179. // Let f = min(Li/Si), where i ∈ {top, right, bottom, left},
  180. // Si is the sum of the two corresponding radii of the corners on side i,
  181. // and Ltop = Lbottom = the width of the box, and Lleft = Lright = the height of the box.
  182. auto l_top = rect.width();
  183. auto l_bottom = l_top;
  184. auto l_left = rect.height();
  185. auto l_right = l_left;
  186. auto s_top = (top_left_radius_px.horizontal_radius + top_right_radius_px.horizontal_radius);
  187. auto s_right = (top_right_radius_px.vertical_radius + bottom_right_radius_px.vertical_radius);
  188. auto s_bottom = (bottom_left_radius_px.horizontal_radius + bottom_right_radius_px.horizontal_radius);
  189. auto s_left = (top_left_radius_px.vertical_radius + bottom_left_radius_px.vertical_radius);
  190. CSSPixelFraction f = 1;
  191. f = (s_top != 0) ? min(f, l_top / s_top) : f;
  192. f = (s_right != 0) ? min(f, l_right / s_right) : f;
  193. f = (s_bottom != 0) ? min(f, l_bottom / s_bottom) : f;
  194. f = (s_left != 0) ? min(f, l_left / s_left) : f;
  195. // If f < 1, then all corner radii are reduced by multiplying them by f.
  196. if (f < 1) {
  197. top_left_radius_px.horizontal_radius *= f;
  198. top_left_radius_px.vertical_radius *= f;
  199. top_right_radius_px.horizontal_radius *= f;
  200. top_right_radius_px.vertical_radius *= f;
  201. bottom_right_radius_px.horizontal_radius *= f;
  202. bottom_right_radius_px.vertical_radius *= f;
  203. bottom_left_radius_px.horizontal_radius *= f;
  204. bottom_left_radius_px.vertical_radius *= f;
  205. }
  206. return Painting::BorderRadiiData { top_left_radius_px, top_right_radius_px, bottom_right_radius_px, bottom_left_radius_px };
  207. }
  208. }