Browse Source

Ladybird: Reimplement the DOM inspector :^)

This has been broken since the switch to the multiprocess architecture
(and even before then was very limited).

This restores the previous functionally and also implements the ability
to inspect individual elements (by selecting them in the tree view).
The inspector also now correctly updates when navigating between pages.
MacDue 2 years ago
parent
commit
aa85a88158

+ 1 - 0
Ladybird/CMakeLists.txt

@@ -82,6 +82,7 @@ set(SOURCES
     ${BROWSER_SOURCE_DIR}/History.cpp
     ${BROWSER_SOURCE_DIR}/History.cpp
     BrowserWindow.cpp
     BrowserWindow.cpp
     ConsoleWidget.cpp
     ConsoleWidget.cpp
+    InspectorWidget.cpp
     ModelTranslator.cpp
     ModelTranslator.cpp
     Settings.cpp
     Settings.cpp
     SettingsDialog.cpp
     SettingsDialog.cpp

+ 76 - 0
Ladybird/InspectorWidget.cpp

@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2022, MacDue <macdue@dueutil.tech>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#define AK_DONT_REPLACE_STD
+
+#include "InspectorWidget.h"
+#include <LibWebView/DOMTreeModel.h>
+#include <QCloseEvent>
+#include <QTreeView>
+#include <QVBoxLayout>
+
+namespace Ladybird {
+
+InspectorWidget::InspectorWidget()
+{
+    setLayout(new QVBoxLayout);
+    m_tree_view = new QTreeView;
+    m_tree_view->setHeaderHidden(true);
+    m_tree_view->expandToDepth(3);
+    layout()->addWidget(m_tree_view);
+    m_tree_view->setModel(&m_dom_model);
+    QObject::connect(m_tree_view->selectionModel(), &QItemSelectionModel::selectionChanged,
+        [this](QItemSelection const& selected, QItemSelection const&) {
+            auto indexes = selected.indexes();
+            if (indexes.size()) {
+                auto index = m_dom_model.to_gui(indexes.first());
+                set_selection(index);
+            }
+        });
+}
+
+void InspectorWidget::clear_dom_json()
+{
+    m_dom_model.set_underlying_model(nullptr);
+}
+
+void InspectorWidget::set_dom_json(StringView dom_json)
+{
+    m_dom_model.set_underlying_model(::WebView::DOMTreeModel::create(dom_json));
+}
+
+void InspectorWidget::closeEvent(QCloseEvent* event)
+{
+    event->accept();
+    if (on_close)
+        on_close();
+}
+
+void InspectorWidget::set_selection(GUI::ModelIndex index)
+{
+    if (!index.is_valid())
+        return;
+
+    auto* json = static_cast<JsonObject const*>(index.internal_data());
+    VERIFY(json);
+
+    Selection selection {};
+    if (json->has_u32("pseudo-element"sv)) {
+        selection.dom_node_id = json->get("parent-id"sv).to_i32();
+        selection.pseudo_element = static_cast<Web::CSS::Selector::PseudoElement>(json->get("pseudo-element"sv).to_u32());
+    } else {
+        selection.dom_node_id = json->get("id"sv).to_i32();
+    }
+
+    if (selection == m_selection)
+        return;
+    m_selection = selection;
+
+    VERIFY(on_dom_node_inspected);
+    on_dom_node_inspected(m_selection.dom_node_id, m_selection.pseudo_element);
+}
+
+}

+ 46 - 0
Ladybird/InspectorWidget.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022, MacDue <macdue@dueutil.tech>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "ModelTranslator.h"
+#include <AK/Optional.h>
+#include <AK/StringView.h>
+#include <LibWeb/CSS/Selector.h>
+#include <QWidget>
+
+class QTreeView;
+
+namespace Ladybird {
+
+class InspectorWidget final : public QWidget {
+    Q_OBJECT
+public:
+    InspectorWidget();
+    virtual ~InspectorWidget() = default;
+
+    struct Selection {
+        i32 dom_node_id { 0 };
+        Optional<Web::CSS::Selector::PseudoElement> pseudo_element {};
+        bool operator==(Selection const& other) const = default;
+    };
+
+    void clear_dom_json();
+    void set_dom_json(StringView dom_json);
+
+    Function<void(i32, Optional<Web::CSS::Selector::PseudoElement>)> on_dom_node_inspected;
+    Function<void()> on_close;
+
+private:
+    void set_selection(GUI::ModelIndex);
+    void closeEvent(QCloseEvent*) override;
+
+    QTreeView* m_tree_view { nullptr };
+    ModelTranslator m_dom_model {};
+    Selection m_selection;
+};
+
+}

+ 44 - 23
Ladybird/WebContentView.cpp

@@ -8,7 +8,7 @@
 
 
 #include "WebContentView.h"
 #include "WebContentView.h"
 #include "ConsoleWidget.h"
 #include "ConsoleWidget.h"
-#include "ModelTranslator.h"
+#include "InspectorWidget.h"
 #include "Utilities.h"
 #include "Utilities.h"
 #include <AK/Assertions.h>
 #include <AK/Assertions.h>
 #include <AK/ByteBuffer.h>
 #include <AK/ByteBuffer.h>
@@ -33,10 +33,10 @@
 #include <LibGfx/PNGWriter.h>
 #include <LibGfx/PNGWriter.h>
 #include <LibGfx/Painter.h>
 #include <LibGfx/Painter.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Rect.h>
+#include <LibGfx/SystemTheme.h>
 #include <LibJS/Runtime/ConsoleObject.h>
 #include <LibJS/Runtime/ConsoleObject.h>
 #include <LibMain/Main.h>
 #include <LibMain/Main.h>
 #include <LibWeb/Loader/ContentFilter.h>
 #include <LibWeb/Loader/ContentFilter.h>
-#include <LibWebView/DOMTreeModel.h>
 #include <LibWebView/WebContentClient.h>
 #include <LibWebView/WebContentClient.h>
 #include <QApplication>
 #include <QApplication>
 #include <QCursor>
 #include <QCursor>
@@ -52,8 +52,6 @@
 #include <QTextEdit>
 #include <QTextEdit>
 #include <QTimer>
 #include <QTimer>
 #include <QToolTip>
 #include <QToolTip>
-#include <QTreeView>
-#include <QVBoxLayout>
 
 
 WebContentView::WebContentView(StringView webdriver_content_ipc_path)
 WebContentView::WebContentView(StringView webdriver_content_ipc_path)
     : m_webdriver_content_ipc_path(webdriver_content_ipc_path)
     : m_webdriver_content_ipc_path(webdriver_content_ipc_path)
@@ -79,6 +77,7 @@ WebContentView::WebContentView(StringView webdriver_content_ipc_path)
 
 
 WebContentView::~WebContentView()
 WebContentView::~WebContentView()
 {
 {
+    clear_inspector_callbacks();
 }
 }
 
 
 void WebContentView::load(AK::URL const& url)
 void WebContentView::load(AK::URL const& url)
@@ -504,34 +503,50 @@ void WebContentView::ensure_inspector_widget()
 {
 {
     if (m_inspector_widget)
     if (m_inspector_widget)
         return;
         return;
-#if 0
-    m_inspector_widget = new QWidget;
+    m_inspector_widget = new Ladybird::InspectorWidget;
     m_inspector_widget->setWindowTitle("Inspector");
     m_inspector_widget->setWindowTitle("Inspector");
-    auto* layout = new QVBoxLayout;
-    m_inspector_widget->setLayout(layout);
-    auto* tree_view = new QTreeView;
-    layout->addWidget(tree_view);
-
-    auto dom_tree = m_page_client->page().top_level_browsing_context().active_document()->dump_dom_tree_as_json();
-    auto dom_tree_model = ::WebView::DOMTreeModel::create(dom_tree);
-    auto* model = new Ladybird::ModelTranslator(dom_tree_model);
-    tree_view->setModel(model);
-    tree_view->setHeaderHidden(true);
-    tree_view->expandToDepth(3);
-
     m_inspector_widget->resize(640, 480);
     m_inspector_widget->resize(640, 480);
+    m_inspector_widget->on_close = [this] {
+        clear_inspected_dom_node();
+    };
 
 
-    QObject::connect(tree_view->selectionModel(), &QItemSelectionModel::currentChanged, [this](QModelIndex const& index, QModelIndex const&) {
-        auto const* json = (JsonObject const*)index.internalPointer();
-        m_page_client->page().top_level_browsing_context().active_document()->set_inspected_node(Web::DOM::Node::from_id(json->get("id"sv).to_i32()));
-    });
-#endif
+    m_inspector_widget->on_dom_node_inspected = [&](auto id, auto pseudo_element) {
+        inspect_dom_node(id, pseudo_element);
+    };
+}
+
+void WebContentView::clear_inspector_callbacks()
+{
+    if (m_inspector_widget)
+        m_inspector_widget->on_close = nullptr;
+}
+
+bool WebContentView::is_inspector_open() const
+{
+    return m_inspector_widget && m_inspector_widget->isVisible();
+}
+
+void WebContentView::inspect_dom_tree()
+{
+    client().async_inspect_dom_tree();
+}
+
+void WebContentView::inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element)
+{
+    // TODO: Use and display response
+    (void)client().inspect_dom_node(node_id, pseudo_element);
+}
+
+void WebContentView::clear_inspected_dom_node()
+{
+    inspect_dom_node(0, {});
 }
 }
 
 
 void WebContentView::show_inspector()
 void WebContentView::show_inspector()
 {
 {
     ensure_inspector_widget();
     ensure_inspector_widget();
     m_inspector_widget->show();
     m_inspector_widget->show();
+    inspect_dom_tree();
 }
 }
 
 
 void WebContentView::set_color_scheme(ColorScheme color_scheme)
 void WebContentView::set_color_scheme(ColorScheme color_scheme)
