1 // Copyright 2019 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 "base/bind.h"
6 #include "base/macros.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/test/bind.h"
9 #include "build/build_config.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/installable/installable_metrics.h"
12 #include "chrome/browser/sync/test/integration/sync_test.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/browser_commands.h"
15 #include "chrome/browser/ui/browser_dialogs.h"
16 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
17 #include "chrome/browser/web_applications/components/app_registry_controller.h"
18 #include "chrome/browser/web_applications/components/app_shortcut_manager.h"
19 #include "chrome/browser/web_applications/components/install_finalizer.h"
20 #include "chrome/browser/web_applications/components/install_manager.h"
21 #include "chrome/browser/web_applications/components/os_integration_manager.h"
22 #include "chrome/browser/web_applications/components/web_application_info.h"
23 #include "chrome/browser/web_applications/test/test_os_integration_manager.h"
24 #include "chrome/browser/web_applications/test/test_web_app_provider.h"
25 #include "chrome/browser/web_applications/test/web_app_install_observer.h"
26 #include "chrome/browser/web_applications/test/web_app_test.h"
27 #include "chrome/browser/web_applications/web_app.h"
28 #include "chrome/browser/web_applications/web_app_provider.h"
29 #include "chrome/browser/web_applications/web_app_registrar.h"
30 #include "chrome/test/base/ui_test_utils.h"
31 #include "content/public/test/browser_test.h"
32 #include "content/public/test/test_utils.h"
33 #include "extensions/browser/app_sorting.h"
34 #include "extensions/browser/extension_system.h"
35 
36 namespace web_app {
37 namespace {
38 
CreateTestWebAppProvider(Profile * profile)39 std::unique_ptr<KeyedService> CreateTestWebAppProvider(Profile* profile) {
40   auto provider = std::make_unique<TestWebAppProvider>(profile);
41   provider->SetOsIntegrationManager(
42       std::make_unique<TestOsIntegrationManager>(profile, nullptr, nullptr));
43   provider->Start();
44   DCHECK(provider);
45   return provider;
46 }
47 
48 class TwoClientWebAppsBMOSyncTest : public SyncTest {
49  public:
TwoClientWebAppsBMOSyncTest()50   TwoClientWebAppsBMOSyncTest()
51       : SyncTest(TWO_CLIENT),
52         test_web_app_provider_creator_(
53             base::BindRepeating(&CreateTestWebAppProvider)) {}
54   ~TwoClientWebAppsBMOSyncTest() override = default;
55 
SetupClients()56   bool SetupClients() override {
57     bool result = SyncTest::SetupClients();
58     if (!result)
59       return result;
60     for (Profile* profile : GetAllProfiles()) {
61       auto* web_app_provider = WebAppProvider::Get(profile);
62       web_app_provider->install_finalizer()
63           .RemoveLegacyInstallFinalizerForTesting();
64       base::RunLoop loop;
65       web_app_provider->on_registry_ready().Post(FROM_HERE, loop.QuitClosure());
66       loop.Run();
67     }
68     return true;
69   }
70 
71   // Installs a dummy app with the given |url| on |profile1| and waits for it to
72   // sync to |profile2|. This ensures that the sync system has fully flushed any
73   // pending changes from |profile1| to |profile2|.
InstallDummyAppAndWaitForSync(const GURL & url,Profile * profile1,Profile * profile2)74   AppId InstallDummyAppAndWaitForSync(const GURL& url,
75                                       Profile* profile1,
76                                       Profile* profile2) {
77     WebApplicationInfo info = WebApplicationInfo();
78     info.title = base::UTF8ToUTF16(url.spec());
79     info.start_url = url;
80     AppId dummy_app_id = InstallApp(info, profile1);
81     EXPECT_EQ(
82         WebAppInstallObserver::CreateInstallListener(profile2, {dummy_app_id})
83             ->AwaitNextInstall(),
84         dummy_app_id);
85     return dummy_app_id;
86   }
87 
GetUserInitiatedAppURL() const88   GURL GetUserInitiatedAppURL() const {
89     return embedded_test_server()->GetURL("/web_apps/basic.html");
90   }
91 
GetUserInitiatedAppURL2() const92   GURL GetUserInitiatedAppURL2() const {
93     return embedded_test_server()->GetURL("/web_apps/no_service_worker.html");
94   }
95 
InstallAppAsUserInitiated(Profile * profile,WebappInstallSource source=WebappInstallSource::OMNIBOX_INSTALL_ICON,GURL start_url=GURL ())96   AppId InstallAppAsUserInitiated(
97       Profile* profile,
98       WebappInstallSource source = WebappInstallSource::OMNIBOX_INSTALL_ICON,
99       GURL start_url = GURL()) {
100     Browser* browser = CreateBrowser(profile);
101     if (!start_url.is_valid())
102       start_url = GetUserInitiatedAppURL();
103     ui_test_utils::NavigateToURL(browser, start_url);
104 
105     AppId app_id;
106     base::RunLoop run_loop;
107     WebAppProvider::Get(profile)
108         ->install_manager()
109         .InstallWebAppFromManifestWithFallback(
110             browser->tab_strip_model()->GetActiveWebContents(),
111             /*force_shortcut_app=*/false, source,
112             base::BindOnce(TestAcceptDialogCallback),
113             base::BindLambdaForTesting(
114                 [&](const AppId& new_app_id, InstallResultCode code) {
115                   EXPECT_EQ(code, InstallResultCode::kSuccessNewInstall);
116                   app_id = new_app_id;
117                   run_loop.Quit();
118                 }));
119     run_loop.Run();
120     return app_id;
121   }
122 
InstallApp(const WebApplicationInfo & info,Profile * profile)123   AppId InstallApp(const WebApplicationInfo& info, Profile* profile) {
124     return InstallApp(info, profile, WebappInstallSource::OMNIBOX_INSTALL_ICON);
125   }
126 
InstallApp(const WebApplicationInfo & info,Profile * profile,WebappInstallSource source)127   AppId InstallApp(const WebApplicationInfo& info,
128                    Profile* profile,
129                    WebappInstallSource source) {
130     DCHECK(info.start_url.is_valid());
131 
132     base::RunLoop run_loop;
133     AppId app_id;
134 
135     WebAppProvider::Get(profile)->install_manager().InstallWebAppFromInfo(
136         std::make_unique<WebApplicationInfo>(info), ForInstallableSite::kYes,
137         source,
138         base::BindLambdaForTesting(
139             [&run_loop, &app_id](const AppId& new_app_id,
140                                  InstallResultCode code) {
141               DCHECK_EQ(code, InstallResultCode::kSuccessNewInstall);
142               app_id = new_app_id;
143               run_loop.Quit();
144             }));
145     run_loop.Run();
146 
147     const AppRegistrar& registrar = GetRegistrar(profile);
148     EXPECT_EQ(base::UTF8ToUTF16(registrar.GetAppShortName(app_id)), info.title);
149     EXPECT_EQ(registrar.GetAppStartUrl(app_id), info.start_url);
150 
151     return app_id;
152   }
153 
GetRegistrar(Profile * profile)154   const WebAppRegistrar& GetRegistrar(Profile* profile) {
155     auto* web_app_registrar =
156         WebAppProvider::Get(profile)->registrar().AsWebAppRegistrar();
157     EXPECT_TRUE(web_app_registrar);
158     return *web_app_registrar;
159   }
160 
GetOsIntegrationManager(Profile * profile)161   TestOsIntegrationManager& GetOsIntegrationManager(Profile* profile) {
162     return reinterpret_cast<TestOsIntegrationManager&>(
163         WebAppProvider::Get(profile)->os_integration_manager());
164   }
165 
GetAppSorting(Profile * profile)166   extensions::AppSorting* GetAppSorting(Profile* profile) {
167     return extensions::ExtensionSystem::Get(profile)->app_sorting();
168   }
169 
AllProfilesHaveSameWebAppIds()170   bool AllProfilesHaveSameWebAppIds() {
171     base::Optional<base::flat_set<AppId>> app_ids;
172     for (Profile* profile : GetAllProfiles()) {
173       base::flat_set<AppId> profile_app_ids(GetRegistrar(profile).GetAppIds());
174       if (!app_ids) {
175         app_ids = profile_app_ids;
176       } else {
177         if (app_ids != profile_app_ids)
178           return false;
179       }
180     }
181     return true;
182   }
183 
184  private:
185   TestWebAppProviderCreator test_web_app_provider_creator_;
186 
187   DISALLOW_COPY_AND_ASSIGN(TwoClientWebAppsBMOSyncTest);
188 };
189 
190 // Test is flaky (crbug.com/1097050)
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,DISABLED_SyncDoubleInstallation)191 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,
192                        DISABLED_SyncDoubleInstallation) {
193   ASSERT_TRUE(SetupSync());
194   ASSERT_TRUE(embedded_test_server()->Start());
195   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
196 
197   // Install web app to both profiles.
198   AppId app_id = InstallAppAsUserInitiated(GetProfile(0));
199   AppId app_id2 = InstallAppAsUserInitiated(GetProfile(1));
200 
201   EXPECT_EQ(app_id, app_id2);
202 
203   // Install a 'dummy' app & wait for installation to ensure sync has processed
204   // the initial apps.
205   InstallDummyAppAndWaitForSync(GURL("http://www.dummy.org/"), GetProfile(0),
206                                 GetProfile(1));
207 
208   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
209 }
210 
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,SyncDoubleInstallationDifferentNames)211 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,
212                        SyncDoubleInstallationDifferentNames) {
213   ASSERT_TRUE(SetupClients());
214   WebApplicationInfo info;
215   info.title = base::UTF8ToUTF16("Test name");
216   info.start_url = GURL("http://www.chromium.org/path");
217 
218   // Install web app to both profiles.
219   AppId app_id = InstallApp(info, GetProfile(0));
220   // The web app has a different title on the second profile.
221   info.title = base::UTF8ToUTF16("Test name 2");
222   AppId app_id2 = InstallApp(info, GetProfile(1));
223 
224   EXPECT_EQ(app_id, app_id2);
225 
226   ASSERT_TRUE(SetupSync());
227 
228   // Install a 'dummy' app & wait for installation to ensure sync has processed
229   // the initial apps.
230   InstallDummyAppAndWaitForSync(GURL("http://www.dummy1.org/"), GetProfile(0),
231                                 GetProfile(1));
232   InstallDummyAppAndWaitForSync(GURL("http://www.dummy2.org/"), GetProfile(1),
233                                 GetProfile(0));
234 
235   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
236   // The titles should respect the installation, even though the sync system
237   // would only have one name.
238   EXPECT_EQ(GetRegistrar(GetProfile(0)).GetAppShortName(app_id), "Test name");
239   EXPECT_EQ(GetRegistrar(GetProfile(1)).GetAppShortName(app_id), "Test name 2");
240 }
241 
242 // Flaky, see crbug.com/1126404.
243 #if defined(OS_MAC) || defined(OS_LINUX) || defined(OS_CHROMEOS)
244 #define MAYBE_SyncDoubleInstallationDifferentUserDisplayMode \
245   DISABLED_SyncDoubleInstallationDifferentUserDisplayMode
246 #else
247 #define MAYBE_SyncDoubleInstallationDifferentUserDisplayMode \
248   SyncDoubleInstallationDifferentUserDisplayMode
249 #endif
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,MAYBE_SyncDoubleInstallationDifferentUserDisplayMode)250 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,
251                        MAYBE_SyncDoubleInstallationDifferentUserDisplayMode) {
252   ASSERT_TRUE(SetupSync());
253   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
254 
255   WebApplicationInfo info;
256   info.title = base::UTF8ToUTF16("Test name");
257   info.start_url = GURL("http://www.chromium.org/path");
258   info.open_as_window = true;
259 
260   // Install web app to both profiles.
261   AppId app_id = InstallApp(info, GetProfile(0));
262   // The web app has a different open on the second profile.
263   info.open_as_window = false;
264   AppId app_id2 = InstallApp(info, GetProfile(1));
265 
266   EXPECT_EQ(app_id, app_id2);
267 
268   // Install a 'dummy' app & wait for installation to ensure sync has processed
269   // the initial apps.
270   InstallDummyAppAndWaitForSync(GURL("http://www.dummy.org/"), GetProfile(0),
271                                 GetProfile(1));
272 
273   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
274 
275   // The user display setting is syned, so these should match. However, the
276   // actual value here is racy.
277   EXPECT_EQ(GetRegistrar(GetProfile(0)).GetAppUserDisplayMode(app_id),
278             GetRegistrar(GetProfile(1)).GetAppUserDisplayMode(app_id));
279 }
280 
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,DisplayMode)281 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest, DisplayMode) {
282   ASSERT_TRUE(SetupSync());
283   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
284   ASSERT_TRUE(embedded_test_server()->Start());
285 
286   // Install web app to profile 0 and wait for it to sync to profile 1.
287   AppId app_id = InstallAppAsUserInitiated(GetProfile(0));
288   EXPECT_EQ(WebAppInstallObserver(GetProfile(1)).AwaitNextInstall(), app_id);
289 
290   WebAppProvider::Get(GetProfile(1))
291       ->registry_controller()
292       .SetAppUserDisplayMode(app_id, web_app::DisplayMode::kBrowser,
293                              /*is_user_action=*/false);
294 
295   // Install a 'dummy' app & wait for installation to ensure sync has processed
296   // the initial apps.
297   InstallDummyAppAndWaitForSync(GURL("http://www.dummy.org/"), GetProfile(1),
298                                 GetProfile(0));
299 
300   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
301 
302   // The change should have synced to profile 0.
303   EXPECT_EQ(GetRegistrar(GetProfile(0)).GetAppUserDisplayMode(app_id),
304             web_app::DisplayMode::kBrowser);
305   // The user display settings is synced, so it should match.
306   EXPECT_EQ(GetRegistrar(GetProfile(0)).GetAppUserDisplayMode(app_id),
307             GetRegistrar(GetProfile(1)).GetAppUserDisplayMode(app_id));
308 }
309 
310 // Although the logic is allowed to be racy, the profiles should still end up
311 // with the same web app ids.
312 #if defined(OS_WIN)
313 // Flaky on windows, https://crbug.com/1111533
314 #define MAYBE_DoubleInstallWithUninstall DISABLED_DoubleInstallWithUninstall
315 #else
316 #define MAYBE_DoubleInstallWithUninstall DoubleInstallWithUninstall
317 #endif
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,MAYBE_DoubleInstallWithUninstall)318 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,
319                        MAYBE_DoubleInstallWithUninstall) {
320   ASSERT_TRUE(SetupSync());
321   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
322   ASSERT_TRUE(embedded_test_server()->Start());
323 
324   // Install web app to both profiles.
325   AppId app_id = InstallAppAsUserInitiated(GetProfile(0));
326   AppId app_id2 = InstallAppAsUserInitiated(GetProfile(1));
327   EXPECT_EQ(app_id, app_id2);
328 
329   // Uninstall the app from one of the profiles.
330   UninstallWebApp(GetProfile(0), app_id);
331 
332   // Install a 'dummy' app & wait for installation to ensure sync has processed
333   // the initial apps.
334   InstallDummyAppAndWaitForSync(GURL("http://www.dummy.org/"), GetProfile(0),
335                                 GetProfile(1));
336 
337   // The apps should either be installed on both or uninstalled on both. This
338   // fails, hence disabled test.
339   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
340 }
341 
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,NotSynced)342 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest, NotSynced) {
343   ASSERT_TRUE(SetupSync());
344   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
345   ASSERT_TRUE(embedded_test_server()->Start());
346 
347   // Install a non-syncing web app.
348   AppId app_id = InstallAppAsUserInitiated(
349       GetProfile(0), WebappInstallSource::EXTERNAL_DEFAULT);
350 
351   // Install a 'dummy' app & wait for installation to ensure sync has processed
352   // the initial apps.
353   InstallDummyAppAndWaitForSync(GURL("http://www.dummy.org/"), GetProfile(0),
354                                 GetProfile(1));
355 
356   // Profile 0 should have an extra unsynced app, and it should not be in
357   // profile 1.
358   EXPECT_FALSE(AllProfilesHaveSameWebAppIds());
359   EXPECT_FALSE(GetRegistrar(GetProfile(1)).IsInstalled(app_id));
360 }
361 
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,NotSyncedThenSynced)362 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest, NotSyncedThenSynced) {
363   ASSERT_TRUE(SetupSync());
364   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
365   ASSERT_TRUE(embedded_test_server()->Start());
366 
367   // Install a non-syncing web app.
368   AppId app_id = InstallAppAsUserInitiated(
369       GetProfile(0), WebappInstallSource::EXTERNAL_DEFAULT);
370 
371   // Install the same app as a syncing app on profile 1.
372   AppId app_id2 = InstallAppAsUserInitiated(GetProfile(1));
373   EXPECT_EQ(app_id, app_id2);
374 
375   // Install a 'dummy' app & wait for installation to ensure sync has processed
376   // the initial apps.
377   InstallDummyAppAndWaitForSync(GURL("http://www.dummy.org/"), GetProfile(0),
378                                 GetProfile(1));
379 
380   // The app is in both profiles.
381   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
382 
383   // The app should have synced from profile 0 to profile 1, which enables sync
384   // on profile 0. So changes should propagate from profile 0 to profile 1 now.
385   WebAppProvider::Get(GetProfile(0))
386       ->registry_controller()
387       .SetAppUserDisplayMode(app_id, web_app::DisplayMode::kBrowser,
388                              /*is_user_action=*/false);
389 
390   // Install a 'dummy' app & wait for installation to ensure sync has processed
391   // the initial apps.
392   InstallDummyAppAndWaitForSync(GURL("http://www.seconddummy.org/"),
393                                 GetProfile(0), GetProfile(1));
394 
395   // Check that profile 1 has the display mode change.
396   EXPECT_EQ(GetRegistrar(GetProfile(1)).GetAppUserDisplayMode(app_id),
397             web_app::DisplayMode::kBrowser);
398 
399   // The user display settings is syned, so it should match.
400   EXPECT_EQ(GetRegistrar(GetProfile(0)).GetAppUserDisplayMode(app_id),
401             GetRegistrar(GetProfile(1)).GetAppUserDisplayMode(app_id));
402 }
403 
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,PolicyAppPersistsUninstalledOnSync)404 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,
405                        PolicyAppPersistsUninstalledOnSync) {
406   ASSERT_TRUE(SetupSync());
407   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
408   ASSERT_TRUE(embedded_test_server()->Start());
409 
410   // Install a non-syncing web app.
411   AppId app_id = InstallAppAsUserInitiated(
412       GetProfile(0), WebappInstallSource::EXTERNAL_POLICY);
413 
414   // Install the same app as a syncing app on profile 1.
415   AppId app_id2 = InstallAppAsUserInitiated(GetProfile(1));
416   EXPECT_EQ(app_id, app_id2);
417 
418   // Install a 'dummy' app & wait for installation to ensure sync has processed
419   // the initial apps.
420   InstallDummyAppAndWaitForSync(GURL("http://www.dummy.org/"), GetProfile(1),
421                                 GetProfile(0));
422 
423   // The app is in both profiles.
424   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
425   const WebApp* app = GetRegistrar(GetProfile(0)).GetAppById(app_id);
426   ASSERT_TRUE(app);
427   EXPECT_TRUE(app->IsPolicyInstalledApp());
428   EXPECT_TRUE(app->IsSynced());
429 
430   // Uninstall the web app on the sync profile.
431   UninstallWebApp(GetProfile(1), app_id);
432 
433   // Install a 'dummy' app & wait for installation to ensure sync has processed
434   // the initial apps.
435   InstallDummyAppAndWaitForSync(GURL("http://www.seconddummy.org/"),
436                                 GetProfile(1), GetProfile(0));
437 
438   // The policy app should remain on profile 0.
439   EXPECT_FALSE(AllProfilesHaveSameWebAppIds());
440   app = GetRegistrar(GetProfile(0)).GetAppById(app_id);
441   ASSERT_TRUE(app);
442   EXPECT_TRUE(app->IsPolicyInstalledApp());
443   EXPECT_FALSE(app->IsSynced());
444 }
445 
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,AppSortingSynced)446 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest, AppSortingSynced) {
447   ASSERT_TRUE(SetupSync());
448   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
449   ASSERT_TRUE(embedded_test_server()->Start());
450 
451   AppId app_id = InstallAppAsUserInitiated(GetProfile(0));
452 
453   syncer::StringOrdinal page_ordinal =
454       GetAppSorting(GetProfile(0))->GetNaturalAppPageOrdinal();
455   syncer::StringOrdinal launch_ordinal =
456       GetAppSorting(GetProfile(0))->CreateNextAppLaunchOrdinal(page_ordinal);
457   GetAppSorting(GetProfile(0))->SetPageOrdinal(app_id, page_ordinal);
458   GetAppSorting(GetProfile(0))->SetAppLaunchOrdinal(app_id, launch_ordinal);
459 
460   // Install a 'dummy' app & wait for installation to ensure sync has processed
461   // the initial apps.
462   InstallDummyAppAndWaitForSync(GURL("http://www.dummy.org/"), GetProfile(0),
463                                 GetProfile(1));
464 
465   // The app is in both profiles.
466   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
467   EXPECT_EQ(page_ordinal, GetAppSorting(GetProfile(1))->GetPageOrdinal(app_id));
468   EXPECT_EQ(launch_ordinal,
469             GetAppSorting(GetProfile(1))->GetAppLaunchOrdinal(app_id));
470 }
471 
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,AppSortingFixCollisions)472 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest, AppSortingFixCollisions) {
473   ASSERT_TRUE(SetupSync());
474   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
475   ASSERT_TRUE(embedded_test_server()->Start());
476 
477   // Install two different apps.
478   AppId app_id1 = InstallAppAsUserInitiated(GetProfile(0));
479   AppId app_id2 = InstallAppAsUserInitiated(
480       GetProfile(0), WebappInstallSource::OMNIBOX_INSTALL_ICON,
481       GetUserInitiatedAppURL2());
482 
483   ASSERT_NE(app_id1, app_id2);
484 
485   // Wait for both of the webapps to be installed on profile 1.
486   WebAppInstallObserver::CreateInstallListener(GetProfile(1),
487                                                {app_id1, app_id2})
488       ->AwaitNextInstall();
489   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
490 
491   syncer::StringOrdinal page_ordinal =
492       GetAppSorting(GetProfile(0))->CreateFirstAppPageOrdinal();
493   syncer::StringOrdinal launch_ordinal =
494       GetAppSorting(GetProfile(0))->CreateNextAppLaunchOrdinal(page_ordinal);
495 
496   GetAppSorting(GetProfile(0))->SetPageOrdinal(app_id1, page_ordinal);
497   GetAppSorting(GetProfile(0))->SetAppLaunchOrdinal(app_id1, launch_ordinal);
498   GetAppSorting(GetProfile(1))->SetPageOrdinal(app_id2, page_ordinal);
499   GetAppSorting(GetProfile(1))->SetAppLaunchOrdinal(app_id2, launch_ordinal);
500 
501   // Install 'dummy' apps & wait for installation to ensure sync has processed
502   // the ordinals both ways.
503   InstallDummyAppAndWaitForSync(GURL("http://www.dummy1.org/"), GetProfile(0),
504                                 GetProfile(1));
505   InstallDummyAppAndWaitForSync(GURL("http://www.dummy2.org/"), GetProfile(1),
506                                 GetProfile(0));
507 
508   // Page & launch ordinals should be synced.
509   EXPECT_EQ(GetAppSorting(GetProfile(0))->GetPageOrdinal(app_id1),
510             GetAppSorting(GetProfile(1))->GetPageOrdinal(app_id1));
511   EXPECT_EQ(GetAppSorting(GetProfile(0))->GetAppLaunchOrdinal(app_id1),
512             GetAppSorting(GetProfile(1))->GetAppLaunchOrdinal(app_id1));
513   EXPECT_EQ(GetAppSorting(GetProfile(0))->GetPageOrdinal(app_id2),
514             GetAppSorting(GetProfile(1))->GetPageOrdinal(app_id2));
515   EXPECT_EQ(GetAppSorting(GetProfile(0))->GetAppLaunchOrdinal(app_id2),
516             GetAppSorting(GetProfile(1))->GetAppLaunchOrdinal(app_id2));
517 
518   // The page of app1 and app2 should be the same.
519   EXPECT_EQ(GetAppSorting(GetProfile(0))->GetPageOrdinal(app_id1),
520             GetAppSorting(GetProfile(0))->GetPageOrdinal(app_id2));
521   // But the launch ordinal must be different.
522   EXPECT_NE(GetAppSorting(GetProfile(0))->GetAppLaunchOrdinal(app_id1),
523             GetAppSorting(GetProfile(0))->GetAppLaunchOrdinal(app_id2));
524 }
525 
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,UninstallSynced)526 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest, UninstallSynced) {
527   ASSERT_TRUE(SetupSync());
528   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
529   ASSERT_TRUE(embedded_test_server()->Start());
530 
531   AppId app_id;
532   // Install & uninstall on profile 0, and validate profile 1 sees it.
533   {
534     base::RunLoop loop;
535     WebAppInstallObserver app_listener(GetProfile(1));
536     app_listener.SetWebAppInstalledDelegate(
537         base::BindLambdaForTesting([&](const AppId& installed_app_id) {
538           app_id = installed_app_id;
539           loop.Quit();
540         }));
541     app_id = InstallAppAsUserInitiated(GetProfile(0));
542     loop.Run();
543     EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
544   }
545 
546   // Uninstall the webapp on profile 0, and validate profile 1 gets the change.
547   {
548     base::RunLoop loop;
549     WebAppInstallObserver app_listener(GetProfile(1));
550     app_listener.SetWebAppUninstalledDelegate(
551         base::BindLambdaForTesting([&](const AppId& uninstalled_app_id) {
552           app_id = uninstalled_app_id;
553           loop.Quit();
554         }));
555     UninstallWebApp(GetProfile(0), app_id);
556     loop.Run();
557     EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
558   }
559 
560   // Next, install on profile 1, uninstall on profile 0, and validate that
561   // profile 1 sees it.
562   {
563     base::RunLoop loop;
564     WebAppInstallObserver app_listener(GetProfile(0));
565     app_listener.SetWebAppInstalledDelegate(
566         base::BindLambdaForTesting([&](const AppId& installed_app_id) {
567           app_id = installed_app_id;
568           loop.Quit();
569         }));
570     app_id = InstallAppAsUserInitiated(GetProfile(1));
571     loop.Run();
572     EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
573   }
574   {
575     base::RunLoop loop;
576     WebAppInstallObserver app_listener(GetProfile(1));
577     app_listener.SetWebAppUninstalledDelegate(
578         base::BindLambdaForTesting([&](const AppId& uninstalled_app_id) {
579           app_id = uninstalled_app_id;
580           loop.Quit();
581         }));
582     UninstallWebApp(GetProfile(0), app_id);
583     loop.Run();
584   }
585 
586   EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
587 }
588 
IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest,NoShortcutsCreatedOnSync)589 IN_PROC_BROWSER_TEST_F(TwoClientWebAppsBMOSyncTest, NoShortcutsCreatedOnSync) {
590   ASSERT_TRUE(SetupSync());
591   ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
592   ASSERT_TRUE(embedded_test_server()->Start());
593 
594   // Install & uninstall on profile 0, and validate profile 1 sees it.
595   {
596     base::RunLoop loop;
597     base::RepeatingCallback<void(const AppId&)> on_installed_closure;
598     base::RepeatingCallback<void(const AppId&)> on_hooks_closure;
599 #if defined(OS_CHROMEOS)
600     on_installed_closure = base::DoNothing();
601     on_hooks_closure = base::BindLambdaForTesting(
602         [&](const AppId& installed_app_id) { loop.Quit(); });
603 #else
604     on_installed_closure = base::BindLambdaForTesting(
605         [&](const AppId& installed_app_id) { loop.Quit(); });
606     on_hooks_closure = base::BindLambdaForTesting(
607         [](const AppId& installed_app_id) { FAIL(); });
608 #endif
609     WebAppInstallObserver app_listener(GetProfile(1));
610     app_listener.SetWebAppInstalledDelegate(on_installed_closure);
611     app_listener.SetWebAppInstalledWithOsHooksDelegate(on_hooks_closure);
612     InstallAppAsUserInitiated(GetProfile(0));
613     loop.Run();
614     EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
615   }
616   EXPECT_EQ(
617       1u, GetOsIntegrationManager(GetProfile(0)).num_create_shortcuts_calls());
618 #if defined(OS_CHROMEOS)
619   auto last_options =
620       GetOsIntegrationManager(GetProfile(1)).get_last_install_options();
621   EXPECT_TRUE(last_options.has_value());
622   OsHooksResults expected_os_hook_requests;
623   expected_os_hook_requests[OsHookType::kShortcuts] = true;
624   expected_os_hook_requests[OsHookType::kRunOnOsLogin] = false;
625   expected_os_hook_requests[OsHookType::kShortcutsMenu] = true;
626   expected_os_hook_requests[OsHookType::kFileHandlers] = true;
627   EXPECT_EQ(expected_os_hook_requests, last_options->os_hooks);
628   EXPECT_TRUE(last_options->add_to_desktop);
629   EXPECT_FALSE(last_options->add_to_quick_launch_bar);
630   EXPECT_EQ(
631       1u, GetOsIntegrationManager(GetProfile(1)).num_create_shortcuts_calls());
632 #else
633   EXPECT_FALSE(GetOsIntegrationManager(GetProfile(1))
634                    .get_last_install_options()
635                    .has_value());
636 #endif
637 }
638 
639 }  // namespace
640 }  // namespace web_app
641