mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-05 07:41:01 +00:00
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:
parent
d4b7dd88b7
commit
72905c84d5
Notes:
github-actions[bot]
2025-02-24 17:07:12 +00:00
Author: https://github.com/trflynn89
Commit: 72905c84d5
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3666
Reviewed-by: https://github.com/AtkinsSJ
10 changed files with 83 additions and 34 deletions
|
@ -519,6 +519,7 @@ void Document::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_style_sheets);
|
visitor.visit(m_style_sheets);
|
||||||
visitor.visit(m_hovered_node);
|
visitor.visit(m_hovered_node);
|
||||||
visitor.visit(m_inspected_node);
|
visitor.visit(m_inspected_node);
|
||||||
|
visitor.visit(m_highlighted_node);
|
||||||
visitor.visit(m_active_favicon);
|
visitor.visit(m_active_favicon);
|
||||||
visitor.visit(m_focused_element);
|
visitor.visit(m_focused_element);
|
||||||
visitor.visit(m_active_element);
|
visitor.visit(m_active_element);
|
||||||
|
@ -1638,29 +1639,36 @@ Layout::Viewport* Document::layout_node()
|
||||||
return static_cast<Layout::Viewport*>(Node::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;
|
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();
|
layout_node->first_paintable()->set_needs_display();
|
||||||
|
|
||||||
m_inspected_node = node;
|
m_highlighted_node = node;
|
||||||
m_inspected_pseudo_element = pseudo_element;
|
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->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;
|
return nullptr;
|
||||||
if (!m_inspected_pseudo_element.has_value() || !m_inspected_node->is_element())
|
|
||||||
return m_inspected_node->layout_node();
|
if (!m_highlighted_pseudo_element.has_value() || !m_highlighted_node->is_element())
|
||||||
auto& element = static_cast<Element&>(*m_inspected_node);
|
return m_highlighted_node->layout_node();
|
||||||
return element.get_pseudo_element_node(m_inspected_pseudo_element.value());
|
|
||||||
|
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)
|
static Node* find_common_ancestor(Node* a, Node* b)
|
||||||
|
|
|
@ -186,11 +186,13 @@ public:
|
||||||
Node* hovered_node() { return m_hovered_node.ptr(); }
|
Node* hovered_node() { return m_hovered_node.ptr(); }
|
||||||
Node const* hovered_node() const { 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>);
|
void set_inspected_node(GC::Ptr<Node>);
|
||||||
Node* inspected_node() { return m_inspected_node.ptr(); }
|
GC::Ptr<Node const> inspected_node() const { return m_inspected_node; }
|
||||||
Node const* inspected_node() const { return m_inspected_node.ptr(); }
|
|
||||||
Layout::Node* inspected_layout_node();
|
void set_highlighted_node(GC::Ptr<Node>, Optional<CSS::Selector::PseudoElement::Type>);
|
||||||
Layout::Node const* inspected_layout_node() const { return const_cast<Document*>(this)->inspected_layout_node(); }
|
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* document_element();
|
||||||
Element const* document_element() const;
|
Element const* document_element() const;
|
||||||
|
@ -866,9 +868,6 @@ private:
|
||||||
GC::Ref<Page> m_page;
|
GC::Ref<Page> m_page;
|
||||||
OwnPtr<CSS::StyleComputer> m_style_computer;
|
OwnPtr<CSS::StyleComputer> m_style_computer;
|
||||||
GC::Ptr<CSS::StyleSheetList> m_style_sheets;
|
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;
|
GC::Ptr<Node> m_active_favicon;
|
||||||
WeakPtr<HTML::BrowsingContext> m_browsing_context;
|
WeakPtr<HTML::BrowsingContext> m_browsing_context;
|
||||||
URL::URL m_url;
|
URL::URL m_url;
|
||||||
|
@ -877,6 +876,11 @@ private:
|
||||||
|
|
||||||
GC::Ptr<Layout::Viewport> m_layout_root;
|
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_normal_link_color;
|
||||||
Optional<Color> m_active_link_color;
|
Optional<Color> m_active_link_color;
|
||||||
Optional<Color> m_visited_link_color;
|
Optional<Color> m_visited_link_color;
|
||||||
|
|
|
@ -475,7 +475,7 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (phase == PaintPhase::Overlay && layout_node().document().inspected_layout_node() == &layout_node_with_style_and_box_metrics()) {
|
if (phase == PaintPhase::Overlay && layout_node().document().highlighted_layout_node() == &layout_node_with_style_and_box_metrics()) {
|
||||||
auto content_rect = absolute_united_content_rect();
|
auto content_rect = absolute_united_content_rect();
|
||||||
auto margin_rect = united_rect_for_continuation_chain(*this, [](PaintableBox const& box) {
|
auto margin_rect = united_rect_for_continuation_chain(*this, [](PaintableBox const& box) {
|
||||||
auto margin_box = box.box_model().margin_box();
|
auto margin_box = box.box_model().margin_box();
|
||||||
|
@ -759,7 +759,7 @@ void paint_text_fragment(PaintContext& context, TextPaintable const& paintable,
|
||||||
auto fragment_absolute_rect = fragment.absolute_rect();
|
auto fragment_absolute_rect = fragment.absolute_rect();
|
||||||
auto fragment_absolute_device_rect = context.enclosing_device_rect(fragment_absolute_rect);
|
auto fragment_absolute_device_rect = context.enclosing_device_rect(fragment_absolute_rect);
|
||||||
|
|
||||||
if (paintable.document().inspected_layout_node() == &paintable.layout_node())
|
if (paintable.document().highlighted_layout_node() == &paintable.layout_node())
|
||||||
context.display_list_recorder().draw_rect(fragment_absolute_device_rect.to_type<int>(), Color::Magenta);
|
context.display_list_recorder().draw_rect(fragment_absolute_device_rect.to_type<int>(), Color::Magenta);
|
||||||
|
|
||||||
auto text = paintable.text_for_rendering();
|
auto text = paintable.text_for_rendering();
|
||||||
|
|
|
@ -161,6 +161,7 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple
|
||||||
};
|
};
|
||||||
|
|
||||||
m_inspector_web_view.on_inspector_selected_dom_node = [this](auto node_id, auto const& pseudo_element) {
|
m_inspector_web_view.on_inspector_selected_dom_node = [this](auto node_id, auto const& pseudo_element) {
|
||||||
|
m_content_web_view.highlight_dom_node(node_id, pseudo_element);
|
||||||
m_content_web_view.inspect_dom_node(node_id, pseudo_element);
|
m_content_web_view.inspect_dom_node(node_id, pseudo_element);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -306,6 +307,7 @@ void InspectorClient::select_default_node()
|
||||||
|
|
||||||
void InspectorClient::clear_selection()
|
void InspectorClient::clear_selection()
|
||||||
{
|
{
|
||||||
|
m_content_web_view.clear_highlighted_dom_node();
|
||||||
m_content_web_view.clear_inspected_dom_node();
|
m_content_web_view.clear_inspected_dom_node();
|
||||||
|
|
||||||
static constexpr auto script = "inspector.clearInspectedDOMNode();"sv;
|
static constexpr auto script = "inspector.clearInspectedDOMNode();"sv;
|
||||||
|
|
|
@ -303,24 +303,34 @@ void ViewImplementation::inspect_dom_tree()
|
||||||
client().async_inspect_dom_tree(page_id());
|
client().async_inspect_dom_tree(page_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewImplementation::inspect_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element)
|
|
||||||
{
|
|
||||||
client().async_inspect_dom_node(page_id(), node_id, move(pseudo_element));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewImplementation::inspect_accessibility_tree()
|
void ViewImplementation::inspect_accessibility_tree()
|
||||||
{
|
{
|
||||||
client().async_inspect_accessibility_tree(page_id());
|
client().async_inspect_accessibility_tree(page_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewImplementation::get_hovered_node_id()
|
||||||
|
{
|
||||||
|
client().async_get_hovered_node_id(page_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewImplementation::inspect_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element)
|
||||||
|
{
|
||||||
|
client().async_inspect_dom_node(page_id(), node_id, move(pseudo_element));
|
||||||
|
}
|
||||||
|
|
||||||
void ViewImplementation::clear_inspected_dom_node()
|
void ViewImplementation::clear_inspected_dom_node()
|
||||||
{
|
{
|
||||||
inspect_dom_node(0, {});
|
inspect_dom_node(0, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewImplementation::get_hovered_node_id()
|
void ViewImplementation::highlight_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element)
|
||||||
{
|
{
|
||||||
client().async_get_hovered_node_id(page_id());
|
client().async_highlight_dom_node(page_id(), node_id, move(pseudo_element));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewImplementation::clear_highlighted_dom_node()
|
||||||
|
{
|
||||||
|
highlight_dom_node(0, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewImplementation::set_dom_node_text(Web::UniqueNodeID node_id, String text)
|
void ViewImplementation::set_dom_node_text(Web::UniqueNodeID node_id, String text)
|
||||||
|
|
|
@ -103,11 +103,15 @@ public:
|
||||||
void get_source();
|
void get_source();
|
||||||
|
|
||||||
void inspect_dom_tree();
|
void inspect_dom_tree();
|
||||||
void inspect_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element);
|
|
||||||
void inspect_accessibility_tree();
|
void inspect_accessibility_tree();
|
||||||
void clear_inspected_dom_node();
|
|
||||||
void get_hovered_node_id();
|
void get_hovered_node_id();
|
||||||
|
|
||||||
|
void inspect_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element);
|
||||||
|
void clear_inspected_dom_node();
|
||||||
|
|
||||||
|
void highlight_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element);
|
||||||
|
void clear_highlighted_dom_node();
|
||||||
|
|
||||||
void set_dom_node_text(Web::UniqueNodeID node_id, String text);
|
void set_dom_node_text(Web::UniqueNodeID node_id, String text);
|
||||||
void set_dom_node_tag(Web::UniqueNodeID node_id, String name);
|
void set_dom_node_tag(Web::UniqueNodeID node_id, String name);
|
||||||
void add_dom_node_attributes(Web::UniqueNodeID node_id, Vector<Attribute> attributes);
|
void add_dom_node_attributes(Web::UniqueNodeID node_id, Vector<Attribute> attributes);
|
||||||
|
|
|
@ -442,7 +442,7 @@ void ConnectionFromClient::inspect_dom_node(u64 page_id, Web::UniqueNodeID const
|
||||||
|
|
||||||
for (auto& navigable : Web::HTML::all_navigables()) {
|
for (auto& navigable : Web::HTML::all_navigables()) {
|
||||||
if (navigable->active_document() != nullptr) {
|
if (navigable->active_document() != nullptr) {
|
||||||
navigable->active_document()->set_inspected_node(nullptr, {});
|
navigable->active_document()->set_inspected_node(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +453,7 @@ void ConnectionFromClient::inspect_dom_node(u64 page_id, Web::UniqueNodeID const
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node->document().set_inspected_node(node, pseudo_element);
|
node->document().set_inspected_node(node);
|
||||||
|
|
||||||
if (node->is_element()) {
|
if (node->is_element()) {
|
||||||
auto& element = as<Web::DOM::Element>(*node);
|
auto& element = as<Web::DOM::Element>(*node);
|
||||||
|
@ -599,6 +599,25 @@ void ConnectionFromClient::inspect_dom_node(u64 page_id, Web::UniqueNodeID const
|
||||||
async_did_inspect_dom_node(page_id, false, {}, {}, {}, {}, {}, {});
|
async_did_inspect_dom_node(page_id, false, {}, {}, {}, {}, {}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnectionFromClient::highlight_dom_node(u64 page_id, Web::UniqueNodeID const& node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element)
|
||||||
|
{
|
||||||
|
auto page = this->page(page_id);
|
||||||
|
if (!page.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto& navigable : Web::HTML::all_navigables()) {
|
||||||
|
if (navigable->active_document() != nullptr) {
|
||||||
|
navigable->active_document()->set_highlighted_node(nullptr, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* node = Web::DOM::Node::from_unique_id(node_id);
|
||||||
|
if (!node || !node->layout_node())
|
||||||
|
return;
|
||||||
|
|
||||||
|
node->document().set_highlighted_node(node, pseudo_element);
|
||||||
|
}
|
||||||
|
|
||||||
void ConnectionFromClient::inspect_accessibility_tree(u64 page_id)
|
void ConnectionFromClient::inspect_accessibility_tree(u64 page_id)
|
||||||
{
|
{
|
||||||
if (auto page = this->page(page_id); page.has_value()) {
|
if (auto page = this->page(page_id); page.has_value()) {
|
||||||
|
|
|
@ -76,6 +76,7 @@ private:
|
||||||
virtual void get_source(u64 page_id) override;
|
virtual void get_source(u64 page_id) override;
|
||||||
virtual void inspect_dom_tree(u64 page_id) override;
|
virtual void inspect_dom_tree(u64 page_id) override;
|
||||||
virtual void inspect_dom_node(u64 page_id, Web::UniqueNodeID const& node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
|
virtual void inspect_dom_node(u64 page_id, Web::UniqueNodeID const& node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
|
||||||
|
virtual void highlight_dom_node(u64 page_id, Web::UniqueNodeID const& node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
|
||||||
virtual void inspect_accessibility_tree(u64 page_id) override;
|
virtual void inspect_accessibility_tree(u64 page_id) override;
|
||||||
virtual void get_hovered_node_id(u64 page_id) override;
|
virtual void get_hovered_node_id(u64 page_id) override;
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ JS_DEFINE_NATIVE_FUNCTION(ConsoleGlobalEnvironmentExtensions::$0_getter)
|
||||||
{
|
{
|
||||||
auto* console_global_object = TRY(get_console(vm));
|
auto* console_global_object = TRY(get_console(vm));
|
||||||
auto& window = *console_global_object->m_window_object;
|
auto& window = *console_global_object->m_window_object;
|
||||||
auto* inspected_node = window.associated_document().inspected_node();
|
auto inspected_node = window.associated_document().inspected_node();
|
||||||
if (!inspected_node)
|
if (!inspected_node)
|
||||||
return JS::js_undefined();
|
return JS::js_undefined();
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ endpoint WebContentServer
|
||||||
get_source(u64 page_id) =|
|
get_source(u64 page_id) =|
|
||||||
inspect_dom_tree(u64 page_id) =|
|
inspect_dom_tree(u64 page_id) =|
|
||||||
inspect_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) =|
|
inspect_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) =|
|
||||||
|
highlight_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) =|
|
||||||
inspect_accessibility_tree(u64 page_id) =|
|
inspect_accessibility_tree(u64 page_id) =|
|
||||||
get_hovered_node_id(u64 page_id) =|
|
get_hovered_node_id(u64 page_id) =|
|
||||||
js_console_input(u64 page_id, ByteString js_source) =|
|
js_console_input(u64 page_id, ByteString js_source) =|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue