hid: Update HidPinnedNotification to show extension names

Update HidPinnedNotification to show extension names and revise icon
string literals to match go/usb-hid-extension-permission-ux
documentation.

Bug: 1353104
Change-Id: I2ca50ae52aaeec9b6ce77a96425fbc342c7fb232
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4452088
Reviewed-by: Reilly Grant <[email protected]>
Commit-Queue: Jack Hsieh <[email protected]>
Code-Coverage: Findit <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1135568}
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 0456210..60acc94 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -1009,11 +1009,17 @@
 
       <if expr="not is_android">
         <!-- WebHID system tray icon -->
-        <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE" desc="Title for the WebHID system tray icon when one or more HID devices are being accessed.">
+        <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION" desc="Title for the WebHID system tray icon when one or more HID devices are being accessed by an extension.">
             {NUM_DEVICES, plural,
-            =0 {Chromium was accessing a HID device}
-            =1 {Chromium is accessing a HID device}
-            other {Chromium is accessing HID devices}}
+            =0 {A Chromium extension was accessing HID devices}
+            =1 {A Chromium extension is accessing 1 HID device}
+            other {A Chromium extension is accessing # HID devices}}
+        </message>
+        <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS" desc="Title for the WebHID system tray icon when HID devices are being accessed by multiple extensions.">
+            {NUM_DEVICES, plural,
+            =0 {Chromium extensions were accessing HID devices}
+            =1 {Chromium extensions are accessing HID devices}
+            other {Chromium extensions are accessing # HID devices}}
         </message>
       </if>
 
diff --git a/chrome/app/chromium_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE.png.sha1 b/chrome/app/chromium_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE.png.sha1
deleted file mode 100644
index b830d56..0000000
--- a/chrome/app/chromium_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-590b07dbedaef63974c924244c3bf44de6bc280e
\ No newline at end of file
diff --git a/chrome/app/chromium_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS.png.sha1 b/chrome/app/chromium_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS.png.sha1
new file mode 100644
index 0000000..a444ee0c
--- /dev/null
+++ b/chrome/app/chromium_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS.png.sha1
@@ -0,0 +1 @@
+e1f083b41db133cc0e9d03ca73e00c8ddd8f0638
\ No newline at end of file
diff --git a/chrome/app/chromium_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION.png.sha1 b/chrome/app/chromium_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION.png.sha1
new file mode 100644
index 0000000..150f8c98
--- /dev/null
+++ b/chrome/app/chromium_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION.png.sha1
@@ -0,0 +1 @@
+3f3b3bab80f0635b73b43a91a7dcf7197dfa80ba
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 91f1e7d..c70bbe1 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -13005,25 +13005,31 @@
     </message>
 
     <if expr="not is_android">
-      <!-- WebHID Notification -->
-      <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE" desc="The text for WebHID system tray icon button for managing HID devices.">
-        Manage HID devices
-      </message>
-      <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE_WITH_PROFILE_NAME" desc="The text with profile name for WebHID system tray icon button for managing HID devices.">
-        Manage HID devices for <ph name="PROFILE_NAME">$1<ex>[email protected]</ex></ph>
-      </message>
+      <!-- WebHID System Tray Icon -->
       <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_HID_SETTINGS" desc="Text for the WebHID system tray icon button that opens the HID settings page.">
         HID settings
       </message>
       <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_ABOUT_HID_DEVICE" desc="Text for the WebHID system tray icon button that opens the learn more page for the HID device.">
         About HID devices
       </message>
-      <message name="IDS_DEVICE_CONNECTED_BY_EXTENSION" desc="Text for the WebHID system tray icon button that displays how many active connections for the extension and opens the extension's site setting page.">
-        {NUM_CONNECTION, plural,
-        =0 {Extension "<ph name="EXTENSION">{1}<ex>Google Translate</ex></ph>" was accessing devices}
-        =1 {Extension "<ph name="EXTENSION">{1}<ex>Google Translate</ex></ph>" is accessing {0} device}
-        other {Extension "<ph name="EXTENSION">{1}<ex>Google Translate</ex></ph>" is accessing {0} devices}}
-      </message>
+      <if expr="is_chromeos">
+        <then>
+          <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_EXTENSION_LIST" desc="Text for the WebHID pinned notification message.">
+            {NUM_EXTENSION, plural,
+            =1 {<ph name="EXTENSION1">{1}<ex>Google Translate</ex></ph> is accessing HID devices}
+            =2 {Extensions accessing devices: <ph name="EXTENSION1">{1}<ex>Google Translate</ex></ph>, <ph name="EXTENSION2">{2}<ex>Secure Shell</ex></ph>}
+            other {Extensions accessing devices: <ph name="EXTENSION1">{1}<ex>Google Translate</ex></ph>, <ph name="EXTENSION2">{2}<ex>Secure Shell</ex></ph> +{3} more}}
+          </message>
+        </then>
+        <else>
+          <message name="IDS_DEVICE_CONNECTED_BY_EXTENSION" desc="Text for the WebHID system tray icon button that displays how many active connections for the extension and opens the extension's site setting page.">
+            {NUM_CONNECTION, plural,
+            =0 {Extension "<ph name="EXTENSION">{1}<ex>Google Translate</ex></ph>" was accessing devices}
+            =1 {Extension "<ph name="EXTENSION">{1}<ex>Google Translate</ex></ph>" is accessing {0} device}
+            other {Extension "<ph name="EXTENSION">{1}<ex>Google Translate</ex></ph>" is accessing {0} devices}}
+          </message>
+        </else>
+      </if>
     </if>
 
     <if expr="chromeos_ash">
diff --git a/chrome/app/generated_resources_grd/IDS_DEVICE_CONNECTED_BY_EXTENSION.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEVICE_CONNECTED_BY_EXTENSION.png.sha1
index 666d848..af43730 100644
--- a/chrome/app/generated_resources_grd/IDS_DEVICE_CONNECTED_BY_EXTENSION.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_DEVICE_CONNECTED_BY_EXTENSION.png.sha1
@@ -1 +1 @@
-475ad0e2633134f0d795024b21bd71696a035651
\ No newline at end of file
+a2b2d8eb0579ed3a99dad7ab91bdfc272ed0ee1f
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE.png.sha1
deleted file mode 100644
index 48de3fdd1..0000000
--- a/chrome/app/generated_resources_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-df167e129e49a5833e8ca321f7ebc6f7cdf4fe01
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE_WITH_PROFILE_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE_WITH_PROFILE_NAME.png.sha1
deleted file mode 100644
index 4a1bb4a..0000000
--- a/chrome/app/generated_resources_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE_WITH_PROFILE_NAME.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-891da6346817ba8cfbeb025930f923d41a2324ca
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_EXTENSION_LIST.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_EXTENSION_LIST.png.sha1
new file mode 100644
index 0000000..a0c63ec
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_EXTENSION_LIST.png.sha1
@@ -0,0 +1 @@
+e192152c9bea3b1f8463eb54ddd8b776931e3510
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index fb3bd7b..79fa786 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1070,11 +1070,17 @@
 
       <if expr="not is_android">
         <!-- WebHID system tray icon -->
-        <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE" desc="Title for the WebHID system tray icon when one or more HID devices are being accessed.">
+        <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION" desc="Title for the WebHID system tray icon when one or more HID devices are being accessed by an extension.">
             {NUM_DEVICES, plural,
-            =0 {Google Chrome was accessing a HID device}
-            =1 {Google Chrome is accessing a HID device}
-            other {Google Chrome is accessing HID devices}}
+            =0 {A Google Chrome extension was accessing HID devices}
+            =1 {A Google Chrome extension is accessing 1 HID device}
+            other {A Google Chrome extension is accessing # HID devices}}
+        </message>
+        <message name="IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS" desc="Title for the WebHID system tray icon when HID devices are being accessed by multiple extensions.">
+            {NUM_DEVICES, plural,
+            =0 {Google Chrome extensions were accessing HID devices}
+            =1 {Google Chrome extensions are accessing HID devices}
+            other {Google Chrome extensions are accessing # HID devices}}
         </message>
       </if>
 
diff --git a/chrome/app/google_chrome_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE.png.sha1
deleted file mode 100644
index b830d56..0000000
--- a/chrome/app/google_chrome_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-590b07dbedaef63974c924244c3bf44de6bc280e
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS.png.sha1
new file mode 100644
index 0000000..6a9c62c
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS.png.sha1
@@ -0,0 +1 @@
+2ae8ffc4166f134e98b5de1a6802ba677789a37f
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION.png.sha1
new file mode 100644
index 0000000..1fa6dbd
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION.png.sha1
@@ -0,0 +1 @@
+26aa7543e4596af969171e0a36e1149721ae9597
\ No newline at end of file
diff --git a/chrome/browser/hid/hid_pinned_notification.cc b/chrome/browser/hid/hid_pinned_notification.cc
index 3a847151..aa08dc47 100644
--- a/chrome/browser/hid/hid_pinned_notification.cc
+++ b/chrome/browser/hid/hid_pinned_notification.cc
@@ -3,19 +3,59 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/hid/hid_pinned_notification.h"
+#include <string>
 
 #include "base/containers/contains.h"
+#include "base/containers/flat_map.h"
+#include "base/i18n/message_formatter.h"
 #include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/hid/hid_connection_tracker.h"
 #include "chrome/browser/hid/hid_connection_tracker_factory.h"
 #include "chrome/browser/notifications/system_notification_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/grit/generated_resources.h"
 #include "extensions/buildflags/buildflags.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_delegate.h"
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+
+namespace {
+
+std::u16string GetMessageLabel(HidConnectionTracker* connection_tracker) {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  std::vector<std::u16string> extension_names;
+  for (const auto& [origin, state] : connection_tracker->origins()) {
+    CHECK_EQ(origin.scheme(), extensions::kExtensionScheme);
+    extension_names.push_back(base::UTF8ToUTF16(state.name));
+  }
+  CHECK(!extension_names.empty());
+  if (extension_names.size() == 1) {
+    return base::i18n::MessageFormatter::FormatWithNumberedArgs(
+        l10n_util::GetStringUTF16(IDS_WEBHID_SYSTEM_TRAY_ICON_EXTENSION_LIST),
+        1, extension_names[0]);
+  } else if (extension_names.size() == 2) {
+    return base::i18n::MessageFormatter::FormatWithNumberedArgs(
+        l10n_util::GetStringUTF16(IDS_WEBHID_SYSTEM_TRAY_ICON_EXTENSION_LIST),
+        2, extension_names[0], extension_names[1]);
+  }
+  return base::i18n::MessageFormatter::FormatWithNumberedArgs(
+      l10n_util::GetStringUTF16(IDS_WEBHID_SYSTEM_TRAY_ICON_EXTENSION_LIST),
+      static_cast<int>(extension_names.size()), extension_names[0],
+      extension_names[1], static_cast<int>(extension_names.size() - 2));
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+  NOTREACHED_NORETURN();
+}
+
+}  // namespace
+
 HidPinnedNotification::HidPinnedNotification() = default;
 
 HidPinnedNotification::~HidPinnedNotification() = default;
@@ -28,7 +68,7 @@
 std::unique_ptr<message_center::Notification>
 HidPinnedNotification::CreateNotification(Profile* profile) {
   message_center::RichNotificationData data;
-  data.buttons.emplace_back(GetManageHidDeviceButtonLabel(profile));
+  data.buttons.emplace_back(GetContentSettingsLabel());
   auto delegate =
       base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
           base::BindRepeating(
@@ -54,8 +94,9 @@
   DCHECK(hid_connection_tracker);
   auto notification = std::make_unique<message_center::Notification>(
       message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
-      GetTitleLabel(hid_connection_tracker->total_connection_count()),
-      /*message=*/std::u16string(), /*icon=*/ui::ImageModel(),
+      GetTitleLabel(hid_connection_tracker->origins().size(),
+                    hid_connection_tracker->total_connection_count()),
+      GetMessageLabel(hid_connection_tracker), /*icon=*/ui::ImageModel(),
       /*display_source=*/std::u16string(), /*origin_url=*/GURL(),
 #if BUILDFLAG(IS_CHROMEOS_ASH)
       message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
@@ -68,8 +109,8 @@
       data, std::move(delegate));
   notification->set_small_image(gfx::Image(GetStatusTrayIcon()));
   notification->set_pinned(true);
-  // Set to system priority so it will never timeout.
-  notification->SetSystemPriority();
+  // Set to low priority so it doesn't create a popup.
+  notification->set_priority(message_center::LOW_PRIORITY);
   return notification;
 }
 
diff --git a/chrome/browser/hid/hid_pinned_notification_unittest.cc b/chrome/browser/hid/hid_pinned_notification_unittest.cc
index 2be074f..45684bd4 100644
--- a/chrome/browser/hid/hid_pinned_notification_unittest.cc
+++ b/chrome/browser/hid/hid_pinned_notification_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/hid/hid_pinned_notification.h"
+#include <string>
 
 #include "chrome/browser/hid/hid_connection_tracker.h"
 #include "chrome/browser/hid/hid_connection_tracker_factory.h"
@@ -10,9 +11,49 @@
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/notifications/system_notification_helper.h"
 #include "chrome/test/base/testing_browser_process.h"
+#include "extensions/common/constants.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+
+namespace {
+
+std::u16string GetExpectedMessage(
+    const std::vector<HidSystemTrayIconTestBase::OriginItem>& origin_items) {
+  auto sorted_origin_items = origin_items;
+  // Sort the |origin_items| by origin. This is necessary because the origin
+  // items for each profile in the pinned notification are created by iterating
+  // through a structure of flat_map<url::Origin, ...>.
+  base::ranges::sort(sorted_origin_items);
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  std::vector<std::string> extension_names;
+  for (const auto& [origin, connection_count, name] : sorted_origin_items) {
+    CHECK(origin.scheme() == extensions::kExtensionScheme);
+    extension_names.push_back(name);
+  }
+  CHECK(!extension_names.empty());
+  if (extension_names.size() == 1) {
+    return base::UTF8ToUTF16(base::StringPrintf("%s is accessing HID devices",
+                                                extension_names[0].c_str()));
+  } else if (extension_names.size() == 2) {
+    return base::UTF8ToUTF16(base::StringPrintf(
+        "Extensions accessing devices: %s, %s", extension_names[0].c_str(),
+        extension_names[1].c_str()));
+  }
+  return base::UTF8ToUTF16(
+      base::StringPrintf("Extensions accessing devices: %s, %s +%zu more",
+                         extension_names[0].c_str(), extension_names[1].c_str(),
+                         extension_names.size() - 2));
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+  NOTREACHED_NORETURN();
+}
+
+}  // namespace
+
 class HidPinnedNotificationTest : public HidSystemTrayIconTestBase {
  public:
   void SetUp() override {
@@ -50,12 +91,14 @@
           HidPinnedNotification::GetNotificationId(profile));
       ASSERT_TRUE(maybe_notification);
       EXPECT_EQ(maybe_notification->title(),
-                GetExpectedTitle(total_connection_count));
-
+                GetExpectedTitle(origin_items.size(), total_connection_count));
+      EXPECT_EQ(maybe_notification->message(),
+                GetExpectedMessage(origin_items));
+      EXPECT_EQ(maybe_notification->priority(), message_center::LOW_PRIORITY);
       EXPECT_EQ(maybe_notification->rich_notification_data().buttons.size(),
                 1u);
       EXPECT_EQ(maybe_notification->rich_notification_data().buttons[0].title,
-                GetExpectedButtonTitleForProfile(profile));
+                u"HID settings");
       EXPECT_TRUE(maybe_notification->delegate());
 
       EXPECT_CALL(*hid_connection_tracker, ShowContentSettingsExceptions());
@@ -110,4 +153,21 @@
 TEST_F(HidPinnedNotificationTest, ExtensionRemoval) {
   TestExtensionRemoval();
 }
+
+// Test message in pinned notification for up to 5 extensions.
+TEST_F(HidPinnedNotificationTest, MultipleExtensionsNotificationMessage) {
+  Profile* profile = CreateTestingProfile("user");
+  HidConnectionTracker* hid_connection_tracker =
+      HidConnectionTrackerFactory::GetForProfile(profile, /*create=*/true);
+
+  std::vector<HidSystemTrayIconTestBase::OriginItem> origin_items;
+  for (size_t idx = 0; idx < 5; idx++) {
+    auto extension =
+        CreateExtensionWithName(base::StringPrintf("Test Extension %zu", idx));
+    AddExtensionToProfile(profile, extension.get());
+    hid_connection_tracker->IncrementConnectionCount(extension.get()->origin());
+    origin_items.emplace_back(extension->origin(), 1, extension->name());
+    CheckIcon({{profile, origin_items}});
+  }
+}
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/hid/hid_status_icon.cc b/chrome/browser/hid/hid_status_icon.cc
index 188079a..d59e2268f 100644
--- a/chrome/browser/hid/hid_status_icon.cc
+++ b/chrome/browser/hid/hid_status_icon.cc
@@ -120,17 +120,6 @@
   }
 }
 
-size_t HidStatusIcon::GetTotalConnectionCount() {
-  size_t total_connection_count = 0;
-  for (const auto& [profile, staging] : profiles_) {
-    auto* hid_connection_tracker =
-        HidConnectionTrackerFactory::GetForProfile(profile, /*create=*/false);
-    DCHECK(hid_connection_tracker);
-    total_connection_count += hid_connection_tracker->total_connection_count();
-  }
-  return total_connection_count;
-}
-
 void HidStatusIcon::RefreshIcon() {
   command_id_callbacks_.clear();
   auto* status_tray = g_browser_process->status_tray();
@@ -152,9 +141,11 @@
   // |...                                           |
   // |---------------Separator----------------------|
   // |ProfileN section                              |
-  auto title_label = GetTitleLabel(GetTotalConnectionCount());
   auto menu = std::make_unique<StatusIconMenuModel>(this);
-  menu->AddTitle(title_label);
+  int total_connection_count = 0;
+  int total_origin_count = 0;
+  // Title will be updated after looping through profiles below.
+  menu->AddTitle(u"");
   AddItem(menu.get(), GetAboutDeviceLabel(),
           base::BindRepeating(&HidStatusIcon::ShowHelpCenterUrl));
   for (const auto& [profile, staging] : profiles_) {
@@ -174,14 +165,20 @@
     auto* connection_tracker =
         HidConnectionTrackerFactory::GetForProfile(profile, /*create=*/false);
     CHECK(connection_tracker);
+    total_origin_count += connection_tracker->origins().size();
     for (const auto& [origin, state] : connection_tracker->origins()) {
       AddItem(menu.get(),
               GetOriginConnectionCountLabel(profile, origin, state.count,
                                             state.name),
               base::BindRepeating(&HidStatusIcon::ShowSiteSettings,
                                   profile->GetWeakPtr(), origin));
+      // Only consider the count that will be shown to the system tray icon, so
+      // that connection count on title and extension buttons can match.
+      total_connection_count += state.count;
     }
   }
+  auto title_label = GetTitleLabel(total_origin_count, total_connection_count);
+  menu->SetLabel(0, title_label);
 
   if (!status_icon_) {
     status_icon_ = status_tray->CreateStatusIcon(
diff --git a/chrome/browser/hid/hid_status_icon.h b/chrome/browser/hid/hid_status_icon.h
index a09fbfa..6d1701b 100644
--- a/chrome/browser/hid/hid_status_icon.h
+++ b/chrome/browser/hid/hid_status_icon.h
@@ -29,9 +29,6 @@
   void ProfileAdded(Profile* profile) override;
   void ProfileRemoved(Profile* profile) override;
 
-  // Get the total connection count from all the profiles being tracked.
-  size_t GetTotalConnectionCount();
-
   // StatusIconMenuModel::Delegate
   void ExecuteCommand(int command_id, int event_flags) override;
 
diff --git a/chrome/browser/hid/hid_status_icon_unittest.cc b/chrome/browser/hid/hid_status_icon_unittest.cc
index 13cf695..b52b084 100644
--- a/chrome/browser/hid/hid_status_icon_unittest.cc
+++ b/chrome/browser/hid/hid_status_icon_unittest.cc
@@ -21,6 +21,7 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry.h"
@@ -135,6 +136,7 @@
     auto sorted_profile_connection_counts = profile_connection_counts;
     base::ranges::sort(sorted_profile_connection_counts);
     size_t total_connection_count = 0;
+    size_t total_origin_count = 0;
     auto* menu_item = status_icon->menu_item();
     // The system tray icon title (i.e menu_idx == 0) will be checked at the end
     // when |total_connection_count| is calculated.
@@ -144,6 +146,7 @@
                            expected_command_id++, /*click=*/false);
     for (const auto& [profile, origin_items] :
          sorted_profile_connection_counts) {
+      total_origin_count += origin_items.size();
       auto sorted_origin_items = origin_items;
       // Sort the |origin_items| by origin. This is necessary because the origin
       // items for each profile in the menu are created by iterating through a
@@ -168,7 +171,11 @@
         total_connection_count += connection_count;
       }
     }
-    CheckMenuItemLabel(menu_item, 0, GetExpectedTitle(total_connection_count));
+    CheckMenuItemLabel(
+        menu_item, 0,
+        GetExpectedTitle(total_origin_count,
+                         override_title_total_connection_count_.value_or(
+                             total_connection_count)));
     EXPECT_LE(expected_command_id, IDC_DEVICE_SYSTEM_TRAY_ICON_LAST + 1);
   }
 
@@ -178,6 +185,13 @@
     ASSERT_TRUE(status_tray);
     EXPECT_TRUE(status_tray->GetStatusIconsForTest().empty());
   }
+
+ protected:
+  // This is specifically used in the test case of
+  // NumCommandIdOverLimitExtensionOrigin, where the input parameter
+  // profile_connection_counts may not capture all of the origins because the
+  // limit is exceeded.
+  absl::optional<int> override_title_total_connection_count_;
 };
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -248,6 +262,8 @@
         {profile, {{extension->origin(), 1, extension->name()}}});
     base::ranges::sort(profile_connection_counts);
     profile_connection_counts.back().second.clear();
+    // The total connection count in the title still captures all of the origins
+    override_title_total_connection_count_ = 20;
     CheckIcon(profile_connection_counts);
   }
 }
