Repository: electron/electron
Pull request: fix: use app name as a part of tray id on Linux

#### Description of Change

Sets `org.freedesktop.StatusNotifierItem.Id` of tray icons to `the name of the app + _status_icon_ + counter` instead of `chrome_status_icon_ + counter`.

Closes https://github.com/electron/electron/issues/40936

Relevant issue on KDE Plasma https://bugs.kde.org/show_bug.cgi?id=470840

#### Checklist

- [ ] PR description included and stakeholders cc'd
- [ ] `npm test` passes
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Fixed ID of tray icons on Linux



Changed files:
filename: patches/chromium/.patches
patch: @@ -144,3 +144,4 @@ chore_disable_protocol_handler_dcheck.patch
 fix_check_for_file_existence_before_setting_mtime.patch
 revert_cleanup_remove_feature_windelayspellcheckserviceinit.patch
 fix_release_mouse_buttons_on_focus_loss_on_wayland.patch
+fix_linux_tray_id.patch

filename: patches/chromium/fix_linux_tray_id.patch
patch: @@ -0,0 +1,76 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Damglador <vse.stopchanskyi@gmail.com>
+Date: Mon, 27 Oct 2025 23:35:43 +0100
+Subject: fix: allow setting tray id
+
+This is needed to allow setting custom tray id when initializing a tray icon.
+With current behaviour all programs get chrome_status_icon_1 as their id in tray.
+So tray can't tell apart Electron apps,
+which introduces issues like https://bugs.kde.org/show_bug.cgi?id=470840.
+
+This patch can be removed after being upstreamed. See discussion at
+https://github.com/electron/electron/pull/48675#issuecomment-3452781711
+for more info.
+
+diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
+index e268573d360b1744c739bc516e81261c3869cd23..cf69e72a1a391451a31e28356cb6b86fc6010646 100644
+--- a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
++++ b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
+@@ -131,8 +131,8 @@ int NextServiceId() {
+   return ++status_icon_count;
+ }
+ 
+-std::string PropertyIdFromId(int service_id) {
+-  return "chrome_status_icon_" + base::NumberToString(service_id);
++std::string PropertyIdFromId(int service_id, const std::string_view app_name) {
++  return base::StrCat({ app_name, "_status_icon_", base::NumberToString(service_id) });
+ }
+ 
+ using DbusImage = std::tuple</*width=*/int32_t,
+@@ -222,12 +222,13 @@ base::FilePath WriteIconFile(size_t icon_file_id,
+ 
+ }  // namespace
+ 
+-StatusIconLinuxDbus::StatusIconLinuxDbus()
++StatusIconLinuxDbus::StatusIconLinuxDbus(const std::string_view app_name)
+     : bus_(dbus_thread_linux::GetSharedSessionBus()),
+       should_write_icon_to_file_(ShouldWriteIconToFile()),
+       icon_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+-           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})) {
++           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
++      app_name_(app_name) {
+   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+   CheckStatusNotifierWatcherHasOwner();
+ }
+@@ -402,7 +403,7 @@ void StatusIconLinuxDbus::OnHostRegisteredResponse(dbus::Response* response) {
+   properties_->SetProperty<"s">(kInterfaceStatusNotifierItem, kPropertyCategory,
+                                 kPropertyValueCategory, false);
+   properties_->SetProperty<"s">(kInterfaceStatusNotifierItem, kPropertyId,
+-                                PropertyIdFromId(service_id_), false);
++                                PropertyIdFromId(service_id_, app_name_), false);
+   properties_->SetProperty<"s">(kInterfaceStatusNotifierItem,
+                                 kPropertyOverlayIconName, "", false);
+   properties_->SetProperty<"s">(kInterfaceStatusNotifierItem, kPropertyStatus,
+diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
+index 78c0ddf50ae6cafed1badaeb951b1dab3110f095..984673954bb7f0b4c902677c70ab88c602201060 100644
+--- a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
++++ b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
+@@ -35,7 +35,7 @@ class StatusIconLinuxDbus : public ui::StatusIconLinux,
+                             public ui::SimpleMenuModel::Delegate,
+                             public base::RefCounted<StatusIconLinuxDbus> {
+  public:
+-  StatusIconLinuxDbus();
++  StatusIconLinuxDbus(const std::string_view app_name = "chrome");
+ 
+   StatusIconLinuxDbus(const StatusIconLinuxDbus&) = delete;
+   StatusIconLinuxDbus& operator=(const StatusIconLinuxDbus&) = delete;
+@@ -136,6 +136,8 @@ class StatusIconLinuxDbus : public ui::StatusIconLinux,
+   size_t icon_file_id_ = 0;
+   base::FilePath icon_file_;
+ 
++  std::string app_name_;
++
+   base::WeakPtrFactory<StatusIconLinuxDbus> weak_factory_{this};
+ };
+ 

filename: shell/browser/ui/tray_icon_linux.cc
patch: @@ -6,6 +6,7 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h"
+#include "shell/browser/browser.h"
 #include "shell/browser/ui/status_icon_gtk.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_rep.h"
@@ -32,7 +33,7 @@ gfx::ImageSkia GetBestImageRep(const gfx::ImageSkia& image) {
 }  // namespace
 
 TrayIconLinux::TrayIconLinux()
-    : status_icon_dbus_(new StatusIconLinuxDbus),
+    : status_icon_dbus_(new StatusIconLinuxDbus(Browser::Get()->GetName())),
       status_icon_type_(StatusIconType::kDbus) {
   status_icon_dbus_->SetDelegate(this);
 }