Declutter: Add app menu entrypoint

Also ensures the tab organization selector doesn't open declutter if it
is currently disabled.

Bug: 369612291
Change-Id: I9ac885c500bc3102b6e5f6533aa8404913e798de
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5892285
Commit-Queue: Emily Shack <emshack@chromium.org>
Reviewed-by: Shibalik Mohapatra <shibalik@chromium.org>
Reviewed-by: Greg Thompson <grt@chromium.org>
Reviewed-by: Dana Fried <dfried@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1363342}
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 32e36fe9..5475c86c 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -135,6 +135,7 @@
 #define IDC_SHOW_ADDRESSES              35043
 #define IDC_ORGANIZE_TABS               35044
 #define IDC_CREATE_NEW_TAB_GROUP        35045
+#define IDC_DECLUTTER_TABS              35046
 
 // Page-manipulation commands that target a specified tab, which may not be the
 // active one.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index eceb4c2..4135fa6 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -9699,7 +9699,18 @@
           =1 {Visited 1 day ago}
           other {Visited # days ago}}
       </message>
-
+      <if expr="use_titlecase">
+        <then>
+          <message name="IDS_DECLUTTER_MENU" desc="In Title Case: The text label for the declutter app menu item.">
+            Close Unused Tabs
+          </message>
+        </then>
+        <else>
+          <message name="IDS_DECLUTTER_MENU" desc="The text label for the declutter app menu item.">
+            Close unused tabs
+          </message>
+        </else>
+      </if>
       <!-- Strings for tab organization -->
       <message name="IDS_TOOLTIP_TAB_ORGANIZE" desc="The tooltip for the Tab Organization button.">
         Organize tabs?
diff --git a/chrome/app/generated_resources_grd/IDS_DECLUTTER_MENU.png.sha1 b/chrome/app/generated_resources_grd/IDS_DECLUTTER_MENU.png.sha1
new file mode 100644
index 0000000..e64771c
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DECLUTTER_MENU.png.sha1
@@ -0,0 +1 @@
+894ae32e7be942fed9b2be7567c66e78344148e0
\ No newline at end of file
diff --git a/chrome/browser/resources/tab_search/tab_organization_selector.ts b/chrome/browser/resources/tab_search/tab_organization_selector.ts
index 51dcdd8..78c2f02 100644
--- a/chrome/browser/resources/tab_search/tab_organization_selector.ts
+++ b/chrome/browser/resources/tab_search/tab_organization_selector.ts
@@ -92,7 +92,13 @@
   }
 
   private updateSelectedFeature_(feature: TabOrganizationFeature) {
-    if (feature !== TabOrganizationFeature.kNone) {
+    if (feature === TabOrganizationFeature.kNone) {
+      return;
+    }
+    if (feature === TabOrganizationFeature.kDeclutter &&
+        this.disableDeclutter_) {
+      this.selectedState_ = TabOrganizationFeature.kSelector;
+    } else {
       this.selectedState_ = feature;
     }
   }
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 86af7456..884e6dd 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -685,6 +685,9 @@
     case IDC_ORGANIZE_TABS:
       StartTabOrganizationRequest(browser_);
       break;
+    case IDC_DECLUTTER_TABS:
+      ShowTabDeclutter(browser_);
+      break;
     case IDC_SHOW_TRANSLATE:
       ShowTranslateBubble(browser_);
       break;
@@ -1244,6 +1247,7 @@
 
   command_updater_.UpdateCommandEnabled(IDC_ORGANIZE_TABS, true);
   command_updater_.UpdateCommandEnabled(IDC_CREATE_NEW_TAB_GROUP, true);
+  command_updater_.UpdateCommandEnabled(IDC_DECLUTTER_TABS, true);
 #if BUILDFLAG(IS_CHROMEOS)
   command_updater_.UpdateCommandEnabled(IDC_TOGGLE_MULTITASK_MENU, true);
 #endif
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 2c902af..8bbb6ac 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -1882,6 +1882,13 @@
   browser->window()->CloseTabSearchBubble();
 }
 
