1 // Copyright 2013 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 <algorithm> 8 #include <string> 9 #include <vector> 10 11 #include "ash/ash_switches.h" 12 #include "ash/shelf/shelf_item_delegate_manager.h" 13 #include "ash/shelf/shelf_model.h" 14 #include "ash/shelf/shelf_model_observer.h" 15 #include "ash/shell.h" 16 #include "ash/test/shelf_item_delegate_manager_test_api.h" 17 #include "base/command_line.h" 18 #include "base/compiler_specific.h" 19 #include "base/files/file_path.h" 20 #include "base/memory/scoped_ptr.h" 21 #include "base/message_loop/message_loop.h" 22 #include "base/strings/utf_string_conversions.h" 23 #include "base/values.h" 24 #include "chrome/browser/extensions/extension_service.h" 25 #include "chrome/browser/extensions/test_extension_system.h" 26 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h" 27 #include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h" 28 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h" 29 #include "chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.h" 30 #include "chrome/browser/ui/browser.h" 31 #include "chrome/browser/ui/browser_commands.h" 32 #include "chrome/browser/ui/browser_finder.h" 33 #include "chrome/browser/ui/browser_list.h" 34 #include "chrome/browser/ui/browser_tabstrip.h" 35 #include "chrome/browser/ui/host_desktop.h" 36 #include "chrome/browser/ui/tabs/tab_strip_model.h" 37 #include "chrome/common/extensions/extension_constants.h" 38 #include "chrome/common/pref_names.h" 39 #include "chrome/test/base/browser_with_test_window_test.h" 40 #include "chrome/test/base/testing_pref_service_syncable.h" 41 #include "chrome/test/base/testing_profile.h" 42 #include "content/public/browser/web_contents.h" 43 #include "content/public/test/test_browser_thread.h" 44 #include "extensions/common/extension.h" 45 #include "extensions/common/manifest_constants.h" 46 #include "testing/gtest/include/gtest/gtest.h" 47 #include "ui/base/models/menu_model.h" 48 49 #if defined(OS_CHROMEOS) 50 #include "apps/app_window_contents.h" 51 #include "apps/shell_window_registry.h" 52 #include "apps/ui/native_app_window.h" 53 #include "ash/test/test_session_state_delegate.h" 54 #include "ash/test/test_shell_delegate.h" 55 #include "chrome/browser/chromeos/login/fake_user_manager.h" 56 #include "chrome/browser/ui/apps/chrome_shell_window_delegate.h" 57 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h" 58 #include "chrome/browser/ui/ash/launcher/shell_window_launcher_controller.h" 59 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" 60 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h" 61 #include "chrome/common/chrome_constants.h" 62 #include "chrome/common/chrome_switches.h" 63 #include "chrome/test/base/testing_browser_process.h" 64 #include "chrome/test/base/testing_profile_manager.h" 65 #include "content/public/browser/web_contents_observer.h" 66 #include "content/public/test/test_utils.h" 67 #include "ui/aura/window.h" 68 #include "ui/views/test/test_views_delegate.h" 69 #endif 70 71 using extensions::Extension; 72 using extensions::Manifest; 73 using extensions::UnloadedExtensionInfo; 74 75 namespace { 76 const char* offline_gmail_url = "https://mail.google.com/mail/mu/u"; 77 const char* gmail_url = "https://mail.google.com/mail/u"; 78 const char* kGmailLaunchURL = "https://mail.google.com/mail/ca"; 79 80 // As defined in /chromeos/dbus/cryptohome_client.cc. 81 const char kUserIdHashSuffix[] = "-hash"; 82 83 // An extension prefix. 84 const char kCrxAppPrefix[] = "_crx_"; 85 86 // ShelfModelObserver implementation that tracks what messages are invoked. 87 class TestShelfModelObserver : public ash::ShelfModelObserver { 88 public: 89 TestShelfModelObserver() 90 : added_(0), 91 removed_(0), 92 changed_(0) { 93 } 94 95 virtual ~TestShelfModelObserver() { 96 } 97 98 // Overridden from ash::ShelfModelObserver: 99 virtual void ShelfItemAdded(int index) OVERRIDE { 100 ++added_; 101 last_index_ = index; 102 } 103 104 virtual void ShelfItemRemoved(int index, ash::LauncherID id) OVERRIDE { 105 ++removed_; 106 last_index_ = index; 107 } 108 109 virtual void ShelfItemChanged(int index, 110 const ash::LauncherItem& old_item) OVERRIDE { 111 ++changed_; 112 last_index_ = index; 113 } 114 115 virtual void ShelfItemMoved(int start_index, int target_index) OVERRIDE { 116 last_index_ = target_index; 117 } 118 119 virtual void ShelfStatusChanged() OVERRIDE { 120 } 121 122 void clear_counts() { 123 added_ = 0; 124 removed_ = 0; 125 changed_ = 0; 126 last_index_ = 0; 127 } 128 129 int added() const { return added_; } 130 int removed() const { return removed_; } 131 int changed() const { return changed_; } 132 int last_index() const { return last_index_; } 133 134 private: 135 int added_; 136 int removed_; 137 int changed_; 138 int last_index_; 139 140 DISALLOW_COPY_AND_ASSIGN(TestShelfModelObserver); 141 }; 142 143 // Test implementation of AppIconLoader. 144 class TestAppIconLoaderImpl : public extensions::AppIconLoader { 145 public: 146 TestAppIconLoaderImpl() : fetch_count_(0) { 147 } 148 149 virtual ~TestAppIconLoaderImpl() { 150 } 151 152 // AppIconLoader implementation: 153 virtual void FetchImage(const std::string& id) OVERRIDE { 154 ++fetch_count_; 155 } 156 157 virtual void ClearImage(const std::string& id) OVERRIDE { 158 } 159 160 virtual void UpdateImage(const std::string& id) OVERRIDE { 161 } 162 163 int fetch_count() const { return fetch_count_; } 164 165 private: 166 int fetch_count_; 167 168 DISALLOW_COPY_AND_ASSIGN(TestAppIconLoaderImpl); 169 }; 170 171 // Test implementation of AppTabHelper. 172 class TestAppTabHelperImpl : public ChromeLauncherController::AppTabHelper { 173 public: 174 TestAppTabHelperImpl() {} 175 virtual ~TestAppTabHelperImpl() {} 176 177 // Sets the id for the specified tab. The id is removed if Remove() is 178 // invoked. 179 void SetAppID(content::WebContents* tab, const std::string& id) { 180 tab_id_map_[tab] = id; 181 } 182 183 // Returns true if there is an id registered for |tab|. 184 bool HasAppID(content::WebContents* tab) const { 185 return tab_id_map_.find(tab) != tab_id_map_.end(); 186 } 187 188 // AppTabHelper implementation: 189 virtual std::string GetAppID(content::WebContents* tab) OVERRIDE { 190 return tab_id_map_.find(tab) != tab_id_map_.end() ? tab_id_map_[tab] : 191 std::string(); 192 } 193 194 virtual bool IsValidIDForCurrentUser(const std::string& id) OVERRIDE { 195 for (TabToStringMap::const_iterator i = tab_id_map_.begin(); 196 i != tab_id_map_.end(); ++i) { 197 if (i->second == id) 198 return true; 199 } 200 return false; 201 } 202 203 virtual void SetCurrentUser(Profile* profile) OVERRIDE { 204 // We can ignore this for now. 205 } 206 207 private: 208 typedef std::map<content::WebContents*, std::string> TabToStringMap; 209 210 TabToStringMap tab_id_map_; 211 212 DISALLOW_COPY_AND_ASSIGN(TestAppTabHelperImpl); 213 }; 214 215 // Test implementation of a V2 app launcher item controller. 216 class TestV2AppLauncherItemController : public LauncherItemController { 217 public: 218 TestV2AppLauncherItemController(const std::string& app_id, 219 ChromeLauncherController* controller) 220 : LauncherItemController(LauncherItemController::TYPE_APP, 221 app_id, 222 controller) { 223 } 224 225 virtual ~TestV2AppLauncherItemController() {} 226 227 // Override for LauncherItemController: 228 virtual bool IsOpen() const OVERRIDE { return true; } 229 virtual bool IsVisible() const OVERRIDE { return true; } 230 virtual void Launch(ash::LaunchSource source, int event_flags) OVERRIDE {} 231 virtual bool Activate(ash::LaunchSource source) OVERRIDE { return false; } 232 virtual void Close() OVERRIDE {} 233 virtual bool ItemSelected(const ui::Event& event) OVERRIDE { return false; } 234 virtual base::string16 GetTitle() OVERRIDE { return base::string16(); } 235 virtual ChromeLauncherAppMenuItems GetApplicationList( 236 int event_flags) OVERRIDE { 237 ChromeLauncherAppMenuItems items; 238 items.push_back( 239 new ChromeLauncherAppMenuItem(base::string16(), NULL, false)); 240 items.push_back( 241 new ChromeLauncherAppMenuItem(base::string16(), NULL, false)); 242 return items.Pass(); 243 } 244 virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE { 245 return NULL; 246 } 247 virtual ash::ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE { 248 return NULL; 249 } 250 virtual bool IsDraggable() OVERRIDE { return false; } 251 virtual bool ShouldShowTooltip() OVERRIDE { return false; } 252 253 private: 254 255 DISALLOW_COPY_AND_ASSIGN(TestV2AppLauncherItemController); 256 }; 257 258 } // namespace 259 260 class ChromeLauncherControllerTest : public BrowserWithTestWindowTest { 261 protected: 262 ChromeLauncherControllerTest() : test_controller_(NULL), 263 extension_service_(NULL) { 264 SetHostDesktopType(chrome::HOST_DESKTOP_TYPE_ASH); 265 } 266 267 virtual ~ChromeLauncherControllerTest() { 268 } 269 270 virtual void SetUp() OVERRIDE { 271 BrowserWithTestWindowTest::SetUp(); 272 273 model_.reset(new ash::ShelfModel); 274 model_observer_.reset(new TestShelfModelObserver); 275 model_->AddObserver(model_observer_.get()); 276 277 if (ash::Shell::HasInstance()) { 278 item_delegate_manager_ = 279 ash::Shell::GetInstance()->shelf_item_delegate_manager(); 280 } else { 281 item_delegate_manager_ = 282 new ash::ShelfItemDelegateManager(model_.get()); 283 } 284 285 DictionaryValue manifest; 286 manifest.SetString(extensions::manifest_keys::kName, 287 "launcher controller test extension"); 288 manifest.SetString(extensions::manifest_keys::kVersion, "1"); 289 manifest.SetString(extensions::manifest_keys::kDescription, 290 "for testing pinned apps"); 291 292 extensions::TestExtensionSystem* extension_system( 293 static_cast<extensions::TestExtensionSystem*>( 294 extensions::ExtensionSystem::Get(profile()))); 295 extension_service_ = extension_system->CreateExtensionService( 296 CommandLine::ForCurrentProcess(), base::FilePath(), false); 297 298 std::string error; 299 extension1_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 300 manifest, 301 Extension::NO_FLAGS, 302 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 303 &error); 304 extension2_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 305 manifest, 306 Extension::NO_FLAGS, 307 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 308 &error); 309 // Fake gmail extension. 310 DictionaryValue manifest_gmail; 311 manifest_gmail.SetString(extensions::manifest_keys::kName, 312 "Gmail launcher controller test extension"); 313 manifest_gmail.SetString(extensions::manifest_keys::kVersion, "1"); 314 manifest_gmail.SetString(extensions::manifest_keys::kDescription, 315 "for testing pinned Gmail"); 316 manifest_gmail.SetString(extensions::manifest_keys::kLaunchWebURL, 317 kGmailLaunchURL); 318 ListValue* list = new ListValue(); 319 list->Append(Value::CreateStringValue("*://mail.google.com/mail/ca")); 320 manifest_gmail.Set(extensions::manifest_keys::kWebURLs, list); 321 322 extension3_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 323 manifest_gmail, 324 Extension::NO_FLAGS, 325 extension_misc::kGmailAppId, 326 &error); 327 328 // Fake search extension. 329 extension4_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 330 manifest, 331 Extension::NO_FLAGS, 332 extension_misc::kGoogleSearchAppId, 333 &error); 334 extension5_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 335 manifest, 336 Extension::NO_FLAGS, 337 "cccccccccccccccccccccccccccccccc", 338 &error); 339 extension6_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 340 manifest, 341 Extension::NO_FLAGS, 342 "dddddddddddddddddddddddddddddddd", 343 &error); 344 extension7_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 345 manifest, 346 Extension::NO_FLAGS, 347 "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 348 &error); 349 extension8_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 350 manifest, 351 Extension::NO_FLAGS, 352 "ffffffffffffffffffffffffffffffff", 353 &error); 354 } 355 356 // Creates a running V2 app (not pinned) of type |app_id|. 357 virtual void CreateRunningV2App(const std::string& app_id) { 358 DCHECK(!test_controller_); 359 ash::LauncherID id = 360 launcher_controller_->CreateAppShortcutLauncherItemWithType( 361 app_id, 362 model_->item_count(), 363 ash::TYPE_PLATFORM_APP); 364 DCHECK(id); 365 // Change the created launcher controller into a V2 app controller. 366 test_controller_ = new TestV2AppLauncherItemController(app_id, 367 launcher_controller_.get()); 368 launcher_controller_->SetItemController(id, test_controller_); 369 } 370 371 // Sets the stage for a multi user test. 372 virtual void SetUpMultiUserScenario(base::ListValue* user_a, 373 base::ListValue* user_b) { 374 InitLauncherController(); 375 EXPECT_EQ("AppList, Chrome, ", GetPinnedAppStatus()); 376 377 // Set an empty pinned pref to begin with. 378 base::ListValue no_user; 379 SetShelfChromeIconIndex(0); 380 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 381 no_user.DeepCopy()); 382 EXPECT_EQ("AppList, Chrome, ", GetPinnedAppStatus()); 383 384 // Assume all applications have been added already. 385 extension_service_->AddExtension(extension1_.get()); 386 extension_service_->AddExtension(extension2_.get()); 387 extension_service_->AddExtension(extension3_.get()); 388 extension_service_->AddExtension(extension4_.get()); 389 extension_service_->AddExtension(extension5_.get()); 390 extension_service_->AddExtension(extension6_.get()); 391 extension_service_->AddExtension(extension7_.get()); 392 extension_service_->AddExtension(extension8_.get()); 393 // There should be nothing in the list by now. 394 EXPECT_EQ("AppList, Chrome, ", GetPinnedAppStatus()); 395 396 // Set user a preferences. 397 InsertPrefValue(user_a, 0, extension1_->id()); 398 InsertPrefValue(user_a, 1, extension2_->id()); 399 InsertPrefValue(user_a, 2, extension3_->id()); 400 InsertPrefValue(user_a, 3, extension4_->id()); 401 InsertPrefValue(user_a, 4, extension5_->id()); 402 InsertPrefValue(user_a, 5, extension6_->id()); 403 404 // Set user b preferences. 405 InsertPrefValue(user_b, 0, extension7_->id()); 406 InsertPrefValue(user_b, 1, extension8_->id()); 407 } 408 409 virtual void TearDown() OVERRIDE { 410 if (!ash::Shell::HasInstance()) 411 delete item_delegate_manager_; 412 model_->RemoveObserver(model_observer_.get()); 413 model_observer_.reset(); 414 launcher_controller_.reset(); 415 model_.reset(); 416 417 BrowserWithTestWindowTest::TearDown(); 418 } 419 420 void AddAppListLauncherItem() { 421 ash::LauncherItem app_list; 422 app_list.type = ash::TYPE_APP_LIST; 423 model_->Add(app_list); 424 } 425 426 void InitLauncherController() { 427 AddAppListLauncherItem(); 428 launcher_controller_.reset( 429 new ChromeLauncherController(profile(), model_.get())); 430 if (!ash::Shell::HasInstance()) 431 SetShelfItemDelegateManager(item_delegate_manager_); 432 launcher_controller_->Init(); 433 } 434 435 void InitLauncherControllerWithBrowser() { 436 chrome::NewTab(browser()); 437 BrowserList::SetLastActive(browser()); 438 InitLauncherController(); 439 } 440 441 void SetAppIconLoader(extensions::AppIconLoader* loader) { 442 launcher_controller_->SetAppIconLoaderForTest(loader); 443 } 444 445 void SetAppTabHelper(ChromeLauncherController::AppTabHelper* helper) { 446 launcher_controller_->SetAppTabHelperForTest(helper); 447 } 448 449 void SetShelfItemDelegateManager(ash::ShelfItemDelegateManager* manager) { 450 launcher_controller_->SetShelfItemDelegateManagerForTest(manager); 451 } 452 453 void InsertPrefValue(base::ListValue* pref_value, 454 int index, 455 const std::string& extension_id) { 456 base::DictionaryValue* entry = new DictionaryValue(); 457 entry->SetString(ash::kPinnedAppsPrefAppIDPath, extension_id); 458 pref_value->Insert(index, entry); 459 } 460 461 // Gets the currently configured app launchers from the controller. 462 void GetAppLaunchers(ChromeLauncherController* controller, 463 std::vector<std::string>* launchers) { 464 launchers->clear(); 465 for (ash::LauncherItems::const_iterator iter(model_->items().begin()); 466 iter != model_->items().end(); ++iter) { 467 ChromeLauncherController::IDToItemControllerMap::const_iterator 468 entry(controller->id_to_item_controller_map_.find(iter->id)); 469 if (iter->type == ash::TYPE_APP_SHORTCUT && 470 entry != controller->id_to_item_controller_map_.end()) { 471 launchers->push_back(entry->second->app_id()); 472 } 473 } 474 } 475 476 // Get the setup of the currently shown launcher items in one string. 477 // Each pinned element will start with a big letter, each running but not 478 // pinned V1 app will start with a small letter and each running but not 479 // pinned V2 app will start with a '*' + small letter. 480 std::string GetPinnedAppStatus() { 481 std::string result; 482 for (int i = 0; i < model_->item_count(); i++) { 483 switch (model_->items()[i].type) { 484 case ash::TYPE_PLATFORM_APP: 485 result+= "*"; 486 // FALLTHROUGH 487 case ash::TYPE_WINDOWED_APP: { 488 const std::string& app = 489 launcher_controller_->GetAppIDForLauncherID( 490 model_->items()[i].id); 491 if (app == extension1_->id()) { 492 result += "app1, "; 493 EXPECT_FALSE( 494 launcher_controller_->IsAppPinned(extension1_->id())); 495 } else if (app == extension2_->id()) { 496 result += "app2, "; 497 EXPECT_FALSE( 498 launcher_controller_->IsAppPinned(extension2_->id())); 499 } else if (app == extension3_->id()) { 500 result += "app3, "; 501 EXPECT_FALSE( 502 launcher_controller_->IsAppPinned(extension3_->id())); 503 } else if (app == extension4_->id()) { 504 result += "app4, "; 505 EXPECT_FALSE( 506 launcher_controller_->IsAppPinned(extension4_->id())); 507 } else if (app == extension5_->id()) { 508 result += "app5, "; 509 EXPECT_FALSE( 510 launcher_controller_->IsAppPinned(extension5_->id())); 511 } else if (app == extension6_->id()) { 512 result += "app6, "; 513 EXPECT_FALSE( 514 launcher_controller_->IsAppPinned(extension6_->id())); 515 } else if (app == extension7_->id()) { 516 result += "app7, "; 517 EXPECT_FALSE( 518 launcher_controller_->IsAppPinned(extension7_->id())); 519 } else if (app == extension8_->id()) { 520 result += "app8, "; 521 EXPECT_FALSE( 522 launcher_controller_->IsAppPinned(extension8_->id())); 523 } else { 524 result += "unknown, "; 525 } 526 break; 527 } 528 case ash::TYPE_APP_SHORTCUT: { 529 const std::string& app = 530 launcher_controller_->GetAppIDForLauncherID( 531 model_->items()[i].id); 532 if (app == extension1_->id()) { 533 result += "App1, "; 534 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 535 } else if (app == extension2_->id()) { 536 result += "App2, "; 537 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); 538 } else if (app == extension3_->id()) { 539 result += "App3, "; 540 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 541 } else if (app == extension4_->id()) { 542 result += "App4, "; 543 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id())); 544 } else if (app == extension5_->id()) { 545 result += "App5, "; 546 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension5_->id())); 547 } else if (app == extension6_->id()) { 548 result += "App6, "; 549 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension6_->id())); 550 } else if (app == extension7_->id()) { 551 result += "App7, "; 552 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension7_->id())); 553 } else if (app == extension8_->id()) { 554 result += "App8, "; 555 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension8_->id())); 556 } else { 557 result += "unknown, "; 558 } 559 break; 560 } 561 case ash::TYPE_BROWSER_SHORTCUT: 562 result += "Chrome, "; 563 break; 564 case ash::TYPE_APP_LIST: 565 result += "AppList, "; 566 break; 567 default: 568 result += "Unknown"; 569 break; 570 } 571 } 572 return result; 573 } 574 575 // Set the index at which the chrome icon should be. 576 void SetShelfChromeIconIndex(int index) { 577 profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex, 578 index); 579 } 580 581 // Needed for extension service & friends to work. 582 scoped_refptr<Extension> extension1_; 583 scoped_refptr<Extension> extension2_; 584 scoped_refptr<Extension> extension3_; 585 scoped_refptr<Extension> extension4_; 586 scoped_refptr<Extension> extension5_; 587 scoped_refptr<Extension> extension6_; 588 scoped_refptr<Extension> extension7_; 589 scoped_refptr<Extension> extension8_; 590 scoped_ptr<ChromeLauncherController> launcher_controller_; 591 scoped_ptr<TestShelfModelObserver> model_observer_; 592 scoped_ptr<ash::ShelfModel> model_; 593 594 // |item_delegate_manager_| owns |test_controller_|. 595 LauncherItemController* test_controller_; 596 597 ExtensionService* extension_service_; 598 599 ash::ShelfItemDelegateManager* item_delegate_manager_; 600 601 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerTest); 602 }; 603 604 // The testing framework to test the legacy shelf layout. 605 class LegacyShelfLayoutChromeLauncherControllerTest 606 : public ChromeLauncherControllerTest { 607 protected: 608 LegacyShelfLayoutChromeLauncherControllerTest() { 609 } 610 611 virtual ~LegacyShelfLayoutChromeLauncherControllerTest() { 612 } 613 614 // Overwrite the Setup function to use the legacy shelf layout option. 615 virtual void SetUp() OVERRIDE { 616 CommandLine::ForCurrentProcess()->AppendSwitch( 617 ash::switches::kAshDisableAlternateShelfLayout); 618 ChromeLauncherControllerTest::SetUp(); 619 } 620 621 private: 622 623 DISALLOW_COPY_AND_ASSIGN(LegacyShelfLayoutChromeLauncherControllerTest); 624 }; 625 626 #if defined(OS_CHROMEOS) 627 // A browser window proxy which is able to associate an aura native window with 628 // it. 629 class TestBrowserWindowAura : public TestBrowserWindow { 630 public: 631 // |native_window| will still be owned by the caller after the constructor 632 // was called. 633 explicit TestBrowserWindowAura(aura::Window* native_window) 634 : native_window_(native_window) { 635 } 636 virtual ~TestBrowserWindowAura() {} 637 638 virtual gfx::NativeWindow GetNativeWindow() OVERRIDE { 639 return native_window_.get(); 640 } 641 642 Browser* browser() { return browser_.get(); } 643 644 void CreateBrowser(const Browser::CreateParams& params) { 645 Browser::CreateParams create_params = params; 646 create_params.window = this; 647 browser_.reset(new Browser(create_params)); 648 } 649 650 private: 651 scoped_ptr<Browser> browser_; 652 scoped_ptr<aura::Window> native_window_; 653 654 DISALLOW_COPY_AND_ASSIGN(TestBrowserWindowAura); 655 }; 656 657 // Creates a test browser window which has a native window. 658 scoped_ptr<TestBrowserWindowAura> CreateTestBrowserWindow( 659 const Browser::CreateParams& params) { 660 // Create a window. 661 aura::Window* window = new aura::Window(NULL); 662 window->set_id(0); 663 window->SetType(aura::client::WINDOW_TYPE_NORMAL); 664 window->Init(ui::LAYER_TEXTURED); 665 window->Show(); 666 667 scoped_ptr<TestBrowserWindowAura> browser_window( 668 new TestBrowserWindowAura(window)); 669 browser_window->CreateBrowser(params); 670 return browser_window.Pass(); 671 } 672 673 // A views delegate which allows creating shell windows. 674 class TestViewsDelegateForAppTest : public views::TestViewsDelegate { 675 public: 676 TestViewsDelegateForAppTest() {} 677 virtual ~TestViewsDelegateForAppTest() {} 678 679 // views::TestViewsDelegate overrides. 680 virtual void OnBeforeWidgetInit( 681 views::Widget::InitParams* params, 682 views::internal::NativeWidgetDelegate* delegate) OVERRIDE { 683 if (!params->parent && !params->context) { 684 // If the window has neither a parent nor a context we add the root window 685 // as parent. 686 params->parent = ash::Shell::GetInstance()->GetPrimaryRootWindow(); 687 } 688 } 689 690 private: 691 DISALLOW_COPY_AND_ASSIGN(TestViewsDelegateForAppTest); 692 }; 693 694 // Watches WebContents and blocks until it is destroyed. This is needed for 695 // the destruction of a V2 application. 696 class WebContentsDestroyedWatcher : public content::WebContentsObserver { 697 public: 698 explicit WebContentsDestroyedWatcher(content::WebContents* web_contents) 699 : content::WebContentsObserver(web_contents), 700 message_loop_runner_(new content::MessageLoopRunner) { 701 EXPECT_TRUE(web_contents != NULL); 702 } 703 virtual ~WebContentsDestroyedWatcher() {} 704 705 // Waits until the WebContents is destroyed. 706 void Wait() { 707 message_loop_runner_->Run(); 708 } 709 710 private: 711 // Overridden WebContentsObserver methods. 712 virtual void WebContentsDestroyed( 713 content::WebContents* web_contents) OVERRIDE { 714 message_loop_runner_->Quit(); 715 } 716 717 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 718 719 DISALLOW_COPY_AND_ASSIGN(WebContentsDestroyedWatcher); 720 }; 721 722 // A V1 windowed application. 723 class V1App : public TestBrowserWindow { 724 public: 725 V1App(Profile* profile, const std::string& app_name) { 726 // Create a window. 727 native_window_.reset(new aura::Window(NULL)); 728 native_window_->set_id(0); 729 native_window_->SetType(aura::client::WINDOW_TYPE_POPUP); 730 native_window_->Init(ui::LAYER_TEXTURED); 731 native_window_->Show(); 732 733 Browser::CreateParams params = Browser::CreateParams::CreateForApp( 734 Browser::TYPE_POPUP, 735 kCrxAppPrefix + app_name, 736 gfx::Rect(), 737 profile, 738 chrome::HOST_DESKTOP_TYPE_ASH); 739 params.window = this; 740 browser_.reset(new Browser(params)); 741 chrome::AddTabAt(browser_.get(), GURL(), 0, true); 742 } 743 744 virtual ~V1App() { 745 // close all tabs. Note that we do not need to destroy the browser itself. 746 browser_->tab_strip_model()->CloseAllTabs(); 747 } 748 749 Browser* browser() { return browser_.get(); } 750 751 // TestBrowserWindow override: 752 virtual gfx::NativeWindow GetNativeWindow() OVERRIDE { 753 return native_window_.get(); 754 } 755 756 private: 757 // The associated browser with this app. 758 scoped_ptr<Browser> browser_; 759 760 // The native window we use. 761 scoped_ptr<aura::Window> native_window_; 762 763 DISALLOW_COPY_AND_ASSIGN(V1App); 764 }; 765 766 // A V2 application which gets created with an |extension| and for a |profile|. 767 // Upon destruction it will properly close the application. 768 class V2App { 769 public: 770 V2App(Profile* profile, const extensions::Extension* extension) { 771 window_ = new apps::ShellWindow(profile, 772 new ChromeShellWindowDelegate(), 773 extension); 774 apps::ShellWindow::CreateParams params = apps::ShellWindow::CreateParams(); 775 window_->Init(GURL(std::string()), 776 new apps::AppWindowContents(window_), 777 params); 778 } 779 780 virtual ~V2App() { 781 WebContentsDestroyedWatcher destroyed_watcher(window_->web_contents()); 782 window_->GetBaseWindow()->Close(); 783 destroyed_watcher.Wait(); 784 } 785 786 private: 787 // The shell window which represents the application. Note that the window 788 // deletes itself asynchronously after window_->GetBaseWindow()->Close() gets 789 // called. 790 apps::ShellWindow* window_; 791 792 DISALLOW_COPY_AND_ASSIGN(V2App); 793 }; 794 795 // The testing framework to test multi profile scenarios. 796 class MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest 797 : public ChromeLauncherControllerTest { 798 protected: 799 MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest() { 800 } 801 802 virtual ~MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest() { 803 } 804 805 // Overwrite the Setup function to enable multi profile and needed objects. 806 virtual void SetUp() OVERRIDE { 807 profile_manager_.reset( 808 new TestingProfileManager(TestingBrowserProcess::GetGlobal())); 809 810 ASSERT_TRUE(profile_manager_->SetUp()); 811 812 // AvatarMenu and multiple profiles works after user logged in. 813 profile_manager_->SetLoggedIn(true); 814 815 // Enabling multi profile requires several flags to be set. 816 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kMultiProfiles); 817 818 // Initialize the UserManager singleton to a fresh FakeUserManager instance. 819 user_manager_enabler_.reset( 820 new chromeos::ScopedUserManagerEnabler(new chromeos::FakeUserManager)); 821 822 // Initialize the rest. 823 ChromeLauncherControllerTest::SetUp(); 824 825 // Get some base objects. 826 session_delegate_ = static_cast<ash::test::TestSessionStateDelegate*>( 827 ash::Shell::GetInstance()->session_state_delegate()); 828 session_delegate_->set_logged_in_users(2); 829 shell_delegate_ = static_cast<ash::test::TestShellDelegate*>( 830 ash::Shell::GetInstance()->delegate()); 831 shell_delegate_->set_multi_profiles_enabled(true); 832 } 833 834 virtual void TearDown() { 835 ChromeLauncherControllerTest::TearDown(); 836 user_manager_enabler_.reset(); 837 for (ProfileToNameMap::iterator it = created_profiles_.begin(); 838 it != created_profiles_.end(); ++it) 839 profile_manager_->DeleteTestingProfile(it->second); 840 841 // A Task is leaked if we don't destroy everything, then run the message 842 // loop. 843 base::MessageLoop::current()->PostTask(FROM_HERE, 844 base::MessageLoop::QuitClosure()); 845 base::MessageLoop::current()->Run(); 846 } 847 848 // Creates a profile for a given |user_name|. Note that this class will keep 849 // the ownership of the created object. 850 TestingProfile* CreateMultiUserProfile(const std::string& user_name) { 851 std::string email_string = user_name + "@example.com"; 852 853 // Add a user to the fake user manager. 854 GetFakeUserManager()->AddUser(email_string); 855 856 GetFakeUserManager()->UserLoggedIn( 857 email_string, 858 email_string + kUserIdHashSuffix, 859 false); 860 861 std::string profile_name = 862 chrome::kProfileDirPrefix + email_string + kUserIdHashSuffix; 863 TestingProfile* profile = profile_manager()->CreateTestingProfile( 864 profile_name, 865 scoped_ptr<PrefServiceSyncable>(), 866 ASCIIToUTF16(email_string), 0, std::string(), 867 TestingProfile::TestingFactories()); 868 profile->set_profile_name(email_string); 869 EXPECT_TRUE(profile); 870 // Remember the profile name so that we can destroy it upon destruction. 871 created_profiles_[profile] = profile_name; 872 return profile; 873 } 874 875 // Switch to another user. 876 void SwitchActiveUser(const std::string& name) { 877 session_delegate()->SwitchActiveUser(name); 878 GetFakeUserManager()->SwitchActiveUser(name); 879 launcher_controller_->browser_status_monitor_for_test()-> 880 ActiveUserChanged(name); 881 launcher_controller_->shell_window_controller_for_test()-> 882 ActiveUserChanged(name); 883 } 884 885 // Creates a browser with a |profile| and load a tab with a |title| and |url|. 886 Browser* CreateBrowserAndTabWithProfile(Profile* profile, 887 const std::string& title, 888 const std::string& url) { 889 Browser::CreateParams params(profile, chrome::HOST_DESKTOP_TYPE_ASH); 890 Browser* browser = chrome::CreateBrowserWithTestWindowForParams(¶ms); 891 chrome::NewTab(browser); 892 893 BrowserList::SetLastActive(browser); 894 NavigateAndCommitActiveTabWithTitle( 895 browser, GURL(url), ASCIIToUTF16(title)); 896 return browser; 897 } 898 899 // Creates a running V1 application. 900 // Note that with the use of the app_tab_helper as done below, this is only 901 // usable with a single v1 application. 902 V1App* CreateRunningV1App(Profile* profile, 903 const std::string& app_name, 904 const std::string& url) { 905 V1App* v1_app = new V1App(profile, app_name); 906 // Create a new app tab helper and assign it to the launcher so that this 907 // app gets properly detected. 908 // TODO(skuhne): Create a more intelligent app tab helper which is able to 909 // detect all running apps properly. 910 TestAppTabHelperImpl* app_tab_helper = new TestAppTabHelperImpl; 911 app_tab_helper->SetAppID( 912 v1_app->browser()->tab_strip_model()->GetWebContentsAt(0), 913 app_name); 914 SetAppTabHelper(app_tab_helper); 915 916 NavigateAndCommitActiveTabWithTitle( 917 v1_app->browser(), GURL(url), ASCIIToUTF16("")); 918 return v1_app; 919 } 920 921 ash::test::TestSessionStateDelegate* 922 session_delegate() { return session_delegate_; } 923 ash::test::TestShellDelegate* shell_delegate() { return shell_delegate_; } 924 925 // Override BrowserWithTestWindowTest: 926 virtual TestingProfile* CreateProfile() OVERRIDE { 927 return CreateMultiUserProfile("user1"); 928 } 929 virtual void DestroyProfile(TestingProfile* profile) OVERRIDE { 930 // Delete the profile through our profile manager. 931 ProfileToNameMap::iterator it = created_profiles_.find(profile); 932 DCHECK(it != created_profiles_.end()); 933 profile_manager_->DeleteTestingProfile(it->second); 934 created_profiles_.erase(it); 935 } 936 937 private: 938 typedef std::map<Profile*, std::string> ProfileToNameMap; 939 TestingProfileManager* profile_manager() { return profile_manager_.get(); } 940 941 chromeos::FakeUserManager* GetFakeUserManager() { 942 return static_cast<chromeos::FakeUserManager*>( 943 chromeos::UserManager::Get()); 944 } 945 946 scoped_ptr<TestingProfileManager> profile_manager_; 947 scoped_ptr<chromeos::ScopedUserManagerEnabler> user_manager_enabler_; 948 949 ash::test::TestSessionStateDelegate* session_delegate_; 950 ash::test::TestShellDelegate* shell_delegate_; 951 952 ProfileToNameMap created_profiles_; 953 954 DISALLOW_COPY_AND_ASSIGN( 955 MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest); 956 }; 957 #endif // defined(OS_CHROMEOS) 958 959 TEST_F(LegacyShelfLayoutChromeLauncherControllerTest, DefaultApps) { 960 InitLauncherController(); 961 // Model should only contain the browser shortcut and app list items. 962 EXPECT_EQ(2, model_->item_count()); 963 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 964 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 965 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 966 967 // Installing |extension3_| should add it to the launcher - behind the 968 // chrome icon. 969 extension_service_->AddExtension(extension3_.get()); 970 EXPECT_EQ("Chrome, App3, AppList, ", GetPinnedAppStatus()); 971 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 972 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 973 } 974 975 // Check that the restauration of launcher items is happening in the same order 976 // as the user has pinned them (on another system) when they are synced reverse 977 // order. 978 TEST_F(LegacyShelfLayoutChromeLauncherControllerTest, 979 RestoreDefaultAppsReverseOrder) { 980 InitLauncherController(); 981 982 base::ListValue policy_value; 983 InsertPrefValue(&policy_value, 0, extension1_->id()); 984 InsertPrefValue(&policy_value, 1, extension2_->id()); 985 InsertPrefValue(&policy_value, 2, extension3_->id()); 986 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 987 policy_value.DeepCopy()); 988 EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex)); 989 // Model should only contain the browser shortcut and app list items. 990 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 991 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 992 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 993 EXPECT_EQ("Chrome, AppList, ", GetPinnedAppStatus()); 994 995 // Installing |extension3_| should add it to the launcher - behind the 996 // chrome icon. 997 ash::LauncherItem item; 998 extension_service_->AddExtension(extension3_.get()); 999 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1000 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1001 EXPECT_EQ("Chrome, App3, AppList, ", GetPinnedAppStatus()); 1002 1003 // Installing |extension2_| should add it to the launcher - behind the 1004 // chrome icon, but in first location. 1005 extension_service_->AddExtension(extension2_.get()); 1006 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1007 EXPECT_EQ("Chrome, App2, App3, AppList, ", GetPinnedAppStatus()); 1008 1009 // Installing |extension1_| should add it to the launcher - behind the 1010 // chrome icon, but in first location. 1011 extension_service_->AddExtension(extension1_.get()); 1012 EXPECT_EQ("Chrome, App1, App2, App3, AppList, ", GetPinnedAppStatus()); 1013 } 1014 1015 // Check that the restauration of launcher items is happening in the same order 1016 // as the user has pinned them (on another system) when they are synced random 1017 // order. 1018 TEST_F(LegacyShelfLayoutChromeLauncherControllerTest, 1019 RestoreDefaultAppsRandomOrder) { 1020 InitLauncherController(); 1021 1022 base::ListValue policy_value; 1023 InsertPrefValue(&policy_value, 0, extension1_->id()); 1024 InsertPrefValue(&policy_value, 1, extension2_->id()); 1025 InsertPrefValue(&policy_value, 2, extension3_->id()); 1026 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1027 policy_value.DeepCopy()); 1028 EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex)); 1029 // Model should only contain the browser shortcut and app list items. 1030 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1031 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1032 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1033 EXPECT_EQ("Chrome, AppList, ", GetPinnedAppStatus()); 1034 1035 // Installing |extension2_| should add it to the launcher - behind the 1036 // chrome icon. 1037 extension_service_->AddExtension(extension2_.get()); 1038 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1039 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1040 EXPECT_EQ("Chrome, App2, AppList, ", GetPinnedAppStatus()); 1041 1042 // Installing |extension1_| should add it to the launcher - behind the 1043 // chrome icon, but in first location. 1044 extension_service_->AddExtension(extension1_.get()); 1045 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1046 EXPECT_EQ("Chrome, App1, App2, AppList, ", GetPinnedAppStatus()); 1047 1048 // Installing |extension3_| should add it to the launcher - behind the 1049 // chrome icon, but in first location. 1050 extension_service_->AddExtension(extension3_.get()); 1051 EXPECT_EQ("Chrome, App1, App2, App3, AppList, ", GetPinnedAppStatus()); 1052 } 1053 1054 // Check that the restauration of launcher items is happening in the same order 1055 // as the user has pinned / moved them (on another system) when they are synced 1056 // random order - including the chrome icon. 1057 TEST_F(LegacyShelfLayoutChromeLauncherControllerTest, 1058 RestoreDefaultAppsRandomOrderChromeMoved) { 1059 InitLauncherController(); 1060 1061 base::ListValue policy_value; 1062 InsertPrefValue(&policy_value, 0, extension1_->id()); 1063 InsertPrefValue(&policy_value, 1, extension2_->id()); 1064 InsertPrefValue(&policy_value, 2, extension3_->id()); 1065 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1066 policy_value.DeepCopy()); 1067 profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex, 1068 1); 1069 // Model should only contain the browser shortcut and app list items. 1070 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1071 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1072 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1073 EXPECT_EQ("Chrome, AppList, ", GetPinnedAppStatus()); 1074 1075 // Installing |extension2_| should add it to the launcher - behind the 1076 // chrome icon. 1077 ash::LauncherItem item; 1078 extension_service_->AddExtension(extension2_.get()); 1079 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1080 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1081 EXPECT_EQ("Chrome, App2, AppList, ", GetPinnedAppStatus()); 1082 1083 // Installing |extension1_| should add it to the launcher - behind the 1084 // chrome icon, but in first location. 1085 extension_service_->AddExtension(extension1_.get()); 1086 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1087 EXPECT_EQ("App1, Chrome, App2, AppList, ", GetPinnedAppStatus()); 1088 1089 // Installing |extension3_| should add it to the launcher - behind the 1090 // chrome icon, but in first location. 1091 extension_service_->AddExtension(extension3_.get()); 1092 EXPECT_EQ("App1, Chrome, App2, App3, AppList, ", GetPinnedAppStatus()); 1093 } 1094 1095 // Check that syncing to a different state does the correct thing. 1096 TEST_F(LegacyShelfLayoutChromeLauncherControllerTest, 1097 RestoreDefaultAppsResyncOrder) { 1098 InitLauncherController(); 1099 base::ListValue policy_value; 1100 InsertPrefValue(&policy_value, 0, extension1_->id()); 1101 InsertPrefValue(&policy_value, 1, extension2_->id()); 1102 InsertPrefValue(&policy_value, 2, extension3_->id()); 1103 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1104 policy_value.DeepCopy()); 1105 EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex)); 1106 extension_service_->AddExtension(extension2_.get()); 1107 extension_service_->AddExtension(extension1_.get()); 1108 extension_service_->AddExtension(extension3_.get()); 1109 EXPECT_EQ("Chrome, App1, App2, App3, AppList, ", GetPinnedAppStatus()); 1110 1111 // Change the order with increasing chrome position and decreasing position. 1112 base::ListValue policy_value1; 1113 InsertPrefValue(&policy_value1, 0, extension3_->id()); 1114 InsertPrefValue(&policy_value1, 1, extension1_->id()); 1115 InsertPrefValue(&policy_value1, 2, extension2_->id()); 1116 profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex, 1117 2); 1118 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1119 policy_value1.DeepCopy()); 1120 EXPECT_EQ("App3, App1, Chrome, App2, AppList, ", GetPinnedAppStatus()); 1121 base::ListValue policy_value2; 1122 InsertPrefValue(&policy_value2, 0, extension2_->id()); 1123 InsertPrefValue(&policy_value2, 1, extension3_->id()); 1124 InsertPrefValue(&policy_value2, 2, extension1_->id()); 1125 profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex, 1126 1); 1127 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1128 policy_value2.DeepCopy()); 1129 EXPECT_EQ("App2, Chrome, App3, App1, AppList, ", GetPinnedAppStatus()); 1130 } 1131 1132 TEST_F(ChromeLauncherControllerTest, DefaultApps) { 1133 InitLauncherController(); 1134 // Model should only contain the browser shortcut and app list items. 1135 EXPECT_EQ(2, model_->item_count()); 1136 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1137 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1138 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1139 1140 // Installing |extension3_| should add it to the launcher - behind the 1141 // chrome icon. 1142 extension_service_->AddExtension(extension3_.get()); 1143 EXPECT_EQ("AppList, Chrome, App3, ", GetPinnedAppStatus()); 1144 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1145 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1146 } 1147 1148 // Check that changing from the alternate shelf layout to the old shelflayout 1149 // and back does keep the app launcher at location #0. 1150 TEST_F(ChromeLauncherControllerTest, 1151 SwitchingFromAlternateShelfLayoutToLegacyAndBack) { 1152 InitLauncherController(); 1153 1154 // We simulate this problem by intentionally placing the app list item in 1155 // the middle of several apps which caused a crash (see crbug.com/329597). 1156 const char kAppLauncherIdPlaceholder[] = "AppLauncherIDPlaceholder--------"; 1157 1158 base::ListValue policy_value; 1159 InsertPrefValue(&policy_value, 0, extension1_->id()); 1160 InsertPrefValue(&policy_value, 1, kAppLauncherIdPlaceholder); 1161 InsertPrefValue(&policy_value, 2, extension2_->id()); 1162 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1163 policy_value.DeepCopy()); 1164 EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex)); 1165 // Model should only contain the browser shortcut and app list items. 1166 extension_service_->AddExtension(extension1_.get()); 1167 extension_service_->AddExtension(extension2_.get()); 1168 EXPECT_EQ("AppList, Chrome, App1, App2, ", GetPinnedAppStatus()); 1169 } 1170 1171 // Check that the restauration of launcher items is happening in the same order 1172 // as the user has pinned them (on another system) when they are synced reverse 1173 // order. 1174 TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsReverseOrder) { 1175 InitLauncherController(); 1176 1177 base::ListValue policy_value; 1178 InsertPrefValue(&policy_value, 0, extension1_->id()); 1179 InsertPrefValue(&policy_value, 1, extension2_->id()); 1180 InsertPrefValue(&policy_value, 2, extension3_->id()); 1181 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1182 policy_value.DeepCopy()); 1183 SetShelfChromeIconIndex(0); 1184 // Model should only contain the browser shortcut and app list items. 1185 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1186 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1187 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1188 EXPECT_EQ("AppList, Chrome, ", GetPinnedAppStatus()); 1189 1190 // Installing |extension3_| should add it to the launcher - behind the 1191 // chrome icon. 1192 ash::LauncherItem item; 1193 extension_service_->AddExtension(extension3_.get()); 1194 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1195 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1196 EXPECT_EQ("AppList, Chrome, App3, ", GetPinnedAppStatus()); 1197 1198 // Installing |extension2_| should add it to the launcher - behind the 1199 // chrome icon, but in first location. 1200 extension_service_->AddExtension(extension2_.get()); 1201 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1202 EXPECT_EQ("AppList, Chrome, App2, App3, ", GetPinnedAppStatus()); 1203 1204 // Installing |extension1_| should add it to the launcher - behind the 1205 // chrome icon, but in first location. 1206 extension_service_->AddExtension(extension1_.get()); 1207 EXPECT_EQ("AppList, Chrome, App1, App2, App3, ", GetPinnedAppStatus()); 1208 } 1209 1210 // Check that the restauration of launcher items is happening in the same order 1211 // as the user has pinned them (on another system) when they are synced random 1212 // order. 1213 TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsRandomOrder) { 1214 InitLauncherController(); 1215 1216 base::ListValue policy_value; 1217 InsertPrefValue(&policy_value, 0, extension1_->id()); 1218 InsertPrefValue(&policy_value, 1, extension2_->id()); 1219 InsertPrefValue(&policy_value, 2, extension3_->id()); 1220 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1221 policy_value.DeepCopy()); 1222 SetShelfChromeIconIndex(0); 1223 // Model should only contain the browser shortcut and app list items. 1224 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1225 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1226 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1227 EXPECT_EQ("AppList, Chrome, ", GetPinnedAppStatus()); 1228 1229 // Installing |extension2_| should add it to the launcher - behind the 1230 // chrome icon. 1231 extension_service_->AddExtension(extension2_.get()); 1232 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1233 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1234 EXPECT_EQ("AppList, Chrome, App2, ", GetPinnedAppStatus()); 1235 1236 // Installing |extension1_| should add it to the launcher - behind the 1237 // chrome icon, but in first location. 1238 extension_service_->AddExtension(extension1_.get()); 1239 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1240 EXPECT_EQ("AppList, Chrome, App1, App2, ", GetPinnedAppStatus()); 1241 1242 // Installing |extension3_| should add it to the launcher - behind the 1243 // chrome icon, but in first location. 1244 extension_service_->AddExtension(extension3_.get()); 1245 EXPECT_EQ("AppList, Chrome, App1, App2, App3, ", GetPinnedAppStatus()); 1246 } 1247 1248 // Check that the restauration of launcher items is happening in the same order 1249 // as the user has pinned / moved them (on another system) when they are synced 1250 // random order - including the chrome icon. 1251 TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsRandomOrderChromeMoved) { 1252 InitLauncherController(); 1253 1254 base::ListValue policy_value; 1255 InsertPrefValue(&policy_value, 0, extension1_->id()); 1256 InsertPrefValue(&policy_value, 1, extension2_->id()); 1257 InsertPrefValue(&policy_value, 2, extension3_->id()); 1258 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1259 policy_value.DeepCopy()); 1260 SetShelfChromeIconIndex(1); 1261 // Model should only contain the browser shortcut and app list items. 1262 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1263 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1264 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1265 EXPECT_EQ("AppList, Chrome, ", GetPinnedAppStatus()); 1266 1267 // Installing |extension2_| should add it to the launcher - behind the 1268 // chrome icon. 1269 ash::LauncherItem item; 1270 extension_service_->AddExtension(extension2_.get()); 1271 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1272 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1273 EXPECT_EQ("AppList, Chrome, App2, ", GetPinnedAppStatus()); 1274 1275 // Installing |extension1_| should add it to the launcher - behind the 1276 // chrome icon, but in first location. 1277 extension_service_->AddExtension(extension1_.get()); 1278 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1279 EXPECT_EQ("AppList, App1, Chrome, App2, ", GetPinnedAppStatus()); 1280 1281 // Installing |extension3_| should add it to the launcher - behind the 1282 // chrome icon, but in first location. 1283 extension_service_->AddExtension(extension3_.get()); 1284 EXPECT_EQ("AppList, App1, Chrome, App2, App3, ", GetPinnedAppStatus()); 1285 } 1286 1287 // Check that syncing to a different state does the correct thing. 1288 TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsResyncOrder) { 1289 InitLauncherController(); 1290 base::ListValue policy_value; 1291 InsertPrefValue(&policy_value, 0, extension1_->id()); 1292 InsertPrefValue(&policy_value, 1, extension2_->id()); 1293 InsertPrefValue(&policy_value, 2, extension3_->id()); 1294 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1295 policy_value.DeepCopy()); 1296 // The shelf layout has always one static item at the beginning (App List). 1297 SetShelfChromeIconIndex(0); 1298 extension_service_->AddExtension(extension2_.get()); 1299 EXPECT_EQ("AppList, Chrome, App2, ", GetPinnedAppStatus()); 1300 extension_service_->AddExtension(extension1_.get()); 1301 EXPECT_EQ("AppList, Chrome, App1, App2, ", GetPinnedAppStatus()); 1302 extension_service_->AddExtension(extension3_.get()); 1303 EXPECT_EQ("AppList, Chrome, App1, App2, App3, ", GetPinnedAppStatus()); 1304 1305 // Change the order with increasing chrome position and decreasing position. 1306 base::ListValue policy_value1; 1307 InsertPrefValue(&policy_value1, 0, extension3_->id()); 1308 InsertPrefValue(&policy_value1, 1, extension1_->id()); 1309 InsertPrefValue(&policy_value1, 2, extension2_->id()); 1310 SetShelfChromeIconIndex(3); 1311 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1312 policy_value1.DeepCopy()); 1313 EXPECT_EQ("AppList, App3, App1, App2, Chrome, ", GetPinnedAppStatus()); 1314 base::ListValue policy_value2; 1315 InsertPrefValue(&policy_value2, 0, extension2_->id()); 1316 InsertPrefValue(&policy_value2, 1, extension3_->id()); 1317 InsertPrefValue(&policy_value2, 2, extension1_->id()); 1318 SetShelfChromeIconIndex(2); 1319 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1320 policy_value2.DeepCopy()); 1321 EXPECT_EQ("AppList, App2, App3, Chrome, App1, ", GetPinnedAppStatus()); 1322 1323 // Check that the chrome icon can also be at the first possible location. 1324 SetShelfChromeIconIndex(0); 1325 base::ListValue policy_value3; 1326 InsertPrefValue(&policy_value3, 0, extension3_->id()); 1327 InsertPrefValue(&policy_value3, 1, extension2_->id()); 1328 InsertPrefValue(&policy_value3, 2, extension1_->id()); 1329 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1330 policy_value3.DeepCopy()); 1331 EXPECT_EQ("AppList, Chrome, App3, App2, App1, ", GetPinnedAppStatus()); 1332 1333 // Check that unloading of extensions works as expected. 1334 extension_service_->UnloadExtension(extension1_->id(), 1335 UnloadedExtensionInfo::REASON_UNINSTALL); 1336 EXPECT_EQ("AppList, Chrome, App3, App2, ", GetPinnedAppStatus()); 1337 1338 extension_service_->UnloadExtension(extension2_->id(), 1339 UnloadedExtensionInfo::REASON_UNINSTALL); 1340 EXPECT_EQ("AppList, Chrome, App3, ", GetPinnedAppStatus()); 1341 1342 // Check that an update of an extension does not crash the system. 1343 extension_service_->UnloadExtension(extension3_->id(), 1344 UnloadedExtensionInfo::REASON_UPDATE); 1345 EXPECT_EQ("AppList, Chrome, App3, ", GetPinnedAppStatus()); 1346 } 1347 1348 // Check that simple locking of an application will 'create' a launcher item. 1349 TEST_F(ChromeLauncherControllerTest, CheckLockApps) { 1350 InitLauncherController(); 1351 // Model should only contain the browser shortcut and app list items. 1352 EXPECT_EQ(2, model_->item_count()); 1353 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1354 EXPECT_FALSE( 1355 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1356 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1357 EXPECT_FALSE( 1358 launcher_controller_->IsWindowedAppInLauncher(extension2_->id())); 1359 1360 launcher_controller_->LockV1AppWithID(extension1_->id()); 1361 1362 EXPECT_EQ(3, model_->item_count()); 1363 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[2].type); 1364 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1365 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1366 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1367 EXPECT_FALSE( 1368 launcher_controller_->IsWindowedAppInLauncher(extension2_->id())); 1369 1370 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 1371 1372 EXPECT_EQ(2, model_->item_count()); 1373 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1374 EXPECT_FALSE( 1375 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1376 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1377 EXPECT_FALSE( 1378 launcher_controller_->IsWindowedAppInLauncher(extension2_->id())); 1379 } 1380 1381 // Check that multiple locks of an application will be properly handled. 1382 TEST_F(ChromeLauncherControllerTest, CheckMultiLockApps) { 1383 InitLauncherController(); 1384 // Model should only contain the browser shortcut and app list items. 1385 EXPECT_EQ(2, model_->item_count()); 1386 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1387 EXPECT_FALSE( 1388 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1389 1390 for (int i = 0; i < 2; i++) { 1391 launcher_controller_->LockV1AppWithID(extension1_->id()); 1392 1393 EXPECT_EQ(3, model_->item_count()); 1394 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[2].type); 1395 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1396 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher( 1397 extension1_->id())); 1398 } 1399 1400 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 1401 1402 EXPECT_EQ(3, model_->item_count()); 1403 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[2].type); 1404 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1405 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1406 1407 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 1408 1409 EXPECT_EQ(2, model_->item_count()); 1410 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1411 EXPECT_FALSE( 1412 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1413 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1414 EXPECT_FALSE( 1415 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1416 } 1417 1418 // Check that already pinned items are not effected by locks. 1419 TEST_F(ChromeLauncherControllerTest, CheckAlreadyPinnedLockApps) { 1420 InitLauncherController(); 1421 // Model should only contain the browser shortcut and app list items. 1422 EXPECT_EQ(2, model_->item_count()); 1423 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1424 EXPECT_FALSE( 1425 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1426 1427 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1428 launcher_controller_->PinAppWithID(extension1_->id()); 1429 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1430 1431 EXPECT_EQ(3, model_->item_count()); 1432 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1433 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1434 EXPECT_FALSE( 1435 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1436 1437 launcher_controller_->LockV1AppWithID(extension1_->id()); 1438 1439 EXPECT_EQ(3, model_->item_count()); 1440 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1441 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1442 EXPECT_FALSE( 1443 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1444 1445 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 1446 1447 EXPECT_EQ(3, model_->item_count()); 1448 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1449 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1450 EXPECT_FALSE( 1451 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1452 1453 launcher_controller_->UnpinAppWithID(extension1_->id()); 1454 1455 EXPECT_EQ(2, model_->item_count()); 1456 } 1457 1458 // Check that already pinned items which get locked stay after unpinning. 1459 TEST_F(ChromeLauncherControllerTest, CheckPinnedAppsStayAfterUnlock) { 1460 InitLauncherController(); 1461 // Model should only contain the browser shortcut and app list items. 1462 EXPECT_EQ(2, model_->item_count()); 1463 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1464 EXPECT_FALSE( 1465 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1466 1467 launcher_controller_->PinAppWithID(extension1_->id()); 1468 1469 EXPECT_EQ(3, model_->item_count()); 1470 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1471 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1472 EXPECT_FALSE( 1473 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1474 1475 launcher_controller_->LockV1AppWithID(extension1_->id()); 1476 1477 EXPECT_EQ(3, model_->item_count()); 1478 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1479 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1480 EXPECT_FALSE( 1481 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1482 1483 launcher_controller_->UnpinAppWithID(extension1_->id()); 1484 1485 EXPECT_EQ(3, model_->item_count()); 1486 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[2].type); 1487 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1488 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1489 1490 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 1491 1492 EXPECT_EQ(2, model_->item_count()); 1493 } 1494 1495 #if defined(OS_CHROMEOS) 1496 // Check that with multi profile V1 apps are properly added / removed from the 1497 // shelf. 1498 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, 1499 V1AppUpdateOnUserSwitch) { 1500 // Create a browser item in the LauncherController. 1501 InitLauncherController(); 1502 EXPECT_EQ(2, model_->item_count()); 1503 { 1504 // Create a "windowed gmail app". 1505 scoped_ptr<V1App> v1_app(CreateRunningV1App( 1506 profile(), extension_misc::kGmailAppId, gmail_url)); 1507 EXPECT_EQ(3, model_->item_count()); 1508 1509 // After switching to a second user the item should be gone. 1510 std::string user2 = "user2"; 1511 TestingProfile* profile2 = CreateMultiUserProfile(user2); 1512 SwitchActiveUser(profile2->GetProfileName()); 1513 EXPECT_EQ(2, model_->item_count()); 1514 1515 // After switching back the item should be back. 1516 SwitchActiveUser(profile()->GetProfileName()); 1517 EXPECT_EQ(3, model_->item_count()); 1518 // Note we destroy now the gmail app with the closure end. 1519 } 1520 EXPECT_EQ(2, model_->item_count()); 1521 } 1522 1523 // Check edge cases with multi profile V1 apps in the shelf. 1524 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, 1525 V1AppUpdateOnUserSwitchEdgecases) { 1526 // Create a browser item in the LauncherController. 1527 InitLauncherController(); 1528 1529 // First test: Create an app when the user is not active. 1530 std::string user2 = "user2"; 1531 TestingProfile* profile2 = CreateMultiUserProfile(user2); 1532 { 1533 // Create a "windowed gmail app". 1534 scoped_ptr<V1App> v1_app(CreateRunningV1App( 1535 profile2, extension_misc::kGmailAppId, gmail_url)); 1536 EXPECT_EQ(2, model_->item_count()); 1537 1538 // However - switching to the user should show it. 1539 SwitchActiveUser(profile2->GetProfileName()); 1540 EXPECT_EQ(3, model_->item_count()); 1541 1542 // Second test: Remove the app when the user is not active and see that it 1543 // works. 1544 SwitchActiveUser(profile()->GetProfileName()); 1545 EXPECT_EQ(2, model_->item_count()); 1546 // Note: the closure ends and the browser will go away. 1547 } 1548 EXPECT_EQ(2, model_->item_count()); 1549 SwitchActiveUser(profile2->GetProfileName()); 1550 EXPECT_EQ(2, model_->item_count()); 1551 SwitchActiveUser(profile()->GetProfileName()); 1552 EXPECT_EQ(2, model_->item_count()); 1553 } 1554 1555 // Check edge case where a visiting V1 app gets closed (crbug.com/321374). 1556 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, 1557 V1CloseOnVisitingDesktop) { 1558 // Create a browser item in the LauncherController. 1559 InitLauncherController(); 1560 1561 chrome::MultiUserWindowManager* manager = 1562 chrome::MultiUserWindowManager::GetInstance(); 1563 1564 // First create an app when the user is active. 1565 std::string user2 = "user2"; 1566 TestingProfile* profile2 = CreateMultiUserProfile(user2); 1567 { 1568 // Create a "windowed gmail app". 1569 scoped_ptr<V1App> v1_app(CreateRunningV1App( 1570 profile(), 1571 extension_misc::kGmailAppId, 1572 kGmailLaunchURL)); 1573 EXPECT_EQ(3, model_->item_count()); 1574 1575 // Transfer the app to the other screen and switch users. 1576 manager->ShowWindowForUser(v1_app->browser()->window()->GetNativeWindow(), 1577 user2); 1578 EXPECT_EQ(3, model_->item_count()); 1579 SwitchActiveUser(profile2->GetProfileName()); 1580 EXPECT_EQ(2, model_->item_count()); 1581 } 1582 // After the app was destroyed, switch back. (which caused already a crash). 1583 SwitchActiveUser(profile()->GetProfileName()); 1584 1585 // Create the same app again - which was also causing the crash. 1586 EXPECT_EQ(2, model_->item_count()); 1587 { 1588 // Create a "windowed gmail app". 1589 scoped_ptr<V1App> v1_app(CreateRunningV1App( 1590 profile(), 1591 extension_misc::kGmailAppId, 1592 kGmailLaunchURL)); 1593 EXPECT_EQ(3, model_->item_count()); 1594 } 1595 SwitchActiveUser(profile2->GetProfileName()); 1596 EXPECT_EQ(2, model_->item_count()); 1597 } 1598 1599 // Check edge cases with multi profile V1 apps in the shelf. 1600 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, 1601 V1AppUpdateOnUserSwitchEdgecases2) { 1602 // Create a browser item in the LauncherController. 1603 InitLauncherController(); 1604 TestAppTabHelperImpl* app_tab_helper = new TestAppTabHelperImpl; 1605 SetAppTabHelper(app_tab_helper); 1606 1607 // First test: Create an app when the user is not active. 1608 std::string user2 = "user2"; 1609 TestingProfile* profile2 = CreateMultiUserProfile(user2); 1610 SwitchActiveUser(profile2->GetProfileName()); 1611 { 1612 // Create a "windowed gmail app". 1613 scoped_ptr<V1App> v1_app(CreateRunningV1App( 1614 profile(), extension_misc::kGmailAppId, gmail_url)); 1615 EXPECT_EQ(2, model_->item_count()); 1616 1617 // However - switching to the user should show it. 1618 SwitchActiveUser(profile()->GetProfileName()); 1619 EXPECT_EQ(3, model_->item_count()); 1620 1621 // Second test: Remove the app when the user is not active and see that it 1622 // works. 1623 SwitchActiveUser(profile2->GetProfileName()); 1624 EXPECT_EQ(2, model_->item_count()); 1625 v1_app.reset(); 1626 } 1627 EXPECT_EQ(2, model_->item_count()); 1628 SwitchActiveUser(profile()->GetProfileName()); 1629 EXPECT_EQ(2, model_->item_count()); 1630 SwitchActiveUser(profile2->GetProfileName()); 1631 EXPECT_EQ(2, model_->item_count()); 1632 } 1633 1634 // Check that activating an item which is on another user's desktop, will bring 1635 // it back. 1636 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, 1637 TestLauncherActivationPullsBackWindow) { 1638 // Create a browser item in the LauncherController. 1639 InitLauncherController(); 1640 chrome::MultiUserWindowManager* manager = 1641 chrome::MultiUserWindowManager::GetInstance(); 1642 1643 // Add two users to the window manager. 1644 std::string user2 = "user2"; 1645 TestingProfile* profile2 = CreateMultiUserProfile(user2); 1646 manager->AddUser(profile()); 1647 manager->AddUser(profile2); 1648 const std::string& current_user = 1649 multi_user_util::GetUserIDFromProfile(profile()); 1650 1651 // Create a browser window with a native window for the current user. 1652 scoped_ptr<BrowserWindow> browser_window(CreateTestBrowserWindow( 1653 Browser::CreateParams(profile(), chrome::HOST_DESKTOP_TYPE_ASH))); 1654 aura::Window* window = browser_window->GetNativeWindow(); 1655 manager->SetWindowOwner(window, current_user); 1656 1657 // Check that an activation of the window on its owner's desktop does not 1658 // change the visibility to another user. 1659 launcher_controller_->ActivateWindowOrMinimizeIfActive(browser_window.get(), 1660 false); 1661 EXPECT_TRUE(manager->IsWindowOnDesktopOfUser(window, current_user)); 1662 1663 // Transfer the window to another user's desktop and check that activating it 1664 // does pull it back to that user. 1665 manager->ShowWindowForUser(window, user2); 1666 EXPECT_FALSE(manager->IsWindowOnDesktopOfUser(window, current_user)); 1667 launcher_controller_->ActivateWindowOrMinimizeIfActive(browser_window.get(), 1668 false); 1669 EXPECT_TRUE(manager->IsWindowOnDesktopOfUser(window, current_user)); 1670 } 1671 #endif 1672 1673 // Check that lock -> pin -> unlock -> unpin does properly transition. 1674 TEST_F(ChromeLauncherControllerTest, CheckLockPinUnlockUnpin) { 1675 InitLauncherController(); 1676 // Model should only contain the browser shortcut and app list items. 1677 EXPECT_EQ(2, model_->item_count()); 1678 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1679 EXPECT_FALSE( 1680 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1681 1682 launcher_controller_->LockV1AppWithID(extension1_->id()); 1683 1684 EXPECT_EQ(3, model_->item_count()); 1685 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[2].type); 1686 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1687 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1688 1689 launcher_controller_->PinAppWithID(extension1_->id()); 1690 1691 EXPECT_EQ(3, model_->item_count()); 1692 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1693 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1694 EXPECT_FALSE( 1695 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1696 1697 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 1698 1699 EXPECT_EQ(3, model_->item_count()); 1700 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1701 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1702 EXPECT_FALSE( 1703 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 1704 1705 launcher_controller_->UnpinAppWithID(extension1_->id()); 1706 1707 EXPECT_EQ(2, model_->item_count()); 1708 } 1709 1710 // Check that a locked (windowed V1 application) will be properly converted 1711 // between locked and pinned when the order gets changed through a profile / 1712 // policy change. 1713 TEST_F(ChromeLauncherControllerTest, RestoreDefaultAndLockedAppsResyncOrder) { 1714 InitLauncherController(); 1715 base::ListValue policy_value0; 1716 InsertPrefValue(&policy_value0, 0, extension1_->id()); 1717 InsertPrefValue(&policy_value0, 1, extension3_->id()); 1718 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1719 policy_value0.DeepCopy()); 1720 // The shelf layout has always one static item at the beginning (App List). 1721 SetShelfChromeIconIndex(0); 1722 extension_service_->AddExtension(extension1_.get()); 1723 EXPECT_EQ("AppList, Chrome, App1, ", GetPinnedAppStatus()); 1724 extension_service_->AddExtension(extension2_.get()); 1725 // No new app icon will be generated. 1726 EXPECT_EQ("AppList, Chrome, App1, ", GetPinnedAppStatus()); 1727 // Add the app as locked app which will add it (un-pinned). 1728 launcher_controller_->LockV1AppWithID(extension2_->id()); 1729 EXPECT_EQ("AppList, Chrome, App1, app2, ", GetPinnedAppStatus()); 1730 extension_service_->AddExtension(extension3_.get()); 1731 EXPECT_EQ("AppList, Chrome, App1, App3, app2, ", GetPinnedAppStatus()); 1732 1733 // Now request to pin all items which should convert the locked item into a 1734 // pinned item. 1735 base::ListValue policy_value1; 1736 InsertPrefValue(&policy_value1, 0, extension3_->id()); 1737 InsertPrefValue(&policy_value1, 1, extension2_->id()); 1738 InsertPrefValue(&policy_value1, 2, extension1_->id()); 1739 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1740 policy_value1.DeepCopy()); 1741 EXPECT_EQ("AppList, Chrome, App3, App2, App1, ", GetPinnedAppStatus()); 1742 1743 // Going back to a status where there is no requirement for app 2 to be pinned 1744 // should convert it back to locked but not pinned and state. The position 1745 // is determined by the |ShelfModel|'s weight system. Since at the moment 1746 // the weight of a running app has the same as a shortcut, it will remain 1747 // where it is. Surely note ideal, but since the sync process can shift around 1748 // locations - as well as many other edge cases - this gets nearly impossible 1749 // to get right. 1750 // TODO(skuhne): Filed crbug.com/293761 to track of this. 1751 base::ListValue policy_value2; 1752 InsertPrefValue(&policy_value2, 0, extension3_->id()); 1753 InsertPrefValue(&policy_value2, 1, extension1_->id()); 1754 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1755 policy_value2.DeepCopy()); 1756 EXPECT_EQ("AppList, Chrome, App3, app2, App1, ", GetPinnedAppStatus()); 1757 1758 // Removing an item should simply close it and everything should shift. 1759 base::ListValue policy_value3; 1760 InsertPrefValue(&policy_value3, 0, extension3_->id()); 1761 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1762 policy_value3.DeepCopy()); 1763 EXPECT_EQ("AppList, Chrome, App3, app2, ", GetPinnedAppStatus()); 1764 } 1765 1766 // Check that a running and not pinned V2 application will be properly converted 1767 // between locked and pinned when the order gets changed through a profile / 1768 // policy change. 1769 TEST_F(ChromeLauncherControllerTest, 1770 RestoreDefaultAndRunningV2AppsResyncOrder) { 1771 InitLauncherController(); 1772 base::ListValue policy_value0; 1773 InsertPrefValue(&policy_value0, 0, extension1_->id()); 1774 InsertPrefValue(&policy_value0, 1, extension3_->id()); 1775 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1776 policy_value0.DeepCopy()); 1777 // The shelf layout has always one static item at the beginning (app List). 1778 SetShelfChromeIconIndex(0); 1779 extension_service_->AddExtension(extension1_.get()); 1780 EXPECT_EQ("AppList, Chrome, App1, ", GetPinnedAppStatus()); 1781 extension_service_->AddExtension(extension2_.get()); 1782 // No new app icon will be generated. 1783 EXPECT_EQ("AppList, Chrome, App1, ", GetPinnedAppStatus()); 1784 // Add the app as an unpinned but running V2 app. 1785 CreateRunningV2App(extension2_->id()); 1786 EXPECT_EQ("AppList, Chrome, App1, *app2, ", GetPinnedAppStatus()); 1787 extension_service_->AddExtension(extension3_.get()); 1788 EXPECT_EQ("AppList, Chrome, App1, App3, *app2, ", GetPinnedAppStatus()); 1789 1790 // Now request to pin all items which should convert the locked item into a 1791 // pinned item. 1792 base::ListValue policy_value1; 1793 InsertPrefValue(&policy_value1, 0, extension3_->id()); 1794 InsertPrefValue(&policy_value1, 1, extension2_->id()); 1795 InsertPrefValue(&policy_value1, 2, extension1_->id()); 1796 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1797 policy_value1.DeepCopy()); 1798 EXPECT_EQ("AppList, Chrome, App3, App2, App1, ", GetPinnedAppStatus()); 1799 1800 // Going back to a status where there is no requirement for app 2 to be pinned 1801 // should convert it back to running V2 app. Since the position is determined 1802 // by the |ShelfModel|'s weight system, it will be after last pinned item. 1803 base::ListValue policy_value2; 1804 InsertPrefValue(&policy_value2, 0, extension3_->id()); 1805 InsertPrefValue(&policy_value2, 1, extension1_->id()); 1806 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1807 policy_value2.DeepCopy()); 1808 EXPECT_EQ("AppList, Chrome, App3, App1, *app2, ", GetPinnedAppStatus()); 1809 1810 // Removing an item should simply close it and everything should shift. 1811 base::ListValue policy_value3; 1812 InsertPrefValue(&policy_value3, 0, extension3_->id()); 1813 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1814 policy_value3.DeepCopy()); 1815 EXPECT_EQ("AppList, Chrome, App3, *app2, ", GetPinnedAppStatus()); 1816 } 1817 1818 // Each user has a different set of applications pinned. Check that when 1819 // switching between the two users, the state gets properly set. 1820 TEST_F(ChromeLauncherControllerTest, UserSwitchIconRestore) { 1821 base::ListValue user_a; 1822 base::ListValue user_b; 1823 SetUpMultiUserScenario(&user_a, &user_b); 1824 // Show user 1. 1825 SetShelfChromeIconIndex(6); 1826 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1827 user_a.DeepCopy()); 1828 EXPECT_EQ("AppList, App1, App2, App3, App4, App5, App6, Chrome, ", 1829 GetPinnedAppStatus()); 1830 1831 // Show user 2. 1832 SetShelfChromeIconIndex(4); 1833 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1834 user_b.DeepCopy()); 1835 1836 EXPECT_EQ("AppList, App7, App8, Chrome, ", GetPinnedAppStatus()); 1837 1838 // Switch back to 1. 1839 SetShelfChromeIconIndex(8); 1840 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1841 user_a.DeepCopy()); 1842 EXPECT_EQ("AppList, App1, App2, App3, App4, App5, App6, Chrome, ", 1843 GetPinnedAppStatus()); 1844 1845 // Switch back to 2. 1846 SetShelfChromeIconIndex(4); 1847 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1848 user_b.DeepCopy()); 1849 EXPECT_EQ("AppList, App7, App8, Chrome, ", GetPinnedAppStatus()); 1850 } 1851 1852 // Each user has a different set of applications pinned, and one user has an 1853 // application running. Check that when switching between the two users, the 1854 // state gets properly set. 1855 TEST_F(ChromeLauncherControllerTest, UserSwitchIconRestoreWithRunningV2App) { 1856 base::ListValue user_a; 1857 base::ListValue user_b; 1858 SetUpMultiUserScenario(&user_a, &user_b); 1859 1860 // Run App1 and assume that it is a V2 app. 1861 CreateRunningV2App(extension1_->id()); 1862 1863 // Show user 1. 1864 SetShelfChromeIconIndex(6); 1865 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1866 user_a.DeepCopy()); 1867 EXPECT_EQ("AppList, App1, App2, App3, App4, App5, App6, Chrome, ", 1868 GetPinnedAppStatus()); 1869 1870 // Show user 2. 1871 SetShelfChromeIconIndex(4); 1872 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1873 user_b.DeepCopy()); 1874 1875 EXPECT_EQ("AppList, App7, App8, Chrome, *app1, ", GetPinnedAppStatus()); 1876 1877 // Switch back to 1. 1878 SetShelfChromeIconIndex(8); 1879 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1880 user_a.DeepCopy()); 1881 EXPECT_EQ("AppList, App1, App2, App3, App4, App5, App6, Chrome, ", 1882 GetPinnedAppStatus()); 1883 1884 // Switch back to 2. 1885 SetShelfChromeIconIndex(4); 1886 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1887 user_b.DeepCopy()); 1888 EXPECT_EQ("AppList, App7, App8, Chrome, *app1, ", GetPinnedAppStatus()); 1889 } 1890 1891 // Each user has a different set of applications pinned, and one user has an 1892 // application running. The chrome icon is not the last item in the list. 1893 // Check that when switching between the two users, the state gets properly set. 1894 // There was once a bug associated with this. 1895 TEST_F(ChromeLauncherControllerTest, 1896 UserSwitchIconRestoreWithRunningV2AppChromeInMiddle) { 1897 base::ListValue user_a; 1898 base::ListValue user_b; 1899 SetUpMultiUserScenario(&user_a, &user_b); 1900 1901 // Run App1 and assume that it is a V2 app. 1902 CreateRunningV2App(extension1_->id()); 1903 1904 // Show user 1. 1905 SetShelfChromeIconIndex(5); 1906 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1907 user_a.DeepCopy()); 1908 EXPECT_EQ("AppList, App1, App2, App3, App4, App5, Chrome, App6, ", 1909 GetPinnedAppStatus()); 1910 1911 // Show user 2. 1912 SetShelfChromeIconIndex(4); 1913 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1914 user_b.DeepCopy()); 1915 1916 EXPECT_EQ("AppList, App7, App8, Chrome, *app1, ", GetPinnedAppStatus()); 1917 1918 // Switch back to 1. 1919 SetShelfChromeIconIndex(5); 1920 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1921 user_a.DeepCopy()); 1922 EXPECT_EQ("AppList, App1, App2, App3, App4, App5, Chrome, App6, ", 1923 GetPinnedAppStatus()); 1924 } 1925 1926 TEST_F(ChromeLauncherControllerTest, Policy) { 1927 extension_service_->AddExtension(extension1_.get()); 1928 extension_service_->AddExtension(extension3_.get()); 1929 1930 base::ListValue policy_value; 1931 InsertPrefValue(&policy_value, 0, extension1_->id()); 1932 InsertPrefValue(&policy_value, 1, extension2_->id()); 1933 profile()->GetTestingPrefService()->SetManagedPref(prefs::kPinnedLauncherApps, 1934 policy_value.DeepCopy()); 1935 1936 // Only |extension1_| should get pinned. |extension2_| is specified but not 1937 // installed, and |extension3_| is part of the default set, but that shouldn't 1938 // take effect when the policy override is in place. 1939 InitLauncherController(); 1940 EXPECT_EQ(3, model_->item_count()); 1941 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1942 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1943 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 1944 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1945 1946 // Installing |extension2_| should add it to the launcher. 1947 extension_service_->AddExtension(extension2_.get()); 1948 EXPECT_EQ(4, model_->item_count()); 1949 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1950 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[3].type); 1951 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 1952 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); 1953 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1954 1955 // Removing |extension1_| from the policy should be reflected in the launcher. 1956 policy_value.Remove(0, NULL); 1957 profile()->GetTestingPrefService()->SetManagedPref(prefs::kPinnedLauncherApps, 1958 policy_value.DeepCopy()); 1959 EXPECT_EQ(3, model_->item_count()); 1960 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 1961 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 1962 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); 1963 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1964 } 1965 1966 TEST_F(ChromeLauncherControllerTest, UnpinWithUninstall) { 1967 extension_service_->AddExtension(extension3_.get()); 1968 extension_service_->AddExtension(extension4_.get()); 1969 1970 InitLauncherController(); 1971 1972 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 1973 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id())); 1974 1975 extension_service_->UnloadExtension(extension3_->id(), 1976 UnloadedExtensionInfo::REASON_UNINSTALL); 1977 1978 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 1979 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id())); 1980 } 1981 1982 TEST_F(ChromeLauncherControllerTest, PrefUpdates) { 1983 extension_service_->AddExtension(extension2_.get()); 1984 extension_service_->AddExtension(extension3_.get()); 1985 extension_service_->AddExtension(extension4_.get()); 1986 1987 InitLauncherController(); 1988 1989 std::vector<std::string> expected_launchers; 1990 std::vector<std::string> actual_launchers; 1991 base::ListValue pref_value; 1992 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 1993 pref_value.DeepCopy()); 1994 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 1995 EXPECT_EQ(expected_launchers, actual_launchers); 1996 1997 // Unavailable extensions don't create launcher items. 1998 InsertPrefValue(&pref_value, 0, extension1_->id()); 1999 InsertPrefValue(&pref_value, 1, extension2_->id()); 2000 InsertPrefValue(&pref_value, 2, extension4_->id()); 2001 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 2002 pref_value.DeepCopy()); 2003 expected_launchers.push_back(extension2_->id()); 2004 expected_launchers.push_back(extension4_->id()); 2005 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 2006 EXPECT_EQ(expected_launchers, actual_launchers); 2007 2008 // Redundant pref entries show up only once. 2009 InsertPrefValue(&pref_value, 2, extension3_->id()); 2010 InsertPrefValue(&pref_value, 2, extension3_->id()); 2011 InsertPrefValue(&pref_value, 5, extension3_->id()); 2012 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 2013 pref_value.DeepCopy()); 2014 expected_launchers.insert(expected_launchers.begin() + 1, extension3_->id()); 2015 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 2016 EXPECT_EQ(expected_launchers, actual_launchers); 2017 2018 // Order changes are reflected correctly. 2019 pref_value.Clear(); 2020 InsertPrefValue(&pref_value, 0, extension4_->id()); 2021 InsertPrefValue(&pref_value, 1, extension3_->id()); 2022 InsertPrefValue(&pref_value, 2, extension2_->id()); 2023 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 2024 pref_value.DeepCopy()); 2025 std::reverse(expected_launchers.begin(), expected_launchers.end()); 2026 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 2027 EXPECT_EQ(expected_launchers, actual_launchers); 2028 2029 // Clearing works. 2030 pref_value.Clear(); 2031 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 2032 pref_value.DeepCopy()); 2033 expected_launchers.clear(); 2034 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 2035 EXPECT_EQ(expected_launchers, actual_launchers); 2036 } 2037 2038 TEST_F(ChromeLauncherControllerTest, PendingInsertionOrder) { 2039 extension_service_->AddExtension(extension1_.get()); 2040 extension_service_->AddExtension(extension3_.get()); 2041 2042 InitLauncherController(); 2043 2044 base::ListValue pref_value; 2045 InsertPrefValue(&pref_value, 0, extension1_->id()); 2046 InsertPrefValue(&pref_value, 1, extension2_->id()); 2047 InsertPrefValue(&pref_value, 2, extension3_->id()); 2048 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 2049 pref_value.DeepCopy()); 2050 2051 std::vector<std::string> expected_launchers; 2052 expected_launchers.push_back(extension1_->id()); 2053 expected_launchers.push_back(extension3_->id()); 2054 std::vector<std::string> actual_launchers; 2055 2056 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 2057 EXPECT_EQ(expected_launchers, actual_launchers); 2058 2059 // Install |extension2| and verify it shows up between the other two. 2060 extension_service_->AddExtension(extension2_.get()); 2061 expected_launchers.insert(expected_launchers.begin() + 1, extension2_->id()); 2062 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 2063 EXPECT_EQ(expected_launchers, actual_launchers); 2064 } 2065 2066 // Checks the created menus and menu lists for correctness. It uses the given 2067 // |controller| to create the objects for the given |item| and checks the 2068 // found item count against the |expected_items|. The |title| list contains the 2069 // menu titles in the order of their appearance in the menu (not including the 2070 // application name). 2071 bool CheckMenuCreation(ChromeLauncherController* controller, 2072 const ash::LauncherItem& item, 2073 size_t expected_items, 2074 base::string16 title[], 2075 bool is_browser) { 2076 ChromeLauncherAppMenuItems items = controller->GetApplicationList(item, 0); 2077 // A new behavior has been added: Only show menus if there is at least one 2078 // item available. 2079 if (expected_items < 1 && is_browser) { 2080 EXPECT_EQ(0u, items.size()); 2081 return items.size() == 0; 2082 } 2083 // There should be one item in there: The title. 2084 EXPECT_EQ(expected_items + 1, items.size()); 2085 EXPECT_FALSE(items[0]->IsEnabled()); 2086 for (size_t i = 0; i < expected_items; i++) { 2087 EXPECT_EQ(title[i], items[1 + i]->title()); 2088 // Check that the first real item has a leading separator. 2089 if (i == 1) 2090 EXPECT_TRUE(items[i]->HasLeadingSeparator()); 2091 else 2092 EXPECT_FALSE(items[i]->HasLeadingSeparator()); 2093 } 2094 2095 scoped_ptr<ash::ShelfMenuModel> menu(new LauncherApplicationMenuItemModel( 2096 controller->GetApplicationList(item, 0))); 2097 // The first element in the menu is a spacing separator. On some systems 2098 // (e.g. Windows) such things do not exist. As such we check the existence 2099 // and adjust dynamically. 2100 int first_item = menu->GetTypeAt(0) == ui::MenuModel::TYPE_SEPARATOR ? 1 : 0; 2101 int expected_menu_items = first_item + 2102 (expected_items ? (expected_items + 3) : 2); 2103 EXPECT_EQ(expected_menu_items, menu->GetItemCount()); 2104 EXPECT_FALSE(menu->IsEnabledAt(first_item)); 2105 if (expected_items) { 2106 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, 2107 menu->GetTypeAt(first_item + 1)); 2108 } 2109 return items.size() == expected_items + 1; 2110 } 2111 2112 // Check that browsers get reflected correctly in the launcher menu. 2113 TEST_F(ChromeLauncherControllerTest, BrowserMenuGeneration) { 2114 EXPECT_EQ(1U, chrome::GetTotalBrowserCount()); 2115 chrome::NewTab(browser()); 2116 2117 InitLauncherController(); 2118 2119 // Check that the browser list is empty at this time. 2120 ash::LauncherItem item_browser; 2121 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; 2122 item_browser.id = 2123 launcher_controller_->GetLauncherIDForAppID(extension_misc::kChromeAppId); 2124 EXPECT_TRUE(CheckMenuCreation( 2125 launcher_controller_.get(), item_browser, 0, NULL, true)); 2126 2127 // Now make the created browser() visible by adding it to the active browser 2128 // list. 2129 BrowserList::SetLastActive(browser()); 2130 base::string16 title1 = ASCIIToUTF16("Test1"); 2131 NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1); 2132 base::string16 one_menu_item[] = { title1 }; 2133 2134 EXPECT_TRUE(CheckMenuCreation( 2135 launcher_controller_.get(), item_browser, 1, one_menu_item, true)); 2136 2137 // Create one more browser/window and check that one more was added. 2138 Browser::CreateParams ash_params(profile(), chrome::HOST_DESKTOP_TYPE_ASH); 2139 scoped_ptr<Browser> browser2( 2140 chrome::CreateBrowserWithTestWindowForParams(&ash_params)); 2141 chrome::NewTab(browser2.get()); 2142 BrowserList::SetLastActive(browser2.get()); 2143 base::string16 title2 = ASCIIToUTF16("Test2"); 2144 NavigateAndCommitActiveTabWithTitle(browser2.get(), GURL("http://test2"), 2145 title2); 2146 2147 // Check that the list contains now two entries - make furthermore sure that 2148 // the active item is the first entry. 2149 base::string16 two_menu_items[] = {title1, title2}; 2150 EXPECT_TRUE(CheckMenuCreation( 2151 launcher_controller_.get(), item_browser, 2, two_menu_items, true)); 2152 2153 // Apparently we have to close all tabs we have. 2154 chrome::CloseTab(browser2.get()); 2155 } 2156 2157 #if defined(OS_CHROMEOS) 2158 // Check the multi profile case where only user related browsers should show 2159 // up. 2160 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, 2161 BrowserMenuGenerationTwoUsers) { 2162 // Create a browser item in the LauncherController. 2163 InitLauncherController(); 2164 2165 ash::LauncherItem item_browser; 2166 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; 2167 item_browser.id = 2168 launcher_controller_->GetLauncherIDForAppID(extension_misc::kChromeAppId); 2169 2170 // Check that the menu is empty. 2171 chrome::NewTab(browser()); 2172 EXPECT_TRUE(CheckMenuCreation( 2173 launcher_controller_.get(), item_browser, 0, NULL, true)); 2174 2175 // Show the created |browser()| by adding it to the active browser list. 2176 BrowserList::SetLastActive(browser()); 2177 base::string16 title1 = ASCIIToUTF16("Test1"); 2178 NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1); 2179 base::string16 one_menu_item1[] = { title1 }; 2180 EXPECT_TRUE(CheckMenuCreation( 2181 launcher_controller_.get(), item_browser, 1, one_menu_item1, true)); 2182 2183 // Create a browser for another user and check that it is not included in the 2184 // users running browser list. 2185 std::string user2 = "user2"; 2186 TestingProfile* profile2 = CreateMultiUserProfile(user2); 2187 scoped_ptr<Browser> browser2( 2188 CreateBrowserAndTabWithProfile(profile2, user2, "http://test2")); 2189 base::string16 one_menu_item2[] = { ASCIIToUTF16(user2) }; 2190 EXPECT_TRUE(CheckMenuCreation( 2191 launcher_controller_.get(), item_browser, 1, one_menu_item1, true)); 2192 2193 // Switch to the other user and make sure that only that browser window gets 2194 // shown. 2195 SwitchActiveUser(profile2->GetProfileName()); 2196 EXPECT_TRUE(CheckMenuCreation( 2197 launcher_controller_.get(), item_browser, 1, one_menu_item2, true)); 2198 2199 // Transferred browsers of other users should not show up in the list. 2200 chrome::MultiUserWindowManager::GetInstance()->ShowWindowForUser( 2201 browser()->window()->GetNativeWindow(), 2202 user2); 2203 EXPECT_TRUE(CheckMenuCreation( 2204 launcher_controller_.get(), item_browser, 1, one_menu_item2, true)); 2205 2206 chrome::CloseTab(browser2.get()); 2207 } 2208 #endif // defined(OS_CHROMEOS) 2209 2210 // Check that V1 apps are correctly reflected in the launcher menu using the 2211 // refocus logic. 2212 // Note that the extension matching logic is tested by the extension system 2213 // and does not need a separate test here. 2214 TEST_F(ChromeLauncherControllerTest, V1AppMenuGeneration) { 2215 EXPECT_EQ(1U, chrome::GetTotalBrowserCount()); 2216 EXPECT_EQ(0, browser()->tab_strip_model()->count()); 2217 2218 InitLauncherControllerWithBrowser(); 2219 2220 // Model should only contain the browser shortcut and app list items. 2221 EXPECT_EQ(2, model_->item_count()); 2222 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 2223 2224 // Installing |extension3_| adds it to the launcher. 2225 ash::LauncherID gmail_id = model_->next_id(); 2226 extension_service_->AddExtension(extension3_.get()); 2227 EXPECT_EQ(3, model_->item_count()); 2228 int gmail_index = model_->ItemIndexByID(gmail_id); 2229 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type); 2230 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 2231 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); 2232 2233 // Check the menu content. 2234 ash::LauncherItem item_browser; 2235 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; 2236 item_browser.id = 2237 launcher_controller_->GetLauncherIDForAppID(extension_misc::kChromeAppId); 2238 2239 ash::LauncherItem item_gmail; 2240 item_gmail.type = ash::TYPE_APP_SHORTCUT; 2241 item_gmail.id = gmail_id; 2242 EXPECT_TRUE(CheckMenuCreation( 2243 launcher_controller_.get(), item_gmail, 0, NULL, false)); 2244 2245 // Set the gmail URL to a new tab. 2246 base::string16 title1 = ASCIIToUTF16("Test1"); 2247 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); 2248 2249 base::string16 one_menu_item[] = { title1 }; 2250 EXPECT_TRUE(CheckMenuCreation( 2251 launcher_controller_.get(), item_gmail, 1, one_menu_item, false)); 2252 2253 // Create one empty tab. 2254 chrome::NewTab(browser()); 2255 base::string16 title2 = ASCIIToUTF16("Test2"); 2256 NavigateAndCommitActiveTabWithTitle( 2257 browser(), 2258 GURL("https://bla"), 2259 title2); 2260 2261 // and another one with another gmail instance. 2262 chrome::NewTab(browser()); 2263 base::string16 title3 = ASCIIToUTF16("Test3"); 2264 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title3); 2265 base::string16 two_menu_items[] = {title1, title3}; 2266 EXPECT_TRUE(CheckMenuCreation( 2267 launcher_controller_.get(), item_gmail, 2, two_menu_items, false)); 2268 2269 // Even though the item is in the V1 app list, it should also be in the 2270 // browser list. 2271 base::string16 browser_menu_item[] = {title3}; 2272 EXPECT_TRUE(CheckMenuCreation( 2273 launcher_controller_.get(), item_browser, 1, browser_menu_item, false)); 2274 2275 // Test that closing of (all) the item(s) does work (and all menus get 2276 // updated properly). 2277 launcher_controller_->Close(item_gmail.id); 2278 2279 EXPECT_TRUE(CheckMenuCreation( 2280 launcher_controller_.get(), item_gmail, 0, NULL, false)); 2281 base::string16 browser_menu_item2[] = { title2 }; 2282 EXPECT_TRUE(CheckMenuCreation( 2283 launcher_controller_.get(), item_browser, 1, browser_menu_item2, false)); 2284 } 2285 2286 #if defined(OS_CHROMEOS) 2287 // Check the multi profile case where only user related apps should show up. 2288 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, 2289 V1AppMenuGenerationTwoUsers) { 2290 // Create a browser item in the LauncherController. 2291 InitLauncherController(); 2292 chrome::NewTab(browser()); 2293 2294 // Installing |extension3_| adds it to the launcher. 2295 ash::LauncherID gmail_id = model_->next_id(); 2296 extension_service_->AddExtension(extension3_.get()); 2297 EXPECT_EQ(3, model_->item_count()); 2298 int gmail_index = model_->ItemIndexByID(gmail_id); 2299 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type); 2300 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 2301 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); 2302 2303 // Check the menu content. 2304 ash::LauncherItem item_browser; 2305 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; 2306 item_browser.id = 2307 launcher_controller_->GetLauncherIDForAppID(extension_misc::kChromeAppId); 2308 2309 ash::LauncherItem item_gmail; 2310 item_gmail.type = ash::TYPE_APP_SHORTCUT; 2311 item_gmail.id = gmail_id; 2312 EXPECT_TRUE(CheckMenuCreation( 2313 launcher_controller_.get(), item_gmail, 0, NULL, false)); 2314 2315 // Set the gmail URL to a new tab. 2316 base::string16 title1 = ASCIIToUTF16("Test1"); 2317 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); 2318 2319 base::string16 one_menu_item[] = { title1 }; 2320 EXPECT_TRUE(CheckMenuCreation( 2321 launcher_controller_.get(), item_gmail, 1, one_menu_item, false)); 2322 2323 // Create a second profile and switch to that user. 2324 std::string user2 = "user2"; 2325 TestingProfile* profile2 = CreateMultiUserProfile(user2); 2326 SwitchActiveUser(profile2->GetProfileName()); 2327 2328 // No item should have content yet. 2329 EXPECT_TRUE(CheckMenuCreation( 2330 launcher_controller_.get(), item_browser, 0, NULL, true)); 2331 EXPECT_TRUE(CheckMenuCreation( 2332 launcher_controller_.get(), item_gmail, 0, NULL, false)); 2333 2334 // Transfer the browser of the first user - it should still not show up. 2335 chrome::MultiUserWindowManager::GetInstance()->ShowWindowForUser( 2336 browser()->window()->GetNativeWindow(), 2337 user2); 2338 2339 EXPECT_TRUE(CheckMenuCreation( 2340 launcher_controller_.get(), item_browser, 0, NULL, true)); 2341 EXPECT_TRUE(CheckMenuCreation( 2342 launcher_controller_.get(), item_gmail, 0, NULL, false)); 2343 } 2344 2345 // Check that V2 applications are creating items properly in the launcher when 2346 // instantiated by the current user. 2347 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, 2348 V2AppHandlingTwoUsers) { 2349 InitLauncherController(); 2350 // We need to create a dummy views delegate to be able to create a V2 app. 2351 scoped_ptr<TestViewsDelegateForAppTest> views_delegate( 2352 new TestViewsDelegateForAppTest()); 2353 // Create a profile for our second user (will be destroyed by the framework). 2354 TestingProfile* profile2 = CreateMultiUserProfile("user2"); 2355 // Check that there is a browser and a app launcher. 2356 EXPECT_EQ(2, model_->item_count()); 2357 2358 // Add a v2 app. 2359 V2App v2_app(profile(), extension1_); 2360 EXPECT_EQ(3, model_->item_count()); 2361 2362 // After switching users the item should go away. 2363 SwitchActiveUser(profile2->GetProfileName()); 2364 EXPECT_EQ(2, model_->item_count()); 2365 2366 // And it should come back when switching back. 2367 SwitchActiveUser(profile()->GetProfileName()); 2368 EXPECT_EQ(3, model_->item_count()); 2369 } 2370 2371 // Check that V2 applications are creating items properly in edge cases: 2372 // a background user creates a V2 app, gets active and inactive again and then 2373 // deletes the app. 2374 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest, 2375 V2AppHandlingTwoUsersEdgeCases) { 2376 InitLauncherController(); 2377 // We need to create a dummy views delegate to be able to create a V2 app. 2378 scoped_ptr<TestViewsDelegateForAppTest> views_delegate( 2379 new TestViewsDelegateForAppTest()); 2380 // Create a profile for our second user (will be destroyed by the framework). 2381 TestingProfile* profile2 = CreateMultiUserProfile("user2"); 2382 // Check that there is a browser and a app launcher. 2383 EXPECT_EQ(2, model_->item_count()); 2384 2385 // Switch to an inactive user. 2386 SwitchActiveUser(profile2->GetProfileName()); 2387 EXPECT_EQ(2, model_->item_count()); 2388 2389 // Add the v2 app to the inactive user and check that no item was added to 2390 // the launcher. 2391 { 2392 V2App v2_app(profile(), extension1_); 2393 EXPECT_EQ(2, model_->item_count()); 2394 2395 // Switch to the primary user and check that the item is shown. 2396 SwitchActiveUser(profile()->GetProfileName()); 2397 EXPECT_EQ(3, model_->item_count()); 2398 2399 // Switch to the second user and check that the item goes away - even if the 2400 // item gets closed. 2401 SwitchActiveUser(profile2->GetProfileName()); 2402 EXPECT_EQ(2, model_->item_count()); 2403 } 2404 2405 // After the application was killed there should be still 2 items. 2406 EXPECT_EQ(2, model_->item_count()); 2407 2408 // Switching then back to the default user should not show the additional item 2409 // anymore. 2410 SwitchActiveUser(profile()->GetProfileName()); 2411 EXPECT_EQ(2, model_->item_count()); 2412 } 2413 #endif // defined(OS_CHROMEOS) 2414 2415 // Checks that the generated menu list properly activates items. 2416 TEST_F(ChromeLauncherControllerTest, V1AppMenuExecution) { 2417 InitLauncherControllerWithBrowser(); 2418 2419 // Add |extension3_| to the launcher and add two items. 2420 GURL gmail = GURL("https://mail.google.com/mail/u"); 2421 ash::LauncherID gmail_id = model_->next_id(); 2422 extension_service_->AddExtension(extension3_.get()); 2423 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); 2424 base::string16 title1 = ASCIIToUTF16("Test1"); 2425 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); 2426 chrome::NewTab(browser()); 2427 base::string16 title2 = ASCIIToUTF16("Test2"); 2428 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title2); 2429 2430 // Check that the menu is properly set. 2431 ash::LauncherItem item_gmail; 2432 item_gmail.type = ash::TYPE_APP_SHORTCUT; 2433 item_gmail.id = gmail_id; 2434 base::string16 two_menu_items[] = {title1, title2}; 2435 EXPECT_TRUE(CheckMenuCreation( 2436 launcher_controller_.get(), item_gmail, 2, two_menu_items, false)); 2437 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); 2438 // Execute the second item in the list (which shouldn't do anything since that 2439 // item is per definition already the active tab). 2440 { 2441 scoped_ptr<ash::ShelfMenuModel> menu(new LauncherApplicationMenuItemModel( 2442 launcher_controller_->GetApplicationList(item_gmail, 0))); 2443 // The first element in the menu is a spacing separator. On some systems 2444 // (e.g. Windows) such things do not exist. As such we check the existence 2445 // and adjust dynamically. 2446 int first_item = 2447 (menu->GetTypeAt(0) == ui::MenuModel::TYPE_SEPARATOR) ? 1 : 0; 2448 menu->ActivatedAt(first_item + 3); 2449 } 2450 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); 2451 2452 // Execute the first item. 2453 { 2454 scoped_ptr<ash::ShelfMenuModel> menu(new LauncherApplicationMenuItemModel( 2455 launcher_controller_->GetApplicationList(item_gmail, 0))); 2456 int first_item = 2457 (menu->GetTypeAt(0) == ui::MenuModel::TYPE_SEPARATOR) ? 1 : 0; 2458 menu->ActivatedAt(first_item + 2); 2459 } 2460 // Now the active tab should be the second item. 2461 EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); 2462 } 2463 2464 // Checks that the generated menu list properly deletes items. 2465 TEST_F(ChromeLauncherControllerTest, V1AppMenuDeletionExecution) { 2466 InitLauncherControllerWithBrowser(); 2467 2468 // Add |extension3_| to the launcher and add two items. 2469 GURL gmail = GURL("https://mail.google.com/mail/u"); 2470 ash::LauncherID gmail_id = model_->next_id(); 2471 extension_service_->AddExtension(extension3_.get()); 2472 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); 2473 base::string16 title1 = ASCIIToUTF16("Test1"); 2474 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); 2475 chrome::NewTab(browser()); 2476 base::string16 title2 = ASCIIToUTF16("Test2"); 2477 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title2); 2478 2479 // Check that the menu is properly set. 2480 ash::LauncherItem item_gmail; 2481 item_gmail.type = ash::TYPE_APP_SHORTCUT; 2482 item_gmail.id = gmail_id; 2483 base::string16 two_menu_items[] = {title1, title2}; 2484 EXPECT_TRUE(CheckMenuCreation( 2485 launcher_controller_.get(), item_gmail, 2, two_menu_items, false)); 2486 2487 int tabs = browser()->tab_strip_model()->count(); 2488 // Activate the proper tab through the menu item. 2489 { 2490 ChromeLauncherAppMenuItems items = 2491 launcher_controller_->GetApplicationList(item_gmail, 0); 2492 items[1]->Execute(0); 2493 EXPECT_EQ(tabs, browser()->tab_strip_model()->count()); 2494 } 2495 2496 // Delete one tab through the menu item. 2497 { 2498 ChromeLauncherAppMenuItems items = 2499 launcher_controller_->GetApplicationList(item_gmail, 0); 2500 items[1]->Execute(ui::EF_SHIFT_DOWN); 2501 EXPECT_EQ(--tabs, browser()->tab_strip_model()->count()); 2502 } 2503 } 2504 2505 // Tests that panels create launcher items correctly 2506 TEST_F(ChromeLauncherControllerTest, AppPanels) { 2507 InitLauncherControllerWithBrowser(); 2508 // App list and Browser shortcut LauncherItems are added. 2509 EXPECT_EQ(2, model_observer_->added()); 2510 2511 TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl(); 2512 SetAppIconLoader(app_icon_loader); 2513 2514 // Test adding an app panel 2515 std::string app_id = extension1_->id(); 2516 ShellWindowLauncherItemController* app_panel_controller = 2517 new ShellWindowLauncherItemController( 2518 LauncherItemController::TYPE_APP_PANEL, 2519 "id", 2520 app_id, 2521 launcher_controller_.get()); 2522 ash::LauncherID launcher_id1 = launcher_controller_->CreateAppLauncherItem( 2523 app_panel_controller, app_id, ash::STATUS_RUNNING); 2524 int panel_index = model_observer_->last_index(); 2525 EXPECT_EQ(3, model_observer_->added()); 2526 EXPECT_EQ(0, model_observer_->changed()); 2527 EXPECT_EQ(1, app_icon_loader->fetch_count()); 2528 model_observer_->clear_counts(); 2529 2530 // App panels should have a separate identifier than the app id 2531 EXPECT_EQ(0, launcher_controller_->GetLauncherIDForAppID(app_id)); 2532 2533 // Setting the app image image should not change the panel if it set its icon 2534 app_panel_controller->set_image_set_by_controller(true); 2535 gfx::ImageSkia image; 2536 launcher_controller_->SetAppImage(app_id, image); 2537 EXPECT_EQ(0, model_observer_->changed()); 2538 model_observer_->clear_counts(); 2539 2540 // Add a second app panel and verify that it get the same index as the first 2541 // one had, being added to the left of the existing panel. 2542 ShellWindowLauncherItemController* app_panel_controller2 = 2543 new ShellWindowLauncherItemController( 2544 LauncherItemController::TYPE_APP_PANEL, 2545 "id", 2546 app_id, 2547 launcher_controller_.get()); 2548 2549 ash::LauncherID launcher_id2 = launcher_controller_->CreateAppLauncherItem( 2550 app_panel_controller2, app_id, ash::STATUS_RUNNING); 2551 EXPECT_EQ(panel_index, model_observer_->last_index()); 2552 EXPECT_EQ(1, model_observer_->added()); 2553 model_observer_->clear_counts(); 2554 2555 launcher_controller_->CloseLauncherItem(launcher_id2); 2556 launcher_controller_->CloseLauncherItem(launcher_id1); 2557 EXPECT_EQ(2, model_observer_->removed()); 2558 } 2559 2560 // Tests that the Gmail extension matches more then the app itself claims with 2561 // the manifest file. 2562 TEST_F(ChromeLauncherControllerTest, GmailMatching) { 2563 InitLauncherControllerWithBrowser(); 2564 2565 // Create a Gmail browser tab. 2566 chrome::NewTab(browser()); 2567 base::string16 title = ASCIIToUTF16("Test"); 2568 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title); 2569 content::WebContents* content = 2570 browser()->tab_strip_model()->GetActiveWebContents(); 2571 2572 // Check that the launcher controller does not recognize the running app. 2573 EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content)); 2574 2575 // Installing |extension3_| adds it to the launcher. 2576 ash::LauncherID gmail_id = model_->next_id(); 2577 extension_service_->AddExtension(extension3_.get()); 2578 EXPECT_EQ(3, model_->item_count()); 2579 int gmail_index = model_->ItemIndexByID(gmail_id); 2580 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type); 2581 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 2582 2583 // Check that it is now handled. 2584 EXPECT_TRUE(launcher_controller_->ContentCanBeHandledByGmailApp(content)); 2585 2586 // Check also that the app has detected that properly. 2587 ash::LauncherItem item_gmail; 2588 item_gmail.type = ash::TYPE_APP_SHORTCUT; 2589 item_gmail.id = gmail_id; 2590 EXPECT_EQ(2U, launcher_controller_->GetApplicationList(item_gmail, 0).size()); 2591 } 2592 2593 // Tests that the Gmail extension does not match the offline verison. 2594 TEST_F(ChromeLauncherControllerTest, GmailOfflineMatching) { 2595 InitLauncherControllerWithBrowser(); 2596 2597 // Create a Gmail browser tab. 2598 chrome::NewTab(browser()); 2599 base::string16 title = ASCIIToUTF16("Test"); 2600 NavigateAndCommitActiveTabWithTitle(browser(), 2601 GURL(offline_gmail_url), 2602 title); 2603 content::WebContents* content = 2604 browser()->tab_strip_model()->GetActiveWebContents(); 2605 2606 // Installing |extension3_| adds it to the launcher. 2607 ash::LauncherID gmail_id = model_->next_id(); 2608 extension_service_->AddExtension(extension3_.get()); 2609 EXPECT_EQ(3, model_->item_count()); 2610 int gmail_index = model_->ItemIndexByID(gmail_id); 2611 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type); 2612 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 2613 2614 // The content should not be able to be handled by the app. 2615 EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content)); 2616 } 2617 2618 // Verify that the launcher item positions are persisted and restored. 2619 TEST_F(ChromeLauncherControllerTest, PersistLauncherItemPositions) { 2620 InitLauncherController(); 2621 2622 TestAppTabHelperImpl* app_tab_helper = new TestAppTabHelperImpl; 2623 SetAppTabHelper(app_tab_helper); 2624 2625 EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[0].type); 2626 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[1].type); 2627 2628 TabStripModel* tab_strip_model = browser()->tab_strip_model(); 2629 EXPECT_EQ(0, tab_strip_model->count()); 2630 chrome::NewTab(browser()); 2631 chrome::NewTab(browser()); 2632 EXPECT_EQ(2, tab_strip_model->count()); 2633 app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); 2634 app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(1), "2"); 2635 2636 EXPECT_FALSE(launcher_controller_->IsAppPinned("1")); 2637 launcher_controller_->PinAppWithID("1"); 2638 EXPECT_TRUE(launcher_controller_->IsAppPinned("1")); 2639 launcher_controller_->PinAppWithID("2"); 2640 2641 EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[0].type); 2642 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[1].type); 2643 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 2644 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[3].type); 2645 2646 // Move browser shortcut item from index 1 to index 3. 2647 model_->Move(1, 3); 2648 EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[0].type); 2649 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 2650 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 2651 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[3].type); 2652 2653 launcher_controller_.reset(); 2654 if (!ash::Shell::HasInstance()) { 2655 delete item_delegate_manager_; 2656 } else { 2657 // Clear already registered ShelfItemDelegate. 2658 ash::test::ShelfItemDelegateManagerTestAPI test(item_delegate_manager_); 2659 test.RemoveAllShelfItemDelegateForTest(); 2660 } 2661 model_.reset(new ash::ShelfModel); 2662 2663 AddAppListLauncherItem(); 2664 launcher_controller_.reset( 2665 ChromeLauncherController::CreateInstance(profile(), model_.get())); 2666 app_tab_helper = new TestAppTabHelperImpl; 2667 app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); 2668 app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(1), "2"); 2669 SetAppTabHelper(app_tab_helper); 2670 if (!ash::Shell::HasInstance()) { 2671 item_delegate_manager_ = new ash::ShelfItemDelegateManager(model_.get()); 2672 SetShelfItemDelegateManager(item_delegate_manager_); 2673 } 2674 launcher_controller_->Init(); 2675 2676 // Check LauncherItems are restored after resetting ChromeLauncherController. 2677 EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[0].type); 2678 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 2679 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 2680 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[3].type); 2681 } 2682 2683 // Verifies pinned apps are persisted and restored. 2684 TEST_F(ChromeLauncherControllerTest, PersistPinned) { 2685 InitLauncherControllerWithBrowser(); 2686 size_t initial_size = model_->items().size(); 2687 2688 TabStripModel* tab_strip_model = browser()->tab_strip_model(); 2689 EXPECT_EQ(1, tab_strip_model->count()); 2690 2691 TestAppTabHelperImpl* app_tab_helper = new TestAppTabHelperImpl; 2692 app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); 2693 SetAppTabHelper(app_tab_helper); 2694 2695 TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl; 2696 SetAppIconLoader(app_icon_loader); 2697 EXPECT_EQ(0, app_icon_loader->fetch_count()); 2698 2699 launcher_controller_->PinAppWithID("1"); 2700 ash::LauncherID id = launcher_controller_->GetLauncherIDForAppID("1"); 2701 int app_index = model_->ItemIndexByID(id); 2702 EXPECT_EQ(1, app_icon_loader->fetch_count()); 2703 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[app_index].type); 2704 EXPECT_TRUE(launcher_controller_->IsAppPinned("1")); 2705 EXPECT_FALSE(launcher_controller_->IsAppPinned("0")); 2706 EXPECT_EQ(initial_size + 1, model_->items().size()); 2707 2708 launcher_controller_.reset(); 2709 if (!ash::Shell::HasInstance()) { 2710 delete item_delegate_manager_; 2711 } else { 2712 // Clear already registered ShelfItemDelegate. 2713 ash::test::ShelfItemDelegateManagerTestAPI test(item_delegate_manager_); 2714 test.RemoveAllShelfItemDelegateForTest(); 2715 } 2716 model_.reset(new ash::ShelfModel); 2717 2718 AddAppListLauncherItem(); 2719 launcher_controller_.reset( 2720 ChromeLauncherController::CreateInstance(profile(), model_.get())); 2721 app_tab_helper = new TestAppTabHelperImpl; 2722 app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); 2723 SetAppTabHelper(app_tab_helper); 2724 app_icon_loader = new TestAppIconLoaderImpl; 2725 SetAppIconLoader(app_icon_loader); 2726 if (!ash::Shell::HasInstance()) { 2727 item_delegate_manager_ = new ash::ShelfItemDelegateManager(model_.get()); 2728 SetShelfItemDelegateManager(item_delegate_manager_); 2729 } 2730 launcher_controller_->Init(); 2731 2732 EXPECT_EQ(1, app_icon_loader->fetch_count()); 2733 ASSERT_EQ(initial_size + 1, model_->items().size()); 2734 EXPECT_TRUE(launcher_controller_->IsAppPinned("1")); 2735 EXPECT_FALSE(launcher_controller_->IsAppPinned("0")); 2736 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[app_index].type); 2737 2738 launcher_controller_->UnpinAppWithID("1"); 2739 ASSERT_EQ(initial_size, model_->items().size()); 2740 } 2741