| OLD | NEW |
| (Empty) |
| 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_impl.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include <map> | |
| 11 #include <memory> | |
| 12 #include <set> | |
| 13 #include <string> | |
| 14 #include <utility> | |
| 15 #include <vector> | |
| 16 | |
| 17 #include "ash/display/display_configuration_controller.h" | |
| 18 #include "ash/display/screen_orientation_controller_chromeos.h" | |
| 19 #include "ash/public/cpp/app_launch_id.h" | |
| 20 #include "ash/public/cpp/shelf_item.h" | |
| 21 #include "ash/public/cpp/shelf_item_delegate.h" | |
| 22 #include "ash/shelf/shelf_application_menu_model.h" | |
| 23 #include "ash/shelf/shelf_constants.h" | |
| 24 #include "ash/shelf/shelf_controller.h" | |
| 25 #include "ash/shelf/shelf_model.h" | |
| 26 #include "ash/shelf/shelf_model_observer.h" | |
| 27 #include "ash/shell.h" | |
| 28 #include "ash/test/ash_test_helper.h" | |
| 29 #include "ash/test/shell_test_api.h" | |
| 30 #include "ash/test/test_shell_delegate.h" | |
| 31 #include "ash/wm/maximize_mode/maximize_mode_controller.h" | |
| 32 #include "ash/wm/window_util.h" | |
| 33 #include "base/command_line.h" | |
| 34 #include "base/compiler_specific.h" | |
| 35 #include "base/files/file_path.h" | |
| 36 #include "base/json/json_string_value_serializer.h" | |
| 37 #include "base/location.h" | |
| 38 #include "base/macros.h" | |
| 39 #include "base/memory/ptr_util.h" | |
| 40 #include "base/run_loop.h" | |
| 41 #include "base/single_thread_task_runner.h" | |
| 42 #include "base/strings/utf_string_conversions.h" | |
| 43 #include "base/values.h" | |
| 44 #include "build/build_config.h" | |
| 45 #include "chrome/browser/chromeos/arc/arc_support_host.h" | |
| 46 #include "chrome/browser/chromeos/arc/arc_util.h" | |
| 47 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" | |
| 48 #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" | |
| 49 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h" | |
| 50 #include "chrome/browser/extensions/extension_service.h" | |
| 51 #include "chrome/browser/extensions/test_extension_system.h" | |
| 52 #include "chrome/browser/lifetime/scoped_keep_alive.h" | |
| 53 #include "chrome/browser/prefs/browser_prefs.h" | |
| 54 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h" | |
| 55 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" | |
| 56 #include "chrome/browser/ui/app_list/arc/arc_app_test.h" | |
| 57 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" | |
| 58 #include "chrome/browser/ui/app_list/arc/arc_default_app_list.h" | |
| 59 #include "chrome/browser/ui/apps/chrome_app_delegate.h" | |
| 60 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h" | |
| 61 #include "chrome/browser/ui/ash/launcher/app_window_launcher_controller.h" | |
| 62 #include "chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h" | |
| 63 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h" | |
| 64 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h" | |
| 65 #include "chrome/browser/ui/ash/launcher/extension_app_window_launcher_item_cont
roller.h" | |
| 66 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h" | |
| 67 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" | |
| 68 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h" | |
| 69 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h" | |
| 70 #include "chrome/browser/ui/browser.h" | |
| 71 #include "chrome/browser/ui/browser_commands.h" | |
| 72 #include "chrome/browser/ui/browser_finder.h" | |
| 73 #include "chrome/browser/ui/browser_list.h" | |
| 74 #include "chrome/browser/ui/browser_tabstrip.h" | |
| 75 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 76 #include "chrome/common/chrome_constants.h" | |
| 77 #include "chrome/common/chrome_switches.h" | |
| 78 #include "chrome/common/extensions/extension_constants.h" | |
| 79 #include "chrome/common/pref_names.h" | |
| 80 #include "chrome/test/base/browser_with_test_window_test.h" | |
| 81 #include "chrome/test/base/test_browser_window_aura.h" | |
| 82 #include "chrome/test/base/testing_browser_process.h" | |
| 83 #include "chrome/test/base/testing_profile.h" | |
| 84 #include "chrome/test/base/testing_profile_manager.h" | |
| 85 #include "chromeos/chromeos_switches.h" | |
| 86 #include "components/arc/arc_util.h" | |
| 87 #include "components/arc/common/app.mojom.h" | |
| 88 #include "components/arc/test/fake_app_instance.h" | |
| 89 #include "components/exo/shell_surface.h" | |
| 90 #include "components/prefs/pref_notifier_impl.h" | |
| 91 #include "components/signin/core/account_id/account_id.h" | |
| 92 #include "components/sync/model/attachments/attachment_service_proxy_for_test.h" | |
| 93 #include "components/sync/model/fake_sync_change_processor.h" | |
| 94 #include "components/sync/model/sync_error_factory_mock.h" | |
| 95 #include "components/sync/protocol/sync.pb.h" | |
| 96 #include "components/sync_preferences/pref_model_associator.h" | |
| 97 #include "components/sync_preferences/testing_pref_service_syncable.h" | |
| 98 #include "components/user_manager/fake_user_manager.h" | |
| 99 #include "content/public/browser/web_contents.h" | |
| 100 #include "content/public/browser/web_contents_observer.h" | |
| 101 #include "content/public/test/test_utils.h" | |
| 102 #include "content/public/test/web_contents_tester.h" | |
| 103 #include "extensions/browser/app_window/app_window_contents.h" | |
| 104 #include "extensions/browser/app_window/app_window_registry.h" | |
| 105 #include "extensions/browser/app_window/native_app_window.h" | |
| 106 #include "extensions/common/extension.h" | |
| 107 #include "extensions/common/manifest_constants.h" | |
| 108 #include "testing/gtest/include/gtest/gtest.h" | |
| 109 #include "ui/aura/client/window_parenting_client.h" | |
| 110 #include "ui/aura/window.h" | |
| 111 #include "ui/base/models/menu_model.h" | |
| 112 #include "ui/display/display.h" | |
| 113 #include "ui/display/display_switches.h" | |
| 114 #include "ui/display/screen.h" | |
| 115 #include "ui/events/base_event_utils.h" | |
| 116 #include "ui/events/event_constants.h" | |
| 117 #include "ui/views/widget/widget.h" | |
| 118 | |
| 119 using base::ASCIIToUTF16; | |
| 120 using extensions::Extension; | |
| 121 using extensions::Manifest; | |
| 122 using extensions::UnloadedExtensionInfo; | |
| 123 using arc::mojom::OrientationLock; | |
| 124 | |
| 125 namespace { | |
| 126 const char* offline_gmail_url = "https://mail.google.com/mail/mu/u"; | |
| 127 const char* gmail_url = "https://mail.google.com/mail/u"; | |
| 128 const char* kGmailLaunchURL = "https://mail.google.com/mail/ca"; | |
| 129 | |
| 130 // An extension prefix. | |
| 131 const char kCrxAppPrefix[] = "_crx_"; | |
| 132 | |
| 133 // Dummy app id is used to put at least one pin record to prevent initializing | |
| 134 // pin model with default apps that can affect some tests. | |
| 135 const char kDummyAppId[] = "dummyappid_dummyappid_dummyappid"; | |
| 136 | |
| 137 // ShelfModelObserver implementation that tracks what messages are invoked. | |
| 138 class TestShelfModelObserver : public ash::ShelfModelObserver { | |
| 139 public: | |
| 140 TestShelfModelObserver() {} | |
| 141 ~TestShelfModelObserver() override {} | |
| 142 | |
| 143 // Overridden from ash::ShelfModelObserver: | |
| 144 void ShelfItemAdded(int index) override { | |
| 145 ++added_; | |
| 146 last_index_ = index; | |
| 147 } | |
| 148 | |
| 149 void ShelfItemRemoved(int index, const ash::ShelfItem& old_item) override { | |
| 150 ++removed_; | |
| 151 last_index_ = index; | |
| 152 } | |
| 153 | |
| 154 void ShelfItemChanged(int index, const ash::ShelfItem& old_item) override { | |
| 155 ++changed_; | |
| 156 last_index_ = index; | |
| 157 } | |
| 158 | |
| 159 void ShelfItemMoved(int start_index, int target_index) override { | |
| 160 last_index_ = target_index; | |
| 161 } | |
| 162 | |
| 163 void clear_counts() { | |
| 164 added_ = 0; | |
| 165 removed_ = 0; | |
| 166 changed_ = 0; | |
| 167 last_index_ = 0; | |
| 168 } | |
| 169 | |
| 170 int added() const { return added_; } | |
| 171 int removed() const { return removed_; } | |
| 172 int changed() const { return changed_; } | |
| 173 int last_index() const { return last_index_; } | |
| 174 | |
| 175 private: | |
| 176 int added_ = 0; | |
| 177 int removed_ = 0; | |
| 178 int changed_ = 0; | |
| 179 int last_index_ = 0; | |
| 180 | |
| 181 DISALLOW_COPY_AND_ASSIGN(TestShelfModelObserver); | |
| 182 }; | |
| 183 | |
| 184 // Test implementation of AppIconLoader. | |
| 185 class TestAppIconLoaderImpl : public AppIconLoader { | |
| 186 public: | |
| 187 TestAppIconLoaderImpl() = default; | |
| 188 ~TestAppIconLoaderImpl() override = default; | |
| 189 | |
| 190 void AddSupportedApp(const std::string& id) { supported_apps_.insert(id); } | |
| 191 | |
| 192 // AppIconLoader implementation: | |
| 193 bool CanLoadImageForApp(const std::string& id) override { | |
| 194 return supported_apps_.find(id) != supported_apps_.end(); | |
| 195 } | |
| 196 void FetchImage(const std::string& id) override { ++fetch_count_; } | |
| 197 void ClearImage(const std::string& id) override { ++clear_count_; } | |
| 198 void UpdateImage(const std::string& id) override {} | |
| 199 | |
| 200 int fetch_count() const { return fetch_count_; } | |
| 201 int clear_count() const { return clear_count_; } | |
| 202 | |
| 203 private: | |
| 204 int fetch_count_ = 0; | |
| 205 int clear_count_ = 0; | |
| 206 std::set<std::string> supported_apps_; | |
| 207 | |
| 208 DISALLOW_COPY_AND_ASSIGN(TestAppIconLoaderImpl); | |
| 209 }; | |
| 210 | |
| 211 // Test implementation of LauncherControllerHelper. | |
| 212 class TestLauncherControllerHelper : public LauncherControllerHelper { | |
| 213 public: | |
| 214 TestLauncherControllerHelper() : LauncherControllerHelper(nullptr) {} | |
| 215 explicit TestLauncherControllerHelper(Profile* profile) | |
| 216 : LauncherControllerHelper(profile) {} | |
| 217 ~TestLauncherControllerHelper() override {} | |
| 218 | |
| 219 // Sets the id for the specified tab. | |
| 220 void SetAppID(content::WebContents* tab, const std::string& id) { | |
| 221 tab_id_map_[tab] = id; | |
| 222 } | |
| 223 | |
| 224 // Returns true if there is an id registered for |tab|. | |
| 225 bool HasAppID(content::WebContents* tab) const { | |
| 226 return tab_id_map_.find(tab) != tab_id_map_.end(); | |
| 227 } | |
| 228 | |
| 229 // LauncherControllerHelper: | |
| 230 std::string GetAppID(content::WebContents* tab) override { | |
| 231 return tab_id_map_.find(tab) != tab_id_map_.end() ? tab_id_map_[tab] : | |
| 232 std::string(); | |
| 233 } | |
| 234 | |
| 235 bool IsValidIDForCurrentUser(const std::string& id) const override { | |
| 236 for (TabToStringMap::const_iterator i = tab_id_map_.begin(); | |
| 237 i != tab_id_map_.end(); ++i) { | |
| 238 if (i->second == id) | |
| 239 return true; | |
| 240 } | |
| 241 return false; | |
| 242 } | |
| 243 | |
| 244 ArcAppListPrefs* GetArcAppListPrefs() const override { return nullptr; } | |
| 245 | |
| 246 private: | |
| 247 typedef std::map<content::WebContents*, std::string> TabToStringMap; | |
| 248 | |
| 249 TabToStringMap tab_id_map_; | |
| 250 | |
| 251 DISALLOW_COPY_AND_ASSIGN(TestLauncherControllerHelper); | |
| 252 }; | |
| 253 | |
| 254 // Test implementation of a V2 app launcher item controller. | |
| 255 class TestV2AppLauncherItemController : public ash::ShelfItemDelegate { | |
| 256 public: | |
| 257 explicit TestV2AppLauncherItemController(const std::string& app_id) | |
| 258 : ash::ShelfItemDelegate(ash::AppLaunchId(app_id)) {} | |
| 259 | |
| 260 ~TestV2AppLauncherItemController() override {} | |
| 261 | |
| 262 // Override for ash::ShelfItemDelegate: | |
| 263 void ItemSelected(std::unique_ptr<ui::Event> event, | |
| 264 int64_t display_id, | |
| 265 ash::ShelfLaunchSource source, | |
| 266 const ItemSelectedCallback& callback) override { | |
| 267 callback.Run(ash::SHELF_ACTION_WINDOW_ACTIVATED, base::nullopt); | |
| 268 } | |
| 269 void ExecuteCommand(uint32_t command_id, int32_t event_flags) override {} | |
| 270 void Close() override {} | |
| 271 | |
| 272 private: | |
| 273 DISALLOW_COPY_AND_ASSIGN(TestV2AppLauncherItemController); | |
| 274 }; | |
| 275 | |
| 276 // A test ShelfController implementation that tracks alignment and auto-hide. | |
| 277 class TestShelfController : public ash::mojom::ShelfController { | |
| 278 public: | |
| 279 TestShelfController() : binding_(this) {} | |
| 280 ~TestShelfController() override {} | |
| 281 | |
| 282 ash::ShelfAlignment alignment() const { return alignment_; } | |
| 283 ash::ShelfAutoHideBehavior auto_hide() const { return auto_hide_; } | |
| 284 | |
| 285 size_t alignment_change_count() const { return alignment_change_count_; } | |
| 286 size_t auto_hide_change_count() const { return auto_hide_change_count_; } | |
| 287 | |
| 288 ash::mojom::ShelfControllerPtr CreateInterfacePtrAndBind() { | |
| 289 return binding_.CreateInterfacePtrAndBind(); | |
| 290 } | |
| 291 | |
| 292 // ash::mojom::ShelfController: | |
| 293 void AddObserver( | |
| 294 ash::mojom::ShelfObserverAssociatedPtrInfo observer) override { | |
| 295 observer_.Bind(std::move(observer)); | |
| 296 } | |
| 297 void SetAlignment(ash::ShelfAlignment alignment, | |
| 298 int64_t display_id) override { | |
| 299 alignment_change_count_++; | |
| 300 alignment_ = alignment; | |
| 301 observer_->OnAlignmentChanged(alignment_, display_id); | |
| 302 } | |
| 303 void SetAutoHideBehavior(ash::ShelfAutoHideBehavior auto_hide, | |
| 304 int64_t display_id) override { | |
| 305 auto_hide_change_count_++; | |
| 306 auto_hide_ = auto_hide; | |
| 307 observer_->OnAutoHideBehaviorChanged(auto_hide_, display_id); | |
| 308 } | |
| 309 void PinItem( | |
| 310 const ash::ShelfItem& item, | |
| 311 ash::mojom::ShelfItemDelegateAssociatedPtrInfo delegate) override {} | |
| 312 void UnpinItem(const std::string& app_id) override {} | |
| 313 void SetItemImage(const std::string& app_id, const SkBitmap& image) override { | |
| 314 } | |
| 315 | |
| 316 private: | |
| 317 ash::ShelfAlignment alignment_ = ash::SHELF_ALIGNMENT_BOTTOM_LOCKED; | |
| 318 ash::ShelfAutoHideBehavior auto_hide_ = ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN; | |
| 319 | |
| 320 size_t alignment_change_count_ = 0; | |
| 321 size_t auto_hide_change_count_ = 0; | |
| 322 | |
| 323 ash::mojom::ShelfObserverAssociatedPtr observer_; | |
| 324 mojo::Binding<ash::mojom::ShelfController> binding_; | |
| 325 | |
| 326 DISALLOW_COPY_AND_ASSIGN(TestShelfController); | |
| 327 }; | |
| 328 | |
| 329 // A callback that does nothing after shelf item selection handling. | |
| 330 void NoopCallback(ash::ShelfAction action, base::Optional<ash::MenuItemList>) {} | |
| 331 | |
| 332 // Simulates selection of the shelf item. | |
| 333 void SelectItem(ash::ShelfItemDelegate* delegate) { | |
| 334 std::unique_ptr<ui::Event> event = base::MakeUnique<ui::MouseEvent>( | |
| 335 ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(), | |
| 336 ui::EF_NONE, 0); | |
| 337 delegate->ItemSelected(std::move(event), display::kInvalidDisplayId, | |
| 338 ash::LAUNCH_FROM_UNKNOWN, base::Bind(&NoopCallback)); | |
| 339 } | |
| 340 | |
| 341 } // namespace | |
| 342 | |
| 343 // A test ChromeLauncherControllerImpl subclass that uses TestShelfController. | |
| 344 class TestChromeLauncherControllerImpl : public ChromeLauncherControllerImpl { | |
| 345 public: | |
| 346 TestChromeLauncherControllerImpl(Profile* profile, ash::ShelfModel* model) | |
| 347 : ChromeLauncherControllerImpl(profile, model) {} | |
| 348 | |
| 349 // ChromeLauncherControllerImpl: | |
| 350 using ChromeLauncherControllerImpl::ReleaseProfile; | |
| 351 bool ConnectToShelfController() override { | |
| 352 // Set the shelf controller pointer to a test instance; this is run in init. | |
| 353 if (!shelf_controller_.is_bound()) | |
| 354 shelf_controller_ = test_shelf_controller_.CreateInterfacePtrAndBind(); | |
| 355 return true; | |
| 356 } | |
| 357 | |
| 358 TestShelfController* test_shelf_controller() { | |
| 359 return &test_shelf_controller_; | |
| 360 } | |
| 361 | |
| 362 private: | |
| 363 TestShelfController test_shelf_controller_; | |
| 364 | |
| 365 DISALLOW_COPY_AND_ASSIGN(TestChromeLauncherControllerImpl); | |
| 366 }; | |
| 367 | |
| 368 // A shell delegate that owns a ChromeLauncherController, like production. | |
| 369 // TODO(msw): Refine ChromeLauncherControllerImpl lifetime management. | |
| 370 // TODO(msw): Avoid relying on TestShellDelegate's ShelfInitializer. | |
| 371 class ChromeLauncherTestShellDelegate : public ash::test::TestShellDelegate { | |
| 372 public: | |
| 373 ChromeLauncherTestShellDelegate() {} | |
| 374 | |
| 375 // Create a ChromeLauncherControllerImpl instance. | |
| 376 ChromeLauncherControllerImpl* CreateLauncherController(Profile* profile) { | |
| 377 launcher_controller_ = base::MakeUnique<ChromeLauncherControllerImpl>( | |
| 378 profile, ash::Shell::Get()->shelf_model()); | |
| 379 return launcher_controller_.get(); | |
| 380 } | |
| 381 | |
| 382 // Create a TestChromeLauncherControllerImpl instance. | |
| 383 TestChromeLauncherControllerImpl* CreateTestLauncherController( | |
| 384 Profile* profile) { | |
| 385 auto controller = base::MakeUnique<TestChromeLauncherControllerImpl>( | |
| 386 profile, ash::Shell::Get()->shelf_model()); | |
| 387 TestChromeLauncherControllerImpl* controller_weak = controller.get(); | |
| 388 launcher_controller_ = std::move(controller); | |
| 389 launcher_controller_->Init(); | |
| 390 return controller_weak; | |
| 391 } | |
| 392 | |
| 393 // ash::test::TestShellDelegate: | |
| 394 void ShelfShutdown() override { launcher_controller_.reset(); } | |
| 395 | |
| 396 private: | |
| 397 std::unique_ptr<ChromeLauncherControllerImpl> launcher_controller_; | |
| 398 | |
| 399 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherTestShellDelegate); | |
| 400 }; | |
| 401 | |
| 402 class ChromeLauncherControllerImplTest : public BrowserWithTestWindowTest { | |
| 403 protected: | |
| 404 ChromeLauncherControllerImplTest() | |
| 405 : BrowserWithTestWindowTest(Browser::TYPE_TABBED, false) {} | |
| 406 | |
| 407 ~ChromeLauncherControllerImplTest() override {} | |
| 408 | |
| 409 void SetUp() override { | |
| 410 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
| 411 command_line->AppendSwitch(switches::kUseFirstDisplayAsInternal); | |
| 412 | |
| 413 app_list::AppListSyncableServiceFactory::SetUseInTesting(); | |
| 414 | |
| 415 shell_delegate_ = new ChromeLauncherTestShellDelegate(); | |
| 416 ash_test_helper()->set_test_shell_delegate(shell_delegate_); | |
| 417 | |
| 418 BrowserWithTestWindowTest::SetUp(); | |
| 419 | |
| 420 if (!profile_manager_) { | |
| 421 profile_manager_.reset( | |
| 422 new TestingProfileManager(TestingBrowserProcess::GetGlobal())); | |
| 423 ASSERT_TRUE(profile_manager_->SetUp()); | |
| 424 } | |
| 425 | |
| 426 model_ = ash::Shell::Get()->shelf_controller()->model(); | |
| 427 model_observer_.reset(new TestShelfModelObserver); | |
| 428 model_->AddObserver(model_observer_.get()); | |
| 429 | |
| 430 base::DictionaryValue manifest; | |
| 431 manifest.SetString(extensions::manifest_keys::kName, | |
| 432 "launcher controller test extension"); | |
| 433 manifest.SetString(extensions::manifest_keys::kVersion, "1"); | |
| 434 manifest.SetString(extensions::manifest_keys::kDescription, | |
| 435 "for testing pinned apps"); | |
| 436 | |
| 437 base::DictionaryValue manifest_platform_app; | |
| 438 manifest_platform_app.SetString(extensions::manifest_keys::kName, | |
| 439 "launcher controller test platform app"); | |
| 440 manifest_platform_app.SetString(extensions::manifest_keys::kVersion, "1"); | |
| 441 manifest_platform_app.SetString(extensions::manifest_keys::kDescription, | |
| 442 "for testing pinned platform apps"); | |
| 443 manifest_platform_app.SetString(extensions::manifest_keys::kApp, "true"); | |
| 444 manifest_platform_app.Set(extensions::manifest_keys::kPlatformAppBackground, | |
| 445 base::MakeUnique<base::DictionaryValue>()); | |
| 446 auto scripts = base::MakeUnique<base::ListValue>(); | |
| 447 scripts->AppendString("main.js"); | |
| 448 manifest_platform_app.Set( | |
| 449 extensions::manifest_keys::kPlatformAppBackgroundScripts, | |
| 450 std::move(scripts)); | |
| 451 | |
| 452 extensions::TestExtensionSystem* extension_system( | |
| 453 static_cast<extensions::TestExtensionSystem*>( | |
| 454 extensions::ExtensionSystem::Get(profile()))); | |
| 455 extension_service_ = extension_system->CreateExtensionService( | |
| 456 base::CommandLine::ForCurrentProcess(), base::FilePath(), false); | |
| 457 extension_service_->Init(); | |
| 458 | |
| 459 if (auto_start_arc_test_) | |
| 460 arc_test_.SetUp(profile()); | |
| 461 | |
| 462 // Wait until |extension_system| is signaled as started. | |
| 463 base::RunLoop run_loop; | |
| 464 extension_system->ready().Post(FROM_HERE, run_loop.QuitClosure()); | |
| 465 run_loop.Run(); | |
| 466 | |
| 467 app_service_ = | |
| 468 app_list::AppListSyncableServiceFactory::GetForProfile(profile()); | |
| 469 StartAppSyncService(syncer::SyncDataList()); | |
| 470 | |
| 471 std::string error; | |
| 472 extension_chrome_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 473 manifest, Extension::NO_FLAGS, | |
| 474 extension_misc::kChromeAppId, &error); | |
| 475 extension1_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 476 manifest, Extension::NO_FLAGS, | |
| 477 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &error); | |
| 478 extension2_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 479 manifest, Extension::NO_FLAGS, | |
| 480 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", &error); | |
| 481 // Fake gmail extension. | |
| 482 base::DictionaryValue manifest_gmail; | |
| 483 manifest_gmail.SetString(extensions::manifest_keys::kName, | |
| 484 "Gmail launcher controller test extension"); | |
| 485 manifest_gmail.SetString(extensions::manifest_keys::kVersion, "1"); | |
| 486 manifest_gmail.SetString(extensions::manifest_keys::kDescription, | |
| 487 "for testing pinned Gmail"); | |
| 488 manifest_gmail.SetString(extensions::manifest_keys::kLaunchWebURL, | |
| 489 kGmailLaunchURL); | |
| 490 auto list = base::MakeUnique<base::ListValue>(); | |
| 491 list->AppendString("*://mail.google.com/mail/ca"); | |
| 492 manifest_gmail.Set(extensions::manifest_keys::kWebURLs, std::move(list)); | |
| 493 | |
| 494 extension3_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 495 manifest_gmail, Extension::NO_FLAGS, | |
| 496 extension_misc::kGmailAppId, &error); | |
| 497 | |
| 498 // Fake google docs extension. | |
| 499 extension4_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 500 manifest, Extension::NO_FLAGS, | |
| 501 extension_misc::kGoogleDocAppId, &error); | |
| 502 extension5_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 503 manifest, Extension::NO_FLAGS, | |
| 504 "cccccccccccccccccccccccccccccccc", &error); | |
| 505 extension6_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 506 manifest, Extension::NO_FLAGS, | |
| 507 "dddddddddddddddddddddddddddddddd", &error); | |
| 508 extension7_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 509 manifest, Extension::NO_FLAGS, | |
| 510 "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", &error); | |
| 511 extension8_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 512 manifest, Extension::NO_FLAGS, | |
| 513 "ffffffffffffffffffffffffffffffff", &error); | |
| 514 extension_platform_app_ = Extension::Create( | |
| 515 base::FilePath(), Manifest::UNPACKED, manifest_platform_app, | |
| 516 Extension::NO_FLAGS, "gggggggggggggggggggggggggggggggg", &error); | |
| 517 arc_support_host_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, | |
| 518 manifest, Extension::NO_FLAGS, | |
| 519 ArcSupportHost::kHostAppId, &error); | |
| 520 extension_service_->AddExtension(extension_chrome_.get()); | |
| 521 } | |
| 522 | |
| 523 // Creates a running platform V2 app (not pinned) of type |app_id|. | |
| 524 virtual void CreateRunningV2App(const std::string& app_id) { | |
| 525 DCHECK(!test_controller_); | |
| 526 // Change the created launcher controller into a V2 app controller. | |
| 527 std::unique_ptr<TestV2AppLauncherItemController> controller = | |
| 528 base::MakeUnique<TestV2AppLauncherItemController>(app_id); | |
| 529 test_controller_ = controller.get(); | |
| 530 ash::ShelfID id = launcher_controller_->InsertAppLauncherItem( | |
| 531 std::move(controller), ash::STATUS_RUNNING, model_->item_count(), | |
| 532 ash::TYPE_APP); | |
| 533 DCHECK(launcher_controller_->IsPlatformApp(id)); | |
| 534 } | |
| 535 | |
| 536 // Sets the stage for a multi user test. | |
| 537 virtual void SetUpMultiUserScenario(syncer::SyncChangeList* user_a, | |
| 538 syncer::SyncChangeList* user_b) { | |
| 539 InitLauncherController(); | |
| 540 EXPECT_EQ("AppList, Chrome", GetPinnedAppStatus()); | |
| 541 | |
| 542 // Set an empty pinned pref to begin with. | |
| 543 syncer::SyncChangeList sync_list; | |
| 544 InsertAddPinChange(&sync_list, 0, extension_misc::kChromeAppId); | |
| 545 SendPinChanges(sync_list, true); | |
| 546 EXPECT_EQ("AppList, Chrome", GetPinnedAppStatus()); | |
| 547 | |
| 548 // Assume all applications have been added already. | |
| 549 extension_service_->AddExtension(extension1_.get()); | |
| 550 extension_service_->AddExtension(extension2_.get()); | |
| 551 extension_service_->AddExtension(extension3_.get()); | |
| 552 extension_service_->AddExtension(extension4_.get()); | |
| 553 extension_service_->AddExtension(extension5_.get()); | |
| 554 extension_service_->AddExtension(extension6_.get()); | |
| 555 extension_service_->AddExtension(extension7_.get()); | |
| 556 extension_service_->AddExtension(extension8_.get()); | |
| 557 extension_service_->AddExtension(extension_platform_app_.get()); | |
| 558 // There should be nothing in the list by now. | |
| 559 EXPECT_EQ("AppList, Chrome", GetPinnedAppStatus()); | |
| 560 | |
| 561 // Set user a preferences. | |
| 562 InsertAddPinChange(user_a, 0, extension1_->id()); | |
| 563 InsertAddPinChange(user_a, 1, extension2_->id()); | |
| 564 InsertAddPinChange(user_a, 2, extension3_->id()); | |
| 565 InsertAddPinChange(user_a, 3, extension_platform_app_->id()); | |
| 566 InsertAddPinChange(user_a, 4, extension4_->id()); | |
| 567 InsertAddPinChange(user_a, 5, extension5_->id()); | |
| 568 InsertAddPinChange(user_a, 6, extension_misc::kChromeAppId); | |
| 569 | |
| 570 // Set user b preferences. | |
| 571 InsertAddPinChange(user_b, 0, extension6_->id()); | |
| 572 InsertAddPinChange(user_b, 1, extension7_->id()); | |
| 573 InsertAddPinChange(user_b, 2, extension8_->id()); | |
| 574 InsertAddPinChange(user_b, 3, extension_misc::kChromeAppId); | |
| 575 } | |
| 576 | |
| 577 void TearDown() override { | |
| 578 arc_test_.TearDown(); | |
| 579 model_->RemoveObserver(model_observer_.get()); | |
| 580 model_observer_.reset(); | |
| 581 launcher_controller_ = nullptr; | |
| 582 BrowserWithTestWindowTest::TearDown(); | |
| 583 } | |
| 584 | |
| 585 BrowserWindow* CreateBrowserWindow() override { | |
| 586 return CreateTestBrowserWindowAura(); | |
| 587 } | |
| 588 | |
| 589 std::unique_ptr<Browser> CreateBrowserWithTestWindowForProfile( | |
| 590 Profile* profile) { | |
| 591 TestBrowserWindow* browser_window = CreateTestBrowserWindowAura(); | |
| 592 new TestBrowserWindowOwner(browser_window); | |
| 593 return base::WrapUnique( | |
| 594 CreateBrowser(profile, Browser::TYPE_TABBED, false, browser_window)); | |
| 595 } | |
| 596 | |
| 597 void AddAppListLauncherItem() { | |
| 598 ash::ShelfItem app_list; | |
| 599 app_list.type = ash::TYPE_APP_LIST; | |
| 600 model_->Add(app_list); | |
| 601 } | |
| 602 | |
| 603 // Create a launcher controller instance, owned by the test shell delegate. | |
| 604 // Returns a pointer to the uninitialized controller. | |
| 605 ChromeLauncherControllerImpl* CreateLauncherController() { | |
| 606 launcher_controller_ = shell_delegate_->CreateLauncherController(profile()); | |
| 607 return launcher_controller_; | |
| 608 } | |
| 609 | |
| 610 // Create and initialize the controller, owned by the test shell delegate. | |
| 611 void InitLauncherController() { CreateLauncherController()->Init(); } | |
| 612 | |
| 613 // Create and initialize the controller; create a tab and show the browser. | |
| 614 void InitLauncherControllerWithBrowser() { | |
| 615 InitLauncherController(); | |
| 616 chrome::NewTab(browser()); | |
| 617 browser()->window()->Show(); | |
| 618 } | |
| 619 | |
| 620 // Destroy the launcher controller instance and clear the local pointer. | |
| 621 void ResetLauncherController() { | |
| 622 launcher_controller_ = nullptr; | |
| 623 shell_delegate_->ShelfShutdown(); | |
| 624 } | |
| 625 | |
| 626 // Destroy and recreate the controller; clear and reinitialize the ShelfModel. | |
| 627 // Returns a pointer to the uninitialized controller, owned by shell delegate. | |
| 628 // TODO(msw): This does not accurately represent ChromeLauncherControllerImpl | |
| 629 // lifetime or usage in production, and does not accurately simulate restarts. | |
| 630 ChromeLauncherControllerImpl* RecreateLauncherController() { | |
| 631 // Destroy any existing controller first; only one may exist at a time. | |
| 632 ResetLauncherController(); | |
| 633 while (model_->item_count() > 0) | |
| 634 model_->RemoveItemAt(0); | |
| 635 AddAppListLauncherItem(); | |
| 636 return CreateLauncherController(); | |
| 637 } | |
| 638 | |
| 639 void StartAppSyncService(const syncer::SyncDataList& init_sync_list) { | |
| 640 app_service_->MergeDataAndStartSyncing( | |
| 641 syncer::APP_LIST, init_sync_list, | |
| 642 base::MakeUnique<syncer::FakeSyncChangeProcessor>(), | |
| 643 base::MakeUnique<syncer::SyncErrorFactoryMock>()); | |
| 644 EXPECT_EQ(init_sync_list.size(), app_service_->sync_items().size()); | |
| 645 } | |
| 646 | |
| 647 void StopAppSyncService() { app_service_->StopSyncing(syncer::APP_LIST); } | |
| 648 | |
| 649 sync_preferences::PrefModelAssociator* GetPrefSyncService() { | |
| 650 sync_preferences::PrefServiceSyncable* pref_sync = | |
| 651 profile()->GetTestingPrefService(); | |
| 652 sync_preferences::PrefModelAssociator* pref_sync_service = | |
| 653 reinterpret_cast<sync_preferences::PrefModelAssociator*>( | |
| 654 pref_sync->GetSyncableService(syncer::PREFERENCES)); | |
| 655 return pref_sync_service; | |
| 656 } | |
| 657 | |
| 658 void StartPrefSyncService(const syncer::SyncDataList& init_sync_list) { | |
| 659 syncer::SyncMergeResult r = GetPrefSyncService()->MergeDataAndStartSyncing( | |
| 660 syncer::PREFERENCES, init_sync_list, | |
| 661 base::MakeUnique<syncer::FakeSyncChangeProcessor>(), | |
| 662 base::MakeUnique<syncer::SyncErrorFactoryMock>()); | |
| 663 EXPECT_FALSE(r.error().IsSet()); | |
| 664 } | |
| 665 | |
| 666 void StartPrefSyncServiceForPins(const base::ListValue& init_value) { | |
| 667 syncer::SyncDataList init_sync_list; | |
| 668 std::string serialized; | |
| 669 JSONStringValueSerializer json(&serialized); | |
| 670 json.Serialize(init_value); | |
| 671 sync_pb::EntitySpecifics one; | |
| 672 sync_pb::PreferenceSpecifics* pref_one = one.mutable_preference(); | |
| 673 pref_one->set_name(prefs::kPinnedLauncherApps); | |
| 674 pref_one->set_value(serialized); | |
| 675 init_sync_list.push_back(syncer::SyncData::CreateRemoteData( | |
| 676 1, one, base::Time(), syncer::AttachmentIdList(), | |
| 677 syncer::AttachmentServiceProxyForTest::Create())); | |
| 678 StartPrefSyncService(init_sync_list); | |
| 679 } | |
| 680 | |
| 681 void StopPrefSyncService() { | |
| 682 GetPrefSyncService()->StopSyncing(syncer::PREFERENCES); | |
| 683 } | |
| 684 | |
| 685 void SetAppIconLoader(std::unique_ptr<AppIconLoader> loader) { | |
| 686 std::vector<std::unique_ptr<AppIconLoader>> loaders; | |
| 687 loaders.push_back(std::move(loader)); | |
| 688 launcher_controller_->SetAppIconLoadersForTest(loaders); | |
| 689 } | |
| 690 | |
| 691 void SetAppIconLoaders(std::unique_ptr<AppIconLoader> loader1, | |
| 692 std::unique_ptr<AppIconLoader> loader2) { | |
| 693 std::vector<std::unique_ptr<AppIconLoader>> loaders; | |
| 694 loaders.push_back(std::move(loader1)); | |
| 695 loaders.push_back(std::move(loader2)); | |
| 696 launcher_controller_->SetAppIconLoadersForTest(loaders); | |
| 697 } | |
| 698 | |
| 699 void SetLauncherControllerHelper(LauncherControllerHelper* helper) { | |
| 700 launcher_controller_->SetLauncherControllerHelperForTest( | |
| 701 base::WrapUnique<LauncherControllerHelper>(helper)); | |
| 702 } | |
| 703 | |
| 704 void InsertPrefValue(base::ListValue* pref_value, | |
| 705 int index, | |
| 706 const std::string& extension_id) { | |
| 707 auto entry = base::MakeUnique<base::DictionaryValue>(); | |
| 708 entry->SetString(ash::launcher::kPinnedAppsPrefAppIDPath, extension_id); | |
| 709 pref_value->Insert(index, std::move(entry)); | |
| 710 } | |
| 711 | |
| 712 void InsertRemoveAllPinsChange(syncer::SyncChangeList* list) { | |
| 713 for (const auto& sync_peer : app_service_->sync_items()) { | |
| 714 sync_pb::EntitySpecifics specifics; | |
| 715 sync_pb::AppListSpecifics* app_list_specifics = | |
| 716 specifics.mutable_app_list(); | |
| 717 app_list_specifics->set_item_id(sync_peer.first); | |
| 718 app_list_specifics->set_item_type(sync_pb::AppListSpecifics::TYPE_APP); | |
| 719 syncer::SyncData sync_data = | |
| 720 syncer::SyncData::CreateLocalData(sync_peer.first, "Test", specifics); | |
| 721 list->push_back(syncer::SyncChange( | |
| 722 FROM_HERE, syncer::SyncChange::ACTION_DELETE, sync_data)); | |
| 723 } | |
| 724 } | |
| 725 | |
| 726 syncer::StringOrdinal GeneratePinPosition(int position) { | |
| 727 syncer::StringOrdinal ordinal_position = | |
| 728 syncer::StringOrdinal::CreateInitialOrdinal(); | |
| 729 for (int i = 0; i < position; ++i) | |
| 730 ordinal_position = ordinal_position.CreateAfter(); | |
| 731 return ordinal_position; | |
| 732 } | |
| 733 | |
| 734 void InsertPinChange(syncer::SyncChangeList* list, | |
| 735 int position, | |
| 736 bool add_pin_change, | |
| 737 const std::string& app_id, | |
| 738 syncer::SyncChange::SyncChangeType type) { | |
| 739 sync_pb::EntitySpecifics specifics; | |
| 740 sync_pb::AppListSpecifics* app_list_specifics = | |
| 741 specifics.mutable_app_list(); | |
| 742 app_list_specifics->set_item_id(app_id); | |
| 743 app_list_specifics->set_item_type(sync_pb::AppListSpecifics::TYPE_APP); | |
| 744 if (add_pin_change) { | |
| 745 if (position >= 0) { | |
| 746 app_list_specifics->set_item_pin_ordinal( | |
| 747 GeneratePinPosition(position).ToInternalValue()); | |
| 748 } else { | |
| 749 app_list_specifics->set_item_pin_ordinal(std::string()); | |
| 750 } | |
| 751 } | |
| 752 syncer::SyncData sync_data = | |
| 753 syncer::SyncData::CreateLocalData(app_id, "Test", specifics); | |
| 754 list->push_back(syncer::SyncChange(FROM_HERE, type, sync_data)); | |
| 755 } | |
| 756 | |
| 757 void InsertAddPinChange(syncer::SyncChangeList* list, | |
| 758 int position, | |
| 759 const std::string& app_id) { | |
| 760 InsertPinChange(list, position, true, app_id, | |
| 761 syncer::SyncChange::ACTION_ADD); | |
| 762 } | |
| 763 | |
| 764 void InsertUpdatePinChange(syncer::SyncChangeList* list, | |
| 765 int position, | |
| 766 const std::string& app_id) { | |
| 767 InsertPinChange(list, position, true, app_id, | |
| 768 syncer::SyncChange::ACTION_UPDATE); | |
| 769 } | |
| 770 | |
| 771 void InsertRemovePinChange(syncer::SyncChangeList* list, | |
| 772 const std::string& app_id) { | |
| 773 InsertPinChange(list, -1, true, app_id, syncer::SyncChange::ACTION_UPDATE); | |
| 774 } | |
| 775 | |
| 776 void InsertLegacyPinChange(syncer::SyncChangeList* list, | |
| 777 const std::string& app_id) { | |
| 778 InsertPinChange(list, -1, false, app_id, syncer::SyncChange::ACTION_UPDATE); | |
| 779 } | |
| 780 | |
| 781 void ResetPinModel() { | |
| 782 syncer::SyncChangeList sync_list; | |
| 783 InsertRemoveAllPinsChange(&sync_list); | |
| 784 InsertAddPinChange(&sync_list, 0, kDummyAppId); | |
| 785 app_service_->ProcessSyncChanges(FROM_HERE, sync_list); | |
| 786 } | |
| 787 | |
| 788 void SendPinChanges(const syncer::SyncChangeList& sync_list, | |
| 789 bool reset_pin_model) { | |
| 790 if (!reset_pin_model) { | |
| 791 app_service_->ProcessSyncChanges(FROM_HERE, sync_list); | |
| 792 } else { | |
| 793 syncer::SyncChangeList combined_sync_list; | |
| 794 InsertRemoveAllPinsChange(&combined_sync_list); | |
| 795 combined_sync_list.insert(combined_sync_list.end(), sync_list.begin(), | |
| 796 sync_list.end()); | |
| 797 app_service_->ProcessSyncChanges(FROM_HERE, combined_sync_list); | |
| 798 } | |
| 799 } | |
| 800 | |
| 801 // Set the index at which the chrome icon should be. | |
| 802 void SetShelfChromeIconIndex(int index) { | |
| 803 DCHECK( | |
| 804 app_service_->GetPinPosition(extension_misc::kChromeAppId).IsValid()); | |
| 805 syncer::StringOrdinal chrome_position; | |
| 806 chrome_position = index == 0 ? GeneratePinPosition(0).CreateBefore() | |
| 807 : GeneratePinPosition(index - 1).CreateBetween( | |
| 808 GeneratePinPosition(index)); | |
| 809 | |
| 810 syncer::SyncChangeList sync_list; | |
| 811 sync_pb::EntitySpecifics specifics; | |
| 812 sync_pb::AppListSpecifics* app_list_specifics = | |
| 813 specifics.mutable_app_list(); | |
| 814 app_list_specifics->set_item_id(extension_misc::kChromeAppId); | |
| 815 app_list_specifics->set_item_type(sync_pb::AppListSpecifics::TYPE_APP); | |
| 816 app_list_specifics->set_item_pin_ordinal(chrome_position.ToInternalValue()); | |
| 817 syncer::SyncData sync_data = syncer::SyncData::CreateLocalData( | |
| 818 extension_misc::kChromeAppId, "Test", specifics); | |
| 819 sync_list.push_back(syncer::SyncChange( | |
| 820 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data)); | |
| 821 app_service_->ProcessSyncChanges(FROM_HERE, sync_list); | |
| 822 } | |
| 823 | |
| 824 // Gets the IDs of the currently pinned app items. | |
| 825 void GetPinnedAppIds(ChromeLauncherControllerImpl* controller, | |
| 826 std::vector<std::string>* app_ids) { | |
| 827 app_ids->clear(); | |
| 828 for (const auto& item : model_->items()) { | |
| 829 if (item.type == ash::TYPE_PINNED_APP) | |
| 830 app_ids->push_back(item.app_launch_id.app_id()); | |
| 831 } | |
| 832 } | |
| 833 | |
| 834 // Get the setup of the currently shown launcher items in one string. | |
| 835 // Each pinned element will start with a big letter, each running but not | |
| 836 // pinned V1 app will start with a small letter and each running but not | |
| 837 // pinned V2 app will start with a '*' + small letter. | |
| 838 std::string GetPinnedAppStatus() { | |
| 839 std::string result; | |
| 840 for (int i = 0; i < model_->item_count(); i++) { | |
| 841 if (!result.empty()) | |
| 842 result.append(", "); | |
| 843 switch (model_->items()[i].type) { | |
| 844 case ash::TYPE_APP: { | |
| 845 if (launcher_controller_->IsPlatformApp(model_->items()[i].id)) | |
| 846 result += "*"; | |
| 847 const std::string& app = | |
| 848 launcher_controller_->GetAppIDForShelfID(model_->items()[i].id); | |
| 849 EXPECT_FALSE(launcher_controller_->IsAppPinned(app)); | |
| 850 if (app == extension1_->id()) { | |
| 851 result += "app1"; | |
| 852 } else if (app == extension2_->id()) { | |
| 853 result += "app2"; | |
| 854 } else if (app == extension3_->id()) { | |
| 855 result += "app3"; | |
| 856 } else if (app == extension4_->id()) { | |
| 857 result += "app4"; | |
| 858 } else if (app == extension5_->id()) { | |
| 859 result += "app5"; | |
| 860 } else if (app == extension6_->id()) { | |
| 861 result += "app6"; | |
| 862 } else if (app == extension7_->id()) { | |
| 863 result += "app7"; | |
| 864 } else if (app == extension8_->id()) { | |
| 865 result += "app8"; | |
| 866 } else if (app == extension_platform_app_->id()) { | |
| 867 result += "platform_app"; | |
| 868 } else { | |
| 869 result += "unknown"; | |
| 870 } | |
| 871 break; | |
| 872 } | |
| 873 case ash::TYPE_PINNED_APP: { | |
| 874 if (launcher_controller_->IsPlatformApp(model_->items()[i].id)) | |
| 875 result += "*"; | |
| 876 const std::string& app = | |
| 877 launcher_controller_->GetAppIDForShelfID(model_->items()[i].id); | |
| 878 EXPECT_TRUE(launcher_controller_->IsAppPinned(app)); | |
| 879 if (app == extension1_->id()) { | |
| 880 result += "App1"; | |
| 881 } else if (app == extension2_->id()) { | |
| 882 result += "App2"; | |
| 883 } else if (app == extension3_->id()) { | |
| 884 result += "App3"; | |
| 885 } else if (app == extension4_->id()) { | |
| 886 result += "App4"; | |
| 887 } else if (app == extension5_->id()) { | |
| 888 result += "App5"; | |
| 889 } else if (app == extension6_->id()) { | |
| 890 result += "App6"; | |
| 891 } else if (app == extension7_->id()) { | |
| 892 result += "App7"; | |
| 893 } else if (app == extension8_->id()) { | |
| 894 result += "App8"; | |
| 895 } else if (app == extension_platform_app_->id()) { | |
| 896 result += "Platform_App"; | |
| 897 } else if (app == arc_support_host_->id()) { | |
| 898 result += "Play Store"; | |
| 899 } else { | |
| 900 bool arc_app_found = false; | |
| 901 for (const auto& arc_app : arc_test_.fake_apps()) { | |
| 902 if (app == ArcAppTest::GetAppId(arc_app)) { | |
| 903 result += arc_app.name; | |
| 904 arc_app_found = true; | |
| 905 break; | |
| 906 } | |
| 907 } | |
| 908 if (!arc_app_found) | |
| 909 result += "unknown"; | |
| 910 } | |
| 911 break; | |
| 912 } | |
| 913 case ash::TYPE_BROWSER_SHORTCUT: | |
| 914 result += "Chrome"; | |
| 915 break; | |
| 916 case ash::TYPE_APP_LIST: | |
| 917 result += "AppList"; | |
| 918 break; | |
| 919 default: | |
| 920 result += "Unknown"; | |
| 921 break; | |
| 922 } | |
| 923 } | |
| 924 return result; | |
| 925 } | |
| 926 | |
| 927 // Remember the order of unpinned but running applications for the current | |
| 928 // user. | |
| 929 void RememberUnpinnedRunningApplicationOrder() { | |
| 930 launcher_controller_->RememberUnpinnedRunningApplicationOrder(); | |
| 931 } | |
| 932 | |
| 933 // Restore the order of running but unpinned applications for a given user. | |
| 934 void RestoreUnpinnedRunningApplicationOrder(const AccountId& account_id) { | |
| 935 launcher_controller_->RestoreUnpinnedRunningApplicationOrder( | |
| 936 account_id.GetUserEmail()); | |
| 937 } | |
| 938 | |
| 939 void SendListOfArcApps() { | |
| 940 arc_test_.app_instance()->RefreshAppList(); | |
| 941 arc_test_.app_instance()->SendRefreshAppList(arc_test_.fake_apps()); | |
| 942 } | |
| 943 | |
| 944 void SendListOfArcShortcuts() { | |
| 945 arc_test_.app_instance()->SendInstallShortcuts(arc_test_.fake_shortcuts()); | |
| 946 } | |
| 947 | |
| 948 void UninstallArcApps() { | |
| 949 arc_test_.app_instance()->RefreshAppList(); | |
| 950 arc_test_.app_instance()->SendRefreshAppList( | |
| 951 std::vector<arc::mojom::AppInfo>()); | |
| 952 } | |
| 953 | |
| 954 // TODO(victorhsieh): Add test coverage for when ARC is started regardless | |
| 955 // Play Store opt-in status, and the followed opt-in and opt-out. | |
| 956 void EnablePlayStore(bool enabled) { | |
| 957 arc::SetArcPlayStoreEnabledForProfile(profile(), enabled); | |
| 958 base::RunLoop().RunUntilIdle(); | |
| 959 } | |
| 960 | |
| 961 void EnableTabletMode(bool enable) { | |
| 962 ash::MaximizeModeController* controller = | |
| 963 ash::Shell::Get()->maximize_mode_controller(); | |
| 964 controller->EnableMaximizeModeWindowManager(enable); | |
| 965 } | |
| 966 | |
| 967 void ValidateArcState(bool arc_enabled, | |
| 968 bool arc_managed, | |
| 969 arc::ArcSessionManager::State state, | |
| 970 const std::string& pin_status) { | |
| 971 EXPECT_EQ(arc_enabled, arc::IsArcPlayStoreEnabledForProfile(profile())); | |
| 972 EXPECT_EQ(arc_managed, | |
| 973 arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile())); | |
| 974 EXPECT_EQ(state, arc_test_.arc_session_manager()->state()); | |
| 975 EXPECT_EQ(pin_status, GetPinnedAppStatus()); | |
| 976 } | |
| 977 | |
| 978 // Creates app window and set optional ARC application id. | |
| 979 views::Widget* CreateArcWindow(const std::string& window_app_id) { | |
| 980 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); | |
| 981 params.bounds = gfx::Rect(5, 5, 20, 20); | |
| 982 params.context = GetContext(); | |
| 983 views::Widget* widget = new views::Widget(); | |
| 984 widget->Init(params); | |
| 985 // Set ARC id before showing the window to be recognized in | |
| 986 // ArcAppWindowLauncherController. | |
| 987 exo::ShellSurface::SetApplicationId(widget->GetNativeWindow(), | |
| 988 window_app_id); | |
| 989 widget->Show(); | |
| 990 widget->Activate(); | |
| 991 return widget; | |
| 992 } | |
| 993 | |
| 994 arc::mojom::AppInfo CreateAppInfo(const std::string& name, | |
| 995 const std::string& activity, | |
| 996 const std::string& package_name, | |
| 997 OrientationLock lock) { | |
| 998 arc::mojom::AppInfo appinfo; | |
| 999 appinfo.name = name; | |
| 1000 appinfo.package_name = package_name; | |
| 1001 appinfo.activity = activity; | |
| 1002 appinfo.orientation_lock = lock; | |
| 1003 return appinfo; | |
| 1004 } | |
| 1005 | |
| 1006 std::string AddArcAppAndShortcut(const arc::mojom::AppInfo& app_info) { | |
| 1007 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs(); | |
| 1008 // Adding app to the prefs, and check that the app is accessible by id. | |
| 1009 prefs->AddAppAndShortcut( | |
| 1010 true /* app_ready */, app_info.name, app_info.package_name, | |
| 1011 app_info.activity, std::string() /* intent_uri */, | |
| 1012 std::string() /* icon_resource_id */, false /* sticky */, | |
| 1013 true /* notifications_enabled */, false /* shortcut */, | |
| 1014 true /* launchable */, app_info.orientation_lock); | |
| 1015 const std::string app_id = | |
| 1016 ArcAppListPrefs::GetAppId(app_info.package_name, app_info.activity); | |
| 1017 EXPECT_TRUE(prefs->GetApp(app_id)); | |
| 1018 return app_id; | |
| 1019 } | |
| 1020 | |
| 1021 void NotifyOnTaskCreated(const arc::mojom::AppInfo& appinfo, | |
| 1022 int32_t task_id) { | |
| 1023 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs(); | |
| 1024 prefs->OnTaskCreated(task_id, appinfo.package_name, appinfo.activity, | |
| 1025 appinfo.name, std::string()); | |
| 1026 } | |
| 1027 | |
| 1028 void NotifyOnTaskOrientationLockRequested(int32_t task_id, | |
| 1029 OrientationLock lock) { | |
| 1030 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs(); | |
| 1031 prefs->OnTaskOrientationLockRequested(task_id, lock); | |
| 1032 } | |
| 1033 | |
| 1034 // Needed for extension service & friends to work. | |
| 1035 scoped_refptr<Extension> extension_chrome_; | |
| 1036 scoped_refptr<Extension> extension1_; | |
| 1037 scoped_refptr<Extension> extension2_; | |
| 1038 scoped_refptr<Extension> extension3_; | |
| 1039 scoped_refptr<Extension> extension4_; | |
| 1040 scoped_refptr<Extension> extension5_; | |
| 1041 scoped_refptr<Extension> extension6_; | |
| 1042 scoped_refptr<Extension> extension7_; | |
| 1043 scoped_refptr<Extension> extension8_; | |
| 1044 scoped_refptr<Extension> extension_platform_app_; | |
| 1045 scoped_refptr<Extension> arc_support_host_; | |
| 1046 | |
| 1047 ArcAppTest arc_test_; | |
| 1048 bool auto_start_arc_test_ = false; | |
| 1049 ChromeLauncherControllerImpl* launcher_controller_ = nullptr; | |
| 1050 ChromeLauncherTestShellDelegate* shell_delegate_ = nullptr; | |
| 1051 std::unique_ptr<TestShelfModelObserver> model_observer_; | |
| 1052 ash::ShelfModel* model_ = nullptr; | |
| 1053 std::unique_ptr<TestingProfileManager> profile_manager_; | |
| 1054 | |
| 1055 // |item_delegate_manager_| owns |test_controller_|. | |
| 1056 ash::ShelfItemDelegate* test_controller_ = nullptr; | |
| 1057 | |
| 1058 ExtensionService* extension_service_ = nullptr; | |
| 1059 | |
| 1060 app_list::AppListSyncableService* app_service_ = nullptr; | |
| 1061 | |
| 1062 private: | |
| 1063 TestBrowserWindow* CreateTestBrowserWindowAura() { | |
| 1064 std::unique_ptr<aura::Window> window(new aura::Window(nullptr)); | |
| 1065 window->set_id(0); | |
| 1066 window->SetType(ui::wm::WINDOW_TYPE_NORMAL); | |
| 1067 window->Init(ui::LAYER_TEXTURED); | |
| 1068 aura::client::ParentWindowWithContext(window.get(), GetContext(), | |
| 1069 gfx::Rect(200, 200)); | |
| 1070 | |
| 1071 return new TestBrowserWindowAura(std::move(window)); | |
| 1072 } | |
| 1073 | |
| 1074 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerImplTest); | |
| 1075 }; | |
| 1076 | |
| 1077 class ChromeLauncherControllerImplWithArcTest | |
| 1078 : public ChromeLauncherControllerImplTest, | |
| 1079 public ::testing::WithParamInterface<bool> { | |
| 1080 protected: | |
| 1081 ChromeLauncherControllerImplWithArcTest() { auto_start_arc_test_ = true; } | |
| 1082 ~ChromeLauncherControllerImplWithArcTest() override {} | |
| 1083 | |
| 1084 void SetUp() override { | |
| 1085 if (GetParam()) | |
| 1086 arc::SetArcAlwaysStartForTesting(); | |
| 1087 ChromeLauncherControllerImplTest::SetUp(); | |
| 1088 } | |
| 1089 | |
| 1090 private: | |
| 1091 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerImplWithArcTest); | |
| 1092 }; | |
| 1093 | |
| 1094 INSTANTIATE_TEST_CASE_P(, | |
| 1095 ChromeLauncherControllerImplWithArcTest, | |
| 1096 ::testing::Bool()); | |
| 1097 | |
| 1098 // Watches WebContents and blocks until it is destroyed. This is needed for | |
| 1099 // the destruction of a V2 application. | |
| 1100 class WebContentsDestroyedWatcher : public content::WebContentsObserver { | |
| 1101 public: | |
| 1102 explicit WebContentsDestroyedWatcher(content::WebContents* web_contents) | |
| 1103 : content::WebContentsObserver(web_contents), | |
| 1104 message_loop_runner_(new content::MessageLoopRunner) { | |
| 1105 EXPECT_TRUE(web_contents != NULL); | |
| 1106 } | |
| 1107 ~WebContentsDestroyedWatcher() override {} | |
| 1108 | |
| 1109 // Waits until the WebContents is destroyed. | |
| 1110 void Wait() { | |
| 1111 message_loop_runner_->Run(); | |
| 1112 } | |
| 1113 | |
| 1114 private: | |
| 1115 // Overridden WebContentsObserver methods. | |
| 1116 void WebContentsDestroyed() override { message_loop_runner_->Quit(); } | |
| 1117 | |
| 1118 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; | |
| 1119 | |
| 1120 DISALLOW_COPY_AND_ASSIGN(WebContentsDestroyedWatcher); | |
| 1121 }; | |
| 1122 | |
| 1123 // A V1 windowed application. | |
| 1124 class V1App : public TestBrowserWindow { | |
| 1125 public: | |
| 1126 V1App(Profile* profile, const std::string& app_name) { | |
| 1127 Browser::CreateParams params = Browser::CreateParams::CreateForApp( | |
| 1128 kCrxAppPrefix + app_name, true /* trusted_source */, gfx::Rect(), | |
| 1129 profile, true); | |
| 1130 params.window = this; | |
| 1131 browser_.reset(new Browser(params)); | |
| 1132 chrome::AddTabAt(browser_.get(), GURL(), 0, true); | |
| 1133 } | |
| 1134 | |
| 1135 ~V1App() override { | |
| 1136 // close all tabs. Note that we do not need to destroy the browser itself. | |
| 1137 browser_->tab_strip_model()->CloseAllTabs(); | |
| 1138 } | |
| 1139 | |
| 1140 Browser* browser() { return browser_.get(); } | |
| 1141 | |
| 1142 private: | |
| 1143 // The associated browser with this app. | |
| 1144 std::unique_ptr<Browser> browser_; | |
| 1145 | |
| 1146 DISALLOW_COPY_AND_ASSIGN(V1App); | |
| 1147 }; | |
| 1148 | |
| 1149 // A V2 application window created with an |extension| and for a |profile|. | |
| 1150 // Upon destruction it will properly close the application; supports panels too. | |
| 1151 class V2App { | |
| 1152 public: | |
| 1153 V2App(Profile* profile, | |
| 1154 const extensions::Extension* extension, | |
| 1155 extensions::AppWindow::WindowType window_type = | |
| 1156 extensions::AppWindow::WINDOW_TYPE_DEFAULT) | |
| 1157 : creator_web_contents_( | |
| 1158 content::WebContentsTester::CreateTestWebContents(profile, | |
| 1159 nullptr)) { | |
| 1160 window_ = new extensions::AppWindow(profile, new ChromeAppDelegate(true), | |
| 1161 extension); | |
| 1162 extensions::AppWindow::CreateParams params; | |
| 1163 params.window_type = window_type; | |
| 1164 // Note: normally, the creator RFH is the background page of the | |
| 1165 // app/extension | |
| 1166 // calling chrome.app.window.create. For unit testing purposes, just passing | |
| 1167 // in a random RenderFrameHost is Good Enoughâ„¢. | |
| 1168 window_->Init(GURL(std::string()), | |
| 1169 new extensions::AppWindowContentsImpl(window_), | |
| 1170 creator_web_contents_->GetMainFrame(), params); | |
| 1171 } | |
| 1172 | |
| 1173 virtual ~V2App() { | |
| 1174 WebContentsDestroyedWatcher destroyed_watcher(window_->web_contents()); | |
| 1175 window_->GetBaseWindow()->Close(); | |
| 1176 destroyed_watcher.Wait(); | |
| 1177 } | |
| 1178 | |
| 1179 extensions::AppWindow* window() { return window_; } | |
| 1180 | |
| 1181 private: | |
| 1182 std::unique_ptr<content::WebContents> creator_web_contents_; | |
| 1183 | |
| 1184 // The app window which represents the application. Note that the window | |
| 1185 // deletes itself asynchronously after window_->GetBaseWindow()->Close() gets | |
| 1186 // called. | |
| 1187 extensions::AppWindow* window_; | |
| 1188 | |
| 1189 DISALLOW_COPY_AND_ASSIGN(V2App); | |
| 1190 }; | |
| 1191 | |
| 1192 // The testing framework to test multi profile scenarios. | |
| 1193 class MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest | |
| 1194 : public ChromeLauncherControllerImplTest { | |
| 1195 protected: | |
| 1196 MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest() {} | |
| 1197 | |
| 1198 ~MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest() | |
| 1199 override {} | |
| 1200 | |
| 1201 // Overwrite the Setup function to enable multi profile and needed objects. | |
| 1202 void SetUp() override { | |
| 1203 profile_manager_.reset( | |
| 1204 new TestingProfileManager(TestingBrowserProcess::GetGlobal())); | |
| 1205 | |
| 1206 ASSERT_TRUE(profile_manager_->SetUp()); | |
| 1207 | |
| 1208 // AvatarMenu and multiple profiles works after user logged in. | |
| 1209 profile_manager_->SetLoggedIn(true); | |
| 1210 | |
| 1211 // Initialize the UserManager singleton to a fresh FakeUserManager instance. | |
| 1212 user_manager_enabler_.reset(new chromeos::ScopedUserManagerEnabler( | |
| 1213 new chromeos::FakeChromeUserManager)); | |
| 1214 | |
| 1215 // Initialize the WallpaperManager singleton. | |
| 1216 chromeos::WallpaperManager::Initialize(); | |
| 1217 | |
| 1218 // Initialize the rest. | |
| 1219 ChromeLauncherControllerImplTest::SetUp(); | |
| 1220 shell_delegate_->set_multi_profiles_enabled(true); | |
| 1221 } | |
| 1222 | |
| 1223 void TearDown() override { | |
| 1224 ChromeLauncherControllerImplTest::TearDown(); | |
| 1225 user_manager_enabler_.reset(); | |
| 1226 for (ProfileToNameMap::iterator it = created_profiles_.begin(); | |
| 1227 it != created_profiles_.end(); ++it) | |
| 1228 profile_manager_->DeleteTestingProfile(it->second); | |
| 1229 chromeos::WallpaperManager::Shutdown(); | |
| 1230 | |
| 1231 // A Task is leaked if we don't destroy everything, then run the message | |
| 1232 // loop. | |
| 1233 base::RunLoop().RunUntilIdle(); | |
| 1234 } | |
| 1235 | |
| 1236 // Creates a profile for a given |user_name|. Note that this class will keep | |
| 1237 // the ownership of the created object. | |
| 1238 TestingProfile* CreateMultiUserProfile(const std::string& user_name) { | |
| 1239 const std::string email_string = user_name + "@example.com"; | |
| 1240 const AccountId account_id(AccountId::FromUserEmail(email_string)); | |
| 1241 // Add a user to the fake user manager. | |
| 1242 GetFakeUserManager()->AddUser(account_id); | |
| 1243 | |
| 1244 GetFakeUserManager()->LoginUser(account_id); | |
| 1245 | |
| 1246 TestingProfile* profile = | |
| 1247 profile_manager()->CreateTestingProfile(account_id.GetUserEmail()); | |
| 1248 EXPECT_TRUE(profile); | |
| 1249 | |
| 1250 // Remember the profile name so that we can destroy it upon destruction. | |
| 1251 created_profiles_[profile] = account_id.GetUserEmail(); | |
| 1252 if (chrome::MultiUserWindowManager::GetInstance()) | |
| 1253 chrome::MultiUserWindowManager::GetInstance()->AddUser(profile); | |
| 1254 if (launcher_controller_) | |
| 1255 launcher_controller_->AdditionalUserAddedToSession(profile); | |
| 1256 return profile; | |
| 1257 } | |
| 1258 | |
| 1259 // Switch to another user. | |
| 1260 void SwitchActiveUser(const AccountId& account_id) { | |
| 1261 GetFakeUserManager()->SwitchActiveUser(account_id); | |
| 1262 chrome::MultiUserWindowManagerChromeOS* manager = | |
| 1263 static_cast<chrome::MultiUserWindowManagerChromeOS*>( | |
| 1264 chrome::MultiUserWindowManager::GetInstance()); | |
| 1265 manager->SetAnimationSpeedForTest( | |
| 1266 chrome::MultiUserWindowManagerChromeOS::ANIMATION_SPEED_DISABLED); | |
| 1267 manager->ActiveUserChanged(GetFakeUserManager()->FindUser(account_id)); | |
| 1268 launcher_controller_->browser_status_monitor_for_test()->ActiveUserChanged( | |
| 1269 account_id.GetUserEmail()); | |
| 1270 | |
| 1271 for (const auto& controller : | |
| 1272 launcher_controller_->app_window_controllers_for_test()) { | |
| 1273 controller->ActiveUserChanged(account_id.GetUserEmail()); | |
| 1274 } | |
| 1275 } | |
| 1276 | |
| 1277 // Creates a browser with a |profile| and load a tab with a |title| and |url|. | |
| 1278 std::unique_ptr<Browser> CreateBrowserAndTabWithProfile( | |
| 1279 Profile* profile, | |
| 1280 const std::string& title, | |
| 1281 const std::string& url) { | |
| 1282 std::unique_ptr<Browser> browser( | |
| 1283 CreateBrowserWithTestWindowForProfile(profile)); | |
| 1284 chrome::NewTab(browser.get()); | |
| 1285 | |
| 1286 browser->window()->Show(); | |
| 1287 NavigateAndCommitActiveTabWithTitle(browser.get(), GURL(url), | |
| 1288 ASCIIToUTF16(title)); | |
| 1289 return browser; | |
| 1290 } | |
| 1291 | |
| 1292 // Creates a running V1 application. | |
| 1293 // Note that with the use of the launcher_controller_helper as done below, | |
| 1294 // this is only usable with a single v1 application. | |
| 1295 V1App* CreateRunningV1App(Profile* profile, | |
| 1296 const std::string& app_name, | |
| 1297 const std::string& url) { | |
| 1298 V1App* v1_app = new V1App(profile, app_name); | |
| 1299 NavigateAndCommitActiveTabWithTitle(v1_app->browser(), GURL(url), | |
| 1300 base::string16()); | |
| 1301 return v1_app; | |
| 1302 } | |
| 1303 | |
| 1304 // Override BrowserWithTestWindowTest: | |
| 1305 TestingProfile* CreateProfile() override { | |
| 1306 return CreateMultiUserProfile("user1"); | |
| 1307 } | |
| 1308 void DestroyProfile(TestingProfile* profile) override { | |
| 1309 // Delete the profile through our profile manager. | |
| 1310 ProfileToNameMap::iterator it = created_profiles_.find(profile); | |
| 1311 DCHECK(it != created_profiles_.end()); | |
| 1312 profile_manager_->DeleteTestingProfile(it->second); | |
| 1313 created_profiles_.erase(it); | |
| 1314 } | |
| 1315 | |
| 1316 private: | |
| 1317 typedef std::map<Profile*, std::string> ProfileToNameMap; | |
| 1318 TestingProfileManager* profile_manager() { return profile_manager_.get(); } | |
| 1319 | |
| 1320 chromeos::FakeChromeUserManager* GetFakeUserManager() { | |
| 1321 return static_cast<chromeos::FakeChromeUserManager*>( | |
| 1322 user_manager::UserManager::Get()); | |
| 1323 } | |
| 1324 | |
| 1325 std::unique_ptr<chromeos::ScopedUserManagerEnabler> user_manager_enabler_; | |
| 1326 | |
| 1327 ProfileToNameMap created_profiles_; | |
| 1328 | |
| 1329 DISALLOW_COPY_AND_ASSIGN( | |
| 1330 MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest); | |
| 1331 }; | |
| 1332 | |
| 1333 class ChromeLauncherControllerImplMultiProfileWithArcTest | |
| 1334 : public MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest
, // NOLINT(whitespace/line_length) | |
| 1335 public ::testing::WithParamInterface<bool> { | |
| 1336 protected: | |
| 1337 ChromeLauncherControllerImplMultiProfileWithArcTest() { | |
| 1338 auto_start_arc_test_ = true; | |
| 1339 } | |
| 1340 ~ChromeLauncherControllerImplMultiProfileWithArcTest() override {} | |
| 1341 | |
| 1342 void SetUp() override { | |
| 1343 if (GetParam()) | |
| 1344 arc::SetArcAlwaysStartForTesting(); | |
| 1345 MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest:: | |
| 1346 SetUp(); | |
| 1347 } | |
| 1348 | |
| 1349 private: | |
| 1350 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerImplMultiProfileWithArcTest); | |
| 1351 }; | |
| 1352 | |
| 1353 INSTANTIATE_TEST_CASE_P(, | |
| 1354 ChromeLauncherControllerImplMultiProfileWithArcTest, | |
| 1355 ::testing::Bool()); | |
| 1356 | |
| 1357 TEST_F(ChromeLauncherControllerImplTest, DefaultApps) { | |
| 1358 InitLauncherController(); | |
| 1359 // The model should only contain the browser shortcut and app list items. | |
| 1360 EXPECT_EQ(2, model_->item_count()); | |
| 1361 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1362 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1363 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1364 | |
| 1365 // Installing |extension3_| should add it to the launcher - behind the | |
| 1366 // chrome icon. | |
| 1367 extension_service_->AddExtension(extension3_.get()); | |
| 1368 EXPECT_EQ("AppList, Chrome, App3", GetPinnedAppStatus()); | |
| 1369 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1370 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1371 } | |
| 1372 | |
| 1373 TEST_P(ChromeLauncherControllerImplWithArcTest, | |
| 1374 ArcAppPinCrossPlatformWorkflow) { | |
| 1375 // Work on ARC disabled platform first. | |
| 1376 const std::string arc_app_id1 = | |
| 1377 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]); | |
| 1378 const std::string arc_app_id2 = | |
| 1379 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]); | |
| 1380 const std::string arc_app_id3 = | |
| 1381 ArcAppTest::GetAppId(arc_test_.fake_apps()[2]); | |
| 1382 | |
| 1383 InitLauncherController(); | |
| 1384 | |
| 1385 extension_service_->AddExtension(extension1_.get()); | |
| 1386 extension_service_->AddExtension(extension2_.get()); | |
| 1387 extension_service_->AddExtension(extension3_.get()); | |
| 1388 | |
| 1389 // extension 1, 3 are pinned by user | |
| 1390 syncer::SyncChangeList sync_list; | |
| 1391 InsertAddPinChange(&sync_list, 0, extension1_->id()); | |
| 1392 InsertAddPinChange(&sync_list, 1, arc_app_id1); | |
| 1393 InsertAddPinChange(&sync_list, 2, extension2_->id()); | |
| 1394 InsertAddPinChange(&sync_list, 3, arc_app_id2); | |
| 1395 InsertAddPinChange(&sync_list, 4, extension3_->id()); | |
| 1396 SendPinChanges(sync_list, true); | |
| 1397 SetShelfChromeIconIndex(1); | |
| 1398 | |
| 1399 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1400 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id1)); | |
| 1401 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1402 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id2)); | |
| 1403 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1404 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id3)); | |
| 1405 EXPECT_EQ("AppList, App1, Chrome, App2, App3", GetPinnedAppStatus()); | |
| 1406 | |
| 1407 // Persist pin state, we don't have active pin for ARC apps yet, but pin | |
| 1408 // model should have it. | |
| 1409 syncer::SyncDataList copy_sync_list = | |
| 1410 app_service_->GetAllSyncData(syncer::APP_LIST); | |
| 1411 | |
| 1412 ResetLauncherController(); | |
| 1413 SendPinChanges(syncer::SyncChangeList(), true); | |
| 1414 StopAppSyncService(); | |
| 1415 EXPECT_EQ(0U, app_service_->sync_items().size()); | |
| 1416 | |
| 1417 // Move to ARC enabled platform, restart syncing with stored data. | |
| 1418 StartAppSyncService(copy_sync_list); | |
| 1419 RecreateLauncherController()->Init(); | |
| 1420 | |
| 1421 // Pins must be automatically updated. | |
| 1422 SendListOfArcApps(); | |
| 1423 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1424 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id1)); | |
| 1425 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1426 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id2)); | |
| 1427 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1428 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id3)); | |
| 1429 | |
| 1430 EXPECT_EQ("AppList, App1, Chrome, Fake App 0, App2, Fake App 1, App3", | |
| 1431 GetPinnedAppStatus()); | |
| 1432 | |
| 1433 // Now move pins on ARC enabled platform. | |
| 1434 model_->Move(1, 4); | |
| 1435 model_->Move(3, 1); | |
| 1436 model_->Move(3, 5); | |
| 1437 model_->Move(4, 2); | |
| 1438 EXPECT_EQ("AppList, App2, Fake App 1, Chrome, App1, Fake App 0, App3", | |
| 1439 GetPinnedAppStatus()); | |
| 1440 | |
| 1441 copy_sync_list = app_service_->GetAllSyncData(syncer::APP_LIST); | |
| 1442 | |
| 1443 ResetLauncherController(); | |
| 1444 ResetPinModel(); | |
| 1445 | |
| 1446 SendPinChanges(syncer::SyncChangeList(), true); | |
| 1447 StopAppSyncService(); | |
| 1448 EXPECT_EQ(0U, app_service_->sync_items().size()); | |
| 1449 | |
| 1450 // Move back to ARC disabled platform. | |
| 1451 // TODO(victorhsieh): Implement opt-out. | |
| 1452 if (arc::ShouldArcAlwaysStart()) | |
| 1453 return; | |
| 1454 EnablePlayStore(false); | |
| 1455 StartAppSyncService(copy_sync_list); | |
| 1456 RecreateLauncherController()->Init(); | |
| 1457 | |
| 1458 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1459 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id1)); | |
| 1460 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1461 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id2)); | |
| 1462 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1463 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id3)); | |
| 1464 EXPECT_EQ("AppList, App2, Chrome, App1, App3", GetPinnedAppStatus()); | |
| 1465 | |
| 1466 // Now move/remove pins on ARC disabled platform. | |
| 1467 model_->Move(4, 2); | |
| 1468 launcher_controller_->UnpinAppWithID(extension2_->id()); | |
| 1469 EXPECT_EQ("AppList, App3, Chrome, App1", GetPinnedAppStatus()); | |
| 1470 EnablePlayStore(true); | |
| 1471 | |
| 1472 SendListOfArcApps(); | |
| 1473 | |
| 1474 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1475 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id1)); | |
| 1476 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1477 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id2)); | |
| 1478 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1479 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id3)); | |
| 1480 EXPECT_EQ("AppList, Fake App 1, App3, Chrome, App1, Fake App 0", | |
| 1481 GetPinnedAppStatus()); | |
| 1482 } | |
| 1483 | |
| 1484 /* | |
| 1485 * Test ChromeLauncherControllerImpl correctly merges policy pinned apps | |
| 1486 * and user pinned apps | |
| 1487 */ | |
| 1488 TEST_F(ChromeLauncherControllerImplTest, MergePolicyAndUserPrefPinnedApps) { | |
| 1489 InitLauncherController(); | |
| 1490 | |
| 1491 extension_service_->AddExtension(extension1_.get()); | |
| 1492 extension_service_->AddExtension(extension3_.get()); | |
| 1493 extension_service_->AddExtension(extension4_.get()); | |
| 1494 extension_service_->AddExtension(extension5_.get()); | |
| 1495 // extension 1, 3 are pinned by user | |
| 1496 syncer::SyncChangeList sync_list; | |
| 1497 InsertAddPinChange(&sync_list, 0, extension1_->id()); | |
| 1498 InsertAddPinChange(&sync_list, 1, extension_misc::kChromeAppId); | |
| 1499 InsertAddPinChange(&sync_list, 2, extension3_->id()); | |
| 1500 SendPinChanges(sync_list, true); | |
| 1501 | |
| 1502 base::ListValue policy_value; | |
| 1503 // extension 2 4 are pinned by policy | |
| 1504 InsertPrefValue(&policy_value, 0, extension2_->id()); | |
| 1505 InsertPrefValue(&policy_value, 1, extension4_->id()); | |
| 1506 profile()->GetTestingPrefService()->SetManagedPref( | |
| 1507 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy()); | |
| 1508 | |
| 1509 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1510 // 2 is not pinned as it's not installed | |
| 1511 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1512 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1513 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id())); | |
| 1514 // install extension 2 and check | |
| 1515 extension_service_->AddExtension(extension2_.get()); | |
| 1516 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1517 | |
| 1518 // Check user can manually pin or unpin these apps | |
| 1519 EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE, | |
| 1520 GetPinnableForAppID(extension1_->id(), profile())); | |
| 1521 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED, | |
| 1522 GetPinnableForAppID(extension2_->id(), profile())); | |
| 1523 EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE, | |
| 1524 GetPinnableForAppID(extension3_->id(), profile())); | |
| 1525 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED, | |
| 1526 GetPinnableForAppID(extension4_->id(), profile())); | |
| 1527 | |
| 1528 // Check the order of shelf pinned apps | |
| 1529 EXPECT_EQ("AppList, App2, App4, App1, Chrome, App3", GetPinnedAppStatus()); | |
| 1530 } | |
| 1531 | |
| 1532 // Check that the restauration of launcher items is happening in the same order | |
| 1533 // as the user has pinned them (on another system) when they are synced reverse | |
| 1534 // order. | |
| 1535 TEST_F(ChromeLauncherControllerImplTest, RestoreDefaultAppsReverseOrder) { | |
| 1536 InitLauncherController(); | |
| 1537 | |
| 1538 syncer::SyncChangeList sync_list; | |
| 1539 InsertAddPinChange(&sync_list, 0, extension1_->id()); | |
| 1540 InsertAddPinChange(&sync_list, 1, extension2_->id()); | |
| 1541 InsertAddPinChange(&sync_list, 2, extension3_->id()); | |
| 1542 SendPinChanges(sync_list, true); | |
| 1543 | |
| 1544 // The model should only contain the browser shortcut and app list items. | |
| 1545 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1546 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1547 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1548 EXPECT_EQ("AppList, Chrome", GetPinnedAppStatus()); | |
| 1549 | |
| 1550 // Installing |extension3_| should add it to the shelf - behind the | |
| 1551 // chrome icon. | |
| 1552 ash::ShelfItem item; | |
| 1553 extension_service_->AddExtension(extension3_.get()); | |
| 1554 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1555 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1556 EXPECT_EQ("AppList, Chrome, App3", GetPinnedAppStatus()); | |
| 1557 | |
| 1558 // Installing |extension2_| should add it to the launcher - behind the | |
| 1559 // chrome icon, but in first location. | |
| 1560 extension_service_->AddExtension(extension2_.get()); | |
| 1561 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1562 EXPECT_EQ("AppList, Chrome, App2, App3", GetPinnedAppStatus()); | |
| 1563 | |
| 1564 // Installing |extension1_| should add it to the launcher - behind the | |
| 1565 // chrome icon, but in first location. | |
| 1566 extension_service_->AddExtension(extension1_.get()); | |
| 1567 EXPECT_EQ("AppList, Chrome, App1, App2, App3", GetPinnedAppStatus()); | |
| 1568 } | |
| 1569 | |
| 1570 // Check that the restauration of launcher items is happening in the same order | |
| 1571 // as the user has pinned them (on another system) when they are synced random | |
| 1572 // order. | |
| 1573 TEST_F(ChromeLauncherControllerImplTest, RestoreDefaultAppsRandomOrder) { | |
| 1574 InitLauncherController(); | |
| 1575 | |
| 1576 syncer::SyncChangeList sync_list; | |
| 1577 InsertAddPinChange(&sync_list, 0, extension1_->id()); | |
| 1578 InsertAddPinChange(&sync_list, 1, extension2_->id()); | |
| 1579 InsertAddPinChange(&sync_list, 2, extension3_->id()); | |
| 1580 SendPinChanges(sync_list, true); | |
| 1581 | |
| 1582 // The model should only contain the browser shortcut and app list items. | |
| 1583 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1584 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1585 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1586 EXPECT_EQ("AppList, Chrome", GetPinnedAppStatus()); | |
| 1587 | |
| 1588 // Installing |extension2_| should add it to the launcher - behind the | |
| 1589 // chrome icon. | |
| 1590 extension_service_->AddExtension(extension2_.get()); | |
| 1591 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1592 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1593 EXPECT_EQ("AppList, Chrome, App2", GetPinnedAppStatus()); | |
| 1594 | |
| 1595 // Installing |extension1_| should add it to the launcher - behind the | |
| 1596 // chrome icon, but in first location. | |
| 1597 extension_service_->AddExtension(extension1_.get()); | |
| 1598 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1599 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 1600 | |
| 1601 // Installing |extension3_| should add it to the launcher - behind the | |
| 1602 // chrome icon, but in first location. | |
| 1603 extension_service_->AddExtension(extension3_.get()); | |
| 1604 EXPECT_EQ("AppList, Chrome, App1, App2, App3", GetPinnedAppStatus()); | |
| 1605 } | |
| 1606 | |
| 1607 // Check that the restauration of launcher items is happening in the same order | |
| 1608 // as the user has pinned / moved them (on another system) when they are synced | |
| 1609 // random order - including the chrome icon. | |
| 1610 TEST_F(ChromeLauncherControllerImplTest, | |
| 1611 RestoreDefaultAppsRandomOrderChromeMoved) { | |
| 1612 InitLauncherController(); | |
| 1613 | |
| 1614 syncer::SyncChangeList sync_list; | |
| 1615 InsertAddPinChange(&sync_list, 0, extension1_->id()); | |
| 1616 InsertAddPinChange(&sync_list, 1, extension_misc::kChromeAppId); | |
| 1617 InsertAddPinChange(&sync_list, 2, extension2_->id()); | |
| 1618 InsertAddPinChange(&sync_list, 3, extension3_->id()); | |
| 1619 SendPinChanges(sync_list, true); | |
| 1620 | |
| 1621 // The model should only contain the browser shortcut and app list items. | |
| 1622 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1623 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 1624 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1625 EXPECT_EQ("AppList, Chrome", GetPinnedAppStatus()); | |
| 1626 | |
| 1627 // Installing |extension2_| should add it to the shelf - behind the | |
| 1628 // chrome icon. | |
| 1629 ash::ShelfItem item; | |
| 1630 extension_service_->AddExtension(extension2_.get()); | |
| 1631 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1632 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1633 EXPECT_EQ("AppList, Chrome, App2", GetPinnedAppStatus()); | |
| 1634 | |
| 1635 // Installing |extension1_| should add it to the launcher - behind the | |
| 1636 // chrome icon, but in first location. | |
| 1637 extension_service_->AddExtension(extension1_.get()); | |
| 1638 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 1639 EXPECT_EQ("AppList, App1, Chrome, App2", GetPinnedAppStatus()); | |
| 1640 | |
| 1641 // Installing |extension3_| should add it to the launcher - behind the | |
| 1642 // chrome icon, but in first location. | |
| 1643 extension_service_->AddExtension(extension3_.get()); | |
| 1644 EXPECT_EQ("AppList, App1, Chrome, App2, App3", GetPinnedAppStatus()); | |
| 1645 } | |
| 1646 | |
| 1647 // Check that syncing to a different state does the correct thing. | |
| 1648 TEST_F(ChromeLauncherControllerImplTest, RestoreDefaultAppsResyncOrder) { | |
| 1649 InitLauncherController(); | |
| 1650 | |
| 1651 syncer::SyncChangeList sync_list0; | |
| 1652 InsertAddPinChange(&sync_list0, 0, extension1_->id()); | |
| 1653 InsertAddPinChange(&sync_list0, 1, extension2_->id()); | |
| 1654 InsertAddPinChange(&sync_list0, 2, extension3_->id()); | |
| 1655 SendPinChanges(sync_list0, true); | |
| 1656 | |
| 1657 // The shelf layout has always one static item at the beginning (App List). | |
| 1658 extension_service_->AddExtension(extension2_.get()); | |
| 1659 EXPECT_EQ("AppList, Chrome, App2", GetPinnedAppStatus()); | |
| 1660 extension_service_->AddExtension(extension1_.get()); | |
| 1661 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 1662 extension_service_->AddExtension(extension3_.get()); | |
| 1663 EXPECT_EQ("AppList, Chrome, App1, App2, App3", GetPinnedAppStatus()); | |
| 1664 | |
| 1665 // Change the order with increasing chrome position and decreasing position. | |
| 1666 syncer::SyncChangeList sync_list1; | |
| 1667 InsertAddPinChange(&sync_list1, 0, extension3_->id()); | |
| 1668 InsertAddPinChange(&sync_list1, 1, extension1_->id()); | |
| 1669 InsertAddPinChange(&sync_list1, 2, extension2_->id()); | |
| 1670 InsertAddPinChange(&sync_list1, 3, extension_misc::kChromeAppId); | |
| 1671 SendPinChanges(sync_list1, true); | |
| 1672 EXPECT_EQ("AppList, App3, App1, App2, Chrome", GetPinnedAppStatus()); | |
| 1673 | |
| 1674 syncer::SyncChangeList sync_list2; | |
| 1675 InsertAddPinChange(&sync_list2, 0, extension2_->id()); | |
| 1676 InsertAddPinChange(&sync_list2, 1, extension3_->id()); | |
| 1677 InsertAddPinChange(&sync_list2, 2, extension_misc::kChromeAppId); | |
| 1678 InsertAddPinChange(&sync_list2, 3, extension1_->id()); | |
| 1679 SendPinChanges(sync_list2, true); | |
| 1680 EXPECT_EQ("AppList, App2, App3, Chrome, App1", GetPinnedAppStatus()); | |
| 1681 | |
| 1682 // Check that the chrome icon can also be at the first possible location. | |
| 1683 syncer::SyncChangeList sync_list3; | |
| 1684 InsertAddPinChange(&sync_list3, 0, extension3_->id()); | |
| 1685 InsertAddPinChange(&sync_list3, 1, extension2_->id()); | |
| 1686 InsertAddPinChange(&sync_list3, 2, extension1_->id()); | |
| 1687 SendPinChanges(sync_list3, true); | |
| 1688 EXPECT_EQ("AppList, Chrome, App3, App2, App1", GetPinnedAppStatus()); | |
| 1689 | |
| 1690 // Check that unloading of extensions works as expected. | |
| 1691 extension_service_->UnloadExtension(extension1_->id(), | |
| 1692 UnloadedExtensionInfo::REASON_UNINSTALL); | |
| 1693 EXPECT_EQ("AppList, Chrome, App3, App2", GetPinnedAppStatus()); | |
| 1694 | |
| 1695 extension_service_->UnloadExtension(extension2_->id(), | |
| 1696 UnloadedExtensionInfo::REASON_UNINSTALL); | |
| 1697 EXPECT_EQ("AppList, Chrome, App3", GetPinnedAppStatus()); | |
| 1698 | |
| 1699 // Check that an update of an extension does not crash the system. | |
| 1700 extension_service_->UnloadExtension(extension3_->id(), | |
| 1701 UnloadedExtensionInfo::REASON_UPDATE); | |
| 1702 EXPECT_EQ("AppList, Chrome, App3", GetPinnedAppStatus()); | |
| 1703 } | |
| 1704 | |
| 1705 // Test the V1 app interaction flow: run it, activate it, close it. | |
| 1706 TEST_F(ChromeLauncherControllerImplTest, V1AppRunActivateClose) { | |
| 1707 InitLauncherController(); | |
| 1708 // The model should only contain the browser shortcut and app list items. | |
| 1709 EXPECT_EQ(2, model_->item_count()); | |
| 1710 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1711 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1712 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1713 | |
| 1714 // Reporting that the app is running should create a new shelf item. | |
| 1715 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING); | |
| 1716 EXPECT_EQ(3, model_->item_count()); | |
| 1717 EXPECT_EQ(ash::TYPE_APP, model_->items()[2].type); | |
| 1718 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[2].status); | |
| 1719 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1720 EXPECT_NE(ash::kInvalidShelfID, | |
| 1721 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1722 | |
| 1723 // Reporting an active status should just update the existing item. | |
| 1724 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_ACTIVE); | |
| 1725 EXPECT_EQ(3, model_->item_count()); | |
| 1726 EXPECT_EQ(ash::STATUS_ACTIVE, model_->items()[2].status); | |
| 1727 | |
| 1728 // Reporting that the app is closed should remove its shelf item. | |
| 1729 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED); | |
| 1730 EXPECT_EQ(2, model_->item_count()); | |
| 1731 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1732 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1733 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1734 | |
| 1735 // Reporting that the app is closed again should have no effect. | |
| 1736 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED); | |
| 1737 EXPECT_EQ(2, model_->item_count()); | |
| 1738 } | |
| 1739 | |
| 1740 // Test the V1 app interaction flow: pin it, run it, close it, unpin it. | |
| 1741 TEST_F(ChromeLauncherControllerImplTest, V1AppPinRunCloseUnpin) { | |
| 1742 InitLauncherController(); | |
| 1743 // The model should only contain the browser shortcut and app list items. | |
| 1744 EXPECT_EQ(2, model_->item_count()); | |
| 1745 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1746 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1747 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1748 | |
| 1749 // Pinning the app should create a new shelf item. | |
| 1750 launcher_controller_->PinAppWithID(extension1_->id()); | |
| 1751 EXPECT_EQ(3, model_->item_count()); | |
| 1752 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 1753 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[2].status); | |
| 1754 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1755 EXPECT_NE(ash::kInvalidShelfID, | |
| 1756 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1757 | |
| 1758 // Reporting that the app is running should just update the existing item. | |
| 1759 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING); | |
| 1760 EXPECT_EQ(3, model_->item_count()); | |
| 1761 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 1762 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[2].status); | |
| 1763 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1764 EXPECT_NE(ash::kInvalidShelfID, | |
| 1765 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1766 | |
| 1767 // Reporting that the app is closed should just update the existing item. | |
| 1768 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED); | |
| 1769 EXPECT_EQ(3, model_->item_count()); | |
| 1770 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 1771 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[2].status); | |
| 1772 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1773 EXPECT_NE(ash::kInvalidShelfID, | |
| 1774 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1775 | |
| 1776 // Unpinning the app should remove its shelf item. | |
| 1777 launcher_controller_->UnpinAppWithID(extension1_->id()); | |
| 1778 EXPECT_EQ(2, model_->item_count()); | |
| 1779 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1780 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1781 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1782 } | |
| 1783 | |
| 1784 // Test the V1 app interaction flow: run it, pin it, close it, unpin it. | |
| 1785 TEST_F(ChromeLauncherControllerImplTest, V1AppRunPinCloseUnpin) { | |
| 1786 InitLauncherController(); | |
| 1787 // The model should only contain the browser shortcut and app list items. | |
| 1788 EXPECT_EQ(2, model_->item_count()); | |
| 1789 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1790 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1791 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1792 | |
| 1793 // Reporting that the app is running should create a new shelf item. | |
| 1794 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING); | |
| 1795 EXPECT_EQ(3, model_->item_count()); | |
| 1796 EXPECT_EQ(ash::TYPE_APP, model_->items()[2].type); | |
| 1797 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[2].status); | |
| 1798 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1799 EXPECT_NE(ash::kInvalidShelfID, | |
| 1800 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1801 | |
| 1802 // Pinning the app should just update the existing item. | |
| 1803 launcher_controller_->PinAppWithID(extension1_->id()); | |
| 1804 EXPECT_EQ(3, model_->item_count()); | |
| 1805 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 1806 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[2].status); | |
| 1807 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1808 EXPECT_NE(ash::kInvalidShelfID, | |
| 1809 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1810 | |
| 1811 // Reporting that the app is closed should just update the existing item. | |
| 1812 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED); | |
| 1813 EXPECT_EQ(3, model_->item_count()); | |
| 1814 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 1815 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[2].status); | |
| 1816 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1817 EXPECT_NE(ash::kInvalidShelfID, | |
| 1818 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1819 | |
| 1820 // Unpinning the app should remove its shelf item. | |
| 1821 launcher_controller_->UnpinAppWithID(extension1_->id()); | |
| 1822 EXPECT_EQ(2, model_->item_count()); | |
| 1823 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1824 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1825 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1826 } | |
| 1827 | |
| 1828 // Test the V1 app interaction flow: pin it, run it, unpin it, close it. | |
| 1829 TEST_F(ChromeLauncherControllerImplTest, V1AppPinRunUnpinClose) { | |
| 1830 InitLauncherController(); | |
| 1831 // The model should only contain the browser shortcut and app list items. | |
| 1832 EXPECT_EQ(2, model_->item_count()); | |
| 1833 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1834 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1835 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1836 | |
| 1837 // Pinning the app should create a new shelf item. | |
| 1838 launcher_controller_->PinAppWithID(extension1_->id()); | |
| 1839 EXPECT_EQ(3, model_->item_count()); | |
| 1840 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 1841 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[2].status); | |
| 1842 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1843 EXPECT_NE(ash::kInvalidShelfID, | |
| 1844 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1845 | |
| 1846 // Reporting that the app is running should just update the existing item. | |
| 1847 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING); | |
| 1848 EXPECT_EQ(3, model_->item_count()); | |
| 1849 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 1850 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[2].status); | |
| 1851 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1852 EXPECT_NE(ash::kInvalidShelfID, | |
| 1853 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1854 | |
| 1855 // Unpinning the app should just update the existing item. | |
| 1856 launcher_controller_->UnpinAppWithID(extension1_->id()); | |
| 1857 EXPECT_EQ(3, model_->item_count()); | |
| 1858 EXPECT_EQ(ash::TYPE_APP, model_->items()[2].type); | |
| 1859 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[2].status); | |
| 1860 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1861 EXPECT_NE(ash::kInvalidShelfID, | |
| 1862 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1863 | |
| 1864 // Reporting that the app is closed should remove its shelf item. | |
| 1865 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED); | |
| 1866 EXPECT_EQ(2, model_->item_count()); | |
| 1867 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 1868 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1869 launcher_controller_->GetShelfIDForAppID(extension1_->id())); | |
| 1870 } | |
| 1871 | |
| 1872 // Ensure unpinned V1 app ordering is properly restored after user changes. | |
| 1873 TEST_F(ChromeLauncherControllerImplTest, CheckRunningV1AppOrder) { | |
| 1874 InitLauncherController(); | |
| 1875 // The model should only contain the browser shortcut and app list items. | |
| 1876 EXPECT_EQ(2, model_->item_count()); | |
| 1877 | |
| 1878 // Add a few running applications. | |
| 1879 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING); | |
| 1880 launcher_controller_->SetV1AppStatus(extension2_->id(), ash::STATUS_RUNNING); | |
| 1881 launcher_controller_->SetV1AppStatus(extension3_->id(), ash::STATUS_RUNNING); | |
| 1882 EXPECT_EQ(5, model_->item_count()); | |
| 1883 // Note that this not only checks the order of applications but also the | |
| 1884 // running type. | |
| 1885 EXPECT_EQ("AppList, Chrome, app1, app2, app3", GetPinnedAppStatus()); | |
| 1886 | |
| 1887 // Remember the current order of applications for the current user. | |
| 1888 const AccountId& current_account_id = | |
| 1889 multi_user_util::GetAccountIdFromProfile(profile()); | |
| 1890 RememberUnpinnedRunningApplicationOrder(); | |
| 1891 | |
| 1892 // Switch some items and check that restoring a user which was not yet | |
| 1893 // remembered changes nothing. | |
| 1894 model_->Move(2, 3); | |
| 1895 EXPECT_EQ("AppList, Chrome, app2, app1, app3", GetPinnedAppStatus()); | |
| 1896 const AccountId second_fake_account_id( | |
| 1897 AccountId::FromUserEmail("second-fake-user@fake.com")); | |
| 1898 RestoreUnpinnedRunningApplicationOrder(second_fake_account_id); | |
| 1899 EXPECT_EQ("AppList, Chrome, app2, app1, app3", GetPinnedAppStatus()); | |
| 1900 | |
| 1901 // Restoring the stored user should however do the right thing. | |
| 1902 RestoreUnpinnedRunningApplicationOrder(current_account_id); | |
| 1903 EXPECT_EQ("AppList, Chrome, app1, app2, app3", GetPinnedAppStatus()); | |
| 1904 | |
| 1905 // Switch again some items and even delete one - making sure that the missing | |
| 1906 // item gets properly handled. | |
| 1907 model_->Move(3, 4); | |
| 1908 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED); | |
| 1909 EXPECT_EQ("AppList, Chrome, app3, app2", GetPinnedAppStatus()); | |
| 1910 RestoreUnpinnedRunningApplicationOrder(current_account_id); | |
| 1911 EXPECT_EQ("AppList, Chrome, app2, app3", GetPinnedAppStatus()); | |
| 1912 | |
| 1913 // Check that removing more items does not crash and changes nothing. | |
| 1914 launcher_controller_->SetV1AppStatus(extension2_->id(), ash::STATUS_CLOSED); | |
| 1915 RestoreUnpinnedRunningApplicationOrder(current_account_id); | |
| 1916 EXPECT_EQ("AppList, Chrome, app3", GetPinnedAppStatus()); | |
| 1917 launcher_controller_->SetV1AppStatus(extension3_->id(), ash::STATUS_CLOSED); | |
| 1918 RestoreUnpinnedRunningApplicationOrder(current_account_id); | |
| 1919 EXPECT_EQ("AppList, Chrome", GetPinnedAppStatus()); | |
| 1920 } | |
| 1921 | |
| 1922 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcDeferredLaunch) { | |
| 1923 InitLauncherController(); | |
| 1924 | |
| 1925 const arc::mojom::AppInfo& app1 = arc_test_.fake_apps()[0]; | |
| 1926 const arc::mojom::AppInfo& app2 = arc_test_.fake_apps()[1]; | |
| 1927 const arc::mojom::AppInfo& app3 = arc_test_.fake_apps()[2]; | |
| 1928 const arc::mojom::ShortcutInfo& shortcut = arc_test_.fake_shortcuts()[0]; | |
| 1929 const std::string arc_app_id1 = ArcAppTest::GetAppId(app1); | |
| 1930 const std::string arc_app_id2 = ArcAppTest::GetAppId(app2); | |
| 1931 const std::string arc_app_id3 = ArcAppTest::GetAppId(app3); | |
| 1932 const std::string arc_shortcut_id = ArcAppTest::GetAppId(shortcut); | |
| 1933 | |
| 1934 SendListOfArcApps(); | |
| 1935 SendListOfArcShortcuts(); | |
| 1936 | |
| 1937 arc_test_.StopArcInstance(); | |
| 1938 | |
| 1939 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1940 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 1941 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1942 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 1943 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1944 launcher_controller_->GetShelfIDForAppID(arc_app_id3)); | |
| 1945 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1946 launcher_controller_->GetShelfIDForAppID(arc_shortcut_id)); | |
| 1947 | |
| 1948 arc::LaunchApp(profile(), arc_app_id1, ui::EF_LEFT_MOUSE_BUTTON); | |
| 1949 arc::LaunchApp(profile(), arc_app_id1, ui::EF_LEFT_MOUSE_BUTTON); | |
| 1950 arc::LaunchApp(profile(), arc_app_id2, ui::EF_LEFT_MOUSE_BUTTON); | |
| 1951 arc::LaunchApp(profile(), arc_app_id3, ui::EF_LEFT_MOUSE_BUTTON); | |
| 1952 arc::LaunchApp(profile(), arc_shortcut_id, ui::EF_LEFT_MOUSE_BUTTON); | |
| 1953 | |
| 1954 const ash::ShelfID shelf_id_app_1 = | |
| 1955 launcher_controller_->GetShelfIDForAppID(arc_app_id1); | |
| 1956 const ash::ShelfID shelf_id_app_2 = | |
| 1957 launcher_controller_->GetShelfIDForAppID(arc_app_id2); | |
| 1958 const ash::ShelfID shelf_id_app_3 = | |
| 1959 launcher_controller_->GetShelfIDForAppID(arc_app_id3); | |
| 1960 const ash::ShelfID shelf_id_shortcut = | |
| 1961 launcher_controller_->GetShelfIDForAppID(arc_shortcut_id); | |
| 1962 EXPECT_NE(ash::kInvalidShelfID, shelf_id_app_1); | |
| 1963 EXPECT_NE(ash::kInvalidShelfID, shelf_id_app_2); | |
| 1964 EXPECT_NE(ash::kInvalidShelfID, shelf_id_app_3); | |
| 1965 EXPECT_NE(ash::kInvalidShelfID, shelf_id_shortcut); | |
| 1966 | |
| 1967 // We activated arc_app_id1 twice but expect one close for item controller | |
| 1968 // stops launching request. | |
| 1969 ash::ShelfItemDelegate* item_delegate = | |
| 1970 model_->GetShelfItemDelegate(shelf_id_app_1); | |
| 1971 ASSERT_NE(nullptr, item_delegate); | |
| 1972 item_delegate->Close(); | |
| 1973 base::RunLoop().RunUntilIdle(); | |
| 1974 | |
| 1975 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1976 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 1977 EXPECT_EQ(shelf_id_app_2, | |
| 1978 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 1979 EXPECT_EQ(shelf_id_app_3, | |
| 1980 launcher_controller_->GetShelfIDForAppID(arc_app_id3)); | |
| 1981 | |
| 1982 arc_test_.RestartArcInstance(); | |
| 1983 SendListOfArcApps(); | |
| 1984 | |
| 1985 base::RunLoop().RunUntilIdle(); | |
| 1986 | |
| 1987 // Now deferred contollers should go away together with shelf items and ARC | |
| 1988 // app instance should receive request for launching apps and shortcuts. | |
| 1989 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1990 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 1991 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1992 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 1993 EXPECT_EQ(ash::kInvalidShelfID, | |
| 1994 launcher_controller_->GetShelfIDForAppID(arc_app_id3)); | |
| 1995 | |
| 1996 ASSERT_EQ(2U, arc_test_.app_instance()->launch_requests().size()); | |
| 1997 ASSERT_EQ(1U, arc_test_.app_instance()->launch_intents().size()); | |
| 1998 | |
| 1999 const arc::FakeAppInstance::Request* request1 = | |
| 2000 arc_test_.app_instance()->launch_requests()[0].get(); | |
| 2001 const arc::FakeAppInstance::Request* request2 = | |
| 2002 arc_test_.app_instance()->launch_requests()[1].get(); | |
| 2003 | |
| 2004 EXPECT_TRUE((request1->IsForApp(app2) && request2->IsForApp(app3)) || | |
| 2005 (request1->IsForApp(app3) && request2->IsForApp(app2))); | |
| 2006 EXPECT_EQ(arc_test_.app_instance()->launch_intents()[0].c_str(), | |
| 2007 shortcut.intent_uri); | |
| 2008 } | |
| 2009 | |
| 2010 // Ensure the deferred controller does not override the active app controller | |
| 2011 // (crbug.com/701152). | |
| 2012 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcDeferredLaunchForActiveApp) { | |
| 2013 InitLauncherController(); | |
| 2014 SendListOfArcApps(); | |
| 2015 arc_test_.StopArcInstance(); | |
| 2016 | |
| 2017 const arc::mojom::AppInfo& app = arc_test_.fake_apps()[0]; | |
| 2018 const std::string app_id = ArcAppTest::GetAppId(app); | |
| 2019 | |
| 2020 launcher_controller_->PinAppWithID(app_id); | |
| 2021 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id)); | |
| 2022 const ash::ShelfID shelf_id = | |
| 2023 launcher_controller_->GetShelfIDForAppID(app_id); | |
| 2024 EXPECT_NE(ash::kInvalidShelfID, shelf_id); | |
| 2025 | |
| 2026 int item_index = model_->ItemIndexByID(shelf_id); | |
| 2027 ASSERT_GE(item_index, 0); | |
| 2028 | |
| 2029 EXPECT_EQ(model_->items()[item_index].status, ash::STATUS_CLOSED); | |
| 2030 EXPECT_EQ(model_->items()[item_index].type, ash::TYPE_PINNED_APP); | |
| 2031 | |
| 2032 // Play Store app is ARC app that might be represented by native Chrome | |
| 2033 // platform app. | |
| 2034 model_->SetShelfItemDelegate( | |
| 2035 shelf_id, base::MakeUnique<ExtensionAppWindowLauncherItemController>( | |
| 2036 ash::AppLaunchId(app_id))); | |
| 2037 launcher_controller_->SetItemStatus(shelf_id, ash::STATUS_RUNNING); | |
| 2038 | |
| 2039 // This launch request should be ignored in case of active app. | |
| 2040 arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON); | |
| 2041 EXPECT_FALSE(launcher_controller_->GetArcDeferredLauncher()->HasApp(app_id)); | |
| 2042 | |
| 2043 // Close app but shortcut should exist. | |
| 2044 launcher_controller_->CloseLauncherItem(shelf_id); | |
| 2045 EXPECT_EQ(shelf_id, launcher_controller_->GetShelfIDForAppID(app_id)); | |
| 2046 | |
| 2047 // This should switch shelf item into closed state. | |
| 2048 item_index = model_->ItemIndexByID(shelf_id); | |
| 2049 ASSERT_GE(item_index, 0); | |
| 2050 EXPECT_EQ(model_->items()[item_index].status, ash::STATUS_CLOSED); | |
| 2051 EXPECT_EQ(model_->items()[item_index].type, ash::TYPE_PINNED_APP); | |
| 2052 | |
| 2053 // Now launch request should not be ignored. | |
| 2054 arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON); | |
| 2055 EXPECT_TRUE(launcher_controller_->GetArcDeferredLauncher()->HasApp(app_id)); | |
| 2056 } | |
| 2057 | |
| 2058 TEST_P(ChromeLauncherControllerImplMultiProfileWithArcTest, ArcMultiUser) { | |
| 2059 SendListOfArcApps(); | |
| 2060 | |
| 2061 InitLauncherController(); | |
| 2062 // TODO(crbug.com/654622): This test breaks with a non-null static instance. | |
| 2063 ChromeLauncherControllerImpl::set_instance_for_test(nullptr); | |
| 2064 | |
| 2065 SetLauncherControllerHelper(new TestLauncherControllerHelper); | |
| 2066 | |
| 2067 // App1 exists all the time. | |
| 2068 // App2 is created when primary user is active and destroyed when secondary | |
| 2069 // user is active. | |
| 2070 // App3 created when secondary user is active. | |
| 2071 | |
| 2072 const std::string user2 = "user2"; | |
| 2073 TestingProfile* profile2 = CreateMultiUserProfile(user2); | |
| 2074 const AccountId account_id( | |
| 2075 multi_user_util::GetAccountIdFromProfile(profile())); | |
| 2076 const AccountId account_id2( | |
| 2077 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 2078 | |
| 2079 const std::string arc_app_id1 = | |
| 2080 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]); | |
| 2081 const std::string arc_app_id2 = | |
| 2082 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]); | |
| 2083 const std::string arc_app_id3 = | |
| 2084 ArcAppTest::GetAppId(arc_test_.fake_apps()[2]); | |
| 2085 | |
| 2086 std::string window_app_id1("org.chromium.arc.1"); | |
| 2087 views::Widget* arc_window1 = CreateArcWindow(window_app_id1); | |
| 2088 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0], | |
| 2089 std::string()); | |
| 2090 EXPECT_NE(ash::kInvalidShelfID, | |
| 2091 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 2092 | |
| 2093 std::string window_app_id2("org.chromium.arc.2"); | |
| 2094 views::Widget* arc_window2 = CreateArcWindow(window_app_id2); | |
| 2095 arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[1], | |
| 2096 std::string()); | |
| 2097 EXPECT_NE(ash::kInvalidShelfID, | |
| 2098 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 2099 | |
| 2100 launcher_controller_->SetProfileForTest(profile2); | |
| 2101 SwitchActiveUser(account_id2); | |
| 2102 | |
| 2103 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2104 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 2105 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2106 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 2107 | |
| 2108 std::string window_app_id3("org.chromium.arc.3"); | |
| 2109 views::Widget* arc_window3 = CreateArcWindow(window_app_id3); | |
| 2110 arc_test_.app_instance()->SendTaskCreated(3, arc_test_.fake_apps()[2], | |
| 2111 std::string()); | |
| 2112 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2113 launcher_controller_->GetShelfIDForAppID(arc_app_id3)); | |
| 2114 | |
| 2115 arc_window2->CloseNow(); | |
| 2116 arc_test_.app_instance()->SendTaskDestroyed(2); | |
| 2117 | |
| 2118 launcher_controller_->SetProfileForTest(profile()); | |
| 2119 SwitchActiveUser(account_id); | |
| 2120 | |
| 2121 EXPECT_NE(ash::kInvalidShelfID, | |
| 2122 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 2123 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2124 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 2125 EXPECT_NE(ash::kInvalidShelfID, | |
| 2126 launcher_controller_->GetShelfIDForAppID(arc_app_id3)); | |
| 2127 | |
| 2128 // Close active window to let test passes. | |
| 2129 arc_window1->CloseNow(); | |
| 2130 arc_window3->CloseNow(); | |
| 2131 } | |
| 2132 | |
| 2133 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcRunningApp) { | |
| 2134 InitLauncherController(); | |
| 2135 | |
| 2136 const std::string arc_app_id = ArcAppTest::GetAppId(arc_test_.fake_apps()[0]); | |
| 2137 SendListOfArcApps(); | |
| 2138 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2139 launcher_controller_->GetShelfIDForAppID(arc_app_id)); | |
| 2140 | |
| 2141 // Normal flow, create/destroy tasks. | |
| 2142 std::string window_app_id1("org.chromium.arc.1"); | |
| 2143 std::string window_app_id2("org.chromium.arc.2"); | |
| 2144 std::string window_app_id3("org.chromium.arc.3"); | |
| 2145 CreateArcWindow(window_app_id1); | |
| 2146 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0], | |
| 2147 std::string()); | |
| 2148 EXPECT_NE(ash::kInvalidShelfID, | |
| 2149 launcher_controller_->GetShelfIDForAppID(arc_app_id)); | |
| 2150 CreateArcWindow(window_app_id2); | |
| 2151 arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[0], | |
| 2152 std::string()); | |
| 2153 EXPECT_NE(ash::kInvalidShelfID, | |
| 2154 launcher_controller_->GetShelfIDForAppID(arc_app_id)); | |
| 2155 arc_test_.app_instance()->SendTaskDestroyed(1); | |
| 2156 EXPECT_NE(ash::kInvalidShelfID, | |
| 2157 launcher_controller_->GetShelfIDForAppID(arc_app_id)); | |
| 2158 arc_test_.app_instance()->SendTaskDestroyed(2); | |
| 2159 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2160 launcher_controller_->GetShelfIDForAppID(arc_app_id)); | |
| 2161 | |
| 2162 // Stopping bridge removes apps. | |
| 2163 CreateArcWindow(window_app_id3); | |
| 2164 arc_test_.app_instance()->SendTaskCreated(3, arc_test_.fake_apps()[0], | |
| 2165 std::string()); | |
| 2166 EXPECT_NE(ash::kInvalidShelfID, | |
| 2167 launcher_controller_->GetShelfIDForAppID(arc_app_id)); | |
| 2168 arc_test_.StopArcInstance(); | |
| 2169 base::RunLoop().RunUntilIdle(); | |
| 2170 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2171 launcher_controller_->GetShelfIDForAppID(arc_app_id)); | |
| 2172 } | |
| 2173 | |
| 2174 // Test race creation/deletion of ARC app. | |
| 2175 // TODO(khmel): Remove after moving everything to wayland protocol. | |
| 2176 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcRaceCreateClose) { | |
| 2177 InitLauncherController(); | |
| 2178 | |
| 2179 const std::string arc_app_id1 = | |
| 2180 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]); | |
| 2181 const std::string arc_app_id2 = | |
| 2182 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]); | |
| 2183 SendListOfArcApps(); | |
| 2184 | |
| 2185 // ARC window created before and closed after mojom notification. | |
| 2186 std::string window_app_id1("org.chromium.arc.1"); | |
| 2187 views::Widget* arc_window = CreateArcWindow(window_app_id1); | |
| 2188 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2189 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 2190 ASSERT_TRUE(arc_window); | |
| 2191 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0], | |
| 2192 std::string()); | |
| 2193 EXPECT_NE(ash::kInvalidShelfID, | |
| 2194 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 2195 arc_test_.app_instance()->SendTaskDestroyed(1); | |
| 2196 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2197 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 2198 arc_window->Close(); | |
| 2199 base::RunLoop().RunUntilIdle(); | |
| 2200 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2201 launcher_controller_->GetShelfIDForAppID(arc_app_id1)); | |
| 2202 | |
| 2203 // ARC window created after and closed before mojom notification. | |
| 2204 std::string window_app_id2("org.chromium.arc.2"); | |
| 2205 arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[1], | |
| 2206 std::string()); | |
| 2207 EXPECT_NE(ash::kInvalidShelfID, | |
| 2208 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 2209 arc_window = CreateArcWindow(window_app_id2); | |
| 2210 ASSERT_TRUE(arc_window); | |
| 2211 EXPECT_NE(ash::kInvalidShelfID, | |
| 2212 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 2213 arc_window->Close(); | |
| 2214 base::RunLoop().RunUntilIdle(); | |
| 2215 // Closing window does not close shelf item. It is closed on task destroy. | |
| 2216 EXPECT_NE(ash::kInvalidShelfID, | |
| 2217 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 2218 arc_test_.app_instance()->SendTaskDestroyed(2); | |
| 2219 EXPECT_EQ(ash::kInvalidShelfID, | |
| 2220 launcher_controller_->GetShelfIDForAppID(arc_app_id2)); | |
| 2221 } | |
| 2222 | |
| 2223 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcWindowRecreation) { | |
| 2224 InitLauncherController(); | |
| 2225 | |
| 2226 const std::string arc_app_id = ArcAppTest::GetAppId(arc_test_.fake_apps()[0]); | |
| 2227 SendListOfArcApps(); | |
| 2228 | |
| 2229 std::string window_app_id("org.chromium.arc.1"); | |
| 2230 views::Widget* arc_window = CreateArcWindow(window_app_id); | |
| 2231 ASSERT_TRUE(arc_window); | |
| 2232 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0], | |
| 2233 std::string()); | |
| 2234 const ash::ShelfID shelf_id = | |
| 2235 launcher_controller_->GetShelfIDForAppID(arc_app_id); | |
| 2236 EXPECT_NE(ash::kInvalidShelfID, shelf_id); | |
| 2237 | |
| 2238 for (int i = 0; i < 3; ++i) { | |
| 2239 arc_window->Close(); | |
| 2240 base::RunLoop().RunUntilIdle(); | |
| 2241 EXPECT_EQ(shelf_id, launcher_controller_->GetShelfIDForAppID(arc_app_id)); | |
| 2242 | |
| 2243 arc_window = CreateArcWindow(window_app_id); | |
| 2244 ASSERT_TRUE(arc_window); | |
| 2245 base::RunLoop().RunUntilIdle(); | |
| 2246 EXPECT_EQ(shelf_id, launcher_controller_->GetShelfIDForAppID(arc_app_id)); | |
| 2247 } | |
| 2248 } | |
| 2249 | |
| 2250 // Validate that ARC app is pinned correctly and pin is removed automatically | |
| 2251 // once app is uninstalled. | |
| 2252 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcAppPin) { | |
| 2253 InitLauncherController(); | |
| 2254 | |
| 2255 const std::string arc_app_id = ArcAppTest::GetAppId(arc_test_.fake_apps()[0]); | |
| 2256 | |
| 2257 SendListOfArcApps(); | |
| 2258 extension_service_->AddExtension(extension1_.get()); | |
| 2259 extension_service_->AddExtension(extension2_.get()); | |
| 2260 | |
| 2261 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 2262 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id)); | |
| 2263 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 2264 | |
| 2265 launcher_controller_->PinAppWithID(extension1_->id()); | |
| 2266 launcher_controller_->PinAppWithID(arc_app_id); | |
| 2267 launcher_controller_->PinAppWithID(extension2_->id()); | |
| 2268 | |
| 2269 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 2270 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id)); | |
| 2271 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 2272 | |
| 2273 EXPECT_EQ("AppList, Chrome, App1, Fake App 0, App2", GetPinnedAppStatus()); | |
| 2274 // In opt-out mode, only system apps are available and can't be uninstalled. | |
| 2275 // Skip the rest of the test. | |
| 2276 if (arc::ShouldArcAlwaysStart()) | |
| 2277 return; | |
| 2278 UninstallArcApps(); | |
| 2279 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id)); | |
| 2280 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 2281 SendListOfArcApps(); | |
| 2282 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id)); | |
| 2283 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 2284 | |
| 2285 // Opt-Out/Opt-In remove item from the shelf. | |
| 2286 launcher_controller_->PinAppWithID(arc_app_id); | |
| 2287 EXPECT_EQ("AppList, Chrome, App1, App2, Fake App 0", GetPinnedAppStatus()); | |
| 2288 EnablePlayStore(false); | |
| 2289 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 2290 EnablePlayStore(true); | |
| 2291 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 2292 SendListOfArcApps(); | |
| 2293 EXPECT_EQ("AppList, Chrome, App1, App2, Fake App 0", GetPinnedAppStatus()); | |
| 2294 } | |
| 2295 | |
| 2296 // Validates that ARC app pins persist across OptOut/OptIn. | |
| 2297 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcAppPinOptOutOptIn) { | |
| 2298 InitLauncherController(); | |
| 2299 | |
| 2300 const std::string arc_app_id1 = | |
| 2301 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]); | |
| 2302 const std::string arc_app_id2 = | |
| 2303 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]); | |
| 2304 | |
| 2305 SendListOfArcApps(); | |
| 2306 extension_service_->AddExtension(extension1_.get()); | |
| 2307 extension_service_->AddExtension(extension2_.get()); | |
| 2308 | |
| 2309 launcher_controller_->PinAppWithID(extension1_->id()); | |
| 2310 launcher_controller_->PinAppWithID(arc_app_id2); | |
| 2311 launcher_controller_->PinAppWithID(extension2_->id()); | |
| 2312 launcher_controller_->PinAppWithID(arc_app_id1); | |
| 2313 | |
| 2314 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 2315 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id1)); | |
| 2316 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 2317 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id2)); | |
| 2318 EXPECT_EQ("AppList, Chrome, App1, Fake App 1, App2, Fake App 0", | |
| 2319 GetPinnedAppStatus()); | |
| 2320 | |
| 2321 // TODO(victorhsieh): Implement opt-out. | |
| 2322 if (arc::ShouldArcAlwaysStart()) | |
| 2323 return; | |
| 2324 EnablePlayStore(false); | |
| 2325 | |
| 2326 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 2327 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 2328 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id1)); | |
| 2329 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 2330 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id2)); | |
| 2331 | |
| 2332 EnablePlayStore(true); | |
| 2333 SendListOfArcApps(); | |
| 2334 base::RunLoop().RunUntilIdle(); | |
| 2335 | |
| 2336 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 2337 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id1)); | |
| 2338 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 2339 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id2)); | |
| 2340 | |
| 2341 EXPECT_EQ("AppList, Chrome, App1, Fake App 1, App2, Fake App 0", | |
| 2342 GetPinnedAppStatus()); | |
| 2343 } | |
| 2344 | |
| 2345 // Check that with multi profile V1 apps are properly added / removed from the | |
| 2346 // shelf. | |
| 2347 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 2348 V1AppUpdateOnUserSwitch) { | |
| 2349 // Create a browser item in the LauncherController. | |
| 2350 InitLauncherController(); | |
| 2351 | |
| 2352 EXPECT_EQ(2, model_->item_count()); | |
| 2353 { | |
| 2354 // Create a "windowed gmail app". | |
| 2355 std::unique_ptr<V1App> v1_app( | |
| 2356 CreateRunningV1App(profile(), extension_misc::kGmailAppId, gmail_url)); | |
| 2357 EXPECT_EQ(3, model_->item_count()); | |
| 2358 | |
| 2359 // After switching to a second user the item should be gone. | |
| 2360 std::string user2 = "user2"; | |
| 2361 TestingProfile* profile2 = CreateMultiUserProfile(user2); | |
| 2362 const AccountId account_id2( | |
| 2363 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 2364 const AccountId account_id( | |
| 2365 multi_user_util::GetAccountIdFromProfile(profile())); | |
| 2366 SwitchActiveUser(account_id2); | |
| 2367 EXPECT_EQ(2, model_->item_count()); | |
| 2368 | |
| 2369 // After switching back the item should be back. | |
| 2370 SwitchActiveUser(account_id); | |
| 2371 EXPECT_EQ(3, model_->item_count()); | |
| 2372 // Note we destroy now the gmail app with the closure end. | |
| 2373 } | |
| 2374 EXPECT_EQ(2, model_->item_count()); | |
| 2375 } | |
| 2376 | |
| 2377 // Check edge cases with multi profile V1 apps in the shelf. | |
| 2378 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 2379 V1AppUpdateOnUserSwitchEdgecases) { | |
| 2380 // Create a browser item in the LauncherController. | |
| 2381 InitLauncherController(); | |
| 2382 | |
| 2383 // First test: Create an app when the user is not active. | |
| 2384 std::string user2 = "user2"; | |
| 2385 TestingProfile* profile2 = CreateMultiUserProfile(user2); | |
| 2386 const AccountId account_id2( | |
| 2387 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 2388 const AccountId account_id( | |
| 2389 multi_user_util::GetAccountIdFromProfile(profile())); | |
| 2390 { | |
| 2391 // Create a "windowed gmail app". | |
| 2392 std::unique_ptr<V1App> v1_app( | |
| 2393 CreateRunningV1App(profile2, extension_misc::kGmailAppId, gmail_url)); | |
| 2394 EXPECT_EQ(2, model_->item_count()); | |
| 2395 | |
| 2396 // However - switching to the user should show it. | |
| 2397 SwitchActiveUser(account_id2); | |
| 2398 EXPECT_EQ(3, model_->item_count()); | |
| 2399 | |
| 2400 // Second test: Remove the app when the user is not active and see that it | |
| 2401 // works. | |
| 2402 SwitchActiveUser(account_id); | |
| 2403 EXPECT_EQ(2, model_->item_count()); | |
| 2404 // Note: the closure ends and the browser will go away. | |
| 2405 } | |
| 2406 EXPECT_EQ(2, model_->item_count()); | |
| 2407 SwitchActiveUser(account_id2); | |
| 2408 EXPECT_EQ(2, model_->item_count()); | |
| 2409 SwitchActiveUser(account_id); | |
| 2410 EXPECT_EQ(2, model_->item_count()); | |
| 2411 } | |
| 2412 | |
| 2413 // Check edge case where a visiting V1 app gets closed (crbug.com/321374). | |
| 2414 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 2415 V1CloseOnVisitingDesktop) { | |
| 2416 // Create a browser item in the LauncherController. | |
| 2417 InitLauncherController(); | |
| 2418 | |
| 2419 chrome::MultiUserWindowManager* manager = | |
| 2420 chrome::MultiUserWindowManager::GetInstance(); | |
| 2421 | |
| 2422 // First create an app when the user is active. | |
| 2423 std::string user2 = "user2"; | |
| 2424 TestingProfile* profile2 = CreateMultiUserProfile(user2); | |
| 2425 const AccountId account_id( | |
| 2426 multi_user_util::GetAccountIdFromProfile(profile())); | |
| 2427 const AccountId account_id2( | |
| 2428 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 2429 { | |
| 2430 // Create a "windowed gmail app". | |
| 2431 std::unique_ptr<V1App> v1_app(CreateRunningV1App( | |
| 2432 profile(), extension_misc::kGmailAppId, kGmailLaunchURL)); | |
| 2433 EXPECT_EQ(3, model_->item_count()); | |
| 2434 | |
| 2435 // Transfer the app to the other screen and switch users. | |
| 2436 manager->ShowWindowForUser(v1_app->browser()->window()->GetNativeWindow(), | |
| 2437 account_id2); | |
| 2438 EXPECT_EQ(3, model_->item_count()); | |
| 2439 SwitchActiveUser(account_id2); | |
| 2440 EXPECT_EQ(2, model_->item_count()); | |
| 2441 } | |
| 2442 // After the app was destroyed, switch back. (which caused already a crash). | |
| 2443 SwitchActiveUser(account_id); | |
| 2444 | |
| 2445 // Create the same app again - which was also causing the crash. | |
| 2446 EXPECT_EQ(2, model_->item_count()); | |
| 2447 { | |
| 2448 // Create a "windowed gmail app". | |
| 2449 std::unique_ptr<V1App> v1_app(CreateRunningV1App( | |
| 2450 profile(), extension_misc::kGmailAppId, kGmailLaunchURL)); | |
| 2451 EXPECT_EQ(3, model_->item_count()); | |
| 2452 } | |
| 2453 SwitchActiveUser(account_id2); | |
| 2454 EXPECT_EQ(2, model_->item_count()); | |
| 2455 } | |
| 2456 | |
| 2457 // Check edge cases with multi profile V1 apps in the shelf. | |
| 2458 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 2459 V1AppUpdateOnUserSwitchEdgecases2) { | |
| 2460 // Create a browser item in the LauncherController. | |
| 2461 InitLauncherController(); | |
| 2462 | |
| 2463 // First test: Create an app when the user is not active. | |
| 2464 std::string user2 = "user2"; | |
| 2465 TestingProfile* profile2 = CreateMultiUserProfile(user2); | |
| 2466 const AccountId account_id( | |
| 2467 multi_user_util::GetAccountIdFromProfile(profile())); | |
| 2468 const AccountId account_id2( | |
| 2469 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 2470 SwitchActiveUser(account_id2); | |
| 2471 { | |
| 2472 // Create a "windowed gmail app". | |
| 2473 std::unique_ptr<V1App> v1_app( | |
| 2474 CreateRunningV1App(profile(), extension_misc::kGmailAppId, gmail_url)); | |
| 2475 EXPECT_EQ(2, model_->item_count()); | |
| 2476 | |
| 2477 // However - switching to the user should show it. | |
| 2478 SwitchActiveUser(account_id); | |
| 2479 EXPECT_EQ(3, model_->item_count()); | |
| 2480 | |
| 2481 // Second test: Remove the app when the user is not active and see that it | |
| 2482 // works. | |
| 2483 SwitchActiveUser(account_id2); | |
| 2484 EXPECT_EQ(2, model_->item_count()); | |
| 2485 v1_app.reset(); | |
| 2486 } | |
| 2487 EXPECT_EQ(2, model_->item_count()); | |
| 2488 SwitchActiveUser(account_id); | |
| 2489 EXPECT_EQ(2, model_->item_count()); | |
| 2490 SwitchActiveUser(account_id2); | |
| 2491 EXPECT_EQ(2, model_->item_count()); | |
| 2492 } | |
| 2493 | |
| 2494 // Check that activating an item which is on another user's desktop, will bring | |
| 2495 // it back. | |
| 2496 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 2497 TestLauncherActivationPullsBackWindow) { | |
| 2498 // Create a browser item in the LauncherController. | |
| 2499 InitLauncherController(); | |
| 2500 chrome::MultiUserWindowManager* manager = | |
| 2501 chrome::MultiUserWindowManager::GetInstance(); | |
| 2502 | |
| 2503 // Create a second test profile. The first is the one in profile() created in | |
| 2504 // BrowserWithTestWindowTest::SetUp(). | |
| 2505 // No need to add the profiles to the MultiUserWindowManager here. | |
| 2506 // CreateMultiUserProfile() already does that. | |
| 2507 TestingProfile* profile2 = CreateMultiUserProfile("user2"); | |
| 2508 const AccountId current_user = | |
| 2509 multi_user_util::GetAccountIdFromProfile(profile()); | |
| 2510 | |
| 2511 // Create a browser window with a native window for the current user. | |
| 2512 std::unique_ptr<Browser> browser( | |
| 2513 CreateBrowserWithTestWindowForProfile(profile())); | |
| 2514 BrowserWindow* browser_window = browser->window(); | |
| 2515 aura::Window* window = browser_window->GetNativeWindow(); | |
| 2516 manager->SetWindowOwner(window, current_user); | |
| 2517 | |
| 2518 // Check that an activation of the window on its owner's desktop does not | |
| 2519 // change the visibility to another user. | |
| 2520 launcher_controller_->ActivateWindowOrMinimizeIfActive(browser_window, false); | |
| 2521 EXPECT_TRUE(manager->IsWindowOnDesktopOfUser(window, current_user)); | |
| 2522 | |
| 2523 // Transfer the window to another user's desktop and check that activating it | |
| 2524 // does pull it back to that user. | |
| 2525 manager->ShowWindowForUser( | |
| 2526 window, multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 2527 EXPECT_FALSE(manager->IsWindowOnDesktopOfUser(window, current_user)); | |
| 2528 launcher_controller_->ActivateWindowOrMinimizeIfActive(browser_window, false); | |
| 2529 EXPECT_TRUE(manager->IsWindowOnDesktopOfUser(window, current_user)); | |
| 2530 } | |
| 2531 | |
| 2532 // Check that a running windowed V1 application will be properly pinned and | |
| 2533 // unpinned when the order gets changed through a profile / policy change. | |
| 2534 TEST_F(ChromeLauncherControllerImplTest, | |
| 2535 RestoreDefaultAndRunningV1AppsResyncOrder) { | |
| 2536 InitLauncherController(); | |
| 2537 | |
| 2538 syncer::SyncChangeList sync_list; | |
| 2539 InsertAddPinChange(&sync_list, 0, extension1_->id()); | |
| 2540 InsertAddPinChange(&sync_list, 1, extension3_->id()); | |
| 2541 SendPinChanges(sync_list, true); | |
| 2542 | |
| 2543 // The shelf layout has always one static item at the beginning (App List). | |
| 2544 extension_service_->AddExtension(extension1_.get()); | |
| 2545 EXPECT_EQ("AppList, Chrome, App1", GetPinnedAppStatus()); | |
| 2546 extension_service_->AddExtension(extension2_.get()); | |
| 2547 // No new app icon will be generated. | |
| 2548 EXPECT_EQ("AppList, Chrome, App1", GetPinnedAppStatus()); | |
| 2549 | |
| 2550 // Set the app status as running, which will add an unpinned item. | |
| 2551 launcher_controller_->SetV1AppStatus(extension2_->id(), ash::STATUS_RUNNING); | |
| 2552 EXPECT_EQ("AppList, Chrome, App1, app2", GetPinnedAppStatus()); | |
| 2553 extension_service_->AddExtension(extension3_.get()); | |
| 2554 EXPECT_EQ("AppList, Chrome, App1, App3, app2", GetPinnedAppStatus()); | |
| 2555 | |
| 2556 // Now request to pin all items, which will pin the running unpinned items. | |
| 2557 syncer::SyncChangeList sync_list1; | |
| 2558 InsertAddPinChange(&sync_list1, 0, extension3_->id()); | |
| 2559 InsertAddPinChange(&sync_list1, 1, extension2_->id()); | |
| 2560 InsertAddPinChange(&sync_list1, 2, extension1_->id()); | |
| 2561 SendPinChanges(sync_list1, true); | |
| 2562 EXPECT_EQ("AppList, Chrome, App3, App2, App1", GetPinnedAppStatus()); | |
| 2563 | |
| 2564 // Removing the requirement for app 2 to be pinned should convert it back to | |
| 2565 // running but not pinned. It should move towards the end of the shelf, after | |
| 2566 // the pinned items, as determined by the |ShelfModel|'s weight system. | |
| 2567 syncer::SyncChangeList sync_list2; | |
| 2568 InsertAddPinChange(&sync_list2, 0, extension3_->id()); | |
| 2569 InsertAddPinChange(&sync_list2, 1, extension1_->id()); | |
| 2570 SendPinChanges(sync_list2, true); | |
| 2571 EXPECT_EQ("AppList, Chrome, App3, App1, app2", GetPinnedAppStatus()); | |
| 2572 | |
| 2573 // Removing an item should simply close it and everything should shift. | |
| 2574 SendPinChanges(syncer::SyncChangeList(), true); | |
| 2575 EXPECT_EQ("AppList, Chrome, App3, app2", GetPinnedAppStatus()); | |
| 2576 } | |
| 2577 | |
| 2578 // Check that a running unpinned V2 application will be properly pinned and | |
| 2579 // unpinned when the order gets changed through a profile / policy change. | |
| 2580 TEST_F(ChromeLauncherControllerImplTest, | |
| 2581 RestoreDefaultAndRunningV2AppsResyncOrder) { | |
| 2582 InitLauncherController(); | |
| 2583 syncer::SyncChangeList sync_list0; | |
| 2584 InsertAddPinChange(&sync_list0, 0, extension1_->id()); | |
| 2585 InsertAddPinChange(&sync_list0, 1, extension3_->id()); | |
| 2586 SendPinChanges(sync_list0, true); | |
| 2587 // The shelf layout has always one static item at the beginning (app List). | |
| 2588 extension_service_->AddExtension(extension1_.get()); | |
| 2589 EXPECT_EQ("AppList, Chrome, App1", GetPinnedAppStatus()); | |
| 2590 extension_service_->AddExtension(extension_platform_app_.get()); | |
| 2591 // No new app icon will be generated. | |
| 2592 EXPECT_EQ("AppList, Chrome, App1", GetPinnedAppStatus()); | |
| 2593 // Add an unpinned but running V2 app. | |
| 2594 CreateRunningV2App(extension_platform_app_->id()); | |
| 2595 EXPECT_EQ("AppList, Chrome, App1, *platform_app", GetPinnedAppStatus()); | |
| 2596 extension_service_->AddExtension(extension3_.get()); | |
| 2597 EXPECT_EQ("AppList, Chrome, App1, App3, *platform_app", GetPinnedAppStatus()); | |
| 2598 | |
| 2599 // Now request to pin all items, which should pin the running unpinned item. | |
| 2600 syncer::SyncChangeList sync_list1; | |
| 2601 InsertAddPinChange(&sync_list1, 0, extension3_->id()); | |
| 2602 InsertAddPinChange(&sync_list1, 1, extension_platform_app_->id()); | |
| 2603 InsertAddPinChange(&sync_list1, 2, extension1_->id()); | |
| 2604 SendPinChanges(sync_list1, true); | |
| 2605 EXPECT_EQ("AppList, Chrome, App3, *Platform_App, App1", GetPinnedAppStatus()); | |
| 2606 | |
| 2607 // Removing the requirement for app 2 to be pinned should convert it back to | |
| 2608 // running but not pinned. It should move towards the end of the shelf, after | |
| 2609 // the pinned items, as determined by the |ShelfModel|'s weight system. | |
| 2610 syncer::SyncChangeList sync_list2; | |
| 2611 InsertAddPinChange(&sync_list2, 0, extension3_->id()); | |
| 2612 InsertAddPinChange(&sync_list2, 1, extension1_->id()); | |
| 2613 SendPinChanges(sync_list2, true); | |
| 2614 EXPECT_EQ("AppList, Chrome, App3, App1, *platform_app", GetPinnedAppStatus()); | |
| 2615 | |
| 2616 // Removing an item should simply close it and everything should shift. | |
| 2617 syncer::SyncChangeList sync_list3; | |
| 2618 InsertAddPinChange(&sync_list3, 0, extension3_->id()); | |
| 2619 SendPinChanges(sync_list3, true); | |
| 2620 EXPECT_EQ("AppList, Chrome, App3, *platform_app", GetPinnedAppStatus()); | |
| 2621 } | |
| 2622 | |
| 2623 // Each user has a different set of applications pinned. Check that when | |
| 2624 // switching between the two users, the state gets properly set. | |
| 2625 TEST_F(ChromeLauncherControllerImplTest, UserSwitchIconRestore) { | |
| 2626 syncer::SyncChangeList user_a; | |
| 2627 syncer::SyncChangeList user_b; | |
| 2628 | |
| 2629 SetUpMultiUserScenario(&user_a, &user_b); | |
| 2630 | |
| 2631 // Show user 1. | |
| 2632 SendPinChanges(user_a, true); | |
| 2633 EXPECT_EQ("AppList, App1, App2, App3, *Platform_App, App4, App5, Chrome", | |
| 2634 GetPinnedAppStatus()); | |
| 2635 | |
| 2636 // Show user 2. | |
| 2637 SendPinChanges(user_b, true); | |
| 2638 EXPECT_EQ("AppList, App6, App7, App8, Chrome", GetPinnedAppStatus()); | |
| 2639 | |
| 2640 // Switch back to 1. | |
| 2641 SendPinChanges(user_a, true); | |
| 2642 EXPECT_EQ("AppList, App1, App2, App3, *Platform_App, App4, App5, Chrome", | |
| 2643 GetPinnedAppStatus()); | |
| 2644 | |
| 2645 // Switch back to 2. | |
| 2646 SendPinChanges(user_b, true); | |
| 2647 EXPECT_EQ("AppList, App6, App7, App8, Chrome", GetPinnedAppStatus()); | |
| 2648 } | |
| 2649 | |
| 2650 // Each user has a different set of applications pinned, and one user has an | |
| 2651 // application running. Check that when switching between the two users, the | |
| 2652 // state gets properly set. | |
| 2653 TEST_F(ChromeLauncherControllerImplTest, | |
| 2654 UserSwitchIconRestoreWithRunningV2App) { | |
| 2655 syncer::SyncChangeList user_a; | |
| 2656 syncer::SyncChangeList user_b; | |
| 2657 | |
| 2658 SetUpMultiUserScenario(&user_a, &user_b); | |
| 2659 | |
| 2660 // Run the platform (V2) app. | |
| 2661 CreateRunningV2App(extension_platform_app_->id()); | |
| 2662 | |
| 2663 // Show user 1. | |
| 2664 SendPinChanges(user_a, true); | |
| 2665 EXPECT_EQ("AppList, App1, App2, App3, *Platform_App, App4, App5, Chrome", | |
| 2666 GetPinnedAppStatus()); | |
| 2667 | |
| 2668 // Show user 2. | |
| 2669 SendPinChanges(user_b, true); | |
| 2670 EXPECT_EQ("AppList, App6, App7, App8, Chrome, *platform_app", | |
| 2671 GetPinnedAppStatus()); | |
| 2672 | |
| 2673 // Switch back to 1. | |
| 2674 SendPinChanges(user_a, true); | |
| 2675 EXPECT_EQ("AppList, App1, App2, App3, *Platform_App, App4, App5, Chrome", | |
| 2676 GetPinnedAppStatus()); | |
| 2677 | |
| 2678 // Switch back to 2. | |
| 2679 SendPinChanges(user_b, true); | |
| 2680 EXPECT_EQ("AppList, App6, App7, App8, Chrome, *platform_app", | |
| 2681 GetPinnedAppStatus()); | |
| 2682 } | |
| 2683 | |
| 2684 // Each user has a different set of applications pinned, and one user has an | |
| 2685 // application running. The chrome icon is not the last item in the list. | |
| 2686 // Check that when switching between the two users, the state gets properly set. | |
| 2687 // There was once a bug associated with this. | |
| 2688 TEST_F(ChromeLauncherControllerImplTest, | |
| 2689 UserSwitchIconRestoreWithRunningV2AppChromeInMiddle) { | |
| 2690 syncer::SyncChangeList user_a; | |
| 2691 syncer::SyncChangeList user_b; | |
| 2692 SetUpMultiUserScenario(&user_a, &user_b); | |
| 2693 | |
| 2694 // Run the platform (V2) app. | |
| 2695 CreateRunningV2App(extension_platform_app_->id()); | |
| 2696 | |
| 2697 // Show user 1. | |
| 2698 SendPinChanges(user_a, true); | |
| 2699 SetShelfChromeIconIndex(5); | |
| 2700 EXPECT_EQ("AppList, App1, App2, App3, *Platform_App, App4, Chrome, App5", | |
| 2701 GetPinnedAppStatus()); | |
| 2702 | |
| 2703 // Show user 2. | |
| 2704 SendPinChanges(user_b, true); | |
| 2705 SetShelfChromeIconIndex(4); | |
| 2706 EXPECT_EQ("AppList, App6, App7, App8, Chrome, *platform_app", | |
| 2707 GetPinnedAppStatus()); | |
| 2708 | |
| 2709 // Switch back to 1. | |
| 2710 SendPinChanges(user_a, true); | |
| 2711 SetShelfChromeIconIndex(5); | |
| 2712 EXPECT_EQ("AppList, App1, App2, App3, *Platform_App, App4, Chrome, App5", | |
| 2713 GetPinnedAppStatus()); | |
| 2714 } | |
| 2715 | |
| 2716 TEST_F(ChromeLauncherControllerImplTest, Policy) { | |
| 2717 extension_service_->AddExtension(extension1_.get()); | |
| 2718 extension_service_->AddExtension(extension3_.get()); | |
| 2719 | |
| 2720 InitLauncherController(); | |
| 2721 | |
| 2722 syncer::SyncChangeList sync_list; | |
| 2723 InsertAddPinChange(&sync_list, 0, extension_misc::kChromeAppId); | |
| 2724 SendPinChanges(sync_list, true); | |
| 2725 | |
| 2726 base::ListValue policy_value; | |
| 2727 InsertPrefValue(&policy_value, 0, extension1_->id()); | |
| 2728 InsertPrefValue(&policy_value, 1, extension2_->id()); | |
| 2729 profile()->GetTestingPrefService()->SetManagedPref( | |
| 2730 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy()); | |
| 2731 | |
| 2732 // Only |extension1_| should get pinned. |extension2_| is specified but not | |
| 2733 // installed, and |extension3_| is part of the default set, but that shouldn't | |
| 2734 // take effect when the policy override is in place. | |
| 2735 ASSERT_EQ(3, model_->item_count()); | |
| 2736 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type); | |
| 2737 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 2738 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 2739 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 2740 | |
| 2741 // Installing |extension2_| should add it to the launcher. | |
| 2742 extension_service_->AddExtension(extension2_.get()); | |
| 2743 ASSERT_EQ(4, model_->item_count()); | |
| 2744 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type); | |
| 2745 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 2746 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 2747 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 2748 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 2749 | |
| 2750 // Removing |extension1_| from the policy should not be reflected in the | |
| 2751 // launcher and pin will exist. | |
| 2752 policy_value.Remove(0, NULL); | |
| 2753 profile()->GetTestingPrefService()->SetManagedPref( | |
| 2754 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy()); | |
| 2755 EXPECT_EQ(4, model_->item_count()); | |
| 2756 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 2757 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id())); | |
| 2758 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id())); | |
| 2759 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 2760 } | |
| 2761 | |
| 2762 TEST_F(ChromeLauncherControllerImplTest, UnpinWithUninstall) { | |
| 2763 extension_service_->AddExtension(extension3_.get()); | |
| 2764 extension_service_->AddExtension(extension4_.get()); | |
| 2765 | |
| 2766 InitLauncherController(); | |
| 2767 | |
| 2768 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 2769 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id())); | |
| 2770 | |
| 2771 extension_service_->UnloadExtension(extension3_->id(), | |
| 2772 UnloadedExtensionInfo::REASON_UNINSTALL); | |
| 2773 | |
| 2774 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 2775 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension4_->id())); | |
| 2776 } | |
| 2777 | |
| 2778 TEST_F(ChromeLauncherControllerImplTest, SyncUpdates) { | |
| 2779 extension_service_->AddExtension(extension2_.get()); | |
| 2780 extension_service_->AddExtension(extension3_.get()); | |
| 2781 extension_service_->AddExtension(extension4_.get()); | |
| 2782 | |
| 2783 InitLauncherController(); | |
| 2784 | |
| 2785 syncer::SyncChangeList sync_list; | |
| 2786 InsertAddPinChange(&sync_list, 10, extension_misc::kChromeAppId); | |
| 2787 SendPinChanges(sync_list, true); | |
| 2788 | |
| 2789 std::vector<std::string> expected_pinned_apps; | |
| 2790 std::vector<std::string> actual_pinned_apps; | |
| 2791 GetPinnedAppIds(launcher_controller_, &actual_pinned_apps); | |
| 2792 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps); | |
| 2793 | |
| 2794 // Unavailable extensions don't create launcher items. | |
| 2795 sync_list.clear(); | |
| 2796 InsertAddPinChange(&sync_list, 0, extension1_->id()); | |
| 2797 InsertAddPinChange(&sync_list, 1, extension2_->id()); | |
| 2798 InsertAddPinChange(&sync_list, 3, extension4_->id()); | |
| 2799 SendPinChanges(sync_list, false); | |
| 2800 | |
| 2801 expected_pinned_apps.push_back(extension2_->id()); | |
| 2802 expected_pinned_apps.push_back(extension4_->id()); | |
| 2803 GetPinnedAppIds(launcher_controller_, &actual_pinned_apps); | |
| 2804 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps); | |
| 2805 | |
| 2806 sync_list.clear(); | |
| 2807 InsertAddPinChange(&sync_list, 2, extension3_->id()); | |
| 2808 SendPinChanges(sync_list, false); | |
| 2809 expected_pinned_apps.insert(expected_pinned_apps.begin() + 1, | |
| 2810 extension3_->id()); | |
| 2811 GetPinnedAppIds(launcher_controller_, &actual_pinned_apps); | |
| 2812 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps); | |
| 2813 | |
| 2814 sync_list.clear(); | |
| 2815 InsertUpdatePinChange(&sync_list, 0, extension4_->id()); | |
| 2816 InsertUpdatePinChange(&sync_list, 1, extension3_->id()); | |
| 2817 InsertUpdatePinChange(&sync_list, 2, extension2_->id()); | |
| 2818 SendPinChanges(sync_list, false); | |
| 2819 std::reverse(expected_pinned_apps.begin(), expected_pinned_apps.end()); | |
| 2820 GetPinnedAppIds(launcher_controller_, &actual_pinned_apps); | |
| 2821 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps); | |
| 2822 | |
| 2823 // Sending legacy sync change without pin info should not affect pin model. | |
| 2824 sync_list.clear(); | |
| 2825 InsertLegacyPinChange(&sync_list, extension4_->id()); | |
| 2826 SendPinChanges(sync_list, false); | |
| 2827 GetPinnedAppIds(launcher_controller_, &actual_pinned_apps); | |
| 2828 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps); | |
| 2829 | |
| 2830 sync_list.clear(); | |
| 2831 InsertRemovePinChange(&sync_list, extension4_->id()); | |
| 2832 SendPinChanges(sync_list, false); | |
| 2833 expected_pinned_apps.erase(expected_pinned_apps.begin()); | |
| 2834 GetPinnedAppIds(launcher_controller_, &actual_pinned_apps); | |
| 2835 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps); | |
| 2836 | |
| 2837 sync_list.clear(); | |
| 2838 InsertRemovePinChange(&sync_list, extension3_->id()); | |
| 2839 InsertRemovePinChange(&sync_list, extension2_->id()); | |
| 2840 SendPinChanges(sync_list, false); | |
| 2841 expected_pinned_apps.clear(); | |
| 2842 GetPinnedAppIds(launcher_controller_, &actual_pinned_apps); | |
| 2843 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps); | |
| 2844 } | |
| 2845 | |
| 2846 TEST_F(ChromeLauncherControllerImplTest, ImportLegacyPin) { | |
| 2847 // Note extension3_ is actually Gmail app which is default pinned. | |
| 2848 extension_service_->AddExtension(extension3_.get()); | |
| 2849 InitLauncherController(); | |
| 2850 | |
| 2851 // Default pins should contain Gmail. Pref is not syncing now. | |
| 2852 EXPECT_EQ("AppList, Chrome, App3", GetPinnedAppStatus()); | |
| 2853 | |
| 2854 extension_service_->AddExtension(extension2_.get()); | |
| 2855 EXPECT_EQ("AppList, Chrome, App3", GetPinnedAppStatus()); | |
| 2856 | |
| 2857 // Initially pins are imported from legacy pref based model. | |
| 2858 base::ListValue value; | |
| 2859 InsertPrefValue(&value, 0, extension4_->id()); | |
| 2860 InsertPrefValue(&value, 1, extension2_->id()); | |
| 2861 InsertPrefValue(&value, 2, extension3_->id()); | |
| 2862 StartPrefSyncServiceForPins(value); | |
| 2863 | |
| 2864 // Imported pins contain App2. App2 should be added to pins now. | |
| 2865 EXPECT_EQ("AppList, Chrome, App2, App3", GetPinnedAppStatus()); | |
| 2866 | |
| 2867 // extension4_ is in the pin list. | |
| 2868 extension_service_->AddExtension(extension4_.get()); | |
| 2869 // extension5_ is not in the pin list. | |
| 2870 extension_service_->AddExtension(extension5_.get()); | |
| 2871 EXPECT_EQ("AppList, Chrome, App4, App2, App3", GetPinnedAppStatus()); | |
| 2872 | |
| 2873 // Apply app sync, unpin one app and pin new one. | |
| 2874 syncer::SyncChangeList sync_list; | |
| 2875 InsertAddPinChange(&sync_list, -1, extension3_->id()); | |
| 2876 InsertAddPinChange(&sync_list, 3, extension5_->id()); | |
| 2877 SendPinChanges(sync_list, false); | |
| 2878 EXPECT_EQ("AppList, Chrome, App4, App2, App5", GetPinnedAppStatus()); | |
| 2879 | |
| 2880 // At this point changing old pref based model does not affect pin model. | |
| 2881 InsertPrefValue(&value, 3, extension5_->id()); | |
| 2882 StopPrefSyncService(); | |
| 2883 StartPrefSyncServiceForPins(value); | |
| 2884 EXPECT_EQ("AppList, Chrome, App4, App2, App5", GetPinnedAppStatus()); | |
| 2885 | |
| 2886 // Next Chrome start should preserve pins. | |
| 2887 RecreateLauncherController()->Init(); | |
| 2888 StopPrefSyncService(); | |
| 2889 StartPrefSyncService(syncer::SyncDataList()); | |
| 2890 EXPECT_EQ("AppList, Chrome, App4, App2, App5", GetPinnedAppStatus()); | |
| 2891 } | |
| 2892 | |
| 2893 TEST_F(ChromeLauncherControllerImplTest, PendingInsertionOrder) { | |
| 2894 extension_service_->AddExtension(extension1_.get()); | |
| 2895 extension_service_->AddExtension(extension3_.get()); | |
| 2896 | |
| 2897 InitLauncherController(); | |
| 2898 | |
| 2899 syncer::SyncChangeList sync_list; | |
| 2900 InsertAddPinChange(&sync_list, 0, extension1_->id()); | |
| 2901 InsertAddPinChange(&sync_list, 1, extension2_->id()); | |
| 2902 InsertAddPinChange(&sync_list, 2, extension3_->id()); | |
| 2903 SendPinChanges(sync_list, true); | |
| 2904 | |
| 2905 std::vector<std::string> expected_pinned_apps; | |
| 2906 expected_pinned_apps.push_back(extension1_->id()); | |
| 2907 expected_pinned_apps.push_back(extension3_->id()); | |
| 2908 std::vector<std::string> actual_pinned_apps; | |
| 2909 | |
| 2910 GetPinnedAppIds(launcher_controller_, &actual_pinned_apps); | |
| 2911 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps); | |
| 2912 | |
| 2913 // Install |extension2| and verify it shows up between the other two. | |
| 2914 extension_service_->AddExtension(extension2_.get()); | |
| 2915 expected_pinned_apps.insert(expected_pinned_apps.begin() + 1, | |
| 2916 extension2_->id()); | |
| 2917 GetPinnedAppIds(launcher_controller_, &actual_pinned_apps); | |
| 2918 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps); | |
| 2919 } | |
| 2920 | |
| 2921 // Ensure |controller| creates the expected menu items for the given shelf item. | |
| 2922 void CheckAppMenu(ChromeLauncherControllerImpl* controller, | |
| 2923 const ash::ShelfItem& item, | |
| 2924 size_t expected_item_count, | |
| 2925 base::string16 expected_item_titles[]) { | |
| 2926 ash::MenuItemList items = controller->GetAppMenuItemsForTesting(item); | |
| 2927 ASSERT_EQ(expected_item_count, items.size()); | |
| 2928 for (size_t i = 0; i < expected_item_count; i++) | |
| 2929 EXPECT_EQ(expected_item_titles[i], items[i]->label); | |
| 2930 } | |
| 2931 | |
| 2932 // Check that browsers get reflected correctly in the launcher menu. | |
| 2933 TEST_F(ChromeLauncherControllerImplTest, BrowserMenuGeneration) { | |
| 2934 EXPECT_EQ(1U, chrome::GetTotalBrowserCount()); | |
| 2935 chrome::NewTab(browser()); | |
| 2936 | |
| 2937 InitLauncherController(); | |
| 2938 | |
| 2939 // Check that the browser list is empty at this time. | |
| 2940 ash::ShelfItem item_browser; | |
| 2941 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; | |
| 2942 item_browser.id = | |
| 2943 launcher_controller_->GetShelfIDForAppID(extension_misc::kChromeAppId); | |
| 2944 CheckAppMenu(launcher_controller_, item_browser, 0, nullptr); | |
| 2945 | |
| 2946 // Now make the created browser() visible by showing its browser window. | |
| 2947 browser()->window()->Show(); | |
| 2948 base::string16 title1 = ASCIIToUTF16("Test1"); | |
| 2949 NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1); | |
| 2950 base::string16 one_menu_item[] = { title1 }; | |
| 2951 | |
| 2952 CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item); | |
| 2953 | |
| 2954 // Create one more browser/window and check that one more was added. | |
| 2955 std::unique_ptr<Browser> browser2( | |
| 2956 CreateBrowserWithTestWindowForProfile(profile())); | |
| 2957 chrome::NewTab(browser2.get()); | |
| 2958 browser2->window()->Show(); | |
| 2959 base::string16 title2 = ASCIIToUTF16("Test2"); | |
| 2960 NavigateAndCommitActiveTabWithTitle(browser2.get(), GURL("http://test2"), | |
| 2961 title2); | |
| 2962 | |
| 2963 // Check that the list contains now two entries - make furthermore sure that | |
| 2964 // the active item is the first entry. | |
| 2965 base::string16 two_menu_items[] = {title1, title2}; | |
| 2966 CheckAppMenu(launcher_controller_, item_browser, 2, two_menu_items); | |
| 2967 | |
| 2968 // Apparently we have to close all tabs we have. | |
| 2969 chrome::CloseTab(browser2.get()); | |
| 2970 } | |
| 2971 | |
| 2972 // Check the multi profile case where only user related browsers should show up. | |
| 2973 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 2974 BrowserMenuGenerationTwoUsers) { | |
| 2975 // Create a browser item in the LauncherController. | |
| 2976 InitLauncherController(); | |
| 2977 | |
| 2978 ash::ShelfItem item_browser; | |
| 2979 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; | |
| 2980 item_browser.id = | |
| 2981 launcher_controller_->GetShelfIDForAppID(extension_misc::kChromeAppId); | |
| 2982 | |
| 2983 // Check that the menu is empty. | |
| 2984 chrome::NewTab(browser()); | |
| 2985 CheckAppMenu(launcher_controller_, item_browser, 0, nullptr); | |
| 2986 | |
| 2987 // Show the created |browser()| by showing its window. | |
| 2988 browser()->window()->Show(); | |
| 2989 base::string16 title1 = ASCIIToUTF16("Test1"); | |
| 2990 NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1); | |
| 2991 base::string16 one_menu_item1[] = { title1 }; | |
| 2992 CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item1); | |
| 2993 | |
| 2994 // Create a browser for another user and check that it is not included in the | |
| 2995 // users running browser list. | |
| 2996 std::string user2 = "user2"; | |
| 2997 TestingProfile* profile2 = CreateMultiUserProfile(user2); | |
| 2998 const AccountId account_id2( | |
| 2999 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 3000 std::unique_ptr<Browser> browser2( | |
| 3001 CreateBrowserAndTabWithProfile(profile2, user2, "http://test2")); | |
| 3002 base::string16 one_menu_item2[] = { ASCIIToUTF16(user2) }; | |
| 3003 CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item1); | |
| 3004 | |
| 3005 // Switch to the other user and make sure that only that browser window gets | |
| 3006 // shown. | |
| 3007 SwitchActiveUser(account_id2); | |
| 3008 CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item2); | |
| 3009 | |
| 3010 // Transferred browsers of other users should not show up in the list. | |
| 3011 chrome::MultiUserWindowManager::GetInstance()->ShowWindowForUser( | |
| 3012 browser()->window()->GetNativeWindow(), account_id2); | |
| 3013 CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item2); | |
| 3014 | |
| 3015 chrome::CloseTab(browser2.get()); | |
| 3016 } | |
| 3017 | |
| 3018 // Check that V1 apps are correctly reflected in the launcher menu using the | |
| 3019 // refocus logic. | |
| 3020 // Note that the extension matching logic is tested by the extension system | |
| 3021 // and does not need a separate test here. | |
| 3022 TEST_F(ChromeLauncherControllerImplTest, V1AppMenuGeneration) { | |
| 3023 EXPECT_EQ(1U, chrome::GetTotalBrowserCount()); | |
| 3024 EXPECT_EQ(0, browser()->tab_strip_model()->count()); | |
| 3025 | |
| 3026 InitLauncherControllerWithBrowser(); | |
| 3027 | |
| 3028 // The model should only contain the browser shortcut and app list items. | |
| 3029 EXPECT_EQ(2, model_->item_count()); | |
| 3030 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 3031 | |
| 3032 // Installing |extension3_| adds it to the launcher. | |
| 3033 ash::ShelfID gmail_id = model_->next_id(); | |
| 3034 extension_service_->AddExtension(extension3_.get()); | |
| 3035 EXPECT_EQ(3, model_->item_count()); | |
| 3036 int gmail_index = model_->ItemIndexByID(gmail_id); | |
| 3037 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[gmail_index].type); | |
| 3038 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 3039 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); | |
| 3040 | |
| 3041 // Check the menu content. | |
| 3042 ash::ShelfItem item_browser; | |
| 3043 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; | |
| 3044 item_browser.id = | |
| 3045 launcher_controller_->GetShelfIDForAppID(extension_misc::kChromeAppId); | |
| 3046 | |
| 3047 ash::ShelfItem item_gmail; | |
| 3048 item_gmail.type = ash::TYPE_PINNED_APP; | |
| 3049 item_gmail.id = gmail_id; | |
| 3050 CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr); | |
| 3051 | |
| 3052 // Set the gmail URL to a new tab. | |
| 3053 base::string16 title1 = ASCIIToUTF16("Test1"); | |
| 3054 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); | |
| 3055 | |
| 3056 base::string16 one_menu_item[] = { title1 }; | |
| 3057 CheckAppMenu(launcher_controller_, item_gmail, 1, one_menu_item); | |
| 3058 | |
| 3059 // Create one empty tab. | |
| 3060 chrome::NewTab(browser()); | |
| 3061 base::string16 title2 = ASCIIToUTF16("Test2"); | |
| 3062 NavigateAndCommitActiveTabWithTitle( | |
| 3063 browser(), | |
| 3064 GURL("https://bla"), | |
| 3065 title2); | |
| 3066 | |
| 3067 // and another one with another gmail instance. | |
| 3068 chrome::NewTab(browser()); | |
| 3069 base::string16 title3 = ASCIIToUTF16("Test3"); | |
| 3070 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title3); | |
| 3071 base::string16 two_menu_items[] = {title1, title3}; | |
| 3072 CheckAppMenu(launcher_controller_, item_gmail, 2, two_menu_items); | |
| 3073 | |
| 3074 // Even though the item is in the V1 app list, it should also be in the | |
| 3075 // browser list. | |
| 3076 base::string16 browser_menu_item[] = {title3}; | |
| 3077 CheckAppMenu(launcher_controller_, item_browser, 1, browser_menu_item); | |
| 3078 | |
| 3079 // Test that closing of (all) the item(s) does work (and all menus get | |
| 3080 // updated properly). | |
| 3081 launcher_controller_->Close(item_gmail.id); | |
| 3082 | |
| 3083 CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr); | |
| 3084 base::string16 browser_menu_item2[] = { title2 }; | |
| 3085 CheckAppMenu(launcher_controller_, item_browser, 1, browser_menu_item2); | |
| 3086 } | |
| 3087 | |
| 3088 // Check the multi profile case where only user related apps should show up. | |
| 3089 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 3090 V1AppMenuGenerationTwoUsers) { | |
| 3091 // Create a browser item in the LauncherController. | |
| 3092 InitLauncherController(); | |
| 3093 chrome::NewTab(browser()); | |
| 3094 | |
| 3095 // Installing |extension3_| adds it to the launcher. | |
| 3096 ash::ShelfID gmail_id = model_->next_id(); | |
| 3097 extension_service_->AddExtension(extension3_.get()); | |
| 3098 EXPECT_EQ(3, model_->item_count()); | |
| 3099 int gmail_index = model_->ItemIndexByID(gmail_id); | |
| 3100 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[gmail_index].type); | |
| 3101 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 3102 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); | |
| 3103 | |
| 3104 // Check the menu content. | |
| 3105 ash::ShelfItem item_browser; | |
| 3106 item_browser.type = ash::TYPE_BROWSER_SHORTCUT; | |
| 3107 item_browser.id = | |
| 3108 launcher_controller_->GetShelfIDForAppID(extension_misc::kChromeAppId); | |
| 3109 | |
| 3110 ash::ShelfItem item_gmail; | |
| 3111 item_gmail.type = ash::TYPE_PINNED_APP; | |
| 3112 item_gmail.id = gmail_id; | |
| 3113 CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr); | |
| 3114 | |
| 3115 // Set the gmail URL to a new tab. | |
| 3116 base::string16 title1 = ASCIIToUTF16("Test1"); | |
| 3117 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); | |
| 3118 | |
| 3119 base::string16 one_menu_item[] = { title1 }; | |
| 3120 CheckAppMenu(launcher_controller_, item_gmail, 1, one_menu_item); | |
| 3121 | |
| 3122 // Create a second profile and switch to that user. | |
| 3123 std::string user2 = "user2"; | |
| 3124 TestingProfile* profile2 = CreateMultiUserProfile(user2); | |
| 3125 const AccountId account_id2( | |
| 3126 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 3127 SwitchActiveUser(account_id2); | |
| 3128 | |
| 3129 // No item should have content yet. | |
| 3130 CheckAppMenu(launcher_controller_, item_browser, 0, nullptr); | |
| 3131 CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr); | |
| 3132 | |
| 3133 // Transfer the browser of the first user - it should still not show up. | |
| 3134 chrome::MultiUserWindowManager::GetInstance()->ShowWindowForUser( | |
| 3135 browser()->window()->GetNativeWindow(), account_id2); | |
| 3136 | |
| 3137 CheckAppMenu(launcher_controller_, item_browser, 0, nullptr); | |
| 3138 CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr); | |
| 3139 } | |
| 3140 | |
| 3141 // Check that V2 applications are creating items properly in the launcher when | |
| 3142 // instantiated by the current user. | |
| 3143 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 3144 V2AppHandlingTwoUsers) { | |
| 3145 InitLauncherController(); | |
| 3146 // Create a profile for our second user (will be destroyed by the framework). | |
| 3147 TestingProfile* profile2 = CreateMultiUserProfile("user2"); | |
| 3148 const AccountId account_id( | |
| 3149 multi_user_util::GetAccountIdFromProfile(profile())); | |
| 3150 const AccountId account_id2( | |
| 3151 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 3152 // Check that there is a browser and a app launcher. | |
| 3153 EXPECT_EQ(2, model_->item_count()); | |
| 3154 | |
| 3155 // Add a v2 app. | |
| 3156 V2App v2_app(profile(), extension1_.get()); | |
| 3157 EXPECT_EQ(3, model_->item_count()); | |
| 3158 | |
| 3159 // After switching users the item should go away. | |
| 3160 SwitchActiveUser(account_id2); | |
| 3161 EXPECT_EQ(2, model_->item_count()); | |
| 3162 | |
| 3163 // And it should come back when switching back. | |
| 3164 SwitchActiveUser(account_id); | |
| 3165 EXPECT_EQ(3, model_->item_count()); | |
| 3166 } | |
| 3167 | |
| 3168 // Check that V2 applications are creating items properly in edge cases: | |
| 3169 // a background user creates a V2 app, gets active and inactive again and then | |
| 3170 // deletes the app. | |
| 3171 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 3172 V2AppHandlingTwoUsersEdgeCases) { | |
| 3173 InitLauncherController(); | |
| 3174 // Create a profile for our second user (will be destroyed by the framework). | |
| 3175 TestingProfile* profile2 = CreateMultiUserProfile("user2"); | |
| 3176 const AccountId account_id( | |
| 3177 multi_user_util::GetAccountIdFromProfile(profile())); | |
| 3178 const AccountId account_id2( | |
| 3179 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 3180 // Check that there is a browser and a app launcher. | |
| 3181 EXPECT_EQ(2, model_->item_count()); | |
| 3182 | |
| 3183 // Switch to an inactive user. | |
| 3184 SwitchActiveUser(account_id2); | |
| 3185 EXPECT_EQ(2, model_->item_count()); | |
| 3186 | |
| 3187 // Add the v2 app to the inactive user and check that no item was added to | |
| 3188 // the launcher. | |
| 3189 { | |
| 3190 V2App v2_app(profile(), extension1_.get()); | |
| 3191 EXPECT_EQ(2, model_->item_count()); | |
| 3192 | |
| 3193 // Switch to the primary user and check that the item is shown. | |
| 3194 SwitchActiveUser(account_id); | |
| 3195 EXPECT_EQ(3, model_->item_count()); | |
| 3196 | |
| 3197 // Switch to the second user and check that the item goes away - even if the | |
| 3198 // item gets closed. | |
| 3199 SwitchActiveUser(account_id2); | |
| 3200 EXPECT_EQ(2, model_->item_count()); | |
| 3201 } | |
| 3202 | |
| 3203 // After the application was killed there should be still 2 items. | |
| 3204 EXPECT_EQ(2, model_->item_count()); | |
| 3205 | |
| 3206 // Switching then back to the default user should not show the additional item | |
| 3207 // anymore. | |
| 3208 SwitchActiveUser(account_id); | |
| 3209 EXPECT_EQ(2, model_->item_count()); | |
| 3210 } | |
| 3211 | |
| 3212 // Check that V2 applications will be made visible on the target desktop if | |
| 3213 // another window of the same type got previously teleported there. | |
| 3214 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 3215 V2AppFollowsTeleportedWindow) { | |
| 3216 InitLauncherController(); | |
| 3217 chrome::MultiUserWindowManager* manager = | |
| 3218 chrome::MultiUserWindowManager::GetInstance(); | |
| 3219 | |
| 3220 // Create and add three users / profiles, and go to #1's desktop. | |
| 3221 TestingProfile* profile1 = CreateMultiUserProfile("user-1"); | |
| 3222 TestingProfile* profile2 = CreateMultiUserProfile("user-2"); | |
| 3223 TestingProfile* profile3 = CreateMultiUserProfile("user-3"); | |
| 3224 const AccountId account_id1( | |
| 3225 multi_user_util::GetAccountIdFromProfile(profile1)); | |
| 3226 const AccountId account_id2( | |
| 3227 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 3228 const AccountId account_id3( | |
| 3229 multi_user_util::GetAccountIdFromProfile(profile3)); | |
| 3230 SwitchActiveUser(account_id1); | |
| 3231 | |
| 3232 // A v2 app for user #1 should be shown first and get hidden when switching to | |
| 3233 // desktop #2. | |
| 3234 V2App v2_app_1(profile1, extension1_.get()); | |
| 3235 EXPECT_TRUE(v2_app_1.window()->GetNativeWindow()->IsVisible()); | |
| 3236 SwitchActiveUser(account_id2); | |
| 3237 EXPECT_FALSE(v2_app_1.window()->GetNativeWindow()->IsVisible()); | |
| 3238 | |
| 3239 // Add a v2 app for user #1 while on desktop #2 should not be shown. | |
| 3240 V2App v2_app_2(profile1, extension1_.get()); | |
| 3241 EXPECT_FALSE(v2_app_1.window()->GetNativeWindow()->IsVisible()); | |
| 3242 EXPECT_FALSE(v2_app_2.window()->GetNativeWindow()->IsVisible()); | |
| 3243 | |
| 3244 // Teleport the app from user #1 to the desktop #2 should show it. | |
| 3245 manager->ShowWindowForUser(v2_app_1.window()->GetNativeWindow(), account_id2); | |
| 3246 EXPECT_TRUE(v2_app_1.window()->GetNativeWindow()->IsVisible()); | |
| 3247 EXPECT_FALSE(v2_app_2.window()->GetNativeWindow()->IsVisible()); | |
| 3248 | |
| 3249 // Creating a new application for user #1 on desktop #2 should teleport it | |
| 3250 // there automatically. | |
| 3251 V2App v2_app_3(profile1, extension1_.get()); | |
| 3252 EXPECT_TRUE(v2_app_1.window()->GetNativeWindow()->IsVisible()); | |
| 3253 EXPECT_FALSE(v2_app_2.window()->GetNativeWindow()->IsVisible()); | |
| 3254 EXPECT_TRUE(v2_app_3.window()->GetNativeWindow()->IsVisible()); | |
| 3255 | |
| 3256 // Switching back to desktop#1 and creating an app for user #1 should move | |
| 3257 // the app on desktop #1. | |
| 3258 SwitchActiveUser(account_id1); | |
| 3259 V2App v2_app_4(profile1, extension1_.get()); | |
| 3260 EXPECT_FALSE(v2_app_1.window()->GetNativeWindow()->IsVisible()); | |
| 3261 EXPECT_TRUE(v2_app_2.window()->GetNativeWindow()->IsVisible()); | |
| 3262 EXPECT_FALSE(v2_app_3.window()->GetNativeWindow()->IsVisible()); | |
| 3263 EXPECT_TRUE(v2_app_4.window()->GetNativeWindow()->IsVisible()); | |
| 3264 | |
| 3265 // Switching to desktop #3 and creating an app for user #1 should place it on | |
| 3266 // that user's desktop (#1). | |
| 3267 SwitchActiveUser(account_id3); | |
| 3268 V2App v2_app_5(profile1, extension1_.get()); | |
| 3269 EXPECT_FALSE(v2_app_5.window()->GetNativeWindow()->IsVisible()); | |
| 3270 SwitchActiveUser(account_id1); | |
| 3271 EXPECT_TRUE(v2_app_5.window()->GetNativeWindow()->IsVisible()); | |
| 3272 | |
| 3273 // Switching to desktop #2, hiding the app window and creating an app should | |
| 3274 // teleport there automatically. | |
| 3275 SwitchActiveUser(account_id2); | |
| 3276 v2_app_1.window()->Hide(); | |
| 3277 V2App v2_app_6(profile1, extension1_.get()); | |
| 3278 EXPECT_FALSE(v2_app_1.window()->GetNativeWindow()->IsVisible()); | |
| 3279 EXPECT_FALSE(v2_app_2.window()->GetNativeWindow()->IsVisible()); | |
| 3280 EXPECT_TRUE(v2_app_6.window()->GetNativeWindow()->IsVisible()); | |
| 3281 } | |
| 3282 | |
| 3283 // Check that V2 applications hide correctly on the shelf when the app window | |
| 3284 // is hidden. | |
| 3285 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerImplTest, | |
| 3286 V2AppHiddenWindows) { | |
| 3287 InitLauncherController(); | |
| 3288 | |
| 3289 TestingProfile* profile2 = CreateMultiUserProfile("user-2"); | |
| 3290 const AccountId account_id( | |
| 3291 multi_user_util::GetAccountIdFromProfile(profile())); | |
| 3292 const AccountId account_id2( | |
| 3293 multi_user_util::GetAccountIdFromProfile(profile2)); | |
| 3294 SwitchActiveUser(account_id); | |
| 3295 EXPECT_EQ(2, model_->item_count()); | |
| 3296 | |
| 3297 V2App v2_app_1(profile(), extension1_.get()); | |
| 3298 EXPECT_EQ(3, model_->item_count()); | |
| 3299 { | |
| 3300 // Hide and show the app. | |
| 3301 v2_app_1.window()->Hide(); | |
| 3302 EXPECT_EQ(2, model_->item_count()); | |
| 3303 | |
| 3304 v2_app_1.window()->Show(extensions::AppWindow::SHOW_ACTIVE); | |
| 3305 EXPECT_EQ(3, model_->item_count()); | |
| 3306 } | |
| 3307 { | |
| 3308 // Switch user, hide and show the app and switch back. | |
| 3309 SwitchActiveUser(account_id2); | |
| 3310 EXPECT_EQ(2, model_->item_count()); | |
| 3311 | |
| 3312 v2_app_1.window()->Hide(); | |
| 3313 EXPECT_EQ(2, model_->item_count()); | |
| 3314 | |
| 3315 v2_app_1.window()->Show(extensions::AppWindow::SHOW_ACTIVE); | |
| 3316 EXPECT_EQ(2, model_->item_count()); | |
| 3317 | |
| 3318 SwitchActiveUser(account_id); | |
| 3319 EXPECT_EQ(3, model_->item_count()); | |
| 3320 } | |
| 3321 { | |
| 3322 // Switch user, hide the app, switch back and then show it again. | |
| 3323 SwitchActiveUser(account_id2); | |
| 3324 EXPECT_EQ(2, model_->item_count()); | |
| 3325 | |
| 3326 v2_app_1.window()->Hide(); | |
| 3327 EXPECT_EQ(2, model_->item_count()); | |
| 3328 | |
| 3329 SwitchActiveUser(account_id); | |
| 3330 // The following expectation does not work in current impl. It was working | |
| 3331 // before because MultiUserWindowManagerChromeOS is not attached to user | |
| 3332 // associated with profile() hence not actually handling windows for the | |
| 3333 // user. It is a real bug. See http://crbug.com/693634 | |
| 3334 // EXPECT_EQ(2, model_->item_count()); | |
| 3335 | |
| 3336 v2_app_1.window()->Show(extensions::AppWindow::SHOW_ACTIVE); | |
| 3337 EXPECT_EQ(3, model_->item_count()); | |
| 3338 } | |
| 3339 { | |
| 3340 // Create a second app, hide and show it and then hide both apps. | |
| 3341 V2App v2_app_2(profile(), extension1_.get()); | |
| 3342 EXPECT_EQ(3, model_->item_count()); | |
| 3343 | |
| 3344 v2_app_2.window()->Hide(); | |
| 3345 EXPECT_EQ(3, model_->item_count()); | |
| 3346 | |
| 3347 v2_app_2.window()->Show(extensions::AppWindow::SHOW_ACTIVE); | |
| 3348 EXPECT_EQ(3, model_->item_count()); | |
| 3349 | |
| 3350 v2_app_1.window()->Hide(); | |
| 3351 v2_app_2.window()->Hide(); | |
| 3352 EXPECT_EQ(2, model_->item_count()); | |
| 3353 } | |
| 3354 } | |
| 3355 | |
| 3356 // Checks that the generated menu list properly activates items. | |
| 3357 TEST_F(ChromeLauncherControllerImplTest, V1AppMenuExecution) { | |
| 3358 InitLauncherControllerWithBrowser(); | |
| 3359 | |
| 3360 // Add |extension3_| to the launcher and add two items. | |
| 3361 GURL gmail = GURL("https://mail.google.com/mail/u"); | |
| 3362 ash::ShelfID gmail_id = model_->next_id(); | |
| 3363 extension_service_->AddExtension(extension3_.get()); | |
| 3364 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); | |
| 3365 base::string16 title1 = ASCIIToUTF16("Test1"); | |
| 3366 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); | |
| 3367 chrome::NewTab(browser()); | |
| 3368 base::string16 title2 = ASCIIToUTF16("Test2"); | |
| 3369 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title2); | |
| 3370 | |
| 3371 // Check that the menu is properly set. | |
| 3372 ash::ShelfItem item_gmail; | |
| 3373 item_gmail.type = ash::TYPE_PINNED_APP; | |
| 3374 item_gmail.id = gmail_id; | |
| 3375 base::string16 two_menu_items[] = {title1, title2}; | |
| 3376 CheckAppMenu(launcher_controller_, item_gmail, 2, two_menu_items); | |
| 3377 ash::ShelfItemDelegate* item_delegate = | |
| 3378 model_->GetShelfItemDelegate(gmail_id); | |
| 3379 ASSERT_TRUE(item_delegate); | |
| 3380 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); | |
| 3381 // Execute the second item in the menu, after the title and two separators, | |
| 3382 // this shouldn't do anything since that item is already the active tab. | |
| 3383 { | |
| 3384 ash::ShelfApplicationMenuModel menu( | |
| 3385 base::string16(), | |
| 3386 launcher_controller_->GetAppMenuItemsForTesting(item_gmail), | |
| 3387 item_delegate); | |
| 3388 menu.ActivatedAt(4); | |
| 3389 } | |
| 3390 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); | |
| 3391 | |
| 3392 // Execute the first item in the menu, after the title and two separators, | |
| 3393 // this should activate the other tab. | |
| 3394 { | |
| 3395 ash::ShelfApplicationMenuModel menu( | |
| 3396 base::string16(), | |
| 3397 launcher_controller_->GetAppMenuItemsForTesting(item_gmail), | |
| 3398 item_delegate); | |
| 3399 menu.ActivatedAt(3); | |
| 3400 } | |
| 3401 EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); | |
| 3402 } | |
| 3403 | |
| 3404 // Checks that the generated menu list properly deletes items. | |
| 3405 TEST_F(ChromeLauncherControllerImplTest, V1AppMenuDeletionExecution) { | |
| 3406 InitLauncherControllerWithBrowser(); | |
| 3407 | |
| 3408 // Add |extension3_| to the launcher and add two items. | |
| 3409 GURL gmail = GURL("https://mail.google.com/mail/u"); | |
| 3410 ash::ShelfID gmail_id = model_->next_id(); | |
| 3411 extension_service_->AddExtension(extension3_.get()); | |
| 3412 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(gmail_url)); | |
| 3413 base::string16 title1 = ASCIIToUTF16("Test1"); | |
| 3414 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1); | |
| 3415 chrome::NewTab(browser()); | |
| 3416 base::string16 title2 = ASCIIToUTF16("Test2"); | |
| 3417 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title2); | |
| 3418 | |
| 3419 // Check that the menu is properly set. | |
| 3420 ash::ShelfItem item_gmail; | |
| 3421 item_gmail.type = ash::TYPE_PINNED_APP; | |
| 3422 item_gmail.id = gmail_id; | |
| 3423 base::string16 two_menu_items[] = {title1, title2}; | |
| 3424 CheckAppMenu(launcher_controller_, item_gmail, 2, two_menu_items); | |
| 3425 | |
| 3426 ash::ShelfItemDelegate* item_delegate = | |
| 3427 model_->GetShelfItemDelegate(gmail_id); | |
| 3428 ASSERT_TRUE(item_delegate); | |
| 3429 int tabs = browser()->tab_strip_model()->count(); | |
| 3430 // Activate the proper tab through the menu item. | |
| 3431 { | |
| 3432 ash::MenuItemList items = | |
| 3433 launcher_controller_->GetAppMenuItemsForTesting(item_gmail); | |
| 3434 item_delegate->ExecuteCommand(items[1]->command_id, ui::EF_NONE); | |
| 3435 EXPECT_EQ(tabs, browser()->tab_strip_model()->count()); | |
| 3436 } | |
| 3437 | |
| 3438 // Delete one tab through the menu item. | |
| 3439 { | |
| 3440 ash::MenuItemList items = | |
| 3441 launcher_controller_->GetAppMenuItemsForTesting(item_gmail); | |
| 3442 item_delegate->ExecuteCommand(items[1]->command_id, ui::EF_SHIFT_DOWN); | |
| 3443 EXPECT_EQ(--tabs, browser()->tab_strip_model()->count()); | |
| 3444 } | |
| 3445 } | |
| 3446 | |
| 3447 // Tests that panels create launcher items correctly | |
| 3448 TEST_F(ChromeLauncherControllerImplTest, AppPanels) { | |
| 3449 InitLauncherController(); | |
| 3450 model_observer_->clear_counts(); | |
| 3451 const std::string app_id = extension1_->id(); | |
| 3452 | |
| 3453 // app_icon_loader is owned by ChromeLauncherControllerImpl. | |
| 3454 TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl(); | |
| 3455 app_icon_loader->AddSupportedApp(app_id); | |
| 3456 SetAppIconLoader(std::unique_ptr<AppIconLoader>(app_icon_loader)); | |
| 3457 | |
| 3458 // Make an app panel; the ShelfItem is added by ash::ShelfWindowWatcher. | |
| 3459 std::unique_ptr<V2App> app_panel1 = base::MakeUnique<V2App>( | |
| 3460 profile(), extension1_.get(), extensions::AppWindow::WINDOW_TYPE_PANEL); | |
| 3461 EXPECT_TRUE(app_panel1->window()->GetNativeWindow()->IsVisible()); | |
| 3462 int panel_index = model_observer_->last_index(); | |
| 3463 EXPECT_EQ(1, model_observer_->added()); | |
| 3464 EXPECT_EQ(1, app_icon_loader->fetch_count()); | |
| 3465 model_observer_->clear_counts(); | |
| 3466 | |
| 3467 // App panels should have a separate identifier than the app id | |
| 3468 EXPECT_EQ(ash::kInvalidShelfID, | |
| 3469 launcher_controller_->GetShelfIDForAppID(app_id)); | |
| 3470 | |
| 3471 // Setting the app image should not change the panel, which has a window icon. | |
| 3472 gfx::ImageSkia image; | |
| 3473 launcher_controller_->OnAppImageUpdated(app_id, image); | |
| 3474 EXPECT_EQ(0, model_observer_->changed()); | |
| 3475 model_observer_->clear_counts(); | |
| 3476 | |
| 3477 // Make a second app panel and verify that it gets the same index as the first | |
| 3478 // panel, being added to the left of the existing panel. | |
| 3479 std::unique_ptr<V2App> app_panel2 = base::MakeUnique<V2App>( | |
| 3480 profile(), extension2_.get(), extensions::AppWindow::WINDOW_TYPE_PANEL); | |
| 3481 EXPECT_EQ(panel_index, model_observer_->last_index()); | |
| 3482 EXPECT_EQ(1, model_observer_->added()); | |
| 3483 model_observer_->clear_counts(); | |
| 3484 | |
| 3485 app_panel1.reset(); | |
| 3486 app_panel2.reset(); | |
| 3487 EXPECT_EQ(2, model_observer_->removed()); | |
| 3488 } | |
| 3489 | |
| 3490 // Tests that the Gmail extension matches more than the app itself claims with | |
| 3491 // the manifest file. | |
| 3492 TEST_F(ChromeLauncherControllerImplTest, GmailMatching) { | |
| 3493 InitLauncherControllerWithBrowser(); | |
| 3494 | |
| 3495 // Create a Gmail browser tab. | |
| 3496 chrome::NewTab(browser()); | |
| 3497 base::string16 title = ASCIIToUTF16("Test"); | |
| 3498 NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title); | |
| 3499 content::WebContents* content = | |
| 3500 browser()->tab_strip_model()->GetActiveWebContents(); | |
| 3501 | |
| 3502 // Check that the launcher controller does not recognize the running app. | |
| 3503 EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content)); | |
| 3504 | |
| 3505 // Installing |extension3_| adds it to the launcher. | |
| 3506 ash::ShelfID gmail_id = model_->next_id(); | |
| 3507 extension_service_->AddExtension(extension3_.get()); | |
| 3508 EXPECT_EQ(3, model_->item_count()); | |
| 3509 int gmail_index = model_->ItemIndexByID(gmail_id); | |
| 3510 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[gmail_index].type); | |
| 3511 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 3512 | |
| 3513 // Check that it is now handled. | |
| 3514 EXPECT_TRUE(launcher_controller_->ContentCanBeHandledByGmailApp(content)); | |
| 3515 | |
| 3516 // Check also that the app has detected that properly. | |
| 3517 ash::ShelfItem item_gmail; | |
| 3518 item_gmail.type = ash::TYPE_PINNED_APP; | |
| 3519 item_gmail.id = gmail_id; | |
| 3520 EXPECT_EQ(1U, | |
| 3521 launcher_controller_->GetAppMenuItemsForTesting(item_gmail).size()); | |
| 3522 } | |
| 3523 | |
| 3524 // Tests that the Gmail extension does not match the offline verison. | |
| 3525 TEST_F(ChromeLauncherControllerImplTest, GmailOfflineMatching) { | |
| 3526 InitLauncherControllerWithBrowser(); | |
| 3527 | |
| 3528 // Create a Gmail browser tab. | |
| 3529 chrome::NewTab(browser()); | |
| 3530 base::string16 title = ASCIIToUTF16("Test"); | |
| 3531 NavigateAndCommitActiveTabWithTitle(browser(), | |
| 3532 GURL(offline_gmail_url), | |
| 3533 title); | |
| 3534 content::WebContents* content = | |
| 3535 browser()->tab_strip_model()->GetActiveWebContents(); | |
| 3536 | |
| 3537 // Installing |extension3_| adds it to the launcher. | |
| 3538 ash::ShelfID gmail_id = model_->next_id(); | |
| 3539 extension_service_->AddExtension(extension3_.get()); | |
| 3540 EXPECT_EQ(3, model_->item_count()); | |
| 3541 int gmail_index = model_->ItemIndexByID(gmail_id); | |
| 3542 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[gmail_index].type); | |
| 3543 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension3_->id())); | |
| 3544 | |
| 3545 // The content should not be able to be handled by the app. | |
| 3546 EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content)); | |
| 3547 } | |
| 3548 | |
| 3549 // Verify that the launcher item positions are persisted and restored. | |
| 3550 TEST_F(ChromeLauncherControllerImplTest, PersistLauncherItemPositions) { | |
| 3551 InitLauncherController(); | |
| 3552 | |
| 3553 TestLauncherControllerHelper* helper = new TestLauncherControllerHelper; | |
| 3554 SetLauncherControllerHelper(helper); | |
| 3555 | |
| 3556 EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[0].type); | |
| 3557 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[1].type); | |
| 3558 | |
| 3559 TabStripModel* tab_strip_model = browser()->tab_strip_model(); | |
| 3560 EXPECT_EQ(0, tab_strip_model->count()); | |
| 3561 chrome::NewTab(browser()); | |
| 3562 chrome::NewTab(browser()); | |
| 3563 EXPECT_EQ(2, tab_strip_model->count()); | |
| 3564 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); | |
| 3565 helper->SetAppID(tab_strip_model->GetWebContentsAt(1), "2"); | |
| 3566 | |
| 3567 EXPECT_FALSE(launcher_controller_->IsAppPinned("1")); | |
| 3568 launcher_controller_->PinAppWithID("1"); | |
| 3569 EXPECT_TRUE(launcher_controller_->IsAppPinned("1")); | |
| 3570 launcher_controller_->PinAppWithID("2"); | |
| 3571 | |
| 3572 EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[0].type); | |
| 3573 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[1].type); | |
| 3574 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 3575 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[3].type); | |
| 3576 | |
| 3577 // Move browser shortcut item from index 1 to index 3. | |
| 3578 model_->Move(1, 3); | |
| 3579 EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[0].type); | |
| 3580 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type); | |
| 3581 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 3582 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[3].type); | |
| 3583 | |
| 3584 RecreateLauncherController(); | |
| 3585 helper = new TestLauncherControllerHelper(profile()); | |
| 3586 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); | |
| 3587 helper->SetAppID(tab_strip_model->GetWebContentsAt(1), "2"); | |
| 3588 SetLauncherControllerHelper(helper); | |
| 3589 launcher_controller_->Init(); | |
| 3590 | |
| 3591 // Check ShelfItems are restored after resetting ChromeLauncherControllerImpl. | |
| 3592 EXPECT_EQ(ash::TYPE_APP_LIST, model_->items()[0].type); | |
| 3593 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type); | |
| 3594 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type); | |
| 3595 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[3].type); | |
| 3596 } | |
| 3597 | |
| 3598 // Verifies pinned apps are persisted and restored. | |
| 3599 TEST_F(ChromeLauncherControllerImplTest, PersistPinned) { | |
| 3600 InitLauncherControllerWithBrowser(); | |
| 3601 size_t initial_size = model_->items().size(); | |
| 3602 | |
| 3603 TabStripModel* tab_strip_model = browser()->tab_strip_model(); | |
| 3604 EXPECT_EQ(1, tab_strip_model->count()); | |
| 3605 | |
| 3606 TestLauncherControllerHelper* helper = new TestLauncherControllerHelper; | |
| 3607 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); | |
| 3608 SetLauncherControllerHelper(helper); | |
| 3609 | |
| 3610 // app_icon_loader is owned by ChromeLauncherControllerImpl. | |
| 3611 TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl; | |
| 3612 app_icon_loader->AddSupportedApp("1"); | |
| 3613 SetAppIconLoader(std::unique_ptr<AppIconLoader>(app_icon_loader)); | |
| 3614 EXPECT_EQ(0, app_icon_loader->fetch_count()); | |
| 3615 | |
| 3616 launcher_controller_->PinAppWithID("1"); | |
| 3617 ash::ShelfID id = launcher_controller_->GetShelfIDForAppID("1"); | |
| 3618 int app_index = model_->ItemIndexByID(id); | |
| 3619 EXPECT_EQ(1, app_icon_loader->fetch_count()); | |
| 3620 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[app_index].type); | |
| 3621 EXPECT_TRUE(launcher_controller_->IsAppPinned("1")); | |
| 3622 EXPECT_FALSE(launcher_controller_->IsAppPinned("0")); | |
| 3623 EXPECT_EQ(initial_size + 1, model_->items().size()); | |
| 3624 | |
| 3625 RecreateLauncherController(); | |
| 3626 helper = new TestLauncherControllerHelper(profile()); | |
| 3627 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); | |
| 3628 SetLauncherControllerHelper(helper); | |
| 3629 // app_icon_loader is owned by ChromeLauncherControllerImpl. | |
| 3630 app_icon_loader = new TestAppIconLoaderImpl; | |
| 3631 app_icon_loader->AddSupportedApp("1"); | |
| 3632 SetAppIconLoader(std::unique_ptr<AppIconLoader>(app_icon_loader)); | |
| 3633 launcher_controller_->Init(); | |
| 3634 | |
| 3635 EXPECT_EQ(1, app_icon_loader->fetch_count()); | |
| 3636 ASSERT_EQ(initial_size + 1, model_->items().size()); | |
| 3637 EXPECT_TRUE(launcher_controller_->IsAppPinned("1")); | |
| 3638 EXPECT_FALSE(launcher_controller_->IsAppPinned("0")); | |
| 3639 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[app_index].type); | |
| 3640 | |
| 3641 launcher_controller_->UnpinAppWithID("1"); | |
| 3642 ASSERT_EQ(initial_size, model_->items().size()); | |
| 3643 } | |
| 3644 | |
| 3645 TEST_F(ChromeLauncherControllerImplTest, MultipleAppIconLoaders) { | |
| 3646 InitLauncherControllerWithBrowser(); | |
| 3647 | |
| 3648 const std::string app_id1 = extension1_->id(); | |
| 3649 const std::string app_id2 = extension2_->id(); | |
| 3650 const std::string app_id3 = extension3_->id(); | |
| 3651 // app_icon_loader1 and app_icon_loader2 are owned by | |
| 3652 // ChromeLauncherControllerImpl. | |
| 3653 TestAppIconLoaderImpl* app_icon_loader1 = new TestAppIconLoaderImpl(); | |
| 3654 TestAppIconLoaderImpl* app_icon_loader2 = new TestAppIconLoaderImpl(); | |
| 3655 app_icon_loader1->AddSupportedApp(app_id1); | |
| 3656 app_icon_loader2->AddSupportedApp(app_id2); | |
| 3657 SetAppIconLoaders(std::unique_ptr<AppIconLoader>(app_icon_loader1), | |
| 3658 std::unique_ptr<AppIconLoader>(app_icon_loader2)); | |
| 3659 | |
| 3660 const ash::ShelfID shelfId3 = launcher_controller_->CreateAppLauncherItem( | |
| 3661 base::MakeUnique<ExtensionAppWindowLauncherItemController>( | |
| 3662 ash::AppLaunchId(app_id3)), | |
| 3663 ash::STATUS_RUNNING); | |
| 3664 EXPECT_EQ(0, app_icon_loader1->fetch_count()); | |
| 3665 EXPECT_EQ(0, app_icon_loader1->clear_count()); | |
| 3666 EXPECT_EQ(0, app_icon_loader2->fetch_count()); | |
| 3667 EXPECT_EQ(0, app_icon_loader2->clear_count()); | |
| 3668 | |
| 3669 const ash::ShelfID shelfId2 = launcher_controller_->CreateAppLauncherItem( | |
| 3670 base::MakeUnique<ExtensionAppWindowLauncherItemController>( | |
| 3671 ash::AppLaunchId(app_id2)), | |
| 3672 ash::STATUS_RUNNING); | |
| 3673 EXPECT_EQ(0, app_icon_loader1->fetch_count()); | |
| 3674 EXPECT_EQ(0, app_icon_loader1->clear_count()); | |
| 3675 EXPECT_EQ(1, app_icon_loader2->fetch_count()); | |
| 3676 EXPECT_EQ(0, app_icon_loader2->clear_count()); | |
| 3677 | |
| 3678 const ash::ShelfID shelfId1 = launcher_controller_->CreateAppLauncherItem( | |
| 3679 base::MakeUnique<ExtensionAppWindowLauncherItemController>( | |
| 3680 ash::AppLaunchId(app_id1)), | |
| 3681 ash::STATUS_RUNNING); | |
| 3682 EXPECT_EQ(1, app_icon_loader1->fetch_count()); | |
| 3683 EXPECT_EQ(0, app_icon_loader1->clear_count()); | |
| 3684 EXPECT_EQ(1, app_icon_loader2->fetch_count()); | |
| 3685 EXPECT_EQ(0, app_icon_loader2->clear_count()); | |
| 3686 | |
| 3687 launcher_controller_->CloseLauncherItem(shelfId1); | |
| 3688 EXPECT_EQ(1, app_icon_loader1->fetch_count()); | |
| 3689 EXPECT_EQ(1, app_icon_loader1->clear_count()); | |
| 3690 EXPECT_EQ(1, app_icon_loader2->fetch_count()); | |
| 3691 EXPECT_EQ(0, app_icon_loader2->clear_count()); | |
| 3692 | |
| 3693 launcher_controller_->CloseLauncherItem(shelfId2); | |
| 3694 EXPECT_EQ(1, app_icon_loader1->fetch_count()); | |
| 3695 EXPECT_EQ(1, app_icon_loader1->clear_count()); | |
| 3696 EXPECT_EQ(1, app_icon_loader2->fetch_count()); | |
| 3697 EXPECT_EQ(1, app_icon_loader2->clear_count()); | |
| 3698 | |
| 3699 launcher_controller_->CloseLauncherItem(shelfId3); | |
| 3700 EXPECT_EQ(1, app_icon_loader1->fetch_count()); | |
| 3701 EXPECT_EQ(1, app_icon_loader1->clear_count()); | |
| 3702 EXPECT_EQ(1, app_icon_loader2->fetch_count()); | |
| 3703 EXPECT_EQ(1, app_icon_loader2->clear_count()); | |
| 3704 } | |
| 3705 | |
| 3706 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcAppPinPolicy) { | |
| 3707 InitLauncherControllerWithBrowser(); | |
| 3708 arc::mojom::AppInfo appinfo = CreateAppInfo( | |
| 3709 "Some App", "SomeActivity", "com.example.app", OrientationLock::NONE); | |
| 3710 const std::string app_id = AddArcAppAndShortcut(appinfo); | |
| 3711 | |
| 3712 // Set policy, that makes pins ARC app. Unlike native extension, for ARC app | |
| 3713 // package_name (not hash) specified as id. In this test we check that | |
| 3714 // by hash we can determine that appropriate package was set by policy. | |
| 3715 base::ListValue policy_value; | |
| 3716 InsertPrefValue(&policy_value, 0, appinfo.package_name); | |
| 3717 profile()->GetTestingPrefService()->SetManagedPref( | |
| 3718 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy()); | |
| 3719 | |
| 3720 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id)); | |
| 3721 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED, | |
| 3722 GetPinnableForAppID(app_id, profile())); | |
| 3723 } | |
| 3724 | |
| 3725 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcManaged) { | |
| 3726 // TODO(victorhsieh): Implement opt-in and opt-out. | |
| 3727 if (arc::ShouldArcAlwaysStart()) | |
| 3728 return; | |
| 3729 | |
| 3730 extension_service_->AddExtension(arc_support_host_.get()); | |
| 3731 // Test enables ARC, so turn it off for initial values. | |
| 3732 EnablePlayStore(false); | |
| 3733 | |
| 3734 InitLauncherController(); | |
| 3735 | |
| 3736 // To prevent import legacy pins each time. | |
| 3737 // Initially pins are imported from legacy pref based model. | |
| 3738 StartPrefSyncService(syncer::SyncDataList()); | |
| 3739 | |
| 3740 // Initial run, ARC is not managed and disabled, Play Store pin should be | |
| 3741 // available. | |
| 3742 ValidateArcState(false, false, arc::ArcSessionManager::State::STOPPED, | |
| 3743 "AppList, Chrome, Play Store"); | |
| 3744 | |
| 3745 // ARC is managed and enabled, Play Store pin should be available. | |
| 3746 // Note: NEGOTIATING_TERMS_OF_SERVICE here means that opt-in flow starts. | |
| 3747 profile()->GetTestingPrefService()->SetManagedPref( | |
| 3748 prefs::kArcEnabled, base::MakeUnique<base::Value>(true)); | |
| 3749 base::RunLoop().RunUntilIdle(); | |
| 3750 ValidateArcState(true, true, | |
| 3751 arc::ArcSessionManager::State::NEGOTIATING_TERMS_OF_SERVICE, | |
| 3752 "AppList, Chrome, Play Store"); | |
| 3753 | |
| 3754 // ARC is managed and disabled, Play Store pin should not be available. | |
| 3755 profile()->GetTestingPrefService()->SetManagedPref( | |
| 3756 prefs::kArcEnabled, base::MakeUnique<base::Value>(false)); | |
| 3757 base::RunLoop().RunUntilIdle(); | |
| 3758 ValidateArcState(false, true, arc::ArcSessionManager::State::STOPPED, | |
| 3759 "AppList, Chrome"); | |
| 3760 | |
| 3761 // ARC is not managed and disabled, Play Store pin should be available. | |
| 3762 profile()->GetTestingPrefService()->RemoveManagedPref(prefs::kArcEnabled); | |
| 3763 base::RunLoop().RunUntilIdle(); | |
| 3764 ValidateArcState(false, false, arc::ArcSessionManager::State::STOPPED, | |
| 3765 "AppList, Chrome, Play Store"); | |
| 3766 | |
| 3767 // ARC is not managed and enabled, Play Store pin should be available. | |
| 3768 EnablePlayStore(true); | |
| 3769 ValidateArcState(true, false, | |
| 3770 arc::ArcSessionManager::State::NEGOTIATING_TERMS_OF_SERVICE, | |
| 3771 "AppList, Chrome, Play Store"); | |
| 3772 | |
| 3773 // User disables ARC. ARC is not managed and disabled, Play Store pin should | |
| 3774 // be automatically removed. | |
| 3775 EnablePlayStore(false); | |
| 3776 ValidateArcState(false, false, arc::ArcSessionManager::State::STOPPED, | |
| 3777 "AppList, Chrome"); | |
| 3778 | |
| 3779 // Even if re-enable it again, Play Store pin does not appear automatically. | |
| 3780 EnablePlayStore(true); | |
| 3781 ValidateArcState(true, false, | |
| 3782 arc::ArcSessionManager::State::NEGOTIATING_TERMS_OF_SERVICE, | |
| 3783 "AppList, Chrome"); | |
| 3784 } | |
| 3785 | |
| 3786 // Test the application menu of a shelf item with multiple ARC windows. | |
| 3787 TEST_P(ChromeLauncherControllerImplWithArcTest, ShelfItemWithMultipleWindows) { | |
| 3788 InitLauncherControllerWithBrowser(); | |
| 3789 | |
| 3790 arc::mojom::AppInfo appinfo = | |
| 3791 CreateAppInfo("Test1", "test", "com.example.app", OrientationLock::NONE); | |
| 3792 AddArcAppAndShortcut(appinfo); | |
| 3793 | |
| 3794 // Widgets will be deleted by the system. | |
| 3795 NotifyOnTaskCreated(appinfo, 1 /* task_id */); | |
| 3796 views::Widget* window1 = CreateArcWindow("org.chromium.arc.1"); | |
| 3797 ASSERT_TRUE(window1); | |
| 3798 EXPECT_TRUE(window1->IsActive()); | |
| 3799 | |
| 3800 NotifyOnTaskCreated(appinfo, 2 /* task_id */); | |
| 3801 views::Widget* window2 = CreateArcWindow("org.chromium.arc.2"); | |
| 3802 ASSERT_TRUE(window2); | |
| 3803 | |
| 3804 EXPECT_FALSE(window1->IsActive()); | |
| 3805 EXPECT_TRUE(window2->IsActive()); | |
| 3806 | |
| 3807 const std::string app_id = ArcAppTest::GetAppId(appinfo); | |
| 3808 | |
| 3809 const ash::ShelfID shelf_id = | |
| 3810 launcher_controller_->GetShelfIDForAppID(app_id); | |
| 3811 ash::ShelfItemDelegate* item_delegate = | |
| 3812 model_->GetShelfItemDelegate(shelf_id); | |
| 3813 ASSERT_TRUE(item_delegate); | |
| 3814 | |
| 3815 // Selecting the item will show its application menu. It does not change the | |
| 3816 // active window. | |
| 3817 SelectItem(item_delegate); | |
| 3818 EXPECT_FALSE(window1->IsActive()); | |
| 3819 EXPECT_TRUE(window2->IsActive()); | |
| 3820 | |
| 3821 // Command ids are just app window indices. Note, apps are registered in | |
| 3822 // opposite order. Last created goes in front. | |
| 3823 ash::MenuItemList items = item_delegate->GetAppMenuItems(0); | |
| 3824 ASSERT_EQ(items.size(), 2U); | |
| 3825 EXPECT_EQ(items[0]->command_id, 0U); | |
| 3826 EXPECT_EQ(items[1]->command_id, 1U); | |
| 3827 | |
| 3828 // Execute command to activate first window. | |
| 3829 item_delegate->ExecuteCommand(items[1]->command_id, 0); | |
| 3830 EXPECT_TRUE(window1->IsActive()); | |
| 3831 EXPECT_FALSE(window2->IsActive()); | |
| 3832 | |
| 3833 // Selecting the item will show its application menu. It does not change the | |
| 3834 // active window. | |
| 3835 SelectItem(item_delegate); | |
| 3836 EXPECT_TRUE(window1->IsActive()); | |
| 3837 EXPECT_FALSE(window2->IsActive()); | |
| 3838 | |
| 3839 // Execute command to activate second window. | |
| 3840 item_delegate->ExecuteCommand(items[0]->command_id, 0); | |
| 3841 EXPECT_FALSE(window1->IsActive()); | |
| 3842 EXPECT_TRUE(window2->IsActive()); | |
| 3843 } | |
| 3844 | |
| 3845 namespace { | |
| 3846 | |
| 3847 class ChromeLauncherControllerOrientationTest | |
| 3848 : public ChromeLauncherControllerImplWithArcTest { | |
| 3849 public: | |
| 3850 ChromeLauncherControllerOrientationTest() {} | |
| 3851 ~ChromeLauncherControllerOrientationTest() override {} | |
| 3852 | |
| 3853 protected: | |
| 3854 void InitApps() { | |
| 3855 appinfo_none_ = | |
| 3856 CreateAppInfo("None", "None", "com.example.app", OrientationLock::NONE); | |
| 3857 appinfo_landscape_ = | |
| 3858 CreateAppInfo("Landscape", "Landscape", "com.example.app", | |
| 3859 OrientationLock::LANDSCAPE); | |
| 3860 appinfo_portrait_ = CreateAppInfo("Portrait", "Portrait", "com.example.app", | |
| 3861 OrientationLock::PORTRAIT); | |
| 3862 appinfo_current_ = CreateAppInfo( | |
| 3863 "LockCurrent", "current", "com.example.app", OrientationLock::CURRENT); | |
| 3864 | |
| 3865 AddArcAppAndShortcut(appinfo_none_); | |
| 3866 AddArcAppAndShortcut(appinfo_landscape_); | |
| 3867 AddArcAppAndShortcut(appinfo_portrait_); | |
| 3868 AddArcAppAndShortcut(appinfo_current_); | |
| 3869 | |
| 3870 ash::ScreenOrientationController* controller = | |
| 3871 ash::Shell::Get()->screen_orientation_controller(); | |
| 3872 | |
| 3873 // Creating a window with NONE orientation will not lock the screen. | |
| 3874 window_none_ = CreateArcWindow(window_app_id_none_); | |
| 3875 NotifyOnTaskCreated(appinfo_none_, task_id_none_); | |
| 3876 EXPECT_FALSE(controller->rotation_locked()); | |
| 3877 EXPECT_EQ(display::Display::ROTATE_0, | |
| 3878 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 3879 | |
| 3880 // Create a arc window with PORTRAIT orientation locks the screen to 90. | |
| 3881 window_portrait_ = CreateArcWindow(window_app_id_portrait_); | |
| 3882 NotifyOnTaskCreated(appinfo_portrait_, task_id_portrait_); | |
| 3883 EXPECT_TRUE(controller->rotation_locked()); | |
| 3884 EXPECT_EQ(display::Display::ROTATE_90, | |
| 3885 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 3886 | |
| 3887 // Create a arc window with LANDSCAPE orientation locks the screen to 0. | |
| 3888 window_landscape_ = CreateArcWindow(window_app_id_landscape_); | |
| 3889 NotifyOnTaskCreated(appinfo_landscape_, task_id_landscape_); | |
| 3890 EXPECT_TRUE(controller->rotation_locked()); | |
| 3891 EXPECT_EQ(display::Display::ROTATE_0, | |
| 3892 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 3893 } | |
| 3894 | |
| 3895 int32_t task_id_none_ = 1; | |
| 3896 int32_t task_id_landscape_ = 2; | |
| 3897 int32_t task_id_portrait_ = 3; | |
| 3898 int32_t task_id_current_ = 4; | |
| 3899 | |
| 3900 // This needs to be kept on the instance because window's property has | |
| 3901 // refeference to this. | |
| 3902 std::string window_app_id_none_ = {"org.chromium.arc.1"}; | |
| 3903 std::string window_app_id_landscape_ = {"org.chromium.arc.2"}; | |
| 3904 std::string window_app_id_portrait_ = {"org.chromium.arc.3"}; | |
| 3905 std::string window_app_id_current_ = {"org.chromium.arc.4"}; | |
| 3906 | |
| 3907 arc::mojom::AppInfo appinfo_none_; | |
| 3908 arc::mojom::AppInfo appinfo_landscape_; | |
| 3909 arc::mojom::AppInfo appinfo_portrait_; | |
| 3910 arc::mojom::AppInfo appinfo_current_; | |
| 3911 | |
| 3912 views::Widget* window_none_ = nullptr; | |
| 3913 views::Widget* window_landscape_ = nullptr; | |
| 3914 views::Widget* window_portrait_ = nullptr; | |
| 3915 views::Widget* window_current_ = nullptr; | |
| 3916 | |
| 3917 private: | |
| 3918 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerOrientationTest); | |
| 3919 }; | |
| 3920 | |
| 3921 INSTANTIATE_TEST_CASE_P(, | |
| 3922 ChromeLauncherControllerOrientationTest, | |
| 3923 ::testing::Bool()); | |
| 3924 | |
| 3925 class ChromeLauncherControllerArcDefaultAppsTest | |
| 3926 : public ChromeLauncherControllerImplTest, | |
| 3927 public ::testing::WithParamInterface<bool> { | |
| 3928 public: | |
| 3929 ChromeLauncherControllerArcDefaultAppsTest() {} | |
| 3930 ~ChromeLauncherControllerArcDefaultAppsTest() override {} | |
| 3931 | |
| 3932 protected: | |
| 3933 void SetUp() override { | |
| 3934 if (GetParam()) | |
| 3935 arc::SetArcAlwaysStartForTesting(); | |
| 3936 ArcDefaultAppList::UseTestAppsDirectory(); | |
| 3937 ChromeLauncherControllerImplTest::SetUp(); | |
| 3938 } | |
| 3939 | |
| 3940 private: | |
| 3941 DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerArcDefaultAppsTest); | |
| 3942 }; | |
| 3943 | |
| 3944 INSTANTIATE_TEST_CASE_P(, | |
| 3945 ChromeLauncherControllerArcDefaultAppsTest, | |
| 3946 ::testing::Bool()); | |
| 3947 | |
| 3948 } // namespace | |
| 3949 | |
| 3950 TEST_P(ChromeLauncherControllerOrientationTest, | |
| 3951 ArcOrientationLockBeforeWindowReady) { | |
| 3952 ASSERT_TRUE(display::Display::HasInternalDisplay()); | |
| 3953 | |
| 3954 extension_service_->AddExtension(arc_support_host_.get()); | |
| 3955 EnablePlayStore(true); | |
| 3956 | |
| 3957 InitLauncherController(); | |
| 3958 | |
| 3959 ash::ScreenOrientationController* controller = | |
| 3960 ash::Shell::Get()->screen_orientation_controller(); | |
| 3961 | |
| 3962 std::string app_id1("org.chromium.arc.1"); | |
| 3963 int task_id1 = 1; | |
| 3964 arc::mojom::AppInfo appinfo1 = | |
| 3965 CreateAppInfo("Test1", "test", "com.example.app", OrientationLock::NONE); | |
| 3966 | |
| 3967 AddArcAppAndShortcut(appinfo1); | |
| 3968 NotifyOnTaskCreated(appinfo1, task_id1); | |
| 3969 NotifyOnTaskOrientationLockRequested(task_id1, OrientationLock::PORTRAIT); | |
| 3970 | |
| 3971 // Widgets will be deleted by the system. | |
| 3972 CreateArcWindow(app_id1); | |
| 3973 | |
| 3974 EXPECT_FALSE(controller->rotation_locked()); | |
| 3975 | |
| 3976 EnableTabletMode(true); | |
| 3977 EXPECT_TRUE(controller->rotation_locked()); | |
| 3978 EXPECT_EQ(display::Display::ROTATE_90, | |
| 3979 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 3980 | |
| 3981 std::string app_id2("org.chromium.arc.2"); | |
| 3982 int task_id2 = 2; | |
| 3983 arc::mojom::AppInfo appinfo2 = | |
| 3984 CreateAppInfo("Test2", "test", "com.example.app", OrientationLock::NONE); | |
| 3985 // Create in tablet mode. | |
| 3986 AddArcAppAndShortcut(appinfo2); | |
| 3987 NotifyOnTaskCreated(appinfo2, task_id2); | |
| 3988 NotifyOnTaskOrientationLockRequested(task_id2, OrientationLock::LANDSCAPE); | |
| 3989 EXPECT_TRUE(controller->rotation_locked()); | |
| 3990 EXPECT_EQ(display::Display::ROTATE_90, | |
| 3991 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 3992 | |
| 3993 // The screen will be locked when the window is created. | |
| 3994 CreateArcWindow(app_id2); | |
| 3995 EXPECT_TRUE(controller->rotation_locked()); | |
| 3996 EXPECT_EQ(display::Display::ROTATE_0, | |
| 3997 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 3998 } | |
| 3999 | |
| 4000 TEST_P(ChromeLauncherControllerOrientationTest, ArcOrientationLock) { | |
| 4001 ASSERT_TRUE(display::Display::HasInternalDisplay()); | |
| 4002 | |
| 4003 extension_service_->AddExtension(arc_support_host_.get()); | |
| 4004 EnablePlayStore(true); | |
| 4005 EnableTabletMode(true); | |
| 4006 | |
| 4007 InitLauncherController(); | |
| 4008 | |
| 4009 InitApps(); | |
| 4010 ash::ScreenOrientationController* controller = | |
| 4011 ash::Shell::Get()->screen_orientation_controller(); | |
| 4012 | |
| 4013 // Activating a window with NON orientation unlocks the screen. | |
| 4014 window_none_->Activate(); | |
| 4015 EXPECT_FALSE(controller->rotation_locked()); | |
| 4016 EXPECT_EQ(display::Display::ROTATE_0, | |
| 4017 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4018 | |
| 4019 // Activating a window with PORTRAIT orientation locks the screen to 90. | |
| 4020 window_portrait_->Activate(); | |
| 4021 EXPECT_TRUE(controller->rotation_locked()); | |
| 4022 EXPECT_EQ(display::Display::ROTATE_90, | |
| 4023 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4024 | |
| 4025 // Disable Tablet mode, and make sure the screen is unlocked. | |
| 4026 EnableTabletMode(false); | |
| 4027 EXPECT_FALSE(controller->rotation_locked()); | |
| 4028 EXPECT_EQ(display::Display::ROTATE_0, | |
| 4029 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4030 | |
| 4031 // Re-enable Tablet mode, and make sure the screen is locked to 90. | |
| 4032 EnableTabletMode(true); | |
| 4033 EXPECT_TRUE(controller->rotation_locked()); | |
| 4034 EXPECT_EQ(display::Display::ROTATE_90, | |
| 4035 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4036 | |
| 4037 window_portrait_->Activate(); | |
| 4038 EXPECT_TRUE(controller->rotation_locked()); | |
| 4039 EXPECT_EQ(display::Display::ROTATE_90, | |
| 4040 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4041 | |
| 4042 window_landscape_->Activate(); | |
| 4043 EXPECT_TRUE(controller->rotation_locked()); | |
| 4044 EXPECT_EQ(display::Display::ROTATE_0, | |
| 4045 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4046 | |
| 4047 // OnTaskOrientationLockRequested can overwrite the current lock. | |
| 4048 NotifyOnTaskOrientationLockRequested(task_id_landscape_, | |
| 4049 OrientationLock::NONE); | |
| 4050 EXPECT_FALSE(controller->rotation_locked()); | |
| 4051 EXPECT_EQ(display::Display::ROTATE_0, | |
| 4052 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4053 | |
| 4054 NotifyOnTaskOrientationLockRequested(task_id_landscape_, | |
| 4055 OrientationLock::PORTRAIT); | |
| 4056 EXPECT_TRUE(controller->rotation_locked()); | |
| 4057 EXPECT_EQ(display::Display::ROTATE_90, | |
| 4058 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4059 | |
| 4060 // Non active window won't change the lock. | |
| 4061 NotifyOnTaskOrientationLockRequested(task_id_none_, | |
| 4062 OrientationLock::LANDSCAPE); | |
| 4063 EXPECT_TRUE(controller->rotation_locked()); | |
| 4064 EXPECT_EQ(display::Display::ROTATE_90, | |
| 4065 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4066 | |
| 4067 // But activating it will change the locked orinetation. | |
| 4068 window_none_->Activate(); | |
| 4069 EXPECT_TRUE(controller->rotation_locked()); | |
| 4070 EXPECT_EQ(display::Display::ROTATE_0, | |
| 4071 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4072 | |
| 4073 // OnTaskOrientationLockRequested will not lock the screen in non Tablet mode. | |
| 4074 EnableTabletMode(false); | |
| 4075 EXPECT_FALSE(controller->rotation_locked()); | |
| 4076 EXPECT_EQ(display::Display::ROTATE_0, | |
| 4077 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4078 | |
| 4079 NotifyOnTaskOrientationLockRequested(task_id_none_, | |
| 4080 OrientationLock::PORTRAIT); | |
| 4081 EXPECT_FALSE(controller->rotation_locked()); | |
| 4082 EXPECT_EQ(display::Display::ROTATE_0, | |
| 4083 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4084 | |
| 4085 // But it remembers the orientation lock and use it when Tablet mode is | |
| 4086 // enabled. | |
| 4087 EnableTabletMode(true); | |
| 4088 EXPECT_TRUE(controller->rotation_locked()); | |
| 4089 EXPECT_EQ(display::Display::ROTATE_90, | |
| 4090 display::Screen::GetScreen()->GetPrimaryDisplay().rotation()); | |
| 4091 | |
| 4092 // Manually unlock first. | |
| 4093 NotifyOnTaskOrientationLockRequested(task_id_none_, OrientationLock::NONE); | |
| 4094 EXPECT_FALSE(controller->rotation_locked()); | |
| 4095 } | |
| 4096 | |
| 4097 TEST_P(ChromeLauncherControllerArcDefaultAppsTest, DefaultApps) { | |
| 4098 arc_test_.SetUp(profile()); | |
| 4099 InitLauncherController(); | |
| 4100 // TODO(crbug.com/709297): Fix this workaround to prevent a TearDown crash. | |
| 4101 std::vector<std::unique_ptr<AppIconLoader>> no_loaders; | |
| 4102 launcher_controller_->SetAppIconLoadersForTest(no_loaders); | |
| 4103 | |
| 4104 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs(); | |
| 4105 EnablePlayStore(false); | |
| 4106 EXPECT_FALSE(arc::IsArcPlayStoreEnabledForProfile(profile())); | |
| 4107 ASSERT_TRUE(prefs->GetAppIds().size()); | |
| 4108 | |
| 4109 const std::string app_id = | |
| 4110 ArcAppTest::GetAppId(arc_test_.fake_default_apps()[0]); | |
| 4111 EXPECT_EQ(ash::kInvalidShelfID, | |
| 4112 launcher_controller_->GetShelfIDForAppID(app_id)); | |
| 4113 EXPECT_TRUE(arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON)); | |
| 4114 EXPECT_TRUE(arc::IsArcPlayStoreEnabledForProfile(profile())); | |
| 4115 EXPECT_NE(ash::kInvalidShelfID, | |
| 4116 launcher_controller_->GetShelfIDForAppID(app_id)); | |
| 4117 | |
| 4118 // Stop ARC again. Shelf item should go away. | |
| 4119 EnablePlayStore(false); | |
| 4120 EXPECT_EQ(ash::kInvalidShelfID, | |
| 4121 launcher_controller_->GetShelfIDForAppID(app_id)); | |
| 4122 | |
| 4123 EXPECT_TRUE(arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON)); | |
| 4124 EXPECT_TRUE(arc::IsArcPlayStoreEnabledForProfile(profile())); | |
| 4125 | |
| 4126 EXPECT_NE(ash::kInvalidShelfID, | |
| 4127 launcher_controller_->GetShelfIDForAppID(app_id)); | |
| 4128 EXPECT_TRUE(launcher_controller_->GetArcDeferredLauncher()->HasApp(app_id)); | |
| 4129 | |
| 4130 std::string window_app_id("org.chromium.arc.1"); | |
| 4131 CreateArcWindow(window_app_id); | |
| 4132 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_default_apps()[0], | |
| 4133 std::string()); | |
| 4134 | |
| 4135 EXPECT_NE(ash::kInvalidShelfID, | |
| 4136 launcher_controller_->GetShelfIDForAppID(app_id)); | |
| 4137 EXPECT_FALSE(launcher_controller_->GetArcDeferredLauncher()->HasApp(app_id)); | |
| 4138 } | |
| 4139 | |
| 4140 TEST_P(ChromeLauncherControllerArcDefaultAppsTest, PlayStoreDeferredLaunch) { | |
| 4141 // Add ARC host app to enable Play Store default app. | |
| 4142 extension_service_->AddExtension(arc_support_host_.get()); | |
| 4143 arc_test_.SetUp(profile()); | |
| 4144 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs(); | |
| 4145 EXPECT_TRUE(prefs->IsRegistered(arc::kPlayStoreAppId)); | |
| 4146 | |
| 4147 InitLauncherController(); | |
| 4148 | |
| 4149 EnablePlayStore(true); | |
| 4150 | |
| 4151 // Pin Play Store. It should be pinned but not scheduled for deferred launch. | |
| 4152 launcher_controller_->PinAppWithID(arc::kPlayStoreAppId); | |
| 4153 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc::kPlayStoreAppId)); | |
| 4154 EXPECT_FALSE(launcher_controller_->GetArcDeferredLauncher()->HasApp( | |
| 4155 arc::kPlayStoreAppId)); | |
| 4156 | |
| 4157 // Simulate click. This should schedule Play Store for deferred launch. | |
| 4158 ash::ShelfItemDelegate* item_delegate = model_->GetShelfItemDelegate( | |
| 4159 launcher_controller_->GetShelfIDForAppID(arc::kPlayStoreAppId)); | |
| 4160 EXPECT_TRUE(item_delegate); | |
| 4161 SelectItem(item_delegate); | |
| 4162 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc::kPlayStoreAppId)); | |
| 4163 EXPECT_TRUE(launcher_controller_->GetArcDeferredLauncher()->HasApp( | |
| 4164 arc::kPlayStoreAppId)); | |
| 4165 } | |
| 4166 | |
| 4167 // Checks the case when several app items have the same ordinal position (which | |
| 4168 // is valid case). | |
| 4169 TEST_F(ChromeLauncherControllerImplTest, CheckPositionConflict) { | |
| 4170 InitLauncherController(); | |
| 4171 | |
| 4172 extension_service_->AddExtension(extension1_.get()); | |
| 4173 extension_service_->AddExtension(extension2_.get()); | |
| 4174 extension_service_->AddExtension(extension3_.get()); | |
| 4175 | |
| 4176 syncer::SyncChangeList sync_list; | |
| 4177 InsertAddPinChange(&sync_list, 0, extension_misc::kChromeAppId); | |
| 4178 InsertAddPinChange(&sync_list, 1, extension1_->id()); | |
| 4179 InsertAddPinChange(&sync_list, 1, extension2_->id()); | |
| 4180 InsertAddPinChange(&sync_list, 1, extension3_->id()); | |
| 4181 SendPinChanges(sync_list, true); | |
| 4182 | |
| 4183 EXPECT_EQ("AppList, Chrome, App1, App2, App3", GetPinnedAppStatus()); | |
| 4184 | |
| 4185 const syncer::StringOrdinal position_chrome = | |
| 4186 app_service_->GetPinPosition(extension_misc::kChromeAppId); | |
| 4187 const syncer::StringOrdinal position_1 = | |
| 4188 app_service_->GetPinPosition(extension1_->id()); | |
| 4189 const syncer::StringOrdinal position_2 = | |
| 4190 app_service_->GetPinPosition(extension2_->id()); | |
| 4191 const syncer::StringOrdinal position_3 = | |
| 4192 app_service_->GetPinPosition(extension3_->id()); | |
| 4193 EXPECT_TRUE(position_chrome.LessThan(position_1)); | |
| 4194 EXPECT_TRUE(position_1.Equals(position_2)); | |
| 4195 EXPECT_TRUE(position_2.Equals(position_3)); | |
| 4196 | |
| 4197 // Move Chrome between App1 and App2. | |
| 4198 // Note, move target_index is in context when moved element is removed from | |
| 4199 // array first. | |
| 4200 model_->Move(1, 2); | |
| 4201 EXPECT_EQ("AppList, App1, Chrome, App2, App3", GetPinnedAppStatus()); | |
| 4202 | |
| 4203 // Expect sync positions for only Chrome is updated and its resolution is | |
| 4204 // after all duplicated ordinals. | |
| 4205 EXPECT_TRUE(position_3.LessThan( | |
| 4206 app_service_->GetPinPosition(extension_misc::kChromeAppId))); | |
| 4207 EXPECT_TRUE( | |
| 4208 position_1.Equals(app_service_->GetPinPosition(extension1_->id()))); | |
| 4209 EXPECT_TRUE( | |
| 4210 position_1.Equals(app_service_->GetPinPosition(extension1_->id()))); | |
| 4211 EXPECT_TRUE( | |
| 4212 position_2.Equals(app_service_->GetPinPosition(extension2_->id()))); | |
| 4213 EXPECT_TRUE( | |
| 4214 position_3.Equals(app_service_->GetPinPosition(extension3_->id()))); | |
| 4215 } | |
| 4216 | |
| 4217 // Test the case when sync app is turned off and we need to use local copy to | |
| 4218 // support user's pins. | |
| 4219 TEST_F(ChromeLauncherControllerImplTest, SyncOffLocalUpdate) { | |
| 4220 InitLauncherController(); | |
| 4221 | |
| 4222 extension_service_->AddExtension(extension1_.get()); | |
| 4223 extension_service_->AddExtension(extension2_.get()); | |
| 4224 | |
| 4225 syncer::SyncChangeList sync_list; | |
| 4226 InsertAddPinChange(&sync_list, 0, extension_misc::kChromeAppId); | |
| 4227 InsertAddPinChange(&sync_list, 1, extension1_->id()); | |
| 4228 InsertAddPinChange(&sync_list, 1, extension2_->id()); | |
| 4229 SendPinChanges(sync_list, true); | |
| 4230 | |
| 4231 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 4232 | |
| 4233 syncer::SyncDataList copy_sync_list = | |
| 4234 app_service_->GetAllSyncData(syncer::APP_LIST); | |
| 4235 | |
| 4236 app_service_->StopSyncing(syncer::APP_LIST); | |
| 4237 RecreateLauncherController()->Init(); | |
| 4238 | |
| 4239 // Pinned state should not change. | |
| 4240 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 4241 launcher_controller_->UnpinAppWithID(extension2_->id()); | |
| 4242 EXPECT_EQ("AppList, Chrome, App1", GetPinnedAppStatus()); | |
| 4243 | |
| 4244 // Resume syncing and sync information overrides local copy. | |
| 4245 StartAppSyncService(copy_sync_list); | |
| 4246 EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus()); | |
| 4247 } | |
| 4248 | |
| 4249 // Tests that shelf profile preferences are loaded on login. | |
| 4250 TEST_F(ChromeLauncherControllerImplTest, PrefsLoadedOnLogin) { | |
| 4251 PrefService* prefs = profile()->GetTestingPrefService(); | |
| 4252 prefs->SetString(prefs::kShelfAlignmentLocal, "Left"); | |
| 4253 prefs->SetString(prefs::kShelfAlignment, "Left"); | |
| 4254 prefs->SetString(prefs::kShelfAutoHideBehaviorLocal, "Always"); | |
| 4255 prefs->SetString(prefs::kShelfAutoHideBehavior, "Always"); | |
| 4256 | |
| 4257 TestChromeLauncherControllerImpl* test_launcher_controller = | |
| 4258 shell_delegate_->CreateTestLauncherController(profile()); | |
| 4259 | |
| 4260 // Simulate login for the test controller. | |
| 4261 test_launcher_controller->ReleaseProfile(); | |
| 4262 test_launcher_controller->AttachProfile(profile()); | |
| 4263 base::RunLoop().RunUntilIdle(); | |
| 4264 | |
| 4265 TestShelfController* shelf_controller = | |
| 4266 test_launcher_controller->test_shelf_controller(); | |
| 4267 ASSERT_TRUE(shelf_controller); | |
| 4268 EXPECT_EQ(ash::SHELF_ALIGNMENT_LEFT, shelf_controller->alignment()); | |
| 4269 EXPECT_EQ(1u, shelf_controller->alignment_change_count()); | |
| 4270 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, | |
| 4271 shelf_controller->auto_hide()); | |
| 4272 EXPECT_EQ(1u, shelf_controller->auto_hide_change_count()); | |
| 4273 } | |
| 4274 | |
| 4275 // Tests that the shelf controller's changes are not wastefully echoed back. | |
| 4276 TEST_F(ChromeLauncherControllerImplTest, DoNotEchoShelfControllerChanges) { | |
| 4277 TestChromeLauncherControllerImpl* test_launcher_controller = | |
| 4278 shell_delegate_->CreateTestLauncherController(profile()); | |
| 4279 | |
| 4280 // Simulate login for the test controller. | |
| 4281 test_launcher_controller->ReleaseProfile(); | |
| 4282 test_launcher_controller->AttachProfile(profile()); | |
| 4283 base::RunLoop().RunUntilIdle(); | |
| 4284 | |
| 4285 TestShelfController* shelf_controller = | |
| 4286 test_launcher_controller->test_shelf_controller(); | |
| 4287 ASSERT_TRUE(shelf_controller); | |
| 4288 EXPECT_EQ(ash::SHELF_ALIGNMENT_BOTTOM, shelf_controller->alignment()); | |
| 4289 EXPECT_EQ(1u, shelf_controller->alignment_change_count()); | |
| 4290 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_NEVER, shelf_controller->auto_hide()); | |
| 4291 EXPECT_EQ(1u, shelf_controller->auto_hide_change_count()); | |
| 4292 | |
| 4293 // Changing settings via the shelf controller causes the launcher controller | |
| 4294 // to update profile prefs. The launcher controller's prefs observation should | |
| 4295 // not cause those same changes to be echoed back to the shelf controller. | |
| 4296 int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); | |
| 4297 shelf_controller->SetAlignment(ash::SHELF_ALIGNMENT_LEFT, display_id); | |
| 4298 EXPECT_EQ(2u, shelf_controller->alignment_change_count()); | |
| 4299 shelf_controller->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, | |
| 4300 display_id); | |
| 4301 EXPECT_EQ(2u, shelf_controller->auto_hide_change_count()); | |
| 4302 base::RunLoop().RunUntilIdle(); | |
| 4303 | |
| 4304 EXPECT_EQ(ash::SHELF_ALIGNMENT_LEFT, shelf_controller->alignment()); | |
| 4305 EXPECT_EQ(2u, shelf_controller->alignment_change_count()); | |
| 4306 EXPECT_EQ(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, | |
| 4307 shelf_controller->auto_hide()); | |
| 4308 EXPECT_EQ(2u, shelf_controller->auto_hide_change_count()); | |
| 4309 | |
| 4310 PrefService* prefs = profile()->GetTestingPrefService(); | |
| 4311 EXPECT_EQ("Left", prefs->GetString(prefs::kShelfAlignmentLocal)); | |
| 4312 EXPECT_EQ("Left", prefs->GetString(prefs::kShelfAlignment)); | |
| 4313 EXPECT_EQ("Always", prefs->GetString(prefs::kShelfAutoHideBehaviorLocal)); | |
| 4314 EXPECT_EQ("Always", prefs->GetString(prefs::kShelfAutoHideBehavior)); | |
| 4315 } | |
| OLD | NEW |