app_context_menu.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// found in the LICENSE file. 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/app_list/app_context_menu.h" 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/bind.h" 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/command_line.h" 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/context_menu_matcher.h" 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/menu_manager.h" 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/prefs/incognito_mode_prefs.h" 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/profiles/profile.h" 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/app_list/app_context_menu_delegate.h" 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/app_list/app_list_controller_delegate.h" 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/common/chrome_switches.h" 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/common/extensions/manifest_url_handler.h" 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/public/common/context_menu_params.h" 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "grit/chromium_strings.h" 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "grit/generated_resources.h" 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#if defined(USE_ASH) 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "ash/shell.h" 2453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#endif 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 265d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)namespace app_list { 275d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles) 285d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)namespace { 2953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)enum CommandId { 31c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) LAUNCH_NEW = 100, 325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) TOGGLE_PIN, 335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) CREATE_SHORTCUTS, 345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) SHOW_APP_INFO, 355d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles) OPTIONS, 3651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) UNINSTALL, 375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) REMOVE_FROM_FOLDER, 385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) MENU_NEW_WINDOW, 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) MENU_NEW_INCOGNITO_WINDOW, 405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Order matters in USE_LAUNCH_TYPE_* and must match the LaunchType enum. 415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) USE_LAUNCH_TYPE_COMMAND_START = 200, 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) USE_LAUNCH_TYPE_PINNED = USE_LAUNCH_TYPE_COMMAND_START, 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) USE_LAUNCH_TYPE_REGULAR, 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) USE_LAUNCH_TYPE_FULLSCREEN, 455d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles) USE_LAUNCH_TYPE_WINDOW, 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) USE_LAUNCH_TYPE_COMMAND_END, 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}; 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool MenuItemHasLauncherContext(const extensions::MenuItem* item) { 505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return item->contexts().Contains(extensions::MenuItem::LAUNCHER); 515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} // namespace 54926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)AppContextMenu::AppContextMenu(AppContextMenuDelegate* delegate, 56926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) Profile* profile, 57926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) const std::string& app_id, 58926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) AppListControllerDelegate* controller) 59926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) : delegate_(delegate), 60926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) profile_(profile), 61926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) app_id_(app_id), 62926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) controller_(controller), 63926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) is_platform_app_(false), 64926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) is_search_result_(false), 65926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) is_in_folder_(false) { 66926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)} 675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 68926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)AppContextMenu::~AppContextMenu() { 695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 71926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)ui::MenuModel* AppContextMenu::GetMenuModel() { 725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!controller_->IsExtensionInstalled(profile_, app_id_)) 73926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return NULL; 745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (menu_model_.get()) 76926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return menu_model_.get(); 775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) menu_model_.reset(new ui::SimpleMenuModel(this)); 795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (app_id_ == extension_misc::kChromeAppId) { 815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) menu_model_->AddItemWithStringId( 82926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) MENU_NEW_WINDOW, 83926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) IDS_APP_LIST_NEW_WINDOW); 845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!profile_->IsOffTheRecord()) { 855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) menu_model_->AddItemWithStringId( 865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) MENU_NEW_INCOGNITO_WINDOW, 875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IDS_APP_LIST_NEW_INCOGNITO_WINDOW); 885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 89926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) if (controller_->CanDoShowAppInfoFlow()) { 905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) menu_model_->AddItemWithStringId(SHOW_APP_INFO, 915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IDS_APP_CONTEXT_MENU_SHOW_INFO); 925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 9451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) extension_menu_items_.reset(new extensions::ContextMenuMatcher( 955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) profile_, this, menu_model_.get(), 965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) base::Bind(MenuItemHasLauncherContext))); 975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // First, add the primary actions. 9943e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) if (!is_platform_app_) 10043e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) menu_model_->AddItem(LAUNCH_NEW, base::string16()); 10143e7502580f146aa5b3db8267ba6dbb5c733a489Torne (Richard Coles) 10253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) // Show Pin/Unpin option if shelf is available. 1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (controller_->GetPinnable() != AppListControllerDelegate::NO_PIN) { 1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) menu_model_->AddItemWithStringId( 1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) TOGGLE_PIN, 10753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) controller_->IsAppPinned(app_id_) ? 1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IDS_APP_LIST_CONTEXT_MENU_UNPIN : 1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IDS_APP_LIST_CONTEXT_MENU_PIN); 1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 112 if (controller_->CanDoCreateShortcutsFlow()) { 113 menu_model_->AddItemWithStringId(CREATE_SHORTCUTS, 114 IDS_NEW_TAB_APP_CREATE_SHORTCUT); 115 } 116 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 117 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); 126 } else { 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); 138#else 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); 147#endif 148 } 149 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); 150 } 151 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_), 156 base::string16(), 157 &index, 158 false); // is_action_menu 159 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); 163 164 if (!is_platform_app_) 165 menu_model_->AddItemWithStringId(OPTIONS, IDS_NEW_TAB_APP_OPTIONS); 166 167 menu_model_->AddItemWithStringId(UNINSTALL, 168 is_platform_app_ 169 ? IDS_APP_LIST_UNINSTALL_ITEM 170 : IDS_APP_LIST_EXTENSIONS_UNINSTALL); 171 172 if (controller_->CanDoShowAppInfoFlow()) { 173 menu_model_->AddItemWithStringId(SHOW_APP_INFO, 174 IDS_APP_CONTEXT_MENU_SHOW_INFO); 175 } 176 } 177 178 return menu_model_.get(); 179} 180 181bool AppContextMenu::IsItemForCommandIdDynamic(int command_id) const { 182 return command_id == TOGGLE_PIN || command_id == LAUNCH_NEW; 183} 184 185base::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; 194#else 195 const bool launches_in_tab = IsCommandIdChecked(USE_LAUNCH_TYPE_PINNED) || 196 IsCommandIdChecked(USE_LAUNCH_TYPE_REGULAR); 197#endif 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); 201 } else { 202 NOTREACHED(); 203 return base::string16(); 204 } 205} 206 207bool 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( 213 command_id)) { 214 return extension_menu_items_->IsCommandIdChecked(command_id); 215 } 216 return false; 217} 218 219bool 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( 228 command_id)) { 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; 238 } 239 return true; 240} 241 242bool AppContextMenu::GetAcceleratorForCommandId( 243 int command_id, 244 ui::Accelerator* acclelrator) { 245 return false; 246} 247 248void 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_); 255 else 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; 273 } 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( 280 command_id)) { 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); 287 } 288} 289 290} // namespace app_list 291