LibWeb+LibWebView+WebContent: Support both inspecting/highlighting nodes

Our own Inspector differs from most other DevTools implementations with
regard to highlighting DOM nodes as you hover elements in the inspected
DOM tree. In other implementations, as you change the hovered node, the
browser will render a box model overlay onto the page for that node. We
currently don't do this; we wait until you click the node, at which
point we both paint the overlay and inspect the node's properties.

This patch does not change that behavior, but separates the IPCs and
internal tracking of inspected nodes to support the standard DevTools
behavior. So the DOM document now stores an inspected node and a
highlighted node. The former is used for features such as "$0" in the
JavaScript console, and the latter is used for the box model overlay.
Our Inspector continues to set these to the same node.
This commit is contained in:
Timothy Flynn 2025-02-21 09:34:02 -05:00 committed by Tim Flynn
commit 72905c84d5
Notes: github-actions[bot] 2025-02-24 17:07:12 +00:00
10 changed files with 83 additions and 34 deletions

View file

@ -519,6 +519,7 @@ void Document::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_style_sheets);
visitor.visit(m_hovered_node);
visitor.visit(m_inspected_node);
visitor.visit(m_highlighted_node);
visitor.visit(m_active_favicon);
visitor.visit(m_focused_element);
visitor.visit(m_active_element);
@ -1638,29 +1639,36 @@ Layout::Viewport* Document::layout_node()
return static_cast<Layout::Viewport*>(Node::layout_node());
}
void Document::set_inspected_node(Node* node, Optional<CSS::Selector::PseudoElement::Type> pseudo_element)
void Document::set_inspected_node(GC::Ptr<Node> node)
{
if (m_inspected_node.ptr() == node && m_inspected_pseudo_element == pseudo_element)
m_inspected_node = node;
}
void Document::set_highlighted_node(GC::Ptr<Node> node, Optional<CSS::Selector::PseudoElement::Type> pseudo_element)
{
if (m_highlighted_node == node && m_highlighted_pseudo_element == pseudo_element)
return;
if (auto layout_node = inspected_layout_node(); layout_node && layout_node->first_paintable())
if (auto layout_node = highlighted_layout_node(); layout_node && layout_node->first_paintable())
layout_node->first_paintable()->set_needs_display();
m_inspected_node = node;
m_inspected_pseudo_element = pseudo_element;
m_highlighted_node = node;
m_highlighted_pseudo_element = pseudo_element;
if (auto layout_node = inspected_layout_node(); layout_node && layout_node->first_paintable())
if (auto layout_node = highlighted_layout_node(); layout_node && layout_node->first_paintable())
layout_node->first_paintable()->set_needs_display();
}
Layout::Node* Document::inspected_layout_node()
GC::Ptr<Layout::Node> Document::highlighted_layout_node()
{
if (!m_inspected_node)
if (!m_highlighted_node)
return nullptr;
if (!m_inspected_pseudo_element.has_value() || !m_inspected_node->is_element())
return m_inspected_node->layout_node();
auto& element = static_cast<Element&>(*m_inspected_node);
return element.get_pseudo_element_node(m_inspected_pseudo_element.value());
if (!m_highlighted_pseudo_element.has_value() || !m_highlighted_node->is_element())
return m_highlighted_node->layout_node();
auto const& element = static_cast<Element const&>(*m_highlighted_node);
return element.get_pseudo_element_node(m_highlighted_pseudo_element.value());
}
static Node* find_common_ancestor(Node* a, Node* b)

View file

@ -186,11 +186,13 @@ public:
Node* hovered_node() { return m_hovered_node.ptr(); }
Node const* hovered_node() const { return m_hovered_node.ptr(); }
void set_inspected_node(Node*, Optional<CSS::Selector::PseudoElement::Type>);
Node* inspected_node() { return m_inspected_node.ptr(); }
Node const* inspected_node() const { return m_inspected_node.ptr(); }
Layout::Node* inspected_layout_node();
Layout::Node const* inspected_layout_node() const { return const_cast<Document*>(this)->inspected_layout_node(); }
void set_inspected_node(GC::Ptr<Node>);
GC::Ptr<Node const> inspected_node() const { return m_inspected_node; }
void set_highlighted_node(GC::Ptr<Node>, Optional<CSS::Selector::PseudoElement::Type>);
GC::Ptr<Node const> highlighted_node() const { return m_highlighted_node; }
GC::Ptr<Layout::Node> highlighted_layout_node();
GC::Ptr<Layout::Node const> highlighted_layout_node() const { return const_cast<Document*>(this)->highlighted_layout_node(); }
Element* document_element();
Element const* document_element() const;
@ -866,9 +868,6 @@ private:
GC::Ref<Page> m_page;
OwnPtr<CSS::StyleComputer> m_style_computer;
GC::Ptr<CSS::StyleSheetList> m_style_sheets;
GC::Ptr<Node> m_hovered_node;
GC::Ptr<Node> m_inspected_node;
Optional<CSS::Selector::PseudoElement::Type> m_inspected_pseudo_element;
GC::Ptr<Node> m_active_favicon;
WeakPtr<HTML::BrowsingContext> m_browsing_context;
URL::URL m_url;
@ -877,6 +876,11 @@ private:
GC::Ptr<Layout::Viewport> m_layout_root;
GC::Ptr<Node> m_hovered_node;
GC::Ptr<Node> m_inspected_node;
GC::Ptr<Node> m_highlighted_node;
Optional<CSS::Selector::PseudoElement::Type> m_highlighted_pseudo_element;
Optional<Color> m_normal_link_color;
Optional<Color> m_active_link_color;
Optional<Color> m_visited_link_color;