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 "chrome/browser/web_applications/manifest_update_manager.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/callback_helpers.h"
11 #include "base/memory/scoped_refptr.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/test/bind.h"
15 #include "base/test/metrics/histogram_tester.h"
16 #include "base/test/scoped_feature_list.h"
17 #include "base/time/time.h"
18 #include "chrome/app/chrome_command_ids.h"
19 #include "chrome/browser/installable/installable_metrics.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_commands.h"
23 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
24 #include "chrome/browser/web_applications/components/app_icon_manager.h"
25 #include "chrome/browser/web_applications/components/app_registry_controller.h"
26 #include "chrome/browser/web_applications/components/app_shortcut_manager.h"
27 #include "chrome/browser/web_applications/components/install_finalizer.h"
28 #include "chrome/browser/web_applications/components/install_manager.h"
29 #include "chrome/browser/web_applications/components/os_integration_manager.h"
30 #include "chrome/browser/web_applications/components/pending_app_manager.h"
31 #include "chrome/browser/web_applications/components/web_app_constants.h"
32 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
33 #include "chrome/browser/web_applications/extensions/bookmark_app_registrar.h"
34 #include "chrome/browser/web_applications/system_web_app_manager.h"
35 #include "chrome/browser/web_applications/test/test_system_web_app_installation.h"
36 #include "chrome/browser/web_applications/test/web_app_install_observer.h"
37 #include "chrome/browser/web_applications/test/web_app_test.h"
38 #include "chrome/browser/web_applications/web_app.h"
39 #include "chrome/browser/web_applications/web_app_registrar.h"
40 #include "chrome/common/chrome_features.h"
41 #include "chrome/test/base/in_process_browser_test.h"
42 #include "chrome/test/base/ui_test_utils.h"
43 #include "content/public/common/content_features.h"
44 #include "content/public/test/browser_test.h"
45 #include "content/public/test/url_loader_interceptor.h"
46 #include "extensions/browser/extension_registry.h"
47 #include "extensions/browser/test_extension_registry_observer.h"
48 #include "net/test/embedded_test_server/http_request.h"
49 #include "net/test/embedded_test_server/http_response.h"
50 #include "testing/gtest/include/gtest/gtest.h"
51 #include "third_party/blink/public/common/manifest/manifest.h"
52 #include "third_party/skia/include/core/SkColor.h"
53 
54 namespace web_app {
55 
56 namespace {
57 
58 constexpr char kUpdateHistogramName[] = "Webapp.Update.ManifestUpdateResult";
59 
60 constexpr char kInstallableIconList[] = R"(
61   [
62     {
63       "src": "launcher-icon-4x.png",
64       "sizes": "192x192",
65       "type": "image/png"
66     }
67   ]
68 )";
69 constexpr SkColor kInstallableIconTopLeftColor =
70     SkColorSetRGB(0x15, 0x96, 0xE0);
71 
72 constexpr char kAnotherInstallableIconList[] = R"(
73   [
74     {
75       "src": "/banners/image-512px.png",
76       "sizes": "512x512",
77       "type": "image/png"
78     }
79   ]
80 )";
81 
82 constexpr char kAnotherShortcutsItemName[] = "Timeline";
83 constexpr char kAnotherShortcutsItemUrl[] = "/shortcut";
84 constexpr char kAnotherShortcutsItemShortName[] = "H";
85 constexpr char kAnotherShortcutsItemDescription[] = "Navigate home";
86 constexpr char kAnotherIconSrc[] = "/launcher-icon-4x.png";
87 constexpr int kAnotherIconSize = 192;
88 
89 constexpr char kShortcutsItem[] = R"(
90   [
91     {
92       "name": "Home",
93       "short_name": "HM",
94       "description": "Go home",
95       "url": ".",
96       "icons": [
97         {
98           "src": "/banners/image-512px.png",
99           "sizes": "512x512",
100           "type": "image/png"
101         }
102       ]
103     }
104   ]
105 )";
106 
107 constexpr char kShortcutsItems[] = R"(
108   [
109     {
110       "name": "Home",
111       "short_name": "HM",
112       "description": "Go home",
113       "url": ".",
114       "icons": [
115         {
116           "src": "/banners/image-512px.png",
117           "sizes": "512x512",
118           "type": "image/png"
119         }
120       ]
121     },
122     {
123       "name": "Settings",
124       "short_name": "ST",
125       "description": "App settings",
126       "url": "/settings",
127       "icons": [
128         {
129           "src": "launcher-icon-4x.png",
130           "sizes": "192x192",
131           "type": "image/png"
132         }
133       ]
134     }
135   ]
136 )";
137 
138 constexpr SkColor kAnotherInstallableIconTopLeftColor =
139     SkColorSetRGB(0x5C, 0x5C, 0x5C);
140 
GetManifestUpdateManager(Browser * browser)141 ManifestUpdateManager& GetManifestUpdateManager(Browser* browser) {
142   return WebAppProviderBase::GetProviderBase(browser->profile())
143       ->manifest_update_manager();
144 }
145 
146 class UpdateCheckResultAwaiter {
147  public:
UpdateCheckResultAwaiter(Browser * browser,const GURL & url)148   explicit UpdateCheckResultAwaiter(Browser* browser, const GURL& url)
149       : browser_(browser), url_(url) {
150     SetCallback();
151   }
152 
SetCallback()153   void SetCallback() {
154     GetManifestUpdateManager(browser_).SetResultCallbackForTesting(
155         base::BindOnce(&UpdateCheckResultAwaiter::OnResult,
156                        base::Unretained(this)));
157   }
158 
AwaitNextResult()159   ManifestUpdateResult AwaitNextResult() && {
160     run_loop_.Run();
161     return *result_;
162   }
163 
OnResult(const GURL & url,ManifestUpdateResult result)164   void OnResult(const GURL& url, ManifestUpdateResult result) {
165     if (url != url_) {
166       SetCallback();
167       return;
168     }
169     result_ = result;
170     run_loop_.Quit();
171   }
172 
173  private:
174   Browser* browser_ = nullptr;
175   const GURL& url_;
176   base::RunLoop run_loop_;
177   base::Optional<ManifestUpdateResult> result_;
178 };
179 
180 }  // namespace
181 
182 class ManifestUpdateManagerBrowserTest : public InProcessBrowserTest {
183  public:
ManifestUpdateManagerBrowserTest()184   ManifestUpdateManagerBrowserTest() {
185     scoped_feature_list_.InitAndEnableFeature(
186         features::kDesktopPWAsLocalUpdating);
187   }
188   ManifestUpdateManagerBrowserTest(const ManifestUpdateManagerBrowserTest&) =
189       delete;
190   ManifestUpdateManagerBrowserTest& operator=(
191       const ManifestUpdateManagerBrowserTest&) = delete;
192 
193   ~ManifestUpdateManagerBrowserTest() override = default;
194 
SetUp()195   void SetUp() override {
196     http_server_.AddDefaultHandlers(GetChromeTestDataDir());
197     http_server_.RegisterRequestHandler(base::BindRepeating(
198         &ManifestUpdateManagerBrowserTest::RequestHandlerOverride,
199         base::Unretained(this)));
200     ASSERT_TRUE(http_server_.Start());
201     // Suppress globally to avoid OS hooks deployed for system web app during
202     // WebAppProvider setup.
203     os_hooks_suppress_ =
204         OsIntegrationManager::ScopedSuppressOsHooksForTesting();
205     InProcessBrowserTest::SetUp();
206   }
207 
SetUpOnMainThread()208   void SetUpOnMainThread() override {
209     // Cannot construct RunLoop in constructor due to threading restrictions.
210     shortcut_run_loop_.emplace();
211   }
212 
OnShortcutInfoRetrieved(std::unique_ptr<ShortcutInfo> shortcut_info)213   void OnShortcutInfoRetrieved(std::unique_ptr<ShortcutInfo> shortcut_info) {
214     if (shortcut_info) {
215       updated_shortcut_top_left_color_ =
216           shortcut_info->favicon.begin()->AsBitmap().getColor(0, 0);
217     }
218     shortcut_run_loop_->Quit();
219   }
220 
CheckShortcutInfoUpdated(const AppId & app_id,SkColor top_left_color)221   void CheckShortcutInfoUpdated(const AppId& app_id, SkColor top_left_color) {
222     GetProvider().os_integration_manager().GetShortcutInfoForApp(
223         app_id, base::BindOnce(
224                     &ManifestUpdateManagerBrowserTest::OnShortcutInfoRetrieved,
225                     base::Unretained(this)));
226     shortcut_run_loop_->Run();
227     EXPECT_EQ(updated_shortcut_top_left_color_, top_left_color);
228   }
229 
RequestHandlerOverride(const net::test_server::HttpRequest & request)230   std::unique_ptr<net::test_server::HttpResponse> RequestHandlerOverride(
231       const net::test_server::HttpRequest& request) {
232     if (request_override_)
233       return request_override_.Run(request);
234     return nullptr;
235   }
236 
OverrideManifest(const char * manifest_template,const std::vector<std::string> & substitutions)237   void OverrideManifest(const char* manifest_template,
238                         const std::vector<std::string>& substitutions) {
239     std::string content = base::ReplaceStringPlaceholders(
240         manifest_template, substitutions, nullptr);
241     request_override_ = base::BindLambdaForTesting(
242         [this, content = std::move(content)](
243             const net::test_server::HttpRequest& request)
244             -> std::unique_ptr<net::test_server::HttpResponse> {
245           if (request.GetURL() != GetManifestURL())
246             return nullptr;
247           auto http_response =
248               std::make_unique<net::test_server::BasicHttpResponse>();
249           http_response->set_code(net::HTTP_FOUND);
250           http_response->set_content(content);
251           return std::move(http_response);
252         });
253   }
254 
GetAppURL() const255   GURL GetAppURL() const {
256     return http_server_.GetURL("/banners/manifest_test_page.html");
257   }
258 
GetManifestURL() const259   GURL GetManifestURL() const {
260     return http_server_.GetURL("/banners/manifest.json");
261   }
262 
InstallWebApp()263   AppId InstallWebApp() {
264     GURL app_url = GetAppURL();
265     ui_test_utils::NavigateToURL(browser(), app_url);
266 
267     AppId app_id;
268     base::RunLoop run_loop;
269     GetProvider().install_manager().InstallWebAppFromManifestWithFallback(
270         browser()->tab_strip_model()->GetActiveWebContents(),
271         /*force_shortcut_app=*/false, WebappInstallSource::OMNIBOX_INSTALL_ICON,
272         base::BindOnce(TestAcceptDialogCallback),
273         base::BindLambdaForTesting(
274             [&](const AppId& new_app_id, InstallResultCode code) {
275               EXPECT_EQ(code, InstallResultCode::kSuccessNewInstall);
276               app_id = new_app_id;
277               run_loop.Quit();
278             }));
279     run_loop.Run();
280     return app_id;
281   }
282 
InstallPolicyApp()283   AppId InstallPolicyApp() {
284     const GURL app_url = GetAppURL();
285     base::RunLoop run_loop;
286     ExternalInstallOptions install_options(
287         app_url, DisplayMode::kStandalone,
288         ExternalInstallSource::kExternalPolicy);
289     install_options.add_to_applications_menu = false;
290     install_options.add_to_desktop = false;
291     install_options.add_to_quick_launch_bar = false;
292     install_options.install_placeholder = true;
293     GetProvider().pending_app_manager().Install(
294         std::move(install_options),
295         base::BindLambdaForTesting(
296             [&](const GURL& installed_app_url, InstallResultCode code) {
297               EXPECT_EQ(installed_app_url, app_url);
298               EXPECT_EQ(code, InstallResultCode::kSuccessNewInstall);
299               run_loop.Quit();
300             }));
301     run_loop.Run();
302     return GetProvider().registrar().LookupExternalAppId(app_url).value();
303   }
304 
SetTimeOverride(base::Time time_override)305   void SetTimeOverride(base::Time time_override) {
306     GetManifestUpdateManager(browser()).set_time_override_for_testing(
307         time_override);
308   }
309 
GetResultAfterPageLoad(const GURL & url,const AppId * app_id)310   ManifestUpdateResult GetResultAfterPageLoad(const GURL& url,
311                                               const AppId* app_id) {
312     UpdateCheckResultAwaiter awaiter(browser(), url);
313     ui_test_utils::NavigateToURL(browser(), url);
314     return std::move(awaiter).AwaitNextResult();
315   }
316 
GetProvider()317   WebAppProviderBase& GetProvider() {
318     return *WebAppProviderBase::GetProviderBase(browser()->profile());
319   }
320 
321  protected:
322   net::EmbeddedTestServer::HandleRequestCallback request_override_;
323 
324   base::HistogramTester histogram_tester_;
325 
326   net::EmbeddedTestServer http_server_;
327 
328  private:
329   base::test::ScopedFeatureList scoped_feature_list_;
330 
331   base::Optional<base::RunLoop> shortcut_run_loop_;
332   base::Optional<SkColor> updated_shortcut_top_left_color_;
333   ScopedOsHooksSuppress os_hooks_suppress_;
334 };
335 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckOutOfScopeNavigation)336 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
337                        CheckOutOfScopeNavigation) {
338   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), nullptr),
339             ManifestUpdateResult::kNoAppInScope);
340 
341   AppId app_id = InstallWebApp();
342 
343   EXPECT_EQ(GetResultAfterPageLoad(GURL("http://example.org"), nullptr),
344             ManifestUpdateResult::kNoAppInScope);
345 
346   histogram_tester_.ExpectTotalCount(kUpdateHistogramName, 0);
347 }
348 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIsThrottled)349 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, CheckIsThrottled) {
350   constexpr base::TimeDelta kDelayBetweenChecks = base::TimeDelta::FromDays(1);
351   base::Time time_override = base::Time::UnixEpoch();
352   SetTimeOverride(time_override);
353 
354   AppId app_id = InstallWebApp();
355   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
356             ManifestUpdateResult::kAppUpToDate);
357 
358   time_override += kDelayBetweenChecks / 2;
359   SetTimeOverride(time_override);
360   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
361             ManifestUpdateResult::kThrottled);
362 
363   time_override += kDelayBetweenChecks;
364   SetTimeOverride(time_override);
365   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
366             ManifestUpdateResult::kAppUpToDate);
367 
368   time_override += kDelayBetweenChecks / 2;
369   SetTimeOverride(time_override);
370   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
371             ManifestUpdateResult::kThrottled);
372 
373   time_override += kDelayBetweenChecks * 2;
374   SetTimeOverride(time_override);
375   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
376             ManifestUpdateResult::kAppUpToDate);
377 
378   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
379                                       ManifestUpdateResult::kThrottled, 2);
380   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
381                                       ManifestUpdateResult::kAppUpToDate, 3);
382 }
383 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckCancelledByWebContentsDestroyed)384 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
385                        CheckCancelledByWebContentsDestroyed) {
386   AppId app_id = InstallWebApp();
387   GetManifestUpdateManager(browser()).hang_update_checks_for_testing();
388 
389   GURL url = GetAppURL();
390   UpdateCheckResultAwaiter awaiter(browser(), url);
391   ui_test_utils::NavigateToURL(browser(), url);
392   chrome::CloseTab(browser());
393   EXPECT_EQ(std::move(awaiter).AwaitNextResult(),
394             ManifestUpdateResult::kWebContentsDestroyed);
395   histogram_tester_.ExpectBucketCount(
396       kUpdateHistogramName, ManifestUpdateResult::kWebContentsDestroyed, 1);
397 }
398 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckCancelledByAppUninstalled)399 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
400                        CheckCancelledByAppUninstalled) {
401   AppId app_id = InstallWebApp();
402   GetManifestUpdateManager(browser()).hang_update_checks_for_testing();
403 
404   GURL url = GetAppURL();
405   UpdateCheckResultAwaiter awaiter(browser(), url);
406   ui_test_utils::NavigateToURL(browser(), url);
407   GetProvider().install_finalizer().UninstallWebAppFromSyncByUser(
408       app_id, base::DoNothing());
409   EXPECT_EQ(std::move(awaiter).AwaitNextResult(),
410             ManifestUpdateResult::kAppUninstalled);
411   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
412                                       ManifestUpdateResult::kAppUninstalled, 1);
413 }
414 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIgnoresWhitespaceDifferences)415 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
416                        CheckIgnoresWhitespaceDifferences) {
417   constexpr char kManifestTemplate[] = R"(
418     {
419       "name": "Test app",
420       "start_url": ".",
421       "scope": "/",
422       "display": "standalone",
423       "icons": $1
424       $2
425     }
426   )";
427   OverrideManifest(kManifestTemplate, {kInstallableIconList, ""});
428   AppId app_id = InstallWebApp();
429 
430   OverrideManifest(kManifestTemplate, {kInstallableIconList, "\n\n\n\n"});
431   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
432             ManifestUpdateResult::kAppUpToDate);
433   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
434                                       ManifestUpdateResult::kAppUpToDate, 1);
435 }
436 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIgnoresNameChange)437 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
438                        CheckIgnoresNameChange) {
439   constexpr char kManifestTemplate[] = R"(
440     {
441       "name": "$1",
442       "start_url": ".",
443       "scope": "/",
444       "display": "standalone",
445       "icons": $2
446     }
447   )";
448   OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList});
449   AppId app_id = InstallWebApp();
450 
451   OverrideManifest(kManifestTemplate,
452                    {"Different app name", kInstallableIconList});
453   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
454             ManifestUpdateResult::kAppUpToDate);
455   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
456                                       ManifestUpdateResult::kAppUpToDate, 1);
457 }
458 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIgnoresShortNameChange)459 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
460                        CheckIgnoresShortNameChange) {
461   constexpr char kManifestTemplate[] = R"(
462     {
463       "name": "Test app name",
464       "short_name": "$1",
465       "start_url": ".",
466       "scope": "/",
467       "display": "standalone",
468       "icons": $2
469     }
470   )";
471   OverrideManifest(kManifestTemplate,
472                    {"Short test app name", kInstallableIconList});
473   AppId app_id = InstallWebApp();
474 
475   OverrideManifest(kManifestTemplate,
476                    {"Different short test app name", kInstallableIconList});
477   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
478             ManifestUpdateResult::kAppUpToDate);
479   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
480                                       ManifestUpdateResult::kAppUpToDate, 1);
481 }
482 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIgnoresStartUrlChange)483 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
484                        CheckIgnoresStartUrlChange) {
485   constexpr char kManifestTemplate[] = R"(
486     {
487       "name": "Test app name",
488       "start_url": "$1",
489       "scope": "/",
490       "display": "standalone",
491       "icons": $2
492     }
493   )";
494   OverrideManifest(kManifestTemplate, {"a.html", kInstallableIconList});
495   AppId app_id = InstallWebApp();
496 
497   OverrideManifest(kManifestTemplate, {"b.html", kInstallableIconList});
498   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
499             ManifestUpdateResult::kAppUpToDate);
500   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
501                                       ManifestUpdateResult::kAppUpToDate, 1);
502 }
503 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIgnoresNoManifestChange)504 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
505                        CheckIgnoresNoManifestChange) {
506   constexpr char kManifestTemplate[] = R"(
507     {
508       "name": "Test app name",
509       "start_url": ".",
510       "scope": "/",
511       "display": "standalone",
512       "icons": $1
513     }
514   )";
515   OverrideManifest(kManifestTemplate, {kInstallableIconList});
516   AppId app_id = InstallWebApp();
517   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
518             ManifestUpdateResult::kAppUpToDate);
519   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
520                                       ManifestUpdateResult::kAppUpToDate, 1);
521 }
522 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIgnoresInvalidManifest)523 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
524                        CheckIgnoresInvalidManifest) {
525   constexpr char kManifestTemplate[] = R"(
526     {
527       "name": "Test app name",
528       "start_url": ".",
529       "scope": "/",
530       "display": "standalone",
531       "icons": $1,
532       $2
533     }
534   )";
535   OverrideManifest(kManifestTemplate, {kInstallableIconList, ""});
536   AppId app_id = InstallWebApp();
537   OverrideManifest(kManifestTemplate, {kInstallableIconList,
538                                        "invalid manifest syntax !@#$%^*&()"});
539   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
540             ManifestUpdateResult::kAppNotEligible);
541   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
542                                       ManifestUpdateResult::kAppNotEligible, 1);
543 }
544 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIgnoresNonLocalApps)545 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
546                        CheckIgnoresNonLocalApps) {
547   constexpr char kManifestTemplate[] = R"(
548     {
549       "name": "Test app name",
550       "start_url": ".",
551       "scope": "/",
552       "display": "standalone",
553       "icons": $1,
554       "theme_color": "$2"
555     }
556   )";
557   OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"});
558   AppId app_id = InstallWebApp();
559 
560   GetProvider().registry_controller().SetAppIsLocallyInstalled(app_id, false);
561   EXPECT_FALSE(GetProvider().registrar().IsLocallyInstalled(app_id));
562 
563   OverrideManifest(kManifestTemplate, {kInstallableIconList, "red"});
564   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
565             ManifestUpdateResult::kNoAppInScope);
566   histogram_tester_.ExpectTotalCount(kUpdateHistogramName, 0);
567 }
568 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIgnoresPlaceholderApps)569 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
570                        CheckIgnoresPlaceholderApps) {
571   // Set up app URL to redirect to force placeholder app to install.
572   const GURL app_url = GetAppURL();
573   request_override_ = base::BindLambdaForTesting(
574       [&app_url](const net::test_server::HttpRequest& request)
575           -> std::unique_ptr<net::test_server::HttpResponse> {
576         if (request.GetURL() != app_url)
577           return nullptr;
578         auto http_response =
579             std::make_unique<net::test_server::BasicHttpResponse>();
580         http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
581         http_response->AddCustomHeader(
582             "Location", "http://other-origin.com/defaultresponse");
583         http_response->set_content("redirect page");
584         return std::move(http_response);
585       });
586 
587   // Install via PendingAppManager, the redirect to a different origin should
588   // cause it to install a placeholder app.
589   AppId app_id = InstallPolicyApp();
590   EXPECT_TRUE(GetProvider().registrar().IsPlaceholderApp(app_id));
591 
592   // Manifest updating should ignore non-redirect loads for placeholder apps
593   // because the PendingAppManager will handle these.
594   constexpr char kManifestTemplate[] = R"(
595     {
596       "name": "Test app name",
597       "start_url": ".",
598       "scope": "/",
599       "display": "standalone",
600       "icons": $1
601     }
602   )";
603   OverrideManifest(kManifestTemplate, {kInstallableIconList});
604   EXPECT_EQ(GetResultAfterPageLoad(app_url, &app_id),
605             ManifestUpdateResult::kAppIsPlaceholder);
606   histogram_tester_.ExpectBucketCount(
607       kUpdateHistogramName, ManifestUpdateResult::kAppIsPlaceholder, 1);
608 }
609 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckFindsThemeColorChange)610 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
611                        CheckFindsThemeColorChange) {
612   constexpr char kManifestTemplate[] = R"(
613     {
614       "name": "Test app name",
615       "start_url": ".",
616       "scope": "/",
617       "display": "standalone",
618       "icons": $1,
619       "theme_color": "$2"
620     }
621   )";
622   OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"});
623   AppId app_id = InstallWebApp();
624   EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorBLUE);
625 
626   // Check that OnWebAppInstalled and OnWebAppUninstalled are not called
627   // if in-place web app update happens.
628   WebAppInstallObserver install_observer(&GetProvider().registrar());
629   install_observer.SetWebAppInstalledDelegate(
630       base::BindLambdaForTesting([](const AppId& app_id) { NOTREACHED(); }));
631   install_observer.SetWebAppUninstalledDelegate(
632       base::BindLambdaForTesting([](const AppId& app_id) { NOTREACHED(); }));
633 
634   // CSS #RRGGBBAA syntax.
635   OverrideManifest(kManifestTemplate, {kInstallableIconList, "#00FF00F0"});
636   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
637             ManifestUpdateResult::kAppUpdated);
638   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
639                                       ManifestUpdateResult::kAppUpdated, 1);
640   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
641 
642   // Updated theme_color loses any transparency.
643   EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id),
644             SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
645 }
646 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckKeepsSameName)647 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, CheckKeepsSameName) {
648   constexpr char kManifestTemplate[] = R"(
649     {
650       "name": "$1",
651       "start_url": ".",
652       "scope": "/",
653       "display": "standalone",
654       "icons": $2,
655       "theme_color": "$3"
656     }
657   )";
658   OverrideManifest(kManifestTemplate,
659                    {"App name 1", kInstallableIconList, "blue"});
660   AppId app_id = InstallWebApp();
661   EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorBLUE);
662   EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id), "App name 1");
663 
664   OverrideManifest(kManifestTemplate,
665                    {"App name 2", kInstallableIconList, "red"});
666   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
667             ManifestUpdateResult::kAppUpdated);
668   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
669                                       ManifestUpdateResult::kAppUpdated, 1);
670   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
671   EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorRED);
672   // The app name must not change without user confirmation.
673   EXPECT_EQ(GetProvider().registrar().GetAppShortName(app_id), "App name 1");
674 }
675 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckFindsIconUrlChange)676 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
677                        CheckFindsIconUrlChange) {
678   constexpr char kManifestTemplate[] = R"(
679     {
680       "name": "Test app name",
681       "start_url": ".",
682       "scope": "/",
683       "display": "standalone",
684       "icons": $1
685     }
686   )";
687   OverrideManifest(kManifestTemplate, {kInstallableIconList});
688   AppId app_id = InstallWebApp();
689 
690   OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList});
691   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
692             ManifestUpdateResult::kAppUpdated);
693   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
694                                       ManifestUpdateResult::kAppUpdated, 1);
695   CheckShortcutInfoUpdated(app_id, kAnotherInstallableIconTopLeftColor);
696 }
697 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckUpdatedPolicyAppsNotUninstallable)698 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
699                        CheckUpdatedPolicyAppsNotUninstallable) {
700   constexpr char kManifestTemplate[] = R"(
701     {
702       "name": "Test app name",
703       "start_url": ".",
704       "scope": "/",
705       "display": "standalone",
706       "theme_color": "$1",
707       "icons": $2
708     }
709   )";
710   OverrideManifest(kManifestTemplate, {"blue", kInstallableIconList});
711   AppId app_id = InstallPolicyApp();
712   EXPECT_FALSE(
713       GetProvider().install_finalizer().CanUserUninstallFromSync(app_id));
714 
715   OverrideManifest(kManifestTemplate, {"red", kInstallableIconList});
716   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
717             ManifestUpdateResult::kAppUpdated);
718   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
719                                       ManifestUpdateResult::kAppUpdated, 1);
720   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
721 
722   // Policy installed apps should continue to be not uninstallable by the user
723   // after updating.
724   EXPECT_FALSE(
725       GetProvider().install_finalizer().CanUserUninstallFromSync(app_id));
726 }
727 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckFindsScopeChange)728 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
729                        CheckFindsScopeChange) {
730   constexpr char kManifestTemplate[] = R"(
731     {
732       "name": "Test app name",
733       "start_url": ".",
734       "scope": "$1",
735       "display": "standalone",
736       "icons": $2
737     }
738   )";
739   OverrideManifest(kManifestTemplate, {"/banners/", kInstallableIconList});
740   AppId app_id = InstallWebApp();
741 
742   OverrideManifest(kManifestTemplate, {"/", kInstallableIconList});
743   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
744             ManifestUpdateResult::kAppUpdated);
745   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
746                                       ManifestUpdateResult::kAppUpdated, 1);
747   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
748   EXPECT_EQ(GetProvider().registrar().GetAppScope(app_id),
749             http_server_.GetURL("/"));
750 }
751 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckFindsDisplayChange)752 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
753                        CheckFindsDisplayChange) {
754   constexpr char kManifestTemplate[] = R"(
755     {
756       "name": "Test app name",
757       "start_url": ".",
758       "scope": "/",
759       "display": "$1",
760       "icons": $2
761     }
762   )";
763   OverrideManifest(kManifestTemplate, {"minimal-ui", kInstallableIconList});
764   AppId app_id = InstallWebApp();
765 
766   OverrideManifest(kManifestTemplate, {"standalone", kInstallableIconList});
767   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
768             ManifestUpdateResult::kAppUpdated);
769   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
770                                       ManifestUpdateResult::kAppUpdated, 1);
771   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
772   EXPECT_EQ(GetProvider().registrar().GetAppDisplayMode(app_id),
773             DisplayMode::kStandalone);
774 }
775 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckFindsDisplayBrowserChange)776 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
777                        CheckFindsDisplayBrowserChange) {
778   constexpr char kManifestTemplate[] = R"(
779     {
780       "name": "Test app name",
781       "start_url": ".",
782       "scope": "/",
783       "display": "$1",
784       "icons": $2
785     }
786   )";
787   OverrideManifest(kManifestTemplate, {"standalone", kInstallableIconList});
788   AppId app_id = InstallWebApp();
789   GetProvider().registry_controller().SetAppUserDisplayMode(
790       app_id, DisplayMode::kStandalone, /*is_user_action=*/false);
791 
792   OverrideManifest(kManifestTemplate, {"browser", kInstallableIconList});
793   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
794             ManifestUpdateResult::kAppUpdated);
795   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
796                                       ManifestUpdateResult::kAppUpdated, 1);
797   EXPECT_EQ(GetProvider().registrar().GetAppDisplayMode(app_id),
798             DisplayMode::kBrowser);
799 
800   // We don't touch the user's launch preference even if the app display mode
801   // changes. Instead the effective display mode changes.
802   EXPECT_EQ(GetProvider().registrar().GetAppUserDisplayMode(app_id),
803             DisplayMode::kStandalone);
804   EXPECT_EQ(GetProvider().registrar().GetAppEffectiveDisplayMode(app_id),
805             DisplayMode::kMinimalUi);
806 }
807 
808 // A dedicated test fixture for DisplayOverride, which is supported
809 // only for the new web apps mode, and requires a command line switch
810 // to enable manifest parsing.
811 class ManifestUpdateManagerBrowserTest_DisplayOverride
812     : public ManifestUpdateManagerBrowserTest {
813  public:
ManifestUpdateManagerBrowserTest_DisplayOverride()814   ManifestUpdateManagerBrowserTest_DisplayOverride() {
815     scoped_feature_list_.InitAndEnableFeature(
816         features::kWebAppManifestDisplayOverride);
817   }
818 
819  private:
820   base::test::ScopedFeatureList scoped_feature_list_;
821 };
822 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,CheckFindsDisplayOverrideChange)823 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,
824                        CheckFindsDisplayOverrideChange) {
825   constexpr char kManifestTemplate[] = R"(
826     {
827       "name": "Test app name",
828       "start_url": ".",
829       "scope": "/",
830       "display": "standalone",
831       "display_override": $1,
832       "icons": $2
833     }
834   )";
835 
836   OverrideManifest(kManifestTemplate,
837                    {R"([ "fullscreen", "standalone" ])", kInstallableIconList});
838   AppId app_id = InstallWebApp();
839 
840   OverrideManifest(kManifestTemplate,
841                    {R"([ "fullscreen", "minimal-ui" ])", kInstallableIconList});
842   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
843             ManifestUpdateResult::kAppUpdated);
844   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
845                                       ManifestUpdateResult::kAppUpdated, 1);
846   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
847 
848   std::vector<DisplayMode> app_display_mode_override =
849       GetProvider().registrar().GetAppDisplayModeOverride(app_id);
850 
851   ASSERT_EQ(2u, app_display_mode_override.size());
852   EXPECT_EQ(DisplayMode::kFullscreen, app_display_mode_override[0]);
853   EXPECT_EQ(DisplayMode::kMinimalUi, app_display_mode_override[1]);
854 }
855 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,CheckFindsNewDisplayOverride)856 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,
857                        CheckFindsNewDisplayOverride) {
858   constexpr char kManifestTemplate[] = R"(
859     {
860       "name": "Test app name",
861       "start_url": ".",
862       "scope": "/",
863       "display": "standalone",
864       $1
865       "icons": $2
866     }
867   )";
868 
869   // No display_override in manifest
870   OverrideManifest(kManifestTemplate, {"", kInstallableIconList});
871   AppId app_id = InstallWebApp();
872 
873   // Add display_override field
874   OverrideManifest(kManifestTemplate,
875                    {R"("display_override": [ "minimal-ui", "standalone" ],)",
876                     kInstallableIconList});
877   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
878             ManifestUpdateResult::kAppUpdated);
879   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
880                                       ManifestUpdateResult::kAppUpdated, 1);
881   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
882 
883   std::vector<DisplayMode> app_display_mode_override =
884       GetProvider().registrar().GetAppDisplayModeOverride(app_id);
885 
886   ASSERT_EQ(2u, app_display_mode_override.size());
887   EXPECT_EQ(DisplayMode::kMinimalUi, app_display_mode_override[0]);
888   EXPECT_EQ(DisplayMode::kStandalone, app_display_mode_override[1]);
889 }
890 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,CheckFindsDeletedDisplayOverride)891 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,
892                        CheckFindsDeletedDisplayOverride) {
893   constexpr char kManifestTemplate[] = R"(
894     {
895       "name": "Test app name",
896       "start_url": ".",
897       "scope": "/",
898       "display": "standalone",
899       $1
900       "icons": $2
901     }
902   )";
903 
904   // Ensure display_override exists in initial manifest
905   OverrideManifest(kManifestTemplate,
906                    {R"("display_override": [ "fullscreen", "minimal-ui" ],)",
907                     kInstallableIconList});
908   AppId app_id = InstallWebApp();
909 
910   // Remove display_override from manifest
911   OverrideManifest(kManifestTemplate, {"", kInstallableIconList});
912   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
913             ManifestUpdateResult::kAppUpdated);
914   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
915                                       ManifestUpdateResult::kAppUpdated, 1);
916   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
917 
918   std::vector<DisplayMode> app_display_mode_override =
919       GetProvider().registrar().GetAppDisplayModeOverride(app_id);
920 
921   ASSERT_EQ(0u, app_display_mode_override.size());
922 }
923 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,CheckFindsInvalidDisplayOverride)924 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,
925                        CheckFindsInvalidDisplayOverride) {
926   constexpr char kManifestTemplate[] = R"(
927     {
928       "name": "Test app name",
929       "start_url": ".",
930       "scope": "/",
931       "display": "standalone",
932       "display_override": $1,
933       "icons": $2
934     }
935   )";
936 
937   OverrideManifest(kManifestTemplate,
938                    {R"([ "browser", "fullscreen" ])", kInstallableIconList});
939   AppId app_id = InstallWebApp();
940 
941   ASSERT_EQ(2u,
942             GetProvider().registrar().GetAppDisplayModeOverride(app_id).size());
943 
944   // display_override contains only invalid values
945   OverrideManifest(kManifestTemplate,
946                    {R"( [ "invalid", 7 ])", kInstallableIconList});
947   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
948             ManifestUpdateResult::kAppUpdated);
949   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
950                                       ManifestUpdateResult::kAppUpdated, 1);
951   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
952 
953   std::vector<DisplayMode> app_display_mode_override =
954       GetProvider().registrar().GetAppDisplayModeOverride(app_id);
955 
956   ASSERT_EQ(0u, app_display_mode_override.size());
957 }
958 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,CheckIgnoresDisplayOverrideInvalidChange)959 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,
960                        CheckIgnoresDisplayOverrideInvalidChange) {
961   constexpr char kManifestTemplate[] = R"(
962     {
963       "name": "Test app name",
964       "start_url": ".",
965       "scope": "/",
966       "display": "standalone",
967       $1
968       "icons": $2
969     }
970   )";
971 
972   // No display_override in manifest
973   OverrideManifest(kManifestTemplate, {"", kInstallableIconList});
974   AppId app_id = InstallWebApp();
975 
976   // display_override contains only invalid values
977   OverrideManifest(
978       kManifestTemplate,
979       {R"("display_override": [ "invalid", 7 ],)", kInstallableIconList});
980   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
981             ManifestUpdateResult::kAppUpToDate);
982   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
983                                       ManifestUpdateResult::kAppUpToDate, 1);
984 }
985 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,CheckIgnoresDisplayOverrideChange)986 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_DisplayOverride,
987                        CheckIgnoresDisplayOverrideChange) {
988   constexpr char kManifestTemplate[] = R"(
989     {
990       "name": "Test app name",
991       "start_url": ".",
992       "scope": "/",
993       "display": "standalone",
994       "display_override": $1,
995       "icons": $2
996     }
997   )";
998 
999   OverrideManifest(kManifestTemplate,
1000                    {R"([ "standard", "fullscreen" ])", kInstallableIconList});
1001   AppId app_id = InstallWebApp();
1002 
1003   // display_override contains an additional invalid value
1004   OverrideManifest(
1005       kManifestTemplate,
1006       {R"([ "invalid", "standard", "fullscreen" ])", kInstallableIconList});
1007   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1008             ManifestUpdateResult::kAppUpToDate);
1009   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1010                                       ManifestUpdateResult::kAppUpToDate, 1);
1011 }
1012 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckFindsIconContentChange)1013 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
1014                        CheckFindsIconContentChange) {
1015   constexpr char kManifest[] = R"(
1016     {
1017       "name": "Test app name",
1018       "start_url": ".",
1019       "scope": "/",
1020       "display": "standalone",
1021       "icons": [
1022         {
1023           "src": "/web_apps/basic-192.png?ignore",
1024           "sizes": "192x192",
1025           "type": "image/png"
1026         }
1027       ]
1028     }
1029   )";
1030   OverrideManifest(kManifest, {});
1031   AppId app_id = InstallWebApp();
1032 
1033   // Replace the contents of basic-192.png with blue-192.png without changing
1034   // the URL.
1035   content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting(
1036       [this](content::URLLoaderInterceptor::RequestParams* params)
1037           -> bool /*intercepted*/ {
1038         if (params->url_request.url ==
1039             http_server_.GetURL("/web_apps/basic-192.png?ignore")) {
1040           content::URLLoaderInterceptor::WriteResponse(
1041               "chrome/test/data/web_apps/blue-192.png", params->client.get());
1042           return true;
1043         }
1044         return false;
1045       }));
1046 
1047   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1048             ManifestUpdateResult::kAppUpdated);
1049   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1050                                       ManifestUpdateResult::kAppUpdated, 1);
1051   CheckShortcutInfoUpdated(app_id, SK_ColorBLUE);
1052 
1053   EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/192,
1054                              /*x=*/0, /*y=*/0),
1055             SK_ColorBLUE);
1056 }
1057 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,CheckIgnoresIconDownloadFail)1058 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest,
1059                        CheckIgnoresIconDownloadFail) {
1060   constexpr char kManifest[] = R"(
1061     {
1062       "name": "Test app name",
1063       "start_url": ".",
1064       "scope": "/",
1065       "display": "standalone",
1066       "icons": [
1067         {
1068           "src": "/web_apps/basic-48.png?ignore",
1069           "sizes": "48x48",
1070           "type": "image/png"
1071         },
1072         {
1073           "src": "/web_apps/basic-192.png?ignore",
1074           "sizes": "192x192",
1075           "type": "image/png"
1076         }
1077       ]
1078     }
1079   )";
1080   OverrideManifest(kManifest, {});
1081   AppId app_id = InstallWebApp();
1082 
1083   // Make basic-48.png fail to download.
1084   // Replace the contents of basic-192.png with blue-192.png without changing
1085   // the URL.
1086   content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting(
1087       [this](content::URLLoaderInterceptor::RequestParams* params)
1088           -> bool /*intercepted*/ {
1089         if (params->url_request.url ==
1090             http_server_.GetURL("/web_apps/basic-48.png?ignore")) {
1091           content::URLLoaderInterceptor::WriteResponse("malformed response", "",
1092                                                        params->client.get());
1093           return true;
1094         }
1095         if (params->url_request.url ==
1096             http_server_.GetURL("/web_apps/basic-192.png?ignore")) {
1097           content::URLLoaderInterceptor::WriteResponse(
1098               "chrome/test/data/web_apps/blue-192.png", params->client.get());
1099           return true;
1100         }
1101         return false;
1102       }));
1103 
1104   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1105             ManifestUpdateResult::kIconDownloadFailed);
1106   histogram_tester_.ExpectBucketCount(
1107       kUpdateHistogramName, ManifestUpdateResult::kIconDownloadFailed, 1);
1108 
1109   EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/48, /*x=*/0,
1110                              /*y=*/0),
1111             SK_ColorBLACK);
1112   EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/192,
1113                              /*x=*/0, /*y=*/0),
1114             SK_ColorBLACK);
1115 }
1116 
1117 class ManifestUpdateManagerSystemAppBrowserTest
1118     : public ManifestUpdateManagerBrowserTest {
1119  public:
1120   static constexpr char kSystemAppManifestText[] =
1121       R"({
1122         "name": "Test System App",
1123         "display": "standalone",
1124         "icons": [
1125           {
1126             "src": "icon-256.png",
1127             "sizes": "256x256",
1128             "type": "image/png"
1129           }
1130         ],
1131         "start_url": "/pwa.html",
1132         "theme_color": "$1"
1133       })";
1134 
ManifestUpdateManagerSystemAppBrowserTest()1135   ManifestUpdateManagerSystemAppBrowserTest()
1136       : system_app_(
1137             TestSystemWebAppInstallation::SetUpStandaloneSingleWindowApp(
1138                 false)) {
1139     system_app_->SetManifest(base::ReplaceStringPlaceholders(
1140         kSystemAppManifestText, {"#0f0"}, nullptr));
1141   }
1142 
SetUpOnMainThread()1143   void SetUpOnMainThread() override { system_app_->WaitForAppInstall(); }
1144 
1145  protected:
1146   std::unique_ptr<TestSystemWebAppInstallation> system_app_;
1147 };
1148 
1149 constexpr char
1150     ManifestUpdateManagerSystemAppBrowserTest::kSystemAppManifestText[];
1151 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerSystemAppBrowserTest,CheckUpdateSkipped)1152 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerSystemAppBrowserTest,
1153                        CheckUpdateSkipped) {
1154   system_app_->SetManifest(base::ReplaceStringPlaceholders(
1155       kSystemAppManifestText, {"#f00"}, nullptr));
1156 
1157   AppId app_id = system_app_->GetAppId();
1158   EXPECT_EQ(GetResultAfterPageLoad(system_app_->GetAppUrl(), &app_id),
1159             ManifestUpdateResult::kAppIsSystemWebApp);
1160 
1161   histogram_tester_.ExpectBucketCount(
1162       kUpdateHistogramName, ManifestUpdateResult::kAppIsSystemWebApp, 1);
1163   EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorGREEN);
1164 }
1165 
1166 using ManifestUpdateManagerWebAppsBrowserTest =
1167     ManifestUpdateManagerBrowserTest;
1168 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,CheckFindsThemeColorChangeForShadowBookmarkApp)1169 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,
1170                        CheckFindsThemeColorChangeForShadowBookmarkApp) {
1171   auto* extensions_registry =
1172       extensions::ExtensionRegistry::Get(browser()->profile());
1173   extensions::TestExtensionRegistryObserver extensions_registry_observer(
1174       extensions_registry);
1175 
1176   extensions::BookmarkAppRegistrar bookmark_app_registrar{browser()->profile()};
1177 
1178   constexpr char kManifestTemplate[] = R"(
1179     {
1180       "name": "Test app name",
1181       "start_url": ".",
1182       "scope": "/",
1183       "display": "standalone",
1184       "icons": $1,
1185       "theme_color": "$2"
1186     }
1187   )";
1188   OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"});
1189   AppId app_id = InstallWebApp();
1190   EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorBLUE);
1191 
1192   scoped_refptr<const extensions::Extension> extension =
1193       extensions_registry_observer.WaitForExtensionInstalled();
1194   EXPECT_EQ(extension->id(), app_id);
1195   EXPECT_EQ(bookmark_app_registrar.GetAppThemeColor(app_id).value(),
1196             SK_ColorBLUE);
1197 
1198   OverrideManifest(kManifestTemplate, {kInstallableIconList, "red"});
1199   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1200             ManifestUpdateResult::kAppUpdated);
1201   CheckShortcutInfoUpdated(app_id, kInstallableIconTopLeftColor);
1202   EXPECT_EQ(GetProvider().registrar().GetAppThemeColor(app_id), SK_ColorRED);
1203 
1204   // Wait for all update events sequentially. Otherwise the test is flaky.
1205   extensions_registry_observer.WaitForExtensionUnloaded();
1206   extensions_registry_observer.WaitForExtensionLoaded();
1207   extension = extensions_registry_observer.WaitForExtensionReady();
1208 
1209   EXPECT_EQ(extension->id(), app_id);
1210   EXPECT_EQ(bookmark_app_registrar.GetAppThemeColor(app_id).value(),
1211             SK_ColorRED);
1212 }
1213 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,CheckFindsAddedShareTarget)1214 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,
1215                        CheckFindsAddedShareTarget) {
1216   constexpr char kManifestTemplate[] = R"(
1217     {
1218       "name": "Test app name",
1219       "start_url": ".",
1220       "scope": "/",
1221       "display": "minimal-ui",
1222       "icons": $1
1223     }
1224   )";
1225 
1226   constexpr char kShareTargetManifestTemplate[] = R"(
1227     {
1228       "name": "Test app name",
1229       "start_url": ".",
1230       "scope": "/",
1231       "display": "minimal-ui",
1232       "share_target": {
1233         "action": "/web_share_target/share.html",
1234         "method": "GET",
1235         "params": {
1236           "url": "link"
1237         }
1238       },
1239       "icons": $1
1240     }
1241   )";
1242 
1243   OverrideManifest(kManifestTemplate, {kInstallableIconList});
1244   AppId app_id = InstallWebApp();
1245 
1246   OverrideManifest(kShareTargetManifestTemplate, {kInstallableIconList});
1247   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1248             ManifestUpdateResult::kAppUpdated);
1249   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1250                                       ManifestUpdateResult::kAppUpdated, 1);
1251 
1252   const WebApp* web_app =
1253       GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
1254   EXPECT_TRUE(web_app->share_target().has_value());
1255   EXPECT_EQ(web_app->share_target()->method, apps::ShareTarget::Method::kGet);
1256 }
1257 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,CheckFindsShareTargetChange)1258 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,
1259                        CheckFindsShareTargetChange) {
1260   constexpr char kShareTargetManifestTemplate[] = R"(
1261     {
1262       "name": "Test app name",
1263       "start_url": ".",
1264       "scope": "/",
1265       "display": "minimal-ui",
1266       "share_target": {
1267         "action": "/web_share_target/share.html",
1268         "method": "$1",
1269         "params": {
1270           "url": "link"
1271         }
1272       },
1273       "icons": $2
1274     }
1275   )";
1276   OverrideManifest(kShareTargetManifestTemplate, {"GET", kInstallableIconList});
1277   AppId app_id = InstallWebApp();
1278 
1279   OverrideManifest(kShareTargetManifestTemplate,
1280                    {"POST", kInstallableIconList});
1281   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1282             ManifestUpdateResult::kAppUpdated);
1283   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1284                                       ManifestUpdateResult::kAppUpdated, 1);
1285 
1286   const WebApp* web_app =
1287       GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
1288   EXPECT_TRUE(web_app->share_target().has_value());
1289   EXPECT_EQ(web_app->share_target()->method, apps::ShareTarget::Method::kPost);
1290 }
1291 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,CheckFindsDeletedShareTarget)1292 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest,
1293                        CheckFindsDeletedShareTarget) {
1294   constexpr char kShareTargetManifestTemplate[] = R"(
1295     {
1296       "name": "Test app name",
1297       "start_url": ".",
1298       "scope": "/",
1299       "display": "minimal-ui",
1300       "share_target": {
1301         "action": "/web_share_target/share.html",
1302         "method": "GET",
1303         "params": {
1304           "url": "link"
1305         }
1306       },
1307       "icons": $1
1308     }
1309   )";
1310 
1311   constexpr char kManifestTemplate[] = R"(
1312     {
1313       "name": "Test app name",
1314       "start_url": ".",
1315       "scope": "/",
1316       "display": "minimal-ui",
1317       "icons": $1
1318     }
1319   )";
1320 
1321   OverrideManifest(kShareTargetManifestTemplate, {kInstallableIconList});
1322   AppId app_id = InstallWebApp();
1323 
1324   OverrideManifest(kManifestTemplate, {kInstallableIconList});
1325   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1326             ManifestUpdateResult::kAppUpdated);
1327   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1328                                       ManifestUpdateResult::kAppUpdated, 1);
1329 
1330   const WebApp* web_app =
1331       GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
1332   EXPECT_FALSE(web_app->share_target().has_value());
1333 }
1334 
1335 class ManifestUpdateManagerBrowserTestWithShortcutsMenu
1336     : public ManifestUpdateManagerBrowserTest {
1337  public:
ManifestUpdateManagerBrowserTestWithShortcutsMenu()1338   ManifestUpdateManagerBrowserTestWithShortcutsMenu() {
1339     scoped_feature_list_.InitAndEnableFeature(
1340         features::kDesktopPWAsAppIconShortcutsMenu);
1341   }
1342 
1343  private:
1344   base::test::ScopedFeatureList scoped_feature_list_;
1345 };
1346 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,CheckFindsShortcutsMenuUpdated)1347 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
1348                        CheckFindsShortcutsMenuUpdated) {
1349   constexpr char kManifestTemplate[] = R"(
1350     {
1351       "name": "Test app name",
1352       "start_url": ".",
1353       "scope": "/",
1354       "display": "standalone",
1355       "icons": $1,
1356       "shortcuts": $2
1357     }
1358   )";
1359   OverrideManifest(kManifestTemplate, {kInstallableIconList, kShortcutsItem});
1360   AppId app_id = InstallWebApp();
1361 
1362   OverrideManifest(kManifestTemplate, {kInstallableIconList, kShortcutsItems});
1363   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1364             ManifestUpdateResult::kAppUpdated);
1365   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1366                                       ManifestUpdateResult::kAppUpdated, 1);
1367   EXPECT_EQ(
1368       GetProvider().registrar().GetAppShortcutsMenuItemInfos(app_id).size(),
1369       2u);
1370 }
1371 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,CheckFindsItemNameUpdated)1372 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
1373                        CheckFindsItemNameUpdated) {
1374   constexpr char kManifestTemplate[] = R"(
1375     {
1376       "name": "Test app name",
1377       "start_url": ".",
1378       "scope": "/",
1379       "display": "standalone",
1380       "icons": $1,
1381       "shortcuts": [
1382         {
1383           "name": "$2",
1384           "short_name": "HM",
1385           "description": "Go home",
1386           "url": ".",
1387           "icons": [
1388             {
1389               "src": "/banners/image-512px.png",
1390               "sizes": "512x512",
1391               "type": "image/png"
1392             }
1393           ]
1394         }
1395       ]
1396     }
1397   )";
1398   OverrideManifest(kManifestTemplate, {kInstallableIconList, "Home"});
1399   AppId app_id = InstallWebApp();
1400 
1401   OverrideManifest(kManifestTemplate,
1402                    {kInstallableIconList, kAnotherShortcutsItemName});
1403   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1404             ManifestUpdateResult::kAppUpdated);
1405   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1406                                       ManifestUpdateResult::kAppUpdated, 1);
1407   EXPECT_EQ(
1408       GetProvider().registrar().GetAppShortcutsMenuItemInfos(app_id)[0].name,
1409       base::UTF8ToUTF16(kAnotherShortcutsItemName));
1410 }
1411 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,CheckIgnoresShortNameAndDescriptionChange)1412 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
1413                        CheckIgnoresShortNameAndDescriptionChange) {
1414   constexpr char kManifestTemplate[] = R"(
1415     {
1416       "name": "Test app name",
1417       "start_url": ".",
1418       "scope": "/",
1419       "display": "standalone",
1420       "icons": $1,
1421       "shortcuts": [
1422         {
1423           "name": "Home",
1424           "short_name": "$2",
1425           "description": "$3",
1426           "url": ".",
1427           "icons": [
1428             {
1429               "src": "/banners/image-512px.png",
1430               "sizes": "512x512",
1431               "type": "image/png"
1432             }
1433           ]
1434         }
1435       ]
1436     }
1437   )";
1438   OverrideManifest(kManifestTemplate, {kInstallableIconList, "HM", "Go home"});
1439   AppId app_id = InstallWebApp();
1440 
1441   OverrideManifest(kManifestTemplate,
1442                    {kInstallableIconList, kAnotherShortcutsItemShortName,
1443                     kAnotherShortcutsItemDescription});
1444   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1445             ManifestUpdateResult::kAppUpToDate);
1446   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1447                                       ManifestUpdateResult::kAppUpToDate, 1);
1448 }
1449 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,CheckFindsItemUrlUpdated)1450 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
1451                        CheckFindsItemUrlUpdated) {
1452   constexpr char kManifestTemplate[] = R"(
1453     {
1454       "name": "Test app name",
1455       "start_url": ".",
1456       "scope": "/",
1457       "display": "standalone",
1458       "icons": $1,
1459       "shortcuts": [
1460         {
1461           "name": "Home",
1462           "short_name": "HM",
1463           "description": "Go home",
1464           "url": "$2",
1465           "icons": [
1466             {
1467               "src": "/banners/image-512px.png",
1468               "sizes": "512x512",
1469               "type": "image/png"
1470             }
1471           ]
1472         }
1473       ]
1474     }
1475   )";
1476   OverrideManifest(kManifestTemplate, {kInstallableIconList, "/"});
1477   AppId app_id = InstallWebApp();
1478 
1479   OverrideManifest(kManifestTemplate,
1480                    {kInstallableIconList, kAnotherShortcutsItemUrl});
1481   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1482             ManifestUpdateResult::kAppUpdated);
1483   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1484                                       ManifestUpdateResult::kAppUpdated, 1);
1485   EXPECT_EQ(
1486       GetProvider().registrar().GetAppShortcutsMenuItemInfos(app_id)[0].url,
1487       http_server_.GetURL(kAnotherShortcutsItemUrl));
1488 }
1489 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,CheckFindsShortcutIconContentChange)1490 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
1491                        CheckFindsShortcutIconContentChange) {
1492   constexpr char kManifest[] = R"(
1493     {
1494       "name": "Test app name",
1495       "start_url": ".",
1496       "scope": "/",
1497       "display": "standalone",
1498       "icons": $1,
1499       "shortcuts": [
1500         {
1501           "name": "Home",
1502           "short_name": "HM",
1503           "description": "Go home",
1504           "url": "/",
1505           "icons": [
1506             {
1507               "src": "/web_apps/basic-192.png?ignore",
1508               "sizes": "192x192",
1509               "type": "image/png"
1510             }
1511           ]
1512         }
1513       ]
1514     }
1515   )";
1516   OverrideManifest(kManifest, {kInstallableIconList});
1517   AppId app_id = InstallWebApp();
1518 
1519   // Replace the contents of basic-192.png with blue-192.png without changing
1520   // the URL.
1521   content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting(
1522       [this](content::URLLoaderInterceptor::RequestParams* params)
1523           -> bool /*intercepted*/ {
1524         if (params->url_request.url ==
1525             http_server_.GetURL("/web_apps/basic-192.png?ignore")) {
1526           content::URLLoaderInterceptor::WriteResponse(
1527               "chrome/test/data/web_apps/blue-192.png", params->client.get());
1528           return true;
1529         }
1530         return false;
1531       }));
1532 
1533   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1534             ManifestUpdateResult::kAppUpdated);
1535   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1536                                       ManifestUpdateResult::kAppUpdated, 1);
1537 
1538   // Check that the installed icon is now blue.
1539   base::RunLoop run_loop;
1540   GetProvider().icon_manager().ReadAllShortcutsMenuIcons(
1541       app_id,
1542       base::BindLambdaForTesting(
1543           [&run_loop](ShortcutsMenuIconsBitmaps shortcuts_menu_icons_bitmaps) {
1544             run_loop.Quit();
1545             EXPECT_EQ(shortcuts_menu_icons_bitmaps[0].at(192).getColor(0, 0),
1546                       SK_ColorBLUE);
1547           }));
1548   run_loop.Run();
1549 }
1550 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,CheckFindsShortcutIconSrcUpdated)1551 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
1552                        CheckFindsShortcutIconSrcUpdated) {
1553   constexpr char kManifestTemplate[] = R"(
1554     {
1555       "name": "Test app name",
1556       "start_url": ".",
1557       "scope": "/",
1558       "display": "standalone",
1559       "icons": $1,
1560       "shortcuts": [
1561         {
1562           "name": "Home",
1563           "short_name": "HM",
1564           "description": "Go home",
1565           "url": ".",
1566           "icons": [
1567             {
1568               "src": "$2",
1569               "sizes": "512x512",
1570               "type": "image/png"
1571             }
1572           ]
1573         }
1574       ]
1575     }
1576   )";
1577   OverrideManifest(kManifestTemplate,
1578                    {kInstallableIconList, "/banners/image-512px.png"});
1579   AppId app_id = InstallWebApp();
1580 
1581   OverrideManifest(kManifestTemplate, {kInstallableIconList, kAnotherIconSrc});
1582   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1583             ManifestUpdateResult::kAppUpdated);
1584   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1585                                       ManifestUpdateResult::kAppUpdated, 1);
1586   EXPECT_EQ(GetProvider()
1587                 .registrar()
1588                 .GetAppShortcutsMenuItemInfos(app_id)[0]
1589                 .shortcut_icon_infos[0]
1590                 .url,
1591             http_server_.GetURL(kAnotherIconSrc));
1592 }
1593 
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,CheckFindsShortcutIconSizesUpdated)1594 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
1595                        CheckFindsShortcutIconSizesUpdated) {
1596   constexpr char kManifestTemplate[] = R"(
1597     {
1598       "name": "Test app name",
1599       "start_url": ".",
1600       "scope": "/",
1601       "display": "standalone",
1602       "icons": $1,
1603       "shortcuts": [
1604         {
1605           "name": "Home",
1606           "short_name": "HM",
1607           "description": "Go home",
1608           "url": ".",
1609           "icons": [
1610             {
1611               "src": "/banners/image-512px.png",
1612               "sizes": "$2",
1613               "type": "image/png"
1614             }
1615           ]
1616         }
1617       ]
1618     }
1619   )";
1620   OverrideManifest(kManifestTemplate, {kInstallableIconList, "512x512"});
1621   AppId app_id = InstallWebApp();
1622 
1623   OverrideManifest(kManifestTemplate,
1624                    {kInstallableIconList,
1625                     gfx::Size(kAnotherIconSize, kAnotherIconSize).ToString()});
1626   EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
1627             ManifestUpdateResult::kAppUpdated);
1628   histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
1629                                       ManifestUpdateResult::kAppUpdated, 1);
1630   EXPECT_EQ(GetProvider()
1631                 .registrar()
1632                 .GetAppShortcutsMenuItemInfos(app_id)[0]
1633                 .shortcut_icon_infos[0]
1634                 .square_size_px,
1635             kAnotherIconSize);
1636 }
1637 
1638 }  // namespace web_app
1639