diff --git a/Libraries/LibWebView/HelperProcess.cpp b/Libraries/LibWebView/HelperProcess.cpp index 30aa7daf90..b131adb9d2 100644 --- a/Libraries/LibWebView/HelperProcess.cpp +++ b/Libraries/LibWebView/HelperProcess.cpp @@ -84,6 +84,7 @@ ErrorOr> launch_web_content_process( IPC::File image_decoder_socket, Optional request_server_socket) { + auto const& chrome_options = WebView::Application::chrome_options(); auto const& web_content_options = WebView::Application::web_content_options(); Vector arguments { @@ -93,6 +94,8 @@ ErrorOr> launch_web_content_process( web_content_options.executable_path.to_byte_string(), }; + if (chrome_options.devtools_port.has_value()) + arguments.append("--devtools"sv); if (web_content_options.config_path.has_value()) { arguments.append("--config-path"sv); arguments.append(web_content_options.config_path.value()); diff --git a/Services/WebContent/CMakeLists.txt b/Services/WebContent/CMakeLists.txt index 9cc9806c1d..56ff509903 100644 --- a/Services/WebContent/CMakeLists.txt +++ b/Services/WebContent/CMakeLists.txt @@ -1,9 +1,11 @@ include(audio) set(SOURCES + BackingStoreManager.cpp ConnectionFromClient.cpp ConsoleGlobalEnvironmentExtensions.cpp - BackingStoreManager.cpp + DevToolsConsoleClient.cpp + InspectorConsoleClient.cpp PageClient.cpp PageHost.cpp WebContentConsoleClient.cpp diff --git a/Services/WebContent/DevToolsConsoleClient.cpp b/Services/WebContent/DevToolsConsoleClient.cpp new file mode 100644 index 0000000000..64e15b3365 --- /dev/null +++ b/Services/WebContent/DevToolsConsoleClient.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace WebContent { + +GC_DEFINE_ALLOCATOR(DevToolsConsoleClient); + +GC::Ref DevToolsConsoleClient::create(JS::Realm& realm, JS::Console& console, PageClient& client) +{ + auto& window = as(realm.global_object()); + auto console_global_environment_extensions = realm.create(realm, window); + + return realm.heap().allocate(realm, console, client, console_global_environment_extensions); +} + +DevToolsConsoleClient::DevToolsConsoleClient(JS::Realm& realm, JS::Console& console, PageClient& client, ConsoleGlobalEnvironmentExtensions& console_global_environment_extensions) + : WebContentConsoleClient(realm, console, client, console_global_environment_extensions) +{ +} + +DevToolsConsoleClient::~DevToolsConsoleClient() = default; + +void DevToolsConsoleClient::handle_result(JS::Value result) +{ + (void)result; +} + +void DevToolsConsoleClient::report_exception(JS::Error const& exception, bool in_promise) +{ + (void)exception; + (void)in_promise; +} + +void DevToolsConsoleClient::send_messages(i32 start_index) +{ + (void)start_index; +} + +// 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer +JS::ThrowCompletionOr DevToolsConsoleClient::printer(JS::Console::LogLevel log_level, PrinterArguments arguments) +{ + (void)log_level; + (void)arguments; + return JS::js_undefined(); +} + +} diff --git a/Services/WebContent/DevToolsConsoleClient.h b/Services/WebContent/DevToolsConsoleClient.h new file mode 100644 index 0000000000..0f86c6320f --- /dev/null +++ b/Services/WebContent/DevToolsConsoleClient.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace WebContent { + +class DevToolsConsoleClient final : public WebContentConsoleClient { + GC_CELL(DevToolsConsoleClient, WebContentConsoleClient); + GC_DECLARE_ALLOCATOR(DevToolsConsoleClient); + +public: + static GC::Ref create(JS::Realm&, JS::Console&, PageClient&); + virtual ~DevToolsConsoleClient() override; + +private: + DevToolsConsoleClient(JS::Realm&, JS::Console&, PageClient&, ConsoleGlobalEnvironmentExtensions&); + + virtual void handle_result(JS::Value) override; + virtual void report_exception(JS::Error const&, bool) override; + virtual void send_messages(i32 start_index) override; + + virtual JS::ThrowCompletionOr printer(JS::Console::LogLevel log_level, PrinterArguments) override; +}; + +} diff --git a/Services/WebContent/Forward.h b/Services/WebContent/Forward.h index c4175753f6..b960f64180 100644 --- a/Services/WebContent/Forward.h +++ b/Services/WebContent/Forward.h @@ -10,6 +10,8 @@ namespace WebContent { class ConnectionFromClient; class ConsoleGlobalEnvironmentExtensions; +class DevToolsConsoleClient; +class InspectorConsoleClient; class PageHost; class PageClient; class WebContentConsoleClient; diff --git a/Services/WebContent/InspectorConsoleClient.cpp b/Services/WebContent/InspectorConsoleClient.cpp new file mode 100644 index 0000000000..7c7d7d0eb6 --- /dev/null +++ b/Services/WebContent/InspectorConsoleClient.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2021, Brandon Scott + * Copyright (c) 2020, Hunter Salyer + * Copyright (c) 2021-2022, Sam Atkins + * Copyright (c) 2024, Gasim Gasimzada + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WebContent { + +GC_DEFINE_ALLOCATOR(InspectorConsoleClient); + +GC::Ref InspectorConsoleClient::create(JS::Realm& realm, JS::Console& console, PageClient& client) +{ + auto& window = as(realm.global_object()); + auto console_global_environment_extensions = realm.create(realm, window); + + return realm.heap().allocate(realm, console, client, console_global_environment_extensions); +} + +InspectorConsoleClient::InspectorConsoleClient(JS::Realm& realm, JS::Console& console, PageClient& client, ConsoleGlobalEnvironmentExtensions& console_global_environment_extensions) + : WebContentConsoleClient(realm, console, client, console_global_environment_extensions) +{ +} + +InspectorConsoleClient::~InspectorConsoleClient() = default; + +void InspectorConsoleClient::handle_result(JS::Value result) +{ + print_html(JS::MarkupGenerator::html_from_value(result).release_value_but_fixme_should_propagate_errors()); +} + +void InspectorConsoleClient::report_exception(JS::Error const& exception, bool in_promise) +{ + print_html(JS::MarkupGenerator::html_from_error(exception, in_promise).release_value_but_fixme_should_propagate_errors()); +} + +void InspectorConsoleClient::send_messages(i32 start_index) +{ + // FIXME: Cap the number of messages we send at once? + auto messages_to_send = m_message_log.size() - start_index; + if (messages_to_send < 1) { + // When the console is first created, it requests any messages that happened before + // then, by requesting with start_index=0. If we don't have any messages at all, that + // is still a valid request, and we can just ignore it. + if (start_index != 0) + m_client->console_peer_did_misbehave("Requested non-existent console message index."); + return; + } + + // FIXME: Replace with a single Vector of message structs + Vector message_types; + Vector messages; + message_types.ensure_capacity(messages_to_send); + messages.ensure_capacity(messages_to_send); + + for (size_t i = start_index; i < m_message_log.size(); i++) { + auto& message = m_message_log[i]; + switch (message.type) { + case ConsoleOutput::Type::HTML: + message_types.append("html"_string); + break; + case ConsoleOutput::Type::Clear: + message_types.append("clear"_string); + break; + case ConsoleOutput::Type::BeginGroup: + message_types.append("group"_string); + break; + case ConsoleOutput::Type::BeginGroupCollapsed: + message_types.append("groupCollapsed"_string); + break; + case ConsoleOutput::Type::EndGroup: + message_types.append("groupEnd"_string); + break; + } + + messages.append(message.data); + } + + m_client->did_get_js_console_messages(start_index, move(message_types), move(messages)); +} + +// 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer +JS::ThrowCompletionOr InspectorConsoleClient::printer(JS::Console::LogLevel log_level, PrinterArguments arguments) +{ + auto styling = escape_html_entities(m_current_message_style.string_view()); + m_current_message_style.clear(); + + if (log_level == JS::Console::LogLevel::Table) { + auto& vm = m_console->realm().vm(); + + auto table_args = arguments.get>(); + auto& table = table_args.at(0).as_object(); + auto& columns = TRY(table.get(vm.names.columns)).as_array().indexed_properties(); + auto& rows = TRY(table.get(vm.names.rows)).as_array().indexed_properties(); + + StringBuilder html; + + html.appendff("
"); + html.appendff(""); + html.appendff(""); + html.appendff(""); + for (auto const& col : columns) { + auto index = col.index(); + auto value = columns.storage()->get(index).value().value; + html.appendff("", value); + } + + html.appendff(""); + html.appendff(""); + html.appendff(""); + + for (auto const& row : rows) { + auto row_index = row.index(); + auto& row_obj = rows.storage()->get(row_index).value().value.as_object(); + html.appendff(""); + + for (auto const& col : columns) { + auto col_index = col.index(); + auto col_name = columns.storage()->get(col_index).value().value; + + auto property_key = TRY(JS::PropertyKey::from_value(vm, col_name)); + auto cell = TRY(row_obj.get(property_key)); + html.appendff(""); + } + + html.appendff(""); + } + + html.appendff(""); + html.appendff("
{}
"); + if (TRY(cell.is_array(vm))) { + AllocatingMemoryStream stream; + JS::PrintContext ctx { vm, stream, true }; + TRY_OR_THROW_OOM(vm, stream.write_until_depleted(" "sv.bytes())); + TRY_OR_THROW_OOM(vm, JS::print(cell, ctx)); + auto output = TRY_OR_THROW_OOM(vm, String::from_stream(stream, stream.used_buffer_size())); + + auto size = cell.as_array().indexed_properties().array_like_size(); + html.appendff("
Array({}){}
", size, output); + + } else if (cell.is_object()) { + AllocatingMemoryStream stream; + JS::PrintContext ctx { vm, stream, true }; + TRY_OR_THROW_OOM(vm, stream.write_until_depleted(" "sv.bytes())); + TRY_OR_THROW_OOM(vm, JS::print(cell, ctx)); + auto output = TRY_OR_THROW_OOM(vm, String::from_stream(stream, stream.used_buffer_size())); + + html.appendff("
Object({{...}}){}
", output); + } else if (cell.is_function() || cell.is_constructor()) { + html.appendff("ƒ"); + } else if (!cell.is_undefined()) { + html.appendff("{}", cell); + } + html.appendff("
"); + html.appendff("
"); + print_html(MUST(html.to_string())); + + auto output = TRY(generically_format_values(table_args)); + m_console->output_debug_message(log_level, output); + + return JS::js_undefined(); + } + + if (log_level == JS::Console::LogLevel::Trace) { + auto trace = arguments.get(); + StringBuilder html; + if (!trace.label.is_empty()) + html.appendff("{}
", styling, escape_html_entities(trace.label)); + + html.append(""sv); + for (auto& function_name : trace.stack) + html.appendff("-> {}
", escape_html_entities(function_name)); + html.append("
"sv); + + print_html(MUST(html.to_string())); + return JS::js_undefined(); + } + + if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) { + auto group = arguments.get(); + begin_group(MUST(String::formatted("{}", styling, escape_html_entities(group.label))), log_level == JS::Console::LogLevel::Group); + return JS::js_undefined(); + } + + auto output = TRY(generically_format_values(arguments.get>())); + m_console->output_debug_message(log_level, output); + + StringBuilder html; + switch (log_level) { + case JS::Console::LogLevel::Debug: + html.appendff("(d) "sv, styling); + break; + case JS::Console::LogLevel::Error: + html.appendff("(e) "sv, styling); + break; + case JS::Console::LogLevel::Info: + html.appendff("(i) "sv, styling); + break; + case JS::Console::LogLevel::Log: + html.appendff(" "sv, styling); + break; + case JS::Console::LogLevel::Warn: + case JS::Console::LogLevel::CountReset: + html.appendff("(w) "sv, styling); + break; + default: + html.appendff(""sv, styling); + break; + } + + html.append(escape_html_entities(output)); + html.append(""sv); + print_html(MUST(html.to_string())); + + return JS::js_undefined(); +} + +} diff --git a/Services/WebContent/InspectorConsoleClient.h b/Services/WebContent/InspectorConsoleClient.h new file mode 100644 index 0000000000..c5985fae06 --- /dev/null +++ b/Services/WebContent/InspectorConsoleClient.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021, Brandon Scott + * Copyright (c) 2020, Hunter Salyer + * Copyright (c) 2021-2022, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace WebContent { + +class InspectorConsoleClient final : public WebContentConsoleClient { + GC_CELL(InspectorConsoleClient, WebContentConsoleClient); + GC_DECLARE_ALLOCATOR(InspectorConsoleClient); + +public: + static GC::Ref create(JS::Realm&, JS::Console&, PageClient&); + virtual ~InspectorConsoleClient() override; + +private: + InspectorConsoleClient(JS::Realm&, JS::Console&, PageClient&, ConsoleGlobalEnvironmentExtensions&); + + virtual void handle_result(JS::Value) override; + virtual void report_exception(JS::Error const&, bool) override; + virtual void send_messages(i32 start_index) override; + + virtual JS::ThrowCompletionOr printer(JS::Console::LogLevel log_level, PrinterArguments) override; + + virtual void add_css_style_to_current_message(StringView style) override + { + m_current_message_style.append(style); + m_current_message_style.append(';'); + } + + StringBuilder m_current_message_style; +}; + +} diff --git a/Services/WebContent/PageClient.cpp b/Services/WebContent/PageClient.cpp index 3d85231880..1b07c89d23 100644 --- a/Services/WebContent/PageClient.cpp +++ b/Services/WebContent/PageClient.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -33,6 +35,7 @@ namespace WebContent { static PageClient::UseSkiaPainter s_use_skia_painter = PageClient::UseSkiaPainter::GPUBackendIfAvailable; static bool s_is_headless { false }; +static bool s_devtools_enabled { false }; GC_DEFINE_ALLOCATOR(PageClient); @@ -51,6 +54,11 @@ void PageClient::set_is_headless(bool is_headless) s_is_headless = is_headless; } +void PageClient::set_devtools_enabled(bool devtools_enabled) +{ + s_devtools_enabled = devtools_enabled; +} + GC::Ref PageClient::create(JS::VM& vm, PageHost& page_host, u64 id) { return vm.heap().allocate(page_host, id); @@ -731,9 +739,13 @@ void PageClient::initialize_js_console(Web::DOM::Document& document) return; auto& realm = document.realm(); - auto console_object = realm.intrinsics().console_object(); - auto console_client = heap().allocate(console_object->console(), document.realm(), *this); + + GC::Ptr console_client; + if (s_devtools_enabled) + console_client = DevToolsConsoleClient::create(document.realm(), console_object->console(), *this); + else + console_client = InspectorConsoleClient::create(document.realm(), console_object->console(), *this); document.set_console_client(console_client); } diff --git a/Services/WebContent/PageClient.h b/Services/WebContent/PageClient.h index 09cd69e8b2..6e234060f3 100644 --- a/Services/WebContent/PageClient.h +++ b/Services/WebContent/PageClient.h @@ -37,6 +37,8 @@ public: virtual bool is_headless() const override; static void set_is_headless(bool); + static void set_devtools_enabled(bool); + virtual bool is_ready_to_paint() const override; virtual Web::Page& page() override { return *m_page; } diff --git a/Services/WebContent/WebContentConsoleClient.cpp b/Services/WebContent/WebContentConsoleClient.cpp index be032a2f90..6cc652aefc 100644 --- a/Services/WebContent/WebContentConsoleClient.cpp +++ b/Services/WebContent/WebContentConsoleClient.cpp @@ -7,17 +7,11 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include -#include #include #include #include -#include #include #include -#include #include #include #include @@ -26,12 +20,12 @@ namespace WebContent { GC_DEFINE_ALLOCATOR(WebContentConsoleClient); -WebContentConsoleClient::WebContentConsoleClient(JS::Console& console, JS::Realm& realm, PageClient& client) +WebContentConsoleClient::WebContentConsoleClient(JS::Realm& realm, JS::Console& console, PageClient& client, ConsoleGlobalEnvironmentExtensions& console_global_environment_extensions) : ConsoleClient(console) + , m_realm(realm) , m_client(client) + , m_console_global_environment_extensions(console_global_environment_extensions) { - auto& window = as(realm.global_object()); - m_console_global_environment_extensions = realm.create(realm, window); } WebContentConsoleClient::~WebContentConsoleClient() = default; @@ -39,15 +33,13 @@ WebContentConsoleClient::~WebContentConsoleClient() = default; void WebContentConsoleClient::visit_edges(JS::Cell::Visitor& visitor) { Base::visit_edges(visitor); + visitor.visit(m_realm); visitor.visit(m_client); visitor.visit(m_console_global_environment_extensions); } void WebContentConsoleClient::handle_input(StringView js_source) { - if (!m_console_global_environment_extensions) - return; - auto& settings = Web::HTML::relevant_settings_object(*m_console_global_environment_extensions); auto script = Web::HTML::ClassicScript::create("(console)", js_source, settings.realm(), settings.api_base_url()); @@ -57,22 +49,22 @@ void WebContentConsoleClient::handle_input(StringView js_source) auto result = script->run(Web::HTML::ClassicScript::RethrowErrors::No, with_scope); if (result.value().has_value()) { - m_console_global_environment_extensions->set_most_recent_result(result.value().value()); - print_html(JS::MarkupGenerator::html_from_value(*result.value()).release_value_but_fixme_should_propagate_errors()); + m_console_global_environment_extensions->set_most_recent_result(*result.value()); + handle_result(*result.value()); } } -void WebContentConsoleClient::report_exception(JS::Error const& exception, bool in_promise) -{ - print_html(JS::MarkupGenerator::html_from_error(exception, in_promise).release_value_but_fixme_should_propagate_errors()); -} - void WebContentConsoleClient::print_html(String const& line) { m_message_log.append({ .type = ConsoleOutput::Type::HTML, .data = line }); m_client->did_output_js_console_message(m_message_log.size() - 1); } +void WebContentConsoleClient::clear() +{ + clear_output(); +} + void WebContentConsoleClient::clear_output() { m_message_log.append({ .type = ConsoleOutput::Type::Clear, .data = String {} }); @@ -91,189 +83,4 @@ void WebContentConsoleClient::end_group() m_client->did_output_js_console_message(m_message_log.size() - 1); } -void WebContentConsoleClient::send_messages(i32 start_index) -{ - // FIXME: Cap the number of messages we send at once? - auto messages_to_send = m_message_log.size() - start_index; - if (messages_to_send < 1) { - // When the console is first created, it requests any messages that happened before - // then, by requesting with start_index=0. If we don't have any messages at all, that - // is still a valid request, and we can just ignore it. - if (start_index != 0) - m_client->console_peer_did_misbehave("Requested non-existent console message index."); - return; - } - - // FIXME: Replace with a single Vector of message structs - Vector message_types; - Vector messages; - message_types.ensure_capacity(messages_to_send); - messages.ensure_capacity(messages_to_send); - - for (size_t i = start_index; i < m_message_log.size(); i++) { - auto& message = m_message_log[i]; - switch (message.type) { - case ConsoleOutput::Type::HTML: - message_types.append("html"_string); - break; - case ConsoleOutput::Type::Clear: - message_types.append("clear"_string); - break; - case ConsoleOutput::Type::BeginGroup: - message_types.append("group"_string); - break; - case ConsoleOutput::Type::BeginGroupCollapsed: - message_types.append("groupCollapsed"_string); - break; - case ConsoleOutput::Type::EndGroup: - message_types.append("groupEnd"_string); - break; - } - - messages.append(message.data); - } - - m_client->did_get_js_console_messages(start_index, move(message_types), move(messages)); -} - -void WebContentConsoleClient::clear() -{ - clear_output(); -} - -// 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer -JS::ThrowCompletionOr WebContentConsoleClient::printer(JS::Console::LogLevel log_level, PrinterArguments arguments) -{ - auto styling = escape_html_entities(m_current_message_style.string_view()); - m_current_message_style.clear(); - - if (log_level == JS::Console::LogLevel::Table) { - auto& vm = m_console->realm().vm(); - - auto table_args = arguments.get>(); - auto& table = table_args.at(0).as_object(); - auto& columns = TRY(table.get(vm.names.columns)).as_array().indexed_properties(); - auto& rows = TRY(table.get(vm.names.rows)).as_array().indexed_properties(); - - StringBuilder html; - - html.appendff("
"); - html.appendff(""); - html.appendff(""); - html.appendff(""); - for (auto const& col : columns) { - auto index = col.index(); - auto value = columns.storage()->get(index).value().value; - html.appendff("", value); - } - - html.appendff(""); - html.appendff(""); - html.appendff(""); - - for (auto const& row : rows) { - auto row_index = row.index(); - auto& row_obj = rows.storage()->get(row_index).value().value.as_object(); - html.appendff(""); - - for (auto const& col : columns) { - auto col_index = col.index(); - auto col_name = columns.storage()->get(col_index).value().value; - - auto property_key = TRY(JS::PropertyKey::from_value(vm, col_name)); - auto cell = TRY(row_obj.get(property_key)); - html.appendff(""); - } - - html.appendff(""); - } - - html.appendff(""); - html.appendff("
{}
"); - if (TRY(cell.is_array(vm))) { - AllocatingMemoryStream stream; - JS::PrintContext ctx { vm, stream, true }; - TRY_OR_THROW_OOM(vm, stream.write_until_depleted(" "sv.bytes())); - TRY_OR_THROW_OOM(vm, JS::print(cell, ctx)); - auto output = TRY_OR_THROW_OOM(vm, String::from_stream(stream, stream.used_buffer_size())); - - auto size = cell.as_array().indexed_properties().array_like_size(); - html.appendff("
Array({}){}
", size, output); - - } else if (cell.is_object()) { - AllocatingMemoryStream stream; - JS::PrintContext ctx { vm, stream, true }; - TRY_OR_THROW_OOM(vm, stream.write_until_depleted(" "sv.bytes())); - TRY_OR_THROW_OOM(vm, JS::print(cell, ctx)); - auto output = TRY_OR_THROW_OOM(vm, String::from_stream(stream, stream.used_buffer_size())); - - html.appendff("
Object({{...}}){}
", output); - } else if (cell.is_function() || cell.is_constructor()) { - html.appendff("ƒ"); - } else if (!cell.is_undefined()) { - html.appendff("{}", cell); - } - html.appendff("
"); - html.appendff("
"); - print_html(MUST(html.to_string())); - - auto output = TRY(generically_format_values(table_args)); - m_console->output_debug_message(log_level, output); - - return JS::js_undefined(); - } - - if (log_level == JS::Console::LogLevel::Trace) { - auto trace = arguments.get(); - StringBuilder html; - if (!trace.label.is_empty()) - html.appendff("{}
", styling, escape_html_entities(trace.label)); - - html.append(""sv); - for (auto& function_name : trace.stack) - html.appendff("-> {}
", escape_html_entities(function_name)); - html.append("
"sv); - - print_html(MUST(html.to_string())); - return JS::js_undefined(); - } - - if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) { - auto group = arguments.get(); - begin_group(MUST(String::formatted("{}", styling, escape_html_entities(group.label))), log_level == JS::Console::LogLevel::Group); - return JS::js_undefined(); - } - - auto output = TRY(generically_format_values(arguments.get>())); - m_console->output_debug_message(log_level, output); - - StringBuilder html; - switch (log_level) { - case JS::Console::LogLevel::Debug: - html.appendff("(d) "sv, styling); - break; - case JS::Console::LogLevel::Error: - html.appendff("(e) "sv, styling); - break; - case JS::Console::LogLevel::Info: - html.appendff("(i) "sv, styling); - break; - case JS::Console::LogLevel::Log: - html.appendff(" "sv, styling); - break; - case JS::Console::LogLevel::Warn: - case JS::Console::LogLevel::CountReset: - html.appendff("(w) "sv, styling); - break; - default: - html.appendff(""sv, styling); - break; - } - - html.append(escape_html_entities(output)); - html.append(""sv); - print_html(MUST(html.to_string())); - - return JS::js_undefined(); -} } diff --git a/Services/WebContent/WebContentConsoleClient.h b/Services/WebContent/WebContentConsoleClient.h index 4635c99aa3..c302f8c43b 100644 --- a/Services/WebContent/WebContentConsoleClient.h +++ b/Services/WebContent/WebContentConsoleClient.h @@ -8,18 +8,16 @@ #pragma once -#include #include -#include #include #include +#include #include -#include #include namespace WebContent { -class WebContentConsoleClient final : public JS::ConsoleClient { +class WebContentConsoleClient : public JS::ConsoleClient { GC_CELL(WebContentConsoleClient, JS::ConsoleClient); GC_DECLARE_ALLOCATOR(WebContentConsoleClient); @@ -27,30 +25,27 @@ public: virtual ~WebContentConsoleClient() override; void handle_input(StringView js_source); - void send_messages(i32 start_index); - void report_exception(JS::Error const&, bool) override; -private: - WebContentConsoleClient(JS::Console&, JS::Realm&, PageClient&); + virtual void handle_result(JS::Value) = 0; + virtual void send_messages(i32 start_index) = 0; + +protected: + WebContentConsoleClient(JS::Realm&, JS::Console&, PageClient&, ConsoleGlobalEnvironmentExtensions&); virtual void visit_edges(JS::Cell::Visitor&) override; - virtual void clear() override; - virtual JS::ThrowCompletionOr printer(JS::Console::LogLevel log_level, PrinterArguments) override; - virtual void add_css_style_to_current_message(StringView style) override - { - m_current_message_style.append(style); - m_current_message_style.append(';'); - } - - GC::Ref m_client; - GC::Ptr m_console_global_environment_extensions; - - void clear_output(); void print_html(String const& line); + + virtual void clear() override; + void clear_output(); + void begin_group(String const& label, bool start_expanded); virtual void end_group() override; + GC::Ref m_realm; + GC::Ref m_client; + GC::Ref m_console_global_environment_extensions; + struct ConsoleOutput { enum class Type { HTML, @@ -63,8 +58,6 @@ private: String data; }; Vector m_message_log; - - StringBuilder m_current_message_style; }; } diff --git a/Services/WebContent/main.cpp b/Services/WebContent/main.cpp index 6f10b26c72..d676be0314 100644 --- a/Services/WebContent/main.cpp +++ b/Services/WebContent/main.cpp @@ -107,6 +107,7 @@ ErrorOr serenity_main(Main::Arguments arguments) bool collect_garbage_on_every_allocation = false; bool is_headless = false; bool disable_scrollbar_painting = false; + bool devtools = false; StringView echo_server_port_string_view {}; Core::ArgsParser args_parser; @@ -129,6 +130,7 @@ ErrorOr serenity_main(Main::Arguments arguments) args_parser.add_option(disable_scrollbar_painting, "Don't paint horizontal or vertical viewport scrollbars", "disable-scrollbar-painting"); args_parser.add_option(echo_server_port_string_view, "Echo server port used in test internals", "echo-server-port", 0, "echo_server_port"); args_parser.add_option(is_headless, "Report that the browser is running in headless mode", "headless"); + args_parser.add_option(devtools, "Report that the browser is running with Firefox DevTools support", "devtools"); args_parser.parse(arguments); @@ -155,6 +157,7 @@ ErrorOr serenity_main(Main::Arguments arguments) WebContent::PageClient::set_use_skia_painter(force_cpu_painting ? WebContent::PageClient::UseSkiaPainter::CPUBackend : WebContent::PageClient::UseSkiaPainter::GPUBackendIfAvailable); WebContent::PageClient::set_is_headless(is_headless); + WebContent::PageClient::set_devtools_enabled(devtools); if (enable_http_cache) { Web::Fetch::Fetching::g_http_cache_enabled = true;