chrome_shell_delegate.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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/chrome_shell_delegate.h"
6
7#include "apps/shell_window.h"
8#include "ash/ash_switches.h"
9#include "ash/host/root_window_host_factory.h"
10#include "ash/launcher/launcher_types.h"
11#include "ash/magnifier/magnifier_constants.h"
12#include "ash/session_state_delegate.h"
13#include "ash/shelf/shelf_widget.h"
14#include "ash/system/tray/system_tray_delegate.h"
15#include "ash/wm/window_properties.h"
16#include "ash/wm/window_util.h"
17#include "base/bind.h"
18#include "base/command_line.h"
19#include "base/prefs/pref_service.h"
20#include "base/strings/utf_string_conversions.h"
21#include "chrome/browser/app_mode/app_mode_utils.h"
22#include "chrome/browser/chrome_notification_types.h"
23#include "chrome/browser/extensions/shell_window_registry.h"
24#include "chrome/browser/lifetime/application_lifetime.h"
25#include "chrome/browser/profiles/profile_manager.h"
26#include "chrome/browser/sessions/tab_restore_service.h"
27#include "chrome/browser/sessions/tab_restore_service_factory.h"
28#include "chrome/browser/sessions/tab_restore_service_observer.h"
29#include "chrome/browser/ui/app_list/app_list_view_delegate.h"
30#include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
31#include "chrome/browser/ui/ash/ash_keyboard_controller_proxy.h"
32#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
33#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
34#include "chrome/browser/ui/ash/user_action_handler.h"
35#include "chrome/browser/ui/ash/window_positioner.h"
36#include "chrome/browser/ui/browser.h"
37#include "chrome/browser/ui/browser_commands.h"
38#include "chrome/browser/ui/browser_finder.h"
39#include "chrome/browser/ui/browser_window.h"
40#include "chrome/browser/ui/extensions/native_app_window.h"
41#include "chrome/browser/ui/host_desktop.h"
42#include "chrome/browser/ui/immersive_fullscreen_configuration.h"
43#include "chrome/common/chrome_switches.h"
44#include "chrome/common/time_format.h"
45#include "content/public/browser/notification_service.h"
46#include "content/public/browser/user_metrics.h"
47#include "grit/chromium_strings.h"
48#include "grit/generated_resources.h"
49#include "ui/aura/client/user_action_client.h"
50#include "ui/aura/window.h"
51#include "ui/base/l10n/l10n_util.h"
52
53#if defined(OS_CHROMEOS)
54#include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h"
55#endif
56
57// static
58ChromeShellDelegate* ChromeShellDelegate::instance_ = NULL;
59
60namespace {
61
62void RestoreTabUsingProfile(Profile* profile) {
63  TabRestoreService* service = TabRestoreServiceFactory::GetForProfile(profile);
64  service->RestoreMostRecentEntry(NULL, chrome::HOST_DESKTOP_TYPE_ASH);
65}
66
67}  // namespace
68
69// TabRestoreHelper is used to restore a tab. In particular when the user
70// attempts to a restore a tab if the TabRestoreService hasn't finished loading
71// this waits for it. Once the TabRestoreService finishes loading the tab is
72// restored.
73class ChromeShellDelegate::TabRestoreHelper : public TabRestoreServiceObserver {
74 public:
75  TabRestoreHelper(ChromeShellDelegate* delegate,
76                   Profile* profile,
77                   TabRestoreService* service)
78      : delegate_(delegate),
79        profile_(profile),
80        tab_restore_service_(service) {
81    tab_restore_service_->AddObserver(this);
82  }
83
84  virtual ~TabRestoreHelper() {
85    tab_restore_service_->RemoveObserver(this);
86  }
87
88  TabRestoreService* tab_restore_service() { return tab_restore_service_; }
89
90  virtual void TabRestoreServiceChanged(TabRestoreService* service) OVERRIDE {
91  }
92  virtual void TabRestoreServiceDestroyed(TabRestoreService* service) OVERRIDE {
93    // This destroys us.
94    delegate_->tab_restore_helper_.reset();
95  }
96
97  virtual void TabRestoreServiceLoaded(TabRestoreService* service) OVERRIDE {
98    RestoreTabUsingProfile(profile_);
99    // This destroys us.
100    delegate_->tab_restore_helper_.reset();
101  }
102
103 private:
104  ChromeShellDelegate* delegate_;
105  Profile* profile_;
106  TabRestoreService* tab_restore_service_;
107
108  DISALLOW_COPY_AND_ASSIGN(TabRestoreHelper);
109};
110
111ChromeShellDelegate::ChromeShellDelegate()
112    : window_positioner_(new ash::WindowPositioner()),
113      weak_factory_(this),
114      launcher_delegate_(NULL) {
115  instance_ = this;
116  PlatformInit();
117}
118
119ChromeShellDelegate::~ChromeShellDelegate() {
120  if (instance_ == this)
121    instance_ = NULL;
122}
123
124bool ChromeShellDelegate::IsMultiProfilesEnabled() const {
125  return CommandLine::ForCurrentProcess()->HasSwitch(switches::kMultiProfiles);
126}
127
128bool ChromeShellDelegate::IsRunningInForcedAppMode() const {
129  return chrome::IsRunningInForcedAppMode();
130}
131
132void ChromeShellDelegate::Exit() {
133  chrome::AttemptUserExit();
134}
135
136void ChromeShellDelegate::NewTab() {
137  Browser* browser = GetTargetBrowser();
138  // If the browser was not active, we call BrowserWindow::Show to make it
139  // visible. Otherwise, we let Browser::NewTab handle the active window change.
140  const bool was_active = browser->window()->IsActive();
141  chrome::NewTab(browser);
142  if (!was_active)
143    browser->window()->Show();
144}
145
146void ChromeShellDelegate::NewWindow(bool is_incognito) {
147  Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
148  chrome::NewEmptyWindow(
149      is_incognito ? profile->GetOffTheRecordProfile() : profile,
150      chrome::HOST_DESKTOP_TYPE_ASH);
151}
152
153void ChromeShellDelegate::ToggleFullscreen() {
154  // Only toggle if the user has a window open.
155  aura::Window* window = ash::wm::GetActiveWindow();
156  if (!window)
157    return;
158
159  bool is_fullscreen = ash::wm::IsWindowFullscreen(window);
160
161  // Windows which cannot be maximized should not be fullscreened.
162  if (!is_fullscreen && !ash::wm::CanMaximizeWindow(window))
163    return;
164
165  Browser* browser = chrome::FindBrowserWithWindow(window);
166  if (browser) {
167    // If a window is fullscreen, exit fullscreen.
168    if (is_fullscreen) {
169      chrome::ToggleFullscreenMode(browser);
170      return;
171    }
172
173    // AppNonClientFrameViewAsh shows only the window controls and no other
174    // window decorations which is pretty close to fullscreen. Put v1 apps
175    // into maximized mode instead of fullscreen to avoid showing the ugly
176    // fullscreen exit bubble.
177#if defined(OS_WIN)
178    if (browser->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_NATIVE) {
179      chrome::ToggleFullscreenMode(browser);
180      return;
181    }
182#endif  // OS_WIN
183    if (browser->is_app() && browser->app_type() != Browser::APP_TYPE_CHILD)
184      ash::wm::ToggleMaximizedWindow(window);
185    else
186      chrome::ToggleFullscreenMode(browser);
187    return;
188  }
189
190  // |window| may belong to a shell window.
191  apps::ShellWindow* shell_window = extensions::ShellWindowRegistry::
192      GetShellWindowForNativeWindowAnyProfile(window);
193  if (shell_window) {
194    if (is_fullscreen)
195      shell_window->Restore();
196    else
197      shell_window->Fullscreen();
198  }
199}
200
201void ChromeShellDelegate::ToggleMaximized() {
202  // Only toggle if the user has a window open.
203  aura::Window* window = ash::wm::GetActiveWindow();
204  if (!window)
205    return;
206
207  // Get out of fullscreen when in fullscreen mode.
208  if (ash::wm::IsWindowFullscreen(window)) {
209    ToggleFullscreen();
210    return;
211  }
212  ash::wm::ToggleMaximizedWindow(window);
213}
214
215void ChromeShellDelegate::RestoreTab() {
216  if (tab_restore_helper_.get()) {
217    DCHECK(!tab_restore_helper_->tab_restore_service()->IsLoaded());
218    return;
219  }
220
221  Browser* browser = chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow());
222  Profile* profile = browser ? browser->profile() : NULL;
223  if (!profile)
224    profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
225  if (profile->IsOffTheRecord())
226    return;
227  TabRestoreService* service =
228      TabRestoreServiceFactory::GetForProfile(profile);
229  if (!service)
230    return;
231
232  if (service->IsLoaded()) {
233    RestoreTabUsingProfile(profile);
234  } else {
235    tab_restore_helper_.reset(new TabRestoreHelper(this, profile, service));
236    service->LoadTabsFromLastSession();
237  }
238}
239
240void ChromeShellDelegate::ShowTaskManager() {
241  chrome::OpenTaskManager(NULL);
242}
243
244content::BrowserContext* ChromeShellDelegate::GetCurrentBrowserContext() {
245  return ProfileManager::GetDefaultProfile();
246}
247
248app_list::AppListViewDelegate*
249    ChromeShellDelegate::CreateAppListViewDelegate() {
250  DCHECK(ash::Shell::HasInstance());
251  // Shell will own the created delegate, and the delegate will own
252  // the controller.
253  Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
254  return new AppListViewDelegate(new AppListControllerDelegateAsh(), profile);
255}
256
257ash::LauncherDelegate* ChromeShellDelegate::CreateLauncherDelegate(
258    ash::LauncherModel* model) {
259  DCHECK(ProfileManager::IsGetDefaultProfileAllowed());
260  // TODO(oshima): This is currently broken with multiple launchers.
261  // Refactor so that there is just one launcher delegate in the
262  // shell.
263  if (!launcher_delegate_) {
264    launcher_delegate_ = ChromeLauncherController::CreateInstance(NULL, model);
265    launcher_delegate_->Init();
266  }
267  return launcher_delegate_;
268}
269
270aura::client::UserActionClient* ChromeShellDelegate::CreateUserActionClient() {
271  return new UserActionHandler;
272}
273
274void ChromeShellDelegate::OpenFeedbackPage() {
275  chrome::OpenFeedbackDialog(GetTargetBrowser());
276}
277
278void ChromeShellDelegate::RecordUserMetricsAction(
279    ash::UserMetricsAction action) {
280  switch (action) {
281    case ash::UMA_ACCEL_KEYBOARD_BRIGHTNESS_DOWN_F6:
282      content::RecordAction(
283          content::UserMetricsAction("Accel_KeyboardBrightnessDown_F6"));
284      break;
285    case ash::UMA_ACCEL_KEYBOARD_BRIGHTNESS_UP_F7:
286      content::RecordAction(
287          content::UserMetricsAction("Accel_KeyboardBrightnessUp_F7"));
288      break;
289    case ash::UMA_ACCEL_LOCK_SCREEN_L:
290      content::RecordAction(
291          content::UserMetricsAction("Accel_LockScreen_L"));
292      break;
293    case ash::UMA_ACCEL_LOCK_SCREEN_LOCK_BUTTON:
294      content::RecordAction(
295          content::UserMetricsAction("Accel_LockScreen_LockButton"));
296      break;
297    case ash::UMA_ACCEL_LOCK_SCREEN_POWER_BUTTON:
298      content::RecordAction(
299          content::UserMetricsAction("Accel_LockScreen_PowerButton"));
300      break;
301    case ash::UMA_ACCEL_FULLSCREEN_F4:
302      content::RecordAction(content::UserMetricsAction("Accel_Fullscreen_F4"));
303      break;
304    case ash::UMA_ACCEL_MAXIMIZE_RESTORE_F4:
305      content::RecordAction(
306          content::UserMetricsAction("Accel_Maximize_Restore_F4"));
307      break;
308    case ash::UMA_ACCEL_NEWTAB_T:
309      content::RecordAction(content::UserMetricsAction("Accel_NewTab_T"));
310      break;
311    case ash::UMA_ACCEL_NEXTWINDOW_F5:
312      content::RecordAction(content::UserMetricsAction("Accel_NextWindow_F5"));
313      break;
314    case ash::UMA_ACCEL_NEXTWINDOW_TAB:
315      content::RecordAction(content::UserMetricsAction("Accel_NextWindow_Tab"));
316      break;
317    case ash::UMA_ACCEL_PREVWINDOW_F5:
318      content::RecordAction(content::UserMetricsAction("Accel_PrevWindow_F5"));
319      break;
320    case ash::UMA_ACCEL_PREVWINDOW_TAB:
321      content::RecordAction(content::UserMetricsAction("Accel_PrevWindow_Tab"));
322      break;
323    case ash::UMA_ACCEL_EXIT_FIRST_Q:
324      content::RecordAction(content::UserMetricsAction("Accel_Exit_First_Q"));
325      break;
326    case ash::UMA_ACCEL_EXIT_SECOND_Q:
327      content::RecordAction(content::UserMetricsAction("Accel_Exit_Second_Q"));
328      break;
329    case ash::UMA_ACCEL_SEARCH_LWIN:
330      content::RecordAction(content::UserMetricsAction("Accel_Search_LWin"));
331      break;
332    case ash::UMA_ACCEL_SHUT_DOWN_POWER_BUTTON:
333      content::RecordAction(
334          content::UserMetricsAction("Accel_ShutDown_PowerButton"));
335      break;
336    case ash::UMA_CLOSE_THROUGH_CONTEXT_MENU:
337      content::RecordAction(content::UserMetricsAction("CloseFromContextMenu"));
338      break;
339    case ash::UMA_LAUNCHER_CLICK_ON_APP:
340      content::RecordAction(content::UserMetricsAction("Launcher_ClickOnApp"));
341      break;
342    case ash::UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON:
343      content::RecordAction(
344          content::UserMetricsAction("Launcher_ClickOnApplistButton"));
345#if defined(OS_CHROMEOS)
346      chromeos::default_pinned_apps_field_trial::RecordShelfClick(
347          chromeos::default_pinned_apps_field_trial::APP_LAUNCHER);
348#endif
349      break;
350    case ash::UMA_MINIMIZE_PER_KEY:
351      content::RecordAction(content::UserMetricsAction("Minimize_UsingKey"));
352      break;
353    case ash::UMA_MOUSE_DOWN:
354      content::RecordAction(content::UserMetricsAction("Mouse_Down"));
355      break;
356    case ash::UMA_TOGGLE_MAXIMIZE_CAPTION_CLICK:
357      content::RecordAction(
358          content::UserMetricsAction("Caption_ClickTogglesMaximize"));
359      break;
360    case ash::UMA_TOGGLE_MAXIMIZE_CAPTION_GESTURE:
361      content::RecordAction(
362          content::UserMetricsAction("Caption_GestureTogglesMaximize"));
363      break;
364    case ash::UMA_TOUCHSCREEN_TAP_DOWN:
365      content::RecordAction(content::UserMetricsAction("Touchscreen_Down"));
366      break;
367    case ash::UMA_TRAY_HELP:
368      content::RecordAction(content::UserMetricsAction("Tray_Help"));
369      break;
370    case ash::UMA_TRAY_LOCK_SCREEN:
371      content::RecordAction(content::UserMetricsAction("Tray_LockScreen"));
372      break;
373    case ash::UMA_TRAY_SHUT_DOWN:
374      content::RecordAction(content::UserMetricsAction("Tray_ShutDown"));
375      break;
376    case ash::UMA_WINDOW_APP_CLOSE_BUTTON_CLICK:
377      content::RecordAction(content::UserMetricsAction("AppCloseButton_Clk"));
378      break;
379    case ash::UMA_WINDOW_CLOSE_BUTTON_CLICK:
380      content::RecordAction(content::UserMetricsAction("CloseButton_Clk"));
381      break;
382    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN:
383      content::RecordAction(content::UserMetricsAction("MaxButton_Clk_ExitFS"));
384      break;
385    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE:
386      content::RecordAction(
387          content::UserMetricsAction("MaxButton_Clk_Restore"));
388      break;
389    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE:
390      content::RecordAction(
391          content::UserMetricsAction("MaxButton_Clk_Maximize"));
392      break;
393    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE:
394      content::RecordAction(content::UserMetricsAction("MinButton_Clk"));
395      break;
396    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE:
397      content::RecordAction(content::UserMetricsAction("MaxButton_Maximize"));
398      break;
399    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT:
400      content::RecordAction(content::UserMetricsAction("MaxButton_MaxLeft"));
401      break;
402    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT:
403      content::RecordAction(content::UserMetricsAction("MaxButton_MaxRight"));
404      break;
405    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_MINIMIZE:
406      content::RecordAction(content::UserMetricsAction("MaxButton_Minimize"));
407      break;
408    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_RESTORE:
409      content::RecordAction(content::UserMetricsAction("MaxButton_Restore"));
410      break;
411    case ash::UMA_WINDOW_MAXIMIZE_BUTTON_SHOW_BUBBLE:
412      content::RecordAction(content::UserMetricsAction("MaxButton_ShowBubble"));
413      break;
414  }
415}
416
417string16 ChromeShellDelegate::GetTimeRemainingString(base::TimeDelta delta) {
418  return TimeFormat::TimeRemainingLong(delta);
419}
420
421string16 ChromeShellDelegate::GetTimeDurationLongString(base::TimeDelta delta) {
422  return TimeFormat::TimeDurationLong(delta);
423}
424
425ui::MenuModel* ChromeShellDelegate::CreateContextMenu(aura::RootWindow* root) {
426  DCHECK(launcher_delegate_);
427  // Don't show context menu for exclusive app runtime mode.
428  if (chrome::IsRunningInAppMode())
429    return NULL;
430
431  return new LauncherContextMenu(launcher_delegate_, root);
432}
433
434ash::RootWindowHostFactory* ChromeShellDelegate::CreateRootWindowHostFactory() {
435  return ash::RootWindowHostFactory::Create();
436}
437
438string16 ChromeShellDelegate::GetProductName() const {
439  return l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
440}
441
442Browser* ChromeShellDelegate::GetTargetBrowser() {
443  Browser* browser = chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow());
444  if (browser)
445    return browser;
446  return chrome::FindOrCreateTabbedBrowser(
447      ProfileManager::GetDefaultProfileOrOffTheRecord(),
448      chrome::HOST_DESKTOP_TYPE_ASH);
449}
450
451keyboard::KeyboardControllerProxy*
452    ChromeShellDelegate::CreateKeyboardControllerProxy() {
453  return new AshKeyboardControllerProxy();
454}
455