+void ShowTabDeclutter(Browser* browser) {
+  const int tab_organization_tab_index = 1;
+  browser->window()->CreateTabSearchBubble(
+      tab_organization_tab_index,
+      tab_search::mojom::TabOrganizationFeature::kDeclutter);
+}
+
 bool CanCloseFind(Browser* browser) {
   WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents();
   if (!current_tab) {
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h
index 71f4ae23..afdf17c 100644
--- a/chrome/browser/ui/browser_commands.h
+++ b/chrome/browser/ui/browser_commands.h
@@ -206,6 +206,7 @@
 void FindInPage(Browser* browser, bool find_next, bool forward_direction);
 void ShowTabSearch(Browser* browser);
 void CloseTabSearch(Browser* browser);
+void ShowTabDeclutter(Browser* browser);
 bool CanCloseFind(Browser* browser);
 void CloseFind(Browser* browser);
 void Zoom(Browser* browser, content::PageZoom zoom);
diff --git a/chrome/browser/ui/browser_commands_browsertest.cc b/chrome/browser/ui/browser_commands_browsertest.cc
index e846908..5e1e1b8 100644
--- a/chrome/browser/ui/browser_commands_browsertest.cc
+++ b/chrome/browser/ui/browser_commands_browsertest.cc
@@ -23,8 +23,10 @@
 #include "chrome/browser/ui/toasts/toast_controller.h"
 #include "chrome/browser/ui/toasts/toast_features.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_entry_id.h"
+#include "chrome/browser/ui/views/tab_search_bubble_host.h"
 #include "chrome/browser/ui/webui/commerce/product_specifications_disclosure_dialog.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -48,6 +50,7 @@
     feature_list_.InitWithFeatures(
         {
             features::kTabOrganization,
+            features::kTabstripDeclutter,
             toast_features::kToastFramework,
             toast_features::kReadingListToast,
             toast_features::kLinkCopiedToast,
@@ -375,6 +378,17 @@
                                       true, 1);
 }
 
+IN_PROC_BROWSER_TEST_F(BrowserCommandsTest, ShowsDeclutter) {
+  TabSearchBubbleHost* tab_search_bubble_host =
+      BrowserView::GetBrowserViewForBrowser(browser())
+          ->GetTabSearchBubbleHost();
+  EXPECT_FALSE(tab_search_bubble_host->bubble_created_time_for_testing());
+
+  chrome::ExecuteCommand(browser(), IDC_DECLUTTER_TABS);
+
+  EXPECT_TRUE(tab_search_bubble_host->bubble_created_time_for_testing());
+}
+
 IN_PROC_BROWSER_TEST_F(BrowserCommandsTest,
                        ConvertPopupToTabbedBrowserShutdownRace) {
   // Confirm we do not incorrectly start shutdown when converting a popup into a
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 2527858f..c909731 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -881,6 +881,15 @@
     }
   }
 
+  if (base::FeatureList::IsEnabled(features::kTabstripDeclutter)) {
+    // TODO(crbug.com/369638354): Use the correct icon once finalized.
+    AddItemWithStringIdAndVectorIcon(this, IDC_DECLUTTER_TABS,
+                                     IDS_DECLUTTER_MENU, kAutoTabGroupsIcon);
+    SetIsNewFeatureAt(
+        GetIndexOfCommandId(IDC_DECLUTTER_TABS).value(),
+        browser->window()->MaybeShowNewBadgeFor(features::kTabstripDeclutter));
+  }
+
   AddItemWithStringIdAndVectorIcon(this, IDC_NAME_WINDOW, IDS_NAME_WINDOW,
                                    kNameWindowIcon);
 
diff --git a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
index 1363254..3d7bceb 100644
--- a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
@@ -143,7 +143,8 @@
 class TestAppMenuModelCR2023 : public AppMenuModelTest {
  public:
   TestAppMenuModelCR2023() {
-    feature_list_.InitWithFeatures({features::kTabOrganization}, {});
+    feature_list_.InitWithFeatures(
+        {features::kTabOrganization, features::kTabstripDeclutter}, {});
   }
 
   TestAppMenuModelCR2023(const TestAppMenuModelCR2023&) = delete;
@@ -378,6 +379,16 @@
   EXPECT_TRUE(toolModel.IsEnabledAt(organize_tabs_index));
 }
 
+TEST_F(TestAppMenuModelCR2023, DeclutterTabsItem) {
+  TabOrganizationUtils::GetInstance()->SetIgnoreOptGuideForTesting(true);
+  AppMenuModel model(this, browser());
+  model.Init();
+  ToolsMenuModel toolModel(&model, browser());
+  size_t declutter_tabs_index =
+      toolModel.GetIndexOfCommandId(IDC_DECLUTTER_TABS).value();
+  EXPECT_TRUE(toolModel.IsEnabledAt(declutter_tabs_index));
+}
+
 TEST_F(TestAppMenuModelCR2023, ModelHasIcons) {
   // Skip the items that are either not supposed to have an icon, or are not
   // ready to be tested. Remove items once they're ready for testing.
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index d76393e..a69151ab 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -1461,6 +1461,12 @@
       user_education::Metadata(
           128, "theocristea@google.com",
           "For passwords manual fallback; shown in the context menu.")));
+
+  registry.RegisterFeature(user_education::NewBadgeSpecification(
+      features::kTabstripDeclutter,
+      user_education::Metadata(
+          132, "emshack@chromium.org",
+          "Shown in app menu when Tab Declutter menu item is enabled.")));
 }
 
 std::unique_ptr<BrowserFeaturePromoController> CreateUserEducationResources(
diff --git a/tools/metrics/histograms/metadata/user_education/histograms.xml b/tools/metrics/histograms/metadata/user_education/histograms.xml
index 6b1d726b..f44e5a1a 100644
--- a/tools/metrics/histograms/metadata/user_education/histograms.xml
+++ b/tools/metrics/histograms/metadata/user_education/histograms.xml
@@ -45,6 +45,8 @@
       summary="Plus address manual fallback entry point in the context menu."/>
   <variant name="TabOrganization"
       summary="Promotion for Organize Tabs in the app menu."/>
+  <variant name="TabstripDeclutter"
+      summary="Promotion for Close Unused Tabs in the app menu."/>
 </variants>
 
 <variants name="TutorialID">