1 // Copyright (c) 2012 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/toolbar/app_menu_model.h"
6
7 #include "base/macros.h"
8 #include "base/memory/ptr_util.h"
9 #include "chrome/app/chrome_command_ids.h"
10 #include "chrome/browser/defaults.h"
11 #include "chrome/browser/prefs/browser_prefs.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/global_error/global_error.h"
14 #include "chrome/browser/ui/global_error/global_error_service.h"
15 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
18 #include "chrome/browser/upgrade_detector/upgrade_detector.h"
19 #include "chrome/test/base/browser_with_test_window_test.h"
20 #include "chrome/test/base/menu_model_test.h"
21 #include "chrome/test/base/testing_browser_process.h"
22 #include "chrome/test/base/testing_profile.h"
23 #include "components/sync_preferences/testing_pref_service_syncable.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "ui/gfx/color_palette.h"
26
27 #if defined(OS_CHROMEOS)
28 #include "chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h"
29 #include "components/policy/core/common/policy_pref_names.h"
30 #endif // defined(OS_CHROMEOS)
31
32 namespace {
33
34 // Error class has a menu item.
35 class MenuError : public GlobalError {
36 public:
MenuError(int command_id)37 explicit MenuError(int command_id)
38 : command_id_(command_id),
39 execute_count_(0) {
40 }
41
execute_count()42 int execute_count() { return execute_count_; }
43
HasMenuItem()44 bool HasMenuItem() override { return true; }
MenuItemCommandID()45 int MenuItemCommandID() override { return command_id_; }
MenuItemLabel()46 base::string16 MenuItemLabel() override { return base::string16(); }
ExecuteMenuItem(Browser * browser)47 void ExecuteMenuItem(Browser* browser) override { execute_count_++; }
48
HasBubbleView()49 bool HasBubbleView() override { return false; }
HasShownBubbleView()50 bool HasShownBubbleView() override { return false; }
ShowBubbleView(Browser * browser)51 void ShowBubbleView(Browser* browser) override { ADD_FAILURE(); }
GetBubbleView()52 GlobalErrorBubbleViewBase* GetBubbleView() override { return NULL; }
53
54 private:
55 int command_id_;
56 int execute_count_;
57
58 DISALLOW_COPY_AND_ASSIGN(MenuError);
59 };
60
61 class FakeIconDelegate : public AppMenuIconController::Delegate {
62 public:
63 FakeIconDelegate() = default;
64
65 // AppMenuIconController::Delegate:
UpdateTypeAndSeverity(AppMenuIconController::TypeAndSeverity type_and_severity)66 void UpdateTypeAndSeverity(
67 AppMenuIconController::TypeAndSeverity type_and_severity) override {}
GetDefaultColorForSeverity(AppMenuIconController::Severity severity) const68 SkColor GetDefaultColorForSeverity(
69 AppMenuIconController::Severity severity) const override {
70 return gfx::kPlaceholderColor;
71 }
72 };
73
74 } // namespace
75
76 class AppMenuModelTest : public BrowserWithTestWindowTest,
77 public ui::AcceleratorProvider {
78 public:
79 AppMenuModelTest() = default;
80 ~AppMenuModelTest() override = default;
81
82 // Don't handle accelerators.
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator) const83 bool GetAcceleratorForCommandId(int command_id,
84 ui::Accelerator* accelerator) const override {
85 return false;
86 }
87
88 private:
89 DISALLOW_COPY_AND_ASSIGN(AppMenuModelTest);
90 };
91
92 // Copies parts of MenuModelTest::Delegate and combines them with the
93 // AppMenuModel since AppMenuModel is now a SimpleMenuModel::Delegate and
94 // not derived from SimpleMenuModel.
95 class TestAppMenuModel : public AppMenuModel {
96 public:
TestAppMenuModel(ui::AcceleratorProvider * provider,Browser * browser,AppMenuIconController * app_menu_icon_controller)97 TestAppMenuModel(ui::AcceleratorProvider* provider,
98 Browser* browser,
99 AppMenuIconController* app_menu_icon_controller)
100 : AppMenuModel(provider, browser, app_menu_icon_controller),
101 execute_count_(0),
102 checked_count_(0),
103 enable_count_(0) {}
104
105 // Testing overrides to ui::SimpleMenuModel::Delegate:
IsCommandIdChecked(int command_id) const106 bool IsCommandIdChecked(int command_id) const override {
107 bool val = AppMenuModel::IsCommandIdChecked(command_id);
108 if (val)
109 checked_count_++;
110 return val;
111 }
112
IsCommandIdEnabled(int command_id) const113 bool IsCommandIdEnabled(int command_id) const override {
114 ++enable_count_;
115 return true;
116 }
117
ExecuteCommand(int command_id,int event_flags)118 void ExecuteCommand(int command_id, int event_flags) override {
119 ++execute_count_;
120 }
121
122 int execute_count_;
123 mutable int checked_count_;
124 mutable int enable_count_;
125 };
126
TEST_F(AppMenuModelTest,Basics)127 TEST_F(AppMenuModelTest, Basics) {
128 // Simulate that an update is available to ensure that the menu includes the
129 // upgrade item for platforms that support it.
130 UpgradeDetector* detector = UpgradeDetector::GetInstance();
131 detector->set_upgrade_notification_stage(
132 UpgradeDetector::UPGRADE_ANNOYANCE_LOW);
133 detector->NotifyUpgrade();
134 EXPECT_TRUE(detector->notify_upgrade());
135
136 FakeIconDelegate fake_delegate;
137 AppMenuIconController app_menu_icon_controller(browser()->profile(),
138 &fake_delegate);
139 TestAppMenuModel model(this, browser(), &app_menu_icon_controller);
140 model.Init();
141 int itemCount = model.GetItemCount();
142
143 // Verify it has items. The number varies by platform, so we don't check
144 // the exact number.
145 EXPECT_GT(itemCount, 10);
146
147 // Verify that the upgrade item is visible if supported.
148 EXPECT_EQ(browser_defaults::kShowUpgradeMenuItem,
149 model.IsCommandIdVisible(IDC_UPGRADE_DIALOG));
150
151 // Execute a couple of the items and make sure it gets back to our delegate.
152 // We can't use CountEnabledExecutable() here because the encoding menu's
153 // delegate is internal, it doesn't use the one we pass in.
154 // Note: the second item in the menu may be a separator if the browser
155 // supports showing upgrade status in the app menu.
156 int item_index = 1;
157 if (model.GetTypeAt(item_index) == ui::MenuModel::TYPE_SEPARATOR)
158 ++item_index;
159 model.ActivatedAt(item_index);
160 EXPECT_TRUE(model.IsEnabledAt(item_index));
161 // Make sure to use the index that is not separator in all configurations.
162 model.ActivatedAt(itemCount - 1);
163 EXPECT_TRUE(model.IsEnabledAt(itemCount - 1));
164
165 EXPECT_EQ(model.execute_count_, 2);
166 EXPECT_EQ(model.enable_count_, 2);
167
168 model.execute_count_ = 0;
169 model.enable_count_ = 0;
170
171 // Choose something from the bookmark submenu and make sure it makes it back
172 // to the delegate as well.
173 int bookmarks_model_index = -1;
174 for (int i = 0; i < itemCount; ++i) {
175 if (model.GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) {
176 // The bookmarks submenu comes after the Tabs and Downloads items.
177 bookmarks_model_index = i + 2;
178 break;
179 }
180 }
181 EXPECT_GT(bookmarks_model_index, -1);
182 ui::MenuModel* bookmarks_model =
183 model.GetSubmenuModelAt(bookmarks_model_index);
184 EXPECT_TRUE(bookmarks_model);
185 // The bookmarks model may be empty until we tell it we're going to show it.
186 bookmarks_model->MenuWillShow();
187 EXPECT_GT(bookmarks_model->GetItemCount(), 1);
188
189 // Bookmark manager item.
190 bookmarks_model->ActivatedAt(4);
191 EXPECT_TRUE(bookmarks_model->IsEnabledAt(4));
192 EXPECT_EQ(model.execute_count_, 1);
193 EXPECT_EQ(model.enable_count_, 1);
194 }
195
196 // Tests global error menu items in the app menu.
TEST_F(AppMenuModelTest,GlobalError)197 TEST_F(AppMenuModelTest, GlobalError) {
198 // Make sure services required for tests are initialized.
199 GlobalErrorService* service =
200 GlobalErrorServiceFactory::GetForProfile(browser()->profile());
201 const int command1 = 1234567;
202 MenuError* error1 = new MenuError(command1);
203 service->AddGlobalError(base::WrapUnique(error1));
204 const int command2 = 1234568;
205 MenuError* error2 = new MenuError(command2);
206 service->AddGlobalError(base::WrapUnique(error2));
207
208 AppMenuModel model(this, browser());
209 model.Init();
210 int index1 = model.GetIndexOfCommandId(command1);
211 EXPECT_GT(index1, -1);
212 int index2 = model.GetIndexOfCommandId(command2);
213 EXPECT_GT(index2, -1);
214
215 EXPECT_TRUE(model.IsEnabledAt(index1));
216 EXPECT_EQ(0, error1->execute_count());
217 model.ActivatedAt(index1);
218 EXPECT_EQ(1, error1->execute_count());
219
220 EXPECT_TRUE(model.IsEnabledAt(index2));
221 EXPECT_EQ(0, error2->execute_count());
222 model.ActivatedAt(index2);
223 EXPECT_EQ(1, error1->execute_count());
224 }
225
226 #if defined(OS_CHROMEOS)
227 // Tests settings menu items is disabled in the app menu when
228 // kSystemFeaturesDisableList is set.
TEST_F(AppMenuModelTest,DisableSettingsItem)229 TEST_F(AppMenuModelTest, DisableSettingsItem) {
230 AppMenuModel model(this, browser());
231 model.Init();
232 int index = model.GetIndexOfCommandId(IDC_OPTIONS);
233 EXPECT_TRUE(model.IsEnabledAt(index));
234
235 {
236 ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
237 policy::policy_prefs::kSystemFeaturesDisableList);
238 base::ListValue* list = update.Get();
239 list->Append(policy::SystemFeature::BROWSER_SETTINGS);
240 }
241 EXPECT_FALSE(model.IsEnabledAt(index));
242
243 {
244 ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
245 policy::policy_prefs::kSystemFeaturesDisableList);
246 base::ListValue* list = update.Get();
247 list->Clear();
248 }
249 EXPECT_TRUE(model.IsEnabledAt(index));
250 }
251 #endif // defined(OS_CHROMEOS)
252