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