1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 12:38:12 +00:00

LibJS: Create JS to HTML markup generator

The new JS::MarkupGenerator class can convert both a JS source string
and a JS Runtime Value into properly formatted HTML using the new
LibWeb System Palette css color values.

It makes more sense for this JS -> HTML process to occur in LibJS
so that it can be used elsewhere, namely Markdown code block syntax
highlighting. It also means the Browser can worry less about LibJS
implementation details.
This commit is contained in:
FalseHonesty 2020-05-25 15:24:46 -04:00 committed by Andreas Kling
parent b352a6b59d
commit 941b028ca3
6 changed files with 392 additions and 160 deletions

View file

@ -30,6 +30,7 @@
#include <LibGUI/JSSyntaxHighlighter.h>
#include <LibGUI/TextBox.h>
#include <LibJS/Interpreter.h>
#include <LibJS/MarkupGenerator.h>
#include <LibJS/Parser.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Date.h>
@ -93,16 +94,14 @@ ConsoleWidget::ConsoleWidget()
if (m_interpreter->exception()) {
StringBuilder output_html;
output_html.append("Uncaught exception: ");
print_value(m_interpreter->exception()->value(), output_html);
output_html.append(JS::MarkupGenerator::html_from_value(m_interpreter->exception()->value()));
print_html(output_html.string_view());
m_interpreter->clear_exception();
return;
}
StringBuilder output_html;
print_value(m_interpreter->last_value(), output_html);
print_html(output_html.string_view());
print_html(JS::MarkupGenerator::html_from_value(m_interpreter->last_value()));
};
}
@ -174,155 +173,6 @@ void ConsoleWidget::print_source_line(const StringView& source)
print_html(html.string_view());
}
void ConsoleWidget::print_value(JS::Value value, StringBuilder& output_html, HashTable<JS::Object*> seen_objects)
{
// FIXME: Support output highlighting
if (value.is_empty()) {
output_html.append("<span class=\"empty-object\">");
output_html.append("&lt;empty&gt;");
output_html.append("</span>");
return;
}
if (value.is_object()) {
if (seen_objects.contains(&value.as_object())) {
// FIXME: Maybe we should only do this for circular references,
// not for all reoccurring objects.
output_html.append("<span class=\"already-printed\">");
output_html.appendf("&lt;already printed Object %p&gt;", &value.as_object());
output_html.append("</span>");
return;
}
seen_objects.set(&value.as_object());
}
if (value.is_array())
return print_array(static_cast<const JS::Array&>(value.as_object()), output_html, seen_objects);
if (value.is_object()) {
auto& object = value.as_object();
if (object.is_function())
return print_function(object, output_html, seen_objects);
if (object.is_date())
return print_date(object, output_html, seen_objects);
if (object.is_error())
return print_error(object, output_html, seen_objects);
return print_object(object, output_html, seen_objects);
}
if (value.is_string())
output_html.append("<span class=\"js-string\">");
else if (value.is_number())
output_html.append("<span class=\"js-number\">");
else if (value.is_boolean())
output_html.append("<span class=\"js-boolean\">");
else if (value.is_null())
output_html.append("<span class=\"js-null\">");
else if (value.is_undefined())
output_html.append("<span class=\"js-undefined\">");
if (value.is_string())
output_html.append('"');
output_html.append(value.to_string_without_side_effects());
if (value.is_string())
output_html.append('"');
output_html.append("</span>");
}
void ConsoleWidget::print_array(const JS::Array& array, StringBuilder& html_output, HashTable<JS::Object*>& seen_objects)
{
html_output.append("<span class=\"js-array-open\">");
html_output.append("[ ");
html_output.append("</span>");
for (size_t i = 0; i < array.elements().size(); ++i) {
print_value(array.elements()[i], html_output, seen_objects);
if (i != array.elements().size() - 1) {
html_output.append("<span class=\"js-array-element-separator\">");
html_output.append(", ");
html_output.append("</span>");
}
}
html_output.append("<span class=\"js-array-close\">");
html_output.append(" ]");
html_output.append("</span>");
}
void ConsoleWidget::print_object(const JS::Object& object, StringBuilder& html_output, HashTable<JS::Object*>& seen_objects)
{
html_output.append("<span class=\"js-object-open\">");
html_output.append("{ ");
html_output.append("</span>");
for (size_t i = 0; i < object.elements().size(); ++i) {
if (object.elements()[i].is_empty())
continue;
html_output.append("<span class=\"js-object-element-index\">");
html_output.appendf("%zu", i);
html_output.append("</span>");
html_output.append(": ");
print_value(object.elements()[i], html_output, seen_objects);
if (i != object.elements().size() - 1) {
html_output.append("<span class=\"js-object-element-separator\">");
html_output.append(", ");
html_output.append("</span>");
}
}
if (!object.elements().is_empty() && object.shape().property_count()) {
html_output.append("<span class=\"js-object-element-separator\">");
html_output.append(", ");
html_output.append("</span>");
}
size_t index = 0;
for (auto& it : object.shape().property_table_ordered()) {
html_output.append("<span class=\"js-object-element-key\">");
html_output.appendf("\"%s\"", it.key.characters());
html_output.append("</span>");
html_output.append(": ");
print_value(object.get_direct(it.value.offset), html_output, seen_objects);
if (index != object.shape().property_count() - 1) {
html_output.append("<span class=\"js-object-element-separator\">");
html_output.append(", ");
html_output.append("</span>");
}
++index;
}
html_output.append("<span class=\"js-object-close\">");
html_output.append(" }");
html_output.append("</span>");
}
void ConsoleWidget::print_function(const JS::Object& function, StringBuilder& html_output, HashTable<JS::Object*>&)
{
html_output.append("<span class=\"js-function\">");
html_output.appendf("[%s]", function.class_name());
html_output.append("</span>");
}
void ConsoleWidget::print_date(const JS::Object& date, StringBuilder& html_output, HashTable<JS::Object*>&)
{
html_output.append("<span class=\"js-date\">");
html_output.appendf("Date %s", static_cast<const JS::Date&>(date).string().characters());
html_output.append("</span>");
}
void ConsoleWidget::print_error(const JS::Object& object, StringBuilder& html_output, HashTable<JS::Object*>&)
{
auto& error = static_cast<const JS::Error&>(object);
html_output.append("<span class=\"js-error-name\">");
html_output.appendf("[%s]", error.name().characters());
html_output.append("</span>");
if (!error.message().is_empty()) {
html_output.append("<span class=\"js-error-message\">");
html_output.appendf(": %s", error.message().characters());
html_output.append("</span>");
}
}
void ConsoleWidget::print_html(const StringView& line)
{
auto paragraph = create_element(m_console_output_container->document(), "p");