diff --git a/chrome/browser/hid/hid_system_tray_icon.cc b/chrome/browser/hid/hid_system_tray_icon.cc
index 8ff5945..7607cfc 100644
--- a/chrome/browser/hid/hid_system_tray_icon.cc
+++ b/chrome/browser/hid/hid_system_tray_icon.cc
@@ -9,11 +9,13 @@
 #include "base/containers/cxx20_erase.h"
 #include "base/functional/bind.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/buildflags/buildflags.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 
@@ -24,23 +26,19 @@
 }
 
 // static
-std::u16string HidSystemTrayIcon::GetManageHidDeviceButtonLabel(
-    Profile* profile) {
-  std::u16string profile_name =
-      base::UTF8ToUTF16(profile->GetProfileUserName());
-  if (profile_name.empty()) {
-    return l10n_util::GetStringUTF16(
-        IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE);
+std::u16string HidSystemTrayIcon::GetTitleLabel(size_t num_origins,
+                                                size_t num_connections) {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  if (num_origins == 1) {
+    return l10n_util::GetPluralStringFUTF16(
+        IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION,
+        static_cast<int>(num_connections));
   }
-  return l10n_util::GetStringFUTF16(
-      IDS_WEBHID_SYSTEM_TRAY_ICON_BUTTON_FOR_MANAGE_HID_DEVICE_WITH_PROFILE_NAME,
-      profile_name);
-}
-
-// static
-std::u16string HidSystemTrayIcon::GetTitleLabel(size_t num_connections) {
-  return l10n_util::GetPluralStringFUTF16(IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE,
-                                          static_cast<int>(num_connections));
+  return l10n_util::GetPluralStringFUTF16(
+      IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS,
+      static_cast<int>(num_connections));
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+  NOTREACHED_NORETURN();
 }
 
 // static
diff --git a/chrome/browser/hid/hid_system_tray_icon.h b/chrome/browser/hid/hid_system_tray_icon.h
index 1662aae..cdae557f 100644
--- a/chrome/browser/hid/hid_system_tray_icon.h
+++ b/chrome/browser/hid/hid_system_tray_icon.h
@@ -51,12 +51,9 @@
   // Get the image for the status tray icon.
   static gfx::ImageSkia GetStatusTrayIcon();
 
-  // Get the label of the button for managing HID device permission on the HID
-  // system tray icon.
-  static std::u16string GetManageHidDeviceButtonLabel(Profile* profile);
-
   // Get the label of the title of the HID system tray icon.
-  static std::u16string GetTitleLabel(size_t num_connections);
+  static std::u16string GetTitleLabel(size_t num_origins,
+                                      size_t num_connections);
 
   // Returns a label for HID settings button.
   static std::u16string GetContentSettingsLabel();
diff --git a/chrome/browser/hid/hid_system_tray_icon_unittest.cc b/chrome/browser/hid/hid_system_tray_icon_unittest.cc
index f256ba4..071e52b 100644
--- a/chrome/browser/hid/hid_system_tray_icon_unittest.cc
+++ b/chrome/browser/hid/hid_system_tray_icon_unittest.cc
@@ -83,12 +83,22 @@
 }
 
 std::u16string HidSystemTrayIconTestBase::GetExpectedTitle(
+    size_t num_origins,
     size_t num_connections) {
-  // It might be either ""Chromium is accessing a HID device" or "Google Chrome
-  // is accessing a HID device" depending is_chrome_branded in the build config
-  // file, hence using l10n_util to get the expected string.
-  return l10n_util::GetPluralStringFUTF16(IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE,
-                                          static_cast<int>(num_connections));
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // The text might use "Google Chrome" or "Chromium" depending
+  // is_chrome_branded in the build config file, hence using l10n_util to get
+  // the expected string.
+  if (num_origins == 1) {
+    return l10n_util::GetPluralStringFUTF16(
+        IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_SINGLE_EXTENSION,
+        static_cast<int>(num_connections));
+  }
+  return l10n_util::GetPluralStringFUTF16(
+      IDS_WEBHID_SYSTEM_TRAY_ICON_TITLE_MULTIPLE_EXTENSIONS,
+      static_cast<int>(num_connections));
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+  NOTREACHED_NORETURN();
 }
 
 BrowserContextKeyedServiceFactory::TestingFactory
diff --git a/chrome/browser/hid/hid_system_tray_icon_unittest.h b/chrome/browser/hid/hid_system_tray_icon_unittest.h
index 3cc3001b..c84f4f7 100644
--- a/chrome/browser/hid/hid_system_tray_icon_unittest.h
+++ b/chrome/browser/hid/hid_system_tray_icon_unittest.h
@@ -42,7 +42,7 @@
   virtual void CheckIconHidden() = 0;
 
   std::u16string GetExpectedButtonTitleForProfile(Profile* profile);
-  std::u16string GetExpectedTitle(size_t num_connections);
+  std::u16string GetExpectedTitle(size_t num_origins, size_t num_connections);
 
   // This is used to inject MockHidConnectionTracker.
   BrowserContextKeyedServiceFactory::TestingFactory