1 // Copyright 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.
5 #include "chrome/browser/ui/app_list/app_context_menu.h"
8 #include "base/command_line.h"
9 #include "chrome/browser/extensions/context_menu_matcher.h"
10 #include "chrome/browser/extensions/menu_manager.h"
11 #include "chrome/browser/prefs/incognito_mode_prefs.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/app_list/app_context_menu_delegate.h"
14 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "chrome/common/extensions/manifest_url_handler.h"
17 #include "content/public/common/context_menu_params.h"
18 #include "grit/chromium_strings.h"
19 #include "grit/generated_resources.h"
20 #include "ui/base/l10n/l10n_util.h"
23 #include "ash/shell.h"
39 MENU_NEW_INCOGNITO_WINDOW,
40 // Order matters in USE_LAUNCH_TYPE_* and must match the LaunchType enum.
41 USE_LAUNCH_TYPE_COMMAND_START = 200,
42 USE_LAUNCH_TYPE_PINNED = USE_LAUNCH_TYPE_COMMAND_START,
43 USE_LAUNCH_TYPE_REGULAR,
44 USE_LAUNCH_TYPE_FULLSCREEN,
45 USE_LAUNCH_TYPE_WINDOW,
46 USE_LAUNCH_TYPE_COMMAND_END,
49 bool MenuItemHasLauncherContext(const extensions::MenuItem* item) {
50 return item->contexts().Contains(extensions::MenuItem::LAUNCHER);
55 AppContextMenu::AppContextMenu(AppContextMenuDelegate* delegate,
57 const std::string& app_id,
58 AppListControllerDelegate* controller)
59 : delegate_(delegate),
62 controller_(controller),
63 is_platform_app_(false),
64 is_search_result_(false),
65 is_in_folder_(false) {
68 AppContextMenu::~AppContextMenu() {
71 ui::MenuModel* AppContextMenu::GetMenuModel() {
72 if (!controller_->IsExtensionInstalled(profile_, app_id_))
75 if (menu_model_.get())
76 return menu_model_.get();
78 menu_model_.reset(new ui::SimpleMenuModel(this));
80 if (app_id_ == extension_misc::kChromeAppId) {
81 menu_model_->AddItemWithStringId(
83 IDS_APP_LIST_NEW_WINDOW);
84 if (!profile_->IsOffTheRecord()) {
85 menu_model_->AddItemWithStringId(
86 MENU_NEW_INCOGNITO_WINDOW,
87 IDS_APP_LIST_NEW_INCOGNITO_WINDOW);
89 if (controller_->CanDoShowAppInfoFlow()) {
90 menu_model_->AddItemWithStringId(SHOW_APP_INFO,
91 IDS_APP_CONTEXT_MENU_SHOW_INFO);
94 extension_menu_items_.reset(new extensions::ContextMenuMatcher(
95 profile_, this, menu_model_.get(),
96 base::Bind(MenuItemHasLauncherContext)));
98 // First, add the primary actions.
99 if (!is_platform_app_)
100 menu_model_->AddItem(LAUNCH_NEW, base::string16());
102 // Show Pin/Unpin option if shelf is available.
103 if (controller_->GetPinnable() != AppListControllerDelegate::NO_PIN) {
104 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
105 menu_model_->AddItemWithStringId(
107 controller_->IsAppPinned(app_id_) ?
108 IDS_APP_LIST_CONTEXT_MENU_UNPIN :
109 IDS_APP_LIST_CONTEXT_MENU_PIN);
112 if (controller_->CanDoCreateShortcutsFlow()) {
113 menu_model_->AddItemWithStringId(CREATE_SHORTCUTS,
114 IDS_NEW_TAB_APP_CREATE_SHORTCUT);
116 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
118 if (!is_platform_app_) {
119 // Streamlined hosted apps can only toggle between USE_LAUNCH_TYPE_WINDOW
120 // and USE_LAUNCH_TYPE_REGULAR.
121 if (CommandLine::ForCurrentProcess()->HasSwitch(
122 switches::kEnableStreamlinedHostedApps)) {
123 menu_model_->AddCheckItemWithStringId(
124 USE_LAUNCH_TYPE_REGULAR,
125 IDS_APP_CONTEXT_MENU_OPEN_TAB);
127 menu_model_->AddCheckItemWithStringId(
128 USE_LAUNCH_TYPE_REGULAR,
129 IDS_APP_CONTEXT_MENU_OPEN_REGULAR);
130 menu_model_->AddCheckItemWithStringId(
131 USE_LAUNCH_TYPE_PINNED,
132 IDS_APP_CONTEXT_MENU_OPEN_PINNED);
133 #if defined(OS_MACOSX)
134 // Mac does not support standalone web app browser windows or maximize.
135 menu_model_->AddCheckItemWithStringId(
136 USE_LAUNCH_TYPE_FULLSCREEN,
137 IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN);
139 menu_model_->AddCheckItemWithStringId(
140 USE_LAUNCH_TYPE_WINDOW,
141 IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
142 // Even though the launch type is Full Screen it is more accurately
143 // described as Maximized in Ash.
144 menu_model_->AddCheckItemWithStringId(
145 USE_LAUNCH_TYPE_FULLSCREEN,
146 IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED);
149 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
152 // Assign unique IDs to commands added by the app itself.
153 int index = USE_LAUNCH_TYPE_COMMAND_END;
154 extension_menu_items_->AppendExtensionItems(
155 extensions::MenuItem::ExtensionKey(app_id_),
158 false); // is_action_menu
160 // If at least 1 item was added, add another separator after the list.
161 if (index > USE_LAUNCH_TYPE_COMMAND_END)
162 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
164 if (!is_platform_app_)
165 menu_model_->AddItemWithStringId(OPTIONS, IDS_NEW_TAB_APP_OPTIONS);
167 menu_model_->AddItemWithStringId(UNINSTALL,
169 ? IDS_APP_LIST_UNINSTALL_ITEM
170 : IDS_APP_LIST_EXTENSIONS_UNINSTALL);
172 if (controller_->CanDoShowAppInfoFlow()) {
173 menu_model_->AddItemWithStringId(SHOW_APP_INFO,
174 IDS_APP_CONTEXT_MENU_SHOW_INFO);
178 return menu_model_.get();
181 bool AppContextMenu::IsItemForCommandIdDynamic(int command_id) const {
182 return command_id == TOGGLE_PIN || command_id == LAUNCH_NEW;
185 base::string16 AppContextMenu::GetLabelForCommandId(int command_id) const {
186 if (command_id == TOGGLE_PIN) {
187 return controller_->IsAppPinned(app_id_) ?
188 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_UNPIN) :
189 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_PIN);
190 } else if (command_id == LAUNCH_NEW) {
191 #if defined(OS_MACOSX)
192 // Even fullscreen windows launch in a browser tab on Mac.
193 const bool launches_in_tab = true;
195 const bool launches_in_tab = IsCommandIdChecked(USE_LAUNCH_TYPE_PINNED) ||
196 IsCommandIdChecked(USE_LAUNCH_TYPE_REGULAR);
198 return launches_in_tab ?
199 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_TAB) :
200 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW);
203 return base::string16();
207 bool AppContextMenu::IsCommandIdChecked(int command_id) const {
208 if (command_id >= USE_LAUNCH_TYPE_COMMAND_START &&
209 command_id < USE_LAUNCH_TYPE_COMMAND_END) {
210 return static_cast<int>(controller_->GetExtensionLaunchType(
211 profile_, app_id_)) + USE_LAUNCH_TYPE_COMMAND_START == command_id;
212 } else if (extensions::ContextMenuMatcher::IsExtensionsCustomCommandId(
214 return extension_menu_items_->IsCommandIdChecked(command_id);
219 bool AppContextMenu::IsCommandIdEnabled(int command_id) const {
220 if (command_id == TOGGLE_PIN) {
221 return controller_->GetPinnable() ==
222 AppListControllerDelegate::PIN_EDITABLE;
223 } else if (command_id == OPTIONS) {
224 return controller_->HasOptionsPage(profile_, app_id_);
225 } else if (command_id == UNINSTALL) {
226 return controller_->UserMayModifySettings(profile_, app_id_);
227 } else if (extensions::ContextMenuMatcher::IsExtensionsCustomCommandId(
229 return extension_menu_items_->IsCommandIdEnabled(command_id);
230 } else if (command_id == MENU_NEW_WINDOW) {
231 // "Normal" windows are not allowed when incognito is enforced.
232 return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) !=
233 IncognitoModePrefs::FORCED;
234 } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) {
235 // Incognito windows are not allowed when incognito is disabled.
236 return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) !=
237 IncognitoModePrefs::DISABLED;
242 bool AppContextMenu::GetAcceleratorForCommandId(
244 ui::Accelerator* acclelrator) {
248 void AppContextMenu::ExecuteCommand(int command_id, int event_flags) {
249 if (command_id == LAUNCH_NEW) {
250 delegate_->ExecuteLaunchCommand(event_flags);
251 } else if (command_id == TOGGLE_PIN && controller_->GetPinnable() ==
252 AppListControllerDelegate::PIN_EDITABLE) {
253 if (controller_->IsAppPinned(app_id_))
254 controller_->UnpinApp(app_id_);
256 controller_->PinApp(app_id_);
257 } else if (command_id == CREATE_SHORTCUTS) {
258 controller_->DoCreateShortcutsFlow(profile_, app_id_);
259 } else if (command_id == SHOW_APP_INFO) {
260 controller_->DoShowAppInfoFlow(profile_, app_id_);
261 } else if (command_id >= USE_LAUNCH_TYPE_COMMAND_START &&
262 command_id < USE_LAUNCH_TYPE_COMMAND_END) {
263 extensions::LaunchType launch_type = static_cast<extensions::LaunchType>(
264 command_id - USE_LAUNCH_TYPE_COMMAND_START);
265 // Streamlined hosted apps can only toggle between LAUNCH_TYPE_WINDOW and
266 // LAUNCH_TYPE_REGULAR.
267 if (CommandLine::ForCurrentProcess()->HasSwitch(
268 switches::kEnableStreamlinedHostedApps)) {
269 launch_type = (controller_->GetExtensionLaunchType(profile_, app_id_) ==
270 extensions::LAUNCH_TYPE_REGULAR) ?
271 extensions::LAUNCH_TYPE_WINDOW :
272 extensions::LAUNCH_TYPE_REGULAR;
274 controller_->SetExtensionLaunchType(profile_, app_id_, launch_type);
275 } else if (command_id == OPTIONS) {
276 controller_->ShowOptionsPage(profile_, app_id_);
277 } else if (command_id == UNINSTALL) {
278 controller_->UninstallApp(profile_, app_id_);
279 } else if (extensions::ContextMenuMatcher::IsExtensionsCustomCommandId(
281 extension_menu_items_->ExecuteCommand(command_id, NULL,
282 content::ContextMenuParams());
283 } else if (command_id == MENU_NEW_WINDOW) {
284 controller_->CreateNewWindow(profile_, false);
285 } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) {
286 controller_->CreateNewWindow(profile_, true);
290 } // namespace app_list