Home | History | Annotate | Download | only in app_list
      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 "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "chrome/app/chrome_command_ids.h"
     10 #include "chrome/browser/chromeos/genius_app/app_id.h"
     11 #include "chrome/browser/extensions/context_menu_matcher.h"
     12 #include "chrome/browser/extensions/menu_manager.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/common/chrome_switches.h"
     18 #include "chrome/common/extensions/extension_constants.h"
     19 #include "chrome/common/extensions/manifest_url_handler.h"
     20 #include "content/public/common/context_menu_params.h"
     21 #include "grit/chromium_strings.h"
     22 #include "grit/generated_resources.h"
     23 #include "ui/base/l10n/l10n_util.h"
     24 
     25 #if defined(USE_ASH)
     26 #include "ash/shell.h"
     27 #endif
     28 
     29 namespace app_list {
     30 
     31 namespace {
     32 
     33 enum CommandId {
     34   LAUNCH_NEW = 100,
     35   TOGGLE_PIN,
     36   CREATE_SHORTCUTS,
     37   SHOW_APP_INFO,
     38   OPTIONS,
     39   UNINSTALL,
     40   REMOVE_FROM_FOLDER,
     41   DETAILS,
     42   MENU_NEW_WINDOW,
     43   MENU_NEW_INCOGNITO_WINDOW,
     44   // Order matters in USE_LAUNCH_TYPE_* and must match the LaunchType enum.
     45   USE_LAUNCH_TYPE_COMMAND_START = 200,
     46   USE_LAUNCH_TYPE_PINNED = USE_LAUNCH_TYPE_COMMAND_START,
     47   USE_LAUNCH_TYPE_REGULAR,
     48   USE_LAUNCH_TYPE_FULLSCREEN,
     49   USE_LAUNCH_TYPE_WINDOW,
     50   USE_LAUNCH_TYPE_COMMAND_END,
     51 };
     52 
     53 bool MenuItemHasLauncherContext(const extensions::MenuItem* item) {
     54   return item->contexts().Contains(extensions::MenuItem::LAUNCHER);
     55 }
     56 
     57 }  // namespace
     58 
     59 AppContextMenu::AppContextMenu(AppContextMenuDelegate* delegate,
     60                                Profile* profile,
     61                                const std::string& app_id,
     62                                AppListControllerDelegate* controller)
     63     : delegate_(delegate),
     64       profile_(profile),
     65       app_id_(app_id),
     66       controller_(controller),
     67       is_platform_app_(false),
     68       is_search_result_(false),
     69       is_in_folder_(false) {
     70 }
     71 
     72 AppContextMenu::~AppContextMenu() {
     73 }
     74 
     75 ui::MenuModel* AppContextMenu::GetMenuModel() {
     76   if (!controller_->IsExtensionInstalled(profile_, app_id_))
     77     return NULL;
     78 
     79   if (menu_model_.get())
     80     return menu_model_.get();
     81 
     82   menu_model_.reset(new ui::SimpleMenuModel(this));
     83 
     84   if (app_id_ == extension_misc::kChromeAppId) {
     85     menu_model_->AddItemWithStringId(
     86         MENU_NEW_WINDOW,
     87         IDS_APP_LIST_NEW_WINDOW);
     88     if (!profile_->IsOffTheRecord()) {
     89       menu_model_->AddItemWithStringId(
     90           MENU_NEW_INCOGNITO_WINDOW,
     91           IDS_APP_LIST_NEW_INCOGNITO_WINDOW);
     92     }
     93   } else {
     94     extension_menu_items_.reset(new extensions::ContextMenuMatcher(
     95         profile_, this, menu_model_.get(),
     96         base::Bind(MenuItemHasLauncherContext)));
     97 
     98     if (!is_platform_app_)
     99       menu_model_->AddItem(LAUNCH_NEW, base::string16());
    100 
    101     int index = 0;
    102     extension_menu_items_->AppendExtensionItems(
    103         extensions::MenuItem::ExtensionKey(app_id_), base::string16(), &index);
    104 
    105     // Show Pin/Unpin option if shelf is available.
    106     if (controller_->GetPinnable() != AppListControllerDelegate::NO_PIN) {
    107       menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
    108       menu_model_->AddItemWithStringId(
    109           TOGGLE_PIN,
    110           controller_->IsAppPinned(app_id_) ?
    111               IDS_APP_LIST_CONTEXT_MENU_UNPIN :
    112               IDS_APP_LIST_CONTEXT_MENU_PIN);
    113     }
    114 
    115     if (controller_->CanDoCreateShortcutsFlow()) {
    116       menu_model_->AddItemWithStringId(CREATE_SHORTCUTS,
    117                                        IDS_NEW_TAB_APP_CREATE_SHORTCUT);
    118     }
    119 
    120     // Don't display the app info dialog for the Store app or the Genius app.
    121     // TODO(sashab): Update the metadata for these apps so their dialogs can be
    122     // re-enabled (see crbug.com/383713).
    123     if (controller_->CanDoShowAppInfoFlow() &&
    124         app_id_ != extension_misc::kWebStoreAppId &&
    125         app_id_ != genius_app::kGeniusAppId) {
    126       menu_model_->AddItemWithStringId(SHOW_APP_INFO,
    127                                        IDS_APP_CONTEXT_MENU_SHOW_INFO);
    128     }
    129 
    130     if (!is_platform_app_) {
    131       menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
    132       // Streamlined hosted apps can only toggle between USE_LAUNCH_TYPE_WINDOW
    133       // and USE_LAUNCH_TYPE_REGULAR.
    134       if (CommandLine::ForCurrentProcess()->HasSwitch(
    135           switches::kEnableStreamlinedHostedApps)) {
    136         menu_model_->AddCheckItemWithStringId(
    137             USE_LAUNCH_TYPE_REGULAR,
    138             IDS_APP_CONTEXT_MENU_OPEN_TAB);
    139       } else {
    140         menu_model_->AddCheckItemWithStringId(
    141             USE_LAUNCH_TYPE_REGULAR,
    142             IDS_APP_CONTEXT_MENU_OPEN_REGULAR);
    143         menu_model_->AddCheckItemWithStringId(
    144             USE_LAUNCH_TYPE_PINNED,
    145             IDS_APP_CONTEXT_MENU_OPEN_PINNED);
    146 #if defined(OS_MACOSX)
    147         // Mac does not support standalone web app browser windows or maximize.
    148         menu_model_->AddCheckItemWithStringId(
    149             USE_LAUNCH_TYPE_FULLSCREEN,
    150             IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN);
    151 #else
    152         menu_model_->AddCheckItemWithStringId(
    153             USE_LAUNCH_TYPE_WINDOW,
    154             IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
    155         // Even though the launch type is Full Screen it is more accurately
    156         // described as Maximized in Ash.
    157         menu_model_->AddCheckItemWithStringId(
    158             USE_LAUNCH_TYPE_FULLSCREEN,
    159             IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED);
    160 #endif
    161       }
    162       menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
    163       menu_model_->AddItemWithStringId(OPTIONS, IDS_NEW_TAB_APP_OPTIONS);
    164     }
    165 
    166     menu_model_->AddItemWithStringId(DETAILS, IDS_NEW_TAB_APP_DETAILS);
    167     menu_model_->AddItemWithStringId(
    168         UNINSTALL,
    169         is_platform_app_ ? IDS_APP_LIST_UNINSTALL_ITEM
    170                          : IDS_EXTENSIONS_UNINSTALL);
    171   }
    172 
    173   return menu_model_.get();
    174 }
    175 
    176 bool AppContextMenu::IsItemForCommandIdDynamic(int command_id) const {
    177   return command_id == TOGGLE_PIN || command_id == LAUNCH_NEW;
    178 }
    179 
    180 base::string16 AppContextMenu::GetLabelForCommandId(int command_id) const {
    181   if (command_id == TOGGLE_PIN) {
    182     return controller_->IsAppPinned(app_id_) ?
    183         l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_UNPIN) :
    184         l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_PIN);
    185   } else if (command_id == LAUNCH_NEW) {
    186 #if defined(OS_MACOSX)
    187     // Even fullscreen windows launch in a browser tab on Mac.
    188     const bool launches_in_tab = true;
    189 #else
    190     const bool launches_in_tab = IsCommandIdChecked(USE_LAUNCH_TYPE_PINNED) ||
    191         IsCommandIdChecked(USE_LAUNCH_TYPE_REGULAR);
    192 #endif
    193     return launches_in_tab ?
    194         l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_TAB) :
    195         l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW);
    196   } else {
    197     NOTREACHED();
    198     return base::string16();
    199   }
    200 }
    201 
    202 bool AppContextMenu::IsCommandIdChecked(int command_id) const {
    203   if (command_id >= USE_LAUNCH_TYPE_COMMAND_START &&
    204       command_id < USE_LAUNCH_TYPE_COMMAND_END) {
    205     return static_cast<int>(controller_->GetExtensionLaunchType(
    206         profile_, app_id_)) + USE_LAUNCH_TYPE_COMMAND_START == command_id;
    207   } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
    208              command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
    209     return extension_menu_items_->IsCommandIdChecked(command_id);
    210   }
    211   return false;
    212 }
    213 
    214 bool AppContextMenu::IsCommandIdEnabled(int command_id) const {
    215   if (command_id == TOGGLE_PIN) {
    216     return controller_->GetPinnable() ==
    217         AppListControllerDelegate::PIN_EDITABLE;
    218   } else if (command_id == OPTIONS) {
    219     return controller_->HasOptionsPage(profile_, app_id_);
    220   } else if (command_id == UNINSTALL) {
    221     return controller_->UserMayModifySettings(profile_, app_id_);
    222   } else if (command_id == DETAILS) {
    223     return controller_->IsAppFromWebStore(profile_, app_id_);
    224   } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
    225              command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
    226     return extension_menu_items_->IsCommandIdEnabled(command_id);
    227   } else if (command_id == MENU_NEW_WINDOW) {
    228     // "Normal" windows are not allowed when incognito is enforced.
    229     return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) !=
    230         IncognitoModePrefs::FORCED;
    231   } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) {
    232     // Incognito windows are not allowed when incognito is disabled.
    233     return IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) !=
    234         IncognitoModePrefs::DISABLED;
    235   }
    236   return true;
    237 }
    238 
    239 bool AppContextMenu::GetAcceleratorForCommandId(
    240     int command_id,
    241     ui::Accelerator* acclelrator) {
    242   return false;
    243 }
    244 
    245 void AppContextMenu::ExecuteCommand(int command_id, int event_flags) {
    246   if (command_id == LAUNCH_NEW) {
    247     delegate_->ExecuteLaunchCommand(event_flags);
    248   } else if (command_id == TOGGLE_PIN && controller_->GetPinnable() ==
    249       AppListControllerDelegate::PIN_EDITABLE) {
    250     if (controller_->IsAppPinned(app_id_))
    251       controller_->UnpinApp(app_id_);
    252     else
    253       controller_->PinApp(app_id_);
    254   } else if (command_id == CREATE_SHORTCUTS) {
    255     controller_->DoCreateShortcutsFlow(profile_, app_id_);
    256   } else if (command_id == SHOW_APP_INFO) {
    257     controller_->DoShowAppInfoFlow(profile_, app_id_);
    258   } else if (command_id >= USE_LAUNCH_TYPE_COMMAND_START &&
    259              command_id < USE_LAUNCH_TYPE_COMMAND_END) {
    260     extensions::LaunchType launch_type = static_cast<extensions::LaunchType>(
    261         command_id - USE_LAUNCH_TYPE_COMMAND_START);
    262     // Streamlined hosted apps can only toggle between LAUNCH_TYPE_WINDOW and
    263     // LAUNCH_TYPE_REGULAR.
    264     if (CommandLine::ForCurrentProcess()->HasSwitch(
    265         switches::kEnableStreamlinedHostedApps)) {
    266       launch_type = (controller_->GetExtensionLaunchType(profile_, app_id_) ==
    267                      extensions::LAUNCH_TYPE_REGULAR) ?
    268                     extensions::LAUNCH_TYPE_WINDOW :
    269                     extensions::LAUNCH_TYPE_REGULAR;
    270     }
    271     controller_->SetExtensionLaunchType(profile_, app_id_, launch_type);
    272   } else if (command_id == OPTIONS) {
    273     controller_->ShowOptionsPage(profile_, app_id_);
    274   } else if (command_id == UNINSTALL) {
    275     controller_->UninstallApp(profile_, app_id_);
    276   } else if (command_id == DETAILS) {
    277     controller_->ShowAppInWebStore(profile_, app_id_, is_search_result_);
    278   } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
    279              command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
    280     extension_menu_items_->ExecuteCommand(command_id, NULL,
    281                                           content::ContextMenuParams());
    282   } else if (command_id == MENU_NEW_WINDOW) {
    283     controller_->CreateNewWindow(profile_, false);
    284   } else if (command_id == MENU_NEW_INCOGNITO_WINDOW) {
    285     controller_->CreateNewWindow(profile_, true);
    286   }
    287 }
    288 
    289 }  // namespace app_list
    290