mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-24 02:03:06 -05:00
811b8a25c2
This was *probably* already safe, but there's no harm in making sure the cached pointer gets marked during GC.
160 lines
5.1 KiB
C++
160 lines
5.1 KiB
C++
/*
|
|
* Copyright (c) 2022-2023, Andreas Kling <kling@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/NonnullOwnPtr.h>
|
|
#include <LibWeb/Layout/Box.h>
|
|
#include <LibWeb/Layout/LineBox.h>
|
|
#include <LibWeb/Layout/TextNode.h>
|
|
|
|
namespace Web::Painting {
|
|
|
|
enum class TraversalDecision {
|
|
Continue,
|
|
SkipChildrenAndContinue,
|
|
Break,
|
|
};
|
|
|
|
enum class PaintPhase {
|
|
Background,
|
|
Border,
|
|
Foreground,
|
|
FocusOutline,
|
|
Overlay,
|
|
};
|
|
|
|
struct HitTestResult {
|
|
JS::Handle<Painting::Paintable> paintable;
|
|
int index_in_node { 0 };
|
|
|
|
enum InternalPosition {
|
|
None,
|
|
Before,
|
|
Inside,
|
|
After,
|
|
};
|
|
InternalPosition internal_position { None };
|
|
|
|
DOM::Node* dom_node();
|
|
DOM::Node const* dom_node() const;
|
|
};
|
|
|
|
enum class HitTestType {
|
|
Exact, // Exact matches only
|
|
TextCursor, // Clicking past the right/bottom edge of text will still hit the text
|
|
};
|
|
|
|
class Paintable : public JS::Cell {
|
|
JS_CELL(Paintable, Cell);
|
|
|
|
public:
|
|
virtual ~Paintable() = default;
|
|
|
|
Paintable const* first_child() const;
|
|
Paintable const* last_child() const;
|
|
Paintable const* next_sibling() const;
|
|
Paintable const* previous_sibling() const;
|
|
|
|
template<typename U, typename Callback>
|
|
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
|
|
{
|
|
if (is<U>(*this)) {
|
|
if (auto decision = callback(static_cast<U const&>(*this)); decision != TraversalDecision::Continue)
|
|
return decision;
|
|
}
|
|
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
|
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
|
|
return TraversalDecision::Break;
|
|
}
|
|
return TraversalDecision::Continue;
|
|
}
|
|
|
|
template<typename U, typename Callback>
|
|
TraversalDecision for_each_in_subtree_of_type(Callback callback) const
|
|
{
|
|
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
|
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
|
|
return TraversalDecision::Break;
|
|
}
|
|
return TraversalDecision::Continue;
|
|
}
|
|
|
|
virtual void paint(PaintContext&, PaintPhase) const { }
|
|
|
|
virtual void before_children_paint(PaintContext&, PaintPhase) const { }
|
|
virtual void after_children_paint(PaintContext&, PaintPhase) const { }
|
|
|
|
virtual Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const;
|
|
|
|
virtual bool wants_mouse_events() const { return false; }
|
|
|
|
enum class DispatchEventOfSameName {
|
|
Yes,
|
|
No,
|
|
};
|
|
// When these methods return true, the DOM event with the same name will be
|
|
// dispatch at the mouse_event_target if it returns a valid DOM::Node, or
|
|
// the layout node's associated DOM node if it doesn't.
|
|
virtual DispatchEventOfSameName handle_mousedown(Badge<EventHandler>, CSSPixelPoint, unsigned button, unsigned modifiers);
|
|
virtual DispatchEventOfSameName handle_mouseup(Badge<EventHandler>, CSSPixelPoint, unsigned button, unsigned modifiers);
|
|
virtual DispatchEventOfSameName handle_mousemove(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers);
|
|
virtual DOM::Node* mouse_event_target() const { return nullptr; }
|
|
|
|
virtual bool handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y);
|
|
|
|
Layout::Node const& layout_node() const { return m_layout_node; }
|
|
Layout::Node& layout_node() { return const_cast<Layout::Node&>(*m_layout_node); }
|
|
|
|
DOM::Node* dom_node() { return layout_node().dom_node(); }
|
|
DOM::Node const* dom_node() const { return layout_node().dom_node(); }
|
|
|
|
auto const& computed_values() const { return m_layout_node->computed_values(); }
|
|
|
|
bool visible_for_hit_testing() const { return computed_values().pointer_events() != CSS::PointerEvents::None; }
|
|
|
|
HTML::BrowsingContext const& browsing_context() const { return m_layout_node->browsing_context(); }
|
|
HTML::BrowsingContext& browsing_context() { return layout_node().browsing_context(); }
|
|
|
|
void set_needs_display() const { const_cast<Layout::Node&>(*m_layout_node).set_needs_display(); }
|
|
|
|
Layout::BlockContainer const* containing_block() const
|
|
{
|
|
if (!m_containing_block.has_value())
|
|
m_containing_block = m_layout_node->containing_block();
|
|
return *m_containing_block;
|
|
}
|
|
|
|
template<typename T>
|
|
bool fast_is() const = delete;
|
|
|
|
protected:
|
|
explicit Paintable(Layout::Node const& layout_node)
|
|
: m_layout_node(layout_node)
|
|
{
|
|
}
|
|
|
|
virtual void visit_edges(Cell::Visitor&) override;
|
|
|
|
private:
|
|
JS::NonnullGCPtr<Layout::Node> m_layout_node;
|
|
Optional<JS::GCPtr<Layout::BlockContainer>> mutable m_containing_block;
|
|
};
|
|
|
|
inline DOM::Node* HitTestResult::dom_node()
|
|
{
|
|
return paintable->dom_node();
|
|
}
|
|
|
|
inline DOM::Node const* HitTestResult::dom_node() const
|
|
{
|
|
return paintable->dom_node();
|
|
}
|
|
|
|
template<>
|
|
inline bool Paintable::fast_is<PaintableBox>() const { return m_layout_node->is_box(); }
|
|
|
|
}
|