LibWebView+UI: Generate the application debug menu

By migrating the debug menu to LibWebView, the AppKit and Qt UIs are now
in sync - the AppKit UI was previously missing some actions.

Further, this inadvertently fixes bugs around applying debug settings to
new web views, especially across site-isolated processes. We were
previously not applying settings appropriately; this now "just works" in
the LibWebView infra.
This commit is contained in:
Timothy Flynn 2025-09-03 09:00:52 -04:00 committed by Tim Flynn
parent 5d8d9b337a
commit 9c99c48f47
Notes: github-actions[bot] 2025-09-11 18:25:07 +00:00
15 changed files with 212 additions and 632 deletions

View file

@ -14,6 +14,7 @@
#include <LibFileSystem/FileSystem.h>
#include <LibImageDecoderClient/Client.h>
#include <LibWeb/CSS/PropertyID.h>
#include <LibWeb/Loader/UserAgent.h>
#include <LibWebView/Application.h>
#include <LibWebView/CookieJar.h>
#include <LibWebView/Database.h>
@ -613,6 +614,37 @@ void Application::display_error_dialog(StringView error_message) const
void Application::initialize_actions()
{
auto debug_request = [this](auto request) {
return [this, request]() {
if (auto view = active_web_view(); view.has_value())
view->debug_request(request);
};
};
auto check = [](auto& action, auto request) {
return [&action, request]() {
ViewImplementation::for_each_view([checked = action->checked(), request](ViewImplementation& view) {
view.debug_request(request, checked ? "on"sv : "off"sv);
return IterationDecision::Continue;
});
};
};
auto add_spoofed_value = [](auto& menu, auto name, auto value, auto& cached_value, auto request) {
auto action = Action::create_checkable(name, ActionID::SpoofUserAgent, [value, &cached_value, request]() {
cached_value = value;
ViewImplementation::for_each_view([&](ViewImplementation& view) {
view.debug_request(request, cached_value);
view.debug_request("clear-cache"sv); // Clear the cache to ensure requests are re-done with the new value.
return IterationDecision::Continue;
});
});
action->set_checked(value == cached_value);
menu->add_action(move(action));
};
m_reload_action = Action::create("Reload"sv, ActionID::Reload, [this]() {
if (auto view = active_web_view(); view.has_value())
view->reload();
@ -635,6 +667,78 @@ void Application::initialize_actions()
if (auto view = active_web_view(); view.has_value())
view->get_source();
});
m_debug_menu = Menu::create("Debug"sv);
m_debug_menu->add_action(Action::create("Dump Session History Tree"sv, ActionID::DumpSessionHistoryTree, debug_request("dump-session-history"sv)));
m_debug_menu->add_action(Action::create("Dump DOM Tree"sv, ActionID::DumpDOMTree, debug_request("dump-dom-tree"sv)));
m_debug_menu->add_action(Action::create("Dump Layout Tree"sv, ActionID::DumpLayoutTree, debug_request("dump-layout-tree"sv)));
m_debug_menu->add_action(Action::create("Dump Paint Tree"sv, ActionID::DumpPaintTree, debug_request("dump-paint-tree"sv)));
m_debug_menu->add_action(Action::create("Dump Stacking Context Tree"sv, ActionID::DumpStackingContextTree, debug_request("dump-stacking-context-tree"sv)));
m_debug_menu->add_action(Action::create("Dump Display List"sv, ActionID::DumpDisplayList, debug_request("dump-display-list"sv)));
m_debug_menu->add_action(Action::create("Dump Style Sheets"sv, ActionID::DumpStyleSheets, debug_request("dump-style-sheets"sv)));
m_debug_menu->add_action(Action::create("Dump All Resolved Styles"sv, ActionID::DumpStyles, debug_request("dump-all-resolved-styles"sv)));
m_debug_menu->add_action(Action::create("Dump CSS Errors"sv, ActionID::DumpCSSErrors, debug_request("dump-all-css-errors"sv)));
m_debug_menu->add_action(Action::create("Dump Cookies"sv, ActionID::DumpCookies, [this]() { m_cookie_jar->dump_cookies(); }));
m_debug_menu->add_action(Action::create("Dump Local Storage"sv, ActionID::DumpLocalStorage, debug_request("dump-local-storage"sv)));
m_debug_menu->add_action(Action::create("Dump GC graph"sv, ActionID::DumpGCGraph, [this]() {
if (auto view = active_web_view(); view.has_value()) {
auto gc_graph_path = view->dump_gc_graph();
warnln("\033[33;1mDumped GC-graph into {}\033[0m", gc_graph_path);
}
}));
m_debug_menu->add_separator();
m_show_line_box_borders_action = Action::create_checkable("Show Line Box Borders"sv, ActionID::ShowLineBoxBorders, check(m_show_line_box_borders_action, "set-line-box-borders"sv));
m_debug_menu->add_action(*m_show_line_box_borders_action);
m_debug_menu->add_separator();
m_debug_menu->add_action(Action::create("Collect Garbage"sv, ActionID::CollectGarbage, debug_request("collect-garbage"sv)));
m_debug_menu->add_action(Action::create("Clear Cache"sv, ActionID::ClearCache, debug_request("clear-cache"sv)));
m_debug_menu->add_action(Action::create("Clear All Cookies"sv, ActionID::ClearCookies, [this]() { m_cookie_jar->clear_all_cookies(); }));
m_debug_menu->add_separator();
auto spoof_user_agent_menu = Menu::create_group("Spoof User Agent"sv);
m_user_agent_string = m_web_content_options.user_agent_preset.has_value()
? *WebView::user_agents.get(*m_web_content_options.user_agent_preset)
: Web::default_user_agent;
add_spoofed_value(spoof_user_agent_menu, "Disabled"sv, Web::default_user_agent, m_user_agent_string, "spoof-user-agent"sv);
for (auto const& user_agent : WebView::user_agents)
add_spoofed_value(spoof_user_agent_menu, user_agent.key, user_agent.value, m_user_agent_string, "spoof-user-agent"sv);
auto navigator_compatibility_mode_menu = Menu::create_group("Navigator Compatibility Mode"sv);
m_navigator_compatibility_mode = "chrome"sv;
add_spoofed_value(navigator_compatibility_mode_menu, "Chrome"sv, "chrome"sv, m_navigator_compatibility_mode, "navigator-compatibility-mode"sv);
add_spoofed_value(navigator_compatibility_mode_menu, "Gecko"sv, "gecko"sv, m_navigator_compatibility_mode, "navigator-compatibility-mode"sv);
add_spoofed_value(navigator_compatibility_mode_menu, "WebKit"sv, "webkit"sv, m_navigator_compatibility_mode, "navigator-compatibility-mode"sv);
m_debug_menu->add_submenu(move(spoof_user_agent_menu));
m_debug_menu->add_submenu(move(navigator_compatibility_mode_menu));
m_debug_menu->add_separator();
m_enable_scripting_action = Action::create_checkable("Enable Scripting"sv, ActionID::EnableScripting, check(m_enable_scripting_action, "scripting"sv));
m_enable_scripting_action->set_checked(m_browser_options.disable_scripting == WebView::DisableScripting::No);
m_debug_menu->add_action(*m_enable_scripting_action);
m_enable_content_filtering_action = Action::create_checkable("Enable Content Filtering"sv, ActionID::EnableContentFiltering, check(m_enable_content_filtering_action, "content-filtering"sv));
m_enable_content_filtering_action->set_checked(true);
m_debug_menu->add_action(*m_enable_content_filtering_action);
m_block_pop_ups_action = Action::create_checkable("Block Pop-ups"sv, ActionID::BlockPopUps, check(m_block_pop_ups_action, "block-pop-ups"sv));
m_block_pop_ups_action->set_checked(m_browser_options.allow_popups == AllowPopups::No);
m_debug_menu->add_action(*m_block_pop_ups_action);
}
void Application::apply_view_options(Badge<ViewImplementation>, ViewImplementation& view)
{
view.debug_request("set-line-box-borders"sv, m_show_line_box_borders_action->checked() ? "on"sv : "off"sv);
view.debug_request("scripting"sv, m_enable_scripting_action->checked() ? "on"sv : "off"sv);
view.debug_request("content-filtering"sv, m_enable_content_filtering_action->checked() ? "on"sv : "off"sv);
view.debug_request("block-pop-ups"sv, m_block_pop_ups_action->checked() ? "on"sv : "off"sv);
view.debug_request("spoof-user-agent"sv, m_user_agent_string);
view.debug_request("navigator-compatibility-mode"sv, m_navigator_compatibility_mode);
}
ErrorOr<Application::DevtoolsState> Application::toggle_devtools_enabled()

