LibWeb+LibWebView: Add a button to the Inspector to export its contents

When working on the Inspector's HTML, it's often kind of tricky to debug
when an element is styled / positioned incorrectly. We don't have a way
to inspect the Inspector itself.

This adds a button to the Inspector to export its HTML/CSS/JS contents
to the downloads directory. This allows for more easily testing changes,
especially by opening the exported HTML in another browser's dev tools.

We will ultimately likely remove this button (or make it hidden) by the
time we are production-ready. But it's quite useful for now.
This commit is contained in:
Timothy Flynn 2024-08-19 12:11:39 -04:00 committed by Andreas Kling
commit 3ec5c1941f
Notes: github-actions[bot] 2024-08-20 07:29:17 +00:00
15 changed files with 123 additions and 9 deletions

View file

@ -7,7 +7,12 @@
#include <AK/Base64.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/LexicalPath.h>
#include <AK/StringBuilder.h>
#include <LibCore/Directory.h>
#include <LibCore/File.h>
#include <LibCore/Resource.h>
#include <LibCore/StandardPaths.h>
#include <LibJS/MarkupGenerator.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWebView/InspectorClient.h>
@ -15,6 +20,9 @@
namespace WebView {
static constexpr auto INSPECTOR_CSS = "resource://ladybird/inspector.css"sv;
static constexpr auto INSPECTOR_JS = "resource://ladybird/inspector.js"sv;
static ErrorOr<JsonValue> parse_json_tree(StringView json)
{
auto parsed_tree = TRY(JsonValue::from_string(json));
@ -179,6 +187,47 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple
m_content_web_view.js_console_input(script.to_byte_string());
};
m_inspector_web_view.on_inspector_exported_inspector_html = [this](String const& html) {
auto inspector_path = LexicalPath::join(Core::StandardPaths::downloads_directory(), "inspector"sv);
if (auto result = Core::Directory::create(inspector_path, Core::Directory::CreateDirectories::Yes); result.is_error()) {
append_console_warning(MUST(String::formatted("Unable to create {}: {}", inspector_path, result.error())));
return;
}
auto export_file = [&](auto name, auto const& contents) {
auto path = inspector_path.append(name);
auto file = Core::File::open(path.string(), Core::File::OpenMode::Write);
if (file.is_error()) {
append_console_warning(MUST(String::formatted("Unable to open {}: {}", path, file.error())));
return false;
}
if (auto result = file.value()->write_until_depleted(contents); result.is_error()) {
append_console_warning(MUST(String::formatted("Unable to save {}: {}", path, result.error())));
return false;
}
return true;
};
auto inspector_css = MUST(Core::Resource::load_from_uri(INSPECTOR_CSS));
auto inspector_js = MUST(Core::Resource::load_from_uri(INSPECTOR_JS));
auto inspector_html = MUST(html.replace(INSPECTOR_CSS, "inspector.css"sv, ReplaceMode::All));
inspector_html = MUST(inspector_html.replace(INSPECTOR_JS, "inspector.js"sv, ReplaceMode::All));
if (!export_file("inspector.html"sv, inspector_html))
return;
if (!export_file("inspector.css"sv, inspector_css->data()))
return;
if (!export_file("inspector.js"sv, inspector_js->data()))
return;
append_console_message(MUST(String::formatted("Exported Inspector files to {}", inspector_path)));
};
load_inspector();
}
@ -359,18 +408,22 @@ void InspectorClient::load_inspector()
builder.append(HTML_HIGHLIGHTER_STYLE);
builder.append(R"~~~(
builder.appendff(R"~~~(
</style>
<link href="resource://ladybird/inspector.css" rel="stylesheet" />
<link href="{}" rel="stylesheet" />
</head>
<body>
<div class="split-view">
<div id="inspector-top" class="split-view-container" style="height: 60%">
<div class="tab-controls-container">
<div class="global-controls"></div>
<div class="tab-controls">
<button id="dom-tree-button" onclick="selectTopTab(this, 'dom-tree')">DOM Tree</button>
<button id="accessibility-tree-button" onclick="selectTopTab(this, 'accessibility-tree')">Accessibility Tree</button>
</div>
<div class="global-controls">
<button id="export-inspector-button" title="Export the Inspector to an HTML file" onclick="inspector.exportInspector()"></button>
</div>
</div>
<div id="dom-tree" class="tab-content html"></div>
<div id="accessibility-tree" class="tab-content"></div>
@ -384,6 +437,7 @@ void InspectorClient::load_inspector()
</div>
<div id="inspector-bottom" class="split-view-container" style="height: calc(40% - 5px)">
<div class="tab-controls-container">
<div class="global-controls"></div>
<div class="tab-controls">
<button id="console-button" onclick="selectBottomTab(this, 'console')">Console</button>
<button id="computed-style-button" onclick="selectBottomTab(this, 'computed-style')">Computed Style</button>
@ -391,6 +445,7 @@ void InspectorClient::load_inspector()
<button id="custom-properties-button" onclick="selectBottomTab(this, 'custom-properties')">Custom Properties</button>
<button id="font-button" onclick="selectBottomTab(this, 'fonts')">Fonts</button>
</div>
<div class="global-controls"></div>
</div>
<div id="console" class="tab-content">
<div class="console">
@ -402,7 +457,8 @@ void InspectorClient::load_inspector()
</div>
</div>
</div>
)~~~"sv);
)~~~",
INSPECTOR_CSS);
auto generate_property_table = [&](auto name) {
builder.appendff(R"~~~(
@ -435,14 +491,15 @@ void InspectorClient::load_inspector()
</div>
)~~~"sv);
builder.append(R"~~~(
builder.appendff(R"~~~(
</div>
</div>
<script type="text/javascript" src="resource://ladybird/inspector.js"></script>
<script type="text/javascript" src="{}"></script>
</body>
</html>
)~~~"sv);
)~~~",
INSPECTOR_JS);
m_inspector_web_view.load_html(builder.string_view());
}