1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.h" 6 7#include <algorithm> 8#include <string> 9#include <vector> 10 11#include "ash/launcher/launcher_model.h" 12#include "ash/launcher/launcher_model_observer.h" 13#include "base/command_line.h" 14#include "base/compiler_specific.h" 15#include "base/files/file_path.h" 16#include "base/memory/scoped_ptr.h" 17#include "base/message_loop/message_loop.h" 18#include "base/strings/utf_string_conversions.h" 19#include "base/values.h" 20#include "chrome/browser/extensions/extension_service.h" 21#include "chrome/browser/extensions/test_extension_system.h" 22#include "chrome/browser/ui/ash/chrome_launcher_prefs.h" 23#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h" 24#include "chrome/browser/ui/ash/launcher/shell_window_launcher_item_controller.h" 25#include "chrome/browser/ui/browser.h" 26#include "chrome/browser/ui/browser_commands.h" 27#include "chrome/browser/ui/browser_finder.h" 28#include "chrome/browser/ui/browser_list.h" 29#include "chrome/browser/ui/host_desktop.h" 30#include "chrome/browser/ui/tabs/tab_strip_model.h" 31#include "chrome/common/extensions/extension.h" 32#include "chrome/common/extensions/extension_constants.h" 33#include "chrome/common/extensions/extension_manifest_constants.h" 34#include "chrome/common/pref_names.h" 35#include "chrome/test/base/browser_with_test_window_test.h" 36#include "chrome/test/base/testing_pref_service_syncable.h" 37#include "chrome/test/base/testing_profile.h" 38#include "content/public/test/test_browser_thread.h" 39#include "testing/gtest/include/gtest/gtest.h" 40#include "ui/base/models/menu_model.h" 41 42using extensions::Extension; 43using extensions::Manifest; 44 45namespace { 46const char* offline_gmail_url = "https://mail.google.com/mail/mu/u"; 47const char* gmail_url = "https://mail.google.com/mail/u"; 48} 49 50namespace { 51 52// LauncherModelObserver implementation that tracks what messages are invoked. 53class TestLauncherModelObserver : public ash::LauncherModelObserver { 54 public: 55 TestLauncherModelObserver() 56 : added_(0), 57 removed_(0), 58 changed_(0) { 59 } 60 61 virtual ~TestLauncherModelObserver() { 62 } 63 64 // LauncherModelObserver 65 virtual void LauncherItemAdded(int index) OVERRIDE { 66 ++added_; 67 last_index_ = index; 68 } 69 70 virtual void LauncherItemRemoved(int index, ash::LauncherID id) OVERRIDE { 71 ++removed_; 72 last_index_ = index; 73 } 74 75 virtual void LauncherItemChanged(int index, 76 const ash::LauncherItem& old_item) OVERRIDE { 77 ++changed_; 78 last_index_ = index; 79 } 80 81 virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE { 82 last_index_ = target_index; 83 } 84 85 virtual void LauncherStatusChanged() OVERRIDE { 86 } 87 88 void clear_counts() { 89 added_ = 0; 90 removed_ = 0; 91 changed_ = 0; 92 last_index_ = 0; 93 } 94 95 int added() const { return added_; } 96 int removed() const { return removed_; } 97 int changed() const { return changed_; } 98 int last_index() const { return last_index_; } 99 100 private: 101 int added_; 102 int removed_; 103 int changed_; 104 int last_index_; 105 106 DISALLOW_COPY_AND_ASSIGN(TestLauncherModelObserver); 107}; 108 109// Test implementation of AppIconLoader. 110class TestAppIconLoaderImpl : public extensions::AppIconLoader { 111 public: 112 TestAppIconLoaderImpl() : fetch_count_(0) { 113 } 114 115 virtual ~TestAppIconLoaderImpl() { 116 } 117 118 // AppIconLoader implementation: 119 virtual void FetchImage(const std::string& id) OVERRIDE { 120 ++fetch_count_; 121 } 122 123 virtual void ClearImage(const std::string& id) OVERRIDE { 124 } 125 126 virtual void UpdateImage(const std::string& id) OVERRIDE { 127 } 128 129 int fetch_count() const { return fetch_count_; } 130 131 private: 132 int fetch_count_; 133 134 DISALLOW_COPY_AND_ASSIGN(TestAppIconLoaderImpl); 135}; 136 137} // namespace 138 139class ChromeLauncherControllerPerAppTest : public BrowserWithTestWindowTest { 140 protected: 141 ChromeLauncherControllerPerAppTest() 142 : extension_service_(NULL) { 143 SetHostDesktopType(chrome::HOST_DESKTOP_TYPE_ASH); 144 } 145 146 virtual ~ChromeLauncherControllerPerAppTest() { 147 } 148 149 virtual void SetUp() OVERRIDE { 150 BrowserWithTestWindowTest::SetUp(); 151 152 model_.reset(new ash::LauncherModel); 153 model_observer_.reset(new TestLauncherModelObserver); 154 model_->AddObserver(model_observer_.get()); 155 156 DictionaryValue manifest; 157 manifest.SetString(extension_manifest_keys::kName, 158 "launcher controller test extension"); 159 manifest.SetString(extension_manifest_keys::kVersion, "1"); 160 manifest.SetString(extension_manifest_keys::kDescription, 161 "for testing pinned apps"); 162 163 extensions::TestExtensionSystem* extension_system( 164 static_cast<extensions::TestExtensionSystem*>( 165 extensions::ExtensionSystem::Get(profile()))); 166 extension_service_ = extension_system->CreateExtensionService( 167 CommandLine::ForCurrentProcess(), base::FilePath(), false); 168 169 std::string error; 170 extension1_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 171 manifest, 172 Extension::NO_FLAGS, 173 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 174 &error); 175 extension2_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 176 manifest, 177 Extension::NO_FLAGS, 178 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 179 &error); 180 // Fake gmail extension. 181 DictionaryValue manifest_gmail; 182 manifest_gmail.SetString(extension_manifest_keys::kName, 183 "Gmail launcher controller test extension"); 184 manifest_gmail.SetString(extension_manifest_keys::kVersion, "1"); 185 manifest_gmail.SetString(extension_manifest_keys::kDescription, 186 "for testing pinned Gmail"); 187 manifest_gmail.SetString(extension_manifest_keys::kLaunchWebURL, 188 "https://mail.google.com/mail/ca"); 189 ListValue* list = new ListValue(); 190 list->Append(Value::CreateStringValue("*://mail.google.com/mail/ca")); 191 manifest_gmail.Set(extension_manifest_keys::kWebURLs, list); 192 193 extension3_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 194 manifest_gmail, 195 Extension::NO_FLAGS, 196 extension_misc::kGmailAppId, 197 &error); 198 199 // Fake search extension. 200 extension4_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, 201 manifest, 202 Extension::NO_FLAGS, 203 extension_misc::kGoogleSearchAppId, 204 &error); 205 } 206 207 virtual void TearDown() OVERRIDE { 208 model_->RemoveObserver(model_observer_.get()); 209 model_observer_.reset(); 210 launcher_controller_.reset(); 211 model_.reset(); 212 213 BrowserWithTestWindowTest::TearDown(); 214 } 215 216 void InitLauncherController() { 217 launcher_controller_.reset( 218 new ChromeLauncherControllerPerApp(profile(), model_.get())); 219 launcher_controller_->Init(); 220 } 221 222 void InitLauncherControllerWithBrowser() { 223 chrome::NewTab(browser()); 224 BrowserList::SetLastActive(browser()); 225 InitLauncherController(); 226 } 227 228 void SetAppIconLoader(extensions::AppIconLoader* loader) { 229 launcher_controller_->SetAppIconLoaderForTest(loader); 230 } 231 232 void InsertPrefValue(base::ListValue* pref_value, 233 int index, 234 const std::string& extension_id) { 235 base::DictionaryValue* entry = new DictionaryValue(); 236 entry->SetString(ash::kPinnedAppsPrefAppIDPath, extension_id); 237 pref_value->Insert(index, entry); 238 } 239 240 // Gets the currently configured app launchers from the controller. 241 void GetAppLaunchers(ChromeLauncherControllerPerApp* controller, 242 std::vector<std::string>* launchers) { 243 launchers->clear(); 244 for (ash::LauncherItems::const_iterator iter(model_->items().begin()); 245 iter != model_->items().end(); ++iter) { 246 ChromeLauncherControllerPerApp::IDToItemControllerMap::const_iterator 247 entry(controller->id_to_item_controller_map_.find(iter->id)); 248 if (iter->type == ash::TYPE_APP_SHORTCUT && 249 entry != controller->id_to_item_controller_map_.end()) { 250 launchers->push_back(entry->second->app_id()); 251 } 252 } 253 } 254 255 std::string GetPinnedAppStatus() { 256 std::string result; 257 for (int i = 0; i < model_->item_count(); i++) { 258 switch (model_->items()[i].type) { 259 case ash::TYPE_APP_SHORTCUT: { 260 const std::string& app = 261 launcher_controller_->GetAppIDForLauncherID( 262 model_->items()[i].id); 263 if (app == extension1_->id()) { 264 result += "App1, "; 265 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 266 } else if (app == extension2_->id()) { 267 result += "App2, "; 268 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); 269 } else if (app == extension3_->id()) { 270 result += "App3, "; 271 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 272 } else { 273 result += "unknown"; 274 } 275 break; 276 } 277 case ash::TYPE_BROWSER_SHORTCUT: 278 result += "Chrome, "; 279 break; 280 case ash::TYPE_APP_LIST: 281 result += "AppList"; 282 break; 283 default: 284 result += "Unknown"; 285 break; 286 } 287 } 288 return result; 289 } 290 291 // Needed for extension service & friends to work. 292 scoped_refptr<Extension> extension1_; 293 scoped_refptr<Extension> extension2_; 294 scoped_refptr<Extension> extension3_; 295 scoped_refptr<Extension> extension4_; 296 scoped_ptr<ChromeLauncherControllerPerApp> launcher_controller_; 297 scoped_ptr<TestLauncherModelObserver> model_observer_; 298 scoped_ptr<ash::LauncherModel> model_; 299 300 ExtensionService* extension_service_; 301 302 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerPerAppTest); 303}; 304 305TEST_F(ChromeLauncherControllerPerAppTest, DefaultApps) { 306 InitLauncherController(); 307 // Model should only contain the browser shortcut and app list items. 308 EXPECT_EQ(2, model_->item_count()); 309 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 310 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 311 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 312 313 // Installing |extension3_| should add it to the launcher - behind the 314 // chrome icon. 315 extension_service_->AddExtension(extension3_.get()); 316 EXPECT_EQ("Chrome, App3, AppList", GetPinnedAppStatus()); 317 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 318 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 319} 320 321// Check that the restauration of launcher items is happening in the same order 322// as the user has pinned them (on another system) when they are synced reverse 323// order. 324TEST_F(ChromeLauncherControllerPerAppTest, RestoreDefaultAppsReverseOrder) { 325 InitLauncherController(); 326 327 base::ListValue policy_value; 328 InsertPrefValue(&policy_value, 0, extension1_->id()); 329 InsertPrefValue(&policy_value, 1, extension2_->id()); 330 InsertPrefValue(&policy_value, 2, extension3_->id()); 331 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 332 policy_value.DeepCopy()); 333 EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex)); 334 // Model should only contain the browser shortcut and app list items. 335 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 336 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 337 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 338 EXPECT_EQ("Chrome, AppList", GetPinnedAppStatus()); 339 340 // Installing |extension3_| should add it to the launcher - behind the 341 // chrome icon. 342 ash::LauncherItem item; 343 extension_service_->AddExtension(extension3_.get()); 344 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 345 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 346 EXPECT_EQ("Chrome, App3, AppList", GetPinnedAppStatus()); 347 348 // Installing |extension2_| should add it to the launcher - behind the 349 // chrome icon, but in first location. 350 extension_service_->AddExtension(extension2_.get()); 351 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 352 EXPECT_EQ("Chrome, App2, App3, AppList", GetPinnedAppStatus()); 353 354 // Installing |extension1_| should add it to the launcher - behind the 355 // chrome icon, but in first location. 356 extension_service_->AddExtension(extension1_.get()); 357 EXPECT_EQ("Chrome, App1, App2, App3, AppList", GetPinnedAppStatus()); 358} 359 360// Check that the restauration of launcher items is happening in the same order 361// as the user has pinned them (on another system) when they are synced random 362// order. 363TEST_F(ChromeLauncherControllerPerAppTest, RestoreDefaultAppsRandomOrder) { 364 InitLauncherController(); 365 366 base::ListValue policy_value; 367 InsertPrefValue(&policy_value, 0, extension1_->id()); 368 InsertPrefValue(&policy_value, 1, extension2_->id()); 369 InsertPrefValue(&policy_value, 2, extension3_->id()); 370 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 371 policy_value.DeepCopy()); 372 EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex)); 373 // Model should only contain the browser shortcut and app list items. 374 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 375 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 376 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 377 EXPECT_EQ("Chrome, AppList", GetPinnedAppStatus()); 378 379 // Installing |extension2_| should add it to the launcher - behind the 380 // chrome icon. 381 extension_service_->AddExtension(extension2_.get()); 382 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 383 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 384 EXPECT_EQ("Chrome, App2, AppList", GetPinnedAppStatus()); 385 386 // Installing |extension1_| should add it to the launcher - behind the 387 // chrome icon, but in first location. 388 extension_service_->AddExtension(extension1_.get()); 389 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 390 EXPECT_EQ("Chrome, App1, App2, AppList", GetPinnedAppStatus()); 391 392 // Installing |extension3_| should add it to the launcher - behind the 393 // chrome icon, but in first location. 394 extension_service_->AddExtension(extension3_.get()); 395 EXPECT_EQ("Chrome, App1, App2, App3, AppList", GetPinnedAppStatus()); 396} 397 398// Check that the restauration of launcher items is happening in the same order 399// as the user has pinned / moved them (on another system) when they are synced 400// random order - including the chrome icon. 401TEST_F(ChromeLauncherControllerPerAppTest, 402 RestoreDefaultAppsRandomOrderChromeMoved) { 403 InitLauncherController(); 404 405 base::ListValue policy_value; 406 InsertPrefValue(&policy_value, 0, extension1_->id()); 407 InsertPrefValue(&policy_value, 1, extension2_->id()); 408 InsertPrefValue(&policy_value, 2, extension3_->id()); 409 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 410 policy_value.DeepCopy()); 411 profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex, 412 1); 413 // Model should only contain the browser shortcut and app list items. 414 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 415 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 416 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 417 EXPECT_EQ("Chrome, AppList", GetPinnedAppStatus()); 418 419 // Installing |extension2_| should add it to the launcher - behind the 420 // chrome icon. 421 ash::LauncherItem item; 422 extension_service_->AddExtension(extension2_.get()); 423 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 424 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 425 EXPECT_EQ("Chrome, App2, AppList", GetPinnedAppStatus()); 426 427 // Installing |extension1_| should add it to the launcher - behind the 428 // chrome icon, but in first location. 429 extension_service_->AddExtension(extension1_.get()); 430 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 431 EXPECT_EQ("App1, Chrome, App2, AppList", GetPinnedAppStatus()); 432 433 // Installing |extension3_| should add it to the launcher - behind the 434 // chrome icon, but in first location. 435 extension_service_->AddExtension(extension3_.get()); 436 EXPECT_EQ("App1, Chrome, App2, App3, AppList", GetPinnedAppStatus()); 437} 438 439// Check that syncing to a different state does the correct thing. 440TEST_F(ChromeLauncherControllerPerAppTest, RestoreDefaultAppsResyncOrder) { 441 InitLauncherController(); 442 base::ListValue policy_value; 443 InsertPrefValue(&policy_value, 0, extension1_->id()); 444 InsertPrefValue(&policy_value, 1, extension2_->id()); 445 InsertPrefValue(&policy_value, 2, extension3_->id()); 446 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 447 policy_value.DeepCopy()); 448 EXPECT_EQ(0, profile()->GetPrefs()->GetInteger(prefs::kShelfChromeIconIndex)); 449 extension_service_->AddExtension(extension2_.get()); 450 extension_service_->AddExtension(extension1_.get()); 451 extension_service_->AddExtension(extension3_.get()); 452 EXPECT_EQ("Chrome, App1, App2, App3, AppList", GetPinnedAppStatus()); 453 454 // Change the order with increasing chrome position and decreasing position. 455 base::ListValue policy_value1; 456 InsertPrefValue(&policy_value1, 0, extension3_->id()); 457 InsertPrefValue(&policy_value1, 1, extension1_->id()); 458 InsertPrefValue(&policy_value1, 2, extension2_->id()); 459 profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex, 460 2); 461 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 462 policy_value1.DeepCopy()); 463 EXPECT_EQ("App3, App1, Chrome, App2, AppList", GetPinnedAppStatus()); 464 base::ListValue policy_value2; 465 InsertPrefValue(&policy_value2, 0, extension2_->id()); 466 InsertPrefValue(&policy_value2, 1, extension3_->id()); 467 InsertPrefValue(&policy_value2, 2, extension1_->id()); 468 profile()->GetTestingPrefService()->SetInteger(prefs::kShelfChromeIconIndex, 469 1); 470 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 471 policy_value2.DeepCopy()); 472 EXPECT_EQ("App2, Chrome, App3, App1, AppList", GetPinnedAppStatus()); 473} 474 475// Check that simple locking of an application will 'create' a launcher item. 476TEST_F(ChromeLauncherControllerPerAppTest, CheckLockApps) { 477 InitLauncherController(); 478 // Model should only contain the browser shortcut and app list items. 479 EXPECT_EQ(2, model_->item_count()); 480 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 481 EXPECT_FALSE( 482 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 483 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 484 EXPECT_FALSE( 485 launcher_controller_->IsWindowedAppInLauncher(extension2_->id())); 486 487 launcher_controller_->LockV1AppWithID(extension1_->id()); 488 489 EXPECT_EQ(3, model_->item_count()); 490 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type); 491 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 492 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 493 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 494 EXPECT_FALSE( 495 launcher_controller_->IsWindowedAppInLauncher(extension2_->id())); 496 497 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 498 499 EXPECT_EQ(2, model_->item_count()); 500 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 501 EXPECT_FALSE( 502 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 503 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 504 EXPECT_FALSE( 505 launcher_controller_->IsWindowedAppInLauncher(extension2_->id())); 506} 507 508// Check that multiple locks of an application will be properly handled. 509TEST_F(ChromeLauncherControllerPerAppTest, CheckMukltiLockApps) { 510 InitLauncherController(); 511 // Model should only contain the browser shortcut and app list items. 512 EXPECT_EQ(2, model_->item_count()); 513 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 514 EXPECT_FALSE( 515 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 516 517 for (int i = 0; i < 2; i++) { 518 launcher_controller_->LockV1AppWithID(extension1_->id()); 519 520 EXPECT_EQ(3, model_->item_count()); 521 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type); 522 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 523 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher( 524 extension1_->id())); 525 } 526 527 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 528 529 EXPECT_EQ(3, model_->item_count()); 530 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type); 531 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 532 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 533 534 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 535 536 EXPECT_EQ(2, model_->item_count()); 537 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 538 EXPECT_FALSE( 539 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 540 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 541 EXPECT_FALSE( 542 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 543} 544 545// Check that already pinned items are not effected by locks. 546TEST_F(ChromeLauncherControllerPerAppTest, CheckAlreadyPinnedLockApps) { 547 InitLauncherController(); 548 // Model should only contain the browser shortcut and app list items. 549 EXPECT_EQ(2, model_->item_count()); 550 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 551 EXPECT_FALSE( 552 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 553 554 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 555 launcher_controller_->PinAppWithID(extension1_->id()); 556 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 557 558 EXPECT_EQ(3, model_->item_count()); 559 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 560 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 561 EXPECT_FALSE( 562 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 563 564 launcher_controller_->LockV1AppWithID(extension1_->id()); 565 566 EXPECT_EQ(3, model_->item_count()); 567 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 568 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 569 EXPECT_FALSE( 570 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 571 572 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 573 574 EXPECT_EQ(3, model_->item_count()); 575 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 576 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 577 EXPECT_FALSE( 578 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 579 580 launcher_controller_->UnpinAppsWithID(extension1_->id()); 581 582 EXPECT_EQ(2, model_->item_count()); 583} 584 585// Check that already pinned items which get locked stay after unpinning. 586TEST_F(ChromeLauncherControllerPerAppTest, CheckPinnedAppsStayAfterUnlock) { 587 InitLauncherController(); 588 // Model should only contain the browser shortcut and app list items. 589 EXPECT_EQ(2, model_->item_count()); 590 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 591 EXPECT_FALSE( 592 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 593 594 launcher_controller_->PinAppWithID(extension1_->id()); 595 596 EXPECT_EQ(3, model_->item_count()); 597 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 598 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 599 EXPECT_FALSE( 600 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 601 602 launcher_controller_->LockV1AppWithID(extension1_->id()); 603 604 EXPECT_EQ(3, model_->item_count()); 605 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 606 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 607 EXPECT_FALSE( 608 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 609 610 launcher_controller_->UnpinAppsWithID(extension1_->id()); 611 612 EXPECT_EQ(3, model_->item_count()); 613 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type); 614 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 615 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 616 617 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 618 619 EXPECT_EQ(2, model_->item_count()); 620} 621 622// Check that lock -> pin -> unlock -> unpin does properly transition. 623TEST_F(ChromeLauncherControllerPerAppTest, CheckLockPinUnlockUnpin) { 624 InitLauncherController(); 625 // Model should only contain the browser shortcut and app list items. 626 EXPECT_EQ(2, model_->item_count()); 627 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 628 EXPECT_FALSE( 629 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 630 631 launcher_controller_->LockV1AppWithID(extension1_->id()); 632 633 EXPECT_EQ(3, model_->item_count()); 634 EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type); 635 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 636 EXPECT_TRUE(launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 637 638 launcher_controller_->PinAppWithID(extension1_->id()); 639 640 EXPECT_EQ(3, model_->item_count()); 641 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 642 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 643 EXPECT_FALSE( 644 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 645 646 launcher_controller_->UnlockV1AppWithID(extension1_->id()); 647 648 EXPECT_EQ(3, model_->item_count()); 649 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 650 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 651 EXPECT_FALSE( 652 launcher_controller_->IsWindowedAppInLauncher(extension1_->id())); 653 654 launcher_controller_->UnpinAppsWithID(extension1_->id()); 655 656 EXPECT_EQ(2, model_->item_count()); 657} 658 659TEST_F(ChromeLauncherControllerPerAppTest, Policy) { 660 extension_service_->AddExtension(extension1_.get()); 661 extension_service_->AddExtension(extension3_.get()); 662 663 base::ListValue policy_value; 664 InsertPrefValue(&policy_value, 0, extension1_->id()); 665 InsertPrefValue(&policy_value, 1, extension2_->id()); 666 profile()->GetTestingPrefService()->SetManagedPref(prefs::kPinnedLauncherApps, 667 policy_value.DeepCopy()); 668 669 // Only |extension1_| should get pinned. |extension2_| is specified but not 670 // installed, and |extension3_| is part of the default set, but that shouldn't 671 // take effect when the policy override is in place. 672 InitLauncherController(); 673 EXPECT_EQ(3, model_->item_count()); 674 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 675 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 676 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); 677 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 678 679 // Installing |extension2_| should add it to the launcher. 680 extension_service_->AddExtension(extension2_.get()); 681 EXPECT_EQ(4, model_->item_count()); 682 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 683 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[2].type); 684 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); 685 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); 686 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 687 688 // Removing |extension1_| from the policy should be reflected in the launcher. 689 policy_value.Remove(0, NULL); 690 profile()->GetTestingPrefService()->SetManagedPref(prefs::kPinnedLauncherApps, 691 policy_value.DeepCopy()); 692 EXPECT_EQ(3, model_->item_count()); 693 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[1].type); 694 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); 695 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); 696 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 697} 698 699TEST_F(ChromeLauncherControllerPerAppTest, UnpinWithUninstall) { 700 extension_service_->AddExtension(extension3_.get()); 701 extension_service_->AddExtension(extension4_.get()); 702 703 InitLauncherController(); 704 705 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 706 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id())); 707 708 extension_service_->UnloadExtension(extension3_->id(), 709 extension_misc::UNLOAD_REASON_UNINSTALL); 710 711 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 712 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id())); 713} 714 715TEST_F(ChromeLauncherControllerPerAppTest, PrefUpdates) { 716 extension_service_->AddExtension(extension2_.get()); 717 extension_service_->AddExtension(extension3_.get()); 718 extension_service_->AddExtension(extension4_.get()); 719 720 InitLauncherController(); 721 722 std::vector<std::string> expected_launchers; 723 std::vector<std::string> actual_launchers; 724 base::ListValue pref_value; 725 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 726 pref_value.DeepCopy()); 727 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 728 EXPECT_EQ(expected_launchers, actual_launchers); 729 730 // Unavailable extensions don't create launcher items. 731 InsertPrefValue(&pref_value, 0, extension1_->id()); 732 InsertPrefValue(&pref_value, 1, extension2_->id()); 733 InsertPrefValue(&pref_value, 2, extension4_->id()); 734 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 735 pref_value.DeepCopy()); 736 expected_launchers.push_back(extension2_->id()); 737 expected_launchers.push_back(extension4_->id()); 738 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 739 EXPECT_EQ(expected_launchers, actual_launchers); 740 741 // Redundant pref entries show up only once. 742 InsertPrefValue(&pref_value, 2, extension3_->id()); 743 InsertPrefValue(&pref_value, 2, extension3_->id()); 744 InsertPrefValue(&pref_value, 5, extension3_->id()); 745 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 746 pref_value.DeepCopy()); 747 expected_launchers.insert(expected_launchers.begin() + 1, extension3_->id()); 748 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 749 EXPECT_EQ(expected_launchers, actual_launchers); 750 751 // Order changes are reflected correctly. 752 pref_value.Clear(); 753 InsertPrefValue(&pref_value, 0, extension4_->id()); 754 InsertPrefValue(&pref_value, 1, extension3_->id()); 755 InsertPrefValue(&pref_value, 2, extension2_->id()); 756 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 757 pref_value.DeepCopy()); 758 std::reverse(expected_launchers.begin(), expected_launchers.end()); 759 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 760 EXPECT_EQ(expected_launchers, actual_launchers); 761 762 // Clearing works. 763 pref_value.Clear(); 764 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 765 pref_value.DeepCopy()); 766 expected_launchers.clear(); 767 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 768 EXPECT_EQ(expected_launchers, actual_launchers); 769} 770 771TEST_F(ChromeLauncherControllerPerAppTest, PendingInsertionOrder) { 772 extension_service_->AddExtension(extension1_.get()); 773 extension_service_->AddExtension(extension3_.get()); 774 775 InitLauncherController(); 776 777 base::ListValue pref_value; 778 InsertPrefValue(&pref_value, 0, extension1_->id()); 779 InsertPrefValue(&pref_value, 1, extension2_->id()); 780 InsertPrefValue(&pref_value, 2, extension3_->id()); 781 profile()->GetTestingPrefService()->SetUserPref(prefs::kPinnedLauncherApps, 782 pref_value.DeepCopy()); 783 784 std::vector<std::string> expected_launchers; 785 expected_launchers.push_back(extension1_->id()); 786 expected_launchers.push_back(extension3_->id()); 787 std::vector<std::string> actual_launchers; 788 789 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 790 EXPECT_EQ(expected_launchers, actual_launchers); 791 792 // Install |extension2| and verify it shows up between the other two. 793 extension_service_->AddExtension(extension2_.get()); 794 expected_launchers.insert(expected_launchers.begin() + 1, extension2_->id()); 795 GetAppLaunchers(launcher_controller_.get(), &actual_launchers); 796 EXPECT_EQ(expected_launchers, actual_launchers); 797} 798 799// Checks the created menus and menu lists for correctness. It uses the given 800// |controller| to create the objects for the given |item| and checks the 801// found item count against the |expected_items|. The |title| list contains the 802// menu titles in the order of their appearance in the menu (not including the 803// application name). 804bool CheckMenuCreation(ChromeLauncherControllerPerApp* controller, 805 const ash::LauncherItem& item, 806 size_t expected_items, 807 string16 title[], 808 bool is_browser) { 809 ChromeLauncherAppMenuItems items = controller->GetApplicationList(item, 0); 810 // A new behavior has been added: Only show menus if there is at least one 811 // item available. 812 if (expected_items < 1 && is_browser) { 813 EXPECT_EQ(0u, items.size()); 814 return items.size() == 0; 815 } 816 // There should be one item in there: The title. 817 EXPECT_EQ(expected_items + 1, items.size()); 818 EXPECT_FALSE(items[0]->IsEnabled()); 819 for (size_t i = 0; i < expected_items; i++) { 820 EXPECT_EQ(title[i], items[1 + i]->title()); 821 // Check that the first real item has a leading separator. 822 if (i == 1) 823 EXPECT_TRUE(items[i]->HasLeadingSeparator()); 824 else 825 EXPECT_FALSE(items[i]->HasLeadingSeparator()); 826 } 827 828 scoped_ptr<ash::LauncherMenuModel> menu( 829 controller->CreateApplicationMenu(item, 0)); 830 // The first element in the menu is a spacing separator. On some systems 831 // (e.g. Windows) such things do not exist. As such we check the existence 832 // and adjust dynamically. 833 int first_item = menu->GetTypeAt(0) == ui::MenuModel::TYPE_SEPARATOR ? 1 : 0; 834 int expected_menu_items = first_item + 835 (expected_items ? (expected_items + 3) : 2); 836 EXPECT_EQ(expected_menu_items, menu->GetItemCount()); 837 EXPECT_FALSE(menu->IsEnabledAt(first_item)); 838 if (expected_items) { 839 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, 840 menu->GetTypeAt(first_item + 1)); 841 } 842 return items.size() == expected_items + 1; 843} 844 845// Check that browsers get reflected correctly in the launcher menu. 846TEST_F(ChromeLauncherControllerPerAppTest, BrowserMenuGeneration) { 847 EXPECT_EQ(1U, chrome::GetTotalBrowserCount()); 848 chrome::NewTab(browser()); 849 850 InitLauncherController(); 851 852 // Check that the browser list is empty at this time. 853 ash::LauncherItem item_browser; 854 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; 855 item_browser.id = 856 launcher_controller_->GetLauncherIDForAppID(extension_misc::kChromeAppId); 857 EXPECT_TRUE(CheckMenuCreation( 858 launcher_controller_.get(), item_browser, 0, NULL, true)); 859 860 // Now make the created browser() visible by adding it to the active browser 861 // list. 862 BrowserList::SetLastActive(browser()); 863 string16 title1 = ASCIIToUTF16("Test1"); 864 NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1); 865 string16 one_menu_item[] = {title1}; 866 867 EXPECT_TRUE(CheckMenuCreation( 868 launcher_controller_.get(), item_browser, 1, one_menu_item, true)); 869 870 // Create one more browser/window and check that one more was added. 871 Browser::CreateParams ash_params(profile(), chrome::HOST_DESKTOP_TYPE_ASH); 872 scoped_ptr<Browser> browser2( 873 chrome::CreateBrowserWithTestWindowForParams(&ash_params)); 874 chrome::NewTab(browser2.get()); 875 BrowserList::SetLastActive(browser2.get()); 876 string16 title2 = ASCIIToUTF16("Test2"); 877 NavigateAndCommitActiveTabWithTitle(browser2.get(), GURL("http://test2"), 878 title2); 879 880 // Check that the list contains now two entries - make furthermore sure that 881 // the active item is the first entry. 882 string16 two_menu_items[] = {title1, title2}; 883 EXPECT_TRUE(CheckMenuCreation( 884 launcher_controller_.get(), item_browser, 2, two_menu_items, true)); 885 886 // Apparently we have to close all tabs we have. 887 chrome::CloseTab(browser2.get()); 888} 889 890// Check that V1 apps are correctly reflected in the launcher menu using the 891// refocus logic. 892// Note that the extension matching logic is tested by the extension system 893// and does not need a separate test here. 894TEST_F(ChromeLauncherControllerPerAppTest, V1AppMenuGeneration) { 895 EXPECT_EQ(1U, chrome::GetTotalBrowserCount()); 896 EXPECT_EQ(0, browser()->tab_strip_model()->count()); 897 898 InitLauncherControllerWithBrowser(); 899 900 // Model should only contain the browser shortcut and app list items. 901 EXPECT_EQ(2, model_->item_count()); 902 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); 903 904 // Installing |extension3_| adds it to the launcher. 905 ash::LauncherID gmail_id = model_->next_id(); 906 extension_service_->AddExtension(extension3_.get()); 907 EXPECT_EQ(3, model_->item_count()); 908 int gmail_index = model_->ItemIndexByID(gmail_id); 909 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type); 910 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 911 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); 912 913 // Check the menu content. 914 ash::LauncherItem item_browser; 915 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; 916 item_browser.id = 917 launcher_controller_->GetLauncherIDForAppID(extension_misc::kChromeAppId); 918 919 ash::LauncherItem item_gmail; 920 item_gmail.type = ash::TYPE_APP_SHORTCUT; 921 item_gmail.id = gmail_id; 922 EXPECT_TRUE(CheckMenuCreation( 923 launcher_controller_.get(), item_gmail, 0, NULL, false)); 924 925 // Set the gmail URL to a new tab. 926 string16 title1 = ASCIIToUTF16("Test1"); 927 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); 928 929 string16 one_menu_item[] = {title1}; 930 EXPECT_TRUE(CheckMenuCreation( 931 launcher_controller_.get(), item_gmail, 1, one_menu_item, false)); 932 933 // Create one empty tab. 934 chrome::NewTab(browser()); 935 string16 title2 = ASCIIToUTF16("Test2"); 936 NavigateAndCommitActiveTabWithTitle( 937 browser(), 938 GURL("https://bla"), 939 title2); 940 941 // and another one with another gmail instance. 942 chrome::NewTab(browser()); 943 string16 title3 = ASCIIToUTF16("Test3"); 944 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title3); 945 string16 two_menu_items[] = {title1, title3}; 946 EXPECT_TRUE(CheckMenuCreation( 947 launcher_controller_.get(), item_gmail, 2, two_menu_items, false)); 948 949 // Even though the item is in the V1 app list, it should also be in the 950 // browser list. 951 string16 browser_menu_item[] = {title3}; 952 EXPECT_TRUE(CheckMenuCreation( 953 launcher_controller_.get(), item_browser, 1, browser_menu_item, false)); 954 955 // Test that closing of (all) the item(s) does work (and all menus get 956 // updated properly). 957 launcher_controller_->Close(item_gmail.id); 958 959 EXPECT_TRUE(CheckMenuCreation( 960 launcher_controller_.get(), item_gmail, 0, NULL, false)); 961 string16 browser_menu_item2[] = {title2}; 962 EXPECT_TRUE(CheckMenuCreation( 963 launcher_controller_.get(), item_browser, 1, browser_menu_item2, false)); 964} 965 966// Checks that the generated menu list properly activates items. 967TEST_F(ChromeLauncherControllerPerAppTest, V1AppMenuExecution) { 968 InitLauncherControllerWithBrowser(); 969 970 // Add |extension3_| to the launcher and add two items. 971 GURL gmail = GURL("https://mail.google.com/mail/u"); 972 ash::LauncherID gmail_id = model_->next_id(); 973 extension_service_->AddExtension(extension3_.get()); 974 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); 975 string16 title1 = ASCIIToUTF16("Test1"); 976 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); 977 chrome::NewTab(browser()); 978 string16 title2 = ASCIIToUTF16("Test2"); 979 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title2); 980 981 // Check that the menu is properly set. 982 ash::LauncherItem item_gmail; 983 item_gmail.type = ash::TYPE_APP_SHORTCUT; 984 item_gmail.id = gmail_id; 985 string16 two_menu_items[] = {title1, title2}; 986 EXPECT_TRUE(CheckMenuCreation( 987 launcher_controller_.get(), item_gmail, 2, two_menu_items, false)); 988 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); 989 // Execute the second item in the list (which shouldn't do anything since that 990 // item is per definition already the active tab). 991 { 992 scoped_ptr<ash::LauncherMenuModel> menu( 993 launcher_controller_->CreateApplicationMenu(item_gmail, 0)); 994 // The first element in the menu is a spacing separator. On some systems 995 // (e.g. Windows) such things do not exist. As such we check the existence 996 // and adjust dynamically. 997 int first_item = 998 (menu->GetTypeAt(0) == ui::MenuModel::TYPE_SEPARATOR) ? 1 : 0; 999 menu->ActivatedAt(first_item + 3); 1000 } 1001 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); 1002 1003 // Execute the first item. 1004 { 1005 scoped_ptr<ash::LauncherMenuModel> menu( 1006 launcher_controller_->CreateApplicationMenu(item_gmail, 0)); 1007 int first_item = 1008 (menu->GetTypeAt(0) == ui::MenuModel::TYPE_SEPARATOR) ? 1 : 0; 1009 menu->ActivatedAt(first_item + 2); 1010 } 1011 // Now the active tab should be the second item. 1012 EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); 1013} 1014 1015// Checks that the generated menu list properly deletes items. 1016TEST_F(ChromeLauncherControllerPerAppTest, V1AppMenuDeletionExecution) { 1017 InitLauncherControllerWithBrowser(); 1018 1019 // Add |extension3_| to the launcher and add two items. 1020 GURL gmail = GURL("https://mail.google.com/mail/u"); 1021 ash::LauncherID gmail_id = model_->next_id(); 1022 extension_service_->AddExtension(extension3_.get()); 1023 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); 1024 string16 title1 = ASCIIToUTF16("Test1"); 1025 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); 1026 chrome::NewTab(browser()); 1027 string16 title2 = ASCIIToUTF16("Test2"); 1028 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title2); 1029 1030 // Check that the menu is properly set. 1031 ash::LauncherItem item_gmail; 1032 item_gmail.type = ash::TYPE_APP_SHORTCUT; 1033 item_gmail.id = gmail_id; 1034 string16 two_menu_items[] = {title1, title2}; 1035 EXPECT_TRUE(CheckMenuCreation( 1036 launcher_controller_.get(), item_gmail, 2, two_menu_items, false)); 1037 1038 int tabs = browser()->tab_strip_model()->count(); 1039 // Activate the proper tab through the menu item. 1040 { 1041 ChromeLauncherAppMenuItems items = 1042 launcher_controller_->GetApplicationList(item_gmail, 0); 1043 items[1]->Execute(0); 1044 EXPECT_EQ(tabs, browser()->tab_strip_model()->count()); 1045 } 1046 1047 // Delete one tab through the menu item. 1048 { 1049 ChromeLauncherAppMenuItems items = 1050 launcher_controller_->GetApplicationList(item_gmail, 0); 1051 items[1]->Execute(ui::EF_SHIFT_DOWN); 1052 EXPECT_EQ(--tabs, browser()->tab_strip_model()->count()); 1053 } 1054} 1055 1056// Tests that panels create launcher items correctly 1057TEST_F(ChromeLauncherControllerPerAppTest, AppPanels) { 1058 InitLauncherControllerWithBrowser(); 1059 EXPECT_EQ(1, model_observer_->added()); 1060 1061 TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl(); 1062 SetAppIconLoader(app_icon_loader); 1063 1064 // Test adding an app panel 1065 std::string app_id = extension1_->id(); 1066 ShellWindowLauncherItemController app_panel_controller( 1067 LauncherItemController::TYPE_APP_PANEL, "id", app_id, 1068 launcher_controller_.get()); 1069 ash::LauncherID launcher_id1 = launcher_controller_->CreateAppLauncherItem( 1070 &app_panel_controller, app_id, ash::STATUS_RUNNING); 1071 int panel_index = model_observer_->last_index(); 1072 EXPECT_EQ(2, model_observer_->added()); 1073 EXPECT_EQ(0, model_observer_->changed()); 1074 EXPECT_EQ(1, app_icon_loader->fetch_count()); 1075 model_observer_->clear_counts(); 1076 1077 // App panels should have a separate identifier than the app id 1078 EXPECT_EQ(0, launcher_controller_->GetLauncherIDForAppID(app_id)); 1079 1080 // Setting the app image image should not change the panel if it set its icon 1081 app_panel_controller.set_image_set_by_controller(true); 1082 gfx::ImageSkia image; 1083 launcher_controller_->SetAppImage(app_id, image); 1084 EXPECT_EQ(0, model_observer_->changed()); 1085 model_observer_->clear_counts(); 1086 1087 // Add a second app panel and verify that it get the same index as the first 1088 // one had, being added to the left of the existing panel. 1089 ash::LauncherID launcher_id2 = launcher_controller_->CreateAppLauncherItem( 1090 &app_panel_controller, app_id, ash::STATUS_RUNNING); 1091 EXPECT_EQ(panel_index, model_observer_->last_index()); 1092 EXPECT_EQ(1, model_observer_->added()); 1093 model_observer_->clear_counts(); 1094 1095 launcher_controller_->CloseLauncherItem(launcher_id2); 1096 launcher_controller_->CloseLauncherItem(launcher_id1); 1097 EXPECT_EQ(2, model_observer_->removed()); 1098} 1099 1100// Tests that the Gmail extension matches more then the app itself claims with 1101// the manifest file. 1102TEST_F(ChromeLauncherControllerPerAppTest, GmailMatching) { 1103 InitLauncherControllerWithBrowser(); 1104 1105 // Create a Gmail browser tab. 1106 chrome::NewTab(browser()); 1107 string16 title = ASCIIToUTF16("Test"); 1108 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title); 1109 content::WebContents* content = 1110 browser()->tab_strip_model()->GetActiveWebContents(); 1111 1112 // Check that the launcher controller does not recognize the running app. 1113 EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content)); 1114 1115 // Installing |extension3_| adds it to the launcher. 1116 ash::LauncherID gmail_id = model_->next_id(); 1117 extension_service_->AddExtension(extension3_.get()); 1118 EXPECT_EQ(3, model_->item_count()); 1119 int gmail_index = model_->ItemIndexByID(gmail_id); 1120 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type); 1121 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 1122 1123 // Check that it is now handled. 1124 EXPECT_TRUE(launcher_controller_->ContentCanBeHandledByGmailApp(content)); 1125 1126 // Check also that the app has detected that properly. 1127 ash::LauncherItem item_gmail; 1128 item_gmail.type = ash::TYPE_APP_SHORTCUT; 1129 item_gmail.id = gmail_id; 1130 EXPECT_EQ(2U, launcher_controller_->GetApplicationList(item_gmail, 0).size()); 1131} 1132 1133// Tests that the Gmail extension does not match the offline verison. 1134TEST_F(ChromeLauncherControllerPerAppTest, GmailOfflineMatching) { 1135 InitLauncherControllerWithBrowser(); 1136 1137 // Create a Gmail browser tab. 1138 chrome::NewTab(browser()); 1139 string16 title = ASCIIToUTF16("Test"); 1140 NavigateAndCommitActiveTabWithTitle(browser(), 1141 GURL(offline_gmail_url), 1142 title); 1143 content::WebContents* content = 1144 browser()->tab_strip_model()->GetActiveWebContents(); 1145 1146 // Installing |extension3_| adds it to the launcher. 1147 ash::LauncherID gmail_id = model_->next_id(); 1148 extension_service_->AddExtension(extension3_.get()); 1149 EXPECT_EQ(3, model_->item_count()); 1150 int gmail_index = model_->ItemIndexByID(gmail_id); 1151 EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[gmail_index].type); 1152 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); 1153 1154 // The content should not be able to be handled by the app. 1155 EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content)); 1156} 1157