View file

@ -74,6 +74,10 @@ public:
Action& select_all_action() { return *m_select_all_action; }
Action& view_source_action() { return *m_view_source_action; }
Menu& debug_menu() { return *m_debug_menu; }
void apply_view_options(Badge<ViewImplementation>, ViewImplementation&);
enum class DevtoolsState {
Disabled,
Enabled,
@ -166,6 +170,14 @@ private:
RefPtr<Action> m_select_all_action;
RefPtr<Action> m_view_source_action;
RefPtr<Menu> m_debug_menu;
RefPtr<Action> m_show_line_box_borders_action;
RefPtr<Action> m_enable_scripting_action;
RefPtr<Action> m_enable_content_filtering_action;
RefPtr<Action> m_block_pop_ups_action;
StringView m_user_agent_string;
StringView m_navigator_compatibility_mode;
#if defined(AK_OS_MACOS)
OwnPtr<MachPortServer> m_mach_port_server;
#endif

View file

@ -52,6 +52,28 @@ enum class ActionID {
UnmuteMedia,
ToggleMediaControlsState,
ToggleMediaLoopState,
DumpSessionHistoryTree,
DumpDOMTree,
DumpLayoutTree,
DumpPaintTree,
DumpStackingContextTree,
DumpDisplayList,
DumpStyleSheets,
DumpStyles,
DumpCSSErrors,
DumpCookies,
DumpLocalStorage,
DumpGCGraph,
ShowLineBoxBorders,
CollectGarbage,
ClearCache,
ClearCookies,
SpoofUserAgent,
NavigatorCompatibilityMode,
EnableScripting,
EnableContentFiltering,
BlockPopUps,
};
class WEBVIEW_API Action

View file

@ -588,11 +588,7 @@ void ViewImplementation::initialize_client(CreateNewClient create_new_client)
if (auto webdriver_content_ipc_path = Application::browser_options().webdriver_content_ipc_path; webdriver_content_ipc_path.has_value())
client().async_connect_to_webdriver(m_client_state.page_index, *webdriver_content_ipc_path);
if (Application::browser_options().allow_popups == AllowPopups::Yes)
client().async_debug_request(m_client_state.page_index, "block-pop-ups"sv, "off"sv);
if (auto const& user_agent_preset = Application::web_content_options().user_agent_preset; user_agent_preset.has_value())
client().async_debug_request(m_client_state.page_index, "spoof-user-agent"sv, *user_agents.get(*user_agent_preset));
Application::the().apply_view_options({}, *this);
default_zoom_level_factor_changed();
languages_changed();