mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:07:35 +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
|
@ -16,22 +16,18 @@
|
|||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibJS/MarkupGenerator.h>
|
||||
#include <LibJS/SyntaxHighlighter.h>
|
||||
#include <LibWebView/ConsoleClient.h>
|
||||
#include <LibWebView/OutOfProcessWebView.h>
|
||||
|
||||
namespace Browser {
|
||||
|
||||
ConsoleWidget::ConsoleWidget()
|
||||
ConsoleWidget::ConsoleWidget(WebView::OutOfProcessWebView& content_view)
|
||||
{
|
||||
set_layout<GUI::VerticalBoxLayout>();
|
||||
set_fill_with_background_color(true);
|
||||
|
||||
m_output_view = add<WebView::OutOfProcessWebView>();
|
||||
m_output_view->use_native_user_style_sheet();
|
||||
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);
|
||||
|
||||
auto& bottom_container = add<GUI::Widget>();
|
||||
bottom_container.set_layout<GUI::HorizontalBoxLayout>();
|
||||
|
@ -52,10 +48,7 @@ ConsoleWidget::ConsoleWidget()
|
|||
m_input->add_current_text_to_history();
|
||||
m_input->clear();
|
||||
|
||||
print_source_line(js_source);
|
||||
|
||||
if (on_js_input)
|
||||
on_js_input(js_source);
|
||||
m_console_client->execute(js_source);
|
||||
};
|
||||
|
||||
set_focus_proxy(m_input);
|
||||
|
@ -65,168 +58,15 @@ ConsoleWidget::ConsoleWidget()
|
|||
clear_button.set_icon(g_icon_bag.delete_icon);
|
||||
clear_button.set_tooltip_deprecated("Clear the console output");
|
||||
clear_button.on_click = [this](auto) {
|
||||
clear_output();
|
||||
m_console_client->clear();
|
||||
};
|
||||
}
|
||||
|
||||
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") {
|
||||
begin_group(message, true);
|
||||
} else if (type == "groupCollapsed") {
|
||||
begin_group(message, false);
|
||||
} else if (type == "groupEnd") {
|
||||
end_group();
|
||||
} 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;
|
||||
|
||||
int parent_id = m_group_stack.is_empty() ? 0 : m_group_stack.last().id;
|
||||
if (parent_id == 0) {
|
||||
builder.append(R"~~~(
|
||||
var parentGroup = document.body;
|
||||
)~~~"sv);
|
||||
} else {
|
||||
builder.appendff(R"~~~(
|
||||
var parentGroup = document.getElementById("group_{}");
|
||||
)~~~",
|
||||
parent_id);
|
||||
}
|
||||
|
||||
builder.append(R"~~~(
|
||||
var p = document.createElement("p");
|
||||
p.innerHTML = ")~~~"sv);
|
||||
builder.append_escaped_for_json(line);
|
||||
builder.append(R"~~~("
|
||||
parentGroup.appendChild(p);
|
||||
)~~~"sv);
|
||||
m_output_view->run_javascript(builder.string_view());
|
||||
// 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.
|
||||
// (See also: begin_group())
|
||||
}
|
||||
|
||||
void ConsoleWidget::clear_output()
|
||||
{
|
||||
m_group_stack.clear();
|
||||
m_output_view->run_javascript(R"~~~(
|
||||
document.body.innerHTML = "";
|
||||
)~~~"sv);
|
||||
}
|
||||
|
||||
void ConsoleWidget::begin_group(StringView label, bool start_expanded)
|
||||
{
|
||||
StringBuilder builder;
|
||||
int parent_id = m_group_stack.is_empty() ? 0 : m_group_stack.last().id;
|
||||
if (parent_id == 0) {
|
||||
builder.append(R"~~~(
|
||||
var parentGroup = document.body;
|
||||
)~~~"sv);
|
||||
} else {
|
||||
builder.appendff(R"~~~(
|
||||
var parentGroup = document.getElementById("group_{}");
|
||||
)~~~",
|
||||
parent_id);
|
||||
}
|
||||
|
||||
Group group;
|
||||
group.id = m_next_group_id++;
|
||||
group.label = label;
|
||||
|
||||
builder.appendff(R"~~~(
|
||||
var group = document.createElement("details");
|
||||
group.id = "group_{}";
|
||||
var label = document.createElement("summary");
|
||||
label.innerHTML = ")~~~",
|
||||
group.id);
|
||||
builder.append_escaped_for_json(label);
|
||||
builder.append(R"~~~(";
|
||||
group.appendChild(label);
|
||||
parentGroup.appendChild(group);
|
||||
)~~~"sv);
|
||||
|
||||
if (start_expanded)
|
||||
builder.append("group.open = true;"sv);
|
||||
|
||||
m_output_view->run_javascript(builder.string_view());
|
||||
// FIXME: Scroll console to bottom - see note in print_html()
|
||||
m_group_stack.append(group);
|
||||
}
|
||||
|
||||
void ConsoleWidget::end_group()
|
||||
{
|
||||
m_group_stack.take_last();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue