diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc
index 9a6db49f0763..533c3ac36694 100644
--- a/shell/browser/native_window.cc
+++ b/shell/browser/native_window.cc
@@ -804,6 +804,18 @@ std::u16string NativeWindow::GetAccessibleWindowTitle() const {
return accessible_title_;
}
+std::string NativeWindow::GetTitle() const {
+ return base::UTF16ToUTF8(WidgetDelegate::GetWindowTitle());
+}
+
+void NativeWindow::SetTitle(const std::string_view title) {
+ if (title == GetTitle())
+ return;
+
+ WidgetDelegate::SetTitle(base::UTF8ToUTF16(title));
+ OnTitleChanged();
+}
+
void NativeWindow::SetAccessibleTitle(const std::string& title) {
accessible_title_ = base::UTF8ToUTF16(title);
}
diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h
index 7a9b762c60b2..96a1cb361e3b 100644
--- a/shell/browser/native_window.h
+++ b/shell/browser/native_window.h
@@ -148,8 +148,6 @@ class NativeWindow : public base::SupportsUserData,
virtual ui::ZOrderLevel GetZOrderLevel() const = 0;
virtual void Center() = 0;
virtual void Invalidate() = 0;
- virtual void SetTitle(const std::string& title) = 0;
- virtual std::string GetTitle() const = 0;
#if BUILDFLAG(IS_MAC)
virtual std::string GetAlwaysOnTopLevel() const = 0;
virtual void SetActive(bool is_key) = 0;
@@ -160,6 +158,9 @@ class NativeWindow : public base::SupportsUserData,
virtual void DetachChildren() = 0;
#endif
+ void SetTitle(std::string_view title);
+ [[nodiscard]] std::string GetTitle() const;
+
// Ability to augment the window title for the screen readers.
void SetAccessibleTitle(const std::string& title);
std::string GetAccessibleTitle();
@@ -437,6 +438,8 @@ class NativeWindow : public base::SupportsUserData,
NativeWindow(const gin_helper::Dictionary& options, NativeWindow* parent);
+ virtual void OnTitleChanged() {}
+
// views::WidgetDelegate:
views::Widget* GetWidget() override;
const views::Widget* GetWidget() const override;
diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h
index fa51e9ee447d..acd6f871a421 100644
--- a/shell/browser/native_window_mac.h
+++ b/shell/browser/native_window_mac.h
@@ -37,6 +37,7 @@ class NativeWindowMac : public NativeWindow,
~NativeWindowMac() override;
// NativeWindow:
+ void OnTitleChanged() override;
void SetContentView(views::View* view) override;
void Close() override;
void CloseImmediately() override;
@@ -85,8 +86,6 @@ class NativeWindowMac : public NativeWindow,
ui::ZOrderLevel GetZOrderLevel() const override;
void Center() override;
void Invalidate() override;
- void SetTitle(const std::string& title) override;
- std::string GetTitle() const override;
void FlashFrame(bool flash) override;
void SetSkipTaskbar(bool skip) override;
void SetExcludedFromShownWindowsMenu(bool excluded) override;
diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm
index 3fef4cba6c24..f848e5cf7884 100644
--- a/shell/browser/native_window_mac.mm
+++ b/shell/browser/native_window_mac.mm
@@ -938,16 +938,13 @@ void NativeWindowMac::Invalidate() {
[[window_ contentView] setNeedsDisplay:YES];
}
-void NativeWindowMac::SetTitle(const std::string& title) {
- [window_ setTitle:base::SysUTF8ToNSString(title)];
+void NativeWindowMac::OnTitleChanged() {
+ [window_ setTitle:base::SysUTF8ToNSString(GetTitle())];
+
if (buttons_proxy_)
[buttons_proxy_ redraw];
}
-std::string NativeWindowMac::GetTitle() const {
- return base::SysNSStringToUTF8([window_ title]);
-}
-
void NativeWindowMac::FlashFrame(bool flash) {
if (flash) {
attention_request_id_ = [NSApp requestUserAttention:NSCriticalRequest];
diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc
index 610ecc7ecad8..0d6e6acd02d1 100644
--- a/shell/browser/native_window_views.cc
+++ b/shell/browser/native_window_views.cc
@@ -198,7 +198,8 @@ class NativeWindowClientView : public views::ClientView {
NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
NativeWindow* parent)
: NativeWindow(options, parent) {
- options.Get(options::kTitle, &title_);
+ if (std::string val; options.Get(options::kTitle, &val))
+ SetTitle(val);
bool menu_bar_autohide;
if (options.Get(options::kAutoHideMenuBar, &menu_bar_autohide))
@@ -1160,15 +1161,6 @@ void NativeWindowViews::Invalidate() {
widget()->SchedulePaintInRect(gfx::Rect(GetBounds().size()));
}
-void NativeWindowViews::SetTitle(const std::string& title) {
- title_ = title;
- widget()->UpdateWindowTitle();
-}
-
-std::string NativeWindowViews::GetTitle() const {
- return title_;
-}
-
void NativeWindowViews::FlashFrame(bool flash) {
#if BUILDFLAG(IS_WIN)
// The Chromium's implementation has a bug stopping flash.
@@ -1750,10 +1742,6 @@ bool NativeWindowViews::CanMinimize() const {
#endif
}
-std::u16string NativeWindowViews::GetWindowTitle() const {
- return base::UTF8ToUTF16(title_);
-}
-
views::View* NativeWindowViews::GetContentsView() {
return root_view_.GetMainView();
}
diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h
index 5bc9db1ea7bb..f79dc9fc1e67 100644
--- a/shell/browser/native_window_views.h
+++ b/shell/browser/native_window_views.h
@@ -98,8 +98,6 @@ class NativeWindowViews : public NativeWindow,
ui::ZOrderLevel GetZOrderLevel() const override;
void Center() override;
void Invalidate() override;
- void SetTitle(const std::string& title) override;
- std::string GetTitle() const override;
void FlashFrame(bool flash) override;
void SetSkipTaskbar(bool skip) override;
void SetExcludedFromShownWindowsMenu(bool excluded) override {}
@@ -191,7 +189,6 @@ class NativeWindowViews : public NativeWindow,
views::View* GetInitiallyFocusedView() override;
bool CanMaximize() const override;
bool CanMinimize() const override;
- std::u16string GetWindowTitle() const override;
views::View* GetContentsView() override;
bool ShouldDescendIntoChildForEventHandling(
gfx::NativeView child,
@@ -327,7 +324,6 @@ class NativeWindowViews : public NativeWindow,
bool maximizable_ = true;
bool minimizable_ = true;
bool fullscreenable_ = true;
- std::string title_;
gfx::Size widget_size_;
double opacity_ = 1.0;
bool widget_destroyed_ = false;
diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts
index f8aebc55d93b..5d44f5281d3f 100755
--- a/spec/api-browser-window-spec.ts
+++ b/spec/api-browser-window-spec.ts
@@ -3888,8 +3888,14 @@ describe('BrowserWindow module', () => {
});
it('works for window events', async () => {
const pageTitleUpdated = once(w, 'page-title-updated');
- w.loadURL('data:text/html,');
+ const newTitle = 'changed';
+ w.loadURL(`data:text/html,`);
await pageTitleUpdated;
+
+ // w.title should update after 'page-title-updated'.
+ // It happens right *after* the event fires though,
+ // so we have to waitUntil it changes
+ waitUntil(() => w.title === newTitle);
});
it('works for stop events', async () => {
@@ -5428,6 +5434,36 @@ describe('BrowserWindow module', () => {
});
});
});
+
+ describe('native window title', () => {
+ describe('with properties', () => {
+ it('can be set with title constructor option', () => {
+ const w = new BrowserWindow({ show: false, title: 'mYtItLe' });
+ expect(w.title).to.eql('mYtItLe');
+ });
+
+ it('can be changed', () => {
+ const w = new BrowserWindow({ show: false });
+ expect(w.title).to.eql('Electron Test Main');
+ w.title = 'NEW TITLE';
+ expect(w.title).to.eql('NEW TITLE');
+ });
+ });
+
+ describe('with functions', () => {
+ it('can be set with minimizable constructor option', () => {
+ const w = new BrowserWindow({ show: false, title: 'mYtItLe' });
+ expect(w.getTitle()).to.eql('mYtItLe');
+ });
+
+ it('can be changed', () => {
+ const w = new BrowserWindow({ show: false });
+ expect(w.getTitle()).to.eql('Electron Test Main');
+ w.setTitle('NEW TITLE');
+ expect(w.getTitle()).to.eql('NEW TITLE');
+ });
+ });
+ });
});
ifdescribe(process.platform !== 'linux')('window states (excluding Linux)', () => {
@@ -5508,36 +5544,6 @@ describe('BrowserWindow module', () => {
});
});
- describe('native window title', () => {
- describe('with properties', () => {
- it('can be set with title constructor option', () => {
- const w = new BrowserWindow({ show: false, title: 'mYtItLe' });
- expect(w.title).to.eql('mYtItLe');
- });
-
- it('can be changed', () => {
- const w = new BrowserWindow({ show: false });
- expect(w.title).to.eql('Electron Test Main');
- w.title = 'NEW TITLE';
- expect(w.title).to.eql('NEW TITLE');
- });
- });
-
- describe('with functions', () => {
- it('can be set with minimizable constructor option', () => {
- const w = new BrowserWindow({ show: false, title: 'mYtItLe' });
- expect(w.getTitle()).to.eql('mYtItLe');
- });
-
- it('can be changed', () => {
- const w = new BrowserWindow({ show: false });
- expect(w.getTitle()).to.eql('Electron Test Main');
- w.setTitle('NEW TITLE');
- expect(w.getTitle()).to.eql('NEW TITLE');
- });
- });
- });
-
describe('minimizable state', () => {
describe('with properties', () => {
it('can be set with minimizable constructor option', () => {