@@ -806,11 +821,15 @@ void WebContentView::notify_server_did_start_loading(Badge<WebContentClient>, AK
 {
 {
     m_url = url;
     m_url = url;
     emit load_started(url, is_redirect);
     emit load_started(url, is_redirect);
+    if (m_inspector_widget)
+        m_inspector_widget->clear_dom_json();
 }
 }
 
 
 void WebContentView::notify_server_did_finish_loading(Badge<WebContentClient>, AK::URL const& url)
 void WebContentView::notify_server_did_finish_loading(Badge<WebContentClient>, AK::URL const& url)
 {
 {
     m_url = url;
     m_url = url;
+    if (is_inspector_open())
+        inspect_dom_tree();
 }
 }
 
 
 void WebContentView::notify_server_did_request_navigate_back(Badge<WebContentClient>)
 void WebContentView::notify_server_did_request_navigate_back(Badge<WebContentClient>)
@@ -916,6 +935,8 @@ void WebContentView::notify_server_did_get_dom_tree(DeprecatedString const& dom_
 {
 {
     if (on_get_dom_tree)
     if (on_get_dom_tree)
         on_get_dom_tree(dom_tree);
         on_get_dom_tree(dom_tree);
+    if (m_inspector_widget)
+        m_inspector_widget->set_dom_json(dom_tree);
 }
 }
 
 
 void WebContentView::notify_server_did_get_dom_node_properties(i32 node_id, DeprecatedString const& specified_style, DeprecatedString const& computed_style, DeprecatedString const& custom_properties, DeprecatedString const& node_box_sizing)
 void WebContentView::notify_server_did_get_dom_node_properties(i32 node_id, DeprecatedString const& specified_style, DeprecatedString const& computed_style, DeprecatedString const& custom_properties, DeprecatedString const& node_box_sizing)

+ 9 - 1
Ladybird/WebContentView.h

@@ -16,6 +16,7 @@
 #include <LibGfx/Forward.h>
 #include <LibGfx/Forward.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/StandardCursor.h>
 #include <LibGfx/StandardCursor.h>
+#include <LibWeb/CSS/Selector.h>
 #include <LibWebView/ViewImplementation.h>
 #include <LibWebView/ViewImplementation.h>
 
 
 #include <LibWeb/Forward.h>
 #include <LibWeb/Forward.h>
@@ -27,6 +28,7 @@ class QLineEdit;
 
 
 namespace Ladybird {
 namespace Ladybird {
 class ConsoleWidget;
 class ConsoleWidget;
+class InspectorWidget;
 }
 }
 
 
 namespace WebView {
 namespace WebView {
@@ -185,13 +187,19 @@ private:
     void ensure_js_console_widget();
     void ensure_js_console_widget();
     void ensure_inspector_widget();
     void ensure_inspector_widget();
 
 
+    bool is_inspector_open() const;
+    void inspect_dom_tree();
+    void inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element);
+    void clear_inspected_dom_node();
+    void clear_inspector_callbacks();
+
     qreal m_inverse_pixel_scaling_ratio { 1.0 };
     qreal m_inverse_pixel_scaling_ratio { 1.0 };
     bool m_should_show_line_box_borders { false };
     bool m_should_show_line_box_borders { false };
 
 
-    QPointer<QWidget> m_inspector_widget;
     QPointer<QDialog> m_dialog;
     QPointer<QDialog> m_dialog;
 
 
     Ladybird::ConsoleWidget* m_console_widget { nullptr };
     Ladybird::ConsoleWidget* m_console_widget { nullptr };
+    Ladybird::InspectorWidget* m_inspector_widget { nullptr };
 
 
     Gfx::IntRect m_viewport_rect;
     Gfx::IntRect m_viewport_rect;