app_context_menu.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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 "chrome/app/chrome_command_ids.h" 8#include "chrome/browser/extensions/context_menu_matcher.h" 9#include "chrome/browser/extensions/extension_service.h" 10#include "chrome/browser/extensions/extension_system.h" 11#include "chrome/browser/extensions/extension_uninstall_dialog.h" 12#include "chrome/browser/extensions/management_policy.h" 13#include "chrome/browser/prefs/incognito_mode_prefs.h" 14#include "chrome/browser/profiles/profile.h" 15#include "chrome/browser/ui/app_list/app_context_menu_delegate.h" 16#include "chrome/browser/ui/app_list/app_list_controller_delegate.h" 17#include "chrome/browser/ui/browser_navigator.h" 18#include "chrome/common/extensions/manifest_url_handler.h" 19#include "content/public/common/context_menu_params.h" 20#include "grit/chromium_strings.h" 21#include "grit/generated_resources.h" 22#include "ui/base/l10n/l10n_util.h" 23 24#if defined(USE_ASH) 25#include "ash/shell.h" 26#endif 27 28using extensions::Extension; 29 30namespace app_list { 31 32namespace { 33 34enum CommandId { 35 LAUNCH_NEW = 100, 36 TOGGLE_PIN, 37 CREATE_SHORTCUTS, 38 OPTIONS, 39 UNINSTALL, 40 DETAILS, 41 MENU_NEW_WINDOW, 42 MENU_NEW_INCOGNITO_WINDOW, 43 // Order matters in LAUNCHER_TYPE_xxxx and must match LaunchType. 44 LAUNCH_TYPE_START = 200, 45 LAUNCH_TYPE_PINNED_TAB = LAUNCH_TYPE_START, 46 LAUNCH_TYPE_REGULAR_TAB, 47 LAUNCH_TYPE_FULLSCREEN, 48 LAUNCH_TYPE_WINDOW, 49 LAUNCH_TYPE_LAST, 50}; 51 52// ExtensionUninstaller runs the extension uninstall flow. It shows the 53// extension uninstall dialog and wait for user to confirm or cancel the 54// uninstall. 55class ExtensionUninstaller : public ExtensionUninstallDialog::Delegate { 56 public: 57 ExtensionUninstaller(Profile* profile, 58 const std::string& extension_id, 59 AppListControllerDelegate* controller) 60 : profile_(profile), 61 app_id_(extension_id), 62 controller_(controller) { 63 } 64 65 void Run() { 66 const Extension* extension = 67 extensions::ExtensionSystem::Get(profile_)->extension_service()-> 68 GetExtensionById(app_id_, true); 69 if (!extension) { 70 CleanUp(); 71 return; 72 } 73 controller_->OnShowExtensionPrompt(); 74 dialog_.reset(ExtensionUninstallDialog::Create(profile_, NULL, this)); 75 dialog_->ConfirmUninstall(extension); 76 } 77 78 private: 79 // Overridden from ExtensionUninstallDialog::Delegate: 80 virtual void ExtensionUninstallAccepted() OVERRIDE { 81 ExtensionService* service = 82 extensions::ExtensionSystem::Get(profile_)->extension_service(); 83 const Extension* extension = service->GetInstalledExtension(app_id_); 84 if (extension) { 85 service->UninstallExtension(app_id_, 86 false, /* external_uninstall*/ 87 NULL); 88 } 89 controller_->OnCloseExtensionPrompt(); 90 CleanUp(); 91 } 92 93 virtual void ExtensionUninstallCanceled() OVERRIDE { 94 controller_->OnCloseExtensionPrompt(); 95 CleanUp(); 96 } 97 98 void CleanUp() { 99 delete this; 100 } 101 102 Profile* profile_; 103 std::string app_id_; 104 AppListControllerDelegate* controller_; 105 scoped_ptr<ExtensionUninstallDialog> dialog_; 106 107 DISALLOW_COPY_AND_ASSIGN(ExtensionUninstaller); 108}; 109 110extensions::ExtensionPrefs::LaunchType GetExtensionLaunchType( 111 Profile* profile, 112 const Extension* extension) { 113 ExtensionService* service = 114 extensions::ExtensionSystem::Get(profile)->extension_service(); 115 return service->extension_prefs()-> 116 GetLaunchType(extension, extensions::ExtensionPrefs::LAUNCH_DEFAULT); 117} 118 119void SetExtensionLaunchType( 120 Profile* profile, 121 const std::string& extension_id, 122 extensions::ExtensionPrefs::LaunchType launch_type) { 123 ExtensionService* service = 124 extensions::ExtensionSystem::Get(profile)->extension_service(); 125 service->extension_prefs()->SetLaunchType(extension_id, launch_type); 126} 127 128bool MenuItemHasLauncherContext(const extensions::MenuItem* item) { 129 return item->contexts().Contains(extensions::MenuItem::LAUNCHER); 130} 131 132} // namespace 133 134AppContextMenu::AppContextMenu(AppContextMenuDelegate* delegate, 135 Profile* profile, 136 const std::string& app_id, 137 AppListControllerDelegate* controller, 138 bool is_platform_app) 139 : delegate_(delegate), 140 profile_(profile), 141 app_id_(app_id), 142 controller_(controller), 143 is_platform_app_(is_platform_app) { 144} 145 146AppContextMenu::~AppContextMenu() {} 147 148ui::MenuModel* AppContextMenu::GetMenuModel() { 149 const Extension* extension = GetExtension(); 150 if (!extension) 151 return NULL; 152 153 if (menu_model_.get()) 154 return menu_model_.get(); 155 156 menu_model_.reset(new ui::SimpleMenuModel(this)); 157 158 if (app_id_ == extension_misc::kChromeAppId) { 159 menu_model_->AddItemWithStringId( 160 MENU_NEW_WINDOW, 161 IDS_APP_LIST_NEW_WINDOW); 162 if (!profile_->IsOffTheRecord()) { 163 menu_model_->AddItemWithStringId( 164 MENU_NEW_INCOGNITO_WINDOW, 165 IDS_APP_LIST_NEW_INCOGNITO_WINDOW); 166 } 167 } else { 168 extension_menu_items_.reset(new extensions::ContextMenuMatcher( 169 profile_, this, menu_model_.get(), 170 base::Bind(MenuItemHasLauncherContext))); 171 172 if (!is_platform_app_) 173 menu_model_->AddItem(LAUNCH_NEW, string16()); 174 175 int index = 0; 176 extension_menu_items_->AppendExtensionItems(app_id_, string16(), 177 &index); 178 179 if (controller_->CanPin()) { 180 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 181 menu_model_->AddItemWithStringId( 182 TOGGLE_PIN, 183 controller_->IsAppPinned(app_id_) ? 184 IDS_APP_LIST_CONTEXT_MENU_UNPIN : 185 IDS_APP_LIST_CONTEXT_MENU_PIN); 186 } 187 188 if (controller_->CanShowCreateShortcutsDialog()) { 189 menu_model_->AddItemWithStringId(CREATE_SHORTCUTS, 190 IDS_NEW_TAB_APP_CREATE_SHORTCUT); 191 } 192 193 if (!is_platform_app_) { 194 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 195 menu_model_->AddCheckItemWithStringId( 196 LAUNCH_TYPE_REGULAR_TAB, 197 IDS_APP_CONTEXT_MENU_OPEN_REGULAR); 198 menu_model_->AddCheckItemWithStringId( 199 LAUNCH_TYPE_PINNED_TAB, 200 IDS_APP_CONTEXT_MENU_OPEN_PINNED); 201#if defined(USE_ASH) 202 if (!ash::Shell::IsForcedMaximizeMode()) 203#endif 204 { 205 menu_model_->AddCheckItemWithStringId( 206 LAUNCH_TYPE_WINDOW, 207 IDS_APP_CONTEXT_MENU_OPEN_WINDOW); 208 // Even though the launch type is Full Screen it is more accurately 209 // described as Maximized in Ash. 210 menu_model_->AddCheckItemWithStringId( 211 LAUNCH_TYPE_FULLSCREEN, 212 IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED); 213 } 214 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 215 menu_model_->AddItemWithStringId(OPTIONS, IDS_NEW_TAB_APP_OPTIONS); 216 } 217 218 menu_model_->AddItemWithStringId(DETAILS, IDS_NEW_TAB_APP_DETAILS); 219 menu_model_->AddItemWithStringId( 220 UNINSTALL, 221 is_platform_app_ ? IDS_APP_LIST_UNINSTALL_ITEM 222 : IDS_EXTENSIONS_UNINSTALL); 223 } 224 225 return menu_model_.get(); 226} 227 228const Extension* AppContextMenu::GetExtension() const { 229 const ExtensionService* service = 230 extensions::ExtensionSystem::Get(profile_)->extension_service(); 231 const Extension* extension = service->GetInstalledExtension(app_id_); 232 return extension; 233} 234 235void AppContextMenu::ShowExtensionOptions() { 236 const Extension* extension = GetExtension(); 237 if (!extension) 238 return; 239 240 chrome::NavigateParams params( 241 profile_, 242 extensions::ManifestURL::GetOptionsPage(extension), 243 content::PAGE_TRANSITION_LINK); 244 chrome::Navigate(¶ms); 245} 246 247void AppContextMenu::ShowExtensionDetails() { 248 const Extension* extension = GetExtension(); 249 if (!extension) 250 return; 251 252 chrome::NavigateParams params( 253 profile_, 254 extensions::ManifestURL::GetDetailsURL(extension), 255 content::PAGE_TRANSITION_LINK); 256 chrome::Navigate(¶ms); 257} 258 259void AppContextMenu::StartExtensionUninstall() { 260 // ExtensionUninstall deletes itself when done or aborted. 261 ExtensionUninstaller* uninstaller = new ExtensionUninstaller(profile_, 262 app_id_, 263 controller_); 264 uninstaller->Run(); 265} 266 267bool AppContextMenu::IsItemForCommandIdDynamic(int command_id) const { 268 return command_id == TOGGLE_PIN || command_id == LAUNCH_NEW; 269} 270 271string16 AppContextMenu::GetLabelForCommandId(int command_id) const { 272 if (command_id == TOGGLE_PIN) { 273 return controller_->IsAppPinned(app_id_) ? 274 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_UNPIN) : 275 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_PIN); 276 } else if (command_id == LAUNCH_NEW) { 277 if (IsCommandIdChecked(LAUNCH_TYPE_PINNED_TAB) || 278 IsCommandIdChecked(LAUNCH_TYPE_REGULAR_TAB)) { 279 return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_TAB); 280 } else { 281 return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW); 282 } 283 } else { 284 NOTREACHED(); 285 return string16(); 286 } 287} 288 289bool AppContextMenu::IsCommandIdChecked(int command_id) const { 290 if (command_id >= LAUNCH_TYPE_START && command_id < LAUNCH_TYPE_LAST) { 291 return static_cast<int>(GetExtensionLaunchType(profile_, GetExtension())) + 292 LAUNCH_TYPE_START == command_id; 293 } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && 294 command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { 295 return extension_menu_items_->IsCommandIdChecked(command_id); 296 } 297 return false; 298} 299 300bool AppContextMenu::IsCommandIdEnabled(int command_id) const { 301 if (command_id == TOGGLE_PIN) { 302 return controller_->CanPin(); 303 } else if (command_id == OPTIONS) { 304 const ExtensionService* service = 305 extensions::ExtensionSystem::Get(profile_)->extension_service(); 306 const Extension* extension = GetExtension(); 307 return service->IsExtensionEnabledForLauncher(app_id_) && 308 extension && 309 !extensions::ManifestURL::GetOptionsPage(extension).is_empty(); 310 } else if (command_id == UNINSTALL) { 311 const Extension* extension = GetExtension(); 312 const extensions::ManagementPolicy* policy = 313 extensions::ExtensionSystem::Get(profile_)->management_policy(); 314 return extension && 315 policy->UserMayModifySettings(extension, NULL); 316 } else if (command_id == DETAILS) { 317 const Extension* extension = GetExtension(); 318 return extension && extension->from_webstore(); 319 } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && 320 command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { 321 return extension_menu_items_->IsCommandIdEnabled(command_id); 322 } else if (command_id == MENU_NEW_WINDOW) { 323 // "Normal" windows are not allowed when incognito is enforced. 324 return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) != 325 IncognitoModePrefs::FORCED; 326 } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) { 327 // Incognito windows are not allowed when incognito is disabled. 328 return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) != 329 IncognitoModePrefs::DISABLED; 330 } 331 return true; 332} 333 334bool AppContextMenu::GetAcceleratorForCommandId( 335 int command_id, 336 ui::Accelerator* acclelrator) { 337 return false; 338} 339 340void AppContextMenu::ExecuteCommand(int command_id, int event_flags) { 341 if (command_id == LAUNCH_NEW) { 342 delegate_->ExecuteLaunchCommand(event_flags); 343 } else if (command_id == TOGGLE_PIN && controller_->CanPin()) { 344 if (controller_->IsAppPinned(app_id_)) 345 controller_->UnpinApp(app_id_); 346 else 347 controller_->PinApp(app_id_); 348 } else if (command_id == CREATE_SHORTCUTS) { 349 controller_->ShowCreateShortcutsDialog(profile_, app_id_); 350 } else if (command_id >= LAUNCH_TYPE_START && 351 command_id < LAUNCH_TYPE_LAST) { 352 SetExtensionLaunchType(profile_, 353 app_id_, 354 static_cast<extensions::ExtensionPrefs::LaunchType>( 355 command_id - LAUNCH_TYPE_START)); 356 } else if (command_id == OPTIONS) { 357 ShowExtensionOptions(); 358 } else if (command_id == UNINSTALL) { 359 StartExtensionUninstall(); 360 } else if (command_id == DETAILS) { 361 ShowExtensionDetails(); 362 } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && 363 command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { 364 extension_menu_items_->ExecuteCommand(command_id, NULL, 365 content::ContextMenuParams()); 366 } else if (command_id == MENU_NEW_WINDOW) { 367 controller_->CreateNewWindow(profile_, false); 368 } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) { 369 controller_->CreateNewWindow(profile_, true); 370 } 371} 372 373} // namespace app_list 374