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();