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