app_context_menu.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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_->CanDoCreateShortcutsFlow(is_platform_app_)) { 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#if defined(OS_MACOSX) 206 // Mac does not support standalone web app browser windows or maximize. 207 menu_model_->AddCheckItemWithStringId( 208 LAUNCH_TYPE_FULLSCREEN, 209 IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN); 210#else 211 menu_model_->AddCheckItemWithStringId( 212 LAUNCH_TYPE_WINDOW, 213 IDS_APP_CONTEXT_MENU_OPEN_WINDOW); 214 // Even though the launch type is Full Screen it is more accurately 215 // described as Maximized in Ash. 216 menu_model_->AddCheckItemWithStringId( 217 LAUNCH_TYPE_FULLSCREEN, 218 IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED); 219#endif 220 } 221 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 222 menu_model_->AddItemWithStringId(OPTIONS, IDS_NEW_TAB_APP_OPTIONS); 223 } 224 225 menu_model_->AddItemWithStringId(DETAILS, IDS_NEW_TAB_APP_DETAILS); 226 menu_model_->AddItemWithStringId( 227 UNINSTALL, 228 is_platform_app_ ? IDS_APP_LIST_UNINSTALL_ITEM 229 : IDS_EXTENSIONS_UNINSTALL); 230 } 231 232 return menu_model_.get(); 233} 234 235const Extension* AppContextMenu::GetExtension() const { 236 const ExtensionService* service = 237 extensions::ExtensionSystem::Get(profile_)->extension_service(); 238 const Extension* extension = service->GetInstalledExtension(app_id_); 239 return extension; 240} 241 242void AppContextMenu::ShowExtensionOptions() { 243 const Extension* extension = GetExtension(); 244 if (!extension) 245 return; 246 247 chrome::NavigateParams params( 248 profile_, 249 extensions::ManifestURL::GetOptionsPage(extension), 250 content::PAGE_TRANSITION_LINK); 251 chrome::Navigate(¶ms); 252} 253 254void AppContextMenu::ShowExtensionDetails() { 255 const Extension* extension = GetExtension(); 256 if (!extension) 257 return; 258 259 chrome::NavigateParams params( 260 profile_, 261 extensions::ManifestURL::GetDetailsURL(extension), 262 content::PAGE_TRANSITION_LINK); 263 chrome::Navigate(¶ms); 264} 265 266void AppContextMenu::StartExtensionUninstall() { 267 // ExtensionUninstall deletes itself when done or aborted. 268 ExtensionUninstaller* uninstaller = new ExtensionUninstaller(profile_, 269 app_id_, 270 controller_); 271 uninstaller->Run(); 272} 273 274bool AppContextMenu::IsItemForCommandIdDynamic(int command_id) const { 275 return command_id == TOGGLE_PIN || command_id == LAUNCH_NEW; 276} 277 278string16 AppContextMenu::GetLabelForCommandId(int command_id) const { 279 if (command_id == TOGGLE_PIN) { 280 return controller_->IsAppPinned(app_id_) ? 281 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_UNPIN) : 282 l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_PIN); 283 } else if (command_id == LAUNCH_NEW) { 284 if (IsCommandIdChecked(LAUNCH_TYPE_PINNED_TAB) || 285 IsCommandIdChecked(LAUNCH_TYPE_REGULAR_TAB)) { 286 return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_TAB); 287 } else { 288 return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW); 289 } 290 } else { 291 NOTREACHED(); 292 return string16(); 293 } 294} 295 296bool AppContextMenu::IsCommandIdChecked(int command_id) const { 297 if (command_id >= LAUNCH_TYPE_START && command_id < LAUNCH_TYPE_LAST) { 298 return static_cast<int>(GetExtensionLaunchType(profile_, GetExtension())) + 299 LAUNCH_TYPE_START == command_id; 300 } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && 301 command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { 302 return extension_menu_items_->IsCommandIdChecked(command_id); 303 } 304 return false; 305} 306 307bool AppContextMenu::IsCommandIdEnabled(int command_id) const { 308 if (command_id == TOGGLE_PIN) { 309 return controller_->CanPin(); 310 } else if (command_id == OPTIONS) { 311 const ExtensionService* service = 312 extensions::ExtensionSystem::Get(profile_)->extension_service(); 313 const Extension* extension = GetExtension(); 314 return service->IsExtensionEnabledForLauncher(app_id_) && 315 extension && 316 !extensions::ManifestURL::GetOptionsPage(extension).is_empty(); 317 } else if (command_id == UNINSTALL) { 318 const Extension* extension = GetExtension(); 319 const extensions::ManagementPolicy* policy = 320 extensions::ExtensionSystem::Get(profile_)->management_policy(); 321 return extension && 322 policy->UserMayModifySettings(extension, NULL); 323 } else if (command_id == DETAILS) { 324 const Extension* extension = GetExtension(); 325 return extension && extension->from_webstore(); 326 } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && 327 command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { 328 return extension_menu_items_->IsCommandIdEnabled(command_id); 329 } else if (command_id == MENU_NEW_WINDOW) { 330 // "Normal" windows are not allowed when incognito is enforced. 331 return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) != 332 IncognitoModePrefs::FORCED; 333 } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) { 334 // Incognito windows are not allowed when incognito is disabled. 335 return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) != 336 IncognitoModePrefs::DISABLED; 337 } 338 return true; 339} 340 341bool AppContextMenu::GetAcceleratorForCommandId( 342 int command_id, 343 ui::Accelerator* acclelrator) { 344 return false; 345} 346 347void AppContextMenu::ExecuteCommand(int command_id, int event_flags) { 348 if (command_id == LAUNCH_NEW) { 349 delegate_->ExecuteLaunchCommand(event_flags); 350 } else if (command_id == TOGGLE_PIN && controller_->CanPin()) { 351 if (controller_->IsAppPinned(app_id_)) 352 controller_->UnpinApp(app_id_); 353 else 354 controller_->PinApp(app_id_); 355 } else if (command_id == CREATE_SHORTCUTS) { 356 controller_->DoCreateShortcutsFlow(profile_, app_id_); 357 } else if (command_id >= LAUNCH_TYPE_START && 358 command_id < LAUNCH_TYPE_LAST) { 359 SetExtensionLaunchType(profile_, 360 app_id_, 361 static_cast<extensions::ExtensionPrefs::LaunchType>( 362 command_id - LAUNCH_TYPE_START)); 363 } else if (command_id == OPTIONS) { 364 ShowExtensionOptions(); 365 } else if (command_id == UNINSTALL) { 366 StartExtensionUninstall(); 367 } else if (command_id == DETAILS) { 368 ShowExtensionDetails(); 369 } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && 370 command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { 371 extension_menu_items_->ExecuteCommand(command_id, NULL, 372 content::ContextMenuParams()); 373 } else if (command_id == MENU_NEW_WINDOW) { 374 controller_->CreateNewWindow(profile_, false); 375 } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) { 376 controller_->CreateNewWindow(profile_, true); 377 } 378} 379 380} // namespace app_list 381