From 82b270a1542e620373cce2d8e1735ff0a10451df Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 24 May 2026 10:02:05 +0200 Subject: [PATCH] UI: Add WebContent PID debug preference Register debug.process.show_web_content_process_id and use it to append the active WebContent process ID to tab titles and tab tooltips in the Qt, Gtk, and AppKit frontends. Keep the stored page title unchanged so toggling the preference can refresh each tab without waiting for a new title update. --- Libraries/LibWebView/Settings.cpp | 10 +++++- Libraries/LibWebView/Settings.h | 1 + Libraries/LibWebView/ViewImplementation.h | 5 +-- UI/AppKit/Interface/Tab.mm | 43 ++++++++++++++++++++++- UI/Gtk/Tab.cpp | 40 ++++++++++++++++++--- UI/Gtk/Tab.h | 11 ++++-- UI/Qt/Tab.cpp | 24 +++++++++++-- UI/Qt/Tab.h | 10 ++++-- 8 files changed, 128 insertions(+), 16 deletions(-) diff --git a/Libraries/LibWebView/Settings.cpp b/Libraries/LibWebView/Settings.cpp index 0f0e6e752c..f7a197ebdb 100644 --- a/Libraries/LibWebView/Settings.cpp +++ b/Libraries/LibWebView/Settings.cpp @@ -60,7 +60,15 @@ static constexpr auto DNS_SETTINGS_KEY = "dnsSettings"sv; static constexpr auto CONFIG_VARIABLES_KEY = "configVariables"sv; -static Array(ConfigVariableID::Count)> const CONFIG_VARIABLE_DEFINITIONS {}; +static Array(ConfigVariableID::Count)> const CONFIG_VARIABLE_DEFINITIONS { { + { + .id = ConfigVariableID::ShowWebContentProcessIDInTabTitle, + .name = "debug.process.show_web_content_process_id"sv, + .title = "Show WebContent process ID in tab titles"sv, + .description = "Append the active WebContent process ID to each tab title and tooltip."sv, + .default_value = false, + }, +} }; ReadonlySpan config_variable_definitions() { diff --git a/Libraries/LibWebView/Settings.h b/Libraries/LibWebView/Settings.h index 5e6ad99de0..19ca31205f 100644 --- a/Libraries/LibWebView/Settings.h +++ b/Libraries/LibWebView/Settings.h @@ -44,6 +44,7 @@ enum class GlobalPrivacyControl { }; enum class ConfigVariableID : u8 { + ShowWebContentProcessIDInTabTitle, Count, }; diff --git a/Libraries/LibWebView/ViewImplementation.h b/Libraries/LibWebView/ViewImplementation.h index 034ff8b581..39a04dfd0c 100644 --- a/Libraries/LibWebView/ViewImplementation.h +++ b/Libraries/LibWebView/ViewImplementation.h @@ -277,6 +277,9 @@ public: Action& toggle_bookmark_action() { return *m_toggle_bookmark_action; } Action& reset_zoom_action() { return *m_reset_zoom_action; } + WebContentClient& client(); + WebContentClient const& client() const; + virtual Web::DevicePixelSize viewport_size() const = 0; virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const = 0; virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint content_position) const = 0; @@ -288,8 +291,6 @@ protected: ViewImplementation(); - WebContentClient& client(); - WebContentClient const& client() const; u64 page_id() const; void set_url(URL::URL); diff --git a/UI/AppKit/Interface/Tab.mm b/UI/AppKit/Interface/Tab.mm index 1e52d3bd0b..2698d169b1 100644 --- a/UI/AppKit/Interface/Tab.mm +++ b/UI/AppKit/Interface/Tab.mm @@ -4,12 +4,15 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include #include #include #include #include #include +#include #import #import @@ -28,6 +31,22 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800; static constexpr CGFloat const TAB_ICON_SIZE = 16; static constexpr NSUInteger const TAB_LOADING_SPINNER_SEGMENT_COUNT = 12; +class TabSettingsObserver final : public WebView::SettingsObserver { +public: + explicit TabSettingsObserver(Function callback) + : m_callback(move(callback)) + { + } + + virtual void config_variable_changed(WebView::ConfigVariableID variable) override + { + m_callback(variable); + } + +private: + Function m_callback; +}; + static NSImage* tab_loading_spinner_icon(NSUInteger frame) { auto* image = [NSImage imageWithSize:NSMakeSize(TAB_ICON_SIZE, TAB_ICON_SIZE) @@ -66,6 +85,7 @@ static NSImage* tab_loading_spinner_icon(NSUInteger frame) BOOL m_loading; NSUInteger m_loading_spinner_frame; __strong NSTimer* m_loading_spinner_timer; + OwnPtr m_settings_observer; } @property (nonatomic, strong) NSString* title; @@ -121,6 +141,15 @@ static NSImage* tab_loading_spinner_icon(NSUInteger frame) self.favicon = [Tab defaultFavicon]; self.title = @"New Tab"; + __weak Tab* weak_self = self; + m_settings_observer = make([weak_self](WebView::ConfigVariableID variable) { + if (variable != WebView::ConfigVariableID::ShowWebContentProcessIDInTabTitle) + return; + Tab* strong_self = weak_self; + if (strong_self == nil) + return; + [strong_self updateTabTitleAndFavicon]; + }); [self updateTabTitleAndFavicon]; [self setTitleVisibility:NSWindowTitleHidden]; @@ -193,6 +222,15 @@ static NSImage* tab_loading_spinner_icon(NSUInteger frame) return self.favicon; } +- (NSString*)displayTitle +{ + if (!WebView::Application::settings().config_variable_as_bool(WebView::ConfigVariableID::ShowWebContentProcessIDInTabTitle)) + return self.title; + + auto title = MUST(String::formatted("{} [{}]", Ladybird::ns_string_to_string(self.title), [[self web_view] view].client().pid())); + return Ladybird::string_to_ns_string(title); +} + - (void)updateLoadingSpinner { if (!m_loading) @@ -255,7 +293,8 @@ static NSImage* tab_loading_spinner_icon(NSUInteger frame) NSFontAttributeName : title_font }; - auto* title_attribute = [[NSAttributedString alloc] initWithString:self.title + auto* display_title = [self displayTitle]; + auto* title_attribute = [[NSAttributedString alloc] initWithString:display_title attributes:title_attributes]; auto* spacing_attribute = [[NSAttributedString alloc] initWithString:@" " @@ -267,6 +306,8 @@ static NSImage* tab_loading_spinner_icon(NSUInteger frame) [title_and_favicon appendAttributedString:title_attribute]; [[self tab] setAttributedTitle:title_and_favicon]; + if ([[self tab] respondsToSelector:@selector(setToolTip:)]) + [(id)[self tab] setToolTip:display_title]; } - (void)togglePageMuteState:(id)button diff --git a/UI/Gtk/Tab.cpp b/UI/Gtk/Tab.cpp index 72dd8979e9..7a8a857d04 100644 --- a/UI/Gtk/Tab.cpp +++ b/UI/Gtk/Tab.cpp @@ -8,8 +8,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -89,16 +91,20 @@ Tab::Tab(BrowserWindow& window, RefPtr parent_client, Tab::~Tab() = default; +void Tab::set_tab_page(AdwTabPage* page) +{ + m_tab_page = page; + update_tab_title(); +} + void Tab::setup_callbacks() { auto* root = GTK_WIDGET(m_web_view); m_view->on_title_change = [this](auto const& title) { - if (m_tab_page) { - auto utf8 = title.to_utf8(); - auto byte_str = ByteString(utf8.bytes_as_string_view()); - adw_tab_page_set_title(m_tab_page, byte_str.characters()); - } + auto utf8 = title.to_utf8(); + m_title = ByteString(utf8.bytes_as_string_view()); + update_tab_title(); }; m_view->on_url_change = [this](auto const& url) { @@ -273,6 +279,30 @@ void Tab::setup_callbacks() create_context_menu(*root, *m_view, m_view->media_context_menu()); } +ByteString Tab::tab_title() const +{ + if (!WebView::Application::settings().config_variable_as_bool(WebView::ConfigVariableID::ShowWebContentProcessIDInTabTitle)) + return m_title; + + return ByteString::formatted("{} [{}]", m_title, m_view->client().pid()); +} + +void Tab::update_tab_title() +{ + if (!m_tab_page) + return; + + auto title = tab_title(); + adw_tab_page_set_title(m_tab_page, title.characters()); + adw_tab_page_set_tooltip(m_tab_page, title.characters()); +} + +void Tab::config_variable_changed(WebView::ConfigVariableID variable) +{ + if (variable == WebView::ConfigVariableID::ShowWebContentProcessIDInTabTitle) + update_tab_title(); +} + void Tab::navigate(URL::URL const& url) { m_view->load(url); diff --git a/UI/Gtk/Tab.h b/UI/Gtk/Tab.h index 2f04fc2528..5f0b62c007 100644 --- a/UI/Gtk/Tab.h +++ b/UI/Gtk/Tab.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -24,11 +25,11 @@ namespace Ladybird { class BrowserWindow; class WebContentView; -class Tab { +class Tab : public WebView::SettingsObserver { public: Tab(BrowserWindow& window, URL::URL url = {}); Tab(BrowserWindow& window, WebView::WebContentClient& parent_client, u64 page_index); - ~Tab(); + virtual ~Tab() override; GtkWidget* widget() const { return GTK_WIDGET(m_web_view); } WebContentView& view() { return *m_view; } @@ -39,11 +40,14 @@ public: bool is_loading() const { return m_is_loading; } AdwTabPage* tab_page() const { return m_tab_page; } - void set_tab_page(AdwTabPage* page) { m_tab_page = page; } + void set_tab_page(AdwTabPage*); private: Tab(BrowserWindow& window, RefPtr parent_client, size_t page_index); void setup_callbacks(); + void update_tab_title(); + ByteString tab_title() const; + virtual void config_variable_changed(WebView::ConfigVariableID) override; void show_select_dropdown(Gfx::IntPoint content_position, i32 minimum_width, Vector items); BrowserWindow& m_window; @@ -53,6 +57,7 @@ private: AdwTabPage* m_tab_page { nullptr }; URL::URL m_initial_url; + ByteString m_title { "New Tab" }; GObjectPtr m_favicon; bool m_is_loading { false }; }; diff --git a/UI/Qt/Tab.cpp b/UI/Qt/Tab.cpp index f338ab94fd..a4b625b2a6 100644 --- a/UI/Qt/Tab.cpp +++ b/UI/Qt/Tab.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -184,7 +185,7 @@ Tab::Tab(BrowserWindow* window, RefPtr parent_client, auto url_serialized = qstring_from_ak_string(url.serialize()); m_title = url_serialized; - emit title_changed(tab_index(), url_serialized); + update_tab_title(); m_favicon = default_favicon(); set_loading(true); @@ -213,7 +214,7 @@ Tab::Tab(BrowserWindow* window, RefPtr parent_client, view().on_title_change = [this](auto const& title) { m_title = qstring_from_utf16_string(title); - emit title_changed(tab_index(), m_title); + update_tab_title(); }; view().on_favicon_change = [this](auto const& bitmap) { @@ -525,6 +526,25 @@ QIcon Tab::tab_icon() const return m_favicon; } +QString Tab::title() const +{ + if (!WebView::Application::settings().config_variable_as_bool(WebView::ConfigVariableID::ShowWebContentProcessIDInTabTitle)) + return m_title; + + return QString("%1 [%2]").arg(m_title).arg(view().client().pid()); +} + +void Tab::update_tab_title() +{ + emit title_changed(tab_index(), title()); +} + +void Tab::config_variable_changed(WebView::ConfigVariableID variable) +{ + if (variable == WebView::ConfigVariableID::ShowWebContentProcessIDInTabTitle) + update_tab_title(); +} + void Tab::set_loading(bool is_loading) { if (m_is_loading == is_loading) diff --git a/UI/Qt/Tab.h b/UI/Qt/Tab.h index d99c4ce8e3..8dc8132b2d 100644 --- a/UI/Qt/Tab.h +++ b/UI/Qt/Tab.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -46,7 +47,9 @@ signals: void mouse_entered(QEnterEvent*); }; -class Tab final : public QWidget { +class Tab final + : public QWidget + , public WebView::SettingsObserver { Q_OBJECT public: @@ -54,6 +57,7 @@ public: virtual ~Tab() override; WebContentView& view() { return *m_view; } + WebContentView const& view() const { return *m_view; } void navigate(URL::URL const&); void load_html(StringView); @@ -70,7 +74,7 @@ public: QIcon const& favicon() const { return m_favicon; } QIcon tab_icon() const; - QString const& title() const { return m_title; } + QString title() const; QMenu* context_menu() const { return m_context_menu; } @@ -93,8 +97,10 @@ signals: private: virtual void resizeEvent(QResizeEvent*) override; virtual bool event(QEvent*) override; + virtual void config_variable_changed(WebView::ConfigVariableID) override; void recreate_toolbar_icons(); + void update_tab_title(); void set_loading(bool); void update_tab_icon(); int tab_index();