Browser+WebContent+LibJS: Support %c specifiers in Console.log()

...and the other Console methods.

This lets you apply styling to a log message or any other text that
passes through the Console `Formatter` operation.

We store the CSS on the ConsoleClient instead of passing it along with
the rest of the message, since I couldn't figure out a nice way of
doing that, as Formatter has to return JS::Values. This way isn't nice,
and has a risk of forgetting to clear the style and having it apply to
subsequent messages, but it works.

This is only supported in the Browser for now. REPL support would
require parsing the CSS and figuring out the relevant ANSI codes. We
also don't filter this styling at all, so you can `position: absolute`
and `transform: translate(...)` all you want, which is less than
ideal.
This commit is contained in:
Sam Atkins 2022-09-20 18:23:04 +01:00 committed by Sam Atkins
parent a875713f2e
commit 7a2da4cabf
Notes: sideshowbarker 2024-07-17 06:45:03 +09:00
5 changed files with 23 additions and 11 deletions

View file

@ -199,7 +199,7 @@ void ConsoleWidget::begin_group(StringView label, bool start_expanded)
var group = document.createElement("details");
group.id = "group_{}";
var label = document.createElement("summary");
label.innerText = ")~~~",
label.innerHTML = ")~~~",
group.id);
builder.append_escaped_for_json(label);
builder.append(R"~~~(";

View file

@ -615,8 +615,7 @@ ThrowCompletionOr<MarkedVector<Value>> ConsoleClient::formatter(MarkedVector<Val
// 6. TODO: process %c
else if (specifier == "%c"sv) {
// NOTE: This has no spec yet. `%c` specifiers treat the argument as CSS styling for the log message.
// For now, we'll just consume the specifier and the argument.
// FIXME: Actually style the message somehow.
add_css_style_to_current_message(TRY(current.to_string(vm)));
converted = js_string(vm, "");
}

View file

@ -108,6 +108,8 @@ public:
ThrowCompletionOr<MarkedVector<Value>> formatter(MarkedVector<Value> const& args);
virtual ThrowCompletionOr<Value> printer(Console::LogLevel log_level, PrinterArguments) = 0;
virtual void add_css_style_to_current_message(StringView) {};
virtual void clear() = 0;
virtual void end_group() = 0;

View file

@ -143,11 +143,14 @@ void WebContentConsoleClient::clear()
// 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer
JS::ThrowCompletionOr<JS::Value> 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::Trace) {
auto trace = arguments.get<JS::Console::Trace>();
StringBuilder html;
if (!trace.label.is_empty())
html.appendff("<span class='title'>{}</span><br>", escape_html_entities(trace.label));
html.appendff("<span class='title' style='{}'>{}</span><br>", styling, escape_html_entities(trace.label));
html.append("<span class='trace'>"sv);
for (auto& function_name : trace.stack)
@ -160,7 +163,7 @@ JS::ThrowCompletionOr<JS::Value> WebContentConsoleClient::printer(JS::Console::L
if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) {
auto group = arguments.get<JS::Console::Group>();
begin_group(group.label, log_level == JS::Console::LogLevel::Group);
begin_group(String::formatted("<span style='{}'>{}</span>", styling, escape_html_entities(group.label)), log_level == JS::Console::LogLevel::Group);
return JS::js_undefined();
}
@ -170,23 +173,23 @@ JS::ThrowCompletionOr<JS::Value> WebContentConsoleClient::printer(JS::Console::L
StringBuilder html;
switch (log_level) {
case JS::Console::LogLevel::Debug:
html.append("<span class=\"debug\">(d) "sv);
html.appendff("<span class=\"debug\" style=\"{}\">(d) "sv, styling);
break;
case JS::Console::LogLevel::Error:
html.append("<span class=\"error\">(e) "sv);
html.appendff("<span class=\"error\" style=\"{}\">(e) "sv, styling);
break;
case JS::Console::LogLevel::Info:
html.append("<span class=\"info\">(i) "sv);
html.appendff("<span class=\"info\" style=\"{}\">(i) "sv, styling);
break;
case JS::Console::LogLevel::Log:
html.append("<span class=\"log\"> "sv);
html.appendff("<span class=\"log\" style=\"{}\"> "sv, styling);
break;
case JS::Console::LogLevel::Warn:
case JS::Console::LogLevel::CountReset:
html.append("<span class=\"warn\">(w) "sv);
html.appendff("<span class=\"warn\" style=\"{}\">(w) "sv, styling);
break;
default:
html.append("<span>"sv);
html.appendff("<span style=\"{}\">"sv, styling);
break;
}

View file

@ -27,6 +27,12 @@ private:
virtual void clear() override;
virtual JS::ThrowCompletionOr<JS::Value> 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(';');
}
ConnectionFromClient& m_client;
WeakPtr<JS::Realm> m_realm;
JS::Handle<ConsoleGlobalObject> m_console_global_object;
@ -48,6 +54,8 @@ private:
String data;
};
Vector<ConsoleOutput> m_message_log;
StringBuilder m_current_message_style;
};
}