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