浏览代码

Ladybird: Implement the JavaScript console using a WebContentView

This aligns the Ladybird console implementation with the Browser console
a bit more, which uses OutOfProcessWebView for rendering console output.
This allows us to style the console output to try and match the system
theme.

Using a WebContentView is simpler than trying to style the old QTextEdit
widget, as the console output is HTML with built-in "-libweb-palette-*"
colors. These will override any color we set on the QTextEdit widget.
Timothy Flynn 2 年之前
父节点
当前提交
4aca24481e
共有 5 个文件被更改,包括 80 次插入22 次删除
  1. 20 7
      Ladybird/BrowserWindow.cpp
  2. 43 7
      Ladybird/ConsoleWidget.cpp
  3. 4 2
      Ladybird/ConsoleWidget.h
  4. 5 4
      Ladybird/WebContentView.cpp
  5. 8 2
      Ladybird/WebContentView.h

+ 20 - 7
Ladybird/BrowserWindow.cpp

@@ -8,6 +8,7 @@
  */
 
 #include "BrowserWindow.h"
+#include "ConsoleWidget.h"
 #include "Settings.h"
 #include "SettingsDialog.h"
 #include "Utilities.h"
@@ -546,8 +547,13 @@ void BrowserWindow::reset_zoom()
 
 void BrowserWindow::select_all()
 {
-    if (auto* tab = m_current_tab)
-        tab->view().select_all();
+    if (!m_current_tab)
+        return;
+
+    if (auto* console = m_current_tab->view().console(); console && console->isActiveWindow())
+        console->view().select_all();
+    else
+        m_current_tab->view().select_all();
 }
 
 void BrowserWindow::update_displayed_zoom_level()
@@ -560,11 +566,18 @@ void BrowserWindow::update_displayed_zoom_level()
 
 void BrowserWindow::copy_selected_text()
 {
-    if (auto* tab = m_current_tab) {
-        auto text = tab->view().selected_text();
-        auto* clipboard = QGuiApplication::clipboard();
-        clipboard->setText(qstring_from_ak_deprecated_string(text));
-    }
+    if (!m_current_tab)
+        return;
+
+    DeprecatedString text;
+
+    if (auto* console = m_current_tab->view().console(); console && console->isActiveWindow())
+        text = console->view().selected_text();
+    else
+        text = m_current_tab->view().selected_text();
+
+    auto* clipboard = QGuiApplication::clipboard();
+    clipboard->setText(qstring_from_ak_deprecated_string(text));
 }
 
 void BrowserWindow::resizeEvent(QResizeEvent* event)

+ 43 - 7
Ladybird/ConsoleWidget.cpp

@@ -11,25 +11,46 @@
 
 #include "ConsoleWidget.h"
 #include "Utilities.h"
+#include "WebContentView.h"
 #include <AK/StringBuilder.h>
 #include <LibJS/MarkupGenerator.h>
 #include <QLineEdit>
