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