diff --git a/Userland/Libraries/LibJS/Console.cpp b/Userland/Libraries/LibJS/Console.cpp index ae5d2bfb7db..3866897c27f 100644 --- a/Userland/Libraries/LibJS/Console.cpp +++ b/Userland/Libraries/LibJS/Console.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2020, Emanuele Torre * Copyright (c) 2020-2021, Linus Groh + * Copyright (c) 2021, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -20,53 +21,58 @@ VM& Console::vm() return m_global_object.vm(); } -Value Console::debug() +// 1.1.3. debug(...data), https://console.spec.whatwg.org/#debug +ThrowCompletionOr Console::debug() { -#ifdef __serenity__ - dbgln("\033[32;1m(js debug)\033[0m {}", vm().join_arguments()); -#endif - if (m_client) - return m_client->debug(); + // 1. Perform Logger("debug", data). + if (m_client) { + auto data = vm_arguments(); + return m_client->logger(LogLevel::Debug, data); + } return js_undefined(); } -Value Console::error() +// 1.1.4. error(...data), https://console.spec.whatwg.org/#error +ThrowCompletionOr Console::error() { -#ifdef __serenity__ - dbgln("\033[32;1m(js error)\033[0m {}", vm().join_arguments()); -#endif - if (m_client) - return m_client->error(); + // 1. Perform Logger("error", data). + if (m_client) { + auto data = vm_arguments(); + return m_client->logger(LogLevel::Error, data); + } return js_undefined(); } -Value Console::info() +// 1.1.5. info(...data), https://console.spec.whatwg.org/#info +ThrowCompletionOr Console::info() { -#ifdef __serenity__ - dbgln("\033[32;1m(js info)\033[0m {}", vm().join_arguments()); -#endif - if (m_client) - return m_client->info(); + // 1. Perform Logger("info", data). + if (m_client) { + auto data = vm_arguments(); + return m_client->logger(LogLevel::Info, data); + } return js_undefined(); } -Value Console::log() +// 1.1.6. log(...data), https://console.spec.whatwg.org/#log +ThrowCompletionOr Console::log() { -#ifdef __serenity__ - dbgln("\033[32;1m(js log)\033[0m {}", vm().join_arguments()); -#endif - if (m_client) - return m_client->log(); + // 1. Perform Logger("log", data). + if (m_client) { + auto data = vm_arguments(); + return m_client->logger(LogLevel::Log, data); + } return js_undefined(); } -Value Console::warn() +// 1.1.9. warn(...data), https://console.spec.whatwg.org/#warn +ThrowCompletionOr Console::warn() { -#ifdef __serenity__ - dbgln("\033[32;1m(js warn)\033[0m {}", vm().join_arguments()); -#endif - if (m_client) - return m_client->warn(); + // 1. Perform Logger("warn", data). + if (m_client) { + auto data = vm_arguments(); + return m_client->logger(LogLevel::Warn, data); + } return js_undefined(); } @@ -127,6 +133,42 @@ bool Console::counter_reset(String label) return true; } +Vector Console::vm_arguments() +{ + Vector arguments; + arguments.ensure_capacity(vm().argument_count()); + for (size_t i = 0; i < vm().argument_count(); ++i) { + arguments.append(vm().argument(i)); + } + return arguments; +} + +void Console::output_debug_message([[maybe_unused]] LogLevel log_level, [[maybe_unused]] String output) const +{ +#ifdef __serenity__ + switch (log_level) { + case JS::Console::LogLevel::Debug: + dbgln("\033[32;1m(js debug)\033[0m {}", output); + break; + case JS::Console::LogLevel::Error: + dbgln("\033[32;1m(js error)\033[0m {}", output); + break; + case JS::Console::LogLevel::Info: + dbgln("\033[32;1m(js info)\033[0m {}", output); + break; + case JS::Console::LogLevel::Log: + dbgln("\033[32;1m(js log)\033[0m {}", output); + break; + case JS::Console::LogLevel::Warn: + dbgln("\033[32;1m(js warn)\033[0m {}", output); + break; + default: + dbgln("\033[32;1m(js)\033[0m {}", output); + break; + } +#endif +} + VM& ConsoleClient::vm() { return global_object().vm(); @@ -142,4 +184,45 @@ Vector ConsoleClient::get_trace() const return trace; } +// 2.1. Logger(logLevel, args), https://console.spec.whatwg.org/#logger +ThrowCompletionOr ConsoleClient::logger(Console::LogLevel log_level, Vector& args) +{ + auto& global_object = this->global_object(); + + // 1. If args is empty, return. + if (args.is_empty()) + return js_undefined(); + + // 2. Let first be args[0]. + auto first = args[0]; + + // 3. Let rest be all elements following first in args. + size_t rest_size = args.size() - 1; + + // 4. If rest is empty, perform Printer(logLevel, « first ») and return. + if (rest_size == 0) { + auto first_as_vector = Vector { first }; + return printer(log_level, first_as_vector); + } + + // 5. If first does not contain any format specifiers, perform Printer(logLevel, args). + if (!TRY(first.to_string(global_object)).contains('%')) { + TRY(printer(log_level, args)); + } else { + // 6. Otherwise, perform Printer(logLevel, Formatter(args)). + auto formatted = TRY(formatter(args)); + TRY(printer(log_level, formatted)); + } + + // 7. Return undefined. + return js_undefined(); +} + +// 2.2. Formatter(args), https://console.spec.whatwg.org/#formatter +ThrowCompletionOr> ConsoleClient::formatter(Vector& args) +{ + // TODO: Actually implement formatting + return args; +} + } diff --git a/Userland/Libraries/LibJS/Console.h b/Userland/Libraries/LibJS/Console.h index 0fb757f7f7b..9461301a983 100644 --- a/Userland/Libraries/LibJS/Console.h +++ b/Userland/Libraries/LibJS/Console.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Emanuele Torre + * Copyright (c) 2021, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,17 +10,39 @@ #include #include #include +#include #include +#include namespace JS { class ConsoleClient; +// https://console.spec.whatwg.org class Console { AK_MAKE_NONCOPYABLE(Console); AK_MAKE_NONMOVABLE(Console); public: + // These are not really levels, but that's the term used in the spec. + enum class LogLevel { + Assert, + Count, + CountReset, + Debug, + Dir, + DirXML, + Error, + Group, + GroupCollapsed, + Info, + Log, + TimeEnd, + TimeLog, + Trace, + Warn, + }; + explicit Console(GlobalObject&); void set_client(ConsoleClient& client) { m_client = &client; } @@ -28,15 +51,16 @@ public: const GlobalObject& global_object() const { return m_global_object; } VM& vm(); + Vector vm_arguments(); HashMap& counters() { return m_counters; } const HashMap& counters() const { return m_counters; } - Value debug(); - Value error(); - Value info(); - Value log(); - Value warn(); + ThrowCompletionOr debug(); + ThrowCompletionOr error(); + ThrowCompletionOr info(); + ThrowCompletionOr log(); + ThrowCompletionOr warn(); Value clear(); Value trace(); Value count(); @@ -46,6 +70,8 @@ public: unsigned counter_increment(String label); bool counter_reset(String label); + void output_debug_message(LogLevel log_level, String output) const; + private: GlobalObject& m_global_object; ConsoleClient* m_client { nullptr }; @@ -60,11 +86,10 @@ public: { } - virtual Value debug() = 0; - virtual Value error() = 0; - virtual Value info() = 0; - virtual Value log() = 0; - virtual Value warn() = 0; + ThrowCompletionOr logger(Console::LogLevel log_level, Vector& args); + ThrowCompletionOr> formatter(Vector& args); + virtual ThrowCompletionOr printer(Console::LogLevel log_level, Vector&) = 0; + virtual Value clear() = 0; virtual Value trace() = 0; virtual Value count() = 0; diff --git a/Userland/Services/WebContent/WebContentConsoleClient.cpp b/Userland/Services/WebContent/WebContentConsoleClient.cpp index 54a0cc6ab98..49662485080 100644 --- a/Userland/Services/WebContent/WebContentConsoleClient.cpp +++ b/Userland/Services/WebContent/WebContentConsoleClient.cpp @@ -115,56 +115,6 @@ void WebContentConsoleClient::send_messages(i32 start_index) m_client.async_did_get_js_console_messages(start_index, message_types, messages); } -JS::Value WebContentConsoleClient::log() -{ - print_html(escape_html_entities(vm().join_arguments())); - return JS::js_undefined(); -} - -JS::Value WebContentConsoleClient::info() -{ - StringBuilder html; - html.append(""); - html.append("(i) "); - html.append(escape_html_entities(vm().join_arguments())); - html.append(""); - print_html(html.string_view()); - return JS::js_undefined(); -} - -JS::Value WebContentConsoleClient::debug() -{ - StringBuilder html; - html.append(""); - html.append("(d) "); - html.append(escape_html_entities(vm().join_arguments())); - html.append(""); - print_html(html.string_view()); - return JS::js_undefined(); -} - -JS::Value WebContentConsoleClient::warn() -{ - StringBuilder html; - html.append(""); - html.append("(w) "); - html.append(escape_html_entities(vm().join_arguments())); - html.append(""); - print_html(html.string_view()); - return JS::js_undefined(); -} - -JS::Value WebContentConsoleClient::error() -{ - StringBuilder html; - html.append(""); - html.append("(e) "); - html.append(escape_html_entities(vm().join_arguments())); - html.append(""); - print_html(html.string_view()); - return JS::js_undefined(); -} - JS::Value WebContentConsoleClient::clear() { clear_output(); @@ -225,4 +175,38 @@ JS::Value WebContentConsoleClient::assert_() return JS::js_undefined(); } +// 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer +JS::ThrowCompletionOr WebContentConsoleClient::printer(JS::Console::LogLevel log_level, Vector& arguments) +{ + auto output = String::join(" ", arguments); + m_console.output_debug_message(log_level, output); + + StringBuilder html; + switch (log_level) { + case JS::Console::LogLevel::Debug: + html.append("(d) "); + break; + case JS::Console::LogLevel::Error: + html.append("(e) "); + break; + case JS::Console::LogLevel::Info: + html.append("(i) "); + break; + case JS::Console::LogLevel::Log: + html.append(" "); + break; + case JS::Console::LogLevel::Warn: + html.append("(w) "); + break; + default: + html.append(""); + break; + } + + html.append(escape_html_entities(output)); + html.append(""); + print_html(html.string_view()); + return JS::js_undefined(); +} + } diff --git a/Userland/Services/WebContent/WebContentConsoleClient.h b/Userland/Services/WebContent/WebContentConsoleClient.h index 16df4bcf9e7..0373344a64c 100644 --- a/Userland/Services/WebContent/WebContentConsoleClient.h +++ b/Userland/Services/WebContent/WebContentConsoleClient.h @@ -24,16 +24,12 @@ public: void send_messages(i32 start_index); private: - virtual JS::Value log() override; - virtual JS::Value info() override; - virtual JS::Value debug() override; - virtual JS::Value warn() override; - virtual JS::Value error() override; virtual JS::Value clear() override; virtual JS::Value trace() override; virtual JS::Value count() override; virtual JS::Value count_reset() override; virtual JS::Value assert_() override; + virtual JS::ThrowCompletionOr printer(JS::Console::LogLevel log_level, Vector&) override; ClientConnection& m_client; WeakPtr m_interpreter; diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp index 85a24ae7e40..335ea81fe64 100644 --- a/Userland/Utilities/js.cpp +++ b/Userland/Utilities/js.cpp @@ -1122,36 +1122,6 @@ public: { } - virtual JS::Value log() override - { - js_outln("{}", vm().join_arguments()); - return JS::js_undefined(); - } - - virtual JS::Value info() override - { - js_outln("(i) {}", vm().join_arguments()); - return JS::js_undefined(); - } - - virtual JS::Value debug() override - { - js_outln("\033[36;1m{}\033[0m", vm().join_arguments()); - return JS::js_undefined(); - } - - virtual JS::Value warn() override - { - js_outln("\033[33;1m{}\033[0m", vm().join_arguments()); - return JS::js_undefined(); - } - - virtual JS::Value error() override - { - js_outln("\033[31;1m{}\033[0m", vm().join_arguments()); - return JS::js_undefined(); - } - virtual JS::Value clear() override { js_out("\033[3J\033[H\033[2J"); @@ -1202,6 +1172,34 @@ public: } return JS::js_undefined(); } + + virtual JS::ThrowCompletionOr printer(JS::Console::LogLevel log_level, Vector& arguments) override + { + auto output = String::join(" ", arguments); + m_console.output_debug_message(log_level, output); + + switch (log_level) { + case JS::Console::LogLevel::Debug: + js_outln("\033[36;1m{}\033[0m", output); + break; + case JS::Console::LogLevel::Error: + js_outln("\033[31;1m{}\033[0m", output); + break; + case JS::Console::LogLevel::Info: + js_outln("(i) {}", output); + break; + case JS::Console::LogLevel::Log: + js_outln("{}", output); + break; + case JS::Console::LogLevel::Warn: + js_outln("\033[33;1m{}\033[0m", output); + break; + default: + js_outln("{}", output); + break; + } + return JS::js_undefined(); + } }; ErrorOr serenity_main(Main::Arguments arguments)