1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
6
7 #include <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/multi_user/multi_user_window_manager_impl.h"
19 #include "ash/public/cpp/app_list/internal_app_id_constants.h"
20 #include "ash/public/cpp/multi_user_window_manager.h"
21 #include "ash/public/cpp/shelf_item.h"
22 #include "ash/public/cpp/shelf_item_delegate.h"
23 #include "ash/public/cpp/shelf_model.h"
24 #include "ash/public/cpp/shelf_types.h"
25 #include "ash/public/cpp/window_properties.h"
26 #include "ash/shelf/shelf_application_menu_model.h"
27 #include "base/callback_helpers.h"
28 #include "base/command_line.h"
29 #include "base/compiler_specific.h"
30 #include "base/feature_list.h"
31 #include "base/files/file_path.h"
32 #include "base/json/json_string_value_serializer.h"
33 #include "base/location.h"
34 #include "base/memory/ptr_util.h"
35 #include "base/metrics/histogram.h"
36 #include "base/metrics/statistics_recorder.h"
37 #include "base/run_loop.h"
38 #include "base/single_thread_task_runner.h"
39 #include "base/strings/utf_string_conversions.h"
40 #include "base/test/scoped_feature_list.h"
41 #include "base/values.h"
42 #include "build/build_config.h"
43 #include "chrome/browser/apps/app_service/app_service_proxy.h"
44 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
45 #include "chrome/browser/apps/app_service/app_service_test.h"
46 #include "chrome/browser/chromeos/arc/arc_util.h"
47 #include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
48 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
49 #include "chrome/browser/chromeos/crostini/crostini_util.h"
50 #include "chrome/browser/chromeos/file_manager/app_id.h"
51 #include "chrome/browser/chromeos/login/demo_mode/demo_mode_test_helper.h"
52 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
53 #include "chrome/browser/chromeos/profiles/profile_helper.h"
54 #include "chrome/browser/extensions/extension_service.h"
55 #include "chrome/browser/extensions/test_extension_system.h"
56 #include "chrome/browser/prefs/browser_prefs.h"
57 #include "chrome/browser/sync/profile_sync_service_factory.h"
58 #include "chrome/browser/ui/app_icon_loader.h"
59 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
60 #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
61 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
62 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
63 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
64 #include "chrome/browser/ui/app_list/arc/arc_default_app_list.h"
65 #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
66 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
67 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
68 #include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_item_controller.h"
69 #include "chrome/browser/ui/ash/launcher/app_window_launcher_controller.h"
70 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
71 #include "chrome/browser/ui/ash/launcher/arc_app_window.h"
72 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
73 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
74 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
75 #include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
76 #include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h"
77 #include "chrome/browser/ui/ash/multi_user/multi_profile_support.h"
78 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
79 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
80 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
81 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
82 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
83 #include "chrome/browser/ui/browser.h"
84 #include "chrome/browser/ui/browser_commands.h"
85 #include "chrome/browser/ui/browser_finder.h"
86 #include "chrome/browser/ui/browser_list.h"
87 #include "chrome/browser/ui/browser_tabstrip.h"
88 #include "chrome/browser/ui/tabs/tab_strip_model.h"
89 #include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
90 #include "chrome/browser/web_applications/components/policy/web_app_policy_constants.h"
91 #include "chrome/browser/web_applications/components/web_app_constants.h"
92 #include "chrome/browser/web_applications/test/test_system_web_app_manager.h"
93 #include "chrome/browser/web_applications/test/test_web_app_provider.h"
94 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
95 #include "chrome/common/chrome_constants.h"
96 #include "chrome/common/chrome_features.h"
97 #include "chrome/common/chrome_switches.h"
98 #include "chrome/common/extensions/extension_constants.h"
99 #include "chrome/common/pref_names.h"
100 #include "chrome/test/base/browser_with_test_window_test.h"
101 #include "chrome/test/base/test_browser_window_aura.h"
102 #include "chrome/test/base/testing_profile.h"
103 #include "chrome/test/base/testing_profile_manager.h"
104 #include "chromeos/constants/chromeos_features.h"
105 #include "chromeos/constants/chromeos_switches.h"
106 #include "components/account_id/account_id.h"
107 #include "components/arc/arc_prefs.h"
108 #include "components/arc/arc_util.h"
109 #include "components/arc/metrics/arc_metrics_constants.h"
110 #include "components/arc/mojom/app.mojom.h"
111 #include "components/arc/test/fake_app_instance.h"
112 #include "components/exo/shell_surface_util.h"
113 #include "components/keep_alive_registry/scoped_keep_alive.h"
114 #include "components/prefs/pref_notifier_impl.h"
115 #include "components/sync/base/model_type.h"
116 #include "components/sync/driver/sync_service.h"
117 #include "components/sync/driver/sync_user_settings.h"
118 #include "components/sync/protocol/sync.pb.h"
119 #include "components/sync/test/model/fake_sync_change_processor.h"
120 #include "components/sync/test/model/sync_error_factory_mock.h"
121 #include "components/sync_preferences/pref_model_associator.h"
122 #include "components/sync_preferences/testing_pref_service_syncable.h"
123 #include "components/user_manager/fake_user_manager.h"
124 #include "components/user_manager/scoped_user_manager.h"
125 #include "content/public/browser/web_contents.h"
126 #include "content/public/browser/web_contents_observer.h"
127 #include "content/public/test/test_utils.h"
128 #include "content/public/test/web_contents_tester.h"
129 #include "extensions/browser/app_window/app_window_contents.h"
130 #include "extensions/browser/app_window/app_window_registry.h"
131 #include "extensions/browser/app_window/native_app_window.h"
132 #include "extensions/browser/extension_registry.h"
133 #include "extensions/common/constants.h"
134 #include "extensions/common/extension.h"
135 #include "extensions/common/manifest_constants.h"
136 #include "extensions/grit/extensions_browser_resources.h"
137 #include "services/network/test/test_network_connection_tracker.h"
138 #include "testing/gtest/include/gtest/gtest.h"
139 #include "ui/aura/client/window_parenting_client.h"
140 #include "ui/aura/window.h"
141 #include "ui/base/models/menu_model.h"
142 #include "ui/base/resource/resource_bundle.h"
143 #include "ui/base/ui_base_features.h"
144 #include "ui/display/display.h"
145 #include "ui/display/display_switches.h"
146 #include "ui/display/screen.h"
147 #include "ui/events/base_event_utils.h"
148 #include "ui/events/event_constants.h"
149 #include "ui/events/types/event_type.h"
150 #include "ui/gfx/image/image_skia_operations.h"
151 #include "ui/gfx/image/image_unittest_util.h"
152 #include "ui/views/widget/widget.h"
153
154 using base::ASCIIToUTF16;
155 using extensions::Extension;
156 using extensions::Manifest;
157 using extensions::UnloadedExtensionReason;
158
159 namespace {
160 constexpr char kOfflineGmailUrl[] = "https://mail.google.com/mail/mu/u";
161 constexpr char kGmailUrl[] = "https://mail.google.com/mail/u";
162 constexpr char kGmailLaunchURL[] = "https://mail.google.com/mail/ca";
163 constexpr char kLaunchURL[] = "https://foo.example/";
164
165 // An extension prefix.
166 constexpr char kCrxAppPrefix[] = "_crx_";
167
168 // Dummy app id is used to put at least one pin record to prevent initializing
169 // pin model with default apps that can affect some tests.
170 constexpr char kDummyAppId[] = "dummyappid_dummyappid_dummyappid";
171
172 // Test implementation of AppIconLoader.
173 class TestAppIconLoaderImpl : public AppIconLoader {
174 public:
175 TestAppIconLoaderImpl() = default;
176 TestAppIconLoaderImpl(const TestAppIconLoaderImpl&) = delete;
177 TestAppIconLoaderImpl& operator=(const TestAppIconLoaderImpl&) = delete;
178 ~TestAppIconLoaderImpl() override = default;
179
AddSupportedApp(const std::string & id)180 void AddSupportedApp(const std::string& id) { supported_apps_.insert(id); }
181
182 // AppIconLoader implementation:
CanLoadImageForApp(const std::string & id)183 bool CanLoadImageForApp(const std::string& id) override {
184 return supported_apps_.find(id) != supported_apps_.end();
185 }
FetchImage(const std::string & id)186 void FetchImage(const std::string& id) override { ++fetch_count_; }
ClearImage(const std::string & id)187 void ClearImage(const std::string& id) override { ++clear_count_; }
UpdateImage(const std::string & id)188 void UpdateImage(const std::string& id) override {}
189
fetch_count() const190 int fetch_count() const { return fetch_count_; }
clear_count() const191 int clear_count() const { return clear_count_; }
192
193 private:
194 int fetch_count_ = 0;
195 int clear_count_ = 0;
196 std::set<std::string> supported_apps_;
197 };
198
199 // Test implementation of LauncherControllerHelper.
200 class TestLauncherControllerHelper : public LauncherControllerHelper {
201 public:
TestLauncherControllerHelper()202 TestLauncherControllerHelper() : LauncherControllerHelper(nullptr) {}
TestLauncherControllerHelper(Profile * profile)203 explicit TestLauncherControllerHelper(Profile* profile)
204 : LauncherControllerHelper(profile) {}
205 TestLauncherControllerHelper(const TestLauncherControllerHelper&) = delete;
206 TestLauncherControllerHelper& operator=(const TestLauncherControllerHelper&) =
207 delete;
208 ~TestLauncherControllerHelper() override = default;
209
210 // Sets the id for the specified tab.
SetAppID(content::WebContents * tab,const std::string & id)211 void SetAppID(content::WebContents* tab, const std::string& id) {
212 tab_id_map_[tab] = id;
213 }
214
215 // Returns true if there is an id registered for |tab|.
HasAppID(content::WebContents * tab) const216 bool HasAppID(content::WebContents* tab) const {
217 return tab_id_map_.find(tab) != tab_id_map_.end();
218 }
219
220 // LauncherControllerHelper:
GetAppID(content::WebContents * tab)221 std::string GetAppID(content::WebContents* tab) override {
222 return tab_id_map_.find(tab) != tab_id_map_.end() ? tab_id_map_[tab]
223 : std::string();
224 }
225
IsValidIDForCurrentUser(const std::string & id) const226 bool IsValidIDForCurrentUser(const std::string& id) const override {
227 for (TabToStringMap::const_iterator i = tab_id_map_.begin();
228 i != tab_id_map_.end(); ++i) {
229 if (i->second == id)
230 return true;
231 }
232 return false;
233 }
234
GetArcAppListPrefs() const235 ArcAppListPrefs* GetArcAppListPrefs() const override { return nullptr; }
236
237 private:
238 typedef std::map<content::WebContents*, std::string> TabToStringMap;
239
240 TabToStringMap tab_id_map_;
241 };
242
243 // Test implementation of a V2 app launcher item controller.
244 class TestV2AppLauncherItemController : public ash::ShelfItemDelegate {
245 public:
TestV2AppLauncherItemController(const std::string & app_id)246 explicit TestV2AppLauncherItemController(const std::string& app_id)
247 : ash::ShelfItemDelegate(ash::ShelfID(app_id)) {}
248
249 TestV2AppLauncherItemController(const TestV2AppLauncherItemController&) =
250 delete;
251 TestV2AppLauncherItemController& operator=(
252 const TestV2AppLauncherItemController&) = delete;
253 ~TestV2AppLauncherItemController() override = default;
254
255 // Override for ash::ShelfItemDelegate:
ItemSelected(std::unique_ptr<ui::Event> event,int64_t display_id,ash::ShelfLaunchSource source,ItemSelectedCallback callback,const ItemFilterPredicate & filter_predicate)256 void ItemSelected(std::unique_ptr<ui::Event> event,
257 int64_t display_id,
258 ash::ShelfLaunchSource source,
259 ItemSelectedCallback callback,
260 const ItemFilterPredicate& filter_predicate) override {
261 std::move(callback).Run(ash::SHELF_ACTION_WINDOW_ACTIVATED, {});
262 }
ExecuteCommand(bool,int64_t,int32_t,int64_t)263 void ExecuteCommand(bool, int64_t, int32_t, int64_t) override {}
Close()264 void Close() override {}
265 };
266
267 // Simulates selection of the shelf item.
SelectItem(ash::ShelfItemDelegate * delegate)268 void SelectItem(ash::ShelfItemDelegate* delegate) {
269 std::unique_ptr<ui::Event> event = std::make_unique<ui::MouseEvent>(
270 ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
271 ui::EF_NONE, 0);
272 delegate->ItemSelected(std::move(event), display::kInvalidDisplayId,
273 ash::LAUNCH_FROM_UNKNOWN, base::DoNothing(),
274 base::NullCallback());
275 }
276
IsWindowOnDesktopOfUser(aura::Window * window,const AccountId & account_id)277 bool IsWindowOnDesktopOfUser(aura::Window* window,
278 const AccountId& account_id) {
279 return MultiUserWindowManagerHelper::GetInstance()->IsWindowOnDesktopOfUser(
280 window, account_id);
281 }
282
283 } // namespace
284
285 class ChromeLauncherControllerTest : public BrowserWithTestWindowTest {
286 protected:
ChromeLauncherControllerTest()287 ChromeLauncherControllerTest()
288 : BrowserWithTestWindowTest(Browser::TYPE_NORMAL) {
289 }
290
291 ChromeLauncherControllerTest(const ChromeLauncherControllerTest&) = delete;
292 ChromeLauncherControllerTest& operator=(const ChromeLauncherControllerTest&) =
293 delete;
294 ~ChromeLauncherControllerTest() override = default;
295
SetUp()296 void SetUp() override {
297 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
298 command_line->AppendSwitch(switches::kUseFirstDisplayAsInternal);
299
300 app_list::AppListSyncableServiceFactory::SetUseInTesting(true);
301
302 BrowserWithTestWindowTest::SetUp();
303
304 model_ = std::make_unique<ash::ShelfModel>();
305
306 base::DictionaryValue manifest;
307 manifest.SetString(extensions::manifest_keys::kName,
308 "launcher controller test extension");
309 manifest.SetString(extensions::manifest_keys::kVersion, "1");
310 manifest.SetInteger(extensions::manifest_keys::kManifestVersion, 2);
311 manifest.SetString(extensions::manifest_keys::kDescription,
312 "for testing pinned apps");
313 // AppService checks the app's type. So set the
314 // manifest_keys::kLaunchWebURL, so that the extension can get the type
315 // from manifest value, and then AppService can get the extension's type.
316 manifest.SetString(extensions::manifest_keys::kLaunchWebURL, kLaunchURL);
317
318 base::DictionaryValue manifest_platform_app;
319 manifest_platform_app.SetString(extensions::manifest_keys::kName,
320 "launcher controller test platform app");
321 manifest_platform_app.SetString(extensions::manifest_keys::kVersion, "1");
322 manifest_platform_app.SetString(extensions::manifest_keys::kDescription,
323 "for testing pinned platform apps");
324 manifest_platform_app.SetString(extensions::manifest_keys::kApp, "true");
325 manifest_platform_app.Set(extensions::manifest_keys::kPlatformAppBackground,
326 std::make_unique<base::DictionaryValue>());
327 auto scripts = std::make_unique<base::ListValue>();
328 scripts->AppendString("main.js");
329 manifest_platform_app.Set(
330 extensions::manifest_keys::kPlatformAppBackgroundScripts,
331 std::move(scripts));
332
333 extensions::TestExtensionSystem* extension_system(
334 static_cast<extensions::TestExtensionSystem*>(
335 extensions::ExtensionSystem::Get(profile())));
336 extension_service_ = extension_system->CreateExtensionService(
337 base::CommandLine::ForCurrentProcess(), base::FilePath(), false);
338 extension_service_->Init();
339
340 DCHECK(profile());
341 extension_registry_ = extensions::ExtensionRegistry::Get(profile());
342 app_service_test_.SetUp(profile());
343
344 if (auto_start_arc_test_)
345 arc_test_.SetUp(profile());
346
347 // Wait until |extension_system| is signaled as started.
348 base::RunLoop run_loop;
349 extension_system->ready().Post(FROM_HERE, run_loop.QuitClosure());
350 run_loop.Run();
351
352 // Many pinned app tests assume OS sync is enabled.
353 if (chromeos::features::IsSplitSettingsSyncEnabled()) {
354 syncer::SyncService* sync_service =
355 ProfileSyncServiceFactory::GetForProfile(profile());
356 sync_service->GetUserSettings()->SetOsSyncFeatureEnabled(true);
357 }
358
359 app_list_syncable_service_ =
360 app_list::AppListSyncableServiceFactory::GetForProfile(profile());
361 StartAppSyncService(app_list_syncable_service_->GetAllSyncDataForTesting());
362
363 std::string error;
364 extension_chrome_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
365 manifest, Extension::NO_FLAGS,
366 extension_misc::kChromeAppId, &error);
367 extension1_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
368 manifest, Extension::NO_FLAGS,
369 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &error);
370 extension2_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
371 manifest, Extension::NO_FLAGS,
372 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", &error);
373 extension5_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
374 manifest, Extension::NO_FLAGS,
375 "cccccccccccccccccccccccccccccccc", &error);
376 extension6_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
377 manifest, Extension::NO_FLAGS,
378 "dddddddddddddddddddddddddddddddd", &error);
379 extension7_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
380 manifest, Extension::NO_FLAGS,
381 "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", &error);
382 extension8_ = Extension::Create(base::FilePath(), Manifest::UNPACKED,
383 manifest, Extension::NO_FLAGS,
384 "ffffffffffffffffffffffffffffffff", &error);
385 extension_platform_app_ = Extension::Create(
386 base::FilePath(), Manifest::UNPACKED, manifest_platform_app,
387 Extension::NO_FLAGS, "gggggggggggggggggggggggggggggggg", &error);
388 arc_support_host_ =
389 Extension::Create(base::FilePath(), Manifest::UNPACKED, manifest,
390 Extension::NO_FLAGS, arc::kPlayStoreAppId, &error);
391 extension_service_->AddExtension(extension_chrome_.get());
392
393 // Fake Gmail app.
394 base::DictionaryValue manifest_gmail;
395 manifest_gmail.SetString(extensions::manifest_keys::kName,
396 "Gmail launcher controller test extension");
397 manifest_gmail.SetString(extensions::manifest_keys::kVersion, "1");
398 manifest_gmail.SetInteger(extensions::manifest_keys::kManifestVersion, 2);
399 manifest_gmail.SetString(extensions::manifest_keys::kDescription,
400 "for testing pinned Gmail");
401 manifest_gmail.SetString(extensions::manifest_keys::kLaunchWebURL,
402 kGmailLaunchURL);
403 auto list = std::make_unique<base::ListValue>();
404 list->AppendString("*://mail.google.com/mail/ca");
405 manifest_gmail.Set(extensions::manifest_keys::kWebURLs, std::move(list));
406
407 extension_gmail_app_ = Extension::Create(
408 base::FilePath(), Manifest::UNPACKED, manifest_gmail,
409 Extension::NO_FLAGS, extension_misc::kGmailAppId, &error);
410 // Fake Google Doc app.
411 extension_doc_app_ = Extension::Create(
412 base::FilePath(), Manifest::UNPACKED, manifest, Extension::NO_FLAGS,
413 extension_misc::kGoogleDocAppId, &error);
414
415 // Fake Youtube app.
416 extension_youtube_app_ = Extension::Create(
417 base::FilePath(), Manifest::UNPACKED, manifest, Extension::NO_FLAGS,
418 extension_misc::kYoutubeAppId, &error);
419
420 // Fake File Manager app.
421 extension_files_app_ = Extension::Create(
422 base::FilePath(), Manifest::UNPACKED, manifest, Extension::NO_FLAGS,
423 extension_misc::kFilesManagerAppId, &error);
424
425 MaybeStartWebAppProvider();
426 }
427
MaybeStartWebAppProvider()428 virtual void MaybeStartWebAppProvider() {
429 web_app::TestWebAppProvider::Get(profile())->Start();
430 }
431
GetLastActiveWindowForItemController(AppWindowLauncherItemController * item_controller)432 ui::BaseWindow* GetLastActiveWindowForItemController(
433 AppWindowLauncherItemController* item_controller) {
434 return item_controller->last_active_window_;
435 }
436
437 // Creates a running platform V2 app (not pinned) of type |app_id|.
CreateRunningV2App(const std::string & app_id)438 virtual void CreateRunningV2App(const std::string& app_id) {
439 DCHECK(!test_controller_);
440 // Change the created launcher controller into a V2 app controller.
441 std::unique_ptr<TestV2AppLauncherItemController> controller =
442 std::make_unique<TestV2AppLauncherItemController>(app_id);
443 test_controller_ = controller.get();
444 ash::ShelfID id = launcher_controller_->InsertAppLauncherItem(
445 std::move(controller), ash::STATUS_RUNNING, model_->item_count(),
446 ash::TYPE_APP);
447 DCHECK(launcher_controller_->IsPlatformApp(id));
448 }
449
450 // Sets the stage for a multi user test.
SetUpMultiUserScenario(syncer::SyncChangeList * user_a,syncer::SyncChangeList * user_b)451 virtual void SetUpMultiUserScenario(syncer::SyncChangeList* user_a,
452 syncer::SyncChangeList* user_b) {
453 InitLauncherController();
454 EXPECT_EQ("Chrome", GetPinnedAppStatus());
455
456 // Set an empty pinned pref to begin with.
457 syncer::SyncChangeList sync_list;
458 InsertAddPinChange(&sync_list, 0, extension_misc::kChromeAppId);
459 SendPinChanges(sync_list, true);
460 EXPECT_EQ("Chrome", GetPinnedAppStatus());
461
462 // Assume all applications have been added already.
463 extension_service_->AddExtension(extension1_.get());
464 extension_service_->AddExtension(extension2_.get());
465 extension_service_->AddExtension(extension_gmail_app_.get());
466 extension_service_->AddExtension(extension_doc_app_.get());
467 extension_service_->AddExtension(extension5_.get());
468 extension_service_->AddExtension(extension6_.get());
469 extension_service_->AddExtension(extension7_.get());
470 extension_service_->AddExtension(extension8_.get());
471 extension_service_->AddExtension(extension_platform_app_.get());
472 // There should be nothing in the list by now.
473 EXPECT_EQ("Chrome", GetPinnedAppStatus());
474
475 // Set user a preferences.
476 InsertAddPinChange(user_a, 0, extension1_->id());
477 InsertAddPinChange(user_a, 1, extension2_->id());
478 InsertAddPinChange(user_a, 2, extension_gmail_app_->id());
479 InsertAddPinChange(user_a, 3, extension_platform_app_->id());
480 InsertAddPinChange(user_a, 4, extension_doc_app_->id());
481 InsertAddPinChange(user_a, 5, extension5_->id());
482 InsertAddPinChange(user_a, 6, extension_misc::kChromeAppId);
483
484 // Set user b preferences.
485 InsertAddPinChange(user_b, 0, extension6_->id());
486 InsertAddPinChange(user_b, 1, extension7_->id());
487 InsertAddPinChange(user_b, 2, extension8_->id());
488 InsertAddPinChange(user_b, 3, extension_misc::kChromeAppId);
489 }
490
TearDown()491 void TearDown() override {
492 arc_test_.TearDown();
493 launcher_controller_ = nullptr;
494 BrowserWithTestWindowTest::TearDown();
495 app_list::AppListSyncableServiceFactory::SetUseInTesting(false);
496 }
497
CreateBrowserWindow()498 std::unique_ptr<BrowserWindow> CreateBrowserWindow() override {
499 return std::unique_ptr<TestBrowserWindow>(CreateTestBrowserWindowAura());
500 }
501
CreateBrowserWithTestWindowForProfile(Profile * profile)502 std::unique_ptr<Browser> CreateBrowserWithTestWindowForProfile(
503 Profile* profile) {
504 TestBrowserWindow* browser_window = CreateTestBrowserWindowAura();
505 new TestBrowserWindowOwner(browser_window);
506 return CreateBrowser(profile, Browser::TYPE_NORMAL, false, browser_window);
507 }
508
509 // Create an uninitialized chrome launcher controller instance.
CreateLauncherController()510 ChromeLauncherController* CreateLauncherController() {
511 launcher_controller_ =
512 std::make_unique<ChromeLauncherController>(profile(), model_.get());
513 return launcher_controller_.get();
514 }
515
516 // Create and initialize the controller, owned by the test shell delegate.
InitLauncherController()517 void InitLauncherController() {
518 CreateLauncherController()->Init();
519 app_service_test_.FlushMojoCalls();
520 }
521
522 // Create and initialize the controller; create a tab and show the browser.
InitLauncherControllerWithBrowser()523 void InitLauncherControllerWithBrowser() {
524 InitLauncherController();
525 chrome::NewTab(browser());
526 browser()->window()->Show();
527 }
528
529 // Destroy the launcher controller instance and clear the local pointer.
ResetLauncherController()530 void ResetLauncherController() { launcher_controller_.reset(); }
531
532 // Destroy and recreate the controller; clear and reinitialize the ShelfModel.
533 // Returns a pointer to the uninitialized controller, owned by shell delegate.
534 // TODO(msw): This does not accurately represent ChromeLauncherController
535 // lifetime or usage in production, and does not accurately simulate restarts.
RecreateLauncherController()536 ChromeLauncherController* RecreateLauncherController() {
537 // Destroy any existing controller first; only one may exist at a time.
538 ResetLauncherController();
539 model_ = std::make_unique<ash::ShelfModel>();
540 return CreateLauncherController();
541 }
542
StartAppSyncService(const syncer::SyncDataList & init_sync_list)543 void StartAppSyncService(const syncer::SyncDataList& init_sync_list) {
544 app_list_syncable_service_->MergeDataAndStartSyncing(
545 syncer::APP_LIST, init_sync_list,
546 std::make_unique<syncer::FakeSyncChangeProcessor>(),
547 std::make_unique<syncer::SyncErrorFactoryMock>());
548 EXPECT_EQ(init_sync_list.size(),
549 app_list_syncable_service_->sync_items().size());
550 }
551
StopAppSyncService()552 void StopAppSyncService() {
553 app_list_syncable_service_->StopSyncing(syncer::APP_LIST);
554 }
555
556 // static
GetPreferencesModelType()557 syncer::ModelType GetPreferencesModelType() {
558 // SplitSettingsSync makes shelf prefs into OS prefs.
559 return chromeos::features::IsSplitSettingsSyncEnabled()
560 ? syncer::OS_PREFERENCES
561 : syncer::PREFERENCES;
562 }
563
GetPrefSyncService()564 sync_preferences::PrefModelAssociator* GetPrefSyncService() {
565 sync_preferences::PrefServiceSyncable* pref_sync =
566 profile()->GetTestingPrefService();
567 sync_preferences::PrefModelAssociator* pref_sync_service =
568 static_cast<sync_preferences::PrefModelAssociator*>(
569 pref_sync->GetSyncableService(GetPreferencesModelType()));
570 return pref_sync_service;
571 }
572
StartPrefSyncService(const syncer::SyncDataList & init_sync_list)573 void StartPrefSyncService(const syncer::SyncDataList& init_sync_list) {
574 base::Optional<syncer::ModelError> error =
575 GetPrefSyncService()->MergeDataAndStartSyncing(
576 GetPreferencesModelType(), init_sync_list,
577 std::make_unique<syncer::FakeSyncChangeProcessor>(),
578 std::make_unique<syncer::SyncErrorFactoryMock>());
579 EXPECT_FALSE(error.has_value());
580 }
581
SetAppIconLoader(std::unique_ptr<AppIconLoader> loader)582 void SetAppIconLoader(std::unique_ptr<AppIconLoader> loader) {
583 std::vector<std::unique_ptr<AppIconLoader>> loaders;
584 loaders.push_back(std::move(loader));
585 launcher_controller_->SetAppIconLoadersForTest(loaders);
586 }
587
SetAppIconLoaders(std::unique_ptr<AppIconLoader> loader1,std::unique_ptr<AppIconLoader> loader2)588 void SetAppIconLoaders(std::unique_ptr<AppIconLoader> loader1,
589 std::unique_ptr<AppIconLoader> loader2) {
590 std::vector<std::unique_ptr<AppIconLoader>> loaders;
591 loaders.push_back(std::move(loader1));
592 loaders.push_back(std::move(loader2));
593 launcher_controller_->SetAppIconLoadersForTest(loaders);
594 }
595
SetLauncherControllerHelper(LauncherControllerHelper * helper)596 void SetLauncherControllerHelper(LauncherControllerHelper* helper) {
597 launcher_controller_->SetLauncherControllerHelperForTest(
598 base::WrapUnique<LauncherControllerHelper>(helper));
599 }
600
AppendPrefValue(base::ListValue * pref_value,const std::string & extension_id)601 void AppendPrefValue(base::ListValue* pref_value,
602 const std::string& extension_id) {
603 base::DictionaryValue entry;
604 entry.SetKey(kPinnedAppsPrefAppIDKey, base::Value(extension_id));
605 pref_value->Append(std::move(entry));
606 }
607
InsertRemoveAllPinsChange(syncer::SyncChangeList * list)608 void InsertRemoveAllPinsChange(syncer::SyncChangeList* list) {
609 for (const auto& sync_peer : app_list_syncable_service_->sync_items()) {
610 sync_pb::EntitySpecifics specifics;
611 sync_pb::AppListSpecifics* app_list_specifics =
612 specifics.mutable_app_list();
613 app_list_specifics->set_item_id(sync_peer.first);
614 app_list_specifics->set_item_type(sync_pb::AppListSpecifics::TYPE_APP);
615 syncer::SyncData sync_data =
616 syncer::SyncData::CreateLocalData(sync_peer.first, "Test", specifics);
617 list->push_back(syncer::SyncChange(
618 FROM_HERE, syncer::SyncChange::ACTION_DELETE, sync_data));
619 }
620 }
621
GeneratePinPosition(int position)622 syncer::StringOrdinal GeneratePinPosition(int position) {
623 syncer::StringOrdinal ordinal_position =
624 syncer::StringOrdinal::CreateInitialOrdinal();
625 for (int i = 0; i < position; ++i)
626 ordinal_position = ordinal_position.CreateAfter();
627 return ordinal_position;
628 }
629
InsertPinChange(syncer::SyncChangeList * list,int position,bool add_pin_change,const std::string & app_id,syncer::SyncChange::SyncChangeType type)630 void InsertPinChange(syncer::SyncChangeList* list,
631 int position,
632 bool add_pin_change,
633 const std::string& app_id,
634 syncer::SyncChange::SyncChangeType type) {
635 sync_pb::EntitySpecifics specifics;
636 sync_pb::AppListSpecifics* app_list_specifics =
637 specifics.mutable_app_list();
638 app_list_specifics->set_item_id(app_id);
639 app_list_specifics->set_item_type(sync_pb::AppListSpecifics::TYPE_APP);
640 if (add_pin_change) {
641 if (position >= 0) {
642 app_list_specifics->set_item_pin_ordinal(
643 GeneratePinPosition(position).ToInternalValue());
644 } else {
645 app_list_specifics->set_item_pin_ordinal(std::string());
646 }
647 }
648 syncer::SyncData sync_data =
649 syncer::SyncData::CreateLocalData(app_id, "Test", specifics);
650 list->push_back(syncer::SyncChange(FROM_HERE, type, sync_data));
651 }
652
InsertAddPinChange(syncer::SyncChangeList * list,int position,const std::string & app_id)653 void InsertAddPinChange(syncer::SyncChangeList* list,
654 int position,
655 const std::string& app_id) {
656 InsertPinChange(list, position, true, app_id,
657 syncer::SyncChange::ACTION_ADD);
658 }
659
InsertUpdatePinChange(syncer::SyncChangeList * list,int position,const std::string & app_id)660 void InsertUpdatePinChange(syncer::SyncChangeList* list,
661 int position,
662 const std::string& app_id) {
663 InsertPinChange(list, position, true, app_id,
664 syncer::SyncChange::ACTION_UPDATE);
665 }
666
InsertRemovePinChange(syncer::SyncChangeList * list,const std::string & app_id)667 void InsertRemovePinChange(syncer::SyncChangeList* list,
668 const std::string& app_id) {
669 InsertPinChange(list, -1, true, app_id, syncer::SyncChange::ACTION_UPDATE);
670 }
671
InsertLegacyPinChange(syncer::SyncChangeList * list,const std::string & app_id)672 void InsertLegacyPinChange(syncer::SyncChangeList* list,
673 const std::string& app_id) {
674 InsertPinChange(list, -1, false, app_id, syncer::SyncChange::ACTION_UPDATE);
675 }
676
ResetPinModel()677 void ResetPinModel() {
678 syncer::SyncChangeList sync_list;
679 InsertRemoveAllPinsChange(&sync_list);
680 InsertAddPinChange(&sync_list, 0, kDummyAppId);
681 app_list_syncable_service_->ProcessSyncChanges(FROM_HERE, sync_list);
682 }
683
SendPinChanges(const syncer::SyncChangeList & sync_list,bool reset_pin_model)684 void SendPinChanges(const syncer::SyncChangeList& sync_list,
685 bool reset_pin_model) {
686 if (!reset_pin_model) {
687 app_list_syncable_service_->ProcessSyncChanges(FROM_HERE, sync_list);
688 } else {
689 syncer::SyncChangeList combined_sync_list;
690 InsertRemoveAllPinsChange(&combined_sync_list);
691 combined_sync_list.insert(combined_sync_list.end(), sync_list.begin(),
692 sync_list.end());
693 app_list_syncable_service_->ProcessSyncChanges(FROM_HERE,
694 combined_sync_list);
695 }
696 content::RunAllTasksUntilIdle();
697 }
698
699 // Set the index at which the chrome icon should be.
SetShelfChromeIconIndex(int index)700 void SetShelfChromeIconIndex(int index) {
701 DCHECK(
702 app_list_syncable_service_->GetPinPosition(extension_misc::kChromeAppId)
703 .IsValid());
704 syncer::StringOrdinal chrome_position;
705 chrome_position = index == 0 ? GeneratePinPosition(0).CreateBefore()
706 : GeneratePinPosition(index - 1).CreateBetween(
707 GeneratePinPosition(index));
708
709 syncer::SyncChangeList sync_list;
710 sync_pb::EntitySpecifics specifics;
711 sync_pb::AppListSpecifics* app_list_specifics =
712 specifics.mutable_app_list();
713 app_list_specifics->set_item_id(extension_misc::kChromeAppId);
714 app_list_specifics->set_item_type(sync_pb::AppListSpecifics::TYPE_APP);
715 app_list_specifics->set_item_pin_ordinal(chrome_position.ToInternalValue());
716 syncer::SyncData sync_data = syncer::SyncData::CreateLocalData(
717 extension_misc::kChromeAppId, "Test", specifics);
718 sync_list.push_back(syncer::SyncChange(
719 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data));
720 app_list_syncable_service_->ProcessSyncChanges(FROM_HERE, sync_list);
721 }
722
723 // Gets the IDs of the currently pinned app items.
GetPinnedAppIds(ChromeLauncherController * controller,std::vector<std::string> * app_ids)724 void GetPinnedAppIds(ChromeLauncherController* controller,
725 std::vector<std::string>* app_ids) {
726 app_ids->clear();
727 for (const auto& item : model_->items()) {
728 if (item.type == ash::TYPE_PINNED_APP)
729 app_ids->push_back(item.id.app_id);
730 }
731 }
732
733 // Get the setup of the currently shown launcher items in one string.
734 // Each pinned element will start with a big letter, each running but not
735 // pinned V1 app will start with a small letter and each running but not
736 // pinned V2 app will start with a '*' + small letter.
GetPinnedAppStatus()737 std::string GetPinnedAppStatus() {
738 std::string result;
739 for (int i = 0; i < model_->item_count(); i++) {
740 if (!result.empty())
741 result.append(", ");
742 switch (model_->items()[i].type) {
743 case ash::TYPE_APP: {
744 if (launcher_controller_->IsPlatformApp(model_->items()[i].id))
745 result += "*";
746 const std::string& app = model_->items()[i].id.app_id;
747 EXPECT_FALSE(launcher_controller_->IsAppPinned(app));
748 if (app == extension1_->id()) {
749 result += "app1";
750 } else if (app == extension2_->id()) {
751 result += "app2";
752 } else if (app == extension5_->id()) {
753 result += "app5";
754 } else if (app == extension6_->id()) {
755 result += "app6";
756 } else if (app == extension7_->id()) {
757 result += "app7";
758 } else if (app == extension8_->id()) {
759 result += "app8";
760 } else if (app == extension_gmail_app_->id()) {
761 result += "gmail";
762 } else if (app == extension_platform_app_->id()) {
763 result += "platform_app";
764 } else if (app == extension_doc_app_->id()) {
765 result += "doc";
766 } else if (app == extension_youtube_app_->id()) {
767 result += "youtube";
768 } else {
769 result += app_service_test_.GetAppName(app);
770 }
771 break;
772 }
773 case ash::TYPE_PINNED_APP: {
774 if (launcher_controller_->IsPlatformApp(model_->items()[i].id))
775 result += "*";
776 const std::string& app = model_->items()[i].id.app_id;
777 EXPECT_TRUE(launcher_controller_->IsAppPinned(app));
778 if (app == extension1_->id()) {
779 result += "App1";
780 } else if (app == extension2_->id()) {
781 result += "App2";
782 } else if (app == extension5_->id()) {
783 result += "App5";
784 } else if (app == extension6_->id()) {
785 result += "App6";
786 } else if (app == extension7_->id()) {
787 result += "App7";
788 } else if (app == extension8_->id()) {
789 result += "App8";
790 } else if (app == extension_gmail_app_->id()) {
791 result += "Gmail";
792 } else if (app == extension_doc_app_->id()) {
793 result += "Doc";
794 } else if (app == extension_youtube_app_->id()) {
795 result += "Youtube";
796 } else if (app == extension_files_app_->id()) {
797 result += "Files";
798 } else if (app == extension_platform_app_->id()) {
799 result += "Platform_App";
800 } else if (app == arc_support_host_->id()) {
801 result += "Play Store";
802 } else if (app == crostini::kCrostiniTerminalSystemAppId) {
803 result += "Terminal";
804 } else {
805 bool arc_app_found = false;
806 for (const auto& arc_app : arc_test_.fake_apps()) {
807 if (app == ArcAppTest::GetAppId(arc_app)) {
808 result += arc_app.name;
809 arc_app_found = true;
810 break;
811 }
812 }
813 if (!arc_app_found) {
814 result += app_service_test_.GetAppName(app);
815 }
816 }
817 break;
818 }
819 case ash::TYPE_BROWSER_SHORTCUT:
820 result += "Chrome";
821 break;
822 default:
823 result += "Unknown";
824 break;
825 }
826 }
827 return result;
828 }
829
830 // Remember the order of unpinned but running applications for the current
831 // user.
RememberUnpinnedRunningApplicationOrder()832 void RememberUnpinnedRunningApplicationOrder() {
833 launcher_controller_->RememberUnpinnedRunningApplicationOrder();
834 }
835
836 // Restore the order of running but unpinned applications for a given user.
RestoreUnpinnedRunningApplicationOrder(const AccountId & account_id)837 void RestoreUnpinnedRunningApplicationOrder(const AccountId& account_id) {
838 launcher_controller_->RestoreUnpinnedRunningApplicationOrder(
839 account_id.GetUserEmail());
840 }
841
SendListOfArcApps()842 void SendListOfArcApps() {
843 arc_test_.app_instance()->SendRefreshAppList(arc_test_.fake_apps());
844 }
845
SendListOfArcShortcuts()846 void SendListOfArcShortcuts() {
847 arc_test_.app_instance()->SendInstallShortcuts(arc_test_.fake_shortcuts());
848 }
849
UninstallArcApps()850 void UninstallArcApps() {
851 arc_test_.app_instance()->SendRefreshAppList(
852 std::vector<arc::mojom::AppInfo>());
853 }
854
855 // TODO(victorhsieh): Add test coverage for when ARC is started regardless
856 // Play Store opt-in status, and the followed opt-in and opt-out.
EnablePlayStore(bool enabled)857 void EnablePlayStore(bool enabled) {
858 arc::SetArcPlayStoreEnabledForProfile(profile(), enabled);
859 base::RunLoop().RunUntilIdle();
860 }
861
ValidateArcState(bool arc_enabled,bool arc_managed,arc::ArcSessionManager::State state,const std::string & pin_status)862 void ValidateArcState(bool arc_enabled,
863 bool arc_managed,
864 arc::ArcSessionManager::State state,
865 const std::string& pin_status) {
866 EXPECT_EQ(arc_enabled, arc::IsArcPlayStoreEnabledForProfile(profile()));
867 EXPECT_EQ(arc_managed,
868 arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile()));
869 EXPECT_EQ(state, arc_test_.arc_session_manager()->state());
870 EXPECT_EQ(pin_status, GetPinnedAppStatus());
871 }
872
873 // Creates app window and set optional ARC application id.
CreateArcWindow(const std::string & window_app_id)874 views::Widget* CreateArcWindow(const std::string& window_app_id) {
875 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
876 params.bounds = gfx::Rect(5, 5, 20, 20);
877 params.context = GetContext();
878 views::Widget* widget = new views::Widget();
879 widget->Init(std::move(params));
880 // Set ARC id before showing the window to be recognized in
881 // ArcAppWindowLauncherController.
882 exo::SetShellApplicationId(widget->GetNativeWindow(), window_app_id);
883 widget->Show();
884 widget->Activate();
885 return widget;
886 }
887
CreateAppInfo(const std::string & name,const std::string & activity,const std::string & package_name)888 arc::mojom::AppInfo CreateAppInfo(const std::string& name,
889 const std::string& activity,
890 const std::string& package_name) {
891 arc::mojom::AppInfo appinfo;
892 appinfo.name = name;
893 appinfo.package_name = package_name;
894 appinfo.activity = activity;
895 return appinfo;
896 }
897
AddArcAppAndShortcut(const arc::mojom::AppInfo & app_info)898 std::string AddArcAppAndShortcut(const arc::mojom::AppInfo& app_info) {
899 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
900 // Adding app to the prefs, and check that the app is accessible by id.
901 prefs->AddAppAndShortcut(
902 app_info.name, app_info.package_name, app_info.activity,
903 std::string() /* intent_uri */, std::string() /* icon_resource_id */,
904 false /* sticky */, true /* notifications_enabled */,
905 true /* app_ready */, false /* suspended */, false /* shortcut */,
906 true /* launchable */);
907 const std::string app_id =
908 ArcAppListPrefs::GetAppId(app_info.package_name, app_info.activity);
909 EXPECT_TRUE(prefs->GetApp(app_id));
910 app_service_test().FlushMojoCalls();
911 return app_id;
912 }
913
NotifyOnTaskCreated(const arc::mojom::AppInfo & appinfo,int32_t task_id)914 void NotifyOnTaskCreated(const arc::mojom::AppInfo& appinfo,
915 int32_t task_id) {
916 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
917 prefs->OnTaskCreated(task_id, appinfo.package_name, appinfo.activity,
918 appinfo.name, std::string());
919 }
920
921 // Creates a window with TYPE_APP shelf item type and the given app_id.
CreateShelfAppWindow(const std::string & app_id)922 views::Widget* CreateShelfAppWindow(const std::string& app_id) {
923 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
924 params.context = GetContext();
925 params.bounds = gfx::Rect(5, 5, 20, 20);
926 views::Widget* widget = new views::Widget();
927 widget->Init(std::move(params));
928
929 aura::Window* window = widget->GetNativeWindow();
930 const ash::ShelfID shelf_id(app_id);
931 window->SetProperty(ash::kShelfIDKey,
932 new std::string(shelf_id.Serialize()));
933 window->SetProperty<int>(ash::kShelfItemTypeKey, ash::TYPE_APP);
934 window->SetProperty(ash::kAppIDKey, new std::string(app_id));
935
936 widget->Show();
937 widget->Activate();
938 return widget;
939 }
940
NotifyOnTaskDestroyed(int32_t task_id)941 void NotifyOnTaskDestroyed(int32_t task_id) {
942 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
943 prefs->OnTaskDestroyed(task_id);
944 }
945
946 // Add extension and allow AppService async callbacks to run.
AddExtension(const Extension * extension)947 void AddExtension(const Extension* extension) {
948 extension_service_->AddExtension(extension);
949 app_service_test_.WaitForAppService();
950 }
951
952 // Remove extension and allow AppService async callbacks to run.
UnloadExtension(const std::string & extension_id,UnloadedExtensionReason reason)953 void UnloadExtension(const std::string& extension_id,
954 UnloadedExtensionReason reason) {
955 extension_service_->UnloadExtension(extension_id, reason);
956 app_service_test_.WaitForAppService();
957 }
958
app_service_test()959 apps::AppServiceTest& app_service_test() { return app_service_test_; }
960
961 // Needed for extension service & friends to work.
962 scoped_refptr<Extension> extension_chrome_;
963 scoped_refptr<Extension> extension1_;
964 scoped_refptr<Extension> extension2_;
965 scoped_refptr<Extension> extension5_;
966 scoped_refptr<Extension> extension6_;
967 scoped_refptr<Extension> extension7_;
968 scoped_refptr<Extension> extension8_;
969 scoped_refptr<Extension> extension_gmail_app_;
970 scoped_refptr<Extension> extension_doc_app_;
971 scoped_refptr<Extension> extension_youtube_app_;
972 scoped_refptr<Extension> extension_files_app_;
973 scoped_refptr<Extension> extension_platform_app_;
974 scoped_refptr<Extension> arc_support_host_;
975
976 ArcAppTest arc_test_;
977 bool auto_start_arc_test_ = false;
978 std::unique_ptr<ChromeLauncherController> launcher_controller_;
979 std::unique_ptr<ash::ShelfModel> model_;
980
981 // |item_delegate_manager_| owns |test_controller_|.
982 ash::ShelfItemDelegate* test_controller_ = nullptr;
983
984 extensions::ExtensionRegistry* extension_registry_ = nullptr;
985
986 extensions::ExtensionService* extension_service_ = nullptr;
987
988 app_list::AppListSyncableService* app_list_syncable_service_ = nullptr;
989
990 private:
CreateTestBrowserWindowAura()991 TestBrowserWindow* CreateTestBrowserWindowAura() {
992 std::unique_ptr<aura::Window> window(
993 new aura::Window(nullptr, aura::client::WINDOW_TYPE_NORMAL));
994 window->set_id(0);
995 window->Init(ui::LAYER_TEXTURED);
996 aura::client::ParentWindowWithContext(window.get(), GetContext(),
997 gfx::Rect(200, 200));
998
999 return new TestBrowserWindowAura(std::move(window));
1000 }
1001
1002 apps::AppServiceTest app_service_test_;
1003 };
1004
1005 class ChromeLauncherControllerWithArcTest
1006 : public ChromeLauncherControllerTest {
1007 protected:
ChromeLauncherControllerWithArcTest()1008 ChromeLauncherControllerWithArcTest() {
1009 auto_start_arc_test_ = true;
1010 }
1011
1012 ChromeLauncherControllerWithArcTest(
1013 const ChromeLauncherControllerWithArcTest&) = delete;
1014 ChromeLauncherControllerWithArcTest& operator=(
1015 const ChromeLauncherControllerWithArcTest&) = delete;
1016 ~ChromeLauncherControllerWithArcTest() override = default;
1017
SetUp()1018 void SetUp() override {
1019 // To prevent crash on test exit and pending decode request.
1020 ArcAppIcon::DisableSafeDecodingForTesting();
1021
1022 ChromeLauncherControllerTest::SetUp();
1023 }
1024 };
1025
1026 // Tests for feature SplitSettingsSync. Exists as a separate class because the
1027 // feature must be initialized before ChromeLauncherControllerTest::SetUp().
1028 class ChromeLauncherControllerSplitSettingsSyncTest
1029 : public ChromeLauncherControllerTest {
1030 public:
ChromeLauncherControllerSplitSettingsSyncTest()1031 ChromeLauncherControllerSplitSettingsSyncTest() {
1032 feature_list_.InitAndEnableFeature(chromeos::features::kSplitSettingsSync);
1033 }
1034 ~ChromeLauncherControllerSplitSettingsSyncTest() override = default;
1035
1036 private:
1037 base::test::ScopedFeatureList feature_list_;
1038 };
1039
1040 // Tests for Lacros integration. Exists as a separate class because the feature
1041 // must be initialized before ChromeLauncherControllerTest::SetUp().
1042 class ChromeLauncherControllerLacrosTest : public ChromeLauncherControllerTest {
1043 public:
ChromeLauncherControllerLacrosTest()1044 ChromeLauncherControllerLacrosTest() {
1045 feature_list_.InitAndEnableFeature(chromeos::features::kLacrosSupport);
1046 }
1047 ChromeLauncherControllerLacrosTest(
1048 const ChromeLauncherControllerLacrosTest&) = delete;
1049 ChromeLauncherControllerLacrosTest& operator=(
1050 const ChromeLauncherControllerLacrosTest&) = delete;
1051 ~ChromeLauncherControllerLacrosTest() override = default;
1052
1053 private:
1054 base::test::ScopedFeatureList feature_list_;
1055 };
1056
1057 class ChromeLauncherControllerExtendedShelfTest
1058 : public ChromeLauncherControllerWithArcTest {
1059 protected:
1060 ChromeLauncherControllerExtendedShelfTest() = default;
1061 ChromeLauncherControllerExtendedShelfTest(
1062 const ChromeLauncherControllerExtendedShelfTest&) = delete;
1063 ChromeLauncherControllerExtendedShelfTest& operator=(
1064 const ChromeLauncherControllerExtendedShelfTest&) = delete;
1065 ~ChromeLauncherControllerExtendedShelfTest() override = default;
1066
SetUp()1067 void SetUp() override {
1068 ChromeLauncherControllerWithArcTest::SetUp();
1069
1070 StartPrefSyncService(syncer::SyncDataList());
1071
1072 extension_service_->AddExtension(extension_gmail_app_.get());
1073 extension_service_->AddExtension(extension_doc_app_.get());
1074 extension_service_->AddExtension(extension_youtube_app_.get());
1075 extension_service_->AddExtension(extension_files_app_.get());
1076 extension_service_->AddExtension(arc_support_host_.get());
1077
1078 std::string error;
1079 base::DictionaryValue manifest;
1080 manifest.SetString(extensions::manifest_keys::kVersion, "1");
1081 manifest.SetInteger(extensions::manifest_keys::kManifestVersion, 2);
1082 manifest.SetString(extensions::manifest_keys::kDescription,
1083 "for testing pinned apps");
1084 // AppService checks the app's type. So set the
1085 // manifest_keys::kLaunchWebURL, so that the extension can get the type
1086 // from manifest value, and then AppService can get the extension's type.
1087 manifest.SetString(extensions::manifest_keys::kLaunchWebURL, kLaunchURL);
1088
1089 const std::vector<std::pair<std::string, std::string>> extra_extensions = {
1090 {extension_misc::kCalendarAppId, "Calendar"},
1091 {extension_misc::kGoogleSheetsAppId, "Sheets"},
1092 {extension_misc::kGoogleSlidesAppId, "Slides"},
1093 {extension_misc::kCameraAppId, "Camera"},
1094 {extension_misc::kGooglePhotosAppId, "Photos"},
1095 };
1096
1097 for (const auto& extension_id_name : extra_extensions) {
1098 manifest.SetString(extensions::manifest_keys::kName,
1099 extension_id_name.second);
1100 scoped_refptr<Extension> extension = Extension::Create(
1101 base::FilePath(), Manifest::UNPACKED, manifest, Extension::NO_FLAGS,
1102 extension_id_name.first, &error);
1103 extension_service_->AddExtension(extension.get());
1104 extra_extensions_.emplace_back(extension);
1105 }
1106 }
1107
1108 private:
1109 std::vector<scoped_refptr<Extension>> extra_extensions_;
1110 };
1111
1112 // A V1 windowed application.
1113 class V1App : public TestBrowserWindow {
1114 public:
V1App(Profile * profile,const std::string & app_name)1115 V1App(Profile* profile, const std::string& app_name) {
1116 Browser::CreateParams params = Browser::CreateParams::CreateForApp(
1117 kCrxAppPrefix + app_name, true /* trusted_source */, gfx::Rect(),
1118 profile, true);
1119 params.window = this;
1120 browser_ = std::unique_ptr<Browser>(Browser::Create(params));
1121 chrome::AddTabAt(browser_.get(), GURL(), 0, true);
1122 }
1123 V1App(const V1App&) = delete;
1124 V1App& operator=(const V1App&) = delete;
~V1App()1125 ~V1App() override {
1126 // close all tabs. Note that we do not need to destroy the browser itself.
1127 browser_->tab_strip_model()->CloseAllTabs();
1128 }
1129
browser()1130 Browser* browser() { return browser_.get(); }
1131
1132 private:
1133 // The associated browser with this app.
1134 std::unique_ptr<Browser> browser_;
1135 };
1136
1137 // A V2 application window created with an |extension| and for a |profile|.
1138 // Upon destruction it will properly close the application.
1139 class V2App {
1140 public:
V2App(Profile * profile,const extensions::Extension * extension,extensions::AppWindow::WindowType window_type=extensions::AppWindow::WINDOW_TYPE_DEFAULT)1141 V2App(Profile* profile,
1142 const extensions::Extension* extension,
1143 extensions::AppWindow::WindowType window_type =
1144 extensions::AppWindow::WINDOW_TYPE_DEFAULT)
1145 : creator_web_contents_(
1146 content::WebContentsTester::CreateTestWebContents(profile,
1147 nullptr)) {
1148 window_ = new extensions::AppWindow(profile, new ChromeAppDelegate(true),
1149 extension);
1150 extensions::AppWindow::CreateParams params;
1151 params.window_type = window_type;
1152 // Note: normally, the creator RFH is the background page of the
1153 // app/extension
1154 // calling chrome.app.window.create. For unit testing purposes, just passing
1155 // in a random RenderFrameHost is Good Enough™.
1156 window_->Init(GURL(std::string()),
1157 new extensions::AppWindowContentsImpl(window_),
1158 creator_web_contents_->GetMainFrame(), params);
1159 }
1160
1161 V2App(const V2App&) = delete;
1162 V2App& operator=(const V2App&) = delete;
~V2App()1163 virtual ~V2App() {
1164 content::WebContentsDestroyedWatcher destroyed_watcher(
1165 window_->web_contents());
1166 window_->GetBaseWindow()->Close();
1167 destroyed_watcher.Wait();
1168 }
1169
window()1170 extensions::AppWindow* window() { return window_; }
1171
1172 private:
1173 std::unique_ptr<content::WebContents> creator_web_contents_;
1174
1175 // The app window which represents the application. Note that the window
1176 // deletes itself asynchronously after window_->GetBaseWindow()->Close() gets
1177 // called.
1178 extensions::AppWindow* window_;
1179 };
1180
1181 // The testing framework to test multi profile scenarios.
1182 class MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest
1183 : public ChromeLauncherControllerTest {
1184 protected:
1185 MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest() = default;
1186 MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest(
1187 const MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest&) =
1188 delete;
1189 MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest& operator=(
1190 const MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest&) =
1191 delete;
1192 ~MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest() override =
1193 default;
1194
1195 // Overwrite the Setup function to enable multi profile and needed objects.
SetUp()1196 void SetUp() override {
1197 // Initialize the UserManager singleton to a fresh FakeUserManager instance.
1198 user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
1199 std::make_unique<chromeos::FakeChromeUserManager>());
1200
1201 // Initialize the rest.
1202 ChromeLauncherControllerTest::SetUp();
1203
1204 // Initialize WallpaperControllerClient.
1205 wallpaper_controller_client_ =
1206 std::make_unique<WallpaperControllerClient>();
1207 wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
1208
1209 // Ensure there are multiple profiles. User 0 is created during setup.
1210 CreateMultiUserProfile("user1");
1211 ASSERT_TRUE(SessionControllerClientImpl::IsMultiProfileAvailable());
1212 }
1213
TearDown()1214 void TearDown() override {
1215 ChromeLauncherControllerTest::TearDown();
1216 user_manager_enabler_.reset();
1217 wallpaper_controller_client_.reset();
1218
1219 // A Task is leaked if we don't destroy everything, then run the message
1220 // loop.
1221 base::RunLoop().RunUntilIdle();
1222 }
1223
MaybeStartWebAppProvider()1224 void MaybeStartWebAppProvider() override {
1225 // Deliberately do nothing; the provider is started in
1226 // CreateMultiUserProfile()
1227 }
1228
1229 // Creates a profile for a given |user_name|. Note that this class will keep
1230 // the ownership of the created object.
CreateMultiUserProfile(const std::string & user_name)1231 TestingProfile* CreateMultiUserProfile(const std::string& user_name) {
1232 const std::string email_string = user_name + "@example.com";
1233 const AccountId account_id(AccountId::FromUserEmail(email_string));
1234 // Add a user to the fake user manager.
1235 auto* user = GetFakeUserManager()->AddUser(account_id);
1236 ash_test_helper()->test_session_controller_client()->AddUserSession(
1237 user->GetDisplayEmail());
1238
1239 GetFakeUserManager()->LoginUser(account_id);
1240
1241 TestingProfile* profile =
1242 profile_manager()->CreateTestingProfile(account_id.GetUserEmail());
1243 EXPECT_TRUE(profile);
1244
1245 web_app::TestWebAppProvider::Get(profile)->Start();
1246
1247 // Remember the profile name so that we can destroy it upon destruction.
1248 created_profiles_[profile] = account_id.GetUserEmail();
1249 if (MultiUserWindowManagerHelper::GetInstance())
1250 MultiUserWindowManagerHelper::GetInstance()->AddUser(profile);
1251 if (launcher_controller_)
1252 launcher_controller_->AdditionalUserAddedToSession(profile);
1253 return profile;
1254 }
1255
1256 // Switch to another user.
SwitchActiveUser(const AccountId & account_id)1257 void SwitchActiveUser(const AccountId& account_id) {
1258 GetFakeUserManager()->SwitchActiveUser(account_id);
1259 ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
1260 ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_DISABLED);
1261 ash::MultiUserWindowManagerImpl::Get()->OnActiveUserSessionChanged(
1262 account_id);
1263 }
1264
1265 // Creates a browser with a |profile| and load a tab with a |title| and |url|.
CreateBrowserAndTabWithProfile(Profile * profile,const std::string & title,const std::string & url)1266 std::unique_ptr<Browser> CreateBrowserAndTabWithProfile(
1267 Profile* profile,
1268 const std::string& title,
1269 const std::string& url) {
1270 std::unique_ptr<Browser> browser(
1271 CreateBrowserWithTestWindowForProfile(profile));
1272 chrome::NewTab(browser.get());
1273
1274 browser->window()->Show();
1275 NavigateAndCommitActiveTabWithTitle(browser.get(), GURL(url),
1276 ASCIIToUTF16(title));
1277 return browser;
1278 }
1279
1280 // Creates a running V1 application.
1281 // Note that with the use of the launcher_controller_helper as done below,
1282 // this is only usable with a single v1 application.
CreateRunningV1App(Profile * profile,const std::string & app_name,const std::string & url)1283 V1App* CreateRunningV1App(Profile* profile,
1284 const std::string& app_name,
1285 const std::string& url) {
1286 V1App* v1_app = new V1App(profile, app_name);
1287 NavigateAndCommitActiveTabWithTitle(v1_app->browser(), GURL(url),
1288 base::string16());
1289 return v1_app;
1290 }
1291
1292 // Override BrowserWithTestWindowTest:
CreateProfile()1293 TestingProfile* CreateProfile() override {
1294 return CreateMultiUserProfile("user0");
1295 }
1296
1297 private:
1298 typedef std::map<Profile*, std::string> ProfileToNameMap;
1299
GetFakeUserManager()1300 chromeos::FakeChromeUserManager* GetFakeUserManager() {
1301 return static_cast<chromeos::FakeChromeUserManager*>(
1302 user_manager::UserManager::Get());
1303 }
1304
1305 std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
1306
1307 std::unique_ptr<WallpaperControllerClient> wallpaper_controller_client_;
1308
1309 TestWallpaperController test_wallpaper_controller_;
1310
1311 ProfileToNameMap created_profiles_;
1312 };
1313
1314 class ChromeLauncherControllerMultiProfileWithArcTest
1315 : public MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest {
1316 protected:
ChromeLauncherControllerMultiProfileWithArcTest()1317 ChromeLauncherControllerMultiProfileWithArcTest() {
1318 auto_start_arc_test_ = true;
1319 }
1320 ChromeLauncherControllerMultiProfileWithArcTest(
1321 const ChromeLauncherControllerMultiProfileWithArcTest&) = delete;
1322 ChromeLauncherControllerMultiProfileWithArcTest& operator=(
1323 const ChromeLauncherControllerMultiProfileWithArcTest&) = delete;
1324 ~ChromeLauncherControllerMultiProfileWithArcTest() override = default;
1325 };
1326
TEST_F(ChromeLauncherControllerTest,DefaultApps)1327 TEST_F(ChromeLauncherControllerTest, DefaultApps) {
1328 InitLauncherController();
1329
1330 // The model should only contain the browser shortcut item.
1331 EXPECT_EQ("Chrome", GetPinnedAppStatus());
1332
1333 // Pinning the non-default app. It should appear at the end. No default app
1334 // is currently installed.
1335 extension_service_->AddExtension(extension1_.get());
1336 launcher_controller_->PinAppWithID(extension1_->id());
1337 EXPECT_EQ("Chrome, App1", GetPinnedAppStatus());
1338
1339 // Install default apps in reverse order, compared how they are declared.
1340 // However pin positions should be in the order as they declared. Note,
1341 // default apps appear on shelf between manually pinned App1.
1342
1343 // Prefs are not yet synced. No default pin appears.
1344 AddExtension(extension_youtube_app_.get());
1345 EXPECT_EQ("Chrome, App1", GetPinnedAppStatus());
1346
1347 StartPrefSyncService(syncer::SyncDataList());
1348 EXPECT_EQ("Chrome, Youtube, App1", GetPinnedAppStatus());
1349
1350 AddExtension(extension_doc_app_.get());
1351 EXPECT_EQ("Chrome, Doc, Youtube, App1", GetPinnedAppStatus());
1352 AddExtension(extension_gmail_app_.get());
1353 EXPECT_EQ("Chrome, Gmail, Doc, Youtube, App1", GetPinnedAppStatus());
1354 AddExtension(extension_files_app_.get());
1355 EXPECT_EQ("Chrome, Files, Gmail, Doc, Youtube, App1", GetPinnedAppStatus());
1356 }
1357
TEST_F(ChromeLauncherControllerSplitSettingsSyncTest,DefaultApps)1358 TEST_F(ChromeLauncherControllerSplitSettingsSyncTest, DefaultApps) {
1359 // Simulate a user who opted out of sync.
1360 syncer::SyncService* sync_service =
1361 ProfileSyncServiceFactory::GetForProfile(profile());
1362 sync_service->GetUserSettings()->SetOsSyncFeatureEnabled(false);
1363
1364 InitLauncherController();
1365 EXPECT_EQ("Chrome", GetPinnedAppStatus());
1366
1367 // Simulate the default app loader installing some apps. Don't start the
1368 // pref sync service, because this user opted out of sync.
1369 AddExtension(extension_youtube_app_.get());
1370 AddExtension(extension_doc_app_.get());
1371 AddExtension(extension_gmail_app_.get());
1372
1373 // Default apps are pinned.
1374 EXPECT_EQ("Chrome, Gmail, Doc, Youtube", GetPinnedAppStatus());
1375 }
1376
TEST_F(ChromeLauncherControllerLacrosTest,LacrosPinnedByDefault)1377 TEST_F(ChromeLauncherControllerLacrosTest, LacrosPinnedByDefault) {
1378 // Checking to see if Lacros is allowed requires a user.
1379 auto user_manager = std::make_unique<chromeos::FakeChromeUserManager>();
1380 auto* fake_user_manager = user_manager.get();
1381 user_manager::ScopedUserManager scoped_user_manager(std::move(user_manager));
1382 AccountId account_id = AccountId::FromUserEmail("user@example.com");
1383 user_manager::User* user = fake_user_manager->AddUser(account_id);
1384 fake_user_manager->LoginUser(account_id);
1385
1386 TestingProfile::Builder profile_builder;
1387 profile_builder.SetProfileName(account_id.GetUserEmail());
1388 std::unique_ptr<TestingProfile> testing_profile = profile_builder.Build();
1389 chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
1390 user, testing_profile.get());
1391
1392 InitLauncherController();
1393 EXPECT_EQ("Chrome, Lacros", GetPinnedAppStatus());
1394 }
1395
TEST_F(ChromeLauncherControllerExtendedShelfTest,ExtendedShelfDefault)1396 TEST_F(ChromeLauncherControllerExtendedShelfTest, ExtendedShelfDefault) {
1397 base::test::ScopedFeatureList scoped_feature_list;
1398 scoped_feature_list.InitAndEnableFeatureWithParameters(
1399 kEnableExtendedShelfLayout,
1400 {std::pair<std::string, std::string>("app_count", "0")});
1401
1402 InitLauncherController();
1403 EXPECT_EQ("Chrome, Files, Gmail, Doc, Youtube, Play Store",
1404 GetPinnedAppStatus());
1405 }
1406
TEST_F(ChromeLauncherControllerExtendedShelfTest,ExtendedShelf7Apps)1407 TEST_F(ChromeLauncherControllerExtendedShelfTest, ExtendedShelf7Apps) {
1408 base::test::ScopedFeatureList scoped_feature_list;
1409 scoped_feature_list.InitAndEnableFeatureWithParameters(
1410 kEnableExtendedShelfLayout,
1411 {std::pair<std::string, std::string>("app_count", "7")});
1412
1413 InitLauncherController();
1414 EXPECT_EQ("Chrome, Files, Gmail, Doc, Photos, Youtube, Play Store",
1415 GetPinnedAppStatus());
1416 }
1417
TEST_F(ChromeLauncherControllerExtendedShelfTest,ExtendedShelf10Apps)1418 TEST_F(ChromeLauncherControllerExtendedShelfTest, ExtendedShelf10Apps) {
1419 base::test::ScopedFeatureList scoped_feature_list;
1420 scoped_feature_list.InitAndEnableFeatureWithParameters(
1421 kEnableExtendedShelfLayout,
1422 {std::pair<std::string, std::string>("app_count", "10")});
1423
1424 InitLauncherController();
1425 EXPECT_EQ(
1426 "Chrome, Files, Gmail, Calendar, Doc, Sheets, Slides, Camera, Photos, "
1427 "Play Store",
1428 GetPinnedAppStatus());
1429 }
1430
TEST_F(ChromeLauncherControllerExtendedShelfTest,UpgradeFromDefault)1431 TEST_F(ChromeLauncherControllerExtendedShelfTest, UpgradeFromDefault) {
1432 InitLauncherController();
1433 EXPECT_EQ("Chrome, Files, Gmail, Doc, Youtube, Play Store",
1434 GetPinnedAppStatus());
1435
1436 // Upgrade happens only in case default layout is active.
1437 base::test::ScopedFeatureList scoped_feature_list;
1438 scoped_feature_list.InitAndEnableFeatureWithParameters(
1439 kEnableExtendedShelfLayout,
1440 {std::pair<std::string, std::string>("app_count", "10")});
1441
1442 // Trigger layout update, app_id does not matter.
1443 AddExtension(extension1_.get());
1444
1445 EXPECT_EQ(
1446 "Chrome, Files, Gmail, Calendar, Doc, Sheets, Slides, Camera, Photos, "
1447 "Play Store",
1448 GetPinnedAppStatus());
1449 }
1450
TEST_F(ChromeLauncherControllerExtendedShelfTest,NoDefaultAfterExperimental)1451 TEST_F(ChromeLauncherControllerExtendedShelfTest, NoDefaultAfterExperimental) {
1452 const std::string expectations =
1453 "Chrome, Files, Gmail, Calendar, Doc, Sheets, "
1454 "Slides, Camera, Photos, Play Store";
1455 {
1456 base::test::ScopedFeatureList scoped_feature_list;
1457 scoped_feature_list.InitAndEnableFeatureWithParameters(
1458 kEnableExtendedShelfLayout,
1459 {std::pair<std::string, std::string>("app_count", "10")});
1460
1461 InitLauncherController();
1462 EXPECT_EQ(expectations, GetPinnedAppStatus());
1463
1464 // Trigger layout update, app_id does not matter. Experiment is still
1465 // forced.
1466 AddExtension(extension1_.get());
1467
1468 // Youtube is included into default but not to expermenetal. Refreshing
1469 // should not affect layout.
1470 EXPECT_EQ(expectations, GetPinnedAppStatus());
1471 }
1472
1473 // Re-update but experiment is off now. No change to layout.
1474 AddExtension(extension2_.get());
1475 EXPECT_EQ(expectations, GetPinnedAppStatus());
1476 }
1477
TEST_F(ChromeLauncherControllerExtendedShelfTest,NoUpgradeFromNonDefault)1478 TEST_F(ChromeLauncherControllerExtendedShelfTest, NoUpgradeFromNonDefault) {
1479 InitLauncherController();
1480 launcher_controller_->UnpinAppWithID(extension_misc::kYoutubeAppId);
1481 EXPECT_EQ("Chrome, Files, Gmail, Doc, Play Store", GetPinnedAppStatus());
1482
1483 // Upgrade does not happen due to default pin layout change.
1484 base::test::ScopedFeatureList scoped_feature_list;
1485 scoped_feature_list.InitAndEnableFeatureWithParameters(
1486 kEnableExtendedShelfLayout,
1487 {std::pair<std::string, std::string>("app_count", "10")});
1488
1489 // Trigger layout update, app_id does not matter.
1490 AddExtension(extension1_.get());
1491
1492 EXPECT_EQ("Chrome, Files, Gmail, Doc, Play Store", GetPinnedAppStatus());
1493 }
1494
TEST_F(ChromeLauncherControllerWithArcTest,ArcAppPinCrossPlatformWorkflow)1495 TEST_F(ChromeLauncherControllerWithArcTest, ArcAppPinCrossPlatformWorkflow) {
1496 // Work on ARC disabled platform first.
1497 const std::string arc_app_id1 =
1498 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]);
1499 const std::string arc_app_id2 =
1500 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]);
1501 const std::string arc_app_id3 =
1502 ArcAppTest::GetAppId(arc_test_.fake_apps()[2]);
1503
1504 InitLauncherController();
1505
1506 extension_service_->AddExtension(extension1_.get());
1507 extension_service_->AddExtension(extension2_.get());
1508 extension_service_->AddExtension(extension_gmail_app_.get());
1509 app_service_test().WaitForAppService();
1510
1511 // extension 1, 3 are pinned by user
1512 syncer::SyncChangeList sync_list;
1513 InsertAddPinChange(&sync_list, 0, extension1_->id());
1514 InsertAddPinChange(&sync_list, 1, arc_app_id1);
1515 InsertAddPinChange(&sync_list, 2, extension2_->id());
1516 InsertAddPinChange(&sync_list, 3, arc_app_id2);
1517 InsertAddPinChange(&sync_list, 4, extension_gmail_app_->id());
1518 SendPinChanges(sync_list, true);
1519 SetShelfChromeIconIndex(1);
1520
1521 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1522 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id1));
1523 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
1524 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id2));
1525 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1526 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id3));
1527 EXPECT_EQ("App1, Chrome, App2, Gmail", GetPinnedAppStatus());
1528
1529 // Persist pin state, we don't have active pin for ARC apps yet, but pin
1530 // model should have it.
1531 syncer::SyncDataList copy_sync_list =
1532 app_list_syncable_service_->GetAllSyncDataForTesting();
1533
1534 ResetLauncherController();
1535 SendPinChanges(syncer::SyncChangeList(), true);
1536 StopAppSyncService();
1537 EXPECT_EQ(0U, app_list_syncable_service_->sync_items().size());
1538
1539 // Move to ARC enabled platform, restart syncing with stored data.
1540 StartAppSyncService(copy_sync_list);
1541 RecreateLauncherController()->Init();
1542
1543 // Pins must be automatically updated.
1544 SendListOfArcApps();
1545 app_service_test().WaitForAppService();
1546 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1547 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id1));
1548 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
1549 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id2));
1550 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1551 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id3));
1552
1553 EXPECT_EQ("App1, Chrome, Fake App 0, App2, Fake App 1, Gmail",
1554 GetPinnedAppStatus());
1555
1556 // Now move pins on ARC enabled platform.
1557 model_->Move(0, 3);
1558 model_->Move(2, 0);
1559 model_->Move(2, 4);
1560 model_->Move(3, 1);
1561 EXPECT_EQ("App2, Fake App 1, Chrome, App1, Fake App 0, Gmail",
1562 GetPinnedAppStatus());
1563
1564 app_service_test().WaitForAppService();
1565 copy_sync_list = app_list_syncable_service_->GetAllSyncDataForTesting();
1566
1567 ResetLauncherController();
1568 ResetPinModel();
1569
1570 SendPinChanges(syncer::SyncChangeList(), true);
1571 StopAppSyncService();
1572 EXPECT_EQ(0U, app_list_syncable_service_->sync_items().size());
1573
1574 // Move back to ARC disabled platform.
1575 EnablePlayStore(false);
1576 StartAppSyncService(copy_sync_list);
1577 RecreateLauncherController()->Init();
1578
1579 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1580 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id1));
1581 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
1582 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id2));
1583 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1584 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id3));
1585 EXPECT_EQ("App2, Chrome, App1, Gmail", GetPinnedAppStatus());
1586
1587 // Now move/remove pins on ARC disabled platform.
1588 model_->Move(3, 1);
1589 launcher_controller_->UnpinAppWithID(extension2_->id());
1590 EXPECT_EQ("Gmail, Chrome, App1", GetPinnedAppStatus());
1591 EnablePlayStore(true);
1592
1593 SendListOfArcApps();
1594 app_service_test().WaitForAppService();
1595
1596 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1597 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id1));
1598 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
1599 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id2));
1600 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1601 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id3));
1602 EXPECT_EQ("Fake App 1, Gmail, Chrome, App1, Fake App 0",
1603 GetPinnedAppStatus());
1604 }
1605
1606 // Ensure correct merging of policy pinned apps and user pinned apps.
TEST_F(ChromeLauncherControllerTest,MergePolicyAndUserPrefPinnedApps)1607 TEST_F(ChromeLauncherControllerTest, MergePolicyAndUserPrefPinnedApps) {
1608 InitLauncherController();
1609
1610 extension_service_->AddExtension(extension1_.get());
1611 extension_service_->AddExtension(extension_gmail_app_.get());
1612 extension_service_->AddExtension(extension_doc_app_.get());
1613 extension_service_->AddExtension(extension5_.get());
1614 // extension 1, 3 are pinned by user
1615 syncer::SyncChangeList sync_list;
1616 InsertAddPinChange(&sync_list, 0, extension1_->id());
1617 InsertAddPinChange(&sync_list, 1, extension_misc::kChromeAppId);
1618 InsertAddPinChange(&sync_list, 2, extension_gmail_app_->id());
1619 SendPinChanges(sync_list, true);
1620
1621 base::ListValue policy_value;
1622 // extension 2 4 are pinned by policy
1623 AppendPrefValue(&policy_value, extension2_->id());
1624 AppendPrefValue(&policy_value, extension_doc_app_->id());
1625 profile()->GetTestingPrefService()->SetManagedPref(
1626 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy());
1627
1628 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1629 // 2 is not pinned as it's not installed
1630 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
1631 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1632 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_doc_app_->id()));
1633 // install extension 2 and check
1634 AddExtension(extension2_.get());
1635 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
1636
1637 // Check user can manually pin or unpin these apps
1638 EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE,
1639 GetPinnableForAppID(extension1_->id(), profile()));
1640 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
1641 GetPinnableForAppID(extension2_->id(), profile()));
1642 EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE,
1643 GetPinnableForAppID(extension_gmail_app_->id(), profile()));
1644 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
1645 GetPinnableForAppID(extension_doc_app_->id(), profile()));
1646
1647 // Check the order of shelf pinned apps
1648 EXPECT_EQ("App2, Doc, App1, Chrome, Gmail", GetPinnedAppStatus());
1649 }
1650
1651 // Check that the restauration of launcher items is happening in the same order
1652 // as the user has pinned them (on another system) when they are synced reverse
1653 // order.
TEST_F(ChromeLauncherControllerTest,RestoreDefaultAppsReverseOrder)1654 TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsReverseOrder) {
1655 InitLauncherController();
1656
1657 syncer::SyncChangeList sync_list;
1658 InsertAddPinChange(&sync_list, 0, extension1_->id());
1659 InsertAddPinChange(&sync_list, 1, extension2_->id());
1660 InsertAddPinChange(&sync_list, 2, extension_gmail_app_->id());
1661 SendPinChanges(sync_list, true);
1662
1663 // The model should only contain the browser shortcut and app list items.
1664 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1665 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
1666 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1667 EXPECT_EQ("Chrome", GetPinnedAppStatus());
1668
1669 // Installing |extension_gmail_app_| should add it to the shelf - behind the
1670 // chrome icon.
1671 ash::ShelfItem item;
1672 AddExtension(extension_gmail_app_.get());
1673 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1674 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
1675 EXPECT_EQ("Chrome, Gmail", GetPinnedAppStatus());
1676
1677 // Installing |extension2_| should add it to the launcher - behind the
1678 // chrome icon, but in first location.
1679 AddExtension(extension2_.get());
1680 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1681 EXPECT_EQ("Chrome, App2, Gmail", GetPinnedAppStatus());
1682
1683 // Installing |extension1_| should add it to the launcher - behind the
1684 // chrome icon, but in first location.
1685 AddExtension(extension1_.get());
1686 EXPECT_EQ("Chrome, App1, App2, Gmail", GetPinnedAppStatus());
1687 }
1688
1689 // Check that the restauration of launcher items is happening in the same order
1690 // as the user has pinned them (on another system) when they are synced random
1691 // order.
TEST_F(ChromeLauncherControllerTest,RestoreDefaultAppsRandomOrder)1692 TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsRandomOrder) {
1693 InitLauncherController();
1694
1695 syncer::SyncChangeList sync_list;
1696 InsertAddPinChange(&sync_list, 0, extension1_->id());
1697 InsertAddPinChange(&sync_list, 1, extension2_->id());
1698 InsertAddPinChange(&sync_list, 2, extension_gmail_app_->id());
1699 SendPinChanges(sync_list, true);
1700
1701 // The model should only contain the browser shortcut and app list items.
1702 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1703 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
1704 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1705 EXPECT_EQ("Chrome", GetPinnedAppStatus());
1706
1707 // Installing |extension2_| should add it to the launcher - behind the
1708 // chrome icon.
1709 AddExtension(extension2_.get());
1710 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1711 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1712 EXPECT_EQ("Chrome, App2", GetPinnedAppStatus());
1713
1714 // Installing |extension1_| should add it to the launcher - behind the
1715 // chrome icon, but in first location.
1716 AddExtension(extension1_.get());
1717 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1718 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
1719
1720 // Installing |extension_gmail_app_| should add it to the launcher - behind
1721 // the chrome icon, but in first location.
1722 AddExtension(extension_gmail_app_.get());
1723 EXPECT_EQ("Chrome, App1, App2, Gmail", GetPinnedAppStatus());
1724 }
1725
1726 // Check that the restauration of launcher items is happening in the same order
1727 // as the user has pinned / moved them (on another system) when they are synced
1728 // random order - including the chrome icon.
TEST_F(ChromeLauncherControllerTest,RestoreDefaultAppsRandomOrderChromeMoved)1729 TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsRandomOrderChromeMoved) {
1730 InitLauncherController();
1731
1732 syncer::SyncChangeList sync_list;
1733 InsertAddPinChange(&sync_list, 0, extension1_->id());
1734 InsertAddPinChange(&sync_list, 1, extension_misc::kChromeAppId);
1735 InsertAddPinChange(&sync_list, 2, extension2_->id());
1736 InsertAddPinChange(&sync_list, 3, extension_gmail_app_->id());
1737 SendPinChanges(sync_list, true);
1738
1739 // The model should only contain the browser shortcut and app list items.
1740 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1741 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
1742 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1743 EXPECT_EQ("Chrome", GetPinnedAppStatus());
1744
1745 // Installing |extension2_| should add it to the shelf - behind the
1746 // chrome icon.
1747 ash::ShelfItem item;
1748 AddExtension(extension2_.get());
1749 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1750 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1751 EXPECT_EQ("Chrome, App2", GetPinnedAppStatus());
1752
1753 // Installing |extension1_| should add it to the launcher - behind the
1754 // chrome icon, but in first location.
1755 AddExtension(extension1_.get());
1756 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
1757 EXPECT_EQ("App1, Chrome, App2", GetPinnedAppStatus());
1758
1759 // Installing |extension_gmail_app_| should add it to the launcher - behind
1760 // the chrome icon, but in first location.
1761 AddExtension(extension_gmail_app_.get());
1762 EXPECT_EQ("App1, Chrome, App2, Gmail", GetPinnedAppStatus());
1763 }
1764
1765 // Check that syncing to a different state does the correct thing.
TEST_F(ChromeLauncherControllerTest,RestoreDefaultAppsResyncOrder)1766 TEST_F(ChromeLauncherControllerTest, RestoreDefaultAppsResyncOrder) {
1767 InitLauncherController();
1768
1769 syncer::SyncChangeList sync_list0;
1770 InsertAddPinChange(&sync_list0, 0, extension1_->id());
1771 InsertAddPinChange(&sync_list0, 1, extension2_->id());
1772 InsertAddPinChange(&sync_list0, 2, extension_gmail_app_->id());
1773 SendPinChanges(sync_list0, true);
1774
1775 // The shelf layout has always one static item at the beginning (App List).
1776 AddExtension(extension2_.get());
1777 EXPECT_EQ("Chrome, App2", GetPinnedAppStatus());
1778 AddExtension(extension1_.get());
1779 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
1780 AddExtension(extension_gmail_app_.get());
1781 EXPECT_EQ("Chrome, App1, App2, Gmail", GetPinnedAppStatus());
1782
1783 // Change the order with increasing chrome position and decreasing position.
1784 syncer::SyncChangeList sync_list1;
1785 InsertAddPinChange(&sync_list1, 0, extension_gmail_app_->id());
1786 InsertAddPinChange(&sync_list1, 1, extension1_->id());
1787 InsertAddPinChange(&sync_list1, 2, extension2_->id());
1788 InsertAddPinChange(&sync_list1, 3, extension_misc::kChromeAppId);
1789 SendPinChanges(sync_list1, true);
1790 EXPECT_EQ("Gmail, App1, App2, Chrome", GetPinnedAppStatus());
1791
1792 syncer::SyncChangeList sync_list2;
1793 InsertAddPinChange(&sync_list2, 0, extension2_->id());
1794 InsertAddPinChange(&sync_list2, 1, extension_gmail_app_->id());
1795 InsertAddPinChange(&sync_list2, 2, extension_misc::kChromeAppId);
1796 InsertAddPinChange(&sync_list2, 3, extension1_->id());
1797 SendPinChanges(sync_list2, true);
1798 EXPECT_EQ("App2, Gmail, Chrome, App1", GetPinnedAppStatus());
1799
1800 // Check that the chrome icon can also be at the first possible location.
1801 syncer::SyncChangeList sync_list3;
1802 InsertAddPinChange(&sync_list3, 0, extension_gmail_app_->id());
1803 InsertAddPinChange(&sync_list3, 1, extension2_->id());
1804 InsertAddPinChange(&sync_list3, 2, extension1_->id());
1805 SendPinChanges(sync_list3, true);
1806 EXPECT_EQ("Chrome, Gmail, App2, App1", GetPinnedAppStatus());
1807
1808 // Check that unloading of extensions works as expected.
1809 UnloadExtension(extension1_->id(), UnloadedExtensionReason::UNINSTALL);
1810 EXPECT_EQ("Chrome, Gmail, App2", GetPinnedAppStatus());
1811
1812 UnloadExtension(extension2_->id(), UnloadedExtensionReason::UNINSTALL);
1813 EXPECT_EQ("Chrome, Gmail", GetPinnedAppStatus());
1814
1815 // Check that an update of an extension does not crash the system.
1816 UnloadExtension(extension_gmail_app_->id(), UnloadedExtensionReason::UPDATE);
1817 EXPECT_EQ("Chrome, Gmail", GetPinnedAppStatus());
1818 }
1819
1820 // Test the V1 app interaction flow: run it, activate it, close it.
TEST_F(ChromeLauncherControllerTest,V1AppRunActivateClose)1821 TEST_F(ChromeLauncherControllerTest, V1AppRunActivateClose) {
1822 InitLauncherController();
1823 // The model should only contain the browser shortcut item.
1824 EXPECT_EQ(1, model_->item_count());
1825 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1826 EXPECT_EQ(nullptr,
1827 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1828
1829 // Reporting that the app is running should create a new shelf item.
1830 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING);
1831 EXPECT_EQ(2, model_->item_count());
1832 EXPECT_EQ(ash::TYPE_APP, model_->items()[1].type);
1833 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[1].status);
1834 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1835 EXPECT_NE(nullptr,
1836 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1837
1838 // Reporting that the app is running again should have no effect.
1839 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING);
1840 EXPECT_EQ(2, model_->item_count());
1841 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[1].status);
1842
1843 // Reporting that the app is closed should remove its shelf item.
1844 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED);
1845 EXPECT_EQ(1, model_->item_count());
1846 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1847 EXPECT_EQ(nullptr,
1848 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1849
1850 // Reporting that the app is closed again should have no effect.
1851 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED);
1852 EXPECT_EQ(1, model_->item_count());
1853 }
1854
1855 // Test the V1 app interaction flow: pin it, run it, close it, unpin it.
TEST_F(ChromeLauncherControllerTest,V1AppPinRunCloseUnpin)1856 TEST_F(ChromeLauncherControllerTest, V1AppPinRunCloseUnpin) {
1857 InitLauncherController();
1858 // The model should only contain the browser shortcut.
1859 EXPECT_EQ(1, model_->item_count());
1860 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1861 EXPECT_EQ(nullptr,
1862 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1863
1864 // Pinning the app should create a new shelf item.
1865 launcher_controller_->PinAppWithID(extension1_->id());
1866 EXPECT_EQ(2, model_->item_count());
1867 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
1868 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[1].status);
1869 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1870 EXPECT_NE(nullptr,
1871 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1872
1873 // Reporting that the app is running should just update the existing item.
1874 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING);
1875 EXPECT_EQ(2, model_->item_count());
1876 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
1877 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[1].status);
1878 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1879 EXPECT_NE(nullptr,
1880 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1881
1882 // Reporting that the app is closed should just update the existing item.
1883 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED);
1884 EXPECT_EQ(2, model_->item_count());
1885 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
1886 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[1].status);
1887 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1888 EXPECT_NE(nullptr,
1889 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1890
1891 // Unpinning the app should remove its shelf item.
1892 launcher_controller_->UnpinAppWithID(extension1_->id());
1893 EXPECT_EQ(1, model_->item_count());
1894 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1895 EXPECT_EQ(nullptr,
1896 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1897 }
1898
1899 // Test the V1 app interaction flow: run it, pin it, close it, unpin it.
TEST_F(ChromeLauncherControllerTest,V1AppRunPinCloseUnpin)1900 TEST_F(ChromeLauncherControllerTest, V1AppRunPinCloseUnpin) {
1901 InitLauncherController();
1902
1903 // The model should only contain the browser shortcut.
1904 EXPECT_EQ(1, model_->item_count());
1905 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1906 EXPECT_EQ(nullptr,
1907 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1908
1909 // Reporting that the app is running should create a new shelf item.
1910 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING);
1911 EXPECT_EQ(2, model_->item_count());
1912 EXPECT_EQ(ash::TYPE_APP, model_->items()[1].type);
1913 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[1].status);
1914 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1915 EXPECT_NE(nullptr,
1916 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1917
1918 // Pinning the app should just update the existing item.
1919 launcher_controller_->PinAppWithID(extension1_->id());
1920 EXPECT_EQ(2, model_->item_count());
1921 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
1922 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[1].status);
1923 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1924 EXPECT_NE(nullptr,
1925 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1926
1927 // Reporting that the app is closed should just update the existing item.
1928 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED);
1929 EXPECT_EQ(2, model_->item_count());
1930 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
1931 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[1].status);
1932 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1933 EXPECT_NE(nullptr,
1934 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1935
1936 // Unpinning the app should remove its shelf item.
1937 launcher_controller_->UnpinAppWithID(extension1_->id());
1938 EXPECT_EQ(1, model_->item_count());
1939 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1940 EXPECT_EQ(nullptr,
1941 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1942 }
1943
1944 // Test the V1 app interaction flow: pin it, run it, unpin it, close it.
TEST_F(ChromeLauncherControllerTest,V1AppPinRunUnpinClose)1945 TEST_F(ChromeLauncherControllerTest, V1AppPinRunUnpinClose) {
1946 InitLauncherController();
1947
1948 // The model should only contain the browser shortcut item.
1949 EXPECT_EQ(1, model_->item_count());
1950 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1951 EXPECT_EQ(nullptr,
1952 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1953
1954 // Pinning the app should create a new shelf item.
1955 launcher_controller_->PinAppWithID(extension1_->id());
1956 EXPECT_EQ(2, model_->item_count());
1957 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
1958 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[1].status);
1959 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1960 EXPECT_NE(nullptr,
1961 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1962
1963 // Reporting that the app is running should just update the existing item.
1964 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING);
1965 EXPECT_EQ(2, model_->item_count());
1966 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
1967 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[1].status);
1968 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
1969 EXPECT_NE(nullptr,
1970 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1971
1972 // Unpinning the app should just update the existing item.
1973 launcher_controller_->UnpinAppWithID(extension1_->id());
1974 EXPECT_EQ(2, model_->item_count());
1975 EXPECT_EQ(ash::TYPE_APP, model_->items()[1].type);
1976 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[1].status);
1977 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1978 EXPECT_NE(nullptr,
1979 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1980
1981 // Reporting that the app is closed should remove its shelf item.
1982 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED);
1983 EXPECT_EQ(1, model_->item_count());
1984 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
1985 EXPECT_EQ(nullptr,
1986 launcher_controller_->GetItem(ash::ShelfID(extension1_->id())));
1987 }
1988
1989 // Ensure unpinned V1 app ordering is properly restored after user changes.
TEST_F(ChromeLauncherControllerTest,CheckRunningV1AppOrder)1990 TEST_F(ChromeLauncherControllerTest, CheckRunningV1AppOrder) {
1991 InitLauncherController();
1992
1993 // The model should only contain the browser shortcut item.
1994 EXPECT_EQ(1, model_->item_count());
1995
1996 // Add a few running applications.
1997 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_RUNNING);
1998 launcher_controller_->SetV1AppStatus(extension2_->id(), ash::STATUS_RUNNING);
1999 launcher_controller_->SetV1AppStatus(extension_gmail_app_->id(),
2000 ash::STATUS_RUNNING);
2001 EXPECT_EQ(4, model_->item_count());
2002 // Note that this not only checks the order of applications but also the
2003 // running type.
2004 EXPECT_EQ("Chrome, app1, app2, gmail", GetPinnedAppStatus());
2005
2006 // Remember the current order of applications for the current user.
2007 const AccountId& current_account_id =
2008 multi_user_util::GetAccountIdFromProfile(profile());
2009 RememberUnpinnedRunningApplicationOrder();
2010
2011 // Switch some items and check that restoring a user which was not yet
2012 // remembered changes nothing.
2013 model_->Move(1, 2);
2014 EXPECT_EQ("Chrome, app2, app1, gmail", GetPinnedAppStatus());
2015 const AccountId second_fake_account_id(
2016 AccountId::FromUserEmail("second-fake-user@fake.com"));
2017 RestoreUnpinnedRunningApplicationOrder(second_fake_account_id);
2018 EXPECT_EQ("Chrome, app2, app1, gmail", GetPinnedAppStatus());
2019
2020 // Restoring the stored user should however do the right thing.
2021 RestoreUnpinnedRunningApplicationOrder(current_account_id);
2022 EXPECT_EQ("Chrome, app1, app2, gmail", GetPinnedAppStatus());
2023
2024 // Switch again some items and even delete one - making sure that the missing
2025 // item gets properly handled.
2026 model_->Move(2, 3);
2027 launcher_controller_->SetV1AppStatus(extension1_->id(), ash::STATUS_CLOSED);
2028 EXPECT_EQ("Chrome, gmail, app2", GetPinnedAppStatus());
2029 RestoreUnpinnedRunningApplicationOrder(current_account_id);
2030 EXPECT_EQ("Chrome, app2, gmail", GetPinnedAppStatus());
2031
2032 // Check that removing more items does not crash and changes nothing.
2033 launcher_controller_->SetV1AppStatus(extension2_->id(), ash::STATUS_CLOSED);
2034 RestoreUnpinnedRunningApplicationOrder(current_account_id);
2035 EXPECT_EQ("Chrome, gmail", GetPinnedAppStatus());
2036 launcher_controller_->SetV1AppStatus(extension_gmail_app_->id(),
2037 ash::STATUS_CLOSED);
2038 RestoreUnpinnedRunningApplicationOrder(current_account_id);
2039 EXPECT_EQ("Chrome", GetPinnedAppStatus());
2040 }
2041
TEST_F(ChromeLauncherControllerWithArcTest,ArcDeferredLaunch)2042 TEST_F(ChromeLauncherControllerWithArcTest, ArcDeferredLaunch) {
2043 InitLauncherController();
2044
2045 const arc::mojom::AppInfo& app1 = arc_test_.fake_apps()[0];
2046 const arc::mojom::AppInfo& app2 = arc_test_.fake_apps()[1];
2047 const arc::mojom::AppInfo& gmail = arc_test_.fake_apps()[2];
2048 const arc::mojom::ShortcutInfo& shortcut = arc_test_.fake_shortcuts()[0];
2049 const std::string arc_app_id1 = ArcAppTest::GetAppId(app1);
2050 const std::string arc_app_id2 = ArcAppTest::GetAppId(app2);
2051 const std::string arc_app_id3 = ArcAppTest::GetAppId(gmail);
2052 const std::string arc_shortcut_id = ArcAppTest::GetAppId(shortcut);
2053
2054 SendListOfArcApps();
2055 SendListOfArcShortcuts();
2056
2057 arc_test_.StopArcInstance();
2058
2059 const ash::ShelfID shelf_id_app_1(arc_app_id1);
2060 const ash::ShelfID shelf_id_app_2(arc_app_id2);
2061 const ash::ShelfID shelf_id_app_3(arc_app_id3);
2062 const ash::ShelfID shelf_id_shortcut(arc_shortcut_id);
2063 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_app_1));
2064 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_app_2));
2065 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_app_3));
2066 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_shortcut));
2067
2068 arc::LaunchApp(profile(), arc_app_id1, ui::EF_LEFT_MOUSE_BUTTON,
2069 arc::UserInteractionType::NOT_USER_INITIATED);
2070 arc::LaunchApp(profile(), arc_app_id1, ui::EF_LEFT_MOUSE_BUTTON,
2071 arc::UserInteractionType::NOT_USER_INITIATED);
2072 arc::LaunchApp(profile(), arc_app_id2, ui::EF_LEFT_MOUSE_BUTTON,
2073 arc::UserInteractionType::NOT_USER_INITIATED);
2074 arc::LaunchApp(profile(), arc_app_id3, ui::EF_LEFT_MOUSE_BUTTON,
2075 arc::UserInteractionType::NOT_USER_INITIATED);
2076 arc::LaunchApp(profile(), arc_shortcut_id, ui::EF_LEFT_MOUSE_BUTTON,
2077 arc::UserInteractionType::NOT_USER_INITIATED);
2078
2079 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id_app_1));
2080 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id_app_2));
2081 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id_app_3));
2082 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id_shortcut));
2083
2084 // We activated arc_app_id1 twice but expect one close for item controller
2085 // stops launching request.
2086 ash::ShelfItemDelegate* item_delegate =
2087 model_->GetShelfItemDelegate(shelf_id_app_1);
2088 ASSERT_NE(nullptr, item_delegate);
2089 item_delegate->Close();
2090 base::RunLoop().RunUntilIdle();
2091
2092 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_app_1));
2093 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id_app_2));
2094 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id_app_3));
2095 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id_shortcut));
2096
2097 arc_test_.RestartArcInstance();
2098 SendListOfArcApps();
2099
2100 base::RunLoop().RunUntilIdle();
2101
2102 // Now spinner controllers should go away together with shelf items and ARC
2103 // app instance should receive request for launching apps and shortcuts.
2104 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_app_1));
2105 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_app_2));
2106 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_app_3));
2107 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id_shortcut));
2108
2109 ASSERT_EQ(2U, arc_test_.app_instance()->launch_requests().size());
2110 ASSERT_EQ(1U, arc_test_.app_instance()->launch_intents().size());
2111
2112 const arc::FakeAppInstance::Request* request1 =
2113 arc_test_.app_instance()->launch_requests()[0].get();
2114 const arc::FakeAppInstance::Request* request2 =
2115 arc_test_.app_instance()->launch_requests()[1].get();
2116
2117 EXPECT_TRUE((request1->IsForApp(app2) && request2->IsForApp(gmail)) ||
2118 (request1->IsForApp(gmail) && request2->IsForApp(app2)));
2119 EXPECT_EQ(arc_test_.app_instance()->launch_intents()[0].c_str(),
2120 shortcut.intent_uri);
2121 }
2122
2123 // Launch is canceled in case app becomes suspended.
TEST_F(ChromeLauncherControllerWithArcTest,ArcDeferredLaunchForSuspendedApp)2124 TEST_F(ChromeLauncherControllerWithArcTest, ArcDeferredLaunchForSuspendedApp) {
2125 InitLauncherController();
2126
2127 arc::mojom::AppInfo app = arc_test_.fake_apps()[0];
2128 const std::string app_id = ArcAppTest::GetAppId(app);
2129
2130 // Register app first.
2131 arc_test_.app_instance()->SendRefreshAppList({app});
2132 arc_test_.StopArcInstance();
2133
2134 // Restart ARC
2135 arc_test_.RestartArcInstance();
2136
2137 // Deferred controller should be allocated on start.
2138 const ash::ShelfID shelf_id(app_id);
2139 arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON,
2140 arc::UserInteractionType::NOT_USER_INITIATED);
2141 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id));
2142
2143 // Send app with suspended state.
2144 app.suspended = true;
2145 arc_test_.app_instance()->SendRefreshAppList({app});
2146
2147 // Controler automatically closed.
2148 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
2149
2150 // And no launch request issued.
2151 EXPECT_TRUE(arc_test_.app_instance()->launch_requests().empty());
2152 }
2153
2154 // Ensure the spinner controller does not override the active app controller
2155 // (crbug.com/701152).
TEST_F(ChromeLauncherControllerWithArcTest,ArcDeferredLaunchForActiveApp)2156 TEST_F(ChromeLauncherControllerWithArcTest, ArcDeferredLaunchForActiveApp) {
2157 InitLauncherController();
2158 SendListOfArcApps();
2159 arc_test_.StopArcInstance();
2160
2161 const arc::mojom::AppInfo& app = arc_test_.fake_apps()[0];
2162 const std::string app_id = ArcAppTest::GetAppId(app);
2163
2164 launcher_controller_->PinAppWithID(app_id);
2165 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
2166 const ash::ShelfID shelf_id(app_id);
2167 const ash::ShelfItem* item = launcher_controller_->GetItem(shelf_id);
2168 ASSERT_NE(nullptr, item);
2169 EXPECT_EQ(ash::STATUS_CLOSED, item->status);
2170 EXPECT_EQ(ash::TYPE_PINNED_APP, item->type);
2171
2172 // Play Store app is ARC app that might be represented by native Chrome
2173 // platform app.
2174 model_->SetShelfItemDelegate(
2175 shelf_id,
2176 std::make_unique<AppServiceAppWindowLauncherItemController>(
2177 shelf_id, launcher_controller_->app_service_app_window_controller()));
2178 launcher_controller_->SetItemStatus(shelf_id, ash::STATUS_RUNNING);
2179
2180 // This launch request should be ignored in case of active app.
2181 arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON,
2182 arc::UserInteractionType::NOT_USER_INITIATED);
2183 EXPECT_FALSE(
2184 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
2185
2186 // Closing the app should leave a pinned but closed shelf item shortcut.
2187 launcher_controller_->CloseLauncherItem(shelf_id);
2188 item = launcher_controller_->GetItem(shelf_id);
2189 ASSERT_NE(nullptr, item);
2190 EXPECT_EQ(ash::STATUS_CLOSED, item->status);
2191 EXPECT_EQ(ash::TYPE_PINNED_APP, item->type);
2192
2193 // Now launch request should not be ignored.
2194 arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON,
2195 arc::UserInteractionType::NOT_USER_INITIATED);
2196 EXPECT_TRUE(
2197 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
2198 }
2199
2200 // TODO(crbug.com/915840): this test is flakey and/or often crashes.
TEST_F(ChromeLauncherControllerMultiProfileWithArcTest,DISABLED_ArcMultiUser)2201 TEST_F(ChromeLauncherControllerMultiProfileWithArcTest, DISABLED_ArcMultiUser) {
2202 SendListOfArcApps();
2203
2204 InitLauncherController();
2205
2206 SetLauncherControllerHelper(new TestLauncherControllerHelper);
2207
2208 // App1 exists all the time.
2209 // App2 is created when primary user is active and destroyed when secondary
2210 // user is active.
2211 // Gmail created when secondary user is active.
2212
2213 const std::string user2 = "user2";
2214 TestingProfile* profile2 = CreateMultiUserProfile(user2);
2215 const AccountId account_id(
2216 multi_user_util::GetAccountIdFromProfile(profile()));
2217 const AccountId account_id2(
2218 multi_user_util::GetAccountIdFromProfile(profile2));
2219
2220 const std::string arc_app_id1 =
2221 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]);
2222 const std::string arc_app_id2 =
2223 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]);
2224 const std::string arc_app_id3 =
2225 ArcAppTest::GetAppId(arc_test_.fake_apps()[2]);
2226
2227 std::string window_app_id1("org.chromium.arc.1");
2228 views::Widget* arc_window1 = CreateArcWindow(window_app_id1);
2229 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0],
2230 std::string());
2231 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id1)));
2232
2233 std::string window_app_id2("org.chromium.arc.2");
2234 views::Widget* arc_window2 = CreateArcWindow(window_app_id2);
2235 arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[1],
2236 std::string());
2237 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id2)));
2238
2239 launcher_controller_->SetProfileForTest(profile2);
2240 SwitchActiveUser(account_id2);
2241
2242 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id1)));
2243 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id2)));
2244
2245 std::string window_app_id3("org.chromium.arc.3");
2246 views::Widget* arc_window3 = CreateArcWindow(window_app_id3);
2247 arc_test_.app_instance()->SendTaskCreated(3, arc_test_.fake_apps()[2],
2248 std::string());
2249 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id3)));
2250
2251 arc_window2->CloseNow();
2252 arc_test_.app_instance()->SendTaskDestroyed(2);
2253
2254 launcher_controller_->SetProfileForTest(profile());
2255 SwitchActiveUser(account_id);
2256
2257 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id1)));
2258 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id2)));
2259 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id3)));
2260
2261 // Close active window to let test passes.
2262 arc_window1->CloseNow();
2263 arc_window3->CloseNow();
2264 }
2265
TEST_F(ChromeLauncherControllerWithArcTest,ArcRunningApp)2266 TEST_F(ChromeLauncherControllerWithArcTest, ArcRunningApp) {
2267 InitLauncherController();
2268
2269 const std::string arc_app_id = ArcAppTest::GetAppId(arc_test_.fake_apps()[0]);
2270 SendListOfArcApps();
2271 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2272
2273 // Normal flow, create/destroy tasks.
2274 std::string window_app_id1("org.chromium.arc.1");
2275 std::string window_app_id2("org.chromium.arc.2");
2276 std::string window_app_id3("org.chromium.arc.3");
2277 CreateArcWindow(window_app_id1);
2278 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0],
2279 std::string());
2280 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2281 CreateArcWindow(window_app_id2);
2282 arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[0],
2283 std::string());
2284 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2285 arc_test_.app_instance()->SendTaskDestroyed(1);
2286 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2287 arc_test_.app_instance()->SendTaskDestroyed(2);
2288 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2289
2290 // Stopping bridge removes apps.
2291 CreateArcWindow(window_app_id3);
2292 arc_test_.app_instance()->SendTaskCreated(3, arc_test_.fake_apps()[0],
2293 std::string());
2294 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2295 arc_test_.StopArcInstance();
2296 base::RunLoop().RunUntilIdle();
2297 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2298 }
2299
2300 // Test race creation/deletion of ARC app.
2301 // TODO(khmel): Remove after moving everything to wayland protocol.
TEST_F(ChromeLauncherControllerWithArcTest,ArcRaceCreateClose)2302 TEST_F(ChromeLauncherControllerWithArcTest, ArcRaceCreateClose) {
2303 InitLauncherController();
2304
2305 const std::string arc_app_id1 =
2306 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]);
2307 const std::string arc_app_id2 =
2308 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]);
2309 SendListOfArcApps();
2310
2311 // ARC window created before and closed after mojom notification.
2312 std::string window_app_id1("org.chromium.arc.1");
2313 views::Widget* arc_window = CreateArcWindow(window_app_id1);
2314 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id1)));
2315 ASSERT_TRUE(arc_window);
2316 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0],
2317 std::string());
2318 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id1)));
2319 arc_test_.app_instance()->SendTaskDestroyed(1);
2320 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id1)));
2321 arc_window->Close();
2322 base::RunLoop().RunUntilIdle();
2323 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id1)));
2324
2325 // ARC window created after and closed before mojom notification.
2326 std::string window_app_id2("org.chromium.arc.2");
2327 arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[1],
2328 std::string());
2329 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id2)));
2330 arc_window = CreateArcWindow(window_app_id2);
2331 ASSERT_TRUE(arc_window);
2332 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id2)));
2333 arc_window->Close();
2334 base::RunLoop().RunUntilIdle();
2335 // Closing window does not close shelf item. It is closed on task destroy.
2336 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id2)));
2337 arc_test_.app_instance()->SendTaskDestroyed(2);
2338 EXPECT_FALSE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id2)));
2339 }
2340
TEST_F(ChromeLauncherControllerWithArcTest,ArcWindowRecreation)2341 TEST_F(ChromeLauncherControllerWithArcTest, ArcWindowRecreation) {
2342 InitLauncherController();
2343
2344 const std::string arc_app_id = ArcAppTest::GetAppId(arc_test_.fake_apps()[0]);
2345 SendListOfArcApps();
2346
2347 std::string window_app_id("org.chromium.arc.1");
2348 views::Widget* arc_window = CreateArcWindow(window_app_id);
2349 ASSERT_TRUE(arc_window);
2350 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0],
2351 std::string());
2352 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2353
2354 for (int i = 0; i < 3; ++i) {
2355 arc_window->Close();
2356 base::RunLoop().RunUntilIdle();
2357 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2358
2359 arc_window = CreateArcWindow(window_app_id);
2360 ASSERT_TRUE(arc_window);
2361 base::RunLoop().RunUntilIdle();
2362 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID(arc_app_id)));
2363 }
2364 }
2365
2366 // Verifies edge cases when Extension app launcher may be overwritten by ARC app
2367 // launcher controller and vice versa. This should not happen in normal cases
2368 // but in case of ARC boot failure this may lead to such situation. This test
2369 // verifies that dynamic change of app launcher controllers is safe.
2370 // See more crbug.com/770005.
TEST_F(ChromeLauncherControllerWithArcTest,OverrideAppItemController)2371 TEST_F(ChromeLauncherControllerWithArcTest, OverrideAppItemController) {
2372 extension_service_->AddExtension(arc_support_host_.get());
2373
2374 InitLauncherController();
2375
2376 SendListOfArcApps();
2377 arc::mojom::AppInfo app_info = CreateAppInfo(
2378 "Play Store", arc::kPlayStoreActivity, arc::kPlayStorePackage);
2379 EXPECT_EQ(arc::kPlayStoreAppId, AddArcAppAndShortcut(app_info));
2380 app_service_test().WaitForAppService();
2381
2382 std::string window_app_id("org.chromium.arc.1");
2383 const ash::ShelfID play_store_shelf_id(arc::kPlayStoreAppId);
2384
2385 launcher_controller_->UnpinAppWithID(arc::kPlayStoreAppId);
2386 EXPECT_FALSE(launcher_controller_->GetItem(play_store_shelf_id));
2387
2388 // Try 4 different scenarios with different creation and destroying orders.
2389
2390 // Scenario 1: Create OptIn, Play Store. Destroy OptIn, Play Store.
2391 {
2392 std::unique_ptr<V2App> play_store_optin =
2393 std::make_unique<V2App>(profile(), arc_support_host_.get(),
2394 extensions::AppWindow::WINDOW_TYPE_DEFAULT);
2395 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2396
2397 views::Widget* arc_window = CreateArcWindow(window_app_id);
2398 ASSERT_TRUE(arc_window);
2399 arc_test_.app_instance()->SendTaskCreated(1, app_info, std::string());
2400 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2401
2402 play_store_optin.reset();
2403 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2404
2405 arc_window->CloseNow();
2406 arc_test_.app_instance()->SendTaskDestroyed(1);
2407 EXPECT_FALSE(launcher_controller_->GetItem(play_store_shelf_id));
2408 }
2409
2410 // Scenario 2: Create OptIn, Play Store. Destroy Play Store, OptIn.
2411 {
2412 std::unique_ptr<V2App> play_store_optin =
2413 std::make_unique<V2App>(profile(), arc_support_host_.get(),
2414 extensions::AppWindow::WINDOW_TYPE_DEFAULT);
2415 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2416
2417 views::Widget* arc_window = CreateArcWindow(window_app_id);
2418 ASSERT_TRUE(arc_window);
2419 arc_test_.app_instance()->SendTaskCreated(1, app_info, std::string());
2420 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2421
2422 arc_window->CloseNow();
2423 arc_test_.app_instance()->SendTaskDestroyed(1);
2424 EXPECT_FALSE(launcher_controller_->GetItem(play_store_shelf_id));
2425
2426 play_store_optin.reset();
2427 EXPECT_FALSE(launcher_controller_->GetItem(play_store_shelf_id));
2428 }
2429
2430 // Scenario 3: Create Play Store, OptIn. Destroy OptIn, Play Store.
2431 {
2432 views::Widget* arc_window = CreateArcWindow(window_app_id);
2433 ASSERT_TRUE(arc_window);
2434 arc_test_.app_instance()->SendTaskCreated(1, app_info, std::string());
2435 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2436
2437 std::unique_ptr<V2App> play_store_optin =
2438 std::make_unique<V2App>(profile(), arc_support_host_.get(),
2439 extensions::AppWindow::WINDOW_TYPE_DEFAULT);
2440 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2441
2442 play_store_optin.reset();
2443 EXPECT_FALSE(launcher_controller_->GetItem(play_store_shelf_id));
2444
2445 arc_window->CloseNow();
2446 arc_test_.app_instance()->SendTaskDestroyed(1);
2447 EXPECT_FALSE(launcher_controller_->GetItem(play_store_shelf_id));
2448 }
2449
2450 // Scenario 4: Create Play Store, OptIn. Destroy Play Store, OptIn.
2451 {
2452 views::Widget* arc_window = CreateArcWindow(window_app_id);
2453 ASSERT_TRUE(arc_window);
2454 arc_test_.app_instance()->SendTaskCreated(1, app_info, std::string());
2455 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2456
2457 std::unique_ptr<V2App> play_store_optin =
2458 std::make_unique<V2App>(profile(), arc_support_host_.get(),
2459 extensions::AppWindow::WINDOW_TYPE_DEFAULT);
2460 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2461
2462 arc_window->CloseNow();
2463 arc_test_.app_instance()->SendTaskDestroyed(1);
2464 EXPECT_TRUE(launcher_controller_->GetItem(play_store_shelf_id));
2465
2466 play_store_optin.reset();
2467 EXPECT_FALSE(launcher_controller_->GetItem(play_store_shelf_id));
2468 }
2469 }
2470
2471 // Validate that ARC app is pinned correctly and pin is removed automatically
2472 // once app is uninstalled.
TEST_F(ChromeLauncherControllerWithArcTest,ArcAppPin)2473 TEST_F(ChromeLauncherControllerWithArcTest, ArcAppPin) {
2474 InitLauncherController();
2475
2476 const std::string arc_app_id = ArcAppTest::GetAppId(arc_test_.fake_apps()[0]);
2477
2478 SendListOfArcApps();
2479 extension_service_->AddExtension(extension1_.get());
2480 extension_service_->AddExtension(extension2_.get());
2481 // Allow async callbacks to run.
2482 base::RunLoop().RunUntilIdle();
2483
2484 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
2485 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id));
2486 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
2487
2488 launcher_controller_->PinAppWithID(extension1_->id());
2489 launcher_controller_->PinAppWithID(arc_app_id);
2490 launcher_controller_->PinAppWithID(extension2_->id());
2491
2492 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
2493 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id));
2494 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
2495
2496 EXPECT_EQ("Chrome, App1, Fake App 0, App2", GetPinnedAppStatus());
2497
2498 UninstallArcApps();
2499 // Allow async callbacks to run.
2500 base::RunLoop().RunUntilIdle();
2501
2502 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id));
2503 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
2504
2505 SendListOfArcApps();
2506 // Allow async callbacks to run.
2507 base::RunLoop().RunUntilIdle();
2508
2509 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id));
2510 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
2511
2512 // Opt-Out/Opt-In remove item from the shelf.
2513 launcher_controller_->PinAppWithID(arc_app_id);
2514 EXPECT_EQ("Chrome, App1, App2, Fake App 0", GetPinnedAppStatus());
2515 EnablePlayStore(false);
2516 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
2517 EnablePlayStore(true);
2518 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
2519
2520 SendListOfArcApps();
2521 // Allow async callbacks to run.
2522 base::RunLoop().RunUntilIdle();
2523
2524 EXPECT_EQ("Chrome, App1, App2, Fake App 0", GetPinnedAppStatus());
2525 }
2526
2527 // Validates that ARC app pins persist across OptOut/OptIn.
TEST_F(ChromeLauncherControllerWithArcTest,ArcAppPinOptOutOptIn)2528 TEST_F(ChromeLauncherControllerWithArcTest, ArcAppPinOptOutOptIn) {
2529 InitLauncherController();
2530
2531 const std::string arc_app_id1 =
2532 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]);
2533 const std::string arc_app_id2 =
2534 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]);
2535
2536 SendListOfArcApps();
2537 extension_service_->AddExtension(extension1_.get());
2538 extension_service_->AddExtension(extension2_.get());
2539
2540 launcher_controller_->PinAppWithID(extension1_->id());
2541 launcher_controller_->PinAppWithID(arc_app_id2);
2542 launcher_controller_->PinAppWithID(extension2_->id());
2543 launcher_controller_->PinAppWithID(arc_app_id1);
2544
2545 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
2546 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id1));
2547 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
2548 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id2));
2549 EXPECT_EQ("Chrome, App1, Fake App 1, App2, Fake App 0", GetPinnedAppStatus());
2550
2551 EnablePlayStore(false);
2552
2553 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
2554 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
2555 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id1));
2556 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
2557 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id2));
2558
2559 EnablePlayStore(true);
2560 SendListOfArcApps();
2561 base::RunLoop().RunUntilIdle();
2562
2563 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
2564 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id1));
2565 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
2566 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id2));
2567
2568 EXPECT_EQ("Chrome, App1, Fake App 1, App2, Fake App 0", GetPinnedAppStatus());
2569 }
2570
TEST_F(ChromeLauncherControllerWithArcTest,DISABLED_ArcCustomAppIcon)2571 TEST_F(ChromeLauncherControllerWithArcTest, DISABLED_ArcCustomAppIcon) {
2572 InitLauncherController();
2573
2574 // Wait until other apps are updated to avoid race condition while accessing
2575 // last updated item.
2576 base::RunLoop().RunUntilIdle();
2577
2578 // Register fake ARC apps.
2579 SendListOfArcApps();
2580 // Use first fake ARC app for testing.
2581 const arc::mojom::AppInfo& app = arc_test_.fake_apps()[0];
2582 const std::string arc_app_id = ArcAppTest::GetAppId(app);
2583 const ash::ShelfID arc_shelf_id(arc_app_id);
2584
2585 // Generate icon for the testing app and use compressed png content as test
2586 // input. Take shortcut to separate from default app icon.
2587 auto icon = arc_test_.app_instance()->GenerateIconResponse(
2588 extension_misc::EXTENSION_ICON_SMALL, false /* app_icon */);
2589 ASSERT_TRUE(icon);
2590 ASSERT_TRUE(icon->icon_png_data.has_value());
2591 EXPECT_FALSE(icon->icon_png_data->empty());
2592 std::string png_data(icon->icon_png_data->begin(),
2593 icon->icon_png_data->end());
2594 // Some input that represents invalid png content.
2595 std::string invalid_png_data("aaaaaa");
2596
2597 EXPECT_FALSE(launcher_controller_->GetItem(arc_shelf_id));
2598 std::string window_app_id1("org.chromium.arc.1");
2599 std::string window_app_id2("org.chromium.arc.2");
2600 views::Widget* window1 = CreateArcWindow(window_app_id1);
2601 ASSERT_TRUE(window1 && window1->GetNativeWindow());
2602 arc_test_.app_instance()->SendTaskCreated(1, app, std::string());
2603
2604 views::Widget* window2 = CreateArcWindow(window_app_id2);
2605 ASSERT_TRUE(window2 && window2->GetNativeWindow());
2606 arc_test_.app_instance()->SendTaskCreated(2, app, std::string());
2607 EXPECT_TRUE(launcher_controller_->GetItem(arc_shelf_id));
2608 ash::ShelfItemDelegate* item_delegate =
2609 model_->GetShelfItemDelegate(arc_shelf_id);
2610 ASSERT_TRUE(item_delegate);
2611 base::RunLoop().RunUntilIdle();
2612
2613 auto get_icon = [=]() {
2614 return *launcher_controller_->GetItem(arc_shelf_id)->image.bitmap();
2615 };
2616 const SkBitmap default_icon = get_icon();
2617
2618 // No custom icon set. Acitivating windows should not change icon.
2619 window1->Activate();
2620 EXPECT_TRUE(gfx::test::AreBitmapsEqual(default_icon, get_icon()));
2621 window2->Activate();
2622 EXPECT_TRUE(gfx::test::AreBitmapsEqual(default_icon, get_icon()));
2623
2624 // Set custom icon on active item. Icon should change to custom.
2625 arc_test_.app_instance()->SendTaskDescription(2, std::string(), png_data);
2626 const SkBitmap custom_icon = get_icon();
2627 EXPECT_FALSE(gfx::test::AreBitmapsEqual(default_icon, custom_icon));
2628
2629 // Switch back to the item without custom icon. Icon should be changed to
2630 // default.
2631 window1->Activate();
2632 EXPECT_TRUE(gfx::test::AreBitmapsEqual(default_icon, get_icon()));
2633
2634 // Test that setting an invalid icon should not change custom icon.
2635 arc_test_.app_instance()->SendTaskDescription(1, std::string(), png_data);
2636 EXPECT_TRUE(gfx::test::AreBitmapsEqual(custom_icon, get_icon()));
2637 arc_test_.app_instance()->SendTaskDescription(1, std::string(),
2638 invalid_png_data);
2639 EXPECT_TRUE(gfx::test::AreBitmapsEqual(custom_icon, get_icon()));
2640
2641 // Check window removing with active custom icon. Reseting custom icon of
2642 // inactive window doesn't reset shelf icon.
2643 arc_test_.app_instance()->SendTaskDescription(2, std::string(),
2644 std::string());
2645 EXPECT_TRUE(gfx::test::AreBitmapsEqual(custom_icon, get_icon()));
2646 // Set custom icon back to validate closing active window later.
2647 arc_test_.app_instance()->SendTaskDescription(2, std::string(), png_data);
2648 EXPECT_TRUE(gfx::test::AreBitmapsEqual(custom_icon, get_icon()));
2649
2650 // Reseting custom icon of active window resets shelf icon.
2651 arc_test_.app_instance()->SendTaskDescription(1, std::string(),
2652 std::string());
2653 // Wait for default icon load.
2654 base::RunLoop().RunUntilIdle();
2655 EXPECT_TRUE(gfx::test::AreBitmapsEqual(default_icon, get_icon()));
2656 window1->CloseNow();
2657 EXPECT_TRUE(gfx::test::AreBitmapsEqual(custom_icon, get_icon()));
2658 }
2659
TEST_F(ChromeLauncherControllerWithArcTest,ArcWindowPackageName)2660 TEST_F(ChromeLauncherControllerWithArcTest, ArcWindowPackageName) {
2661 InitLauncherController();
2662 SendListOfArcApps();
2663 app_service_test().WaitForAppService();
2664
2665 std::string window_app_id1("org.chromium.arc.1");
2666 std::string window_app_id2("org.chromium.arc.2");
2667 std::string window_app_id3("org.chromium.arc.3");
2668 views::Widget* arc_window1 = CreateArcWindow(window_app_id1);
2669 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_apps()[0],
2670 std::string());
2671 const std::string* package_name1 =
2672 arc_window1->GetNativeWindow()->GetProperty(ash::kArcPackageNameKey);
2673 ASSERT_TRUE(package_name1);
2674 EXPECT_EQ(*package_name1, arc_test_.fake_apps()[0].package_name);
2675
2676 views::Widget* arc_window2 = CreateArcWindow(window_app_id2);
2677 arc_test_.app_instance()->SendTaskCreated(2, arc_test_.fake_apps()[1],
2678 std::string());
2679 const std::string* package_name2 =
2680 arc_window2->GetNativeWindow()->GetProperty(ash::kArcPackageNameKey);
2681 ASSERT_TRUE(package_name2);
2682 EXPECT_EQ(*package_name2, arc_test_.fake_apps()[1].package_name);
2683
2684 // Create another window with the same package name.
2685 views::Widget* arc_window3 = CreateArcWindow(window_app_id3);
2686 arc_test_.app_instance()->SendTaskCreated(3, arc_test_.fake_apps()[1],
2687 std::string());
2688 const std::string* package_name3 =
2689 arc_window3->GetNativeWindow()->GetProperty(ash::kArcPackageNameKey);
2690 ASSERT_TRUE(package_name3);
2691 EXPECT_EQ(*package_name3, arc_test_.fake_apps()[1].package_name);
2692
2693 arc_window1->CloseNow();
2694 arc_window2->CloseNow();
2695 arc_window3->CloseNow();
2696 }
2697
2698 // Check that with multi profile V1 apps are properly added / removed from the
2699 // shelf.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,V1AppUpdateOnUserSwitch)2700 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
2701 V1AppUpdateOnUserSwitch) {
2702 // Create a browser item in the LauncherController.
2703 InitLauncherController();
2704
2705 EXPECT_EQ(1, model_->item_count());
2706 {
2707 // Create a "windowed gmail app".
2708 std::unique_ptr<V1App> v1_app(
2709 CreateRunningV1App(profile(), extension_misc::kGmailAppId, kGmailUrl));
2710 EXPECT_EQ(2, model_->item_count());
2711
2712 // After switching to a second user the item should be gone.
2713 std::string user2 = "user2";
2714 TestingProfile* profile2 = CreateMultiUserProfile(user2);
2715 const AccountId account_id2(
2716 multi_user_util::GetAccountIdFromProfile(profile2));
2717 const AccountId account_id(
2718 multi_user_util::GetAccountIdFromProfile(profile()));
2719 SwitchActiveUser(account_id2);
2720 EXPECT_EQ(1, model_->item_count());
2721
2722 // After switching back the item should be back.
2723 SwitchActiveUser(account_id);
2724 EXPECT_EQ(2, model_->item_count());
2725 // Note we destroy now the gmail app with the closure end.
2726 }
2727 EXPECT_EQ(1, model_->item_count());
2728 }
2729
2730 // Check edge cases with multi profile V1 apps in the shelf.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,V1AppUpdateOnUserSwitchEdgecases)2731 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
2732 V1AppUpdateOnUserSwitchEdgecases) {
2733 // Create a browser item in the LauncherController.
2734 InitLauncherController();
2735
2736 // First test: Create an app when the user is not active.
2737 std::string user2 = "user2";
2738 TestingProfile* profile2 = CreateMultiUserProfile(user2);
2739 const AccountId account_id2(
2740 multi_user_util::GetAccountIdFromProfile(profile2));
2741 const AccountId account_id(
2742 multi_user_util::GetAccountIdFromProfile(profile()));
2743 {
2744 // Create a "windowed gmail app".
2745 std::unique_ptr<V1App> v1_app(
2746 CreateRunningV1App(profile2, extension_misc::kGmailAppId, kGmailUrl));
2747 EXPECT_EQ(1, model_->item_count());
2748
2749 // However - switching to the user should show it.
2750 SwitchActiveUser(account_id2);
2751 EXPECT_EQ(2, model_->item_count());
2752
2753 // Second test: Remove the app when the user is not active and see that it
2754 // works.
2755 SwitchActiveUser(account_id);
2756 EXPECT_EQ(1, model_->item_count());
2757 // Note: the closure ends and the browser will go away.
2758 }
2759 EXPECT_EQ(1, model_->item_count());
2760 SwitchActiveUser(account_id2);
2761 EXPECT_EQ(1, model_->item_count());
2762 SwitchActiveUser(account_id);
2763 EXPECT_EQ(1, model_->item_count());
2764 }
2765
2766 // Check edge case where a visiting V1 app gets closed (crbug.com/321374).
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,V1CloseOnVisitingDesktop)2767 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
2768 V1CloseOnVisitingDesktop) {
2769 // Create a browser item in the LauncherController.
2770 InitLauncherController();
2771
2772 // First create an app when the user is active.
2773 std::string user2 = "user2";
2774 TestingProfile* profile2 = CreateMultiUserProfile(user2);
2775 const AccountId account_id(
2776 multi_user_util::GetAccountIdFromProfile(profile()));
2777 const AccountId account_id2(
2778 multi_user_util::GetAccountIdFromProfile(profile2));
2779 {
2780 // Create a "windowed gmail app".
2781 std::unique_ptr<V1App> v1_app(CreateRunningV1App(
2782 profile(), extension_misc::kGmailAppId, kGmailLaunchURL));
2783 EXPECT_EQ(2, model_->item_count());
2784 SwitchActiveUser(account_id2);
2785 EXPECT_EQ(1, model_->item_count());
2786 }
2787 // After the app was destroyed, switch back. (which caused already a crash).
2788 SwitchActiveUser(account_id);
2789
2790 // Create the same app again - which was also causing the crash.
2791 EXPECT_EQ(1, model_->item_count());
2792 {
2793 // Create a "windowed gmail app".
2794 std::unique_ptr<V1App> v1_app(CreateRunningV1App(
2795 profile(), extension_misc::kGmailAppId, kGmailLaunchURL));
2796 EXPECT_EQ(2, model_->item_count());
2797 }
2798 SwitchActiveUser(account_id2);
2799 EXPECT_EQ(1, model_->item_count());
2800 }
2801
2802 // Check edge cases with multi profile V1 apps in the shelf.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,V1AppUpdateOnUserSwitchEdgecases2)2803 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
2804 V1AppUpdateOnUserSwitchEdgecases2) {
2805 // Create a browser item in the LauncherController.
2806 InitLauncherController();
2807
2808 // First test: Create an app when the user is not active.
2809 std::string user2 = "user2";
2810 TestingProfile* profile2 = CreateMultiUserProfile(user2);
2811 const AccountId account_id(
2812 multi_user_util::GetAccountIdFromProfile(profile()));
2813 const AccountId account_id2(
2814 multi_user_util::GetAccountIdFromProfile(profile2));
2815 SwitchActiveUser(account_id2);
2816 {
2817 // Create a "windowed gmail app".
2818 std::unique_ptr<V1App> v1_app(
2819 CreateRunningV1App(profile(), extension_misc::kGmailAppId, kGmailUrl));
2820 EXPECT_EQ(1, model_->item_count());
2821
2822 // However - switching to the user should show it.
2823 SwitchActiveUser(account_id);
2824 EXPECT_EQ(2, model_->item_count());
2825
2826 // Second test: Remove the app when the user is not active and see that it
2827 // works.
2828 SwitchActiveUser(account_id2);
2829 EXPECT_EQ(1, model_->item_count());
2830 v1_app.reset();
2831 }
2832 EXPECT_EQ(1, model_->item_count());
2833 SwitchActiveUser(account_id);
2834 EXPECT_EQ(1, model_->item_count());
2835 SwitchActiveUser(account_id2);
2836 EXPECT_EQ(1, model_->item_count());
2837 }
2838
2839 // Check that activating an item which is on another user's desktop, will bring
2840 // it back.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,TestLauncherActivationPullsBackWindow)2841 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
2842 TestLauncherActivationPullsBackWindow) {
2843 // Create a browser item in the LauncherController.
2844 InitLauncherController();
2845 ash::MultiUserWindowManager* window_manager =
2846 MultiUserWindowManagerHelper::GetWindowManager();
2847
2848 // Create a second test profile. The first is the one in profile() created in
2849 // BrowserWithTestWindowTest::SetUp().
2850 // No need to add the profiles to the MultiUserWindowManagerHelper here.
2851 // CreateMultiUserProfile() already does that.
2852 TestingProfile* profile2 = CreateMultiUserProfile("user2");
2853 const AccountId current_user =
2854 multi_user_util::GetAccountIdFromProfile(profile());
2855
2856 // Create a browser window with a native window for the current user.
2857 std::unique_ptr<Browser> browser(
2858 CreateBrowserWithTestWindowForProfile(profile()));
2859 BrowserWindow* browser_window = browser->window();
2860 aura::Window* window = browser_window->GetNativeWindow();
2861 window_manager->SetWindowOwner(window, current_user);
2862
2863 // Check that an activation of the window on its owner's desktop does not
2864 // change the visibility to another user.
2865 launcher_controller_->ActivateWindowOrMinimizeIfActive(browser_window, false);
2866 EXPECT_TRUE(IsWindowOnDesktopOfUser(window, current_user));
2867
2868 // Transfer the window to another user's desktop and check that activating it
2869 // does pull it back to that user.
2870 window_manager->ShowWindowForUser(
2871 window, multi_user_util::GetAccountIdFromProfile(profile2));
2872 EXPECT_FALSE(IsWindowOnDesktopOfUser(window, current_user));
2873 launcher_controller_->ActivateWindowOrMinimizeIfActive(browser_window, false);
2874 EXPECT_TRUE(IsWindowOnDesktopOfUser(window, current_user));
2875 }
2876
2877 // Check that a running windowed V1 application will be properly pinned and
2878 // unpinned when the order gets changed through a profile / policy change.
TEST_F(ChromeLauncherControllerTest,RestoreDefaultAndRunningV1AppsResyncOrder)2879 TEST_F(ChromeLauncherControllerTest,
2880 RestoreDefaultAndRunningV1AppsResyncOrder) {
2881 InitLauncherController();
2882
2883 StartPrefSyncService(syncer::SyncDataList());
2884
2885 syncer::SyncChangeList sync_list;
2886 InsertAddPinChange(&sync_list, 0, extension1_->id());
2887 InsertAddPinChange(&sync_list, 1, extension_gmail_app_->id());
2888 SendPinChanges(sync_list, true);
2889
2890 // The shelf layout has always one static item at the beginning (App List).
2891 AddExtension(extension1_.get());
2892 EXPECT_EQ("Chrome, App1", GetPinnedAppStatus());
2893 AddExtension(extension2_.get());
2894 // No new app icon will be generated.
2895 EXPECT_EQ("Chrome, App1", GetPinnedAppStatus());
2896
2897 // Set the app status as running, which will add an unpinned item.
2898 launcher_controller_->SetV1AppStatus(extension2_->id(), ash::STATUS_RUNNING);
2899 EXPECT_EQ("Chrome, App1, app2", GetPinnedAppStatus());
2900 AddExtension(extension_gmail_app_.get());
2901 EXPECT_EQ("Chrome, App1, Gmail, app2", GetPinnedAppStatus());
2902
2903 // Now request to pin all items, which will pin the running unpinned items.
2904 syncer::SyncChangeList sync_list1;
2905 InsertAddPinChange(&sync_list1, 0, extension_gmail_app_->id());
2906 InsertAddPinChange(&sync_list1, 1, extension2_->id());
2907 InsertAddPinChange(&sync_list1, 2, extension1_->id());
2908 SendPinChanges(sync_list1, true);
2909 EXPECT_EQ("Chrome, Gmail, App2, App1", GetPinnedAppStatus());
2910
2911 // Removing the requirement for app 2 to be pinned should convert it back to
2912 // running but not pinned. It should move towards the end of the shelf, after
2913 // the pinned items, as determined by the |ShelfModel|'s weight system.
2914 syncer::SyncChangeList sync_list2;
2915 InsertAddPinChange(&sync_list2, 0, extension_gmail_app_->id());
2916 InsertAddPinChange(&sync_list2, 1, extension1_->id());
2917 SendPinChanges(sync_list2, true);
2918 EXPECT_EQ("Chrome, Gmail, App1, app2", GetPinnedAppStatus());
2919
2920 // Removing an item should simply close it and everything should shift.
2921 syncer::SyncChangeList sync_list3;
2922 InsertRemovePinChange(&sync_list3, extension1_->id());
2923 SendPinChanges(sync_list3, false /* reset_pin_model */);
2924 EXPECT_EQ("Chrome, Gmail, app2", GetPinnedAppStatus());
2925 }
2926
2927 // Check that a running unpinned V2 application will be properly pinned and
2928 // unpinned when the order gets changed through a profile / policy change.
TEST_F(ChromeLauncherControllerTest,RestoreDefaultAndRunningV2AppsResyncOrder)2929 TEST_F(ChromeLauncherControllerTest,
2930 RestoreDefaultAndRunningV2AppsResyncOrder) {
2931 InitLauncherController();
2932 syncer::SyncChangeList sync_list0;
2933 InsertAddPinChange(&sync_list0, 0, extension1_->id());
2934 InsertAddPinChange(&sync_list0, 1, extension_gmail_app_->id());
2935 SendPinChanges(sync_list0, true);
2936 // The shelf layout has always one static item at the beginning (app List).
2937 AddExtension(extension1_.get());
2938 EXPECT_EQ("Chrome, App1", GetPinnedAppStatus());
2939 AddExtension(extension_platform_app_.get());
2940 // No new app icon will be generated.
2941 EXPECT_EQ("Chrome, App1", GetPinnedAppStatus());
2942 // Add an unpinned but running V2 app.
2943 CreateRunningV2App(extension_platform_app_->id());
2944 EXPECT_EQ("Chrome, App1, *platform_app", GetPinnedAppStatus());
2945 AddExtension(extension_gmail_app_.get());
2946 EXPECT_EQ("Chrome, App1, Gmail, *platform_app", GetPinnedAppStatus());
2947
2948 // Now request to pin all items, which should pin the running unpinned item.
2949 syncer::SyncChangeList sync_list1;
2950 InsertAddPinChange(&sync_list1, 0, extension_gmail_app_->id());
2951 InsertAddPinChange(&sync_list1, 1, extension_platform_app_->id());
2952 InsertAddPinChange(&sync_list1, 2, extension1_->id());
2953 SendPinChanges(sync_list1, true);
2954 EXPECT_EQ("Chrome, Gmail, *Platform_App, App1", GetPinnedAppStatus());
2955
2956 // Removing the requirement for app 2 to be pinned should convert it back to
2957 // running but not pinned. It should move towards the end of the shelf, after
2958 // the pinned items, as determined by the |ShelfModel|'s weight system.
2959 syncer::SyncChangeList sync_list2;
2960 InsertAddPinChange(&sync_list2, 0, extension_gmail_app_->id());
2961 InsertAddPinChange(&sync_list2, 1, extension1_->id());
2962 SendPinChanges(sync_list2, true);
2963 EXPECT_EQ("Chrome, Gmail, App1, *platform_app", GetPinnedAppStatus());
2964
2965 // Removing an item should simply close it and everything should shift.
2966 syncer::SyncChangeList sync_list3;
2967 InsertAddPinChange(&sync_list3, 0, extension_gmail_app_->id());
2968 SendPinChanges(sync_list3, true);
2969 EXPECT_EQ("Chrome, Gmail, *platform_app", GetPinnedAppStatus());
2970 }
2971
2972 // Each user has a different set of applications pinned. Check that when
2973 // switching between the two users, the state gets properly set.
TEST_F(ChromeLauncherControllerTest,UserSwitchIconRestore)2974 TEST_F(ChromeLauncherControllerTest, UserSwitchIconRestore) {
2975 syncer::SyncChangeList user_a;
2976 syncer::SyncChangeList user_b;
2977
2978 SetUpMultiUserScenario(&user_a, &user_b);
2979
2980 // Show user 1.
2981 SendPinChanges(user_a, true);
2982 EXPECT_EQ("App1, App2, Gmail, *Platform_App, Doc, App5, Chrome",
2983 GetPinnedAppStatus());
2984
2985 // Show user 2.
2986 SendPinChanges(user_b, true);
2987 EXPECT_EQ("App6, App7, App8, Chrome", GetPinnedAppStatus());
2988
2989 // Switch back to 1.
2990 SendPinChanges(user_a, true);
2991 EXPECT_EQ("App1, App2, Gmail, *Platform_App, Doc, App5, Chrome",
2992 GetPinnedAppStatus());
2993
2994 // Switch back to 2.
2995 SendPinChanges(user_b, true);
2996 EXPECT_EQ("App6, App7, App8, Chrome", GetPinnedAppStatus());
2997 }
2998
2999 // Each user has a different set of applications pinned, and one user has an
3000 // application running. Check that when switching between the two users, the
3001 // state gets properly set.
TEST_F(ChromeLauncherControllerTest,UserSwitchIconRestoreWithRunningV2App)3002 TEST_F(ChromeLauncherControllerTest, UserSwitchIconRestoreWithRunningV2App) {
3003 syncer::SyncChangeList user_a;
3004 syncer::SyncChangeList user_b;
3005
3006 SetUpMultiUserScenario(&user_a, &user_b);
3007
3008 // Run the platform (V2) app.
3009 CreateRunningV2App(extension_platform_app_->id());
3010
3011 // Show user 1.
3012 SendPinChanges(user_a, true);
3013 EXPECT_EQ("App1, App2, Gmail, *Platform_App, Doc, App5, Chrome",
3014 GetPinnedAppStatus());
3015
3016 // Show user 2.
3017 SendPinChanges(user_b, true);
3018 EXPECT_EQ("App6, App7, App8, Chrome, *platform_app", GetPinnedAppStatus());
3019
3020 // Switch back to 1.
3021 SendPinChanges(user_a, true);
3022 EXPECT_EQ("App1, App2, Gmail, *Platform_App, Doc, App5, Chrome",
3023 GetPinnedAppStatus());
3024
3025 // Switch back to 2.
3026 SendPinChanges(user_b, true);
3027 EXPECT_EQ("App6, App7, App8, Chrome, *platform_app", GetPinnedAppStatus());
3028 }
3029
3030 // Each user has a different set of applications pinned, and one user has an
3031 // application running. The chrome icon is not the last item in the list.
3032 // Check that when switching between the two users, the state gets properly set.
3033 // There was once a bug associated with this.
TEST_F(ChromeLauncherControllerTest,UserSwitchIconRestoreWithRunningV2AppChromeInMiddle)3034 TEST_F(ChromeLauncherControllerTest,
3035 UserSwitchIconRestoreWithRunningV2AppChromeInMiddle) {
3036 syncer::SyncChangeList user_a;
3037 syncer::SyncChangeList user_b;
3038 SetUpMultiUserScenario(&user_a, &user_b);
3039
3040 // Run the platform (V2) app.
3041 CreateRunningV2App(extension_platform_app_->id());
3042
3043 // Show user 1.
3044 SendPinChanges(user_a, true);
3045 SetShelfChromeIconIndex(5);
3046 EXPECT_EQ("App1, App2, Gmail, *Platform_App, Doc, Chrome, App5",
3047 GetPinnedAppStatus());
3048
3049 // Show user 2.
3050 SendPinChanges(user_b, true);
3051 SetShelfChromeIconIndex(3);
3052 EXPECT_EQ("App6, App7, App8, Chrome, *platform_app", GetPinnedAppStatus());
3053
3054 // Switch back to 1.
3055 SendPinChanges(user_a, true);
3056 SetShelfChromeIconIndex(5);
3057 EXPECT_EQ("App1, App2, Gmail, *Platform_App, Doc, Chrome, App5",
3058 GetPinnedAppStatus());
3059 }
3060
TEST_F(ChromeLauncherControllerTest,Policy)3061 TEST_F(ChromeLauncherControllerTest, Policy) {
3062 extension_service_->AddExtension(extension2_.get());
3063 extension_service_->AddExtension(extension_gmail_app_.get());
3064
3065 // Pin policy should be initilized before controller start.
3066 base::ListValue policy_value;
3067 AppendPrefValue(&policy_value, extension1_->id());
3068 AppendPrefValue(&policy_value, extension2_->id());
3069 profile()->GetTestingPrefService()->SetManagedPref(
3070 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy());
3071
3072 InitLauncherController();
3073
3074 // Only |extension2_| should get pinned. |extension1_| is specified but not
3075 // installed, and |extension_gmail_app_| is part of the default set, but that
3076 // shouldn't take effect when the policy override is in place.
3077 EXPECT_EQ("Chrome, App2", GetPinnedAppStatus());
3078
3079 // Installing |extension1_| should add it to the launcher. Note, App1 goes
3080 // before App2 that is aligned with the pin order in policy.
3081 AddExtension(extension1_.get());
3082 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
3083
3084 // Removing |extension1_| from the policy should not be reflected in the
3085 // launcher and pin will exist.
3086 policy_value.Remove(0, nullptr);
3087 profile()->GetTestingPrefService()->SetManagedPref(
3088 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy());
3089 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
3090 }
3091
TEST_F(ChromeLauncherControllerTest,UnpinWithUninstall)3092 TEST_F(ChromeLauncherControllerTest, UnpinWithUninstall) {
3093 extension_service_->AddExtension(extension_gmail_app_.get());
3094 extension_service_->AddExtension(extension_doc_app_.get());
3095
3096 InitLauncherController();
3097 StartPrefSyncService(syncer::SyncDataList());
3098
3099 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
3100 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_doc_app_->id()));
3101
3102 UnloadExtension(extension_gmail_app_->id(),
3103 UnloadedExtensionReason::UNINSTALL);
3104
3105 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
3106 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_doc_app_->id()));
3107 }
3108
TEST_F(ChromeLauncherControllerTest,SyncUpdates)3109 TEST_F(ChromeLauncherControllerTest, SyncUpdates) {
3110 extension_service_->AddExtension(extension2_.get());
3111 extension_service_->AddExtension(extension_gmail_app_.get());
3112 extension_service_->AddExtension(extension_doc_app_.get());
3113
3114 InitLauncherController();
3115
3116 syncer::SyncChangeList sync_list;
3117 InsertAddPinChange(&sync_list, 10, extension_misc::kChromeAppId);
3118 SendPinChanges(sync_list, true);
3119
3120 std::vector<std::string> expected_pinned_apps;
3121 std::vector<std::string> actual_pinned_apps;
3122 GetPinnedAppIds(launcher_controller_.get(), &actual_pinned_apps);
3123 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
3124
3125 // Unavailable extensions don't create launcher items.
3126 sync_list.clear();
3127 InsertAddPinChange(&sync_list, 0, extension1_->id());
3128 InsertAddPinChange(&sync_list, 1, extension2_->id());
3129 InsertAddPinChange(&sync_list, 3, extension_doc_app_->id());
3130 SendPinChanges(sync_list, false);
3131
3132 expected_pinned_apps.push_back(extension2_->id());
3133 expected_pinned_apps.push_back(extension_doc_app_->id());
3134 GetPinnedAppIds(launcher_controller_.get(), &actual_pinned_apps);
3135 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
3136
3137 sync_list.clear();
3138 InsertAddPinChange(&sync_list, 2, extension_gmail_app_->id());
3139 SendPinChanges(sync_list, false);
3140 expected_pinned_apps.insert(expected_pinned_apps.begin() + 1,
3141 extension_gmail_app_->id());
3142 GetPinnedAppIds(launcher_controller_.get(), &actual_pinned_apps);
3143 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
3144
3145 sync_list.clear();
3146 InsertUpdatePinChange(&sync_list, 0, extension_doc_app_->id());
3147 InsertUpdatePinChange(&sync_list, 1, extension_gmail_app_->id());
3148 InsertUpdatePinChange(&sync_list, 2, extension2_->id());
3149 SendPinChanges(sync_list, false);
3150 std::reverse(expected_pinned_apps.begin(), expected_pinned_apps.end());
3151 GetPinnedAppIds(launcher_controller_.get(), &actual_pinned_apps);
3152 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
3153
3154 // Sending legacy sync change without pin info should not affect pin model.
3155 sync_list.clear();
3156 InsertLegacyPinChange(&sync_list, extension_doc_app_->id());
3157 SendPinChanges(sync_list, false);
3158 GetPinnedAppIds(launcher_controller_.get(), &actual_pinned_apps);
3159 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
3160
3161 sync_list.clear();
3162 InsertRemovePinChange(&sync_list, extension_doc_app_->id());
3163 SendPinChanges(sync_list, false);
3164 expected_pinned_apps.erase(expected_pinned_apps.begin());
3165 GetPinnedAppIds(launcher_controller_.get(), &actual_pinned_apps);
3166 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
3167
3168 sync_list.clear();
3169 InsertRemovePinChange(&sync_list, extension_gmail_app_->id());
3170 InsertRemovePinChange(&sync_list, extension2_->id());
3171 SendPinChanges(sync_list, false);
3172 expected_pinned_apps.clear();
3173 GetPinnedAppIds(launcher_controller_.get(), &actual_pinned_apps);
3174 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
3175 }
3176
TEST_F(ChromeLauncherControllerTest,PendingInsertionOrder)3177 TEST_F(ChromeLauncherControllerTest, PendingInsertionOrder) {
3178 extension_service_->AddExtension(extension1_.get());
3179 extension_service_->AddExtension(extension_gmail_app_.get());
3180
3181 InitLauncherController();
3182
3183 syncer::SyncChangeList sync_list;
3184 InsertAddPinChange(&sync_list, 0, extension1_->id());
3185 InsertAddPinChange(&sync_list, 1, extension2_->id());
3186 InsertAddPinChange(&sync_list, 2, extension_gmail_app_->id());
3187 SendPinChanges(sync_list, true);
3188
3189 std::vector<std::string> expected_pinned_apps;
3190 expected_pinned_apps.push_back(extension1_->id());
3191 expected_pinned_apps.push_back(extension_gmail_app_->id());
3192 std::vector<std::string> actual_pinned_apps;
3193
3194 GetPinnedAppIds(launcher_controller_.get(), &actual_pinned_apps);
3195 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
3196
3197 // Install |extension2| and verify it shows up between the other two.
3198 AddExtension(extension2_.get());
3199 expected_pinned_apps.insert(expected_pinned_apps.begin() + 1,
3200 extension2_->id());
3201 GetPinnedAppIds(launcher_controller_.get(), &actual_pinned_apps);
3202 EXPECT_EQ(expected_pinned_apps, actual_pinned_apps);
3203 }
3204
3205 // Ensure |controller| creates the expected menu items for the given shelf item.
CheckAppMenu(ChromeLauncherController * controller,const ash::ShelfItem & item,size_t expected_item_count,base::string16 expected_item_titles[])3206 void CheckAppMenu(ChromeLauncherController* controller,
3207 const ash::ShelfItem& item,
3208 size_t expected_item_count,
3209 base::string16 expected_item_titles[]) {
3210 auto items = controller->GetAppMenuItemsForTesting(item);
3211 ASSERT_EQ(expected_item_count, items.size());
3212 for (size_t i = 0; i < expected_item_count; i++)
3213 EXPECT_EQ(expected_item_titles[i], items[i].title);
3214 }
3215
3216 // Check that browsers get reflected correctly in the launcher menu.
TEST_F(ChromeLauncherControllerTest,BrowserMenuGeneration)3217 TEST_F(ChromeLauncherControllerTest, BrowserMenuGeneration) {
3218 EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
3219 chrome::NewTab(browser());
3220
3221 InitLauncherController();
3222
3223 // Check that the browser list is empty at this time.
3224 ash::ShelfItem item_browser;
3225 item_browser.type = ash::TYPE_BROWSER_SHORTCUT;
3226 item_browser.id = ash::ShelfID(extension_misc::kChromeAppId);
3227 CheckAppMenu(launcher_controller_.get(), item_browser, 0, nullptr);
3228
3229 // Now make the created browser() visible by showing its browser window.
3230 browser()->window()->Show();
3231 base::string16 title1 = ASCIIToUTF16("Test1");
3232 NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1);
3233 base::string16 one_menu_item[] = {title1};
3234
3235 CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item);
3236
3237 // Create one more browser/window and check that one more was added.
3238 std::unique_ptr<Browser> browser2(
3239 CreateBrowserWithTestWindowForProfile(profile()));
3240 chrome::NewTab(browser2.get());
3241 browser2->window()->Show();
3242 base::string16 title2 = ASCIIToUTF16("Test2");
3243 NavigateAndCommitActiveTabWithTitle(browser2.get(), GURL("http://test2"),
3244 title2);
3245
3246 // Check that the list contains now two entries - make furthermore sure that
3247 // the active item is the first entry.
3248 base::string16 two_menu_items[] = {title1, title2};
3249 CheckAppMenu(launcher_controller_.get(), item_browser, 2, two_menu_items);
3250
3251 // Apparently we have to close all tabs we have.
3252 chrome::CloseTab(browser2.get());
3253 }
3254
3255 // Check the multi profile case where only user related browsers should show up.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,BrowserMenuGenerationTwoUsers)3256 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
3257 BrowserMenuGenerationTwoUsers) {
3258 // Create a browser item in the LauncherController.
3259 InitLauncherController();
3260
3261 ash::ShelfItem item_browser;
3262 item_browser.type = ash::TYPE_BROWSER_SHORTCUT;
3263 item_browser.id = ash::ShelfID(extension_misc::kChromeAppId);
3264
3265 // Check that the menu is empty.
3266 chrome::NewTab(browser());
3267 CheckAppMenu(launcher_controller_.get(), item_browser, 0, nullptr);
3268
3269 // Show the created |browser()| by showing its window.
3270 browser()->window()->Show();
3271 base::string16 title1 = ASCIIToUTF16("Test1");
3272 NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1);
3273 base::string16 one_menu_item1[] = {title1};
3274 CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item1);
3275
3276 // Create a browser for another user and check that it is not included in the
3277 // users running browser list.
3278 std::string user2 = "user2";
3279 TestingProfile* profile2 = CreateMultiUserProfile(user2);
3280 const AccountId account_id2(
3281 multi_user_util::GetAccountIdFromProfile(profile2));
3282 std::unique_ptr<Browser> browser2(
3283 CreateBrowserAndTabWithProfile(profile2, user2, "http://test2"));
3284 base::string16 one_menu_item2[] = {ASCIIToUTF16(user2)};
3285 CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item1);
3286
3287 // Switch to the other user and make sure that only that browser window gets
3288 // shown.
3289 SwitchActiveUser(account_id2);
3290 CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item2);
3291
3292 // Transferred browsers of other users should not show up in the list.
3293 MultiUserWindowManagerHelper::GetWindowManager()->ShowWindowForUser(
3294 browser()->window()->GetNativeWindow(), account_id2);
3295 CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item2);
3296
3297 chrome::CloseTab(browser2.get());
3298 }
3299
3300 // Check that V1 apps are correctly reflected in the launcher menu using the
3301 // refocus logic.
3302 // Note that the extension matching logic is tested by the extension system
3303 // and does not need a separate test here.
TEST_F(ChromeLauncherControllerTest,V1AppMenuGeneration)3304 TEST_F(ChromeLauncherControllerTest, V1AppMenuGeneration) {
3305 EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
3306 EXPECT_EQ(0, browser()->tab_strip_model()->count());
3307
3308 InitLauncherControllerWithBrowser();
3309 StartPrefSyncService(syncer::SyncDataList());
3310
3311 // The model should only contain the browser shortcut item.
3312 EXPECT_EQ(1, model_->item_count());
3313 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
3314
3315 // Installing |extension_gmail_app_| pins it to the launcher.
3316 const ash::ShelfID gmail_id(extension_gmail_app_->id());
3317 AddExtension(extension_gmail_app_.get());
3318 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
3319 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(kGmailUrl));
3320
3321 // Check the menu content.
3322 ash::ShelfItem item_browser;
3323 item_browser.type = ash::TYPE_BROWSER_SHORTCUT;
3324 item_browser.id = ash::ShelfID(extension_misc::kChromeAppId);
3325
3326 ash::ShelfItem item_gmail;
3327 item_gmail.type = ash::TYPE_PINNED_APP;
3328 item_gmail.id = gmail_id;
3329 CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
3330
3331 // Set the gmail URL to a new tab.
3332 base::string16 title1 = ASCIIToUTF16("Test1");
3333 NavigateAndCommitActiveTabWithTitle(browser(), GURL(kGmailUrl), title1);
3334
3335 base::string16 one_menu_item[] = {title1};
3336 CheckAppMenu(launcher_controller_.get(), item_gmail, 1, one_menu_item);
3337
3338 // Create one empty tab.
3339 chrome::NewTab(browser());
3340 base::string16 title2 = ASCIIToUTF16("Test2");
3341 NavigateAndCommitActiveTabWithTitle(browser(), GURL("https://bla"), title2);
3342
3343 // and another one with another gmail instance.
3344 chrome::NewTab(browser());
3345 base::string16 title3 = ASCIIToUTF16("Test3");
3346 NavigateAndCommitActiveTabWithTitle(browser(), GURL(kGmailUrl), title3);
3347 base::string16 two_menu_items[] = {title1, title3};
3348 CheckAppMenu(launcher_controller_.get(), item_gmail, 2, two_menu_items);
3349
3350 // Even though the item is in the V1 app list, it should also be in the
3351 // browser list.
3352 base::string16 browser_menu_item[] = {title3};
3353 CheckAppMenu(launcher_controller_.get(), item_browser, 1, browser_menu_item);
3354
3355 // Test that closing of (all) the item(s) does work (and all menus get
3356 // updated properly).
3357 launcher_controller_->Close(item_gmail.id);
3358
3359 CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
3360 base::string16 browser_menu_item2[] = {title2};
3361 CheckAppMenu(launcher_controller_.get(), item_browser, 1, browser_menu_item2);
3362 }
3363
3364 // Check the multi profile case where only user related apps should show up.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,V1AppMenuGenerationTwoUsers)3365 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
3366 V1AppMenuGenerationTwoUsers) {
3367 // Create a browser item in the LauncherController.
3368 InitLauncherController();
3369 StartPrefSyncService(syncer::SyncDataList());
3370 chrome::NewTab(browser());
3371
3372 // Installing |extension_gmail_app_| pins it to the launcher.
3373 const ash::ShelfID gmail_id(extension_gmail_app_->id());
3374 AddExtension(extension_gmail_app_.get());
3375 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
3376 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(kGmailUrl));
3377
3378 // Check the menu content.
3379 ash::ShelfItem item_browser;
3380 item_browser.type = ash::TYPE_BROWSER_SHORTCUT;
3381 item_browser.id = ash::ShelfID(extension_misc::kChromeAppId);
3382
3383 ash::ShelfItem item_gmail;
3384 item_gmail.type = ash::TYPE_PINNED_APP;
3385 item_gmail.id = gmail_id;
3386 CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
3387
3388 // Set the gmail URL to a new tab.
3389 base::string16 title1 = ASCIIToUTF16("Test1");
3390 NavigateAndCommitActiveTabWithTitle(browser(), GURL(kGmailUrl), title1);
3391
3392 base::string16 one_menu_item[] = {title1};
3393 CheckAppMenu(launcher_controller_.get(), item_gmail, 1, one_menu_item);
3394
3395 // Create a second profile and switch to that user.
3396 std::string user2 = "user2";
3397 TestingProfile* profile2 = CreateMultiUserProfile(user2);
3398 const AccountId account_id2(
3399 multi_user_util::GetAccountIdFromProfile(profile2));
3400 SwitchActiveUser(account_id2);
3401
3402 // No item should have content yet.
3403 CheckAppMenu(launcher_controller_.get(), item_browser, 0, nullptr);
3404 CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
3405
3406 // Transfer the browser of the first user - it should still not show up.
3407 MultiUserWindowManagerHelper::GetWindowManager()->ShowWindowForUser(
3408 browser()->window()->GetNativeWindow(), account_id2);
3409
3410 CheckAppMenu(launcher_controller_.get(), item_browser, 0, nullptr);
3411 CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
3412 }
3413
3414 // Check that V2 applications are creating items properly in the launcher when
3415 // instantiated by the current user.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,V2AppHandlingTwoUsers)3416 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
3417 V2AppHandlingTwoUsers) {
3418 InitLauncherController();
3419 const AccountId account_id(
3420 multi_user_util::GetAccountIdFromProfile(profile()));
3421 // Check that there is a browser.
3422 EXPECT_EQ(1, model_->item_count());
3423
3424 // Add a v2 app.
3425 AddExtension(extension1_.get());
3426 V2App v2_app(profile(), extension1_.get());
3427 EXPECT_EQ(2, model_->item_count());
3428
3429 // Create a profile for our second user (will be destroyed by the framework).
3430 TestingProfile* profile2 = CreateMultiUserProfile("user2");
3431 const AccountId account_id2(
3432 multi_user_util::GetAccountIdFromProfile(profile2));
3433
3434 // After switching users the item should go away.
3435 SwitchActiveUser(account_id2);
3436 EXPECT_EQ(1, model_->item_count());
3437
3438 // And it should come back when switching back.
3439 SwitchActiveUser(account_id);
3440 EXPECT_EQ(2, model_->item_count());
3441 }
3442
3443 // Check that V2 applications are creating items properly in edge cases:
3444 // a background user creates a V2 app, gets active and inactive again and then
3445 // deletes the app.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,V2AppHandlingTwoUsersEdgeCases)3446 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
3447 V2AppHandlingTwoUsersEdgeCases) {
3448 InitLauncherController();
3449 // Create a profile for our second user (will be destroyed by the framework).
3450 TestingProfile* profile2 = CreateMultiUserProfile("user2");
3451 const AccountId account_id(
3452 multi_user_util::GetAccountIdFromProfile(profile()));
3453 const AccountId account_id2(
3454 multi_user_util::GetAccountIdFromProfile(profile2));
3455 // Check that there is a browser, back button and a app launcher.
3456 EXPECT_EQ(1, model_->item_count());
3457
3458 // Switch to an inactive user.
3459 SwitchActiveUser(account_id2);
3460 EXPECT_EQ(1, model_->item_count());
3461
3462 // Add the v2 app to the inactive user and check that no item was added to
3463 // the launcher.
3464 {
3465 AddExtension(extension1_.get());
3466 V2App v2_app(profile(), extension1_.get());
3467 EXPECT_EQ(1, model_->item_count());
3468
3469 // Switch to the primary user and check that the item is shown.
3470 SwitchActiveUser(account_id);
3471 EXPECT_EQ(2, model_->item_count());
3472
3473 // Switch to the second user and check that the item goes away - even if the
3474 // item gets closed.
3475 SwitchActiveUser(account_id2);
3476 EXPECT_EQ(1, model_->item_count());
3477 }
3478
3479 app_service_test().WaitForAppService();
3480 // After the application was killed there should still be 1 item.
3481 EXPECT_EQ(1, model_->item_count());
3482
3483 // Switching then back to the default user should not show the additional item
3484 // anymore.
3485 SwitchActiveUser(account_id);
3486 EXPECT_EQ(1, model_->item_count());
3487 }
3488
TEST_F(ChromeLauncherControllerTest,Active)3489 TEST_F(ChromeLauncherControllerTest, Active) {
3490 InitLauncherController();
3491
3492 // Creates a new app window.
3493 int initial_item_count = model_->item_count();
3494 AddExtension(extension1_.get());
3495 V2App app_1(profile(), extension1_.get());
3496 EXPECT_TRUE(app_1.window()->GetNativeWindow()->IsVisible());
3497 EXPECT_EQ(initial_item_count + 1, model_->item_count());
3498 ash::ShelfItemDelegate* app_item_delegate_1 =
3499 model_->GetShelfItemDelegate(model_->items()[initial_item_count].id);
3500 ASSERT_TRUE(app_item_delegate_1);
3501 AppWindowLauncherItemController* app_item_controller_1 =
3502 app_item_delegate_1->AsAppWindowLauncherItemController();
3503 ASSERT_TRUE(app_item_controller_1);
3504 ui::BaseWindow* last_active =
3505 GetLastActiveWindowForItemController(app_item_controller_1);
3506 EXPECT_EQ(app_1.window()->GetNativeWindow(), last_active->GetNativeWindow());
3507 // Change the status so that we can verify it gets reset when the active
3508 // window changes.
3509 launcher_controller_->SetItemStatus(app_item_delegate_1->shelf_id(),
3510 ash::STATUS_ATTENTION);
3511
3512 // Creates another app window, which should become active and reset |app_1|'s
3513 // status (to running).
3514 AddExtension(extension2_.get());
3515 V2App app_2(profile(), extension2_.get());
3516 EXPECT_TRUE(app_2.window()->GetNativeWindow()->IsVisible());
3517 EXPECT_EQ(initial_item_count + 2, model_->item_count());
3518 ash::ShelfItemDelegate* app_item_delegate_2 =
3519 model_->GetShelfItemDelegate(model_->items()[initial_item_count + 1].id);
3520 ASSERT_TRUE(app_item_delegate_2);
3521 AppWindowLauncherItemController* app_item_controller_2 =
3522 app_item_delegate_2->AsAppWindowLauncherItemController();
3523 ASSERT_TRUE(app_item_controller_2);
3524 last_active = GetLastActiveWindowForItemController(app_item_controller_2);
3525 EXPECT_EQ(app_2.window()->GetNativeWindow(), last_active->GetNativeWindow());
3526 const ash::ShelfItem* shelf_item_1 =
3527 launcher_controller_->GetItem(app_item_delegate_1->shelf_id());
3528 ASSERT_TRUE(shelf_item_1);
3529 EXPECT_EQ(ash::STATUS_RUNNING, shelf_item_1->status);
3530
3531 launcher_controller_->SetItemStatus(app_item_delegate_2->shelf_id(),
3532 ash::STATUS_ATTENTION);
3533
3534 // Activate the first window, which should reset the status of the
3535 // second apps window.
3536 app_1.window()->GetBaseWindow()->Activate();
3537 const ash::ShelfItem* shelf_item_2 =
3538 launcher_controller_->GetItem(app_item_delegate_2->shelf_id());
3539 ASSERT_TRUE(shelf_item_2);
3540 EXPECT_EQ(ash::STATUS_RUNNING, shelf_item_2->status);
3541 }
3542
3543 // Check that V2 applications will be made visible on the target desktop if
3544 // another window of the same type got previously teleported there.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,V2AppFollowsTeleportedWindow)3545 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
3546 V2AppFollowsTeleportedWindow) {
3547 InitLauncherController();
3548 ash::MultiUserWindowManager* window_manager =
3549 MultiUserWindowManagerHelper::GetWindowManager();
3550
3551 // Create and add three users / profiles, and go to #1's desktop.
3552 TestingProfile* profile1 = CreateMultiUserProfile("user-1");
3553 TestingProfile* profile2 = CreateMultiUserProfile("user-2");
3554 TestingProfile* profile3 = CreateMultiUserProfile("user-3");
3555 const AccountId account_id1(
3556 multi_user_util::GetAccountIdFromProfile(profile1));
3557 const AccountId account_id2(
3558 multi_user_util::GetAccountIdFromProfile(profile2));
3559 const AccountId account_id3(
3560 multi_user_util::GetAccountIdFromProfile(profile3));
3561
3562 extensions::TestExtensionSystem* extension_system1(
3563 static_cast<extensions::TestExtensionSystem*>(
3564 extensions::ExtensionSystem::Get(profile1)));
3565 extensions::ExtensionService* extension_service1 =
3566 extension_system1->CreateExtensionService(
3567 base::CommandLine::ForCurrentProcess(), base::FilePath(), false);
3568 extension_service1->Init();
3569 apps::AppServiceProxy* proxy1 =
3570 apps::AppServiceProxyFactory::GetForProfile(profile1);
3571
3572 SwitchActiveUser(account_id1);
3573
3574 // A v2 app for user #1 should be shown first and get hidden when switching
3575 // to desktop #2.
3576 extension_service1->AddExtension(extension1_.get());
3577 proxy1->FlushMojoCallsForTesting();
3578 V2App v2_app_1(profile1, extension1_.get());
3579 EXPECT_TRUE(v2_app_1.window()->GetNativeWindow()->IsVisible());
3580 SwitchActiveUser(account_id2);
3581 app_service_test().FlushMojoCalls();
3582 EXPECT_FALSE(v2_app_1.window()->GetNativeWindow()->IsVisible());
3583
3584 // Add a v2 app for user #1 while on desktop #2 should not be shown.
3585 V2App v2_app_2(profile1, extension1_.get());
3586 EXPECT_FALSE(v2_app_1.window()->GetNativeWindow()->IsVisible());
3587 EXPECT_FALSE(v2_app_2.window()->GetNativeWindow()->IsVisible());
3588
3589 // Teleport the app from user #1 to the desktop #2 should show it.
3590 window_manager->ShowWindowForUser(v2_app_1.window()->GetNativeWindow(),
3591 account_id2);
3592 EXPECT_TRUE(v2_app_1.window()->GetNativeWindow()->IsVisible());
3593 EXPECT_FALSE(v2_app_2.window()->GetNativeWindow()->IsVisible());
3594
3595 // Creating a new application for user #1 on desktop #2 should teleport it
3596 // there automatically.
3597 V2App v2_app_3(profile1, extension1_.get());
3598 EXPECT_TRUE(v2_app_1.window()->GetNativeWindow()->IsVisible());
3599 EXPECT_FALSE(v2_app_2.window()->GetNativeWindow()->IsVisible());
3600 EXPECT_TRUE(v2_app_3.window()->GetNativeWindow()->IsVisible());
3601
3602 // Switching back to desktop#1 and creating an app for user #1 should move
3603 // the app on desktop #1.
3604 SwitchActiveUser(account_id1);
3605 app_service_test().FlushMojoCalls();
3606 V2App v2_app_4(profile1, extension1_.get());
3607 EXPECT_FALSE(v2_app_1.window()->GetNativeWindow()->IsVisible());
3608 EXPECT_TRUE(v2_app_2.window()->GetNativeWindow()->IsVisible());
3609 EXPECT_FALSE(v2_app_3.window()->GetNativeWindow()->IsVisible());
3610 EXPECT_TRUE(v2_app_4.window()->GetNativeWindow()->IsVisible());
3611
3612 // Switching to desktop #3 and creating an app for user #1 should place it
3613 // on that user's desktop (#1).
3614 SwitchActiveUser(account_id3);
3615 app_service_test().FlushMojoCalls();
3616 V2App v2_app_5(profile1, extension1_.get());
3617 EXPECT_FALSE(v2_app_5.window()->GetNativeWindow()->IsVisible());
3618 SwitchActiveUser(account_id1);
3619 EXPECT_TRUE(v2_app_5.window()->GetNativeWindow()->IsVisible());
3620
3621 // Switching to desktop #2, hiding the app window and creating an app should
3622 // teleport there automatically.
3623 SwitchActiveUser(account_id2);
3624 app_service_test().FlushMojoCalls();
3625 v2_app_1.window()->Hide();
3626 V2App v2_app_6(profile1, extension1_.get());
3627 EXPECT_FALSE(v2_app_1.window()->GetNativeWindow()->IsVisible());
3628 EXPECT_FALSE(v2_app_2.window()->GetNativeWindow()->IsVisible());
3629 EXPECT_TRUE(v2_app_6.window()->GetNativeWindow()->IsVisible());
3630 }
3631
3632 // Check that V2 applications hide correctly on the shelf when the app window
3633 // is hidden.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,V2AppHiddenWindows)3634 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
3635 V2AppHiddenWindows) {
3636 InitLauncherController();
3637
3638 TestingProfile* profile2 = CreateMultiUserProfile("user-2");
3639 const AccountId account_id2(
3640 multi_user_util::GetAccountIdFromProfile(profile2));
3641 // If switch to account_id2 is not run, the following switch to account_id
3642 // is invalid, because the user account is not changed, so switch to
3643 // account_id2 first.
3644 SwitchActiveUser(account_id2);
3645
3646 const AccountId account_id(
3647 multi_user_util::GetAccountIdFromProfile(profile()));
3648 SwitchActiveUser(account_id);
3649 EXPECT_EQ(1, model_->item_count());
3650
3651 AddExtension(extension1_.get());
3652 V2App v2_app_1(profile(), extension1_.get());
3653 EXPECT_EQ(2, model_->item_count());
3654 {
3655 // Hide and show the app.
3656 v2_app_1.window()->Hide();
3657 EXPECT_EQ(1, model_->item_count());
3658
3659 v2_app_1.window()->Show(extensions::AppWindow::SHOW_ACTIVE);
3660 EXPECT_EQ(2, model_->item_count());
3661 }
3662 {
3663 // Switch user, hide and show the app and switch back.
3664 SwitchActiveUser(account_id2);
3665 EXPECT_EQ(1, model_->item_count());
3666
3667 v2_app_1.window()->Hide();
3668 EXPECT_EQ(1, model_->item_count());
3669
3670 v2_app_1.window()->Show(extensions::AppWindow::SHOW_ACTIVE);
3671 EXPECT_EQ(1, model_->item_count());
3672
3673 SwitchActiveUser(account_id);
3674 EXPECT_EQ(2, model_->item_count());
3675 }
3676 {
3677 // Switch user, hide the app, switch back and then show it again.
3678 SwitchActiveUser(account_id2);
3679 EXPECT_EQ(1, model_->item_count());
3680
3681 v2_app_1.window()->Hide();
3682 EXPECT_EQ(1, model_->item_count());
3683
3684 SwitchActiveUser(account_id);
3685 // The following expectation does not work in current impl. It was working
3686 // before because MultiProfileSupport is not attached to user
3687 // associated with profile() hence not actually handling windows for the
3688 // user. It is a real bug. See http://crbug.com/693634
3689 // EXPECT_EQ(2, model_->item_count());
3690
3691 v2_app_1.window()->Show(extensions::AppWindow::SHOW_ACTIVE);
3692 EXPECT_EQ(2, model_->item_count());
3693 }
3694 {
3695 // Create a second app, hide and show it and then hide both apps.
3696 V2App v2_app_2(profile(), extension1_.get());
3697 EXPECT_EQ(2, model_->item_count());
3698
3699 v2_app_2.window()->Hide();
3700 EXPECT_EQ(2, model_->item_count());
3701
3702 v2_app_2.window()->Show(extensions::AppWindow::SHOW_ACTIVE);
3703 EXPECT_EQ(2, model_->item_count());
3704
3705 v2_app_1.window()->Hide();
3706 v2_app_2.window()->Hide();
3707 EXPECT_EQ(1, model_->item_count());
3708 }
3709 }
3710
3711 // Checks that spinners are hidden and restored on profile switching
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,SpinnersUpdateOnUserSwitch)3712 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
3713 SpinnersUpdateOnUserSwitch) {
3714 InitLauncherController();
3715
3716 const AccountId account_id(
3717 multi_user_util::GetAccountIdFromProfile(profile()));
3718 const std::string user2 = "user2";
3719 const TestingProfile* profile2 = CreateMultiUserProfile(user2);
3720 const AccountId account_id2(
3721 multi_user_util::GetAccountIdFromProfile(profile2));
3722
3723 const std::string app_id = extension1_->id();
3724 extension_service_->AddExtension(extension1_.get());
3725
3726 EXPECT_EQ(1, model_->item_count());
3727 EXPECT_FALSE(
3728 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3729
3730 // Add a spinner to the shelf
3731 launcher_controller_->GetShelfSpinnerController()->AddSpinnerToShelf(
3732 app_id, std::make_unique<ShelfSpinnerItemController>(app_id));
3733 EXPECT_EQ(2, model_->item_count());
3734 EXPECT_TRUE(
3735 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3736
3737 // Switch to a new profile
3738 SwitchActiveUser(account_id2);
3739 EXPECT_EQ(1, model_->item_count());
3740 EXPECT_FALSE(
3741 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3742
3743 // Switch back
3744 SwitchActiveUser(account_id);
3745 EXPECT_EQ(2, model_->item_count());
3746 EXPECT_TRUE(
3747 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3748
3749 // Close the spinner
3750 launcher_controller_->GetShelfSpinnerController()->CloseSpinner(app_id);
3751 EXPECT_EQ(1, model_->item_count());
3752 EXPECT_FALSE(
3753 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3754 }
3755
3756 // Checks that pinned spinners are hidden and restored on profile switching
3757 // but are not removed when the spinner closes.
TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,PinnedSpinnersUpdateOnUserSwitch)3758 TEST_F(MultiProfileMultiBrowserShelfLayoutChromeLauncherControllerTest,
3759 PinnedSpinnersUpdateOnUserSwitch) {
3760 InitLauncherController();
3761
3762 const AccountId account_id(
3763 multi_user_util::GetAccountIdFromProfile(profile()));
3764 const std::string user2 = "user2";
3765 const TestingProfile* profile2 = CreateMultiUserProfile(user2);
3766 const AccountId account_id2(
3767 multi_user_util::GetAccountIdFromProfile(profile2));
3768
3769 const std::string app_id = extension1_->id();
3770 AddExtension(extension1_.get());
3771
3772 EXPECT_EQ(1, model_->item_count());
3773 EXPECT_FALSE(
3774 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3775
3776 // Pin an app to the shelf
3777 launcher_controller_->PinAppWithID(app_id);
3778 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
3779 EXPECT_EQ(2, model_->item_count());
3780 EXPECT_FALSE(
3781 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3782
3783 // Activate the spinner
3784 launcher_controller_->GetShelfSpinnerController()->AddSpinnerToShelf(
3785 app_id, std::make_unique<ShelfSpinnerItemController>(app_id));
3786 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
3787 EXPECT_EQ(2, model_->item_count());
3788 EXPECT_TRUE(
3789 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3790
3791 // Switch to a new profile
3792 SwitchActiveUser(account_id2);
3793 app_service_test().FlushMojoCalls();
3794 EXPECT_FALSE(launcher_controller_->IsAppPinned(app_id));
3795 EXPECT_EQ(1, model_->item_count());
3796 EXPECT_FALSE(
3797 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3798
3799 // Switch back
3800 SwitchActiveUser(account_id);
3801 app_service_test().FlushMojoCalls();
3802 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
3803 EXPECT_EQ(2, model_->item_count());
3804 EXPECT_TRUE(
3805 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3806
3807 // Close the spinner
3808 launcher_controller_->GetShelfSpinnerController()->CloseSpinner(app_id);
3809 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
3810 EXPECT_EQ(2, model_->item_count());
3811 EXPECT_FALSE(
3812 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
3813 }
3814
3815 // Checks that the generated menu list properly activates items.
TEST_F(ChromeLauncherControllerTest,V1AppMenuExecution)3816 TEST_F(ChromeLauncherControllerTest, V1AppMenuExecution) {
3817 InitLauncherControllerWithBrowser();
3818 StartPrefSyncService(syncer::SyncDataList());
3819
3820 // Add |extension_gmail_app_| to the launcher and add two items.
3821 GURL gmail = GURL("https://mail.google.com/mail/u");
3822 const ash::ShelfID gmail_id(extension_gmail_app_->id());
3823 AddExtension(extension_gmail_app_.get());
3824 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(kGmailUrl));
3825 base::string16 title1 = ASCIIToUTF16("Test1");
3826 NavigateAndCommitActiveTabWithTitle(browser(), GURL(kGmailUrl), title1);
3827 chrome::NewTab(browser());
3828 base::string16 title2 = ASCIIToUTF16("Test2");
3829 NavigateAndCommitActiveTabWithTitle(browser(), GURL(kGmailUrl), title2);
3830 app_service_test().WaitForAppService();
3831
3832 // Check that the menu is properly set.
3833 ash::ShelfItem item_gmail;
3834 item_gmail.type = ash::TYPE_PINNED_APP;
3835 item_gmail.id = gmail_id;
3836 base::string16 two_menu_items[] = {title1, title2};
3837 CheckAppMenu(launcher_controller_.get(), item_gmail, 2, two_menu_items);
3838 ash::ShelfItemDelegate* item_delegate =
3839 model_->GetShelfItemDelegate(gmail_id);
3840 ASSERT_TRUE(item_delegate);
3841 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
3842 // Execute the second item in the menu, after the title,
3843 // this shouldn't do anything since that item is already the active tab.
3844 {
3845 ash::ShelfApplicationMenuModel menu(
3846 base::string16(),
3847 launcher_controller_->GetAppMenuItemsForTesting(item_gmail),
3848 item_delegate);
3849 menu.ActivatedAt(2);
3850 }
3851 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
3852
3853 // Execute the first item in the menu, after the title,
3854 // this should activate the other tab.
3855 {
3856 ash::ShelfApplicationMenuModel menu(
3857 base::string16(),
3858 launcher_controller_->GetAppMenuItemsForTesting(item_gmail),
3859 item_delegate);
3860 menu.ActivatedAt(1);
3861 }
3862 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
3863 }
3864
3865 // Checks that the generated menu list properly deletes items.
TEST_F(ChromeLauncherControllerTest,V1AppMenuDeletionExecution)3866 TEST_F(ChromeLauncherControllerTest, V1AppMenuDeletionExecution) {
3867 InitLauncherControllerWithBrowser();
3868 StartPrefSyncService(syncer::SyncDataList());
3869
3870 // Add |extension_gmail_app_| to the launcher and add two items.
3871 const ash::ShelfID gmail_id(extension_gmail_app_->id());
3872 AddExtension(extension_gmail_app_.get());
3873 launcher_controller_->SetRefocusURLPatternForTest(gmail_id, GURL(kGmailUrl));
3874 base::string16 title1 = ASCIIToUTF16("Test1");
3875 NavigateAndCommitActiveTabWithTitle(browser(), GURL(kGmailUrl), title1);
3876 chrome::NewTab(browser());
3877 base::string16 title2 = ASCIIToUTF16("Test2");
3878 NavigateAndCommitActiveTabWithTitle(browser(), GURL(kGmailUrl), title2);
3879 app_service_test().WaitForAppService();
3880
3881 // Check that the menu is properly set.
3882 ash::ShelfItem item_gmail;
3883 item_gmail.type = ash::TYPE_PINNED_APP;
3884 item_gmail.id = gmail_id;
3885 base::string16 two_menu_items[] = {title1, title2};
3886 CheckAppMenu(launcher_controller_.get(), item_gmail, 2, two_menu_items);
3887
3888 ash::ShelfItemDelegate* item_delegate =
3889 model_->GetShelfItemDelegate(gmail_id);
3890 ASSERT_TRUE(item_delegate);
3891 int tabs = browser()->tab_strip_model()->count();
3892 // Activate the proper tab through the menu item.
3893 {
3894 auto items = launcher_controller_->GetAppMenuItemsForTesting(item_gmail);
3895 item_delegate->ExecuteCommand(false, 1, ui::EF_NONE,
3896 display::kInvalidDisplayId);
3897 EXPECT_EQ(tabs, browser()->tab_strip_model()->count());
3898 }
3899
3900 // Delete one tab through the menu item.
3901 {
3902 auto items = launcher_controller_->GetAppMenuItemsForTesting(item_gmail);
3903 item_delegate->ExecuteCommand(false, 1, ui::EF_SHIFT_DOWN,
3904 display::kInvalidDisplayId);
3905 EXPECT_EQ(--tabs, browser()->tab_strip_model()->count());
3906 }
3907 }
3908
3909 // Tests that the Gmail extension matches more than the app itself claims with
3910 // the manifest file.
TEST_F(ChromeLauncherControllerTest,GmailMatching)3911 TEST_F(ChromeLauncherControllerTest, GmailMatching) {
3912 InitLauncherControllerWithBrowser();
3913 StartPrefSyncService(syncer::SyncDataList());
3914
3915 // Create a Gmail browser tab.
3916 chrome::NewTab(browser());
3917 base::string16 title = ASCIIToUTF16("Test");
3918 NavigateAndCommitActiveTabWithTitle(browser(), GURL(kGmailUrl), title);
3919 content::WebContents* content =
3920 browser()->tab_strip_model()->GetActiveWebContents();
3921
3922 // Check that the launcher controller does not recognize the running app.
3923 EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content));
3924
3925 // Installing |extension_gmail_app_| pins it to the launcher.
3926 const ash::ShelfID gmail_id(extension_gmail_app_->id());
3927 AddExtension(extension_gmail_app_.get());
3928 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
3929
3930 // Check that it is now handled.
3931 EXPECT_TRUE(launcher_controller_->ContentCanBeHandledByGmailApp(content));
3932
3933 // Check also that the app has detected that properly.
3934 ash::ShelfItem item_gmail;
3935 item_gmail.type = ash::TYPE_PINNED_APP;
3936 item_gmail.id = gmail_id;
3937 EXPECT_EQ(1U,
3938 launcher_controller_->GetAppMenuItemsForTesting(item_gmail).size());
3939 }
3940
3941 // Tests that the Gmail extension does not match the offline version.
TEST_F(ChromeLauncherControllerTest,GmailOfflineMatching)3942 TEST_F(ChromeLauncherControllerTest, GmailOfflineMatching) {
3943 InitLauncherControllerWithBrowser();
3944
3945 StartPrefSyncService(syncer::SyncDataList());
3946
3947 // Create a Gmail browser tab.
3948 chrome::NewTab(browser());
3949 base::string16 title = ASCIIToUTF16("Test");
3950 NavigateAndCommitActiveTabWithTitle(browser(), GURL(kOfflineGmailUrl), title);
3951 content::WebContents* content =
3952 browser()->tab_strip_model()->GetActiveWebContents();
3953
3954 // Installing |extension_gmail_app_| pins it to the launcher.
3955 const ash::ShelfID gmail_id(extension_gmail_app_->id());
3956 AddExtension(extension_gmail_app_.get());
3957 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension_gmail_app_->id()));
3958
3959 // The content should not be able to be handled by the app.
3960 EXPECT_FALSE(launcher_controller_->ContentCanBeHandledByGmailApp(content));
3961 }
3962
3963 // Verify that the launcher item positions are persisted and restored.
TEST_F(ChromeLauncherControllerTest,PersistLauncherItemPositions)3964 TEST_F(ChromeLauncherControllerTest, PersistLauncherItemPositions) {
3965 InitLauncherController();
3966
3967 TestLauncherControllerHelper* helper = new TestLauncherControllerHelper;
3968 SetLauncherControllerHelper(helper);
3969
3970 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[0].type);
3971
3972 TabStripModel* tab_strip_model = browser()->tab_strip_model();
3973 EXPECT_EQ(0, tab_strip_model->count());
3974 chrome::NewTab(browser());
3975 chrome::NewTab(browser());
3976 EXPECT_EQ(2, tab_strip_model->count());
3977 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
3978 helper->SetAppID(tab_strip_model->GetWebContentsAt(1), "2");
3979
3980 EXPECT_FALSE(launcher_controller_->IsAppPinned("1"));
3981 launcher_controller_->PinAppWithID("1");
3982 EXPECT_TRUE(launcher_controller_->IsAppPinned("1"));
3983 launcher_controller_->PinAppWithID("2");
3984
3985 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[0].type);
3986 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
3987 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type);
3988
3989 // Move browser shortcut item from index 0 to index 2.
3990 model_->Move(0, 2);
3991 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[0].type);
3992 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
3993 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[2].type);
3994
3995 RecreateLauncherController();
3996 helper = new TestLauncherControllerHelper(profile());
3997 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
3998 helper->SetAppID(tab_strip_model->GetWebContentsAt(1), "2");
3999 SetLauncherControllerHelper(helper);
4000 launcher_controller_->Init();
4001
4002 // Check ShelfItems are restored after resetting ChromeLauncherController.
4003 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[0].type);
4004 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
4005 EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[2].type);
4006 }
4007
4008 // Verifies pinned apps are persisted and restored.
TEST_F(ChromeLauncherControllerTest,PersistPinned)4009 TEST_F(ChromeLauncherControllerTest, PersistPinned) {
4010 InitLauncherControllerWithBrowser();
4011 size_t initial_size = model_->items().size();
4012
4013 TabStripModel* tab_strip_model = browser()->tab_strip_model();
4014 EXPECT_EQ(1, tab_strip_model->count());
4015
4016 TestLauncherControllerHelper* helper = new TestLauncherControllerHelper;
4017 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
4018 SetLauncherControllerHelper(helper);
4019
4020 // app_icon_loader is owned by ChromeLauncherController.
4021 TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl;
4022 app_icon_loader->AddSupportedApp("1");
4023 SetAppIconLoader(std::unique_ptr<AppIconLoader>(app_icon_loader));
4024 EXPECT_EQ(0, app_icon_loader->fetch_count());
4025
4026 launcher_controller_->PinAppWithID("1");
4027 const int app_index = model_->ItemIndexByID(ash::ShelfID("1"));
4028 EXPECT_EQ(1, app_icon_loader->fetch_count());
4029 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[app_index].type);
4030 EXPECT_TRUE(launcher_controller_->IsAppPinned("1"));
4031 EXPECT_FALSE(launcher_controller_->IsAppPinned("0"));
4032 EXPECT_EQ(initial_size + 1, model_->items().size());
4033
4034 RecreateLauncherController();
4035 helper = new TestLauncherControllerHelper(profile());
4036 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
4037 SetLauncherControllerHelper(helper);
4038 // app_icon_loader is owned by ChromeLauncherController.
4039 app_icon_loader = new TestAppIconLoaderImpl;
4040 app_icon_loader->AddSupportedApp("1");
4041 SetAppIconLoader(std::unique_ptr<AppIconLoader>(app_icon_loader));
4042 launcher_controller_->Init();
4043
4044 EXPECT_EQ(1, app_icon_loader->fetch_count());
4045 ASSERT_EQ(initial_size + 1, model_->items().size());
4046 EXPECT_TRUE(launcher_controller_->IsAppPinned("1"));
4047 EXPECT_FALSE(launcher_controller_->IsAppPinned("0"));
4048 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[app_index].type);
4049
4050 launcher_controller_->UnpinAppWithID("1");
4051 EXPECT_EQ(initial_size + 1, model_->items().size());
4052
4053 tab_strip_model->CloseWebContentsAt(0, 0);
4054 EXPECT_EQ(initial_size, model_->items().size());
4055 }
4056
4057 // Verifies that ShelfID property is updated for browsers that are present when
4058 // ChromeLauncherController is created.
TEST_F(ChromeLauncherControllerTest,ExistingBrowserWindowShelfIDSet)4059 TEST_F(ChromeLauncherControllerTest, ExistingBrowserWindowShelfIDSet) {
4060 InitLauncherControllerWithBrowser();
4061 launcher_controller_->PinAppWithID("1");
4062
4063 TabStripModel* tab_strip_model = browser()->tab_strip_model();
4064 ASSERT_EQ(1, tab_strip_model->count());
4065
4066 TestLauncherControllerHelper* helper = new TestLauncherControllerHelper;
4067 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "0");
4068 SetLauncherControllerHelper(helper);
4069
4070 RecreateLauncherController();
4071 helper = new TestLauncherControllerHelper(profile());
4072 helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
4073 SetLauncherControllerHelper(helper);
4074 launcher_controller_->Init();
4075
4076 EXPECT_TRUE(launcher_controller_->GetItem(ash::ShelfID("1")));
4077 EXPECT_EQ(ash::ShelfID("1"),
4078 ash::ShelfID::Deserialize(
4079 browser()->window()->GetNativeWindow()->GetProperty(
4080 ash::kShelfIDKey)));
4081 }
4082
TEST_F(ChromeLauncherControllerTest,MultipleAppIconLoaders)4083 TEST_F(ChromeLauncherControllerTest, MultipleAppIconLoaders) {
4084 InitLauncherControllerWithBrowser();
4085
4086 const ash::ShelfID shelf_id1(extension1_->id());
4087 const ash::ShelfID shelf_id2(extension2_->id());
4088 const ash::ShelfID shelf_id3(extension_gmail_app_->id());
4089 // app_icon_loader1 and app_icon_loader2 are owned by
4090 // ChromeLauncherController.
4091 TestAppIconLoaderImpl* app_icon_loader1 = new TestAppIconLoaderImpl();
4092 TestAppIconLoaderImpl* app_icon_loader2 = new TestAppIconLoaderImpl();
4093 app_icon_loader1->AddSupportedApp(shelf_id1.app_id);
4094 app_icon_loader2->AddSupportedApp(shelf_id2.app_id);
4095 SetAppIconLoaders(std::unique_ptr<AppIconLoader>(app_icon_loader1),
4096 std::unique_ptr<AppIconLoader>(app_icon_loader2));
4097
4098 launcher_controller_->CreateAppLauncherItem(
4099 std::make_unique<AppServiceAppWindowLauncherItemController>(
4100 shelf_id3, launcher_controller_->app_service_app_window_controller()),
4101 ash::STATUS_RUNNING);
4102 EXPECT_EQ(0, app_icon_loader1->fetch_count());
4103 EXPECT_EQ(0, app_icon_loader1->clear_count());
4104 EXPECT_EQ(0, app_icon_loader2->fetch_count());
4105 EXPECT_EQ(0, app_icon_loader2->clear_count());
4106
4107 launcher_controller_->CreateAppLauncherItem(
4108 std::make_unique<AppServiceAppWindowLauncherItemController>(
4109 shelf_id2, launcher_controller_->app_service_app_window_controller()),
4110 ash::STATUS_RUNNING);
4111 EXPECT_EQ(0, app_icon_loader1->fetch_count());
4112 EXPECT_EQ(0, app_icon_loader1->clear_count());
4113 EXPECT_EQ(1, app_icon_loader2->fetch_count());
4114 EXPECT_EQ(0, app_icon_loader2->clear_count());
4115
4116 launcher_controller_->CreateAppLauncherItem(
4117 std::make_unique<AppServiceAppWindowLauncherItemController>(
4118 shelf_id1, launcher_controller_->app_service_app_window_controller()),
4119 ash::STATUS_RUNNING);
4120 EXPECT_EQ(1, app_icon_loader1->fetch_count());
4121 EXPECT_EQ(0, app_icon_loader1->clear_count());
4122 EXPECT_EQ(1, app_icon_loader2->fetch_count());
4123 EXPECT_EQ(0, app_icon_loader2->clear_count());
4124
4125 launcher_controller_->CloseLauncherItem(shelf_id1);
4126 EXPECT_EQ(1, app_icon_loader1->fetch_count());
4127 EXPECT_EQ(1, app_icon_loader1->clear_count());
4128 EXPECT_EQ(1, app_icon_loader2->fetch_count());
4129 EXPECT_EQ(0, app_icon_loader2->clear_count());
4130
4131 launcher_controller_->CloseLauncherItem(shelf_id2);
4132 EXPECT_EQ(1, app_icon_loader1->fetch_count());
4133 EXPECT_EQ(1, app_icon_loader1->clear_count());
4134 EXPECT_EQ(1, app_icon_loader2->fetch_count());
4135 EXPECT_EQ(1, app_icon_loader2->clear_count());
4136
4137 launcher_controller_->CloseLauncherItem(shelf_id3);
4138 EXPECT_EQ(1, app_icon_loader1->fetch_count());
4139 EXPECT_EQ(1, app_icon_loader1->clear_count());
4140 EXPECT_EQ(1, app_icon_loader2->fetch_count());
4141 EXPECT_EQ(1, app_icon_loader2->clear_count());
4142 }
4143
TEST_F(ChromeLauncherControllerWithArcTest,ArcAppPinPolicy)4144 TEST_F(ChromeLauncherControllerWithArcTest, ArcAppPinPolicy) {
4145 InitLauncherControllerWithBrowser();
4146 arc::mojom::AppInfo appinfo =
4147 CreateAppInfo("Some App", "SomeActivity", "com.example.app");
4148 const std::string app_id = AddArcAppAndShortcut(appinfo);
4149
4150 // Set policy, that makes pins ARC app. Unlike native extension, for ARC app
4151 // package_name (not hash) specified as id. In this test we check that
4152 // by hash we can determine that appropriate package was set by policy.
4153 base::ListValue policy_value;
4154 AppendPrefValue(&policy_value, appinfo.package_name);
4155 profile()->GetTestingPrefService()->SetManagedPref(
4156 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy());
4157
4158 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
4159 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
4160 GetPinnableForAppID(app_id, profile()));
4161 }
4162
TEST_F(ChromeLauncherControllerWithArcTest,ArcManaged)4163 TEST_F(ChromeLauncherControllerWithArcTest, ArcManaged) {
4164 extension_service_->AddExtension(arc_support_host_.get());
4165 // Test enables ARC, so turn it off for initial values.
4166 EnablePlayStore(false);
4167
4168 InitLauncherController();
4169
4170 // To prevent import legacy pins each time.
4171 // Initially pins are imported from legacy pref based model.
4172 StartPrefSyncService(syncer::SyncDataList());
4173
4174 // Initial run, ARC is not managed and disabled, Play Store pin should be
4175 // available.
4176 ValidateArcState(false, false, arc::ArcSessionManager::State::STOPPED,
4177 "Chrome, Play Store");
4178
4179 // ARC is managed and enabled, Play Store pin should be available.
4180 // Note: CHECKING_ANDROID_MANAGEMENT here means that opt-in flow is skipped.
4181 profile()->GetTestingPrefService()->SetManagedPref(
4182 arc::prefs::kArcEnabled, std::make_unique<base::Value>(true));
4183 base::RunLoop().RunUntilIdle();
4184 ValidateArcState(true, true,
4185 arc::ArcSessionManager::State::CHECKING_ANDROID_MANAGEMENT,
4186 "Chrome, Play Store");
4187
4188 // ARC is managed and disabled, Play Store pin should not be available.
4189 profile()->GetTestingPrefService()->SetManagedPref(
4190 arc::prefs::kArcEnabled, std::make_unique<base::Value>(false));
4191 base::RunLoop().RunUntilIdle();
4192 ValidateArcState(false, true, arc::ArcSessionManager::State::STOPPED,
4193 "Chrome");
4194
4195 // ARC is not managed and disabled, Play Store pin should be available.
4196 profile()->GetTestingPrefService()->RemoveManagedPref(
4197 arc::prefs::kArcEnabled);
4198 base::RunLoop().RunUntilIdle();
4199 ValidateArcState(false, false, arc::ArcSessionManager::State::STOPPED,
4200 "Chrome, Play Store");
4201
4202 // ARC is not managed and enabled, Play Store pin should be available.
4203 // Note: NEGOTIATING_TERMS_OF_SERVICE here means that opt-in flow starts.
4204 EnablePlayStore(true);
4205 ValidateArcState(true, false,
4206 arc::ArcSessionManager::State::NEGOTIATING_TERMS_OF_SERVICE,
4207 "Chrome, Play Store");
4208
4209 // User disables ARC. ARC is not managed and disabled, Play Store pin should
4210 // be automatically removed.
4211 EnablePlayStore(false);
4212 ValidateArcState(false, false, arc::ArcSessionManager::State::STOPPED,
4213 "Chrome");
4214
4215 // Even if re-enable it again, Play Store pin does not appear automatically.
4216 EnablePlayStore(true);
4217 ValidateArcState(true, false,
4218 arc::ArcSessionManager::State::NEGOTIATING_TERMS_OF_SERVICE,
4219 "Chrome");
4220 }
4221
4222 // Test the application menu of a shelf item with multiple ARC windows.
TEST_F(ChromeLauncherControllerWithArcTest,ShelfItemWithMultipleWindows)4223 TEST_F(ChromeLauncherControllerWithArcTest, ShelfItemWithMultipleWindows) {
4224 InitLauncherControllerWithBrowser();
4225
4226 arc::mojom::AppInfo appinfo =
4227 CreateAppInfo("Test1", "test", "com.example.app");
4228 AddArcAppAndShortcut(appinfo);
4229
4230 // Widgets will be deleted by the system.
4231 NotifyOnTaskCreated(appinfo, 1 /* task_id */);
4232 views::Widget* window1 = CreateArcWindow("org.chromium.arc.1");
4233 ASSERT_TRUE(window1);
4234 EXPECT_TRUE(window1->IsActive());
4235
4236 NotifyOnTaskCreated(appinfo, 2 /* task_id */);
4237 views::Widget* window2 = CreateArcWindow("org.chromium.arc.2");
4238 ASSERT_TRUE(window2);
4239
4240 EXPECT_FALSE(window1->IsActive());
4241 EXPECT_TRUE(window2->IsActive());
4242
4243 const std::string app_id = ArcAppTest::GetAppId(appinfo);
4244 ash::ShelfItemDelegate* item_delegate =
4245 model_->GetShelfItemDelegate(ash::ShelfID(app_id));
4246 ASSERT_TRUE(item_delegate);
4247
4248 // Selecting the item will show its application menu. It does not change the
4249 // active window.
4250 SelectItem(item_delegate);
4251 EXPECT_FALSE(window1->IsActive());
4252 EXPECT_TRUE(window2->IsActive());
4253
4254 // Command ids are just app window indices. Note, apps are registered in
4255 // opposite order. Last created goes in front.
4256 auto items = item_delegate->GetAppMenuItems(0, base::NullCallback());
4257 ASSERT_EQ(items.size(), 2U);
4258
4259 // Execute command 1 to activate the first window.
4260 item_delegate->ExecuteCommand(false, 1, ui::EF_NONE,
4261 display::kInvalidDisplayId);
4262 EXPECT_TRUE(window1->IsActive());
4263 EXPECT_FALSE(window2->IsActive());
4264
4265 // Selecting the item will show its application menu. It does not change the
4266 // active window.
4267 SelectItem(item_delegate);
4268 EXPECT_TRUE(window1->IsActive());
4269 EXPECT_FALSE(window2->IsActive());
4270
4271 // Execute command 0 to activate the second window.
4272 item_delegate->ExecuteCommand(false, 0, ui::EF_NONE,
4273 display::kInvalidDisplayId);
4274 EXPECT_FALSE(window1->IsActive());
4275 EXPECT_TRUE(window2->IsActive());
4276 }
4277
4278 namespace {
4279
4280 class ChromeLauncherControllerArcDefaultAppsTest
4281 : public ChromeLauncherControllerTest {
4282 public:
4283 ChromeLauncherControllerArcDefaultAppsTest() = default;
4284 ChromeLauncherControllerArcDefaultAppsTest(
4285 const ChromeLauncherControllerArcDefaultAppsTest&) = delete;
4286 ChromeLauncherControllerArcDefaultAppsTest& operator=(
4287 const ChromeLauncherControllerArcDefaultAppsTest&) = delete;
4288 ~ChromeLauncherControllerArcDefaultAppsTest() override = default;
4289
4290 protected:
SetUp()4291 void SetUp() override {
4292 ArcAppIcon::DisableSafeDecodingForTesting();
4293 ArcDefaultAppList::UseTestAppsDirectory();
4294 ChromeLauncherControllerTest::SetUp();
4295 }
4296 };
4297
4298 class ChromeLauncherControllerPlayStoreAvailabilityTest
4299 : public ChromeLauncherControllerTest,
4300 public ::testing::WithParamInterface<bool> {
4301 public:
4302 ChromeLauncherControllerPlayStoreAvailabilityTest() = default;
4303 ChromeLauncherControllerPlayStoreAvailabilityTest(
4304 const ChromeLauncherControllerPlayStoreAvailabilityTest&) = delete;
4305 ChromeLauncherControllerPlayStoreAvailabilityTest& operator=(
4306 const ChromeLauncherControllerPlayStoreAvailabilityTest&) = delete;
4307 ~ChromeLauncherControllerPlayStoreAvailabilityTest() override = default;
4308
4309 protected:
SetUp()4310 void SetUp() override {
4311 if (GetParam())
4312 arc::SetArcAlwaysStartWithoutPlayStoreForTesting();
4313 // To prevent crash on test exit and pending decode request.
4314 ArcAppIcon::DisableSafeDecodingForTesting();
4315 ArcDefaultAppList::UseTestAppsDirectory();
4316 ChromeLauncherControllerTest::SetUp();
4317 }
4318 };
4319
4320 } // namespace
4321
TEST_F(ChromeLauncherControllerArcDefaultAppsTest,DefaultApps)4322 TEST_F(ChromeLauncherControllerArcDefaultAppsTest, DefaultApps) {
4323 arc_test_.SetUp(profile());
4324 InitLauncherController();
4325
4326 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
4327 EnablePlayStore(false);
4328 EXPECT_FALSE(arc::IsArcPlayStoreEnabledForProfile(profile()));
4329 ASSERT_TRUE(prefs->GetAppIds().size());
4330
4331 const std::string app_id =
4332 ArcAppTest::GetAppId(arc_test_.fake_default_apps()[0]);
4333 const ash::ShelfID shelf_id(app_id);
4334 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
4335 EXPECT_TRUE(arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON,
4336 arc::UserInteractionType::NOT_USER_INITIATED));
4337 EXPECT_TRUE(arc::IsArcPlayStoreEnabledForProfile(profile()));
4338 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id));
4339
4340 // Stop ARC again. Shelf item should go away.
4341 EnablePlayStore(false);
4342
4343 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
4344
4345 EXPECT_TRUE(arc::LaunchApp(profile(), app_id, ui::EF_LEFT_MOUSE_BUTTON,
4346 arc::UserInteractionType::NOT_USER_INITIATED));
4347 EXPECT_TRUE(arc::IsArcPlayStoreEnabledForProfile(profile()));
4348 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id));
4349
4350 auto* item_delegate = model_->GetShelfItemDelegate(shelf_id);
4351 ASSERT_TRUE(item_delegate);
4352 EXPECT_TRUE(
4353 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
4354 // Initially, a default icon is set for the shelf item.
4355 EXPECT_FALSE(item_delegate->image_set_by_controller());
4356 auto get_icon = [=]() {
4357 return *launcher_controller_->GetItem(shelf_id)->image.bitmap();
4358 };
4359 const SkBitmap default_icon = get_icon();
4360
4361 std::string window_app_id("org.chromium.arc.1");
4362 CreateArcWindow(window_app_id);
4363 arc_test_.app_instance()->SendTaskCreated(1, arc_test_.fake_default_apps()[0],
4364 std::string());
4365 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id));
4366 // Refresh delegate, it was changed.
4367 item_delegate = model_->GetShelfItemDelegate(shelf_id);
4368 ASSERT_TRUE(item_delegate);
4369 EXPECT_FALSE(
4370 launcher_controller_->GetShelfSpinnerController()->HasApp(app_id));
4371 EXPECT_FALSE(item_delegate->image_set_by_controller());
4372 EXPECT_TRUE(gfx::test::AreBitmapsEqual(default_icon, get_icon()));
4373
4374 // Wait for the real app icon image to be decoded and set for the shelf item.
4375 base::RunLoop().RunUntilIdle();
4376 EXPECT_FALSE(gfx::test::AreBitmapsEqual(default_icon, get_icon()));
4377 }
4378
TEST_F(ChromeLauncherControllerArcDefaultAppsTest,PlayStoreDeferredLaunch)4379 TEST_F(ChromeLauncherControllerArcDefaultAppsTest, PlayStoreDeferredLaunch) {
4380 // Add ARC host app to enable Play Store default app.
4381 extension_service_->AddExtension(arc_support_host_.get());
4382 arc_test_.SetUp(profile());
4383 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
4384 EXPECT_TRUE(prefs->IsRegistered(arc::kPlayStoreAppId));
4385
4386 InitLauncherController();
4387
4388 EnablePlayStore(true);
4389
4390 // Pin Play Store. It should be pinned but not scheduled for deferred launch.
4391 launcher_controller_->PinAppWithID(arc::kPlayStoreAppId);
4392 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc::kPlayStoreAppId));
4393 EXPECT_FALSE(launcher_controller_->GetShelfSpinnerController()->HasApp(
4394 arc::kPlayStoreAppId));
4395
4396 // Simulate click. This should schedule Play Store for deferred launch.
4397 ash::ShelfItemDelegate* item_delegate =
4398 model_->GetShelfItemDelegate(ash::ShelfID(arc::kPlayStoreAppId));
4399 EXPECT_TRUE(item_delegate);
4400 SelectItem(item_delegate);
4401 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc::kPlayStoreAppId));
4402 EXPECT_TRUE(launcher_controller_->GetShelfSpinnerController()->HasApp(
4403 arc::kPlayStoreAppId));
4404 }
4405
TEST_F(ChromeLauncherControllerArcDefaultAppsTest,PlayStoreLaunchMetric)4406 TEST_F(ChromeLauncherControllerArcDefaultAppsTest, PlayStoreLaunchMetric) {
4407 extension_service_->AddExtension(arc_support_host_.get());
4408 arc_test_.SetUp(profile());
4409 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
4410
4411 InitLauncherController();
4412 EnablePlayStore(true);
4413
4414 // Play Store available now as a default app but is not ready yet.
4415 std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
4416 prefs->GetApp(arc::kPlayStoreAppId);
4417 ASSERT_TRUE(app_info);
4418 EXPECT_FALSE(app_info->ready);
4419
4420 constexpr char kHistogramName[] = "Arc.PlayStoreLaunch.TimeDelta";
4421
4422 // Launch Play Store in deferred mode.
4423 arc::LaunchApp(profile(), arc::kPlayStoreAppId, ui::EF_LEFT_MOUSE_BUTTON,
4424 arc::UserInteractionType::NOT_USER_INITIATED);
4425 // This is deferred launch, no actual intents are delivered to ARC.
4426 EXPECT_EQ(0U, arc_test_.app_instance()->launch_intents().size());
4427 arc::mojom::AppInfo app;
4428 app.activity = arc::kPlayStoreActivity;
4429 app.package_name = arc::kPlayStorePackage;
4430 arc_test_.app_instance()->SendRefreshAppList({app});
4431 ASSERT_EQ(1U, arc_test_.app_instance()->launch_intents().size());
4432 std::string play_store_window_id("org.chromium.arc.1");
4433 views::Widget* play_store_window = CreateArcWindow(play_store_window_id);
4434 arc_test_.app_instance()->SendTaskCreated(
4435 1, app, arc_test_.app_instance()->launch_intents()[0]);
4436 EXPECT_TRUE(
4437 launcher_controller_->GetItem(ash::ShelfID(arc::kPlayStoreAppId)));
4438 // UMA is reported since app becomes ready.
4439 base::HistogramBase* const histogram =
4440 base::StatisticsRecorder::FindHistogram(kHistogramName);
4441 ASSERT_TRUE(histogram);
4442 std::unique_ptr<base::HistogramSamples> samples = histogram->SnapshotDelta();
4443 ASSERT_EQ(1, samples->TotalCount());
4444 play_store_window->Close();
4445
4446 // Launch Play Store in app-ready mode.
4447 arc::LaunchApp(profile(), arc::kPlayStoreAppId, ui::EF_LEFT_MOUSE_BUTTON,
4448 arc::UserInteractionType::NOT_USER_INITIATED);
4449 ASSERT_EQ(2U, arc_test_.app_instance()->launch_intents().size());
4450 play_store_window_id = "org.chromium.arc.2";
4451 play_store_window = CreateArcWindow(play_store_window_id);
4452 arc_test_.app_instance()->SendTaskCreated(
4453 2, app, arc_test_.app_instance()->launch_intents()[1]);
4454 EXPECT_TRUE(
4455 launcher_controller_->GetItem(ash::ShelfID(arc::kPlayStoreAppId)));
4456 // UMA is reported for app-ready launch. Note, previous call of SnapshotDelta
4457 // resets samples, so we expect here only one recorded.
4458 EXPECT_EQ(1, histogram->SnapshotDelta()->TotalCount());
4459 play_store_window->Close();
4460 }
4461
4462 // Tests that the Play Store is not visible in AOSP image and visible in default
4463 // images.
TEST_P(ChromeLauncherControllerPlayStoreAvailabilityTest,Visible)4464 TEST_P(ChromeLauncherControllerPlayStoreAvailabilityTest, Visible) {
4465 extension_service_->AddExtension(arc_support_host_.get());
4466 arc_test_.SetUp(profile());
4467
4468 InitLauncherController();
4469 StartPrefSyncService(syncer::SyncDataList());
4470
4471 ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
4472 EXPECT_EQ(arc::IsPlayStoreAvailable(),
4473 prefs->IsRegistered(arc::kPlayStoreAppId));
4474 // If the Play Store available, it is pinned by default.
4475 EXPECT_EQ(arc::IsPlayStoreAvailable(),
4476 launcher_controller_->IsAppPinned(arc::kPlayStoreAppId));
4477 arc_test_.TearDown();
4478 }
4479
4480 // Checks the case when several app items have the same ordinal position (which
4481 // is valid case).
TEST_F(ChromeLauncherControllerTest,CheckPositionConflict)4482 TEST_F(ChromeLauncherControllerTest, CheckPositionConflict) {
4483 InitLauncherController();
4484
4485 extension_service_->AddExtension(extension1_.get());
4486 extension_service_->AddExtension(extension2_.get());
4487 extension_service_->AddExtension(extension_gmail_app_.get());
4488
4489 syncer::SyncChangeList sync_list;
4490 InsertAddPinChange(&sync_list, 0, extension_misc::kChromeAppId);
4491 InsertAddPinChange(&sync_list, 1, extension1_->id());
4492 InsertAddPinChange(&sync_list, 1, extension2_->id());
4493 InsertAddPinChange(&sync_list, 1, extension_gmail_app_->id());
4494 SendPinChanges(sync_list, true);
4495
4496 EXPECT_EQ("Chrome, App1, App2, Gmail", GetPinnedAppStatus());
4497
4498 const syncer::StringOrdinal position_chrome =
4499 app_list_syncable_service_->GetPinPosition(extension_misc::kChromeAppId);
4500 const syncer::StringOrdinal position_1 =
4501 app_list_syncable_service_->GetPinPosition(extension1_->id());
4502 const syncer::StringOrdinal position_2 =
4503 app_list_syncable_service_->GetPinPosition(extension2_->id());
4504 const syncer::StringOrdinal position_3 =
4505 app_list_syncable_service_->GetPinPosition(extension_gmail_app_->id());
4506 EXPECT_TRUE(position_chrome.LessThan(position_1));
4507 EXPECT_TRUE(position_1.Equals(position_2));
4508 EXPECT_TRUE(position_2.Equals(position_3));
4509
4510 // Move Chrome between App1 and App2.
4511 // Note, move target_index is in context when moved element is removed from
4512 // array first.
4513 model_->Move(0, 1);
4514 EXPECT_EQ("App1, Chrome, App2, Gmail", GetPinnedAppStatus());
4515
4516 // Expect sync positions for only Chrome is updated and its resolution is
4517 // after all duplicated ordinals.
4518 EXPECT_TRUE(position_3.LessThan(app_list_syncable_service_->GetPinPosition(
4519 extension_misc::kChromeAppId)));
4520 EXPECT_TRUE(position_1.Equals(
4521 app_list_syncable_service_->GetPinPosition(extension1_->id())));
4522 EXPECT_TRUE(position_1.Equals(
4523 app_list_syncable_service_->GetPinPosition(extension1_->id())));
4524 EXPECT_TRUE(position_2.Equals(
4525 app_list_syncable_service_->GetPinPosition(extension2_->id())));
4526 EXPECT_TRUE(position_3.Equals(
4527 app_list_syncable_service_->GetPinPosition(extension_gmail_app_->id())));
4528 }
4529
4530 // Test the case when sync app is turned off and we need to use local copy to
4531 // support user's pins.
TEST_F(ChromeLauncherControllerTest,SyncOffLocalUpdate)4532 TEST_F(ChromeLauncherControllerTest, SyncOffLocalUpdate) {
4533 InitLauncherController();
4534
4535 extension_service_->AddExtension(extension1_.get());
4536 extension_service_->AddExtension(extension2_.get());
4537
4538 syncer::SyncChangeList sync_list;
4539 InsertAddPinChange(&sync_list, 0, extension_misc::kChromeAppId);
4540 InsertAddPinChange(&sync_list, 1, extension1_->id());
4541 InsertAddPinChange(&sync_list, 1, extension2_->id());
4542 SendPinChanges(sync_list, true);
4543
4544 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
4545
4546 syncer::SyncDataList copy_sync_list =
4547 app_list_syncable_service_->GetAllSyncDataForTesting();
4548
4549 app_list_syncable_service_->StopSyncing(syncer::APP_LIST);
4550 RecreateLauncherController()->Init();
4551
4552 // Pinned state should not change.
4553 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
4554 launcher_controller_->UnpinAppWithID(extension2_->id());
4555 EXPECT_EQ("Chrome, App1", GetPinnedAppStatus());
4556
4557 // Resume syncing and sync information overrides local copy.
4558 StartAppSyncService(copy_sync_list);
4559 EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
4560 }
4561
4562 // Test the Settings can be pinned and unpinned.
TEST_F(ChromeLauncherControllerTest,InternalAppPinUnpin)4563 TEST_F(ChromeLauncherControllerTest, InternalAppPinUnpin) {
4564 InitLauncherController();
4565 // The model should only contain the browser shortcut item.
4566 EXPECT_EQ(1, model_->item_count());
4567
4568 const std::string app_id = ash::kInternalAppIdSettings;
4569 EXPECT_FALSE(launcher_controller_->IsAppPinned(app_id));
4570
4571 // Pin Settings.
4572 launcher_controller_->PinAppWithID(app_id);
4573 EXPECT_EQ(2, model_->item_count());
4574 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
4575 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[1].status);
4576 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
4577
4578 // Unpin Settings.
4579 launcher_controller_->UnpinAppWithID(app_id);
4580 EXPECT_EQ(1, model_->item_count());
4581 EXPECT_FALSE(launcher_controller_->IsAppPinned(app_id));
4582 }
4583
4584 // Test that internal app can be added and removed on shelf.
TEST_F(ChromeLauncherControllerTest,InternalAppWindowRecreation)4585 TEST_F(ChromeLauncherControllerTest, InternalAppWindowRecreation) {
4586 InitLauncherController();
4587
4588 // Only test the first internal app. The others should be the same.
4589 const auto& internal_app = app_list::GetInternalAppList(profile()).front();
4590 const std::string app_id = internal_app.app_id;
4591 const ash::ShelfID shelf_id(app_id);
4592 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
4593
4594 views::Widget* internal_app_window = CreateShelfAppWindow(app_id);
4595 ASSERT_TRUE(internal_app_window);
4596 base::RunLoop().RunUntilIdle();
4597 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id));
4598
4599 internal_app_window->Close();
4600 base::RunLoop().RunUntilIdle();
4601 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
4602
4603 // Create and close again.
4604 internal_app_window = CreateShelfAppWindow(app_id);
4605 ASSERT_TRUE(internal_app_window);
4606 base::RunLoop().RunUntilIdle();
4607 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id));
4608
4609 internal_app_window->Close();
4610 base::RunLoop().RunUntilIdle();
4611 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
4612 }
4613
4614 // Test that internal app can be added and removed by SetProperty of
4615 // ash::kShelfIDKey.
TEST_F(ChromeLauncherControllerTest,InternalAppWindowPropertyChanged)4616 TEST_F(ChromeLauncherControllerTest, InternalAppWindowPropertyChanged) {
4617 InitLauncherController();
4618
4619 // Only test the first internal app. The others should be the same.
4620 const auto internal_app = app_list::GetInternalAppList(profile()).front();
4621 std::string app_id;
4622 ash::ShelfID shelf_id;
4623 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
4624
4625 // Set an empty ash::kShelfIDKey.
4626 views::Widget* internal_app_window = CreateShelfAppWindow(app_id);
4627 ASSERT_TRUE(internal_app_window);
4628 base::RunLoop().RunUntilIdle();
4629 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
4630
4631 // Set an invalid ash::kShelfIDKey.
4632 app_id = "An invalid internal app id";
4633 shelf_id = ash::ShelfID(app_id);
4634 internal_app_window->GetNativeWindow()->SetProperty(
4635 ash::kShelfIDKey, new std::string(shelf_id.Serialize()));
4636 base::RunLoop().RunUntilIdle();
4637 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
4638
4639 // Set a valid ash::kShelfIDKey.
4640 app_id = internal_app.app_id;
4641 shelf_id = ash::ShelfID(app_id);
4642 internal_app_window->GetNativeWindow()->SetProperty(
4643 ash::kShelfIDKey, new std::string(shelf_id.Serialize()));
4644 base::RunLoop().RunUntilIdle();
4645 EXPECT_TRUE(launcher_controller_->GetItem(shelf_id));
4646
4647 internal_app_window->Close();
4648 base::RunLoop().RunUntilIdle();
4649 EXPECT_FALSE(launcher_controller_->GetItem(shelf_id));
4650 }
4651
4652 class ChromeLauncherControllerDemoModeTest
4653 : public ChromeLauncherControllerTest {
4654 protected:
ChromeLauncherControllerDemoModeTest()4655 ChromeLauncherControllerDemoModeTest() { auto_start_arc_test_ = true; }
4656 ChromeLauncherControllerDemoModeTest(
4657 const ChromeLauncherControllerDemoModeTest&) = delete;
4658 ChromeLauncherControllerDemoModeTest& operator=(
4659 const ChromeLauncherControllerDemoModeTest&) = delete;
4660 ~ChromeLauncherControllerDemoModeTest() override = default;
4661
SetUp()4662 void SetUp() override {
4663 // To prevent crash on test exit and pending decode request.
4664 ArcAppIcon::DisableSafeDecodingForTesting();
4665
4666 ChromeLauncherControllerTest::SetUp();
4667
4668 // Fake Demo Mode.
4669 demo_mode_test_helper_ = std::make_unique<chromeos::DemoModeTestHelper>();
4670 demo_mode_test_helper_->InitializeSession();
4671 }
4672
TearDown()4673 void TearDown() override {
4674 demo_mode_test_helper_.reset();
4675
4676 ChromeLauncherControllerTest::TearDown();
4677 }
4678
4679 private:
4680 std::unique_ptr<chromeos::DemoModeTestHelper> demo_mode_test_helper_;
4681 };
4682
TEST_F(ChromeLauncherControllerDemoModeTest,PinnedAppsOnline)4683 TEST_F(ChromeLauncherControllerDemoModeTest, PinnedAppsOnline) {
4684 network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
4685 network::mojom::ConnectionType::CONNECTION_ETHERNET);
4686
4687 InitLauncherControllerWithBrowser();
4688
4689 base::ListValue policy_value;
4690
4691 extension_service_->AddExtension(extension1_.get());
4692 extension_service_->AddExtension(extension2_.get());
4693 AppendPrefValue(&policy_value, extension1_->id());
4694 AppendPrefValue(&policy_value, extension2_->id());
4695
4696 arc::mojom::AppInfo appinfo =
4697 CreateAppInfo("Some App", "SomeActivity", "com.example.app");
4698 const std::string app_id = AddArcAppAndShortcut(appinfo);
4699
4700 arc::mojom::AppInfo online_only_appinfo =
4701 CreateAppInfo("Some App", "SomeActivity", "com.example.onlineonly");
4702 const std::string online_only_app_id =
4703 AddArcAppAndShortcut(online_only_appinfo);
4704
4705 AppendPrefValue(&policy_value, appinfo.package_name);
4706 AppendPrefValue(&policy_value, online_only_appinfo.package_name);
4707
4708 // If the device is offline, extension2 and onlineonly should be unpinned.
4709 chromeos::DemoSession::Get()->OverrideIgnorePinPolicyAppsForTesting(
4710 {extension2_->id(), online_only_appinfo.package_name});
4711
4712 profile()->GetTestingPrefService()->SetManagedPref(
4713 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy());
4714
4715 app_service_test().FlushMojoCalls();
4716
4717 // Since the device is online, all policy pinned apps are pinned.
4718 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
4719 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
4720 GetPinnableForAppID(extension1_->id(), profile()));
4721
4722 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
4723 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
4724 GetPinnableForAppID(extension2_->id(), profile()));
4725
4726 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
4727 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
4728 GetPinnableForAppID(app_id, profile()));
4729
4730 EXPECT_TRUE(launcher_controller_->IsAppPinned(online_only_app_id));
4731 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
4732 GetPinnableForAppID(online_only_app_id, profile()));
4733 }
4734
TEST_F(ChromeLauncherControllerDemoModeTest,PinnedAppsOffline)4735 TEST_F(ChromeLauncherControllerDemoModeTest, PinnedAppsOffline) {
4736 network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
4737 network::mojom::ConnectionType::CONNECTION_NONE);
4738
4739 InitLauncherControllerWithBrowser();
4740
4741 base::ListValue policy_value;
4742
4743 extension_service_->AddExtension(extension1_.get());
4744 extension_service_->AddExtension(extension2_.get());
4745 AppendPrefValue(&policy_value, extension1_->id());
4746 AppendPrefValue(&policy_value, extension2_->id());
4747
4748 arc::mojom::AppInfo appinfo =
4749 CreateAppInfo("Some App", "SomeActivity", "com.example.app");
4750 const std::string app_id = AddArcAppAndShortcut(appinfo);
4751
4752 arc::mojom::AppInfo online_only_appinfo =
4753 CreateAppInfo("Some App", "SomeActivity", "com.example.onlineonly");
4754 const std::string online_only_app_id =
4755 AddArcAppAndShortcut(online_only_appinfo);
4756
4757 AppendPrefValue(&policy_value, appinfo.package_name);
4758 AppendPrefValue(&policy_value, online_only_appinfo.package_name);
4759
4760 // If the device is offline, extension2 and onlineonly should be unpinned.
4761 chromeos::DemoSession::Get()->OverrideIgnorePinPolicyAppsForTesting(
4762 {extension2_->id(), online_only_appinfo.package_name});
4763
4764 profile()->GetTestingPrefService()->SetManagedPref(
4765 prefs::kPolicyPinnedLauncherApps, policy_value.CreateDeepCopy());
4766 app_service_test().FlushMojoCalls();
4767
4768 // Since the device is online, the policy pinned apps that shouldn't be pinned
4769 // in Demo Mode are unpinned.
4770 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
4771 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
4772 GetPinnableForAppID(extension1_->id(), profile()));
4773
4774 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension2_->id()));
4775 EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE,
4776 GetPinnableForAppID(extension2_->id(), profile()));
4777
4778 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
4779 EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
4780 GetPinnableForAppID(app_id, profile()));
4781
4782 EXPECT_FALSE(launcher_controller_->IsAppPinned(online_only_app_id));
4783 EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE,
4784 GetPinnableForAppID(online_only_app_id, profile()));
4785
4786 // Pin a Chrome app that would have been pinned by policy but was suppressed
4787 // for Demo Mode.
4788 launcher_controller_->PinAppWithID(extension2_->id());
4789 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
4790 EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE,
4791 GetPinnableForAppID(extension2_->id(), profile()));
4792
4793 // Pin an ARC app that would have been pinned by policy but was suppressed
4794 // for Demo Mode.
4795 launcher_controller_->PinAppWithID(online_only_app_id);
4796 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
4797 EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE,
4798 GetPinnableForAppID(online_only_app_id, profile()));
4799 }
4800
TEST_F(ChromeLauncherControllerTest,CrostiniTerminalPinUnpin)4801 TEST_F(ChromeLauncherControllerTest, CrostiniTerminalPinUnpin) {
4802 InitLauncherController();
4803
4804 // Load pinned Terminal from prefs without Crostini UI being allowed
4805 syncer::SyncChangeList sync_list;
4806 InsertAddPinChange(&sync_list, 1, crostini::kCrostiniTerminalSystemAppId);
4807 SendPinChanges(sync_list, true);
4808 EXPECT_EQ("Chrome", GetPinnedAppStatus());
4809
4810 // Reload after allowing Crostini UI
4811 crostini::CrostiniTestHelper test_helper(profile());
4812 test_helper.ReInitializeAppServiceIntegration();
4813 // TODO(crubug.com/918739): Fix pins are not refreshed on enabling Crostini.
4814 // As a workaround add any app that triggers pin update.
4815 AddExtension(extension1_.get());
4816 EXPECT_EQ("Chrome, Terminal", GetPinnedAppStatus());
4817
4818 // Unpin the Terminal
4819 launcher_controller_->UnpinAppWithID(crostini::kCrostiniTerminalSystemAppId);
4820 EXPECT_EQ("Chrome", GetPinnedAppStatus());
4821
4822 // Pin Terminal again.
4823 launcher_controller_->PinAppWithID(crostini::kCrostiniTerminalSystemAppId);
4824 EXPECT_EQ("Chrome, Terminal", GetPinnedAppStatus());
4825 }
4826
4827 // TODO(crbug.com/846546) Recognising app id from the browser app_name is only
4828 // necessary because the crostini terminal is a little hacky. Pending a better
4829 // terminal implementation we should remove this test.
TEST_F(ChromeLauncherControllerTest,CrostiniBrowserWindowsRecogniseShelfItem)4830 TEST_F(ChromeLauncherControllerTest, CrostiniBrowserWindowsRecogniseShelfItem) {
4831 InitLauncherController();
4832 crostini::CrostiniTestHelper helper(profile());
4833
4834 // We want to match this shelf item.
4835 ash::ShelfItem item;
4836 item.id = ash::ShelfID("blah");
4837 item.type = ash::ShelfItemType::TYPE_APP;
4838 model_->Add(item);
4839
4840 // We manually create a browser window with the correct app_name, as this is
4841 // how the app_id is communicated.
4842 Browser::CreateParams params = Browser::CreateParams::CreateForApp(
4843 crostini::AppNameFromCrostiniAppId(item.id.app_id),
4844 true /* trusted_srouce */, browser()->window()->GetBounds(), profile(),
4845 true /* user_gesture */);
4846 params.window = browser()->window();
4847 params.type = Browser::TYPE_NORMAL;
4848 Browser* b = Browser::Create(params);
4849 set_browser(b);
4850 chrome::NewTab(browser());
4851 browser()->window()->Show();
4852
4853 EXPECT_EQ(launcher_controller_->GetAppIDForWebContents(
4854 browser()->tab_strip_model()->GetActiveWebContents()),
4855 item.id.app_id);
4856 }
4857
4858 // Tests behavior for ensuring some component apps can be marked unpinnable.
TEST_F(ChromeLauncherControllerTest,UnpinnableComponentApps)4859 TEST_F(ChromeLauncherControllerTest, UnpinnableComponentApps) {
4860 InitLauncherController();
4861
4862 const char* kPinnableApp = file_manager::kFileManagerAppId;
4863 const char* kNoPinApps[] = {file_manager::kGalleryAppId,
4864 extension_misc::kFeedbackExtensionId};
4865
4866 EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE,
4867 GetPinnableForAppID(kPinnableApp, profile()));
4868 for (const char* id : kNoPinApps) {
4869 EXPECT_EQ(AppListControllerDelegate::NO_PIN,
4870 GetPinnableForAppID(id, profile()));
4871 }
4872 }
4873
TEST_F(ChromeLauncherControllerTest,DoNotShowInShelf)4874 TEST_F(ChromeLauncherControllerTest, DoNotShowInShelf) {
4875 syncer::SyncChangeList sync_list;
4876 InsertAddPinChange(&sync_list, 0, extension1_->id());
4877 InsertAddPinChange(&sync_list, 0, extension2_->id());
4878 SendPinChanges(sync_list, true);
4879
4880 AddExtension(extension1_.get());
4881 AddExtension(extension2_.get());
4882
4883 // Set App1.show_in_shelf to false.
4884 std::vector<apps::mojom::AppPtr> apps;
4885 apps::mojom::AppPtr app = apps::mojom::App::New();
4886 app->app_type = apps::mojom::AppType::kExtension;
4887 app->app_id = extension1_->id();
4888 app->show_in_shelf = apps::mojom::OptionalBool::kFalse;
4889 apps.push_back(std::move(app));
4890 apps::AppServiceProxyFactory::GetForProfile(profile())
4891 ->AppRegistryCache()
4892 .OnApps(std::move(apps));
4893
4894 InitLauncherController();
4895 EXPECT_EQ("Chrome, App2", GetPinnedAppStatus());
4896 }
4897
TEST_F(ChromeLauncherControllerWithArcTest,ReplacePinnedItem)4898 TEST_F(ChromeLauncherControllerWithArcTest, ReplacePinnedItem) {
4899 InitLauncherController();
4900 SendListOfArcApps();
4901
4902 const std::string arc_app_id1 =
4903 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]);
4904 const std::string arc_app_id2 =
4905 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]);
4906
4907 extension_service_->AddExtension(extension1_.get());
4908 extension_service_->AddExtension(extension2_.get());
4909
4910 launcher_controller_->PinAppWithID(extension1_->id());
4911 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
4912 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id1));
4913
4914 // Replace pin extension to ARC app
4915 launcher_controller_->ReplacePinnedItem(extension1_->id(), arc_app_id1);
4916 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id1));
4917 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
4918
4919 // Replace pin ARC app to ARC app
4920 launcher_controller_->ReplacePinnedItem(arc_app_id1, arc_app_id2);
4921 EXPECT_TRUE(launcher_controller_->IsAppPinned(arc_app_id2));
4922 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id1));
4923
4924 // Replace pin ARC app to extension app
4925 launcher_controller_->ReplacePinnedItem(arc_app_id2, extension1_->id());
4926 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
4927 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id2));
4928
4929 // Replace pin extension app to extension app
4930 launcher_controller_->ReplacePinnedItem(extension1_->id(), extension2_->id());
4931 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
4932 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
4933
4934 // Try to replace item that is not pinned.
4935 launcher_controller_->ReplacePinnedItem(arc_app_id2, extension1_->id());
4936 EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
4937 EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id2));
4938
4939 // Try to replace item with item that is already pinned.
4940 launcher_controller_->PinAppWithID(extension1_->id());
4941 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
4942 launcher_controller_->ReplacePinnedItem(extension2_->id(), extension1_->id());
4943 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
4944 EXPECT_TRUE(launcher_controller_->IsAppPinned(extension2_->id()));
4945 }
4946
TEST_F(ChromeLauncherControllerWithArcTest,PinAtIndex)4947 TEST_F(ChromeLauncherControllerWithArcTest, PinAtIndex) {
4948 InitLauncherController();
4949 SendListOfArcApps();
4950
4951 const std::string arc_app_id1 =
4952 ArcAppTest::GetAppId(arc_test_.fake_apps()[0]);
4953 const std::string arc_app_id2 =
4954 ArcAppTest::GetAppId(arc_test_.fake_apps()[1]);
4955
4956 extension_service_->AddExtension(extension1_.get());
4957 extension_service_->AddExtension(extension2_.get());
4958
4959 int index = 0;
4960 launcher_controller_->PinAppAtIndex(extension1_->id(), index);
4961 EXPECT_EQ(index,
4962 launcher_controller_->PinnedItemIndexByAppID(extension1_->id()));
4963
4964 launcher_controller_->PinAppAtIndex(extension2_->id(), index);
4965 EXPECT_EQ(index,
4966 launcher_controller_->PinnedItemIndexByAppID(extension2_->id()));
4967 EXPECT_NE(index,
4968 launcher_controller_->PinnedItemIndexByAppID(extension1_->id()));
4969
4970 index = 3;
4971 launcher_controller_->PinAppAtIndex(arc_app_id1, index);
4972 EXPECT_EQ(index, launcher_controller_->PinnedItemIndexByAppID(arc_app_id1));
4973
4974 // Test pinning at invalid index.
4975 index = -100;
4976 launcher_controller_->PinAppAtIndex(arc_app_id2, index);
4977 EXPECT_NE(index, launcher_controller_->PinnedItemIndexByAppID(arc_app_id2));
4978 EXPECT_EQ(-1, launcher_controller_->PinnedItemIndexByAppID(arc_app_id2));
4979
4980 // Test pinning already pinned app.
4981 index = 0;
4982 launcher_controller_->PinAppAtIndex(arc_app_id1, index);
4983 EXPECT_NE(index, launcher_controller_->PinnedItemIndexByAppID(arc_app_id1));
4984 EXPECT_EQ(3, launcher_controller_->PinnedItemIndexByAppID(arc_app_id1));
4985 }
4986
4987 class ChromeLauncherControllerWebAppTest : public ChromeLauncherControllerTest {
4988 protected:
ChromeLauncherControllerWebAppTest()4989 ChromeLauncherControllerWebAppTest() {}
4990
4991 ~ChromeLauncherControllerWebAppTest() override = default;
4992
MaybeStartWebAppProvider()4993 void MaybeStartWebAppProvider() override {
4994 auto system_web_app_manager =
4995 std::make_unique<web_app::TestSystemWebAppManager>(profile());
4996
4997 auto* provider = web_app::TestWebAppProvider::Get(profile());
4998 provider->SetSystemWebAppManager(std::move(system_web_app_manager));
4999 provider->SetRunSubsystemStartupTasks(true);
5000 provider->Start();
5001 }
5002 };
5003
5004 // Test the web app interaction flow: pin it, run it, unpin it, close it.
TEST_F(ChromeLauncherControllerWebAppTest,WebAppPinRunUnpinClose)5005 TEST_F(ChromeLauncherControllerWebAppTest, WebAppPinRunUnpinClose) {
5006 constexpr char kWebAppUrl[] = "https://webappone.com/";
5007 constexpr char kWebAppName[] = "WebApp1";
5008
5009 InitLauncherController();
5010
5011 const web_app::AppId app_id =
5012 web_app::InstallDummyWebApp(profile(), kWebAppName, GURL(kWebAppUrl));
5013 base::RunLoop().RunUntilIdle();
5014
5015 // The model should only contain the browser shortcut item.
5016 EXPECT_EQ(1, model_->item_count());
5017 EXPECT_FALSE(launcher_controller_->IsAppPinned(app_id));
5018 EXPECT_EQ(nullptr, launcher_controller_->GetItem(ash::ShelfID(app_id)));
5019
5020 // Pinning the app should create a new shelf item.
5021 launcher_controller_->PinAppWithID(app_id);
5022 EXPECT_EQ(2, model_->item_count());
5023 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
5024 EXPECT_EQ(ash::STATUS_CLOSED, model_->items()[1].status);
5025 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
5026 EXPECT_NE(nullptr, launcher_controller_->GetItem(ash::ShelfID(app_id)));
5027
5028 // Reporting that the app is running should just update the existing item.
5029 launcher_controller_->SetV1AppStatus(app_id, ash::STATUS_RUNNING);
5030 EXPECT_EQ(2, model_->item_count());
5031 EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[1].type);
5032 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[1].status);
5033 EXPECT_TRUE(launcher_controller_->IsAppPinned(app_id));
5034 EXPECT_NE(nullptr, launcher_controller_->GetItem(ash::ShelfID(app_id)));
5035
5036 // Unpinning the app should just update the existing item.
5037 launcher_controller_->UnpinAppWithID(app_id);
5038 EXPECT_EQ(2, model_->item_count());
5039 EXPECT_EQ(ash::TYPE_APP, model_->items()[1].type);
5040 EXPECT_EQ(ash::STATUS_RUNNING, model_->items()[1].status);
5041 EXPECT_FALSE(launcher_controller_->IsAppPinned(app_id));
5042 EXPECT_NE(nullptr, launcher_controller_->GetItem(ash::ShelfID(app_id)));
5043
5044 // Reporting that the app is closed should remove its shelf item.
5045 launcher_controller_->SetV1AppStatus(app_id, ash::STATUS_CLOSED);
5046 EXPECT_EQ(1, model_->item_count());
5047 EXPECT_FALSE(launcher_controller_->IsAppPinned(app_id));
5048 EXPECT_EQ(nullptr, launcher_controller_->GetItem(ash::ShelfID(app_id)));
5049 }
5050
5051 INSTANTIATE_TEST_SUITE_P(All,
5052 ChromeLauncherControllerPlayStoreAvailabilityTest,
5053 ::testing::Values(false, true));
5054