1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/ui/ash/launcher/shelf_context_menu.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "ash/public/cpp/app_menu_constants.h"
11 #include "ash/public/cpp/shelf_item.h"
12 #include "ash/public/cpp/shelf_model.h"
13 #include "base/files/file_path.h"
14 #include "base/macros.h"
15 #include "base/run_loop.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/test/bind.h"
19 #include "chrome/app/chrome_command_ids.h"
20 #include "chrome/browser/apps/app_service/app_service_test.h"
21 #include "chrome/browser/chromeos/arc/icon_decode_request.h"
22 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
23 #include "chrome/browser/chromeos/crostini/crostini_shelf_utils.h"
24 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
25 #include "chrome/browser/chromeos/crostini/crostini_util.h"
26 #include "chrome/browser/chromeos/guest_os/guest_os_registry_service.h"
27 #include "chrome/browser/chromeos/guest_os/guest_os_registry_service_factory.h"
28 #include "chrome/browser/extensions/extension_service.h"
29 #include "chrome/browser/extensions/test_extension_system.h"
30 #include "chrome/browser/installable/installable_metrics.h"
31 #include "chrome/browser/prefs/incognito_mode_prefs.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
34 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
35 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
36 #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
37 #include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
38 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
39 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
40 #include "chrome/browser/ui/ash/launcher/extension_shelf_context_menu.h"
41 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
42 #include "chrome/browser/web_applications/test/test_system_web_app_manager.h"
43 #include "chrome/browser/web_applications/test/test_web_app_provider.h"
44 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
45 #include "chrome/test/base/chrome_ash_test_base.h"
46 #include "chrome/test/base/testing_profile.h"
47 #include "components/arc/metrics/arc_metrics_constants.h"
48 #include "components/arc/mojom/app.mojom.h"
49 #include "components/arc/test/fake_app_instance.h"
50 #include "components/exo/shell_surface_util.h"
51 #include "components/prefs/pref_service.h"
52 #include "components/session_manager/core/session_manager.h"
53 #include "ui/aura/window_event_dispatcher.h"
54 #include "ui/base/l10n/l10n_util.h"
55 #include "ui/display/display.h"
56 #include "ui/views/widget/widget.h"
57 #include "url/gurl.h"
58 
59 using crostini::CrostiniTestHelper;
60 
61 namespace {
62 
GetAppMenuItems(ash::ShelfItemDelegate * delegate,int event_flags)63 ash::ShelfItemDelegate::AppMenuItems GetAppMenuItems(
64     ash::ShelfItemDelegate* delegate,
65     int event_flags) {
66   return delegate->GetAppMenuItems(event_flags, base::NullCallback());
67 }
68 
IsItemPresentInMenu(ui::MenuModel * menu,int command_id)69 bool IsItemPresentInMenu(ui::MenuModel* menu, int command_id) {
70   ui::MenuModel* model = menu;
71   int index = 0;
72   return ui::MenuModel::GetModelAndIndexForCommandId(command_id, &model,
73                                                      &index);
74 }
75 
IsItemEnabledInMenu(ui::MenuModel * menu,int command_id)76 bool IsItemEnabledInMenu(ui::MenuModel* menu, int command_id) {
77   ui::MenuModel* model = menu;
78   int index = 0;
79   return ui::MenuModel::GetModelAndIndexForCommandId(command_id, &model,
80                                                      &index) &&
81          menu->IsEnabledAt(index);
82 }
83 
GetAppNameInShelfGroup(uint32_t task_id)84 std::string GetAppNameInShelfGroup(uint32_t task_id) {
85   return base::StringPrintf("AppInShelfGroup%d", task_id);
86 }
87 
88 class ShelfContextMenuTest : public ChromeAshTestBase {
89  protected:
90   ShelfContextMenuTest() = default;
91   ~ShelfContextMenuTest() override = default;
92 
SetUp()93   void SetUp() override {
94     ChromeAshTestBase::SetUp();
95 
96     extensions::TestExtensionSystem* extension_system(
97         static_cast<extensions::TestExtensionSystem*>(
98             extensions::ExtensionSystem::Get(&profile_)));
99     extension_service_ = extension_system->CreateExtensionService(
100         base::CommandLine::ForCurrentProcess(), base::FilePath(), false);
101     extension_service_->Init();
102 
103     ConfigureWebAppProvider();
104     web_app::TestWebAppProvider::Get(&profile_)->Start();
105 
106     crostini_helper_ = std::make_unique<CrostiniTestHelper>(profile());
107     crostini_helper_->ReInitializeAppServiceIntegration();
108 
109     app_service_test_.SetUp(&profile_);
110     arc_test_.SetUp(&profile_);
111 
112     session_manager_ = std::make_unique<session_manager::SessionManager>();
113     model_ = std::make_unique<ash::ShelfModel>();
114     launcher_controller_ =
115         std::make_unique<ChromeLauncherController>(&profile_, model_.get());
116     launcher_controller_->Init();
117 
118     // Disable safe icon decoding to ensure ArcAppShortcutRequests returns in
119     // the test environment.
120     ArcAppIcon::DisableSafeDecodingForTesting();
121     arc::IconDecodeRequest::DisableSafeDecodingForTesting();
122   }
123 
CreateShelfContextMenu(ash::ShelfItemType shelf_item_type,int64_t display_id)124   std::unique_ptr<ShelfContextMenu> CreateShelfContextMenu(
125       ash::ShelfItemType shelf_item_type,
126       int64_t display_id) {
127     ash::ShelfItem item;
128     item.id = ash::ShelfID("idmockidmockidmockidmockidmockid");
129     item.type = shelf_item_type;
130     return ShelfContextMenu::Create(controller(), &item, display_id);
131   }
132 
133   // Creates app window and set optional ARC application id.
CreateArcWindow(const std::string & window_app_id)134   views::Widget* CreateArcWindow(const std::string& window_app_id) {
135     views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
136     views::Widget* widget = new views::Widget();
137     params.context = GetContext();
138     widget->Init(std::move(params));
139     widget->Show();
140     widget->Activate();
141     exo::SetShellApplicationId(widget->GetNativeWindow(), window_app_id);
142     return widget;
143   }
144 
GetMenuModel(ShelfContextMenu * shelf_context_menu)145   std::unique_ptr<ui::MenuModel> GetMenuModel(
146       ShelfContextMenu* shelf_context_menu) {
147     base::RunLoop run_loop;
148     std::unique_ptr<ui::MenuModel> menu;
149     shelf_context_menu->GetMenuModel(base::BindLambdaForTesting(
150         [&](std::unique_ptr<ui::SimpleMenuModel> created_menu) {
151           menu = std::move(created_menu);
152           run_loop.Quit();
153         }));
154     run_loop.Run();
155     return menu;
156   }
157 
GetContextMenu(ash::ShelfItemDelegate * item_delegate,int64_t display_id)158   std::unique_ptr<ui::MenuModel> GetContextMenu(
159       ash::ShelfItemDelegate* item_delegate,
160       int64_t display_id) {
161     base::RunLoop run_loop;
162     std::unique_ptr<ui::MenuModel> menu;
163     item_delegate->GetContextMenu(
164         display_id, base::BindLambdaForTesting(
165                         [&](std::unique_ptr<ui::SimpleMenuModel> created_menu) {
166                           menu = std::move(created_menu);
167                           run_loop.Quit();
168                         }));
169     run_loop.Run();
170     return menu;
171   }
172 
TearDown()173   void TearDown() override {
174     launcher_controller_.reset();
175 
176     arc_test_.TearDown();
177 
178     crostini_helper_.reset();
179 
180     ChromeAshTestBase::TearDown();
181   }
182 
arc_test()183   ArcAppTest& arc_test() { return arc_test_; }
184 
app_service_test()185   apps::AppServiceTest& app_service_test() { return app_service_test_; }
186 
profile()187   TestingProfile* profile() { return &profile_; }
188 
crostini_helper()189   CrostiniTestHelper* crostini_helper() { return crostini_helper_.get(); }
190 
controller()191   ChromeLauncherController* controller() { return launcher_controller_.get(); }
192 
model()193   ash::ShelfModel* model() { return model_.get(); }
194 
SendRefreshAppList(const std::vector<arc::mojom::AppInfo> & apps)195   void SendRefreshAppList(const std::vector<arc::mojom::AppInfo>& apps) {
196     arc_test_.app_instance()->SendRefreshAppList(apps);
197     app_service_test_.FlushMojoCalls();
198   }
199 
LaunchApp(const std::string & app_id,const arc::mojom::AppInfo & info,int32_t task_id)200   void LaunchApp(const std::string& app_id,
201                  const arc::mojom::AppInfo& info,
202                  int32_t task_id) {
203     arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON,
204                    arc::UserInteractionType::NOT_USER_INITIATED);
205 
206     // AppService checks the task id to decide whether the app is running, so
207     // create the task id to simulate the running app.
208     arc_test_.app_instance()->SendTaskCreated(task_id, info, std::string());
209   }
210 
211  private:
ConfigureWebAppProvider()212   void ConfigureWebAppProvider() {
213     auto system_web_app_manager =
214         std::make_unique<web_app::TestSystemWebAppManager>(&profile_);
215 
216     auto* provider = web_app::TestWebAppProvider::Get(&profile_);
217     provider->SetSystemWebAppManager(std::move(system_web_app_manager));
218     provider->SetRunSubsystemStartupTasks(true);
219   }
220 
221   TestingProfile profile_;
222   std::unique_ptr<CrostiniTestHelper> crostini_helper_;
223   ArcAppTest arc_test_;
224   apps::AppServiceTest app_service_test_;
225   std::unique_ptr<session_manager::SessionManager> session_manager_;
226   std::unique_ptr<ash::ShelfModel> model_;
227   std::unique_ptr<ChromeLauncherController> launcher_controller_;
228   extensions::ExtensionService* extension_service_ = nullptr;
229 
230   DISALLOW_COPY_AND_ASSIGN(ShelfContextMenuTest);
231 };
232 
233 // Verifies that "New Incognito window" menu item in the launcher context
234 // menu is disabled when Incognito mode is switched off (by a policy).
TEST_F(ShelfContextMenuTest,NewIncognitoWindowMenuIsDisabledWhenIncognitoModeOff)235 TEST_F(ShelfContextMenuTest,
236        NewIncognitoWindowMenuIsDisabledWhenIncognitoModeOff) {
237   const int64_t display_id = GetPrimaryDisplay().id();
238   // Initially, "New Incognito window" should be enabled.
239   std::unique_ptr<ShelfContextMenu> shelf_context_menu =
240       CreateShelfContextMenu(ash::TYPE_BROWSER_SHORTCUT, display_id);
241   std::unique_ptr<ui::MenuModel> menu = GetMenuModel(shelf_context_menu.get());
242   ASSERT_TRUE(IsItemPresentInMenu(menu.get(), ash::MENU_NEW_INCOGNITO_WINDOW));
243   EXPECT_TRUE(
244       shelf_context_menu->IsCommandIdEnabled(ash::MENU_NEW_INCOGNITO_WINDOW));
245 
246   // Disable Incognito mode.
247   IncognitoModePrefs::SetAvailability(profile()->GetPrefs(),
248                                       IncognitoModePrefs::DISABLED);
249   shelf_context_menu =
250       CreateShelfContextMenu(ash::TYPE_BROWSER_SHORTCUT, display_id);
251   menu = GetMenuModel(shelf_context_menu.get());
252   // The item should be disabled, and therefore not added to the menu.
253   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_NEW_INCOGNITO_WINDOW));
254   EXPECT_FALSE(
255       shelf_context_menu->IsCommandIdEnabled(ash::MENU_NEW_INCOGNITO_WINDOW));
256 }
257 
258 // Verifies that "New window" menu item in the launcher context
259 // menu is disabled when Incognito mode is forced (by a policy).
TEST_F(ShelfContextMenuTest,NewWindowMenuIsDisabledWhenIncognitoModeForced)260 TEST_F(ShelfContextMenuTest, NewWindowMenuIsDisabledWhenIncognitoModeForced) {
261   const int64_t display_id = GetPrimaryDisplay().id();
262   // Initially, "New window" should be enabled.
263   std::unique_ptr<ShelfContextMenu> shelf_context_menu =
264       CreateShelfContextMenu(ash::TYPE_BROWSER_SHORTCUT, display_id);
265   std::unique_ptr<ui::MenuModel> menu = GetMenuModel(shelf_context_menu.get());
266   ASSERT_TRUE(IsItemPresentInMenu(menu.get(), ash::MENU_NEW_WINDOW));
267   EXPECT_TRUE(shelf_context_menu->IsCommandIdEnabled(ash::MENU_NEW_WINDOW));
268 
269   // Disable Incognito mode.
270   IncognitoModePrefs::SetAvailability(profile()->GetPrefs(),
271                                       IncognitoModePrefs::FORCED);
272   shelf_context_menu =
273       CreateShelfContextMenu(ash::TYPE_BROWSER_SHORTCUT, display_id);
274   menu = GetMenuModel(shelf_context_menu.get());
275   ASSERT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_NEW_WINDOW));
276   EXPECT_FALSE(shelf_context_menu->IsCommandIdEnabled(ash::MENU_NEW_WINDOW));
277 }
278 
279 // Verifies that "Close" is not shown in context menu if no browser window is
280 // opened.
TEST_F(ShelfContextMenuTest,DesktopShellShelfContextMenuVerifyCloseItem)281 TEST_F(ShelfContextMenuTest, DesktopShellShelfContextMenuVerifyCloseItem) {
282   const int64_t display_id = GetPrimaryDisplay().id();
283   std::unique_ptr<ShelfContextMenu> shelf_context_menu =
284       CreateShelfContextMenu(ash::TYPE_BROWSER_SHORTCUT, display_id);
285   std::unique_ptr<ui::MenuModel> menu = GetMenuModel(shelf_context_menu.get());
286   ASSERT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_CLOSE));
287 }
288 
289 // Verifies context menu and app menu items for ARC app.
290 // The 0th item is sticky but not the following.
TEST_F(ShelfContextMenuTest,ArcLauncherMenusCheck)291 TEST_F(ShelfContextMenuTest, ArcLauncherMenusCheck) {
292   arc_test().app_instance()->SendRefreshAppList(
293       std::vector<arc::mojom::AppInfo>(arc_test().fake_apps().begin(),
294                                        arc_test().fake_apps().begin() + 1));
295   app_service_test().WaitForAppService();
296   const std::string app_id = ArcAppTest::GetAppId(arc_test().fake_apps()[0]);
297   const std::string app_name = arc_test().fake_apps()[0].name;
298 
299   controller()->PinAppWithID(app_id);
300 
301   const ash::ShelfID shelf_id(app_id);
302   const ash::ShelfItem* item = controller()->GetItem(shelf_id);
303   ASSERT_TRUE(item);
304   EXPECT_EQ(base::UTF8ToUTF16(app_name), item->title);
305   ash::ShelfItemDelegate* item_delegate =
306       model()->GetShelfItemDelegate(shelf_id);
307   ASSERT_TRUE(item_delegate);
308   EXPECT_TRUE(GetAppMenuItems(item_delegate, 0 /* event_flags */).empty());
309 
310   const int64_t display_id = GetPrimaryDisplay().id();
311   std::unique_ptr<ui::MenuModel> menu =
312       GetContextMenu(item_delegate, display_id);
313   ASSERT_TRUE(menu);
314 
315   // ARC app is pinned but not running.
316   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_OPEN_NEW));
317   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_PIN));
318   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::SHOW_APP_INFO));
319   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
320   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_CLOSE));
321 
322   // ARC app is running.
323   std::string window_app_id1("org.chromium.arc.1");
324   CreateArcWindow(window_app_id1);
325   arc_test().app_instance()->SendTaskCreated(1, arc_test().fake_apps()[0],
326                                              std::string());
327   app_service_test().WaitForAppService();
328 
329   item_delegate = model()->GetShelfItemDelegate(shelf_id);
330   ASSERT_TRUE(item_delegate);
331   auto menu_list = GetAppMenuItems(item_delegate, 0 /* event_flags */);
332   ASSERT_EQ(1U, menu_list.size());
333   EXPECT_EQ(base::UTF8ToUTF16(app_name), menu_list[0].title);
334 
335   menu = GetContextMenu(item_delegate, display_id);
336   ASSERT_TRUE(menu);
337 
338   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_OPEN_NEW));
339   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_PIN));
340   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_CLOSE));
341   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::SHOW_APP_INFO));
342   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
343 
344   // ARC non-launchable app is running.
345   const std::string app_id2 = ArcAppTest::GetAppId(arc_test().fake_apps()[1]);
346   const std::string app_name2 = arc_test().fake_apps()[1].name;
347   std::string window_app_id2("org.chromium.arc.2");
348   CreateArcWindow(window_app_id2);
349   arc_test().app_instance()->SendTaskCreated(2, arc_test().fake_apps()[1],
350                                              std::string());
351   app_service_test().WaitForAppService();
352   const ash::ShelfID shelf_id2(app_id2);
353   const ash::ShelfItem* item2 = controller()->GetItem(shelf_id2);
354   ASSERT_TRUE(item2);
355   EXPECT_EQ(base::UTF8ToUTF16(app_name2), item2->title);
356   ash::ShelfItemDelegate* item_delegate2 =
357       model()->GetShelfItemDelegate(shelf_id2);
358   ASSERT_TRUE(item_delegate2);
359 
360   menu_list = GetAppMenuItems(item_delegate2, 0 /* event_flags */);
361   ASSERT_EQ(1U, menu_list.size());
362   EXPECT_EQ(base::UTF8ToUTF16(app_name2), menu_list[0].title);
363 
364   menu = GetContextMenu(item_delegate2, display_id);
365   ASSERT_TRUE(menu);
366 
367   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_OPEN_NEW));
368   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_PIN));
369   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_CLOSE));
370   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::SHOW_APP_INFO));
371   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
372 
373   // Shelf group context menu.
374   std::vector<arc::mojom::ShortcutInfo> shortcuts = arc_test().fake_shortcuts();
375   shortcuts[0].intent_uri +=
376       ";S.org.chromium.arc.shelf_group_id=arc_test_shelf_group;end";
377   arc_test().app_instance()->SendInstallShortcuts(shortcuts);
378   app_service_test().WaitForAppService();
379   const std::string app_id3 =
380       arc::ArcAppShelfId("arc_test_shelf_group",
381                          ArcAppTest::GetAppId(arc_test().fake_apps()[2]))
382           .ToString();
383 
384   constexpr int apps_to_test_in_shelf_group = 2;
385   const std::string app_name3 = arc_test().fake_apps()[2].name;
386   for (uint32_t i = 0; i < apps_to_test_in_shelf_group; ++i) {
387     const uint32_t task_id = 3 + i;
388     std::string window_app_id3 =
389         base::StringPrintf("org.chromium.arc.%d", task_id);
390     CreateArcWindow(window_app_id3);
391     arc_test().app_instance()->SendTaskCreated(
392         task_id, arc_test().fake_apps()[2], shortcuts[0].intent_uri);
393     // Set custom name.
394     arc_test().app_instance()->SendTaskDescription(
395         task_id, GetAppNameInShelfGroup(task_id),
396         std::string() /* icon_png_data_as_string */);
397     app_service_test().WaitForAppService();
398     const ash::ShelfID shelf_id3(app_id3);
399     const ash::ShelfItem* item3 = controller()->GetItem(shelf_id3);
400     ASSERT_TRUE(item3);
401 
402     // Validate item label is correct
403     EXPECT_EQ(base::UTF8ToUTF16(app_name3), item3->title);
404 
405     ash::ShelfItemDelegate* item_delegate3 =
406         model()->GetShelfItemDelegate(shelf_id3);
407     ASSERT_TRUE(item_delegate3);
408 
409     menu = GetContextMenu(item_delegate3, display_id);
410     ASSERT_TRUE(menu);
411 
412     EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_OPEN_NEW));
413     EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_PIN));
414     EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_CLOSE));
415     EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::SHOW_APP_INFO));
416     EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
417 
418     menu_list = GetAppMenuItems(item_delegate3, 0 /* event_flags */);
419     ASSERT_EQ(i + 1, menu_list.size());
420 
421     // Ensure custom names are set in the app menu items. Note, they are
422     // in reverse order, based on activation order.
423     for (uint32_t j = 0; j <= i; ++j) {
424       EXPECT_EQ(base::UTF8ToUTF16(GetAppNameInShelfGroup(3 + j)),
425                 menu_list[i - j].title);
426     }
427   }
428 }
429 
TEST_F(ShelfContextMenuTest,ArcLauncherSuspendAppMenu)430 TEST_F(ShelfContextMenuTest, ArcLauncherSuspendAppMenu) {
431   arc::mojom::AppInfo app = arc_test().fake_apps()[0];
432   app.suspended = true;
433   SendRefreshAppList({app});
434   const std::string app_id = ArcAppTest::GetAppId(app);
435 
436   controller()->PinAppWithID(app_id);
437 
438   const ash::ShelfID shelf_id(app_id);
439   const ash::ShelfItem* item = controller()->GetItem(shelf_id);
440   ASSERT_TRUE(item);
441   ash::ShelfItemDelegate* item_delegate =
442       model()->GetShelfItemDelegate(shelf_id);
443   ASSERT_TRUE(item_delegate);
444   EXPECT_TRUE(GetAppMenuItems(item_delegate, 0 /* event_flags */).empty());
445 
446   const int64_t display_id = GetPrimaryDisplay().id();
447   std::unique_ptr<ui::MenuModel> menu =
448       GetContextMenu(item_delegate, display_id);
449   ASSERT_TRUE(menu);
450 
451   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_OPEN_NEW));
452   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_PIN));
453   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_CLOSE));
454   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::SHOW_APP_INFO));
455   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
456 }
457 
TEST_F(ShelfContextMenuTest,ArcDeferredShelfContextMenuItemCheck)458 TEST_F(ShelfContextMenuTest, ArcDeferredShelfContextMenuItemCheck) {
459   SendRefreshAppList(std::vector<arc::mojom::AppInfo>(
460       arc_test().fake_apps().begin(), arc_test().fake_apps().begin() + 2));
461   const std::string app_id1 = ArcAppTest::GetAppId(arc_test().fake_apps()[0]);
462   const std::string app_id2 = ArcAppTest::GetAppId(arc_test().fake_apps()[1]);
463 
464   controller()->PinAppWithID(app_id1);
465 
466   arc_test().StopArcInstance();
467 
468   const ash::ShelfID shelf_id1(app_id1);
469   const ash::ShelfID shelf_id2(app_id2);
470 
471   EXPECT_TRUE(controller()->GetItem(shelf_id1));
472   EXPECT_FALSE(controller()->GetItem(shelf_id2));
473 
474   LaunchApp(app_id1, arc_test().fake_apps()[0], 1);
475   LaunchApp(app_id2, arc_test().fake_apps()[1], 2);
476 
477   EXPECT_TRUE(controller()->GetItem(shelf_id1));
478   EXPECT_TRUE(controller()->GetItem(shelf_id2));
479 
480   ash::ShelfItemDelegate* item_delegate =
481       model()->GetShelfItemDelegate(shelf_id1);
482   ASSERT_TRUE(item_delegate);
483   std::unique_ptr<ui::MenuModel> menu =
484       GetContextMenu(item_delegate, 0 /* display_id */);
485   ASSERT_TRUE(menu);
486 
487   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_OPEN_NEW));
488   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_PIN));
489   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_CLOSE));
490   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::SHOW_APP_INFO));
491   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
492 
493   item_delegate = model()->GetShelfItemDelegate(shelf_id2);
494   ASSERT_TRUE(item_delegate);
495   menu = GetContextMenu(item_delegate, 0 /* display_id */);
496   ASSERT_TRUE(menu);
497 
498   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_OPEN_NEW));
499   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_PIN));
500   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_CLOSE));
501   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::SHOW_APP_INFO));
502   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
503 }
504 
TEST_F(ShelfContextMenuTest,CommandIdsMatchEnumsForHistograms)505 TEST_F(ShelfContextMenuTest, CommandIdsMatchEnumsForHistograms) {
506   // Tests that CommandId enums are not changed as the values are used in
507   // histograms.
508   EXPECT_EQ(0, ash::MENU_OPEN_NEW);
509   EXPECT_EQ(1, ash::MENU_CLOSE);
510   EXPECT_EQ(2, ash::MENU_PIN);
511   EXPECT_EQ(3, ash::LAUNCH_TYPE_PINNED_TAB);
512   EXPECT_EQ(4, ash::LAUNCH_TYPE_REGULAR_TAB);
513   EXPECT_EQ(5, ash::LAUNCH_TYPE_FULLSCREEN);
514   EXPECT_EQ(6, ash::LAUNCH_TYPE_WINDOW);
515   EXPECT_EQ(7, ash::MENU_NEW_WINDOW);
516   EXPECT_EQ(8, ash::MENU_NEW_INCOGNITO_WINDOW);
517   EXPECT_EQ(9, ash::NOTIFICATION_CONTAINER);
518 }
519 
TEST_F(ShelfContextMenuTest,ArcContextMenuOptions)520 TEST_F(ShelfContextMenuTest, ArcContextMenuOptions) {
521   // Tests that there are the right number of ARC app context menu options. If
522   // you're adding a context menu option ensure that you have added the enum to
523   // tools/metrics/histograms/enums.xml and that you haven't modified the order
524   // of the existing enums.
525   SendRefreshAppList(std::vector<arc::mojom::AppInfo>(
526       arc_test().fake_apps().begin(), arc_test().fake_apps().begin() + 1));
527   const std::string app_id = ArcAppTest::GetAppId(arc_test().fake_apps()[0]);
528   const ash::ShelfID shelf_id(app_id);
529 
530   controller()->PinAppWithID(app_id);
531   const ash::ShelfItem* item = controller()->GetItem(shelf_id);
532   ASSERT_TRUE(item);
533   ash::ShelfItemDelegate* item_delegate =
534       model()->GetShelfItemDelegate(shelf_id);
535   ASSERT_TRUE(item_delegate);
536   int64_t primary_id = GetPrimaryDisplay().id();
537   std::unique_ptr<ui::MenuModel> menu =
538       GetContextMenu(item_delegate, primary_id);
539 
540   // Test that there are 9 items in an ARC app context menu.
541   EXPECT_EQ(9, menu->GetItemCount());
542 }
543 
544 // Tests that the context menu of internal app  is correct.
TEST_F(ShelfContextMenuTest,InternalAppShelfContextMenu)545 TEST_F(ShelfContextMenuTest, InternalAppShelfContextMenu) {
546   const std::vector<app_list::InternalApp> internal_apps(
547       app_list::GetInternalAppList(profile()));
548   for (const auto& internal_app : internal_apps) {
549     if (!internal_app.show_in_launcher)
550       continue;
551 
552     const std::string app_id = internal_app.app_id;
553     const ash::ShelfID shelf_id(app_id);
554     // Pin internal app.
555     controller()->PinAppWithID(app_id);
556     const ash::ShelfItem* item = controller()->GetItem(ash::ShelfID(app_id));
557     ASSERT_TRUE(item);
558     EXPECT_EQ(l10n_util::GetStringUTF16(internal_app.name_string_resource_id),
559               item->title);
560     ash::ShelfItemDelegate* item_delegate =
561         model()->GetShelfItemDelegate(shelf_id);
562     ASSERT_TRUE(item_delegate);
563 
564     const int64_t display_id = GetPrimaryDisplay().id();
565     std::unique_ptr<ui::MenuModel> menu =
566         GetContextMenu(item_delegate, display_id);
567     ASSERT_TRUE(menu);
568 
569     // Internal app is pinned but not running.
570     EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_OPEN_NEW));
571     EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::MENU_PIN));
572     EXPECT_FALSE(IsItemPresentInMenu(menu.get(), ash::MENU_CLOSE));
573   }
574 }
575 
576 // Tests that the number of context menu options of internal app is correct.
TEST_F(ShelfContextMenuTest,InternalAppShelfContextMenuOptionsNumber)577 TEST_F(ShelfContextMenuTest, InternalAppShelfContextMenuOptionsNumber) {
578   const std::vector<app_list::InternalApp> internal_apps(
579       app_list::GetInternalAppList(profile()));
580   for (const auto& internal_app : internal_apps) {
581     const std::string app_id = internal_app.app_id;
582     const ash::ShelfID shelf_id(app_id);
583     // Pin internal app.
584     controller()->PinAppWithID(app_id);
585     const ash::ShelfItem* item = controller()->GetItem(ash::ShelfID(app_id));
586     ASSERT_TRUE(item);
587 
588     ash::ShelfItemDelegate* item_delegate =
589         model()->GetShelfItemDelegate(shelf_id);
590     ASSERT_TRUE(item_delegate);
591     int64_t primary_id = GetPrimaryDisplay().id();
592     std::unique_ptr<ui::MenuModel> menu =
593         GetContextMenu(item_delegate, primary_id);
594 
595     const int expected_options_num = internal_app.show_in_launcher ? 2 : 1;
596     EXPECT_EQ(expected_options_num, menu->GetItemCount());
597   }
598 }
599 
600 // Checks some properties for crostini's terminal app's context menu,
601 // specifically that every menu item has an icon.
TEST_F(ShelfContextMenuTest,CrostiniTerminalApp)602 TEST_F(ShelfContextMenuTest, CrostiniTerminalApp) {
603   const std::string app_id = crostini::kCrostiniTerminalSystemAppId;
604   crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
605       crostini::kCrostiniDefaultVmName);
606 
607   controller()->PinAppWithID(app_id);
608   const ash::ShelfItem* item = controller()->GetItem(ash::ShelfID(app_id));
609   ASSERT_TRUE(item);
610 
611   ash::ShelfItemDelegate* item_delegate =
612       model()->GetShelfItemDelegate(ash::ShelfID(app_id));
613   ASSERT_TRUE(item_delegate);
614   int64_t primary_id = GetPrimaryDisplay().id();
615   std::unique_ptr<ui::MenuModel> menu =
616       GetContextMenu(item_delegate, primary_id);
617 
618   // Check that every menu item has an icon
619   for (int i = 0; i < menu->GetItemCount(); ++i)
620     EXPECT_FALSE(menu->GetIconAt(i).IsEmpty());
621 
622   // When crostini is running, the terminal should have an option to kill the
623   // vm.
624   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::SHUTDOWN_GUEST_OS));
625 }
626 
627 // Checks the context menu for a "normal" crostini app (i.e. a registered one).
628 // Particularly, we ensure that the density changing option exists.
TEST_F(ShelfContextMenuTest,CrostiniNormalApp)629 TEST_F(ShelfContextMenuTest, CrostiniNormalApp) {
630   const std::string app_name = "foo";
631   crostini_helper()->AddApp(crostini::CrostiniTestHelper::BasicApp(app_name));
632   app_service_test().FlushMojoCalls();
633   const std::string app_id =
634       crostini::CrostiniTestHelper::GenerateAppId(app_name);
635   guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile())
636       ->AppLaunched(app_id);
637 
638   controller()->PinAppWithID(app_id);
639   const ash::ShelfItem* item = controller()->GetItem(ash::ShelfID(app_id));
640   ASSERT_TRUE(item);
641 
642   ash::ShelfItemDelegate* item_delegate =
643       model()->GetShelfItemDelegate(ash::ShelfID(app_id));
644   ASSERT_TRUE(item_delegate);
645   int64_t primary_id = GetPrimaryDisplay().id();
646 
647   // We force a scale factor of 2.0, to check that the normal app has a menu
648   // option to change the dpi settings.
649   UpdateDisplay("1920x1080*2.0");
650 
651   std::unique_ptr<ui::MenuModel> menu =
652       GetContextMenu(item_delegate, primary_id);
653 
654   // Check that every menu item has an icon
655   for (int i = 0; i < menu->GetItemCount(); ++i)
656     EXPECT_FALSE(menu->GetIconAt(i).IsEmpty());
657 
658   // Precisely which density option is shown is not important to us, we only
659   // care that one is shown.
660   EXPECT_TRUE(IsItemEnabledInMenu(menu.get(), ash::CROSTINI_USE_LOW_DENSITY) ||
661               IsItemEnabledInMenu(menu.get(), ash::CROSTINI_USE_HIGH_DENSITY));
662   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
663   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::SHOW_APP_INFO));
664   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
665 }
666 
667 // Confirms the menu items for unregistered crostini apps (i.e. apps that do not
668 // have an associated .desktop file, and therefore can only be closed).
TEST_F(ShelfContextMenuTest,CrostiniUnregisteredApps)669 TEST_F(ShelfContextMenuTest, CrostiniUnregisteredApps) {
670   const std::string fake_window_app_id = "foo";
671   const std::string fake_window_startup_id = "bar";
672   const std::string app_id = crostini::GetCrostiniShelfAppId(
673       profile(), &fake_window_app_id, &fake_window_startup_id);
674   controller()->PinAppWithID(app_id);
675   const ash::ShelfItem* item = controller()->GetItem(ash::ShelfID(app_id));
676   ASSERT_TRUE(item);
677 
678   ash::ShelfItemDelegate* item_delegate =
679       model()->GetShelfItemDelegate(ash::ShelfID(app_id));
680   ASSERT_TRUE(item_delegate);
681   int64_t primary_id = GetPrimaryDisplay().id();
682   std::unique_ptr<ui::MenuModel> menu =
683       GetContextMenu(item_delegate, primary_id);
684 
685   EXPECT_EQ(menu->GetItemCount(), 1);
686   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::MENU_NEW_WINDOW));
687 }
688 
TEST_F(ShelfContextMenuTest,WebApp)689 TEST_F(ShelfContextMenuTest, WebApp) {
690   constexpr char kWebAppUrl[] = "https://webappone.com/";
691   constexpr char kWebAppName[] = "WebApp1";
692 
693   app_service_test().FlushMojoCalls();
694   const web_app::AppId app_id =
695       web_app::InstallDummyWebApp(profile(), kWebAppName, GURL(kWebAppUrl));
696 
697   controller()->PinAppWithID(app_id);
698   const ash::ShelfItem* item = controller()->GetItem(ash::ShelfID(app_id));
699   ASSERT_TRUE(item);
700 
701   ash::ShelfItemDelegate* item_delegate =
702       model()->GetShelfItemDelegate(ash::ShelfID(app_id));
703   ASSERT_TRUE(item_delegate);
704   int64_t primary_id = GetPrimaryDisplay().id();
705   std::unique_ptr<ui::MenuModel> menu =
706       GetContextMenu(item_delegate, primary_id);
707 
708   // Check that every menu item has an icon
709   for (int i = 0; i < menu->GetItemCount(); ++i)
710     EXPECT_FALSE(menu->GetIconAt(i).IsEmpty());
711 
712   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
713   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::SHOW_APP_INFO));
714   EXPECT_FALSE(IsItemEnabledInMenu(menu.get(), ash::UNINSTALL));
715 }
716 
717 }  // namespace
718