mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 19:07:34 +00:00
Ladybird+LibWebView: Extract common JS console functionality to a helper
This creates WebView::ConsoleClient to handle functionality that will be common to the JS consoles of all Ladybird chromes. This will let each chrome focus on just the UI. Note that this includes the `console.group` functionality that only the Serenity chrome previously had. This was a FIXME in the Qt chrome, and it is implemented such that all chromes will receive this functionality for free.
This commit is contained in:
parent
c9b9278092
commit
bf59e06d2a
11 changed files with 300 additions and 366 deletions
|
@ -10,35 +10,26 @@
|
|||
#include "ConsoleWidget.h"
|
||||
#include "StringUtils.h"
|
||||
#include "WebContentView.h"
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibJS/MarkupGenerator.h>
|
||||
#include <LibWebView/ConsoleClient.h>
|
||||
#include <QFontDatabase>
|
||||
#include <QKeyEvent>
|
||||
#include <QLineEdit>
|
||||
#include <QPalette>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
bool is_using_dark_system_theme(QWidget&);
|
||||
|
||||
ConsoleWidget::ConsoleWidget()
|
||||
ConsoleWidget::ConsoleWidget(WebContentView& content_view)
|
||||
{
|
||||
setLayout(new QVBoxLayout);
|
||||
|
||||
m_output_view = new WebContentView({}, WebView::EnableCallgrindProfiling::No, UseLagomNetworking::No);
|
||||
m_output_view->use_native_user_style_sheet();
|
||||
if (is_using_dark_system_theme(*this))
|
||||
m_output_view->update_palette(WebContentView::PaletteMode::Dark);
|
||||
|
||||
m_output_view->load("data:text/html,<html style=\"font: 10pt monospace;\"></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);
|
||||
};
|
||||
m_console_client = make<WebView::ConsoleClient>(content_view, *m_output_view);
|
||||
|
||||
layout()->addWidget(m_output_view);
|
||||
|
||||
|
@ -59,117 +50,17 @@ ConsoleWidget::ConsoleWidget()
|
|||
clear_button->setText("X");
|
||||
clear_button->setToolTip("Clear the console output");
|
||||
QObject::connect(clear_button, &QPushButton::pressed, [this] {
|
||||
clear_output();
|
||||
client().clear();
|
||||
});
|
||||
|
||||
m_input->setFocus();
|
||||
}
|
||||
|
||||
void ConsoleWidget::request_console_messages()
|
||||
{
|
||||
VERIFY(!m_waiting_for_messages);
|
||||
VERIFY(on_request_messages);
|
||||
on_request_messages(m_highest_received_message_index + 1);
|
||||
m_waiting_for_messages = true;
|
||||
}
|
||||
|
||||
void ConsoleWidget::notify_about_new_console_message(i32 message_index)
|
||||
{
|
||||
if (message_index <= m_highest_received_message_index) {
|
||||
dbgln("Notified about console message we already have");
|
||||
return;
|
||||
}
|
||||
if (message_index <= m_highest_notified_message_index) {
|
||||
dbgln("Notified about console message we're already aware of");
|
||||
return;
|
||||
}
|
||||
|
||||
m_highest_notified_message_index = message_index;
|
||||
if (!m_waiting_for_messages)
|
||||
request_console_messages();
|
||||
}
|
||||
|
||||
void ConsoleWidget::handle_console_messages(i32 start_index, Vector<DeprecatedString> const& message_types, Vector<DeprecatedString> const& messages)
|
||||
{
|
||||
i32 end_index = start_index + message_types.size() - 1;
|
||||
if (end_index <= m_highest_received_message_index) {
|
||||
dbgln("Received old console messages");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < message_types.size(); i++) {
|
||||
auto& type = message_types[i];
|
||||
auto& message = messages[i];
|
||||
|
||||
if (type == "html") {
|
||||
print_html(message);
|
||||
} else if (type == "clear") {
|
||||
clear_output();
|
||||
} else if (type == "group") {
|
||||
// FIXME: Implement.
|
||||
} else if (type == "groupCollapsed") {
|
||||
// FIXME: Implement.
|
||||
} else if (type == "groupEnd") {
|
||||
// FIXME: Implement.
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
m_highest_received_message_index = end_index;
|
||||
m_waiting_for_messages = false;
|
||||
|
||||
if (m_highest_received_message_index < m_highest_notified_message_index)
|
||||
request_console_messages();
|
||||
}
|
||||
|
||||
void ConsoleWidget::print_source_line(StringView source)
|
||||
{
|
||||
StringBuilder html;
|
||||
html.append("<span class=\"repl-indicator\">"sv);
|
||||
html.append("> "sv);
|
||||
html.append("</span>"sv);
|
||||
|
||||
html.append(JS::MarkupGenerator::html_from_source(source).release_value_but_fixme_should_propagate_errors());
|
||||
|
||||
print_html(html.string_view());
|
||||
}
|
||||
|
||||
void ConsoleWidget::print_html(StringView line)
|
||||
{
|
||||
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: It should be sufficient to scrollTo a y value of document.documentElement.offsetHeight,
|
||||
// but due to an unknown bug offsetHeight seems to not be properly updated after spamming
|
||||
// a lot of document changes.
|
||||
//
|
||||
// The setTimeout makes the scrollTo async and allows the DOM to be updated.
|
||||
builder.append("setTimeout(function() { window.scrollTo(0, 1_000_000_000); }, 0);"sv);
|
||||
|
||||
m_output_view->run_javascript(builder.string_view());
|
||||
}
|
||||
|
||||
void ConsoleWidget::clear_output()
|
||||
{
|
||||
m_output_view->run_javascript(R"~~~(
|
||||
document.body.innerHTML = "";
|
||||
)~~~"sv);
|
||||
}
|
||||
ConsoleWidget::~ConsoleWidget() = default;
|
||||
|
||||
void ConsoleWidget::reset()
|
||||
{
|
||||
clear_output();
|
||||
m_highest_notified_message_index = -1;
|
||||
m_highest_received_message_index = -1;
|
||||
m_waiting_for_messages = false;
|
||||
m_console_client->reset();
|
||||
}
|
||||
|
||||
void ConsoleInputEdit::keyPressEvent(QKeyEvent* event)
|
||||
|
@ -188,12 +79,14 @@ void ConsoleInputEdit::keyPressEvent(QKeyEvent* event)
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_Up:
|
||||
if (m_history_index > 0) {
|
||||
m_history_index--;
|
||||
setText(qstring_from_ak_deprecated_string(m_history.at(m_history_index)));
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Return: {
|
||||
auto js_source = ak_deprecated_string_from_qstring(text());
|
||||
if (js_source.is_whitespace())
|
||||
|
@ -204,15 +97,12 @@ void ConsoleInputEdit::keyPressEvent(QKeyEvent* event)
|
|||
m_history_index = m_history.size();
|
||||
}
|
||||
|
||||
m_console_widget.client().execute(js_source);
|
||||
clear();
|
||||
|
||||
m_console_widget.print_source_line(js_source);
|
||||
|
||||
if (m_console_widget.on_js_input)
|
||||
m_console_widget.on_js_input(js_source);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
QLineEdit::keyPressEvent(event);
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/DeprecatedString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibWebView/Forward.h>
|
||||
#include <QLineEdit>
|
||||
#include <QWidget>
|
||||
|
||||
class QLineEdit;
|
||||
namespace Ladybird {
|
||||
|
||||
class WebContentView;
|
||||
|
@ -23,30 +23,19 @@ class WebContentView;
|
|||
class ConsoleWidget final : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConsoleWidget();
|
||||
virtual ~ConsoleWidget() = default;
|
||||
|
||||
void notify_about_new_console_message(i32 message_index);
|
||||
void handle_console_messages(i32 start_index, Vector<DeprecatedString> const& message_types, Vector<DeprecatedString> const& messages);
|
||||
void print_source_line(StringView);
|
||||
void print_html(StringView);
|
||||
void reset();
|
||||
explicit ConsoleWidget(WebContentView& content_view);
|
||||
virtual ~ConsoleWidget();
|
||||
|
||||
WebView::ConsoleClient& client() { return *m_console_client; }
|
||||
WebContentView& view() { return *m_output_view; }
|
||||
|
||||
Function<void(DeprecatedString const&)> on_js_input;
|
||||
Function<void(i32)> on_request_messages;
|
||||
void reset();
|
||||
|
||||
private:
|
||||
void request_console_messages();
|
||||
void clear_output();
|
||||
OwnPtr<WebView::ConsoleClient> m_console_client;
|
||||
|
||||
WebContentView* m_output_view { nullptr };
|
||||
QLineEdit* m_input { nullptr };
|
||||
|
||||
i32 m_highest_notified_message_index { -1 };
|
||||
i32 m_highest_received_message_index { -1 };
|
||||
bool m_waiting_for_messages { false };
|
||||
};
|
||||
|
||||
class ConsoleInputEdit final : public QLineEdit {
|
||||
|
|
|
@ -272,16 +272,6 @@ Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path, WebView::
|
|||
m_inspector_widget->set_accessibility_json(accessibility_tree);
|
||||
};
|
||||
|
||||
view().on_received_console_message = [this](auto message_index) {
|
||||
if (m_console_widget)
|
||||
m_console_widget->notify_about_new_console_message(message_index);
|
||||
};
|
||||
|
||||
view().on_received_console_messages = [this](auto start_index, auto& message_types, auto& messages) {
|
||||
if (m_console_widget)
|
||||
m_console_widget->handle_console_messages(start_index, message_types, messages);
|
||||
};
|
||||
|
||||
auto* take_visible_screenshot_action = new QAction("Take &Visible Screenshot", this);
|
||||
take_visible_screenshot_action->setIcon(QIcon(QString("%1/res/icons/16x16/filetype-image.png").arg(s_serenity_resource_root.characters())));
|
||||
QObject::connect(take_visible_screenshot_action, &QAction::triggered, this, [this]() {
|
||||
|
@ -702,7 +692,7 @@ void Tab::show_inspector_window(InspectorTarget inspector_target)
|
|||
void Tab::show_console_window()
|
||||
{
|
||||
if (!m_console_widget) {
|
||||
m_console_widget = new Ladybird::ConsoleWidget;
|
||||
m_console_widget = new Ladybird::ConsoleWidget(view());
|
||||
m_console_widget->setWindowTitle("JS Console");
|
||||
m_console_widget->resize(640, 480);
|
||||
|
||||
|
@ -719,12 +709,6 @@ void Tab::show_console_window()
|
|||
auto screen_position = QCursor::pos();
|
||||
m_console_context_menu->exec(screen_position);
|
||||
};
|
||||
m_console_widget->on_js_input = [this](auto js_source) {
|
||||
view().js_console_input(js_source);
|
||||
};
|
||||
m_console_widget->on_request_messages = [this](i32 start_index) {
|
||||
view().js_console_request_messages(start_index);
|
||||
};
|
||||
}
|
||||
|
||||
m_console_widget->show();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue