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/command_line.h" 17#include "base/prefs/pref_service.h" 18#include "chrome/browser/extensions/context_menu_matcher.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/chrome_switches.h" 25#include "chrome/common/extensions/extension_constants.h" 26#include "content/public/common/context_menu_params.h" 27#include "grit/ash_strings.h" 28#include "grit/generated_resources.h" 29#include "ui/base/l10n/l10n_util.h" 30 31namespace { 32 33bool MenuItemHasLauncherContext(const extensions::MenuItem* item) { 34 return item->contexts().Contains(extensions::MenuItem::LAUNCHER); 35} 36 37} // namespace 38 39LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, 40 const ash::ShelfItem* item, 41 aura::Window* root) 42 : ui::SimpleMenuModel(NULL), 43 controller_(controller), 44 item_(*item), 45 shelf_alignment_menu_(root), 46 root_window_(root), 47 item_delegate_(NULL) { 48 DCHECK(item); 49 DCHECK(root_window_); 50 Init(); 51} 52 53LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, 54 ash::ShelfItemDelegate* item_delegate, 55 ash::ShelfItem* item, 56 aura::Window* root) 57 : ui::SimpleMenuModel(NULL), 58 controller_(controller), 59 item_(*item), 60 shelf_alignment_menu_(root), 61 root_window_(root), 62 item_delegate_(item_delegate) { 63 DCHECK(item); 64 DCHECK(root_window_); 65 Init(); 66} 67 68LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, 69 aura::Window* root) 70 : ui::SimpleMenuModel(NULL), 71 controller_(controller), 72 item_(ash::ShelfItem()), 73 shelf_alignment_menu_(root), 74 extension_items_(new extensions::ContextMenuMatcher( 75 controller->profile(), this, this, 76 base::Bind(MenuItemHasLauncherContext))), 77 root_window_(root), 78 item_delegate_(NULL) { 79 DCHECK(root_window_); 80 Init(); 81} 82 83void LauncherContextMenu::Init() { 84 extension_items_.reset(new extensions::ContextMenuMatcher( 85 controller_->profile(), this, this, 86 base::Bind(MenuItemHasLauncherContext))); 87 set_delegate(this); 88 89 if (is_valid_item()) { 90 if (item_.type == ash::TYPE_APP_SHORTCUT || 91 item_.type == ash::TYPE_WINDOWED_APP) { 92 // V1 apps can be started from the menu - but V2 apps should not. 93 if (!controller_->IsPlatformApp(item_.id)) { 94 AddItem(MENU_OPEN_NEW, base::string16()); 95 AddSeparator(ui::NORMAL_SEPARATOR); 96 } 97 AddItem( 98 MENU_PIN, 99 l10n_util::GetStringUTF16(controller_->IsPinned(item_.id) ? 100 IDS_LAUNCHER_CONTEXT_MENU_UNPIN : 101 IDS_LAUNCHER_CONTEXT_MENU_PIN)); 102 if (controller_->IsOpen(item_.id)) { 103 AddItem(MENU_CLOSE, 104 l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_CLOSE)); 105 } 106 if (!controller_->IsPlatformApp(item_.id) && 107 item_.type != ash::TYPE_WINDOWED_APP) { 108 AddSeparator(ui::NORMAL_SEPARATOR); 109 if (CommandLine::ForCurrentProcess()->HasSwitch( 110 switches::kEnableStreamlinedHostedApps)) { 111 // Streamlined hosted apps launch in a window by default. This menu 112 // item is re-interpreted as a single, toggle-able option to launch 113 // the hosted app as a tab. 114 AddCheckItemWithStringId( 115 LAUNCH_TYPE_REGULAR_TAB, 116 IDS_APP_CONTEXT_MENU_OPEN_TAB); 117 } else { 118 AddCheckItemWithStringId( 119 LAUNCH_TYPE_REGULAR_TAB, 120 IDS_APP_CONTEXT_MENU_OPEN_REGULAR); 121 AddCheckItemWithStringId( 122 LAUNCH_TYPE_PINNED_TAB, 123 IDS_APP_CONTEXT_MENU_OPEN_PINNED); 124 AddCheckItemWithStringId( 125 LAUNCH_TYPE_WINDOW, 126 IDS_APP_CONTEXT_MENU_OPEN_WINDOW); 127 // Even though the launch type is Full Screen it is more accurately 128 // described as Maximized in Ash. 129 AddCheckItemWithStringId( 130 LAUNCH_TYPE_FULLSCREEN, 131 IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED); 132 } 133 } 134 } else if (item_.type == ash::TYPE_BROWSER_SHORTCUT) { 135 AddItem(MENU_NEW_WINDOW, 136 l10n_util::GetStringUTF16(IDS_LAUNCHER_NEW_WINDOW)); 137 if (!controller_->IsLoggedInAsGuest()) { 138 AddItem(MENU_NEW_INCOGNITO_WINDOW, 139 l10n_util::GetStringUTF16(IDS_LAUNCHER_NEW_INCOGNITO_WINDOW)); 140 } 141 } else if (item_.type == ash::TYPE_DIALOG) { 142 AddItem(MENU_CLOSE, 143 l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_CLOSE)); 144 } else { 145 if (item_.type == ash::TYPE_PLATFORM_APP) { 146 AddItem( 147 MENU_PIN, 148 l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_PIN)); 149 AddItem(MENU_INSTALL, l10n_util::GetStringUTF16(IDS_APP_INSTALL_TITLE)); 150 } 151 if (controller_->IsOpen(item_.id)) { 152 AddItem(MENU_CLOSE, 153 l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_CLOSE)); 154 } 155 } 156 AddSeparator(ui::NORMAL_SEPARATOR); 157 if (item_.type == ash::TYPE_APP_SHORTCUT || 158 item_.type == ash::TYPE_WINDOWED_APP || 159 item_.type == ash::TYPE_PLATFORM_APP) { 160 const extensions::MenuItem::ExtensionKey app_key( 161 controller_->GetAppIDForShelfID(item_.id)); 162 if (!app_key.empty()) { 163 int index = 0; 164 extension_items_->AppendExtensionItems( 165 app_key, base::string16(), &index); 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 190LauncherContextMenu::~LauncherContextMenu() { 191} 192 193bool LauncherContextMenu::IsItemForCommandIdDynamic(int command_id) const { 194 return command_id == MENU_OPEN_NEW; 195} 196 197base::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 218bool 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 240bool 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 264bool 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 278bool LauncherContextMenu::GetAcceleratorForCommandId( 279 int command_id, 280 ui::Accelerator* accelerator) { 281 return false; 282} 283 284void 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 (CommandLine::ForCurrentProcess()->HasSwitch( 315 switches::kEnableStreamlinedHostedApps)) { 316 launch_type = controller_->GetLaunchType(item_.id) == 317 extensions::LAUNCH_TYPE_REGULAR 318 ? extensions::LAUNCH_TYPE_WINDOW 319 : extensions::LAUNCH_TYPE_REGULAR; 320 } 321 controller_->SetLaunchType(item_.id, launch_type); 322 break; 323 } 324 case LAUNCH_TYPE_WINDOW: 325 controller_->SetLaunchType(item_.id, extensions::LAUNCH_TYPE_WINDOW); 326 break; 327 case LAUNCH_TYPE_FULLSCREEN: 328 controller_->SetLaunchType(item_.id, extensions::LAUNCH_TYPE_FULLSCREEN); 329 break; 330 case MENU_AUTO_HIDE: 331 controller_->ToggleShelfAutoHideBehavior(root_window_); 332 break; 333 case MENU_NEW_WINDOW: 334 controller_->CreateNewWindow(); 335 break; 336 case MENU_NEW_INCOGNITO_WINDOW: 337 controller_->CreateNewIncognitoWindow(); 338 break; 339 case MENU_ALIGNMENT_MENU: 340 break; 341#if defined(OS_CHROMEOS) 342 case MENU_CHANGE_WALLPAPER: 343 ash::Shell::GetInstance()->user_wallpaper_delegate()-> 344 OpenSetWallpaperPage(); 345 break; 346#endif 347 default: 348 extension_items_->ExecuteCommand(command_id, NULL, 349 content::ContextMenuParams()); 350 } 351} 352