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/chrome_launcher_controller.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/launcher/launcher.h" 12 #include "ash/launcher/launcher_model.h" 13 #include "ash/shell.h" 14 #include "ash/test/launcher_view_test_api.h" 15 #include "ash/test/shell_test_api.h" 16 #include "ash/wm/window_util.h" 17 #include "base/command_line.h" 18 #include "base/strings/stringprintf.h" 19 #include "base/strings/utf_string_conversions.h" 20 #include "chrome/browser/automation/automation_util.h" 21 #include "chrome/browser/chrome_notification_types.h" 22 #include "chrome/browser/extensions/extension_apitest.h" 23 #include "chrome/browser/extensions/extension_browsertest.h" 24 #include "chrome/browser/extensions/extension_function_test_utils.h" 25 #include "chrome/browser/extensions/extension_service.h" 26 #include "chrome/browser/extensions/extension_system.h" 27 #include "chrome/browser/extensions/extension_test_message_listener.h" 28 #include "chrome/browser/extensions/platform_app_browsertest_util.h" 29 #include "chrome/browser/profiles/profile.h" 30 #include "chrome/browser/ui/browser.h" 31 #include "chrome/browser/ui/browser_commands.h" 32 #include "chrome/browser/ui/browser_window.h" 33 #include "chrome/browser/ui/extensions/application_launch.h" 34 #include "chrome/browser/ui/host_desktop.h" 35 #include "chrome/browser/ui/tabs/tab_strip_model.h" 36 #include "chrome/common/chrome_switches.h" 37 #include "chrome/common/extensions/extension_constants.h" 38 #include "chrome/test/base/ui_test_utils.h" 39 #include "content/public/browser/notification_service.h" 40 #include "content/public/browser/notification_source.h" 41 #include "content/public/browser/web_contents.h" 42 #include "testing/gtest/include/gtest/gtest.h" 43 #include "ui/aura/client/aura_constants.h" 44 #include "ui/aura/window.h" 45 46 using apps::ShellWindow; 47 using extensions::Extension; 48 using content::WebContents; 49 50 class LauncherPlatformAppBrowserTest 51 : public extensions::PlatformAppBrowserTest { 52 protected: 53 LauncherPlatformAppBrowserTest() 54 : launcher_(NULL), 55 controller_(NULL) { 56 } 57 58 virtual ~LauncherPlatformAppBrowserTest() {} 59 60 ash::LauncherModel* launcher_model() { 61 return ash::test::ShellTestApi(ash::Shell::GetInstance()).launcher_model(); 62 } 63 64 virtual void RunTestOnMainThreadLoop() OVERRIDE { 65 launcher_ = ash::Launcher::ForPrimaryDisplay(); 66 controller_ = static_cast<ChromeLauncherController*>(launcher_->delegate()); 67 return extensions::PlatformAppBrowserTest::RunTestOnMainThreadLoop(); 68 } 69 70 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 71 PlatformAppBrowserTest::SetUpCommandLine(command_line); 72 command_line->AppendSwitch(ash::switches::kAshDisablePerAppLauncher); 73 } 74 75 ash::LauncherID CreateAppShortcutLauncherItem(const std::string& name) { 76 return controller_->CreateAppShortcutLauncherItem( 77 name, controller_->model()->item_count()); 78 } 79 80 const ash::LauncherItem& GetLastLauncherItem() { 81 // Unless there are any panels, the item at index [count - 1] will be 82 // the app list, and the item at [count - 2] will be the desited item. 83 return launcher_model()->items()[launcher_model()->item_count() - 2]; 84 } 85 86 const ash::LauncherItem& GetLastLauncherPanelItem() { 87 // Panels show up on the right side of the launcher, so the desired item 88 // will be the last one. 89 return launcher_model()->items()[launcher_model()->item_count() - 1]; 90 } 91 92 ash::Launcher* launcher_; 93 ChromeLauncherController* controller_; 94 }; 95 96 class LauncherAppBrowserTest : public ExtensionBrowserTest { 97 protected: 98 LauncherAppBrowserTest() 99 : launcher_(NULL), 100 model_(NULL) { 101 } 102 103 virtual ~LauncherAppBrowserTest() {} 104 105 virtual void RunTestOnMainThreadLoop() OVERRIDE { 106 launcher_ = ash::Launcher::ForPrimaryDisplay(); 107 model_ = 108 ash::test::ShellTestApi(ash::Shell::GetInstance()).launcher_model(); 109 return ExtensionBrowserTest::RunTestOnMainThreadLoop(); 110 } 111 112 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 113 ExtensionBrowserTest::SetUpCommandLine(command_line); 114 command_line->AppendSwitch(ash::switches::kAshDisablePerAppLauncher); 115 } 116 117 const Extension* LoadAndLaunchExtension( 118 const char* name, 119 extension_misc::LaunchContainer container, 120 WindowOpenDisposition disposition) { 121 EXPECT_TRUE(LoadExtension(test_data_dir_.AppendASCII(name))); 122 123 ExtensionService* service = extensions::ExtensionSystem::Get( 124 browser()->profile())->extension_service(); 125 const Extension* extension = 126 service->GetExtensionById(last_loaded_extension_id_, false); 127 EXPECT_TRUE(extension); 128 129 chrome::OpenApplication(chrome::AppLaunchParams( 130 browser()->profile(), extension, container, disposition)); 131 return extension; 132 } 133 134 ash::LauncherID CreateShortcut(const char* name) { 135 ExtensionService* service = extensions::ExtensionSystem::Get( 136 browser()->profile())->extension_service(); 137 LoadExtension(test_data_dir_.AppendASCII(name)); 138 139 // First get app_id. 140 const Extension* extension = 141 service->GetExtensionById(last_loaded_extension_id_, false); 142 const std::string app_id = extension->id(); 143 144 // Then create a shortcut. 145 ChromeLauncherController* controller = 146 static_cast<ChromeLauncherController*>(launcher_->delegate()); 147 int item_count = model_->item_count(); 148 ash::LauncherID shortcut_id = controller->CreateAppShortcutLauncherItem( 149 app_id, item_count); 150 controller->PersistPinnedState(); 151 EXPECT_EQ(++item_count, model_->item_count()); 152 ash::LauncherItem item = *model_->ItemByID(shortcut_id); 153 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type); 154 return item.id; 155 } 156 157 ash::LauncherID PinFakeApp(const std::string& name) { 158 ChromeLauncherController* controller = 159 static_cast<ChromeLauncherController*>(launcher_->delegate()); 160 return controller->CreateAppShortcutLauncherItem( 161 name, model_->item_count()); 162 } 163 164 ash::Launcher* launcher_; 165 ash::LauncherModel* model_; 166 }; 167 168 // Test that we can launch a platform app and get a running item. 169 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, LaunchUnpinned) { 170 int item_count = launcher_model()->item_count(); 171 const Extension* extension = LoadAndLaunchPlatformApp("launch"); 172 ShellWindow* window = CreateShellWindow(extension); 173 ++item_count; 174 ASSERT_EQ(item_count, launcher_model()->item_count()); 175 const ash::LauncherItem& item = GetLastLauncherItem(); 176 EXPECT_EQ(ash::TYPE_PLATFORM_APP, item.type); 177 EXPECT_EQ(ash::STATUS_ACTIVE, item.status); 178 CloseShellWindow(window); 179 --item_count; 180 EXPECT_EQ(item_count, launcher_model()->item_count()); 181 } 182 183 // Test that we can launch a platform app that already has a shortcut. 184 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, LaunchPinned) { 185 int item_count = launcher_model()->item_count(); 186 187 // First get app_id. 188 const Extension* extension = LoadAndLaunchPlatformApp("launch"); 189 const std::string app_id = extension->id(); 190 191 // Then create a shortcut. 192 ash::LauncherID shortcut_id = CreateAppShortcutLauncherItem(app_id); 193 ++item_count; 194 ASSERT_EQ(item_count, launcher_model()->item_count()); 195 ash::LauncherItem item = *launcher_model()->ItemByID(shortcut_id); 196 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type); 197 EXPECT_EQ(ash::STATUS_CLOSED, item.status); 198 199 // Open a window. Confirm the item is now running. 200 ShellWindow* window = CreateShellWindow(extension); 201 ash::wm::ActivateWindow(window->GetNativeWindow()); 202 ASSERT_EQ(item_count, launcher_model()->item_count()); 203 item = *launcher_model()->ItemByID(shortcut_id); 204 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type); 205 EXPECT_EQ(ash::STATUS_ACTIVE, item.status); 206 207 // Then close it, make sure there's still an item. 208 CloseShellWindow(window); 209 ASSERT_EQ(item_count, launcher_model()->item_count()); 210 item = *launcher_model()->ItemByID(shortcut_id); 211 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type); 212 EXPECT_EQ(ash::STATUS_CLOSED, item.status); 213 } 214 215 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, PinRunning) { 216 // Run. 217 int item_count = launcher_model()->item_count(); 218 const Extension* extension = LoadAndLaunchPlatformApp("launch"); 219 ShellWindow* window = CreateShellWindow(extension); 220 ++item_count; 221 ASSERT_EQ(item_count, launcher_model()->item_count()); 222 const ash::LauncherItem& item1 = GetLastLauncherItem(); 223 ash::LauncherID id = item1.id; 224 EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type); 225 EXPECT_EQ(ash::STATUS_ACTIVE, item1.status); 226 227 // Create a shortcut. The app item should be after it. 228 ash::LauncherID foo_id = CreateAppShortcutLauncherItem("foo"); 229 ++item_count; 230 ASSERT_EQ(item_count, launcher_model()->item_count()); 231 EXPECT_LT(launcher_model()->ItemIndexByID(foo_id), 232 launcher_model()->ItemIndexByID(id)); 233 234 // Pin the app. The item should remain. 235 controller_->Pin(id); 236 ASSERT_EQ(item_count, launcher_model()->item_count()); 237 const ash::LauncherItem& item2 = *launcher_model()->ItemByID(id); 238 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item2.type); 239 EXPECT_EQ(ash::STATUS_ACTIVE, item2.status); 240 241 // New shortcuts should come after the item. 242 ash::LauncherID bar_id = CreateAppShortcutLauncherItem("bar"); 243 ++item_count; 244 ASSERT_EQ(item_count, launcher_model()->item_count()); 245 EXPECT_LT(launcher_model()->ItemIndexByID(id), 246 launcher_model()->ItemIndexByID(bar_id)); 247 248 // Then close it, make sure the item remains. 249 CloseShellWindow(window); 250 ASSERT_EQ(item_count, launcher_model()->item_count()); 251 } 252 253 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, UnpinRunning) { 254 int item_count = launcher_model()->item_count(); 255 256 // First get app_id. 257 const Extension* extension = LoadAndLaunchPlatformApp("launch"); 258 const std::string app_id = extension->id(); 259 260 // Then create a shortcut. 261 ash::LauncherID shortcut_id = CreateAppShortcutLauncherItem(app_id); 262 ++item_count; 263 ASSERT_EQ(item_count, launcher_model()->item_count()); 264 ash::LauncherItem item = *launcher_model()->ItemByID(shortcut_id); 265 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type); 266 EXPECT_EQ(ash::STATUS_CLOSED, item.status); 267 268 // Create a second shortcut. This will be needed to force the first one to 269 // move once it gets unpinned. 270 ash::LauncherID foo_id = CreateAppShortcutLauncherItem("foo"); 271 ++item_count; 272 ASSERT_EQ(item_count, launcher_model()->item_count()); 273 EXPECT_LT(launcher_model()->ItemIndexByID(shortcut_id), 274 launcher_model()->ItemIndexByID(foo_id)); 275 276 // Open a window. Confirm the item is now running. 277 ShellWindow* window = CreateShellWindow(extension); 278 ash::wm::ActivateWindow(window->GetNativeWindow()); 279 ASSERT_EQ(item_count, launcher_model()->item_count()); 280 item = *launcher_model()->ItemByID(shortcut_id); 281 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, item.type); 282 EXPECT_EQ(ash::STATUS_ACTIVE, item.status); 283 284 // Unpin the app. The item should remain. 285 controller_->Unpin(shortcut_id); 286 ASSERT_EQ(item_count, launcher_model()->item_count()); 287 item = *launcher_model()->ItemByID(shortcut_id); 288 EXPECT_EQ(ash::TYPE_PLATFORM_APP, item.type); 289 EXPECT_EQ(ash::STATUS_ACTIVE, item.status); 290 // The item should have moved after the other shortcuts. 291 EXPECT_GT(launcher_model()->ItemIndexByID(shortcut_id), 292 launcher_model()->ItemIndexByID(foo_id)); 293 294 // Then close it, make sure the item's gone. 295 CloseShellWindow(window); 296 --item_count; 297 ASSERT_EQ(item_count, launcher_model()->item_count()); 298 } 299 300 // Test that we can launch a platform app with more than one window. 301 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, MultipleWindows) { 302 int item_count = launcher_model()->item_count(); 303 304 // First run app. 305 const Extension* extension = LoadAndLaunchPlatformApp("launch"); 306 ShellWindow* window1 = CreateShellWindow(extension); 307 ++item_count; 308 ASSERT_EQ(item_count, launcher_model()->item_count()); 309 const ash::LauncherItem& item1 = GetLastLauncherItem(); 310 ash::LauncherID item_id = item1.id; 311 EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type); 312 EXPECT_EQ(ash::STATUS_ACTIVE, item1.status); 313 314 // Add second window. 315 ShellWindow* window2 = CreateShellWindow(extension); 316 // Confirm item stays. 317 ASSERT_EQ(item_count, launcher_model()->item_count()); 318 const ash::LauncherItem& item2 = *launcher_model()->ItemByID(item_id); 319 EXPECT_EQ(ash::STATUS_ACTIVE, item2.status); 320 321 // Close second window. 322 CloseShellWindow(window2); 323 // Confirm item stays. 324 ASSERT_EQ(item_count, launcher_model()->item_count()); 325 const ash::LauncherItem& item3 = *launcher_model()->ItemByID(item_id); 326 EXPECT_EQ(ash::STATUS_ACTIVE, item3.status); 327 328 // Close first window. 329 CloseShellWindow(window1); 330 // Confirm item is removed. 331 --item_count; 332 ASSERT_EQ(item_count, launcher_model()->item_count()); 333 } 334 335 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, MultipleApps) { 336 int item_count = launcher_model()->item_count(); 337 338 // First run app. 339 const Extension* extension1 = LoadAndLaunchPlatformApp("launch"); 340 ShellWindow* window1 = CreateShellWindow(extension1); 341 ++item_count; 342 ASSERT_EQ(item_count, launcher_model()->item_count()); 343 const ash::LauncherItem& item1 = GetLastLauncherItem(); 344 ash::LauncherID item_id1 = item1.id; 345 EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type); 346 EXPECT_EQ(ash::STATUS_ACTIVE, item1.status); 347 348 // Then run second app. 349 const Extension* extension2 = LoadAndLaunchPlatformApp("launch_2"); 350 ShellWindow* window2 = CreateShellWindow(extension2); 351 ++item_count; 352 ASSERT_EQ(item_count, launcher_model()->item_count()); 353 const ash::LauncherItem& item2 = GetLastLauncherItem(); 354 ash::LauncherID item_id2 = item2.id; 355 EXPECT_EQ(ash::TYPE_PLATFORM_APP, item2.type); 356 EXPECT_EQ(ash::STATUS_ACTIVE, item2.status); 357 358 EXPECT_NE(item_id1, item_id2); 359 EXPECT_EQ(ash::STATUS_RUNNING, 360 launcher_model()->ItemByID(item_id1)->status); 361 362 // Close second app. 363 CloseShellWindow(window2); 364 --item_count; 365 ASSERT_EQ(item_count, launcher_model()->item_count()); 366 // First app should be active again. 367 EXPECT_EQ(ash::STATUS_ACTIVE, 368 launcher_model()->ItemByID(item_id1)->status); 369 370 // Close first app. 371 CloseShellWindow(window1); 372 --item_count; 373 ASSERT_EQ(item_count, launcher_model()->item_count()); 374 } 375 376 // Test that we can launch a platform app panel and get a running item. 377 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, LaunchPanelWindow) { 378 int item_count = launcher_model()->item_count(); 379 const Extension* extension = LoadAndLaunchPlatformApp("launch"); 380 ShellWindow::CreateParams params; 381 params.window_type = ShellWindow::WINDOW_TYPE_PANEL; 382 params.focused = false; 383 ShellWindow* window = CreateShellWindowFromParams(extension, params); 384 ++item_count; 385 ASSERT_EQ(item_count, launcher_model()->item_count()); 386 const ash::LauncherItem& item = GetLastLauncherPanelItem(); 387 EXPECT_EQ(ash::TYPE_APP_PANEL, item.type); 388 // Opening a panel does not activate it. 389 EXPECT_EQ(ash::STATUS_RUNNING, item.status); 390 CloseShellWindow(window); 391 --item_count; 392 EXPECT_EQ(item_count, launcher_model()->item_count()); 393 } 394 395 #if defined(OS_CHROMEOS) 396 #define MAYBE_WindowActivation DISABLED_WindowActivation 397 #else 398 #define MAYBE_WindowActivation WindowActivation 399 #endif 400 // Confirm that app windows can be reactivated by clicking their icons and that 401 // the correct activation order is maintained. 402 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, MAYBE_WindowActivation) { 403 int item_count = launcher_model()->item_count(); 404 405 // First run app. 406 const Extension* extension1 = LoadAndLaunchPlatformApp("launch"); 407 ShellWindow* window1 = CreateShellWindow(extension1); 408 ++item_count; 409 ASSERT_EQ(item_count, launcher_model()->item_count()); 410 const ash::LauncherItem& item1 = GetLastLauncherItem(); 411 ash::LauncherID item_id1 = item1.id; 412 EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type); 413 EXPECT_EQ(ash::STATUS_ACTIVE, item1.status); 414 415 // Then run second app. 416 const Extension* extension2 = LoadAndLaunchPlatformApp("launch_2"); 417 ShellWindow* window2 = CreateShellWindow(extension2); 418 ++item_count; 419 ASSERT_EQ(item_count, launcher_model()->item_count()); 420 const ash::LauncherItem& item2 = GetLastLauncherItem(); 421 ash::LauncherID item_id2 = item2.id; 422 EXPECT_EQ(ash::TYPE_PLATFORM_APP, item2.type); 423 EXPECT_EQ(ash::STATUS_ACTIVE, item2.status); 424 425 EXPECT_NE(item_id1, item_id2); 426 EXPECT_EQ(ash::STATUS_RUNNING, 427 launcher_model()->ItemByID(item_id1)->status); 428 429 // Activate first one. 430 launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1)); 431 EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id1)->status); 432 EXPECT_EQ(ash::STATUS_RUNNING, 433 launcher_model()->ItemByID(item_id2)->status); 434 EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow())); 435 EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow())); 436 437 // Activate second one. 438 launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2)); 439 EXPECT_EQ(ash::STATUS_RUNNING, 440 launcher_model()->ItemByID(item_id1)->status); 441 EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id2)->status); 442 EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow())); 443 EXPECT_TRUE(ash::wm::IsActiveWindow(window2->GetNativeWindow())); 444 445 // Add window for app1. This will activate it. 446 ShellWindow* window1b = CreateShellWindow(extension1); 447 ash::wm::ActivateWindow(window1b->GetNativeWindow()); 448 EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow())); 449 EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow())); 450 EXPECT_TRUE(ash::wm::IsActiveWindow(window1b->GetNativeWindow())); 451 452 // Activate launcher item for app1, this will activate the first app window. 453 launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1)); 454 EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow())); 455 EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow())); 456 launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1)); 457 EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow())); 458 459 // Activate the second app again 460 launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2)); 461 EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow())); 462 EXPECT_TRUE(ash::wm::IsActiveWindow(window2->GetNativeWindow())); 463 EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow())); 464 465 // Activate the first app again 466 launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1)); 467 EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow())); 468 EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow())); 469 EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow())); 470 471 // Close second app. 472 CloseShellWindow(window2); 473 --item_count; 474 EXPECT_EQ(item_count, launcher_model()->item_count()); 475 // First app should be active again. 476 EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id1)->status); 477 478 // Close first app. 479 CloseShellWindow(window1b); 480 CloseShellWindow(window1); 481 --item_count; 482 EXPECT_EQ(item_count, launcher_model()->item_count()); 483 } 484 485 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, BrowserActivation) { 486 int item_count = launcher_model()->item_count(); 487 488 // First run app. 489 const Extension* extension1 = LoadAndLaunchPlatformApp("launch"); 490 CreateShellWindow(extension1); 491 ++item_count; 492 ASSERT_EQ(item_count, launcher_model()->item_count()); 493 const ash::LauncherItem& item1 = GetLastLauncherItem(); 494 ash::LauncherID item_id1 = item1.id; 495 EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type); 496 EXPECT_EQ(ash::STATUS_ACTIVE, item1.status); 497 498 ash::wm::ActivateWindow(browser()->window()->GetNativeWindow()); 499 EXPECT_EQ(ash::STATUS_RUNNING, 500 launcher_model()->ItemByID(item_id1)->status); 501 } 502 503 // Test that draw attention sets the launcher item status. 504 IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, DrawAttention) { 505 const Extension* extension = LoadAndLaunchPlatformApp("launch"); 506 ShellWindow* shell_window = CreateShellWindow(extension); 507 const ash::LauncherItem& item = GetLastLauncherItem(); 508 EXPECT_EQ(ash::STATUS_ACTIVE, item.status); 509 // Set Minimize window to deactivate the launcher item. 510 shell_window->GetBaseWindow()->Minimize(); 511 EXPECT_EQ(ash::STATUS_RUNNING, item.status); 512 // Set DrawAttention property. 513 shell_window->GetNativeWindow()->SetProperty( 514 aura::client::kDrawAttentionKey, true); 515 EXPECT_EQ(ash::STATUS_ATTENTION, item.status); 516 // Activate window, should clear DrawAttention. 517 shell_window->GetBaseWindow()->Activate(); 518 EXPECT_EQ(ash::STATUS_ACTIVE, item.status); 519 EXPECT_FALSE(shell_window->GetNativeWindow()->GetProperty( 520 aura::client::kDrawAttentionKey)); 521 } 522 523 // Test that we can launch an app with a shortcut. 524 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchPinned) { 525 TabStripModel* tab_strip = browser()->tab_strip_model(); 526 int tab_count = tab_strip->count(); 527 ash::LauncherID shortcut_id = CreateShortcut("app1"); 528 EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status); 529 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id)); 530 EXPECT_EQ(++tab_count, tab_strip->count()); 531 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status); 532 WebContents* tab = tab_strip->GetActiveWebContents(); 533 content::WindowedNotificationObserver close_observer( 534 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 535 content::Source<WebContents>(tab)); 536 browser()->tab_strip_model()->CloseSelectedTabs(); 537 close_observer.Wait(); 538 EXPECT_EQ(--tab_count, tab_strip->count()); 539 EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status); 540 } 541 542 // Launch the app first and then create the shortcut. 543 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchUnpinned) { 544 TabStripModel* tab_strip = browser()->tab_strip_model(); 545 int tab_count = tab_strip->count(); 546 LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, 547 NEW_FOREGROUND_TAB); 548 EXPECT_EQ(++tab_count, tab_strip->count()); 549 ash::LauncherID shortcut_id = CreateShortcut("app1"); 550 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status); 551 WebContents* tab = tab_strip->GetActiveWebContents(); 552 content::WindowedNotificationObserver close_observer( 553 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 554 content::Source<WebContents>(tab)); 555 browser()->tab_strip_model()->CloseSelectedTabs(); 556 close_observer.Wait(); 557 EXPECT_EQ(--tab_count, tab_strip->count()); 558 EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status); 559 } 560 561 // Launches an app in the background and then tries to open it. This is test for 562 // a crash we had. 563 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchInBackground) { 564 TabStripModel* tab_strip = browser()->tab_strip_model(); 565 int tab_count = tab_strip->count(); 566 LoadAndLaunchExtension("app1", extension_misc::LAUNCH_TAB, 567 NEW_BACKGROUND_TAB); 568 EXPECT_EQ(++tab_count, tab_strip->count()); 569 ChromeLauncherController::instance()->LaunchApp(last_loaded_extension_id_, 0); 570 } 571 572 // Confirm that clicking a icon for an app running in one of 2 maxmized windows 573 // activates the right window. 574 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchMaximized) { 575 aura::Window* window1 = browser()->window()->GetNativeWindow(); 576 ash::wm::MaximizeWindow(window1); 577 content::WindowedNotificationObserver open_observer( 578 chrome::NOTIFICATION_BROWSER_WINDOW_READY, 579 content::NotificationService::AllSources()); 580 chrome::NewEmptyWindow(browser()->profile(), chrome::HOST_DESKTOP_TYPE_ASH); 581 open_observer.Wait(); 582 Browser* browser2 = content::Source<Browser>(open_observer.source()).ptr(); 583 aura::Window* window2 = browser2->window()->GetNativeWindow(); 584 TabStripModel* tab_strip = browser2->tab_strip_model(); 585 int tab_count = tab_strip->count(); 586 ash::wm::MaximizeWindow(window2); 587 588 ash::LauncherID shortcut_id = CreateShortcut("app1"); 589 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id)); 590 EXPECT_EQ(++tab_count, tab_strip->count()); 591 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status); 592 593 window1->Show(); 594 ash::wm::ActivateWindow(window1); 595 EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut_id)).status); 596 597 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id)); 598 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status); 599 } 600 601 // Activating the same app multiple times should launch only a single copy. 602 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, ActivateApp) { 603 TabStripModel* tab_strip = browser()->tab_strip_model(); 604 int tab_count = tab_strip->count(); 605 const Extension* extension = 606 LoadExtension(test_data_dir_.AppendASCII("app1")); 607 608 ChromeLauncherController::instance()->ActivateApp(extension->id(), 0); 609 EXPECT_EQ(++tab_count, tab_strip->count()); 610 ChromeLauncherController::instance()->ActivateApp(extension->id(), 0); 611 EXPECT_EQ(tab_count, tab_strip->count()); 612 } 613 614 // Launching the same app multiple times should launch a copy for each call. 615 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, LaunchApp) { 616 TabStripModel* tab_strip = browser()->tab_strip_model(); 617 int tab_count = tab_strip->count(); 618 const Extension* extension = 619 LoadExtension(test_data_dir_.AppendASCII("app1")); 620 621 ChromeLauncherController::instance()->LaunchApp(extension->id(), 0); 622 EXPECT_EQ(++tab_count, tab_strip->count()); 623 ChromeLauncherController::instance()->LaunchApp(extension->id(), 0); 624 EXPECT_EQ(++tab_count, tab_strip->count()); 625 } 626 627 // Launch 2 apps and toggle which is active. 628 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MultipleApps) { 629 int item_count = model_->item_count(); 630 TabStripModel* tab_strip = browser()->tab_strip_model(); 631 int tab_count = tab_strip->count(); 632 ash::LauncherID shortcut1 = CreateShortcut("app1"); 633 EXPECT_EQ(++item_count, model_->item_count()); 634 ash::LauncherID shortcut2 = CreateShortcut("app2"); 635 EXPECT_EQ(++item_count, model_->item_count()); 636 637 // Launch first app. 638 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut1)); 639 EXPECT_EQ(++tab_count, tab_strip->count()); 640 WebContents* tab1 = tab_strip->GetActiveWebContents(); 641 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status); 642 643 // Launch second app. 644 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut2)); 645 EXPECT_EQ(++tab_count, tab_strip->count()); 646 WebContents* tab2 = tab_strip->GetActiveWebContents(); 647 ASSERT_NE(tab1, tab2); 648 EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut1)).status); 649 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status); 650 651 // Reactivate first app. 652 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut1)); 653 EXPECT_EQ(tab_count, tab_strip->count()); 654 EXPECT_EQ(tab_strip->GetActiveWebContents(), tab1); 655 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status); 656 EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut2)).status); 657 658 // Open second tab for second app. This should activate it. 659 ui_test_utils::NavigateToURLWithDisposition( 660 browser(), 661 GURL("http://www.example.com/path3/foo.html"), 662 NEW_FOREGROUND_TAB, 663 0); 664 EXPECT_EQ(++tab_count, tab_strip->count()); 665 WebContents* tab3 = tab_strip->GetActiveWebContents(); 666 EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut1)).status); 667 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status); 668 669 // Reactivate first app. 670 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut1)); 671 EXPECT_EQ(tab_count, tab_strip->count()); 672 EXPECT_EQ(tab_strip->GetActiveWebContents(), tab1); 673 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status); 674 EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut2)).status); 675 676 // And second again. This time the second tab should become active. 677 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut2)); 678 EXPECT_EQ(tab_count, tab_strip->count()); 679 EXPECT_EQ(tab_strip->GetActiveWebContents(), tab3); 680 EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut1)).status); 681 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status); 682 } 683 684 // Confirm that a page can be navigated from and to while maintaining the 685 // correct running state. 686 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, Navigation) { 687 ash::LauncherID shortcut_id = CreateShortcut("app1"); 688 EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status); 689 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id)); 690 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status); 691 692 // Navigate away. 693 ui_test_utils::NavigateToURL( 694 browser(), GURL("http://www.example.com/path0/bar.html")); 695 EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status); 696 697 // Navigate back. 698 ui_test_utils::NavigateToURL( 699 browser(), GURL("http://www.example.com/path1/foo.html")); 700 EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status); 701 } 702 703 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, MultipleOwnedTabs) { 704 TabStripModel* tab_strip = browser()->tab_strip_model(); 705 int tab_count = tab_strip->count(); 706 ash::LauncherID shortcut_id = CreateShortcut("app1"); 707 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id)); 708 EXPECT_EQ(++tab_count, tab_strip->count()); 709 EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status); 710 711 // Create new tab owned by app. 712 ui_test_utils::NavigateToURLWithDisposition( 713 browser(), 714 GURL("http://www.example.com/path2/bar.html"), 715 NEW_FOREGROUND_TAB, 716 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 717 EXPECT_EQ(++tab_count, tab_strip->count()); 718 // Confirm app is still active. 719 EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status); 720 WebContents* second_tab = tab_strip->GetActiveWebContents(); 721 722 // Create new tab not owned by app. 723 ui_test_utils::NavigateToURLWithDisposition( 724 browser(), 725 GURL("http://www.example.com/path3/foo.html"), 726 NEW_FOREGROUND_TAB, 727 0); 728 EXPECT_EQ(++tab_count, tab_strip->count()); 729 // No longer active. 730 EXPECT_EQ(ash::STATUS_RUNNING, model_->ItemByID(shortcut_id)->status); 731 732 // Activating app makes second tab active again. 733 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id)); 734 EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status); 735 EXPECT_EQ(tab_strip->GetActiveWebContents(), second_tab); 736 } 737 738 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, RefocusFilter) { 739 ChromeLauncherController* controller = 740 static_cast<ChromeLauncherController*>(launcher_->delegate()); 741 TabStripModel* tab_strip = browser()->tab_strip_model(); 742 int tab_count = tab_strip->count(); 743 ash::LauncherID shortcut_id = CreateShortcut("app1"); 744 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id)); 745 EXPECT_EQ(++tab_count, tab_strip->count()); 746 EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status); 747 WebContents* first_tab = tab_strip->GetActiveWebContents(); 748 749 controller->SetRefocusURLPatternForTest( 750 shortcut_id, GURL("http://www.example.com/path1/*")); 751 // Create new tab owned by app. 752 ui_test_utils::NavigateToURLWithDisposition( 753 browser(), 754 GURL("http://www.example.com/path2/bar.html"), 755 NEW_FOREGROUND_TAB, 756 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 757 EXPECT_EQ(++tab_count, tab_strip->count()); 758 // Confirm app is still active. 759 EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status); 760 761 // Create new tab not owned by app. 762 ui_test_utils::NavigateToURLWithDisposition( 763 browser(), 764 GURL("http://www.example.com/path3/foo.html"), 765 NEW_FOREGROUND_TAB, 766 0); 767 EXPECT_EQ(++tab_count, tab_strip->count()); 768 // No longer active. 769 EXPECT_EQ(ash::STATUS_RUNNING, model_->ItemByID(shortcut_id)->status); 770 771 // Activating app makes first tab active again, because second tab isn't 772 // in its refocus url path. 773 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id)); 774 EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status); 775 EXPECT_EQ(tab_strip->GetActiveWebContents(), first_tab); 776 } 777 778 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, RefocusFilterLaunch) { 779 ChromeLauncherController* controller = 780 static_cast<ChromeLauncherController*>(launcher_->delegate()); 781 TabStripModel* tab_strip = browser()->tab_strip_model(); 782 int tab_count = tab_strip->count(); 783 ash::LauncherID shortcut_id = CreateShortcut("app1"); 784 controller->SetRefocusURLPatternForTest( 785 shortcut_id, GURL("http://www.example.com/path1/*")); 786 787 // Create new tab owned by app. 788 ui_test_utils::NavigateToURLWithDisposition( 789 browser(), 790 GURL("http://www.example.com/path2/bar.html"), 791 NEW_FOREGROUND_TAB, 792 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 793 EXPECT_EQ(++tab_count, tab_strip->count()); 794 WebContents* first_tab = tab_strip->GetActiveWebContents(); 795 // Confirm app is active. 796 EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status); 797 798 // Activating app should launch new tab, because second tab isn't 799 // in its refocus url path. 800 launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id)); 801 EXPECT_EQ(++tab_count, tab_strip->count()); 802 WebContents* second_tab = tab_strip->GetActiveWebContents(); 803 EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status); 804 EXPECT_NE(first_tab, second_tab); 805 EXPECT_EQ(tab_strip->GetActiveWebContents(), second_tab); 806 } 807 808 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, OverflowBubble) { 809 // Make sure to have a browser window 810 chrome::NewTab(browser()); 811 812 // No overflow yet. 813 EXPECT_FALSE(launcher_->IsShowingOverflowBubble()); 814 815 ash::test::LauncherViewTestAPI test(launcher_->GetLauncherViewForTest()); 816 817 int items_added = 0; 818 while (!test.IsOverflowButtonVisible()) { 819 std::string fake_app_id = base::StringPrintf("fake_app_%d", items_added); 820 PinFakeApp(fake_app_id); 821 822 ++items_added; 823 ASSERT_LT(items_added, 10000); 824 } 825 826 // Now show overflow bubble. 827 test.ShowOverflowBubble(); 828 EXPECT_TRUE(launcher_->IsShowingOverflowBubble()); 829 830 // Unpin first pinned app and there should be no crash. 831 ChromeLauncherController* controller = 832 static_cast<ChromeLauncherController*>(launcher_->delegate()); 833 controller->UnpinAppsWithID(std::string("fake_app_0")); 834 835 test.RunMessageLoopUntilAnimationsDone(); 836 EXPECT_FALSE(launcher_->IsShowingOverflowBubble()); 837 } 838