Paintable.h 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. * Copyright (c) 2022-2023, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/NonnullOwnPtr.h>
  8. #include <LibWeb/Layout/Box.h>
  9. #include <LibWeb/Layout/LineBox.h>
  10. #include <LibWeb/Layout/TextNode.h>
  11. namespace Web::Painting {
  12. enum class TraversalDecision {
  13. Continue,
  14. SkipChildrenAndContinue,
  15. Break,
  16. };
  17. enum class PaintPhase {
  18. Background,
  19. Border,
  20. Foreground,
  21. Outline,
  22. Overlay,
  23. };
  24. struct HitTestResult {
  25. JS::Handle<Paintable> paintable;
  26. int index_in_node { 0 };
  27. enum InternalPosition {
  28. None,
  29. Before,
  30. Inside,
  31. After,
  32. };
  33. InternalPosition internal_position { None };
  34. DOM::Node* dom_node();
  35. DOM::Node const* dom_node() const;
  36. };
  37. enum class HitTestType {
  38. Exact, // Exact matches only
  39. TextCursor, // Clicking past the right/bottom edge of text will still hit the text
  40. };
  41. class Paintable
  42. : public JS::Cell
  43. , public TreeNode<Paintable> {
  44. JS_CELL(Paintable, Cell);
  45. public:
  46. virtual ~Paintable();
  47. [[nodiscard]] bool is_visible() const;
  48. [[nodiscard]] bool is_positioned() const { return m_positioned; }
  49. [[nodiscard]] bool is_fixed_position() const { return m_fixed_position; }
  50. [[nodiscard]] bool is_absolutely_positioned() const { return m_absolutely_positioned; }
  51. [[nodiscard]] bool is_floating() const { return m_floating; }
  52. [[nodiscard]] bool is_inline() const { return m_inline; }
  53. [[nodiscard]] CSS::Display display() const { return layout_node().display(); }
  54. template<typename U, typename Callback>
  55. TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
  56. {
  57. if (is<U>(*this)) {
  58. if (auto decision = callback(static_cast<U const&>(*this)); decision != TraversalDecision::Continue)
  59. return decision;
  60. }
  61. for (auto* child = first_child(); child; child = child->next_sibling()) {
  62. if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
  63. return TraversalDecision::Break;
  64. }
  65. return TraversalDecision::Continue;
  66. }
  67. template<typename U, typename Callback>
  68. TraversalDecision for_each_in_subtree_of_type(Callback callback) const
  69. {
  70. for (auto* child = first_child(); child; child = child->next_sibling()) {
  71. if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
  72. return TraversalDecision::Break;
  73. }
  74. return TraversalDecision::Continue;
  75. }
  76. template<typename Callback>
  77. TraversalDecision for_each_in_inclusive_subtree(Callback callback)
  78. {
  79. if (auto decision = callback(*this); decision != TraversalDecision::Continue)
  80. return decision;
  81. for (auto* child = first_child(); child; child = child->next_sibling()) {
  82. if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
  83. return TraversalDecision::Break;
  84. }
  85. return TraversalDecision::Continue;
  86. }
  87. template<typename Callback>
  88. TraversalDecision for_each_in_inclusive_subtree(Callback callback) const
  89. {
  90. if (auto decision = callback(*this); decision != TraversalDecision::Continue)
  91. return decision;
  92. for (auto* child = first_child(); child; child = child->next_sibling()) {
  93. if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
  94. return TraversalDecision::Break;
  95. }
  96. return TraversalDecision::Continue;
  97. }
  98. template<typename Callback>
  99. TraversalDecision for_each_in_subtree(Callback callback) const
  100. {
  101. for (auto* child = first_child(); child; child = child->next_sibling()) {
  102. if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
  103. return TraversalDecision::Break;
  104. }
  105. return TraversalDecision::Continue;
  106. }
  107. StackingContext* stacking_context() { return m_stacking_context; }
  108. StackingContext const* stacking_context() const { return m_stacking_context; }
  109. void set_stacking_context(NonnullOwnPtr<StackingContext>);
  110. StackingContext* enclosing_stacking_context();
  111. void invalidate_stacking_context();
  112. virtual void before_paint(PaintContext&, PaintPhase) const { }
  113. virtual void after_paint(PaintContext&, PaintPhase) const { }
  114. virtual void paint(PaintContext&, PaintPhase) const { }
  115. virtual void before_children_paint(PaintContext&, PaintPhase) const { }
  116. virtual void after_children_paint(PaintContext&, PaintPhase) const { }
  117. virtual void apply_scroll_offset(PaintContext&, PaintPhase) const { }
  118. virtual void reset_scroll_offset(PaintContext&, PaintPhase) const { }
  119. virtual void apply_clip_overflow_rect(PaintContext&, PaintPhase) const { }
  120. virtual void clear_clip_overflow_rect(PaintContext&, PaintPhase) const { }
  121. [[nodiscard]] virtual TraversalDecision hit_test(CSSPixelPoint, HitTestType, Function<TraversalDecision(HitTestResult)> const& callback) const;
  122. virtual bool wants_mouse_events() const { return false; }
  123. virtual bool forms_unconnected_subtree() const { return false; }
  124. enum class DispatchEventOfSameName {
  125. Yes,
  126. No,
  127. };
  128. // When these methods return true, the DOM event with the same name will be
  129. // dispatch at the mouse_event_target if it returns a valid DOM::Node, or
  130. // the layout node's associated DOM node if it doesn't.
  131. virtual DispatchEventOfSameName handle_mousedown(Badge<EventHandler>, CSSPixelPoint, unsigned button, unsigned modifiers);
  132. virtual DispatchEventOfSameName handle_mouseup(Badge<EventHandler>, CSSPixelPoint, unsigned button, unsigned modifiers);
  133. virtual DispatchEventOfSameName handle_mousemove(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers);
  134. virtual DOM::Node* mouse_event_target() const { return nullptr; }
  135. virtual bool handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y);
  136. Layout::Node const& layout_node() const { return m_layout_node; }
  137. Layout::Node& layout_node() { return const_cast<Layout::Node&>(*m_layout_node); }
  138. [[nodiscard]] JS::GCPtr<DOM::Node> dom_node();
  139. [[nodiscard]] JS::GCPtr<DOM::Node const> dom_node() const;
  140. void set_dom_node(JS::GCPtr<DOM::Node>);
  141. auto const& computed_values() const { return m_layout_node->computed_values(); }
  142. bool visible_for_hit_testing() const { return computed_values().pointer_events() != CSS::PointerEvents::None; }
  143. [[nodiscard]] HTML::BrowsingContext const& browsing_context() const;
  144. [[nodiscard]] HTML::BrowsingContext& browsing_context();
  145. JS::GCPtr<HTML::Navigable> navigable() const;
  146. virtual void set_needs_display() const;
  147. PaintableBox* containing_block() const
  148. {
  149. if (!m_containing_block.has_value()) {
  150. auto containing_layout_box = m_layout_node->containing_block();
  151. if (containing_layout_box)
  152. m_containing_block = const_cast<PaintableBox*>(containing_layout_box->paintable_box());
  153. else
  154. m_containing_block = nullptr;
  155. }
  156. return *m_containing_block;
  157. }
  158. template<typename T>
  159. bool fast_is() const = delete;
  160. [[nodiscard]] virtual bool is_paintable_box() const { return false; }
  161. [[nodiscard]] virtual bool is_paintable_with_lines() const { return false; }
  162. [[nodiscard]] virtual bool is_inline_paintable() const { return false; }
  163. [[nodiscard]] virtual bool is_svg_paintable() const { return false; }
  164. [[nodiscard]] virtual bool is_text_paintable() const { return false; }
  165. DOM::Document const& document() const { return layout_node().document(); }
  166. DOM::Document& document() { return layout_node().document(); }
  167. CSSPixelPoint box_type_agnostic_position() const;
  168. enum class SelectionState : u8 {
  169. None, // No selection
  170. Start, // Selection starts in this Node
  171. End, // Selection ends in this Node
  172. StartAndEnd, // Selection starts and ends in this Node
  173. Full, // Selection starts before and ends after this Node
  174. };
  175. SelectionState selection_state() const { return m_selection_state; }
  176. void set_selection_state(SelectionState state) { m_selection_state = state; }
  177. protected:
  178. explicit Paintable(Layout::Node const&);
  179. virtual void visit_edges(Cell::Visitor&) override;
  180. private:
  181. JS::GCPtr<DOM::Node> m_dom_node;
  182. JS::NonnullGCPtr<Layout::Node const> m_layout_node;
  183. JS::NonnullGCPtr<HTML::BrowsingContext> m_browsing_context;
  184. Optional<JS::GCPtr<PaintableBox>> mutable m_containing_block;
  185. OwnPtr<StackingContext> m_stacking_context;
  186. SelectionState m_selection_state { SelectionState::None };
  187. bool m_positioned : 1 { false };
  188. bool m_fixed_position : 1 { false };
  189. bool m_absolutely_positioned : 1 { false };
  190. bool m_floating : 1 { false };
  191. bool m_inline : 1 { false };
  192. };
  193. inline DOM::Node* HitTestResult::dom_node()
  194. {
  195. return paintable->dom_node();
  196. }
  197. inline DOM::Node const* HitTestResult::dom_node() const
  198. {
  199. return paintable->dom_node();
  200. }
  201. template<>
  202. inline bool Paintable::fast_is<PaintableBox>() const { return is_paintable_box(); }
  203. template<>
  204. inline bool Paintable::fast_is<PaintableWithLines>() const { return is_paintable_with_lines(); }
  205. template<>
  206. inline bool Paintable::fast_is<TextPaintable>() const { return is_text_paintable(); }
  207. }