Home | History | Annotate | Download | only in launcher
      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