app_context_menu.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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. 4 5#include "chrome/browser/ui/app_list/app_context_menu.h" 6 7#include "base/bind.h" 8#include "chrome/browser/extensions/context_menu_matcher.h" 9#include "chrome/browser/extensions/extension_util.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/extensions/manifest_url_handler.h" 16#include "chrome/grit/chromium_strings.h" 17#include "chrome/grit/generated_resources.h" 18#include "content/public/common/context_menu_params.h" 19#include "ui/base/l10n/l10n_util.h" 20 21#if defined(USE_ASH) 22#include "ash/shell.h" 23#endif 24 25namespace app_list { 26 27namespace { 28 29enum CommandId { 30 LAUNCH_NEW = 100, 31 TOGGLE_PIN, 32 CREATE_SHORTCUTS, 33 SHOW_APP_INFO, 34 OPTIONS, 35 UNINSTALL, 36 REMOVE_FROM_FOLDER, 37 MENU_NEW_WINDOW, 38 MENU_NEW_INCOGNITO_WINDOW, 39 // Order matters in USE_LAUNCH_TYPE_* and must match the LaunchType enum. 40 USE_LAUNCH_TYPE_COMMAND_START = 200, 41 USE_LAUNCH_TYPE_PINNED = USE_LAUNCH_TYPE_COMMAND_START, 42 USE_LAUNCH_TYPE_REGULAR, 43 USE_LAUNCH_TYPE_FULLSCREEN, 44 USE_LAUNCH_TYPE_WINDOW, 45 USE_LAUNCH_TYPE_COMMAND_END, 46}; 47 48bool MenuItemHasLauncherContext(const extensions::MenuItem* item) { 49 return item->contexts().Contains(extensions::MenuItem::LAUNCHER); 50} 51 52} // namespace 53 54AppContextMenu::AppContextMenu(AppContextMenuDelegate* delegate, 55 Profile* profile, 56 const std::string& app_id, 57 AppListControllerDelegate* controller) 58 : delegate_(delegate), 59 profile_(profile), 60 app_id_(app_id), 61 controller_(controller), 62 is_platform_app_(false), 63 is_search_result_(false), 64 is_in_folder_(false) { 65} 66 67AppContextMenu::~AppContextMenu() { 68} 69 70ui::MenuModel* AppContextMenu::GetMenuModel() { 71 if (!controller_->IsExtensionInstalled(profile_, app_id_)) 72 return NULL; 73 74 if (menu_model_.get()) 75 return menu_model_.get(); 76 77 menu_model_.reset(new ui::SimpleMenuModel(this)); 78 79 if (app_id_ == extension_misc::kChromeAppId) { 80 menu_model_->AddItemWithStringId( 81 MENU_NEW_WINDOW, 82 IDS_APP_LIST_NEW_WINDOW); 83 if (!profile_->IsOffTheRecord()) { 84 menu_model_->AddItemWithStringId( 85 MENU_NEW_INCOGNITO_WINDOW, 86 IDS_APP_LIST_NEW_INCOGNITO_WINDOW); 87 } 88 if (controller_->CanDoShowAppInfoFlow()) { 89 menu_model_->AddItemWithStringId(SHOW_APP_INFO, 90 IDS_APP_CONTEXT_MENU_SHOW_INFO); 91 } 92 } else { 93 extension_menu_items_.reset(new extensions::ContextMenuMatcher( 94 profile_, this, menu_model_.get(), 95 base::Bind(MenuItemHasLauncherContext))); 96 97 // First, add the primary actions. 98 if (!is_platform_app_) 99 menu_model_->AddItem(LAUNCH_NEW, base::string16()); 100 101 // Show Pin/Unpin option if shelf is available. 102 if (controller_->GetPinnable() != AppListControllerDelegate::NO_PIN) { 103 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 104 menu_model_->AddItemWithStringId( 105 TOGGLE_PIN, 106 controller_->IsAppPinned(app_id_) ? 107 IDS_APP_LIST_CONTEXT_MENU_UNPIN : 108 IDS_APP_LIST_CONTEXT_MENU_PIN); 109 } 110 111 if (controller_->CanDoCreateShortcutsFlow()) { 112 menu_model_->AddItemWithStringId(CREATE_SHORTCUTS, 113 IDS_NEW_TAB_APP_CREATE_SHORTCUT); 114 } 115 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 116 117 if (!is_platform_app_) { 118 // Streamlined hosted apps can only toggle between USE_LAUNCH_TYPE_WINDOW 119 // and USE_LAUNCH_TYPE_REGULAR. 120 if (extensions::util::IsStreamlinedHostedAppsEnabled()) { 121 menu_model_->AddCheckItemWithStringId( 122 USE_LAUNCH_TYPE_REGULAR, 123 IDS_APP_CONTEXT_MENU_OPEN_TAB); 124 } else { 125 menu_model_->AddCheckItemWithStringId( 126 USE_LAUNCH_TYPE_REGULAR, 127 IDS_APP_CONTEXT_MENU_OPEN_REGULAR); 128 menu_model_->AddCheckItemWithStringId( 129 USE_LAUNCH_TYPE_PINNED, 130 IDS_APP_CONTEXT_MENU_OPEN_PINNED); 131#if defined(OS_MACOSX) 132 // Mac does not support standalone web app browser windows or maximize. 133 menu_model_->AddCheckItemWithStringId( 134 USE_LAUNCH_TYPE_FULLSCREEN, 135 IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN); 136#else 137 menu_model_->AddCheckItemWithStringId( 138 USE_LAUNCH_TYPE_WINDOW, 139 IDS_APP_CONTEXT_MENU_OPEN_WINDOW); 140 // Even though the launch type is Full Screen it is more accurately 141 // described as Maximized in Ash. 142 menu_model_->AddCheckItemWithStringId( 143 USE_LAUNCH_TYPE_FULLSCREEN, 144 IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED); 145#endif 146 } 147 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 148 } 149 150 // Assign unique IDs to commands added by the app itself. 151 int index = USE_LAUNCH_TYPE_COMMAND_END; 152 extension_menu_items_->AppendExtensionItems( 153 extensions::MenuItem::ExtensionKey(app_id_), 154 base::string16(), 155 &index, 156 false); // is_action_menu 157 158 // If at least 1 item was added, add another separator after the list. 159 if (index > USE_LAUNCH_TYPE_COMMAND_END) 160 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 161 162 if (!is_platform_app_) 163 menu_model_->AddItemWithStringId(OPTIONS, IDS_NEW_TAB_APP_OPTIONS); 164 165 menu_model_->AddItemWithStringId(UNINSTALL, 166 is_platform_app_ 167 ? IDS_APP_LIST_UNINSTALL_ITEM 168 : IDS_APP_LIST_EXTENSIONS_UNINSTALL); 169 170 if (controller_->CanDoShowAppInfoFlow()) { 171 menu_model_->AddItemWithStringId(SHOW_APP_INFO, 172 IDS_APP_CONTEXT_MENU_SHOW_INFO); 173 } 174 } 175 176 return menu_model_.get(); 177} 178 179bool AppContextMenu::IsItemForCommandIdDynamic(int command_id) const { 180 return command_id == TOGGLE_PIN || command_id == LAUNCH_NEW; 181} 182 183base::string16 AppContextMenu::GetLabelForCommandId(int command_id) const { 184 if (command_id == TOGGLE_PIN) { 185 return controller_->IsAppPinned(app_id_) ? 186 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_UNPIN) : 187 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_PIN); 188 } else if (command_id == LAUNCH_NEW) { 189#if defined(OS_MACOSX) 190 // Even fullscreen windows launch in a browser tab on Mac. 191 const bool launches_in_tab = true; 192#else 193 const bool launches_in_tab = IsCommandIdChecked(USE_LAUNCH_TYPE_PINNED) || 194 IsCommandIdChecked(USE_LAUNCH_TYPE_REGULAR); 195#endif 196 return launches_in_tab ? 197 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_TAB) : 198 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW); 199 } else { 200 NOTREACHED(); 201 return base::string16(); 202 } 203} 204 205bool AppContextMenu::IsCommandIdChecked(int command_id) const { 206 if (command_id >= USE_LAUNCH_TYPE_COMMAND_START && 207 command_id < USE_LAUNCH_TYPE_COMMAND_END) { 208 return static_cast<int>(controller_->GetExtensionLaunchType( 209 profile_, app_id_)) + USE_LAUNCH_TYPE_COMMAND_START == command_id; 210 } else if (extensions::ContextMenuMatcher::IsExtensionsCustomCommandId( 211 command_id)) { 212 return extension_menu_items_->IsCommandIdChecked(command_id); 213 } 214 return false; 215} 216 217bool AppContextMenu::IsCommandIdEnabled(int command_id) const { 218 if (command_id == TOGGLE_PIN) { 219 return controller_->GetPinnable() == 220 AppListControllerDelegate::PIN_EDITABLE; 221 } else if (command_id == OPTIONS) { 222 return controller_->HasOptionsPage(profile_, app_id_); 223 } else if (command_id == UNINSTALL) { 224 return controller_->UserMayModifySettings(profile_, app_id_); 225 } else if (extensions::ContextMenuMatcher::IsExtensionsCustomCommandId( 226 command_id)) { 227 return extension_menu_items_->IsCommandIdEnabled(command_id); 228 } else if (command_id == MENU_NEW_WINDOW) { 229 // "Normal" windows are not allowed when incognito is enforced. 230 return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) != 231 IncognitoModePrefs::FORCED; 232 } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) { 233 // Incognito windows are not allowed when incognito is disabled. 234 return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) != 235 IncognitoModePrefs::DISABLED; 236 } 237 return true; 238} 239 240bool AppContextMenu::GetAcceleratorForCommandId( 241 int command_id, 242 ui::Accelerator* acclelrator) { 243 return false; 244} 245 246void AppContextMenu::ExecuteCommand(int command_id, int event_flags) { 247 if (command_id == LAUNCH_NEW) { 248 delegate_->ExecuteLaunchCommand(event_flags); 249 } else if (command_id == TOGGLE_PIN && controller_->GetPinnable() == 250 AppListControllerDelegate::PIN_EDITABLE) { 251 if (controller_->IsAppPinned(app_id_)) 252 controller_->UnpinApp(app_id_); 253 else 254 controller_->PinApp(app_id_); 255 } else if (command_id == CREATE_SHORTCUTS) { 256 controller_->DoCreateShortcutsFlow(profile_, app_id_); 257 } else if (command_id == SHOW_APP_INFO) { 258 controller_->DoShowAppInfoFlow(profile_, app_id_); 259 } else if (command_id >= USE_LAUNCH_TYPE_COMMAND_START && 260 command_id < USE_LAUNCH_TYPE_COMMAND_END) { 261 extensions::LaunchType launch_type = static_cast<extensions::LaunchType>( 262 command_id - USE_LAUNCH_TYPE_COMMAND_START); 263 // Streamlined hosted apps can only toggle between LAUNCH_TYPE_WINDOW and 264 // LAUNCH_TYPE_REGULAR. 265 if (extensions::util::IsStreamlinedHostedAppsEnabled()) { 266 launch_type = (controller_->GetExtensionLaunchType(profile_, app_id_) == 267 extensions::LAUNCH_TYPE_REGULAR) ? 268 extensions::LAUNCH_TYPE_WINDOW : 269 extensions::LAUNCH_TYPE_REGULAR; 270 } 271 controller_->SetExtensionLaunchType(profile_, app_id_, launch_type); 272 } else if (command_id == OPTIONS) { 273 controller_->ShowOptionsPage(profile_, app_id_); 274 } else if (command_id == UNINSTALL) { 275 controller_->UninstallApp(profile_, app_id_); 276 } else if (extensions::ContextMenuMatcher::IsExtensionsCustomCommandId( 277 command_id)) { 278 extension_menu_items_->ExecuteCommand(command_id, NULL, 279 content::ContextMenuParams()); 280 } else if (command_id == MENU_NEW_WINDOW) { 281 controller_->CreateNewWindow(profile_, false); 282 } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) { 283 controller_->CreateNewWindow(profile_, true); 284 } 285} 286 287} // namespace app_list 288