1 // Copyright 2016 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/installable/installable_manager.h"
6
7 #include <tuple>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/optional.h"
12 #include "base/run_loop.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/test/bind.h"
16 #include "base/test/metrics/histogram_tester.h"
17 #include "base/test/scoped_feature_list.h"
18 #include "base/threading/sequenced_task_runner_handle.h"
19 #include "chrome/browser/banners/app_banner_manager_desktop.h"
20 #include "chrome/browser/installable/installable_logging.h"
21 #include "chrome/browser/installable/installable_manager.h"
22 #include "chrome/browser/installable/installable_metrics.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/tabs/tab_strip_model.h"
26 #include "chrome/browser/web_applications/test/service_worker_registration_waiter.h"
27 #include "chrome/test/base/in_process_browser_test.h"
28 #include "chrome/test/base/ui_test_utils.h"
29 #include "content/public/common/content_features.h"
30 #include "content/public/test/browser_test.h"
31 #include "content/public/test/browser_test_utils.h"
32 #include "net/test/embedded_test_server/embedded_test_server.h"
33 #include "third_party/blink/public/common/features.h"
34
35 namespace {
36
37 const char kInsecureOrigin[] = "http://www.google.com";
38 const char kOtherInsecureOrigin[] = "http://maps.google.com";
39 const char kUnsafeSecureOriginFlag[] =
40 "unsafely-treat-insecure-origin-as-secure";
41
GetManifestParams()42 InstallableParams GetManifestParams() {
43 InstallableParams params;
44 params.check_eligibility = true;
45 params.wait_for_worker = true;
46 return params;
47 }
48
GetWebAppParams()49 InstallableParams GetWebAppParams() {
50 InstallableParams params = GetManifestParams();
51 params.valid_manifest = true;
52 params.has_worker = true;
53 params.valid_primary_icon = true;
54 params.wait_for_worker = true;
55 return params;
56 }
57
GetPrimaryIconParams()58 InstallableParams GetPrimaryIconParams() {
59 InstallableParams params = GetManifestParams();
60 params.valid_primary_icon = true;
61 params.wait_for_worker = true;
62 return params;
63 }
64
GetPrimaryIconAndSplashIconParams()65 InstallableParams GetPrimaryIconAndSplashIconParams() {
66 InstallableParams params = GetManifestParams();
67 params.valid_primary_icon = true;
68 params.valid_splash_icon = true;
69 params.wait_for_worker = true;
70 return params;
71 }
72
GetPrimaryIconPreferMaskableParams()73 InstallableParams GetPrimaryIconPreferMaskableParams() {
74 InstallableParams params = GetManifestParams();
75 params.valid_primary_icon = true;
76 params.prefer_maskable_icon = true;
77 params.wait_for_worker = true;
78 return params;
79 }
80
GetPrimaryIconPreferMaskableAndSplashIconParams()81 InstallableParams GetPrimaryIconPreferMaskableAndSplashIconParams() {
82 InstallableParams params = GetManifestParams();
83 params.valid_primary_icon = true;
84 params.prefer_maskable_icon = true;
85 params.valid_splash_icon = true;
86 params.wait_for_worker = true;
87 return params;
88 }
89
90 } // anonymous namespace
91
92 // Used only for testing pages with no service workers. This class will dispatch
93 // a RunLoop::QuitClosure when it begins waiting for a service worker to be
94 // registered.
95 class LazyWorkerInstallableManager : public InstallableManager {
96 public:
LazyWorkerInstallableManager(content::WebContents * web_contents,base::OnceClosure quit_closure)97 LazyWorkerInstallableManager(content::WebContents* web_contents,
98 base::OnceClosure quit_closure)
99 : InstallableManager(web_contents),
100 quit_closure_(std::move(quit_closure)) {}
101 ~LazyWorkerInstallableManager() override = default;
102
103 protected:
OnWaitingForServiceWorker()104 void OnWaitingForServiceWorker() override { std::move(quit_closure_).Run(); }
105
106 private:
107 base::OnceClosure quit_closure_;
108 };
109
110 // Used only for testing pages where the manifest URL is changed. This class
111 // will dispatch a RunLoop::QuitClosure when internal state is reset.
112 class ResetDataInstallableManager : public InstallableManager {
113 public:
ResetDataInstallableManager(content::WebContents * web_contents)114 explicit ResetDataInstallableManager(content::WebContents* web_contents)
115 : InstallableManager(web_contents) {}
~ResetDataInstallableManager()116 ~ResetDataInstallableManager() override {}
117
SetQuitClosure(base::RepeatingClosure quit_closure)118 void SetQuitClosure(base::RepeatingClosure quit_closure) {
119 quit_closure_ = quit_closure;
120 }
121
122 protected:
OnResetData()123 void OnResetData() override {
124 if (quit_closure_)
125 quit_closure_.Run();
126 }
127
128 private:
129 base::RepeatingClosure quit_closure_;
130 };
131
132 class CallbackTester {
133 public:
CallbackTester(base::RepeatingClosure quit_closure)134 explicit CallbackTester(base::RepeatingClosure quit_closure)
135 : quit_closure_(quit_closure) {}
136
OnDidFinishInstallableCheck(const InstallableData & data)137 void OnDidFinishInstallableCheck(const InstallableData& data) {
138 errors_ = data.errors;
139 manifest_url_ = data.manifest_url;
140 if (data.manifest)
141 manifest_ = *data.manifest;
142 primary_icon_url_ = data.primary_icon_url;
143 if (data.primary_icon)
144 primary_icon_.reset(new SkBitmap(*data.primary_icon));
145 has_maskable_primary_icon_ = data.has_maskable_primary_icon;
146 splash_icon_url_ = data.splash_icon_url;
147 if (data.splash_icon)
148 splash_icon_.reset(new SkBitmap(*data.splash_icon));
149 valid_manifest_ = data.valid_manifest;
150 has_worker_ = data.has_worker;
151 base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, quit_closure_);
152 }
153
errors() const154 const std::vector<InstallableStatusCode>& errors() const { return errors_; }
manifest_url() const155 const GURL& manifest_url() const { return manifest_url_; }
manifest() const156 const blink::Manifest& manifest() const { return manifest_; }
primary_icon_url() const157 const GURL& primary_icon_url() const { return primary_icon_url_; }
primary_icon() const158 const SkBitmap* primary_icon() const { return primary_icon_.get(); }
has_maskable_primary_icon() const159 bool has_maskable_primary_icon() const { return has_maskable_primary_icon_; }
splash_icon_url() const160 const GURL& splash_icon_url() const { return splash_icon_url_; }
splash_icon() const161 const SkBitmap* splash_icon() const { return splash_icon_.get(); }
valid_manifest() const162 bool valid_manifest() const { return valid_manifest_; }
has_worker() const163 bool has_worker() const { return has_worker_; }
164
165 private:
166 base::RepeatingClosure quit_closure_;
167 std::vector<InstallableStatusCode> errors_;
168 GURL manifest_url_;
169 blink::Manifest manifest_;
170 GURL primary_icon_url_;
171 std::unique_ptr<SkBitmap> primary_icon_;
172 bool has_maskable_primary_icon_;
173 GURL splash_icon_url_;
174 std::unique_ptr<SkBitmap> splash_icon_;
175 bool valid_manifest_;
176 bool has_worker_;
177 };
178
179 class NestedCallbackTester {
180 public:
NestedCallbackTester(InstallableManager * manager,const InstallableParams & params,base::OnceClosure quit_closure)181 NestedCallbackTester(InstallableManager* manager,
182 const InstallableParams& params,
183 base::OnceClosure quit_closure)
184 : manager_(manager),
185 params_(params),
186 quit_closure_(std::move(quit_closure)) {}
187
Run()188 void Run() {
189 manager_->GetData(
190 params_, base::BindOnce(&NestedCallbackTester::OnDidFinishFirstCheck,
191 base::Unretained(this)));
192 }
193
OnDidFinishFirstCheck(const InstallableData & data)194 void OnDidFinishFirstCheck(const InstallableData& data) {
195 errors_ = data.errors;
196 manifest_url_ = data.manifest_url;
197 manifest_ = *data.manifest;
198 primary_icon_url_ = data.primary_icon_url;
199 if (data.primary_icon)
200 primary_icon_.reset(new SkBitmap(*data.primary_icon));
201 valid_manifest_ = data.valid_manifest;
202 has_worker_ = data.has_worker;
203
204 manager_->GetData(
205 params_, base::BindOnce(&NestedCallbackTester::OnDidFinishSecondCheck,
206 base::Unretained(this)));
207 }
208
OnDidFinishSecondCheck(const InstallableData & data)209 void OnDidFinishSecondCheck(const InstallableData& data) {
210 EXPECT_EQ(errors_, data.errors);
211 EXPECT_EQ(manifest_url_, data.manifest_url);
212 EXPECT_EQ(primary_icon_url_, data.primary_icon_url);
213 EXPECT_EQ(primary_icon_.get(), data.primary_icon);
214 EXPECT_EQ(valid_manifest_, data.valid_manifest);
215 EXPECT_EQ(has_worker_, data.has_worker);
216 EXPECT_EQ(manifest_.IsEmpty(), data.manifest->IsEmpty());
217 EXPECT_EQ(manifest_.start_url, data.manifest->start_url);
218 EXPECT_EQ(manifest_.display, data.manifest->display);
219 EXPECT_EQ(manifest_.name, data.manifest->name);
220 EXPECT_EQ(manifest_.short_name, data.manifest->short_name);
221 EXPECT_EQ(manifest_.display_override, data.manifest->display_override);
222
223 std::move(quit_closure_).Run();
224 }
225
226 private:
227 InstallableManager* manager_;
228 InstallableParams params_;
229 base::OnceClosure quit_closure_;
230 std::vector<InstallableStatusCode> errors_;
231 GURL manifest_url_;
232 blink::Manifest manifest_;
233 GURL primary_icon_url_;
234 std::unique_ptr<SkBitmap> primary_icon_;
235 bool valid_manifest_;
236 bool has_worker_;
237 };
238
239 class InstallableManagerBrowserTest : public InProcessBrowserTest {
240 public:
SetUpOnMainThread()241 void SetUpOnMainThread() override {
242 ASSERT_TRUE(embedded_test_server()->Start());
243
244 // Make sure app banners are disabled in the browser so they do not
245 // interfere with the test.
246 banners::AppBannerManagerDesktop::DisableTriggeringForTesting();
247 }
248
249 // Returns a test server URL to a page controlled by a service worker with
250 // |manifest_url| injected as the manifest tag.
GetURLOfPageWithServiceWorkerAndManifest(const std::string & manifest_url)251 std::string GetURLOfPageWithServiceWorkerAndManifest(
252 const std::string& manifest_url) {
253 return "/banners/manifest_test_page.html?manifest=" +
254 embedded_test_server()->GetURL(manifest_url).spec();
255 }
256
NavigateAndRunInstallableManager(Browser * browser,CallbackTester * tester,const InstallableParams & params,const std::string & url)257 void NavigateAndRunInstallableManager(Browser* browser,
258 CallbackTester* tester,
259 const InstallableParams& params,
260 const std::string& url) {
261 GURL test_url = embedded_test_server()->GetURL(url);
262 ui_test_utils::NavigateToURL(browser, test_url);
263 RunInstallableManager(browser, tester, params);
264 }
265
266 std::vector<content::InstallabilityError>
NavigateAndGetAllInstallabilityErrors(Browser * browser,const std::string & url)267 NavigateAndGetAllInstallabilityErrors(Browser* browser,
268 const std::string& url) {
269 GURL test_url = embedded_test_server()->GetURL(url);
270 ui_test_utils::NavigateToURL(browser, test_url);
271 InstallableManager* manager = GetManager(browser);
272
273 base::RunLoop run_loop;
274 std::vector<content::InstallabilityError> result;
275
276 manager->GetAllErrors(base::BindLambdaForTesting(
277 [&](std::vector<content::InstallabilityError> installability_errors) {
278 result = std::move(installability_errors);
279 run_loop.Quit();
280 }));
281 run_loop.Run();
282 return result;
283 }
284
RunInstallableManager(Browser * browser,CallbackTester * tester,const InstallableParams & params)285 void RunInstallableManager(Browser* browser,
286 CallbackTester* tester,
287 const InstallableParams& params) {
288 InstallableManager* manager = GetManager(browser);
289 manager->GetData(
290 params, base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
291 base::Unretained(tester)));
292 }
293
GetManager(Browser * browser)294 InstallableManager* GetManager(Browser* browser) {
295 content::WebContents* web_contents =
296 browser->tab_strip_model()->GetActiveWebContents();
297 InstallableManager::CreateForWebContents(web_contents);
298 InstallableManager* manager =
299 InstallableManager::FromWebContents(web_contents);
300 CHECK(manager);
301
302 return manager;
303 }
304 };
305
306 class InstallableManagerAllowlistOriginBrowserTest
307 : public InstallableManagerBrowserTest {
SetUpCommandLine(base::CommandLine * command_line)308 void SetUpCommandLine(base::CommandLine* command_line) override {
309 command_line->AppendSwitchASCII(kUnsafeSecureOriginFlag, kInsecureOrigin);
310 }
311 };
312
313 class InstallableManagerOfflineCapabilityBrowserTest
314 : public InstallableManagerBrowserTest,
315 public testing::WithParamInterface<std::tuple<bool, bool>> {
316 public:
InstallableManagerOfflineCapabilityBrowserTest()317 InstallableManagerOfflineCapabilityBrowserTest()
318 : is_offline_check_feature_enabled_(std::get<0>(GetParam())),
319 is_service_worker_offline_supported_(std::get<1>(GetParam())) {
320 if (is_offline_check_feature_enabled_) {
321 scoped_feature_list_.InitAndEnableFeatureWithParameters(
322 blink::features::kCheckOfflineCapability,
323 {{"check_mode", "enforce"}});
324 } else {
325 scoped_feature_list_.InitAndDisableFeature(
326 blink::features::kCheckOfflineCapability);
327 }
328 }
329 ~InstallableManagerOfflineCapabilityBrowserTest() override = default;
330
IsServiceWorkerOfflineSupported()331 bool IsServiceWorkerOfflineSupported() {
332 return is_service_worker_offline_supported_;
333 }
334
335 // The page supports the offline environment if a service worker has a fetch
336 // event handler that returns a response or the feature flag
337 // |kCheckOfflineCapability| is disabled.
OfflineSupported()338 bool OfflineSupported() {
339 return (!is_offline_check_feature_enabled_)
340 || is_service_worker_offline_supported_;
341 }
342
343 // Assume that the name of HTML files using a service worker with an empty
344 // fetch event handler includes "_empty_fetch_handler" suffix.
GetPath(std::string base)345 const std::string GetPath(std::string base) {
346 if (is_service_worker_offline_supported_)
347 return base + ".html";
348 return base + "_empty_fetch_handler.html";
349 }
350
351 private:
352 base::test::ScopedFeatureList scoped_feature_list_;
353
354 const bool is_offline_check_feature_enabled_;
355 const bool is_service_worker_offline_supported_;
356 };
357
358 INSTANTIATE_TEST_SUITE_P(All, InstallableManagerOfflineCapabilityBrowserTest,
359 testing::Combine(testing::Bool(), testing::Bool()));
360
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,ManagerBeginsInEmptyState)361 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
362 ManagerBeginsInEmptyState) {
363 // Ensure that the InstallableManager starts off with everything null.
364 InstallableManager* manager = GetManager(browser());
365
366 EXPECT_TRUE(manager->manifest().IsEmpty());
367 EXPECT_TRUE(manager->manifest_url().is_empty());
368 EXPECT_TRUE(manager->icons_.empty());
369 EXPECT_FALSE(manager->valid_manifest());
370 EXPECT_FALSE(manager->has_worker());
371
372 EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error());
373 EXPECT_EQ(NO_ERROR_DETECTED, manager->valid_manifest_error());
374 EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error());
375 EXPECT_TRUE(!manager->task_queue_.HasCurrent());
376 }
377
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,ManagerInIncognito)378 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, ManagerInIncognito) {
379 // Ensure that the InstallableManager returns an error if called in an
380 // incognito profile.
381 Browser* incognito_browser =
382 OpenURLOffTheRecord(browser()->profile(), GURL("about:blank"));
383 InstallableManager* manager = GetManager(incognito_browser);
384
385 base::RunLoop run_loop;
386 std::unique_ptr<CallbackTester> tester(
387 new CallbackTester(run_loop.QuitClosure()));
388
389 ui_test_utils::NavigateToURL(
390 incognito_browser,
391 embedded_test_server()->GetURL("/banners/manifest_test_page.html"));
392
393 RunInstallableManager(incognito_browser, tester.get(), GetManifestParams());
394 run_loop.Run();
395
396 EXPECT_TRUE(manager->manifest().IsEmpty());
397 EXPECT_TRUE(manager->manifest_url().is_empty());
398 EXPECT_TRUE(manager->icons_.empty());
399 EXPECT_FALSE(manager->valid_manifest());
400 EXPECT_FALSE(manager->has_worker());
401
402 EXPECT_EQ(IN_INCOGNITO, manager->eligibility_error());
403 EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error());
404 EXPECT_EQ(NO_ERROR_DETECTED, manager->valid_manifest_error());
405 EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error());
406 EXPECT_TRUE(!manager->task_queue_.HasCurrent());
407 }
408
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckNoManifest)409 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckNoManifest) {
410 // Ensure that a page with no manifest returns the appropriate error and with
411 // null fields for everything.
412 base::HistogramTester histograms;
413 base::RunLoop run_loop;
414 std::unique_ptr<CallbackTester> tester(
415 new CallbackTester(run_loop.QuitClosure()));
416
417 // Navigating resets histogram state, so do it before recording a histogram.
418 ui_test_utils::NavigateToURL(
419 browser(),
420 embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"));
421 RunInstallableManager(browser(), tester.get(), GetManifestParams());
422 run_loop.Run();
423
424 // If there is no manifest, everything should be empty.
425 EXPECT_TRUE(tester->manifest().IsEmpty());
426 EXPECT_TRUE(tester->manifest_url().is_empty());
427 EXPECT_TRUE(tester->primary_icon_url().is_empty());
428 EXPECT_EQ(nullptr, tester->primary_icon());
429 EXPECT_FALSE(tester->has_maskable_primary_icon());
430 EXPECT_FALSE(tester->valid_manifest());
431 EXPECT_FALSE(tester->has_worker());
432 EXPECT_TRUE(tester->splash_icon_url().is_empty());
433 EXPECT_EQ(nullptr, tester->splash_icon());
434 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MANIFEST}, tester->errors());
435 }
436
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckManifest404)437 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckManifest404) {
438 base::RunLoop run_loop;
439 std::unique_ptr<CallbackTester> tester(
440 new CallbackTester(run_loop.QuitClosure()));
441
442 NavigateAndRunInstallableManager(browser(), tester.get(), GetManifestParams(),
443 GetURLOfPageWithServiceWorkerAndManifest(
444 "/banners/manifest_missing.json"));
445 run_loop.Run();
446
447 // The installable manager should return a manifest URL even if it 404s.
448 // However, the check should fail with a ManifestEmpty error.
449 EXPECT_TRUE(tester->manifest().IsEmpty());
450
451 EXPECT_FALSE(tester->manifest_url().is_empty());
452 EXPECT_TRUE(tester->primary_icon_url().is_empty());
453 EXPECT_EQ(nullptr, tester->primary_icon());
454 EXPECT_FALSE(tester->has_maskable_primary_icon());
455 EXPECT_FALSE(tester->valid_manifest());
456 EXPECT_FALSE(tester->has_worker());
457 EXPECT_TRUE(tester->splash_icon_url().is_empty());
458 EXPECT_EQ(nullptr, tester->splash_icon());
459 EXPECT_EQ(std::vector<InstallableStatusCode>{MANIFEST_EMPTY},
460 tester->errors());
461 }
462
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckManifestOnly)463 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckManifestOnly) {
464 // Verify that asking for just the manifest works as expected.
465 base::RunLoop run_loop;
466 std::unique_ptr<CallbackTester> tester(
467 new CallbackTester(run_loop.QuitClosure()));
468
469 NavigateAndRunInstallableManager(browser(), tester.get(), GetManifestParams(),
470 "/banners/manifest_test_page.html");
471 run_loop.Run();
472
473 EXPECT_FALSE(tester->manifest().IsEmpty());
474 EXPECT_FALSE(tester->manifest_url().is_empty());
475
476 EXPECT_TRUE(tester->primary_icon_url().is_empty());
477 EXPECT_EQ(nullptr, tester->primary_icon());
478 EXPECT_FALSE(tester->has_maskable_primary_icon());
479 EXPECT_FALSE(tester->valid_manifest());
480 EXPECT_FALSE(tester->has_worker());
481 EXPECT_TRUE(tester->splash_icon_url().is_empty());
482 EXPECT_EQ(nullptr, tester->splash_icon());
483 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
484 }
485
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckInstallableParamsDefaultConstructor)486 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
487 CheckInstallableParamsDefaultConstructor) {
488 // Verify that using InstallableParams' default constructor is equivalent to
489 // just asking for the manifest alone.
490 base::RunLoop run_loop;
491 std::unique_ptr<CallbackTester> tester(
492 new CallbackTester(run_loop.QuitClosure()));
493
494 InstallableParams params;
495 NavigateAndRunInstallableManager(browser(), tester.get(), params,
496 "/banners/manifest_test_page.html");
497 run_loop.Run();
498
499 EXPECT_FALSE(tester->manifest().IsEmpty());
500 EXPECT_FALSE(tester->manifest_url().is_empty());
501
502 EXPECT_TRUE(tester->primary_icon_url().is_empty());
503 EXPECT_EQ(nullptr, tester->primary_icon());
504 EXPECT_FALSE(tester->has_maskable_primary_icon());
505 EXPECT_FALSE(tester->valid_manifest());
506 EXPECT_FALSE(tester->has_worker());
507 EXPECT_TRUE(tester->splash_icon_url().is_empty());
508 EXPECT_EQ(nullptr, tester->splash_icon());
509 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
510 }
511
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckManifestWithIconThatIsTooSmall)512 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
513 CheckManifestWithIconThatIsTooSmall) {
514 // This page has a manifest with only a 48x48 icon which is too small to be
515 // installable. Asking for a primary icon should fail with NO_ACCEPTABLE_ICON.
516 {
517 base::RunLoop run_loop;
518 std::unique_ptr<CallbackTester> tester(
519 new CallbackTester(run_loop.QuitClosure()));
520
521 NavigateAndRunInstallableManager(
522 browser(), tester.get(), GetPrimaryIconParams(),
523 GetURLOfPageWithServiceWorkerAndManifest(
524 "/banners/manifest_too_small_icon.json"));
525 run_loop.Run();
526
527 EXPECT_FALSE(tester->manifest().IsEmpty());
528 EXPECT_FALSE(tester->manifest_url().is_empty());
529
530 EXPECT_TRUE(tester->primary_icon_url().is_empty());
531 EXPECT_EQ(nullptr, tester->primary_icon());
532 EXPECT_FALSE(tester->valid_manifest());
533 EXPECT_FALSE(tester->has_worker());
534 EXPECT_TRUE(tester->splash_icon_url().is_empty());
535 EXPECT_EQ(nullptr, tester->splash_icon());
536 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON},
537 tester->errors());
538 }
539
540 // Ask for everything except splash icon. This should fail with
541 // NO_ACCEPTABLE_ICON - the primary icon fetch has already failed, so that
542 // cached error stops the installable check from being performed.
543 {
544 base::RunLoop run_loop;
545 std::unique_ptr<CallbackTester> tester(
546 new CallbackTester(run_loop.QuitClosure()));
547
548 RunInstallableManager(browser(), tester.get(), GetWebAppParams());
549 run_loop.Run();
550
551 EXPECT_FALSE(tester->manifest().IsEmpty());
552 EXPECT_FALSE(tester->manifest_url().is_empty());
553
554 EXPECT_TRUE(tester->primary_icon_url().is_empty());
555 EXPECT_EQ(nullptr, tester->primary_icon());
556 EXPECT_FALSE(tester->valid_manifest());
557 EXPECT_FALSE(tester->has_worker());
558 EXPECT_TRUE(tester->splash_icon_url().is_empty());
559 EXPECT_EQ(nullptr, tester->splash_icon());
560 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON},
561 tester->errors());
562 }
563
564 // Ask for a splash icon. This should fail to get a splash icon but not record
565 // an error.
566 {
567 base::RunLoop run_loop;
568 std::unique_ptr<CallbackTester> tester(
569 new CallbackTester(run_loop.QuitClosure()));
570
571 InstallableParams params = GetPrimaryIconAndSplashIconParams();
572 params.valid_primary_icon = false;
573 RunInstallableManager(browser(), tester.get(), params);
574 run_loop.Run();
575
576 EXPECT_FALSE(tester->manifest().IsEmpty());
577 EXPECT_FALSE(tester->manifest_url().is_empty());
578
579 EXPECT_TRUE(tester->primary_icon_url().is_empty());
580 EXPECT_EQ(nullptr, tester->primary_icon());
581 EXPECT_TRUE(tester->splash_icon_url().is_empty());
582 EXPECT_EQ(nullptr, tester->splash_icon());
583 EXPECT_FALSE(tester->valid_manifest());
584 EXPECT_FALSE(tester->has_worker());
585 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
586 }
587 }
588
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckManifestWithOnlyRelatedApplications)589 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
590 CheckManifestWithOnlyRelatedApplications) {
591 // This page has a manifest with only related applications specified. Asking
592 // for just the manifest should succeed.
593 {
594 base::RunLoop run_loop;
595 std::unique_ptr<CallbackTester> tester(
596 new CallbackTester(run_loop.QuitClosure()));
597
598 NavigateAndRunInstallableManager(browser(), tester.get(),
599 GetManifestParams(),
600 GetURLOfPageWithServiceWorkerAndManifest(
601 "/banners/play_app_manifest.json"));
602 run_loop.Run();
603
604 EXPECT_FALSE(tester->manifest().IsEmpty());
605 EXPECT_FALSE(tester->manifest_url().is_empty());
606 EXPECT_TRUE(tester->manifest().prefer_related_applications);
607
608 EXPECT_TRUE(tester->primary_icon_url().is_empty());
609 EXPECT_EQ(nullptr, tester->primary_icon());
610 EXPECT_FALSE(tester->valid_manifest());
611 EXPECT_FALSE(tester->has_worker());
612 EXPECT_TRUE(tester->splash_icon_url().is_empty());
613 EXPECT_EQ(nullptr, tester->splash_icon());
614 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
615 }
616
617 // Ask for a primary icon (but don't navigate). This should fail with
618 // NO_ACCEPTABLE_ICON.
619 {
620 base::RunLoop run_loop;
621 std::unique_ptr<CallbackTester> tester(
622 new CallbackTester(run_loop.QuitClosure()));
623
624 RunInstallableManager(browser(), tester.get(), GetPrimaryIconParams());
625 run_loop.Run();
626
627 EXPECT_FALSE(tester->manifest().IsEmpty());
628 EXPECT_FALSE(tester->manifest_url().is_empty());
629 EXPECT_TRUE(tester->manifest().prefer_related_applications);
630
631 EXPECT_TRUE(tester->primary_icon_url().is_empty());
632 EXPECT_EQ(nullptr, tester->primary_icon());
633 EXPECT_FALSE(tester->valid_manifest());
634 EXPECT_FALSE(tester->has_worker());
635 EXPECT_TRUE(tester->splash_icon_url().is_empty());
636 EXPECT_EQ(nullptr, tester->splash_icon());
637 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON},
638 tester->errors());
639 }
640
641 // Ask for everything except splash icon. This should fail with
642 // NO_ACCEPTABLE_ICON - the primary icon fetch has already failed, so that
643 // cached error stops the installable check from being performed.
644 {
645 base::RunLoop run_loop;
646 std::unique_ptr<CallbackTester> tester(
647 new CallbackTester(run_loop.QuitClosure()));
648
649 RunInstallableManager(browser(), tester.get(), GetWebAppParams());
650 run_loop.Run();
651
652 EXPECT_FALSE(tester->manifest().IsEmpty());
653 EXPECT_FALSE(tester->manifest_url().is_empty());
654 EXPECT_TRUE(tester->manifest().prefer_related_applications);
655
656 EXPECT_TRUE(tester->primary_icon_url().is_empty());
657 EXPECT_EQ(nullptr, tester->primary_icon());
658 EXPECT_FALSE(tester->valid_manifest());
659 EXPECT_FALSE(tester->has_worker());
660 EXPECT_TRUE(tester->splash_icon_url().is_empty());
661 EXPECT_EQ(nullptr, tester->splash_icon());
662 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON},
663 tester->errors());
664 }
665
666 // Do not ask for primary icon. This should fail with several validity
667 // errors.
668 {
669 base::RunLoop run_loop;
670 std::unique_ptr<CallbackTester> tester(
671 new CallbackTester(run_loop.QuitClosure()));
672
673 InstallableParams params = GetWebAppParams();
674 params.valid_primary_icon = false;
675 RunInstallableManager(browser(), tester.get(), params);
676 run_loop.Run();
677
678 EXPECT_FALSE(tester->manifest().IsEmpty());
679 EXPECT_FALSE(tester->manifest_url().is_empty());
680 EXPECT_TRUE(tester->manifest().prefer_related_applications);
681
682 EXPECT_TRUE(tester->primary_icon_url().is_empty());
683 EXPECT_EQ(nullptr, tester->primary_icon());
684 EXPECT_TRUE(tester->splash_icon_url().is_empty());
685 EXPECT_EQ(nullptr, tester->splash_icon());
686 EXPECT_FALSE(tester->valid_manifest());
687 EXPECT_FALSE(tester->has_worker());
688 EXPECT_EQ(
689 std::vector<InstallableStatusCode>(
690 {START_URL_NOT_VALID, MANIFEST_MISSING_NAME_OR_SHORT_NAME,
691 MANIFEST_DISPLAY_NOT_SUPPORTED, MANIFEST_MISSING_SUITABLE_ICON}),
692 tester->errors());
693 }
694 }
695
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckManifestAndIcon)696 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckManifestAndIcon) {
697 // Add to homescreen checks for manifest + primary icon.
698 {
699 base::RunLoop run_loop;
700 std::unique_ptr<CallbackTester> tester(
701 new CallbackTester(run_loop.QuitClosure()));
702
703 NavigateAndRunInstallableManager(browser(), tester.get(),
704 GetPrimaryIconParams(),
705 "/banners/manifest_test_page.html");
706 run_loop.Run();
707
708 EXPECT_FALSE(tester->manifest().IsEmpty());
709 EXPECT_FALSE(tester->manifest_url().is_empty());
710
711 EXPECT_FALSE(tester->primary_icon_url().is_empty());
712 EXPECT_NE(nullptr, tester->primary_icon());
713 EXPECT_FALSE(tester->valid_manifest());
714 EXPECT_FALSE(tester->has_worker());
715 EXPECT_TRUE(tester->splash_icon_url().is_empty());
716 EXPECT_EQ(nullptr, tester->splash_icon());
717 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
718 }
719
720 // Add to homescreen checks for manifest + primary icon + splash icon.
721 {
722 base::RunLoop run_loop;
723 std::unique_ptr<CallbackTester> tester(
724 new CallbackTester(run_loop.QuitClosure()));
725
726 RunInstallableManager(browser(), tester.get(),
727 GetPrimaryIconAndSplashIconParams());
728 run_loop.Run();
729
730 EXPECT_FALSE(tester->manifest().IsEmpty());
731 EXPECT_FALSE(tester->manifest_url().is_empty());
732
733 EXPECT_FALSE(tester->primary_icon_url().is_empty());
734 EXPECT_NE(nullptr, tester->primary_icon());
735 EXPECT_FALSE(tester->valid_manifest());
736 EXPECT_FALSE(tester->has_worker());
737 EXPECT_FALSE(tester->splash_icon_url().is_empty());
738 EXPECT_NE(nullptr, tester->splash_icon());
739 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
740 }
741
742 // Navigate to a page with a good maskable icon for primary icon and a bad any
743 // icon for splash icon. This should fail with NO_ICON_AVAILABLE, but still
744 // have the manifest and primary icon.
745 {
746 base::RunLoop run_loop;
747 std::unique_ptr<CallbackTester> tester(
748 new CallbackTester(run_loop.QuitClosure()));
749 NavigateAndRunInstallableManager(
750 browser(), tester.get(),
751 GetPrimaryIconPreferMaskableAndSplashIconParams(),
752 GetURLOfPageWithServiceWorkerAndManifest(
753 "/banners/manifest_bad_non_maskable_icon.json"));
754 run_loop.Run();
755 EXPECT_FALSE(tester->manifest().IsEmpty());
756 EXPECT_FALSE(tester->manifest_url().is_empty());
757 EXPECT_FALSE(tester->primary_icon_url().is_empty());
758 EXPECT_NE(nullptr, tester->primary_icon());
759 EXPECT_TRUE(tester->splash_icon_url().is_empty());
760 EXPECT_EQ(nullptr, tester->splash_icon());
761 EXPECT_FALSE(tester->valid_manifest());
762 EXPECT_FALSE(tester->has_worker());
763 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ICON_AVAILABLE},
764 tester->errors());
765 }
766 }
767
IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,CheckWebapp)768 IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,
769 CheckWebapp) {
770 // Request everything except splash icon.
771 {
772 base::HistogramTester histograms;
773 base::RunLoop run_loop;
774 std::unique_ptr<CallbackTester> tester(
775 new CallbackTester(run_loop.QuitClosure()));
776
777 // Navigating resets histogram state, so do it before recording a histogram.
778 ui_test_utils::NavigateToURL(
779 browser(),
780 embedded_test_server()->GetURL(
781 GetPath("/banners/manifest_test_page")));
782 RunInstallableManager(browser(), tester.get(), GetWebAppParams());
783 run_loop.Run();
784
785 EXPECT_FALSE(tester->manifest().IsEmpty());
786 EXPECT_FALSE(tester->manifest_url().is_empty());
787 EXPECT_FALSE(tester->primary_icon_url().is_empty());
788 EXPECT_NE(nullptr, tester->primary_icon());
789 EXPECT_TRUE(tester->valid_manifest());
790 if (OfflineSupported()) {
791 EXPECT_TRUE(tester->has_worker());
792 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
793 } else {
794 EXPECT_FALSE(tester->has_worker());
795 EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE},
796 tester->errors());
797 }
798 EXPECT_TRUE(tester->splash_icon_url().is_empty());
799 EXPECT_EQ(nullptr, tester->splash_icon());
800
801 // Verify that the returned state matches manager internal state.
802 InstallableManager* manager = GetManager(browser());
803
804 EXPECT_FALSE(manager->manifest().IsEmpty());
805 EXPECT_FALSE(manager->manifest_url().is_empty());
806 EXPECT_TRUE(manager->valid_manifest());
807 if (OfflineSupported()) {
808 EXPECT_TRUE(manager->has_worker());
809 EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error());
810 } else {
811 EXPECT_FALSE(manager->has_worker());
812 EXPECT_EQ(NOT_OFFLINE_CAPABLE, manager->worker_error());
813 }
814 EXPECT_EQ(1u, manager->icons_.size());
815 EXPECT_FALSE((
816 manager->icon_url(InstallableManager::IconUsage::kPrimary).is_empty()));
817 EXPECT_NE(nullptr,
818 (manager->icon(InstallableManager::IconUsage::kPrimary)));
819 EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error());
820 EXPECT_EQ(NO_ERROR_DETECTED, manager->valid_manifest_error());
821 EXPECT_EQ(NO_ERROR_DETECTED,
822 (manager->icon_error(InstallableManager::IconUsage::kPrimary)));
823 EXPECT_TRUE(!manager->task_queue_.HasCurrent());
824 }
825
826 // Request everything except splash icon again without navigating away. This
827 // should work fine.
828 {
829 base::RunLoop run_loop;
830 std::unique_ptr<CallbackTester> tester(
831 new CallbackTester(run_loop.QuitClosure()));
832
833 RunInstallableManager(browser(), tester.get(), GetWebAppParams());
834 run_loop.Run();
835
836 EXPECT_FALSE(tester->manifest().IsEmpty());
837 EXPECT_FALSE(tester->manifest_url().is_empty());
838 EXPECT_FALSE(tester->primary_icon_url().is_empty());
839 EXPECT_NE(nullptr, tester->primary_icon());
840 EXPECT_TRUE(tester->valid_manifest());
841 if (OfflineSupported()) {
842 EXPECT_TRUE(tester->has_worker());
843 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
844 } else {
845 EXPECT_FALSE(tester->has_worker());
846 EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE},
847 tester->errors());
848 }
849 EXPECT_TRUE(tester->splash_icon_url().is_empty());
850 EXPECT_EQ(nullptr, tester->splash_icon());
851
852 // Verify that the returned state matches manager internal state.
853 InstallableManager* manager = GetManager(browser());
854
855 EXPECT_FALSE(manager->manifest().IsEmpty());
856 EXPECT_FALSE(manager->manifest_url().is_empty());
857 EXPECT_TRUE(manager->valid_manifest());
858 if (OfflineSupported()) {
859 EXPECT_TRUE(manager->has_worker());
860 EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error());
861 } else {
862 EXPECT_FALSE(manager->has_worker());
863 EXPECT_EQ(NOT_OFFLINE_CAPABLE, manager->worker_error());
864 }
865 EXPECT_EQ(1u, manager->icons_.size());
866 EXPECT_FALSE((
867 manager->icon_url(InstallableManager::IconUsage::kPrimary).is_empty()));
868 EXPECT_NE(nullptr,
869 (manager->icon(InstallableManager::IconUsage::kPrimary)));
870 EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error());
871 EXPECT_EQ(NO_ERROR_DETECTED, manager->valid_manifest_error());
872 EXPECT_EQ(NO_ERROR_DETECTED,
873 (manager->icon_error(InstallableManager::IconUsage::kPrimary)));
874 EXPECT_TRUE(!manager->task_queue_.HasCurrent());
875 }
876
877 {
878 // Check that a subsequent navigation resets state.
879 ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
880 InstallableManager* manager = GetManager(browser());
881
882 EXPECT_TRUE(manager->manifest().IsEmpty());
883 EXPECT_TRUE(manager->manifest_url().is_empty());
884 EXPECT_FALSE(manager->valid_manifest());
885 EXPECT_FALSE(manager->has_worker());
886 EXPECT_TRUE(manager->icons_.empty());
887 EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error());
888 EXPECT_EQ(NO_ERROR_DETECTED, manager->valid_manifest_error());
889 EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error());
890 EXPECT_TRUE(!manager->task_queue_.HasCurrent());
891 }
892 }
893
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckMaskableIcon)894 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckMaskableIcon) {
895 // Checks that InstallableManager chooses the correct primary icon when the
896 // manifest contains maskable icons.
897
898 // Checks that if a MASKABLE icon is specified, it is used as primary icon.
899 {
900 base::RunLoop run_loop;
901 std::unique_ptr<CallbackTester> tester(
902 new CallbackTester(run_loop.QuitClosure()));
903
904 NavigateAndRunInstallableManager(browser(), tester.get(),
905 GetPrimaryIconPreferMaskableParams(),
906 GetURLOfPageWithServiceWorkerAndManifest(
907 "/banners/manifest_maskable.json"));
908
909 run_loop.Run();
910
911 EXPECT_FALSE(tester->manifest().IsEmpty());
912 EXPECT_FALSE(tester->manifest_url().is_empty());
913
914 EXPECT_FALSE(tester->primary_icon_url().is_empty());
915 EXPECT_NE(nullptr, tester->primary_icon());
916 EXPECT_TRUE(tester->has_maskable_primary_icon());
917
918 EXPECT_FALSE(tester->valid_manifest());
919 EXPECT_FALSE(tester->has_worker());
920 EXPECT_TRUE(tester->splash_icon_url().is_empty());
921 EXPECT_EQ(nullptr, tester->splash_icon());
922 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
923 }
924
925 // Checks that we don't pick a MASKABLE icon if it was not requested.
926 {
927 base::RunLoop run_loop;
928 std::unique_ptr<CallbackTester> tester(
929 new CallbackTester(run_loop.QuitClosure()));
930
931 NavigateAndRunInstallableManager(browser(), tester.get(),
932 GetPrimaryIconParams(),
933 GetURLOfPageWithServiceWorkerAndManifest(
934 "/banners/manifest_maskable.json"));
935
936 run_loop.Run();
937
938 EXPECT_FALSE(tester->manifest().IsEmpty());
939 EXPECT_FALSE(tester->manifest_url().is_empty());
940
941 EXPECT_FALSE(tester->primary_icon_url().is_empty());
942 EXPECT_NE(nullptr, tester->primary_icon());
943 EXPECT_FALSE(tester->has_maskable_primary_icon());
944
945 EXPECT_FALSE(tester->valid_manifest());
946 EXPECT_FALSE(tester->has_worker());
947 EXPECT_TRUE(tester->splash_icon_url().is_empty());
948 EXPECT_EQ(nullptr, tester->splash_icon());
949 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
950 }
951
952 // Checks that we fall back to using an ANY icon if a MASKABLE icon is
953 // requested but not in the manifest.
954 {
955 base::RunLoop run_loop;
956 std::unique_ptr<CallbackTester> tester(
957 new CallbackTester(run_loop.QuitClosure()));
958
959 NavigateAndRunInstallableManager(browser(), tester.get(),
960 GetPrimaryIconPreferMaskableParams(),
961 "/banners/manifest_test_page.html");
962
963 run_loop.Run();
964
965 EXPECT_FALSE(tester->manifest().IsEmpty());
966 EXPECT_FALSE(tester->manifest_url().is_empty());
967
968 EXPECT_FALSE(tester->primary_icon_url().is_empty());
969 EXPECT_NE(nullptr, tester->primary_icon());
970 EXPECT_FALSE(tester->has_maskable_primary_icon());
971
972 EXPECT_FALSE(tester->valid_manifest());
973 EXPECT_FALSE(tester->has_worker());
974 EXPECT_TRUE(tester->splash_icon_url().is_empty());
975 EXPECT_EQ(nullptr, tester->splash_icon());
976 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
977 }
978
979 // Checks that we fall back to using an ANY icon if a MASKABLE icon is
980 // requested but the maskable icon is bad.
981 {
982 base::RunLoop run_loop;
983 std::unique_ptr<CallbackTester> tester(
984 new CallbackTester(run_loop.QuitClosure()));
985
986 NavigateAndRunInstallableManager(
987 browser(), tester.get(), GetPrimaryIconPreferMaskableParams(),
988 GetURLOfPageWithServiceWorkerAndManifest(
989 "/banners/manifest_bad_maskable.json"));
990
991 run_loop.Run();
992
993 EXPECT_FALSE(tester->manifest().IsEmpty());
994 EXPECT_FALSE(tester->manifest_url().is_empty());
995
996 EXPECT_FALSE(tester->primary_icon_url().is_empty());
997 EXPECT_NE(nullptr, tester->primary_icon());
998 EXPECT_FALSE(tester->has_maskable_primary_icon());
999
1000 EXPECT_FALSE(tester->valid_manifest());
1001 EXPECT_FALSE(tester->has_worker());
1002 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1003 EXPECT_EQ(nullptr, tester->splash_icon());
1004 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1005 }
1006 }
1007
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckNavigationWithoutRunning)1008 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1009 CheckNavigationWithoutRunning) {
1010 {
1011 // Expect the call to ManifestAndIconTimeout to kick off an installable
1012 // check and fail it on a not installable page.
1013 base::HistogramTester histograms;
1014 ui_test_utils::NavigateToURL(
1015 browser(),
1016 embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"));
1017
1018 InstallableManager* manager = GetManager(browser());
1019
1020 base::RunLoop run_loop;
1021 std::unique_ptr<CallbackTester> tester(
1022 new CallbackTester(run_loop.QuitClosure()));
1023
1024 // Set up a GetData call which will not record an installable metric to
1025 // ensure we wait until the previous check has finished.
1026 manager->GetData(
1027 GetManifestParams(),
1028 base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1029 base::Unretained(tester.get())));
1030 run_loop.Run();
1031
1032 ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
1033 }
1034
1035 {
1036 // Expect the call to ManifestAndIconTimeout to kick off an installable
1037 // check and pass it on an installable page.
1038 base::HistogramTester histograms;
1039 ui_test_utils::NavigateToURL(
1040 browser(),
1041 embedded_test_server()->GetURL("/banners/manifest_test_page.html"));
1042
1043 InstallableManager* manager = GetManager(browser());
1044
1045 base::RunLoop run_loop;
1046 std::unique_ptr<CallbackTester> tester(
1047 new CallbackTester(run_loop.QuitClosure()));
1048
1049 // Set up a GetData call which will not record an installable metric to
1050 // ensure we wait until the previous check has finished.
1051 manager->GetData(
1052 GetManifestParams(),
1053 base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1054 base::Unretained(tester.get())));
1055 run_loop.Run();
1056
1057 ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
1058 }
1059 }
1060
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckWebappInIframe)1061 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckWebappInIframe) {
1062 base::RunLoop run_loop;
1063 std::unique_ptr<CallbackTester> tester(
1064 new CallbackTester(run_loop.QuitClosure()));
1065
1066 NavigateAndRunInstallableManager(browser(), tester.get(), GetWebAppParams(),
1067 "/banners/iframe_test_page.html");
1068 run_loop.Run();
1069
1070 // The installable manager should only retrieve items in the main frame;
1071 // everything should be empty here.
1072 EXPECT_TRUE(tester->manifest().IsEmpty());
1073 EXPECT_TRUE(tester->manifest_url().is_empty());
1074 EXPECT_TRUE(tester->primary_icon_url().is_empty());
1075 EXPECT_EQ(nullptr, tester->primary_icon());
1076 EXPECT_FALSE(tester->valid_manifest());
1077 EXPECT_FALSE(tester->has_worker());
1078 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1079 EXPECT_EQ(nullptr, tester->splash_icon());
1080 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MANIFEST}, tester->errors());
1081 }
1082
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckPageWithManifestAndNoServiceWorker)1083 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1084 CheckPageWithManifestAndNoServiceWorker) {
1085 // Just fetch the manifest. This should have no error.
1086 {
1087 base::RunLoop run_loop;
1088 std::unique_ptr<CallbackTester> tester(
1089 new CallbackTester(run_loop.QuitClosure()));
1090
1091 NavigateAndRunInstallableManager(
1092 browser(), tester.get(), GetManifestParams(),
1093 "/banners/manifest_no_service_worker.html");
1094 run_loop.Run();
1095
1096 EXPECT_FALSE(tester->manifest().IsEmpty());
1097 EXPECT_FALSE(tester->manifest_url().is_empty());
1098
1099 EXPECT_TRUE(tester->primary_icon_url().is_empty());
1100 EXPECT_EQ(nullptr, tester->primary_icon());
1101 EXPECT_FALSE(tester->valid_manifest());
1102 EXPECT_FALSE(tester->has_worker());
1103 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1104 EXPECT_EQ(nullptr, tester->splash_icon());
1105 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1106 }
1107
1108 // Fetching the full criteria should fail if we don't wait for the worker.
1109 {
1110 base::RunLoop run_loop;
1111 std::unique_ptr<CallbackTester> tester(
1112 new CallbackTester(run_loop.QuitClosure()));
1113
1114 InstallableParams params = GetWebAppParams();
1115 params.wait_for_worker = false;
1116 RunInstallableManager(browser(), tester.get(), params);
1117 run_loop.Run();
1118
1119 EXPECT_FALSE(tester->manifest().IsEmpty());
1120 EXPECT_FALSE(tester->manifest_url().is_empty());
1121
1122 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1123 EXPECT_NE(nullptr, tester->primary_icon());
1124 EXPECT_TRUE(tester->valid_manifest());
1125 EXPECT_FALSE(tester->has_worker());
1126 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1127 EXPECT_EQ(nullptr, tester->splash_icon());
1128 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MATCHING_SERVICE_WORKER},
1129 tester->errors());
1130 }
1131 }
1132
IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,CheckLazyServiceWorkerPassesWhenWaiting)1133 IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,
1134 CheckLazyServiceWorkerPassesWhenWaiting) {
1135 base::RunLoop tester_run_loop, sw_run_loop;
1136 std::unique_ptr<CallbackTester> tester(
1137 new CallbackTester(tester_run_loop.QuitClosure()));
1138
1139 content::WebContents* web_contents =
1140 browser()->tab_strip_model()->GetActiveWebContents();
1141 auto manager = std::make_unique<LazyWorkerInstallableManager>(
1142 web_contents, sw_run_loop.QuitClosure());
1143
1144 {
1145 // Load a URL with no service worker.
1146 GURL test_url = embedded_test_server()->GetURL(
1147 "/banners/manifest_no_service_worker.html");
1148 ui_test_utils::NavigateToURL(browser(), test_url);
1149
1150 // Kick off fetching the data. This should block on waiting for a worker.
1151 manager->GetData(
1152 GetWebAppParams(),
1153 base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1154 base::Unretained(tester.get())));
1155 sw_run_loop.Run();
1156 }
1157
1158 // We should now be waiting for the service worker.
1159 EXPECT_TRUE(tester->manifest().IsEmpty());
1160 EXPECT_FALSE(manager->manifest().IsEmpty());
1161 EXPECT_FALSE(manager->manifest_url().is_empty());
1162 EXPECT_TRUE(manager->valid_manifest());
1163 EXPECT_FALSE(manager->has_worker());
1164 EXPECT_EQ(1u, manager->icons_.size());
1165 EXPECT_FALSE(
1166 (manager->icon_url(InstallableManager::IconUsage::kPrimary).is_empty()));
1167 EXPECT_NE(nullptr, (manager->icon(InstallableManager::IconUsage::kPrimary)));
1168 EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error());
1169 EXPECT_EQ(NO_ERROR_DETECTED, manager->valid_manifest_error());
1170 EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error());
1171 EXPECT_EQ(NO_ERROR_DETECTED,
1172 (manager->icon_error(InstallableManager::IconUsage::kPrimary)));
1173 EXPECT_TRUE(!manager->task_queue_.HasCurrent());
1174 EXPECT_TRUE(!manager->task_queue_.paused_tasks_.empty());
1175
1176 {
1177 // Fetching just the manifest and icons should not hang while the other call
1178 // is waiting for a worker.
1179 base::RunLoop run_loop;
1180 std::unique_ptr<CallbackTester> nested_tester(
1181 new CallbackTester(run_loop.QuitClosure()));
1182 manager->GetData(
1183 GetPrimaryIconParams(),
1184 base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1185 base::Unretained(nested_tester.get())));
1186 run_loop.Run();
1187
1188 EXPECT_FALSE(nested_tester->manifest().IsEmpty());
1189 EXPECT_FALSE(nested_tester->manifest_url().is_empty());
1190 EXPECT_FALSE(nested_tester->primary_icon_url().is_empty());
1191 EXPECT_NE(nullptr, nested_tester->primary_icon());
1192 EXPECT_TRUE(nested_tester->valid_manifest());
1193 EXPECT_FALSE(nested_tester->has_worker());
1194 EXPECT_TRUE(nested_tester->splash_icon_url().is_empty());
1195 EXPECT_EQ(nullptr, nested_tester->splash_icon());
1196 EXPECT_EQ(std::vector<InstallableStatusCode>{}, nested_tester->errors());
1197 }
1198
1199 // Load the service worker.
1200 if (IsServiceWorkerOfflineSupported()) {
1201 EXPECT_TRUE(content::ExecuteScript(
1202 web_contents,
1203 "navigator.serviceWorker.register('service_worker.js');"
1204 ));
1205 } else {
1206 EXPECT_TRUE(content::ExecuteScript(
1207 web_contents,
1208 "navigator.serviceWorker.register("
1209 "'service_worker_empty_fetch_handler.js');"));
1210 }
1211 tester_run_loop.Run();
1212
1213 // We should have passed now.
1214 EXPECT_FALSE(tester->manifest().IsEmpty());
1215 EXPECT_FALSE(tester->manifest_url().is_empty());
1216 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1217 EXPECT_NE(nullptr, tester->primary_icon());
1218 EXPECT_TRUE(tester->valid_manifest());
1219 if (OfflineSupported()) {
1220 EXPECT_TRUE(tester->has_worker());
1221 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1222 } else {
1223 EXPECT_FALSE(tester->has_worker());
1224 EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE},
1225 tester->errors());
1226 }
1227 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1228 EXPECT_EQ(nullptr, tester->splash_icon());
1229
1230 // Verify internal state.
1231 EXPECT_FALSE(manager->manifest().IsEmpty());
1232 EXPECT_FALSE(manager->manifest_url().is_empty());
1233 EXPECT_TRUE(manager->valid_manifest());
1234 if (OfflineSupported()) {
1235 EXPECT_TRUE(manager->has_worker());
1236 EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error());
1237 } else {
1238 EXPECT_FALSE(manager->has_worker());
1239 EXPECT_EQ(NOT_OFFLINE_CAPABLE, manager->worker_error());
1240 }
1241 EXPECT_EQ(1u, manager->icons_.size());
1242 EXPECT_FALSE(
1243 (manager->icon_url(InstallableManager::IconUsage::kPrimary).is_empty()));
1244 EXPECT_NE(nullptr, (manager->icon(InstallableManager::IconUsage::kPrimary)));
1245 EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error());
1246 EXPECT_EQ(NO_ERROR_DETECTED, manager->valid_manifest_error());
1247 EXPECT_EQ(NO_ERROR_DETECTED,
1248 (manager->icon_error(InstallableManager::IconUsage::kPrimary)));
1249 EXPECT_TRUE(!manager->task_queue_.HasCurrent());
1250 EXPECT_FALSE(!manager->task_queue_.paused_tasks_.empty());
1251 }
1252
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckLazyServiceWorkerNoFetchHandlerFails)1253 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1254 CheckLazyServiceWorkerNoFetchHandlerFails) {
1255 base::RunLoop tester_run_loop, sw_run_loop;
1256 std::unique_ptr<CallbackTester> tester(
1257 new CallbackTester(tester_run_loop.QuitClosure()));
1258
1259 content::WebContents* web_contents =
1260 browser()->tab_strip_model()->GetActiveWebContents();
1261 auto manager = std::make_unique<LazyWorkerInstallableManager>(
1262 web_contents, sw_run_loop.QuitClosure());
1263
1264 // Load a URL with no service worker.
1265 GURL test_url = embedded_test_server()->GetURL(
1266 "/banners/manifest_no_service_worker.html");
1267 ui_test_utils::NavigateToURL(browser(), test_url);
1268
1269 // Kick off fetching the data. This should block on waiting for a worker.
1270 manager->GetData(GetWebAppParams(),
1271 base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1272 base::Unretained(tester.get())));
1273 sw_run_loop.Run();
1274
1275 // We should now be waiting for the service worker.
1276 EXPECT_TRUE(!manager->task_queue_.HasCurrent());
1277 EXPECT_TRUE(!manager->task_queue_.paused_tasks_.empty());
1278
1279 // Load the service worker with no fetch handler.
1280 EXPECT_TRUE(content::ExecuteScript(web_contents,
1281 "navigator.serviceWorker.register('"
1282 "service_worker_no_fetch_handler.js');"));
1283 tester_run_loop.Run();
1284
1285 // We should fail the check.
1286 EXPECT_FALSE(tester->manifest().IsEmpty());
1287 EXPECT_FALSE(tester->manifest_url().is_empty());
1288 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1289 EXPECT_NE(nullptr, tester->primary_icon());
1290 EXPECT_TRUE(tester->valid_manifest());
1291 EXPECT_FALSE(tester->has_worker());
1292 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1293 EXPECT_EQ(nullptr, tester->splash_icon());
1294 EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE},
1295 tester->errors());
1296 }
1297
IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,CheckServiceWorkerErrorIsNotCached)1298 IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,
1299 CheckServiceWorkerErrorIsNotCached) {
1300 content::WebContents* web_contents =
1301 browser()->tab_strip_model()->GetActiveWebContents();
1302 base::RunLoop sw_run_loop;
1303 auto manager = std::make_unique<LazyWorkerInstallableManager>(
1304 web_contents, sw_run_loop.QuitClosure());
1305
1306 // Load a URL with no service worker.
1307 GURL test_url = embedded_test_server()->GetURL(
1308 "/banners/manifest_no_service_worker.html");
1309 ui_test_utils::NavigateToURL(browser(), test_url);
1310
1311 {
1312 base::RunLoop tester_run_loop;
1313 std::unique_ptr<CallbackTester> tester(
1314 new CallbackTester(tester_run_loop.QuitClosure()));
1315
1316 InstallableParams params = GetWebAppParams();
1317 params.wait_for_worker = false;
1318 manager->GetData(
1319 params, base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1320 base::Unretained(tester.get())));
1321 tester_run_loop.Run();
1322
1323 // We should have returned with an error.
1324 EXPECT_FALSE(tester->manifest().IsEmpty());
1325 EXPECT_TRUE(tester->valid_manifest());
1326 EXPECT_FALSE(tester->has_worker());
1327 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MATCHING_SERVICE_WORKER},
1328 tester->errors());
1329 }
1330
1331 {
1332 base::RunLoop tester_run_loop;
1333 std::unique_ptr<CallbackTester> tester(
1334 new CallbackTester(tester_run_loop.QuitClosure()));
1335
1336 InstallableParams params = GetWebAppParams();
1337 manager->GetData(
1338 params, base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1339 base::Unretained(tester.get())));
1340 sw_run_loop.Run();
1341
1342 if (IsServiceWorkerOfflineSupported()) {
1343 EXPECT_TRUE(content::ExecuteScript(
1344 web_contents,
1345 "navigator.serviceWorker.register('service_worker.js');"));
1346 } else {
1347 EXPECT_TRUE(content::ExecuteScript(
1348 web_contents,
1349 "navigator.serviceWorker.register("
1350 "'service_worker_empty_fetch_handler.js');"));
1351 }
1352 tester_run_loop.Run();
1353
1354 // The callback result will depend on the state of offline support.
1355 EXPECT_FALSE(tester->manifest().IsEmpty());
1356 EXPECT_TRUE(tester->valid_manifest());
1357 if (OfflineSupported()) {
1358 EXPECT_TRUE(tester->has_worker());
1359 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1360 } else {
1361 EXPECT_FALSE(tester->has_worker());
1362 EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE},
1363 tester->errors());
1364 }
1365 }
1366 }
1367
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckPageWithNoServiceWorkerFetchHandler)1368 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1369 CheckPageWithNoServiceWorkerFetchHandler) {
1370 base::RunLoop run_loop;
1371 std::unique_ptr<CallbackTester> tester(
1372 new CallbackTester(run_loop.QuitClosure()));
1373
1374 NavigateAndRunInstallableManager(
1375 browser(), tester.get(), GetWebAppParams(),
1376 "/banners/no_sw_fetch_handler_test_page.html");
1377
1378 RunInstallableManager(browser(), tester.get(), GetWebAppParams());
1379 run_loop.Run();
1380
1381 EXPECT_FALSE(tester->manifest().IsEmpty());
1382 EXPECT_FALSE(tester->manifest_url().is_empty());
1383
1384 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1385 EXPECT_NE(nullptr, tester->primary_icon());
1386 EXPECT_TRUE(tester->valid_manifest());
1387 EXPECT_FALSE(tester->has_worker());
1388 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1389 EXPECT_EQ(nullptr, tester->splash_icon());
1390 EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE},
1391 tester->errors());
1392 }
1393
IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,CheckPageWithNestedServiceWorkerCanBeInstalled)1394 IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,
1395 CheckPageWithNestedServiceWorkerCanBeInstalled) {
1396 base::RunLoop run_loop;
1397 std::unique_ptr<CallbackTester> tester(
1398 new CallbackTester(run_loop.QuitClosure()));
1399
1400 NavigateAndRunInstallableManager(browser(), tester.get(), GetWebAppParams(),
1401 GetPath("/banners/nested_sw_test_page"));
1402
1403 RunInstallableManager(browser(), tester.get(), GetWebAppParams());
1404 run_loop.Run();
1405
1406 EXPECT_FALSE(tester->manifest().IsEmpty());
1407 EXPECT_FALSE(tester->manifest_url().is_empty());
1408
1409 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1410 EXPECT_NE(nullptr, tester->primary_icon());
1411 EXPECT_TRUE(tester->valid_manifest());
1412 if (OfflineSupported()) {
1413 EXPECT_TRUE(tester->has_worker());
1414 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1415 } else {
1416 EXPECT_FALSE(tester->has_worker());
1417 EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE},
1418 tester->errors());
1419 }
1420 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1421 EXPECT_EQ(nullptr, tester->splash_icon());
1422 }
1423
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckDataUrlIcon)1424 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckDataUrlIcon) {
1425 // Verify that InstallableManager can handle data URL icons.
1426 base::RunLoop run_loop;
1427 std::unique_ptr<CallbackTester> tester(
1428 new CallbackTester(run_loop.QuitClosure()));
1429
1430 NavigateAndRunInstallableManager(browser(), tester.get(), GetWebAppParams(),
1431 GetURLOfPageWithServiceWorkerAndManifest(
1432 "/banners/manifest_data_url_icon.json"));
1433 run_loop.Run();
1434
1435
1436 EXPECT_FALSE(tester->manifest().IsEmpty());
1437 EXPECT_FALSE(tester->manifest_url().is_empty());
1438
1439 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1440 EXPECT_NE(nullptr, tester->primary_icon());
1441 EXPECT_EQ(144, tester->primary_icon()->width());
1442 EXPECT_TRUE(tester->valid_manifest());
1443 EXPECT_TRUE(tester->has_worker());
1444 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1445 EXPECT_EQ(nullptr, tester->splash_icon());
1446 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1447 }
1448
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckManifestCorruptedIcon)1449 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1450 CheckManifestCorruptedIcon) {
1451 // Verify that the returned InstallableData::primary_icon is null if the web
1452 // manifest points to a corrupt primary icon.
1453 base::RunLoop run_loop;
1454 std::unique_ptr<CallbackTester> tester(
1455 new CallbackTester(run_loop.QuitClosure()));
1456
1457 NavigateAndRunInstallableManager(browser(), tester.get(),
1458 GetPrimaryIconParams(),
1459 GetURLOfPageWithServiceWorkerAndManifest(
1460 "/banners/manifest_bad_icon.json"));
1461 run_loop.Run();
1462
1463 EXPECT_FALSE(tester->manifest().IsEmpty());
1464 EXPECT_FALSE(tester->manifest_url().is_empty());
1465 EXPECT_TRUE(tester->primary_icon_url().is_empty());
1466 EXPECT_EQ(nullptr, tester->primary_icon());
1467 EXPECT_FALSE(tester->valid_manifest());
1468 EXPECT_FALSE(tester->has_worker());
1469 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1470 EXPECT_EQ(nullptr, tester->splash_icon());
1471 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ICON_AVAILABLE},
1472 tester->errors());
1473 }
1474
IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,CheckChangeInIconDimensions)1475 IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest,
1476 CheckChangeInIconDimensions) {
1477 // Verify that a follow-up request for a primary icon with a different size
1478 // works.
1479 {
1480 base::RunLoop run_loop;
1481 std::unique_ptr<CallbackTester> tester(
1482 new CallbackTester(run_loop.QuitClosure()));
1483
1484 NavigateAndRunInstallableManager(browser(), tester.get(), GetWebAppParams(),
1485 GetPath("/banners/manifest_test_page"));
1486 run_loop.Run();
1487
1488 EXPECT_FALSE(tester->manifest_url().is_empty());
1489 EXPECT_FALSE(tester->manifest().IsEmpty());
1490 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1491 EXPECT_NE(nullptr, tester->primary_icon());
1492 EXPECT_TRUE(tester->valid_manifest());
1493 if (OfflineSupported()) {
1494 EXPECT_TRUE(tester->has_worker());
1495 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1496 } else {
1497 EXPECT_FALSE(tester->has_worker());
1498 EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE},
1499 tester->errors());
1500 }
1501 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1502 EXPECT_EQ(nullptr, tester->splash_icon());
1503 }
1504
1505 {
1506 base::RunLoop run_loop;
1507 std::unique_ptr<CallbackTester> tester(
1508 new CallbackTester(run_loop.QuitClosure()));
1509 RunInstallableManager(browser(), tester.get(), GetWebAppParams());
1510
1511 run_loop.Run();
1512
1513 // The smaller primary icon requirements should allow this to pass.
1514 EXPECT_FALSE(tester->manifest_url().is_empty());
1515 EXPECT_FALSE(tester->manifest().IsEmpty());
1516 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1517 EXPECT_NE(nullptr, tester->primary_icon());
1518 EXPECT_TRUE(tester->valid_manifest());
1519 if (OfflineSupported()) {
1520 EXPECT_TRUE(tester->has_worker());
1521 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1522 } else {
1523 EXPECT_FALSE(tester->has_worker());
1524 EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE},
1525 tester->errors());
1526 }
1527 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1528 EXPECT_EQ(nullptr, tester->splash_icon());
1529 }
1530 }
1531
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckNestedCallsToGetData)1532 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1533 CheckNestedCallsToGetData) {
1534 // Verify that we can call GetData while in a callback from GetData.
1535 base::RunLoop run_loop;
1536 InstallableParams params = GetWebAppParams();
1537 std::unique_ptr<NestedCallbackTester> tester(new NestedCallbackTester(
1538 GetManager(browser()), params, run_loop.QuitClosure()));
1539
1540 tester->Run();
1541 run_loop.Run();
1542 }
1543
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,ManifestUrlChangeFlushesState)1544 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1545 ManifestUrlChangeFlushesState) {
1546 content::WebContents* web_contents =
1547 browser()->tab_strip_model()->GetActiveWebContents();
1548 auto manager = std::make_unique<ResetDataInstallableManager>(web_contents);
1549
1550 // Start on a page with no manifest.
1551 ui_test_utils::NavigateToURL(
1552 browser(),
1553 embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"));
1554
1555 {
1556 // Fetch the data. This should return an empty manifest.
1557 base::RunLoop run_loop;
1558 std::unique_ptr<CallbackTester> tester(
1559 new CallbackTester(run_loop.QuitClosure()));
1560
1561 manager->GetData(
1562 GetWebAppParams(),
1563 base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1564 base::Unretained(tester.get())));
1565 run_loop.Run();
1566
1567 EXPECT_TRUE(tester->manifest().IsEmpty());
1568 EXPECT_EQ(NO_MANIFEST, manager->manifest_error());
1569 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MANIFEST},
1570 tester->errors());
1571 }
1572
1573 {
1574 // Injecting a manifest URL but not navigating should flush the state.
1575 base::RunLoop run_loop;
1576 manager->SetQuitClosure(run_loop.QuitClosure());
1577 EXPECT_TRUE(content::ExecuteScript(web_contents, "addManifestLinkTag()"));
1578 run_loop.Run();
1579
1580 EXPECT_TRUE(manager->manifest().IsEmpty());
1581 EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error());
1582 }
1583
1584 {
1585 // Fetch the data again. This should succeed.
1586 base::RunLoop run_loop;
1587 std::unique_ptr<CallbackTester> tester(
1588 new CallbackTester(run_loop.QuitClosure()));
1589
1590 manager->GetData(
1591 GetWebAppParams(),
1592 base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1593 base::Unretained(tester.get())));
1594 run_loop.Run();
1595
1596 EXPECT_FALSE(tester->manifest().IsEmpty());
1597 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1598 EXPECT_EQ(base::ASCIIToUTF16("Manifest test app"), tester->manifest().name);
1599 EXPECT_EQ(base::string16(),
1600 tester->manifest().short_name.value_or(base::string16()));
1601 }
1602
1603 {
1604 // Flush the state again by changing the manifest URL.
1605 base::RunLoop run_loop;
1606 manager->SetQuitClosure(run_loop.QuitClosure());
1607
1608 GURL manifest_url = embedded_test_server()->GetURL(
1609 "/banners/manifest_short_name_only.json");
1610 EXPECT_TRUE(content::ExecuteScript(
1611 web_contents, "changeManifestUrl('" + manifest_url.spec() + "');"));
1612 run_loop.Run();
1613
1614 EXPECT_TRUE(manager->manifest().IsEmpty());
1615 }
1616
1617 {
1618 // Fetch again. This should return the data from the new manifest.
1619 base::RunLoop run_loop;
1620 std::unique_ptr<CallbackTester> tester(
1621 new CallbackTester(run_loop.QuitClosure()));
1622
1623 manager->GetData(
1624 GetWebAppParams(),
1625 base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck,
1626 base::Unretained(tester.get())));
1627 run_loop.Run();
1628
1629 EXPECT_FALSE(tester->manifest().IsEmpty());
1630 EXPECT_EQ(base::string16(),
1631 tester->manifest().name.value_or(base::string16()));
1632 EXPECT_EQ(base::ASCIIToUTF16("Manifest"), tester->manifest().short_name);
1633 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1634 }
1635 }
1636
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,DebugModeWithNoManifest)1637 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, DebugModeWithNoManifest) {
1638 // Ensure that a page with no manifest stops with NO_MANIFEST in debug mode.
1639 base::RunLoop run_loop;
1640 std::unique_ptr<CallbackTester> tester(
1641 new CallbackTester(run_loop.QuitClosure()));
1642
1643 InstallableParams params = GetWebAppParams();
1644 params.is_debug_mode = true;
1645 ui_test_utils::NavigateToURL(
1646 browser(),
1647 embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"));
1648 RunInstallableManager(browser(), tester.get(), params);
1649 run_loop.Run();
1650
1651 EXPECT_EQ(std::vector<InstallableStatusCode>({NO_MANIFEST}),
1652 tester->errors());
1653 }
1654
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,DebugModeAccumulatesErrorsWithManifest)1655 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1656 DebugModeAccumulatesErrorsWithManifest) {
1657 base::RunLoop run_loop;
1658 std::unique_ptr<CallbackTester> tester(
1659 new CallbackTester(run_loop.QuitClosure()));
1660
1661 InstallableParams params = GetWebAppParams();
1662 params.is_debug_mode = true;
1663 NavigateAndRunInstallableManager(browser(), tester.get(), params,
1664 GetURLOfPageWithServiceWorkerAndManifest(
1665 "/banners/play_app_manifest.json"));
1666 run_loop.Run();
1667
1668 EXPECT_EQ(std::vector<InstallableStatusCode>(
1669 {START_URL_NOT_VALID, MANIFEST_MISSING_NAME_OR_SHORT_NAME,
1670 MANIFEST_DISPLAY_NOT_SUPPORTED, MANIFEST_MISSING_SUITABLE_ICON,
1671 NO_ACCEPTABLE_ICON}),
1672 tester->errors());
1673 }
1674
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,DebugModeBadFallbackMaskable)1675 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1676 DebugModeBadFallbackMaskable) {
1677 base::RunLoop run_loop;
1678 std::unique_ptr<CallbackTester> tester(
1679 new CallbackTester(run_loop.QuitClosure()));
1680
1681 InstallableParams params = GetPrimaryIconPreferMaskableParams();
1682 params.is_debug_mode = true;
1683
1684 NavigateAndRunInstallableManager(
1685 browser(), tester.get(), params,
1686 GetURLOfPageWithServiceWorkerAndManifest(
1687 "/banners/manifest_one_bad_maskable.json"));
1688
1689 run_loop.Run();
1690
1691 EXPECT_FALSE(tester->manifest().IsEmpty());
1692 EXPECT_FALSE(tester->manifest_url().is_empty());
1693
1694 EXPECT_TRUE(tester->primary_icon_url().is_empty());
1695 EXPECT_EQ(nullptr, tester->primary_icon());
1696 EXPECT_FALSE(tester->has_maskable_primary_icon());
1697
1698 EXPECT_FALSE(tester->valid_manifest());
1699 EXPECT_FALSE(tester->has_worker());
1700 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1701 EXPECT_EQ(nullptr, tester->splash_icon());
1702 EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON},
1703 tester->errors());
1704 }
1705
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,GetAllInatallabilityErrorsNoErrors)1706 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1707 GetAllInatallabilityErrorsNoErrors) {
1708 EXPECT_EQ(std::vector<content::InstallabilityError>{},
1709 NavigateAndGetAllInstallabilityErrors(
1710 browser(), "/banners/manifest_test_page.html"));
1711 }
1712
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,GetAllInatallabilityErrorsWithNoManifest)1713 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1714 GetAllInatallabilityErrorsWithNoManifest) {
1715 EXPECT_EQ(std::vector<content::InstallabilityError>{GetInstallabilityError(
1716 NO_MANIFEST)},
1717 NavigateAndGetAllInstallabilityErrors(
1718 browser(), "/banners/no_manifest_test_page.html"));
1719 }
1720
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,GetAllInatallabilityErrorsWithPlayAppManifest)1721 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1722 GetAllInatallabilityErrorsWithPlayAppManifest) {
1723 EXPECT_EQ(std::vector<content::InstallabilityError>(
1724 {GetInstallabilityError(START_URL_NOT_VALID),
1725 GetInstallabilityError(MANIFEST_MISSING_NAME_OR_SHORT_NAME),
1726 GetInstallabilityError(MANIFEST_DISPLAY_NOT_SUPPORTED),
1727 GetInstallabilityError(MANIFEST_MISSING_SUITABLE_ICON),
1728 GetInstallabilityError(NO_ACCEPTABLE_ICON)}),
1729 NavigateAndGetAllInstallabilityErrors(
1730 browser(), GetURLOfPageWithServiceWorkerAndManifest(
1731 "/banners/play_app_manifest.json")));
1732 }
1733
IN_PROC_BROWSER_TEST_F(InstallableManagerAllowlistOriginBrowserTest,SecureOriginCheckRespectsUnsafeFlag)1734 IN_PROC_BROWSER_TEST_F(InstallableManagerAllowlistOriginBrowserTest,
1735 SecureOriginCheckRespectsUnsafeFlag) {
1736 // The allowlisted origin should be regarded as secure.
1737 ui_test_utils::NavigateToURL(browser(), GURL(kInsecureOrigin));
1738 content::WebContents* contents =
1739 browser()->tab_strip_model()->GetActiveWebContents();
1740 EXPECT_TRUE(InstallableManager::IsContentSecure(contents));
1741
1742 // While a non-allowlisted origin should not.
1743 ui_test_utils::NavigateToURL(browser(), GURL(kOtherInsecureOrigin));
1744 EXPECT_FALSE(InstallableManager::IsContentSecure(contents));
1745 }
1746
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,NarrowServiceWorker)1747 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, NarrowServiceWorker) {
1748 const GURL url =
1749 embedded_test_server()->GetURL("/banners/scope_c/scope_c.html");
1750 {
1751 web_app::ServiceWorkerRegistrationWaiter registration_waiter(
1752 browser()->profile(), url);
1753 ui_test_utils::NavigateToURL(browser(), url);
1754 registration_waiter.AwaitRegistration();
1755 }
1756 base::RunLoop run_loop;
1757 std::unique_ptr<CallbackTester> tester(
1758 new CallbackTester(run_loop.QuitClosure()));
1759
1760 InstallableParams params = GetWebAppParams();
1761 params.wait_for_worker = false;
1762
1763 RunInstallableManager(browser(), tester.get(), params);
1764 run_loop.Run();
1765
1766 EXPECT_EQ(std::vector<InstallableStatusCode>({NO_MATCHING_SERVICE_WORKER}),
1767 tester->errors());
1768 }
1769
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,CheckSplashIcon)1770 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckSplashIcon) {
1771 // Checks that InstallableManager chooses the correct splash icon.
1772
1773 // Test page has a manifest with only one icon, primary icon and splash icon
1774 // should be the same one.
1775 {
1776 base::RunLoop run_loop;
1777 std::unique_ptr<CallbackTester> tester(
1778 new CallbackTester(run_loop.QuitClosure()));
1779
1780 NavigateAndRunInstallableManager(browser(), tester.get(),
1781 GetPrimaryIconAndSplashIconParams(),
1782 GetURLOfPageWithServiceWorkerAndManifest(
1783 "/banners/manifest_one_icon.json"));
1784
1785 run_loop.Run();
1786
1787 EXPECT_FALSE(tester->manifest().IsEmpty());
1788 EXPECT_FALSE(tester->manifest_url().is_empty());
1789
1790 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1791 EXPECT_NE(nullptr, tester->primary_icon());
1792 EXPECT_FALSE(tester->valid_manifest());
1793 EXPECT_FALSE(tester->has_worker());
1794 EXPECT_FALSE(tester->splash_icon_url().is_empty());
1795 EXPECT_NE(nullptr, tester->splash_icon());
1796 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1797
1798 EXPECT_EQ(tester->primary_icon_url(), tester->splash_icon_url());
1799 }
1800
1801 // Test page has a manifest with only one maskable icon. This should fail to
1802 // get a splash icon but not record an error.
1803 {
1804 base::RunLoop run_loop;
1805 std::unique_ptr<CallbackTester> tester(
1806 new CallbackTester(run_loop.QuitClosure()));
1807
1808 NavigateAndRunInstallableManager(
1809 browser(), tester.get(),
1810 GetPrimaryIconPreferMaskableAndSplashIconParams(),
1811 GetURLOfPageWithServiceWorkerAndManifest(
1812 "/banners/manifest_one_maskable.json"));
1813
1814 run_loop.Run();
1815
1816 EXPECT_FALSE(tester->manifest().IsEmpty());
1817 EXPECT_FALSE(tester->manifest_url().is_empty());
1818
1819 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1820 EXPECT_NE(nullptr, tester->primary_icon());
1821 EXPECT_TRUE(tester->has_maskable_primary_icon());
1822 EXPECT_FALSE(tester->valid_manifest());
1823 EXPECT_FALSE(tester->has_worker());
1824 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1825 EXPECT_EQ(nullptr, tester->splash_icon());
1826 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1827 }
1828 }
1829
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,ManifestLinkChangeReportsError)1830 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
1831 ManifestLinkChangeReportsError) {
1832 InstallableManager* manager = GetManager(browser());
1833
1834 base::RunLoop run_loop;
1835 std::unique_ptr<CallbackTester> tester(
1836 new CallbackTester(run_loop.QuitClosure()));
1837
1838 NavigateAndRunInstallableManager(browser(), tester.get(), GetManifestParams(),
1839 "/banners/manifest_test_page.html");
1840 // Simulate a manifest URL update by just calling the observer function.
1841 static_cast<content::WebContentsObserver*>(manager)->DidUpdateWebManifestURL(
1842 nullptr, base::nullopt);
1843 run_loop.Run();
1844
1845 ASSERT_EQ(tester->errors().size(), 1u);
1846 EXPECT_EQ(tester->errors()[0], MANIFEST_URL_CHANGED);
1847 }
1848
1849 // A dedicated test fixture for DisplayOverride, which is supported
1850 // only for the new web apps mode, and requires a command line switch
1851 // to enable manifest parsing.
1852 class InstallableManagerBrowserTest_DisplayOverride
1853 : public InstallableManagerBrowserTest {
1854 public:
InstallableManagerBrowserTest_DisplayOverride()1855 InstallableManagerBrowserTest_DisplayOverride() {
1856 scoped_feature_list_.InitAndEnableFeature(
1857 features::kWebAppManifestDisplayOverride);
1858 }
1859
1860 private:
1861 base::test::ScopedFeatureList scoped_feature_list_;
1862 };
1863
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest_DisplayOverride,CheckManifestOnly)1864 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest_DisplayOverride,
1865 CheckManifestOnly) {
1866 base::RunLoop run_loop;
1867 std::unique_ptr<CallbackTester> tester(
1868 new CallbackTester(run_loop.QuitClosure()));
1869
1870 NavigateAndRunInstallableManager(
1871 browser(), tester.get(), GetManifestParams(),
1872 GetURLOfPageWithServiceWorkerAndManifest(
1873 "/banners/manifest_display_override.json"));
1874 run_loop.Run();
1875
1876 EXPECT_FALSE(tester->manifest().IsEmpty());
1877 EXPECT_FALSE(tester->manifest_url().is_empty());
1878 ASSERT_EQ(2u, tester->manifest().display_override.size());
1879 EXPECT_EQ(blink::mojom::DisplayMode::kMinimalUi,
1880 tester->manifest().display_override[0]);
1881 EXPECT_EQ(blink::mojom::DisplayMode::kStandalone,
1882 tester->manifest().display_override[1]);
1883
1884 EXPECT_TRUE(tester->primary_icon_url().is_empty());
1885 EXPECT_EQ(nullptr, tester->primary_icon());
1886 EXPECT_FALSE(tester->has_maskable_primary_icon());
1887 EXPECT_FALSE(tester->valid_manifest());
1888 EXPECT_FALSE(tester->has_worker());
1889 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1890 EXPECT_EQ(nullptr, tester->splash_icon());
1891 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1892 }
1893
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest_DisplayOverride,ManifestDisplayOverrideReportsError)1894 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest_DisplayOverride,
1895 ManifestDisplayOverrideReportsError) {
1896 base::RunLoop run_loop;
1897 std::unique_ptr<CallbackTester> tester(
1898 new CallbackTester(run_loop.QuitClosure()));
1899
1900 NavigateAndRunInstallableManager(
1901 browser(), tester.get(), GetWebAppParams(),
1902 GetURLOfPageWithServiceWorkerAndManifest(
1903 "/banners/manifest_display_override_contains_browser.json"));
1904 run_loop.Run();
1905
1906 EXPECT_FALSE(tester->manifest().IsEmpty());
1907 EXPECT_FALSE(tester->manifest_url().is_empty());
1908 ASSERT_EQ(3u, tester->manifest().display_override.size());
1909 EXPECT_EQ(blink::mojom::DisplayMode::kBrowser,
1910 tester->manifest().display_override[0]);
1911 EXPECT_EQ(blink::mojom::DisplayMode::kMinimalUi,
1912 tester->manifest().display_override[1]);
1913 EXPECT_EQ(blink::mojom::DisplayMode::kStandalone,
1914 tester->manifest().display_override[2]);
1915 EXPECT_EQ(
1916 std::vector<InstallableStatusCode>{
1917 MANIFEST_DISPLAY_OVERRIDE_NOT_SUPPORTED},
1918 tester->errors());
1919 }
1920
IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest_DisplayOverride,FallbackToDisplayBrowser)1921 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest_DisplayOverride,
1922 FallbackToDisplayBrowser) {
1923 base::RunLoop run_loop;
1924 std::unique_ptr<CallbackTester> tester(
1925 new CallbackTester(run_loop.QuitClosure()));
1926
1927 NavigateAndRunInstallableManager(
1928 browser(), tester.get(), GetWebAppParams(),
1929 GetURLOfPageWithServiceWorkerAndManifest(
1930 "/banners/manifest_display_override_display_is_browser.json"));
1931 run_loop.Run();
1932
1933 EXPECT_FALSE(tester->manifest().IsEmpty());
1934 EXPECT_FALSE(tester->manifest_url().is_empty());
1935 ASSERT_EQ(1u, tester->manifest().display_override.size());
1936 EXPECT_EQ(blink::mojom::DisplayMode::kStandalone,
1937 tester->manifest().display_override[0]);
1938
1939 EXPECT_FALSE(tester->primary_icon_url().is_empty());
1940 EXPECT_NE(nullptr, tester->primary_icon());
1941 EXPECT_EQ(144, tester->primary_icon()->width());
1942 EXPECT_TRUE(tester->valid_manifest());
1943 EXPECT_TRUE(tester->has_worker());
1944 EXPECT_TRUE(tester->splash_icon_url().is_empty());
1945 EXPECT_EQ(nullptr, tester->splash_icon());
1946 EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors());
1947 }
1948