+#include <QPalette>
 #include <QPushButton>
 #include <QTextEdit>
 #include <QVBoxLayout>
 
 namespace Ladybird {
 
+static bool is_using_dark_system_theme(QWidget& widget)
+{
+    // FIXME: Qt does not provide any method to query if the system is using a dark theme. We will have to implement
+    //        platform-specific methods if we wish to have better detection. For now, this inspects if Qt is using a
+    //        dark color for widget backgrounds using Rec. 709 luma coefficients.
+    //        https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients
+
+    auto color = widget.palette().color(widget.backgroundRole());
+    auto luma = 0.2126f * color.redF() + 0.7152f * color.greenF() + 0.0722f * color.blueF();
+
+    return luma <= 0.5f;
+}
+
 ConsoleWidget::ConsoleWidget()
 {
     setLayout(new QVBoxLayout);
 
-    m_output_view = new QTextEdit(this);
-    m_output_view->setReadOnly(true);
-    layout()->addWidget(m_output_view);
+    m_output_view = new WebContentView({}, WebView::EnableCallgrindProfiling::No);
+    if (is_using_dark_system_theme(*this))
+        m_output_view->update_palette(WebContentView::PaletteMode::Dark);
 
-    if (on_request_messages)
-        on_request_messages(0);
+    m_output_view->load("data:text/html,<html></html>"sv);
+    // Wait until our output WebView is loaded, and then request any messages that occurred before we existed
+    m_output_view->on_load_finish = [this](auto&) {
+        if (on_request_messages)
+            on_request_messages(0);
+    };
+
+    layout()->addWidget(m_output_view);
 
     auto* bottom_container = new QWidget(this);
     bottom_container->setLayout(new QHBoxLayout);
@@ -139,12 +160,27 @@ void ConsoleWidget::print_source_line(StringView source)
 
 void ConsoleWidget::print_html(StringView line)
 {
-    m_output_view->append(QString::fromUtf8(line.characters_without_null_termination(), line.length()));
+    StringBuilder builder;
+
+    builder.append(R"~~~(
+        var p = document.createElement("p");
+        p.innerHTML = ")~~~"sv);
+    builder.append_escaped_for_json(line);
+    builder.append(R"~~~("
+        document.body.appendChild(p);
+)~~~"sv);
+
+    // FIXME: Make it scroll to the bottom, using `window.scrollTo()` in the JS above.
+    //        We used to call `m_output_view->scroll_to_bottom();` here, but that does not work because
+    //        it runs synchronously, meaning it happens before the HTML is output via IPC above.
+    m_output_view->run_javascript(builder.string_view());
 }
 
 void ConsoleWidget::clear_output()
 {
-    m_output_view->clear();
+    m_output_view->run_javascript(R"~~~(
+        document.body.innerHTML = "";
+    )~~~"sv);
 }
 
 void ConsoleWidget::reset()

+ 4 - 2
Ladybird/ConsoleWidget.h

@@ -15,7 +15,7 @@
 #include <QWidget>
 
 class QLineEdit;
-class QTextEdit;
+class WebContentView;
 
 namespace Ladybird {
 
@@ -31,6 +31,8 @@ public:
     void print_html(StringView);
     void reset();
 
+    WebContentView& view() { return *m_output_view; }
+
     Function<void(DeprecatedString const&)> on_js_input;
     Function<void(i32)> on_request_messages;
 
@@ -38,7 +40,7 @@ private:
     void request_console_messages();
     void clear_output();
 
-    QTextEdit* m_output_view { nullptr };
+    WebContentView* m_output_view { nullptr };
     QLineEdit* m_input { nullptr };
 
     i32 m_highest_notified_message_index { -1 };

+ 5 - 4
Ladybird/WebContentView.cpp

@@ -567,11 +567,12 @@ void WebContentView::hideEvent(QHideEvent* event)
     client().async_set_system_visibility_state(false);
 }
 
-static Core::AnonymousBuffer make_system_theme_from_qt_palette(QWidget& widget)
+static Core::AnonymousBuffer make_system_theme_from_qt_palette(QWidget& widget, WebContentView::PaletteMode mode)
 {
     auto qt_palette = widget.palette();
 
-    auto theme = Gfx::load_system_theme(DeprecatedString::formatted("{}/res/themes/Default.ini", s_serenity_resource_root)).release_value_but_fixme_should_propagate_errors();
+    auto theme_file = mode == WebContentView::PaletteMode::Default ? "Default"sv : "Dark"sv;
+    auto theme = Gfx::load_system_theme(DeprecatedString::formatted("{}/res/themes/{}.ini", s_serenity_resource_root, theme_file)).release_value_but_fixme_should_propagate_errors();
     auto palette_impl = Gfx::PaletteImpl::create_with_anonymous_buffer(theme);
     auto palette = Gfx::Palette(move(palette_impl));
 
@@ -594,9 +595,9 @@ static Core::AnonymousBuffer make_system_theme_from_qt_palette(QWidget& widget)
     return theme;
 }
 
-void WebContentView::update_palette()
+void WebContentView::update_palette(PaletteMode mode)
 {
-    client().async_update_system_theme(make_system_theme_from_qt_palette(*this));
+    client().async_update_system_theme(make_system_theme_from_qt_palette(*this, mode));
 }
 
 void WebContentView::create_client(WebView::EnableCallgrindProfiling enable_callgrind_profiling)

+ 8 - 2
Ladybird/WebContentView.h

@@ -98,6 +98,8 @@ public:
     void show_js_console();
     void show_inspector();
 
+    Ladybird::ConsoleWidget* console() { return m_console_widget; };
+
     ErrorOr<String> dump_layout_tree();
 
     void set_viewport_rect(Gfx::IntRect);
@@ -107,6 +109,12 @@ public:
     Gfx::IntPoint to_content(Gfx::IntPoint) const;
     Gfx::IntPoint to_widget(Gfx::IntPoint) const;
 
+    enum class PaletteMode {
+        Default,
+        Dark,
+    };
+    void update_palette(PaletteMode = PaletteMode::Default);
+
     virtual void notify_server_did_layout(Badge<WebContentClient>, Gfx::IntSize content_size) override;
     virtual void notify_server_did_paint(Badge<WebContentClient>, i32 bitmap_id) override;
     virtual void notify_server_did_invalidate_content_rect(Badge<WebContentClient>, Gfx::IntRect const&) override;
@@ -198,8 +206,6 @@ private:
     bool is_inspector_open() const;
     void close_sub_widgets();
 
-    void update_palette();
-
     qreal m_inverse_pixel_scaling_ratio { 1.0 };
     bool m_should_show_line_box_borders { false };