1 // Copyright (c) 2012 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/ash/launcher/launcher_context_menu.h" 6 7 #include <string> 8 9 #include "ash/desktop_background/user_wallpaper_delegate.h" 10 #include "ash/metrics/user_metrics_recorder.h" 11 #include "ash/root_window_controller.h" 12 #include "ash/shelf/shelf_item_delegate.h" 13 #include "ash/shelf/shelf_widget.h" 14 #include "ash/shell.h" 15 #include "base/bind.h" 16 #include "base/prefs/pref_service.h" 17 #include "chrome/browser/extensions/context_menu_matcher.h" 18 #include "chrome/browser/extensions/extension_util.h" 19 #include "chrome/browser/fullscreen.h" 20 #include "chrome/browser/prefs/incognito_mode_prefs.h" 21 #include "chrome/browser/profiles/profile.h" 22 #include "chrome/browser/ui/ash/chrome_shell_delegate.h" 23 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" 24 #include "chrome/common/extensions/extension_constants.h" 25 #include "chrome/grit/generated_resources.h" 26 #include "content/public/common/context_menu_params.h" 27 #include "grit/ash_strings.h" 28 #include "ui/base/l10n/l10n_util.h" 29 30 namespace { 31 32 bool MenuItemHasLauncherContext(const extensions::MenuItem* item) { 33 return item->contexts().Contains(extensions::MenuItem::LAUNCHER); 34 } 35 36 } // namespace 37 38 LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, 39 const ash::ShelfItem* item, 40 aura::Window* root) 41 : ui::SimpleMenuModel(NULL), 42 controller_(controller), 43 item_(*item), 44 shelf_alignment_menu_(root), 45 root_window_(root), 46 item_delegate_(NULL) { 47 DCHECK(item); 48 DCHECK(root_window_); 49 Init(); 50 } 51 52 LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, 53 ash::ShelfItemDelegate* item_delegate, 54 ash::ShelfItem* item, 55 aura::Window* root) 56 : ui::SimpleMenuModel(NULL), 57 controller_(controller), 58 item_(*item), 59 shelf_alignment_menu_(root), 60 root_window_(root), 61 item_delegate_(item_delegate) { 62 DCHECK(item); 63 DCHECK(root_window_); 64 Init(); 65 } 66 67 LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, 68 aura::Window* root) 69 : ui::SimpleMenuModel(NULL), 70 controller_(controller), 71 item_(ash::ShelfItem()), 72 shelf_alignment_menu_(root), 73 extension_items_(new extensions::ContextMenuMatcher( 74 controller->profile(), this, this, 75 base::Bind(MenuItemHasLauncherContext))), 76 root_window_(root), 77 item_delegate_(NULL) { 78 DCHECK(root_window_); 79 Init(); 80 } 81 82 void LauncherContextMenu::Init() { 83 extension_items_.reset(new extensions::ContextMenuMatcher( 84 controller_->profile(), this, this, 85 base::Bind(MenuItemHasLauncherContext))); 86 set_delegate(this); 87 88 if (is_valid_item()) { 89 if (item_.type == ash::TYPE_APP_SHORTCUT || 90 item_.type == ash::TYPE_WINDOWED_APP) { 91 // V1 apps can be started from the menu - but V2 apps should not. 92 if (!controller_->IsPlatformApp(item_.id)) { 93 AddItem(MENU_OPEN_NEW, base::string16()); 94 AddSeparator(ui::NORMAL_SEPARATOR); 95 } 96 AddItem( 97 MENU_PIN, 98 l10n_util::GetStringUTF16(controller_->IsPinned(item_.id) ? 99 IDS_LAUNCHER_CONTEXT_MENU_UNPIN : 100 IDS_LAUNCHER_CONTEXT_MENU_PIN)); 101 if (controller_->IsOpen(item_.id)) { 102 AddItem(MENU_CLOSE, 103 l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_CLOSE)); 104 } 105 if (!controller_->IsPlatformApp(item_.id) && 106 item_.type != ash::TYPE_WINDOWED_APP) { 107 AddSeparator(ui::NORMAL_SEPARATOR); 108 if (extensions::util::IsStreamlinedHostedAppsEnabled()) { 109 // Streamlined hosted apps launch in a window by default. This menu 110 // item is re-interpreted as a single, toggle-able option to launch 111 // the hosted app as a tab. 112 AddCheckItemWithStringId( 113 LAUNCH_TYPE_REGULAR_TAB, 114 IDS_APP_CONTEXT_MENU_OPEN_TAB); 115 } else { 116 AddCheckItemWithStringId( 117 LAUNCH_TYPE_REGULAR_TAB, 118 IDS_APP_CONTEXT_MENU_OPEN_REGULAR); 119 AddCheckItemWithStringId( 120 LAUNCH_TYPE_PINNED_TAB, 121 IDS_APP_CONTEXT_MENU_OPEN_PINNED); 122 AddCheckItemWithStringId( 123 LAUNCH_TYPE_WINDOW, 124 IDS_APP_CONTEXT_MENU_OPEN_WINDOW); 125 // Even though the launch type is Full Screen it is more accurately 126 // described as Maximized in Ash. 127 AddCheckItemWithStringId( 128 LAUNCH_TYPE_FULLSCREEN, 129 IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED); 130 } 131 } 132 } else if (item_.type == ash::TYPE_BROWSER_SHORTCUT) { 133 AddItem(MENU_NEW_WINDOW, 134 l10n_util::GetStringUTF16(IDS_LAUNCHER_NEW_WINDOW)); 135 if (!controller_->IsLoggedInAsGuest()) { 136 AddItem(MENU_NEW_INCOGNITO_WINDOW, 137 l10n_util::GetStringUTF16(IDS_LAUNCHER_NEW_INCOGNITO_WINDOW)); 138 } 139 } else if (item_.type == ash::TYPE_DIALOG) { 140 AddItem(MENU_CLOSE, 141 l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_CLOSE)); 142 } else { 143 if (item_.type == ash::TYPE_PLATFORM_APP) { 144 AddItem( 145 MENU_PIN, 146 l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_PIN)); 147 AddItem(MENU_INSTALL, l10n_util::GetStringUTF16(IDS_APP_INSTALL_TITLE)); 148 } 149 if (controller_->IsOpen(item_.id)) { 150 AddItem(MENU_CLOSE, 151 l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_CLOSE)); 152 } 153 } 154 AddSeparator(ui::NORMAL_SEPARATOR); 155 if (item_.type == ash::TYPE_APP_SHORTCUT || 156 item_.type == ash::TYPE_WINDOWED_APP || 157 item_.type == ash::TYPE_PLATFORM_APP) { 158 const extensions::MenuItem::ExtensionKey app_key( 159 controller_->GetAppIDForShelfID(item_.id)); 160 if (!app_key.empty()) { 161 int index = 0; 162 extension_items_->AppendExtensionItems(app_key, 163 base::string16(), 164 &index, 165 false); // is_action_menu 166 AddSeparator(ui::NORMAL_SEPARATOR); 167 } 168 } 169 } 170 // In fullscreen, the launcher is either hidden or autohidden depending on 171 // the type of fullscreen. Do not show the auto-hide menu item while in 172 // fullscreen because it is confusing when the preference appears not to 173 // apply. 174 if (!IsFullScreenMode() && 175 controller_->CanUserModifyShelfAutoHideBehavior(root_window_)) { 176 AddCheckItemWithStringId(MENU_AUTO_HIDE, 177 IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE); 178 } 179 if (ash::ShelfWidget::ShelfAlignmentAllowed()) { 180 AddSubMenuWithStringId(MENU_ALIGNMENT_MENU, 181 IDS_ASH_SHELF_CONTEXT_MENU_POSITION, 182 &shelf_alignment_menu_); 183 } 184 #if defined(OS_CHROMEOS) 185 AddItem(MENU_CHANGE_WALLPAPER, 186 l10n_util::GetStringUTF16(IDS_AURA_SET_DESKTOP_WALLPAPER)); 187 #endif 188 } 189 190 LauncherContextMenu::~LauncherContextMenu() { 191 } 192 193 bool LauncherContextMenu::IsItemForCommandIdDynamic(int command_id) const { 194 return command_id == MENU_OPEN_NEW; 195 } 196 197 base::string16 LauncherContextMenu::GetLabelForCommandId(int command_id) const { 198 if (command_id == MENU_OPEN_NEW) { 199 if (item_.type == ash::TYPE_PLATFORM_APP) { 200 return l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_NEW_WINDOW); 201 } 202 switch (controller_->GetLaunchType(item_.id)) { 203 case extensions::LAUNCH_TYPE_PINNED: 204 case extensions::LAUNCH_TYPE_REGULAR: 205 return l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_NEW_TAB); 206 case extensions::LAUNCH_TYPE_FULLSCREEN: 207 case extensions::LAUNCH_TYPE_WINDOW: 208 return l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_NEW_WINDOW); 209 default: 210 NOTREACHED(); 211 return base::string16(); 212 } 213 } 214 NOTREACHED(); 215 return base::string16(); 216 } 217 218 bool LauncherContextMenu::IsCommandIdChecked(int command_id) const { 219 switch (command_id) { 220 case LAUNCH_TYPE_PINNED_TAB: 221 return controller_->GetLaunchType(item_.id) == 222 extensions::LAUNCH_TYPE_PINNED; 223 case LAUNCH_TYPE_REGULAR_TAB: 224 return controller_->GetLaunchType(item_.id) == 225 extensions::LAUNCH_TYPE_REGULAR; 226 case LAUNCH_TYPE_WINDOW: 227 return controller_->GetLaunchType(item_.id) == 228 extensions::LAUNCH_TYPE_WINDOW; 229 case LAUNCH_TYPE_FULLSCREEN: 230 return controller_->GetLaunchType(item_.id) == 231 extensions::LAUNCH_TYPE_FULLSCREEN; 232 case MENU_AUTO_HIDE: 233 return controller_->GetShelfAutoHideBehavior(root_window_) == 234 ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS; 235 default: 236 return extension_items_->IsCommandIdChecked(command_id); 237 } 238 } 239 240 bool LauncherContextMenu::IsCommandIdEnabled(int command_id) const { 241 switch (command_id) { 242 case MENU_PIN: 243 return controller_->IsPinnable(item_.id); 244 #if defined(OS_CHROMEOS) 245 case MENU_CHANGE_WALLPAPER: 246 return ash::Shell::GetInstance()->user_wallpaper_delegate()-> 247 CanOpenSetWallpaperPage(); 248 #endif 249 case MENU_NEW_WINDOW: 250 // "Normal" windows are not allowed when incognito is enforced. 251 return IncognitoModePrefs::GetAvailability( 252 controller_->profile()->GetPrefs()) != IncognitoModePrefs::FORCED; 253 case MENU_AUTO_HIDE: 254 return controller_->CanUserModifyShelfAutoHideBehavior(root_window_); 255 case MENU_NEW_INCOGNITO_WINDOW: 256 // Incognito windows are not allowed when incognito is disabled. 257 return IncognitoModePrefs::GetAvailability( 258 controller_->profile()->GetPrefs()) != IncognitoModePrefs::DISABLED; 259 default: 260 return extension_items_->IsCommandIdEnabled(command_id); 261 } 262 } 263 264 bool LauncherContextMenu::IsCommandIdVisible(int command_id) const { 265 if (item_.type != ash::TYPE_PLATFORM_APP) 266 return true; 267 268 switch (command_id) { 269 case MENU_PIN: 270 return !controller_->CanInstall(item_.id); 271 case MENU_INSTALL: 272 return controller_->CanInstall(item_.id); 273 default: 274 return true; 275 } 276 } 277 278 bool LauncherContextMenu::GetAcceleratorForCommandId( 279 int command_id, 280 ui::Accelerator* accelerator) { 281 return false; 282 } 283 284 void LauncherContextMenu::ExecuteCommand(int command_id, int event_flags) { 285 switch (static_cast<MenuItem>(command_id)) { 286 case MENU_OPEN_NEW: 287 controller_->Launch(item_.id, ui::EF_NONE); 288 break; 289 case MENU_CLOSE: 290 if (item_.type == ash::TYPE_DIALOG) { 291 DCHECK(item_delegate_); 292 item_delegate_->Close(); 293 } else { 294 // TODO(simonhong): Use ShelfItemDelegate::Close(). 295 controller_->Close(item_.id); 296 } 297 ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction( 298 ash::UMA_CLOSE_THROUGH_CONTEXT_MENU); 299 break; 300 case MENU_PIN: 301 controller_->TogglePinned(item_.id); 302 break; 303 case MENU_INSTALL: 304 controller_->Install(item_.id); 305 break; 306 case LAUNCH_TYPE_PINNED_TAB: 307 controller_->SetLaunchType(item_.id, extensions::LAUNCH_TYPE_PINNED); 308 break; 309 case LAUNCH_TYPE_REGULAR_TAB: { 310 extensions::LaunchType launch_type = 311 extensions::LAUNCH_TYPE_REGULAR; 312 // Streamlined hosted apps can only toggle between LAUNCH_WINDOW and 313 // LAUNCH_REGULAR. 314 if (extensions::util::IsStreamlinedHostedAppsEnabled()) { 315 launch_type = controller_->GetLaunchType(item_.id) == 316 extensions::LAUNCH_TYPE_REGULAR 317 ? extensions::LAUNCH_TYPE_WINDOW 318 : extensions::LAUNCH_TYPE_REGULAR; 319 } 320 controller_->SetLaunchType(item_.id, launch_type); 321 break; 322 } 323 case LAUNCH_TYPE_WINDOW: 324 controller_->SetLaunchType(item_.id, extensions::LAUNCH_TYPE_WINDOW); 325 break; 326 case LAUNCH_TYPE_FULLSCREEN: 327 controller_->SetLaunchType(item_.id, extensions::LAUNCH_TYPE_FULLSCREEN); 328 break; 329 case MENU_AUTO_HIDE: 330 controller_->ToggleShelfAutoHideBehavior(root_window_); 331 break; 332 case MENU_NEW_WINDOW: 333 controller_->CreateNewWindow(); 334 break; 335 case MENU_NEW_INCOGNITO_WINDOW: 336 controller_->CreateNewIncognitoWindow(); 337 break; 338 case MENU_ALIGNMENT_MENU: 339 break; 340 #if defined(OS_CHROMEOS) 341 case MENU_CHANGE_WALLPAPER: 342 ash::Shell::GetInstance()->user_wallpaper_delegate()-> 343 OpenSetWallpaperPage(); 344 break; 345 #endif 346 default: 347 extension_items_->ExecuteCommand(command_id, NULL, 348 content::ContextMenuParams()); 349 } 350 } 351