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