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