Home | History | Annotate | Download | only in launcher
      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