1 // Copyright 2017 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 "ash/shelf/shelf_context_menu_model.h"
6 
7 #include "ash/public/cpp/app_menu_constants.h"
8 #include "ash/public/cpp/shelf_item_delegate.h"
9 #include "ash/public/cpp/wallpaper_controller_client.h"
10 #include "ash/session/test_session_controller_client.h"
11 #include "ash/shelf/shelf.h"
12 #include "ash/shell.h"
13 #include "ash/test/ash_test_base.h"
14 #include "ash/test_shell_delegate.h"
15 #include "ash/wallpaper/wallpaper_controller_impl.h"
16 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "ui/display/display.h"
19 #include "ui/views/widget/widget.h"
20 
21 namespace ash {
22 namespace {
23 
24 using CommandId = ShelfContextMenuModel::CommandId;
25 
26 class ShelfContextMenuModelTest : public AshTestBase {
27  public:
28   ShelfContextMenuModelTest() = default;
29   ~ShelfContextMenuModelTest() override = default;
30 
SetUp()31   void SetUp() override {
32     AshTestBase::SetUp();
33     TestSessionControllerClient* session = GetSessionControllerClient();
34     session->AddUserSession("user1@test.com");
35     session->SetSessionState(session_manager::SessionState::ACTIVE);
36     session->SwitchActiveUser(AccountId::FromUserEmail("user1@test.com"));
37   }
38 
39  private:
40   DISALLOW_COPY_AND_ASSIGN(ShelfContextMenuModelTest);
41 };
42 
43 // A test wallpaper controller client class.
44 class TestWallpaperControllerClient : public WallpaperControllerClient {
45  public:
46   TestWallpaperControllerClient() = default;
47   virtual ~TestWallpaperControllerClient() = default;
48 
open_count() const49   size_t open_count() const { return open_count_; }
50 
51   // WallpaperControllerClient:
OpenWallpaperPicker()52   void OpenWallpaperPicker() override { open_count_++; }
MaybeClosePreviewWallpaper()53   void MaybeClosePreviewWallpaper() override {}
54 
55  private:
56   size_t open_count_ = 0;
57 
58   DISALLOW_COPY_AND_ASSIGN(TestWallpaperControllerClient);
59 };
60 
61 // A test shelf item delegate that records the commands sent for execution.
62 class TestShelfItemDelegate : public ShelfItemDelegate {
63  public:
TestShelfItemDelegate()64   TestShelfItemDelegate() : ShelfItemDelegate(ShelfID()) {}
65   ~TestShelfItemDelegate() override = default;
66 
last_executed_command() const67   int last_executed_command() const { return last_executed_command_; }
68 
69   // ShelfItemDelegate:
ItemSelected(std::unique_ptr<ui::Event> event,int64_t display_id,ShelfLaunchSource source,ItemSelectedCallback callback,const ItemFilterPredicate & filter_predicate)70   void ItemSelected(std::unique_ptr<ui::Event> event,
71                     int64_t display_id,
72                     ShelfLaunchSource source,
73                     ItemSelectedCallback callback,
74                     const ItemFilterPredicate& filter_predicate) override {}
ExecuteCommand(bool from_context_menu,int64_t command_id,int32_t event_flags,int64_t display_id)75   void ExecuteCommand(bool from_context_menu,
76                       int64_t command_id,
77                       int32_t event_flags,
78                       int64_t display_id) override {
79     ASSERT_TRUE(from_context_menu);
80     last_executed_command_ = command_id;
81   }
Close()82   void Close() override {}
83 
84  private:
85   int last_executed_command_ = 0;
86 
87   DISALLOW_COPY_AND_ASSIGN(TestShelfItemDelegate);
88 };
89 
90 // Tests the default items in a shelf context menu.
TEST_F(ShelfContextMenuModelTest,Basic)91 TEST_F(ShelfContextMenuModelTest, Basic) {
92   ShelfContextMenuModel menu(nullptr, GetPrimaryDisplay().id());
93 
94   ASSERT_EQ(3, menu.GetItemCount());
95   EXPECT_EQ(CommandId::MENU_AUTO_HIDE, menu.GetCommandIdAt(0));
96   EXPECT_EQ(CommandId::MENU_ALIGNMENT_MENU, menu.GetCommandIdAt(1));
97   EXPECT_EQ(CommandId::MENU_CHANGE_WALLPAPER, menu.GetCommandIdAt(2));
98   for (int i = 0; i < menu.GetItemCount(); ++i) {
99     EXPECT_TRUE(menu.IsEnabledAt(i));
100     EXPECT_TRUE(menu.IsVisibleAt(i));
101   }
102 
103   // Check the alignment submenu.
104   EXPECT_EQ(ui::MenuModel::TYPE_SUBMENU, menu.GetTypeAt(1));
105   ui::MenuModel* submenu = menu.GetSubmenuModelAt(1);
106   ASSERT_TRUE(submenu);
107   ASSERT_EQ(3, submenu->GetItemCount());
108   EXPECT_EQ(CommandId::MENU_ALIGNMENT_LEFT, submenu->GetCommandIdAt(0));
109   EXPECT_EQ(CommandId::MENU_ALIGNMENT_BOTTOM, submenu->GetCommandIdAt(1));
110   EXPECT_EQ(CommandId::MENU_ALIGNMENT_RIGHT, submenu->GetCommandIdAt(2));
111 }
112 
113 // Test invocation of the default menu items.
TEST_F(ShelfContextMenuModelTest,Invocation)114 TEST_F(ShelfContextMenuModelTest, Invocation) {
115   int64_t primary_id = GetPrimaryDisplay().id();
116   Shelf* shelf = GetPrimaryShelf();
117 
118   // Check the shelf auto-hide behavior and menu interaction.
119   ShelfContextMenuModel menu1(nullptr, primary_id);
120   EXPECT_EQ(ShelfAutoHideBehavior::kNever, shelf->auto_hide_behavior());
121   menu1.ActivatedAt(0);
122   EXPECT_EQ(ShelfAutoHideBehavior::kAlways, shelf->auto_hide_behavior());
123 
124   // Recreate the menu, auto-hide should still be enabled.
125   ShelfContextMenuModel menu2(nullptr, primary_id);
126   EXPECT_EQ(ShelfAutoHideBehavior::kAlways, shelf->auto_hide_behavior());
127 
128   // By default the shelf should be on bottom, shelf alignment options in order:
129   // Left, Bottom, Right. Bottom should be checked.
130   ui::MenuModel* submenu = menu2.GetSubmenuModelAt(1);
131   EXPECT_TRUE(submenu->IsItemCheckedAt(1));
132   EXPECT_EQ(ShelfAlignment::kBottom, shelf->alignment());
133 
134   // Activate the left shelf alignment option.
135   submenu->ActivatedAt(0);
136   EXPECT_EQ(ShelfAlignment::kLeft, shelf->alignment());
137 
138   // Recreate the menu, it should show left alignment checked.
139   ShelfContextMenuModel menu3(nullptr, primary_id);
140   submenu = menu3.GetSubmenuModelAt(1);
141   EXPECT_TRUE(submenu->IsItemCheckedAt(0));
142 
143   TestWallpaperControllerClient client;
144   Shell::Get()->wallpaper_controller()->SetClient(&client);
145   EXPECT_EQ(0u, client.open_count());
146 
147   // Click the third option, wallpaper picker. It should open.
148   menu3.ActivatedAt(2);
149   EXPECT_EQ(1u, client.open_count());
150 }
151 
152 // Tests custom items in a shelf context menu for an application.
TEST_F(ShelfContextMenuModelTest,CustomItems)153 TEST_F(ShelfContextMenuModelTest, CustomItems) {
154   // Pass a valid delegate to indicate the menu is for an application.
155   TestShelfItemDelegate delegate;
156   ShelfContextMenuModel menu(&delegate, GetPrimaryDisplay().id());
157 
158   // Because the delegate is valid, the context menu will not have the desktop
159   // menu options (autohide, shelf position, and wallpaper picker).
160   ASSERT_EQ(0, menu.GetItemCount());
161 
162   // Add some custom items.
163   menu.AddItem(203, base::ASCIIToUTF16("item"));
164   menu.AddCheckItem(107, base::ASCIIToUTF16("check"));
165   menu.AddRadioItem(101, base::ASCIIToUTF16("radio"), 0);
166   ui::SimpleMenuModel submenu(nullptr);
167   menu.AddSubMenu(55, base::ASCIIToUTF16("submenu"), &submenu);
168 
169   // Ensure the menu contents match the items above.
170   ASSERT_EQ(4, menu.GetItemCount());
171   EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(0));
172   EXPECT_EQ(ui::MenuModel::TYPE_CHECK, menu.GetTypeAt(1));
173   EXPECT_EQ(ui::MenuModel::TYPE_RADIO, menu.GetTypeAt(2));
174   EXPECT_EQ(ui::MenuModel::TYPE_SUBMENU, menu.GetTypeAt(3));
175 
176   // Invoking a custom item should execute the command id on the delegate.
177   menu.ActivatedAt(1);
178   EXPECT_EQ(107, delegate.last_executed_command());
179 }
180 
181 // Tests fullscreen's per-display removal of "Autohide shelf": crbug.com/496681
TEST_F(ShelfContextMenuModelTest,AutohideShelfOptionOnExternalDisplay)182 TEST_F(ShelfContextMenuModelTest, AutohideShelfOptionOnExternalDisplay) {
183   UpdateDisplay("940x550,940x550");
184   int64_t primary_id = GetPrimaryDisplay().id();
185   int64_t secondary_id = GetSecondaryDisplay().id();
186 
187   // Create a normal window on the primary display.
188   std::unique_ptr<views::Widget> widget = CreateTestWidget();
189   widget->Show();
190   widget->SetFullscreen(true);
191 
192   ShelfContextMenuModel primary_menu(nullptr, primary_id);
193   ShelfContextMenuModel secondary_menu(nullptr, secondary_id);
194   EXPECT_EQ(-1, primary_menu.GetIndexOfCommandId(CommandId::MENU_AUTO_HIDE));
195   EXPECT_NE(-1, secondary_menu.GetIndexOfCommandId(CommandId::MENU_AUTO_HIDE));
196 }
197 
198 // Tests that the autohide and alignment menu options are not included in tablet
199 // mode.
TEST_F(ShelfContextMenuModelTest,ExcludeClamshellOptionsOnTabletMode)200 TEST_F(ShelfContextMenuModelTest, ExcludeClamshellOptionsOnTabletMode) {
201   TabletModeController* tablet_mode_controller =
202       Shell::Get()->tablet_mode_controller();
203   int64_t primary_id = GetPrimaryDisplay().id();
204 
205   // In tablet mode, the wallpaper picker and auto-hide should be the only two
206   // options because other options are disabled.
207   tablet_mode_controller->SetEnabledForTest(true);
208   ShelfContextMenuModel menu1(nullptr, primary_id);
209   EXPECT_EQ(2, menu1.GetItemCount());
210   EXPECT_EQ(ShelfContextMenuModel::MENU_AUTO_HIDE, menu1.GetCommandIdAt(0));
211   EXPECT_EQ(ShelfContextMenuModel::MENU_CHANGE_WALLPAPER,
212             menu1.GetCommandIdAt(1));
213 
214   // Test that a menu shown out of tablet mode includes all three options:
215   // MENU_AUTO_HIDE, MENU_ALIGNMENT_MENU, and MENU_CHANGE_WALLPAPER.
216   tablet_mode_controller->SetEnabledForTest(false);
217   ShelfContextMenuModel menu2(nullptr, primary_id);
218   EXPECT_EQ(3, menu2.GetItemCount());
219 
220   // Test the auto hide option.
221   EXPECT_EQ(ShelfContextMenuModel::MENU_AUTO_HIDE, menu2.GetCommandIdAt(0));
222   EXPECT_TRUE(menu2.IsEnabledAt(0));
223 
224   // Test the shelf alignment menu option.
225   EXPECT_EQ(ShelfContextMenuModel::MENU_ALIGNMENT_MENU,
226             menu2.GetCommandIdAt(1));
227   EXPECT_TRUE(menu2.IsEnabledAt(1));
228 
229   // Test the shelf alignment submenu.
230   ui::MenuModel* submenu = menu2.GetSubmenuModelAt(1);
231   EXPECT_EQ(ShelfContextMenuModel::MENU_ALIGNMENT_LEFT,
232             submenu->GetCommandIdAt(0));
233   EXPECT_TRUE(submenu->IsEnabledAt(0));
234 
235   EXPECT_EQ(ShelfContextMenuModel::MENU_ALIGNMENT_BOTTOM,
236             submenu->GetCommandIdAt(1));
237   EXPECT_TRUE(submenu->IsEnabledAt(1));
238 
239   EXPECT_EQ(ShelfContextMenuModel::MENU_ALIGNMENT_RIGHT,
240             submenu->GetCommandIdAt(2));
241   EXPECT_TRUE(submenu->IsEnabledAt(2));
242 
243   // Test the wallpaper picker option.
244   EXPECT_EQ(ShelfContextMenuModel::MENU_CHANGE_WALLPAPER,
245             menu2.GetCommandIdAt(2));
246   EXPECT_TRUE(menu2.IsEnabledAt(2));
247 }
248 
TEST_F(ShelfContextMenuModelTest,CommandIdsMatchEnumsForHistograms)249 TEST_F(ShelfContextMenuModelTest, CommandIdsMatchEnumsForHistograms) {
250   // Tests that CommandId enums are not changed as the values are used in
251   // histograms.
252   EXPECT_EQ(500, ShelfContextMenuModel::MENU_AUTO_HIDE);
253   EXPECT_EQ(501, ShelfContextMenuModel::MENU_ALIGNMENT_MENU);
254   EXPECT_EQ(502, ShelfContextMenuModel::MENU_ALIGNMENT_LEFT);
255   EXPECT_EQ(503, ShelfContextMenuModel::MENU_ALIGNMENT_RIGHT);
256   EXPECT_EQ(504, ShelfContextMenuModel::MENU_ALIGNMENT_BOTTOM);
257   EXPECT_EQ(505, ShelfContextMenuModel::MENU_CHANGE_WALLPAPER);
258 }
259 
TEST_F(ShelfContextMenuModelTest,ShelfContextMenuOptions)260 TEST_F(ShelfContextMenuModelTest, ShelfContextMenuOptions) {
261   // Tests that there are exactly 3 shelf context menu options. If you're adding
262   // a context menu option ensure that you have added the enum to
263   // tools/metrics/enums.xml and that you haven't modified the order of the
264   // existing enums.
265   ShelfContextMenuModel menu(nullptr, GetPrimaryDisplay().id());
266   EXPECT_EQ(3, menu.GetItemCount());
267 }
268 
TEST_F(ShelfContextMenuModelTest,NotificationContainerEnabled)269 TEST_F(ShelfContextMenuModelTest, NotificationContainerEnabled) {
270   // Tests that NOTIFICATION_CONTAINER is enabled. This ensures that the
271   // container is able to handle gesture events.
272   ShelfContextMenuModel menu(nullptr, GetPrimaryDisplay().id());
273   menu.AddItem(NOTIFICATION_CONTAINER, base::string16());
274 
275   EXPECT_TRUE(menu.IsCommandIdEnabled(NOTIFICATION_CONTAINER));
276 }
277 
278 }  // namespace
279 }  // namespace ash
280