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 <utility>
6 #include <vector>
7 
8 #include "base/bind.h"
9 #include "base/callback.h"
10 #include "base/macros.h"
11 #include "base/optional.h"
12 #include "base/run_loop.h"
13 #include "base/strings/string16.h"
14 #include "base/test/bind.h"
15 #include "base/test/metrics/histogram_tester.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "chrome/browser/banners/app_banner_manager.h"
18 #include "chrome/browser/banners/app_banner_manager_browsertest_base.h"
19 #include "chrome/browser/banners/app_banner_manager_desktop.h"
20 #include "chrome/browser/banners/app_banner_metrics.h"
21 #include "chrome/browser/banners/app_banner_settings_helper.h"
22 #include "chrome/browser/engagement/site_engagement_score.h"
23 #include "chrome/browser/engagement/site_engagement_service.h"
24 #include "chrome/browser/installable/installable_logging.h"
25 #include "chrome/browser/installable/installable_manager.h"
26 #include "chrome/browser/installable/installable_metrics.h"
27 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/browser/ui/browser.h"
29 #include "chrome/browser/ui/tabs/tab_strip_model.h"
30 #include "chrome/test/base/ui_test_utils.h"
31 #include "content/public/test/browser_test.h"
32 #include "content/public/test/browser_test_utils.h"
33 
34 namespace banners {
35 
36 using State = AppBannerManager::State;
37 
38 // Browser tests for web app banners.
39 // NOTE: this test relies on service workers; failures and flakiness may be due
40 // to changes in SW code.
41 class AppBannerManagerTest : public AppBannerManager {
42  public:
AppBannerManagerTest(content::WebContents * web_contents)43   explicit AppBannerManagerTest(content::WebContents* web_contents)
44       : AppBannerManager(web_contents) {}
45 
~AppBannerManagerTest()46   ~AppBannerManagerTest() override {}
47 
RequestAppBanner(const GURL & validated_url)48   void RequestAppBanner(const GURL& validated_url) override {
49     // Filter out about:blank navigations - we use these in testing to force
50     // Stop() to be called.
51     if (validated_url == GURL("about:blank"))
52       return;
53 
54     AppBannerManager::RequestAppBanner(validated_url);
55   }
56 
banner_shown()57   bool banner_shown() { return banner_shown_.get() && *banner_shown_; }
58 
install_source()59   WebappInstallSource install_source() {
60     if (install_source_.get())
61       return *install_source_;
62 
63     return WebappInstallSource::COUNT;
64   }
65 
clear_will_show()66   void clear_will_show() { banner_shown_.reset(); }
67 
state()68   State state() { return AppBannerManager::state(); }
69 
70   // Configures a callback to be invoked when the app banner flow finishes.
PrepareDone(base::OnceClosure on_done)71   void PrepareDone(base::OnceClosure on_done) { on_done_ = std::move(on_done); }
72 
73   // Configures a callback to be invoked from OnBannerPromptReply.
PrepareBannerPromptReply(base::OnceClosure on_banner_prompt_reply)74   void PrepareBannerPromptReply(base::OnceClosure on_banner_prompt_reply) {
75     on_banner_prompt_reply_ = std::move(on_banner_prompt_reply);
76   }
77 
78  protected:
79   // All calls to RequestAppBanner should terminate in one of Stop() (not
80   // showing banner), UpdateState(State::PENDING_ENGAGEMENT) (waiting for
81   // sufficient engagement), or ShowBannerUi(). Override these methods to
82   // capture test status.
Stop(InstallableStatusCode code)83   void Stop(InstallableStatusCode code) override {
84     AppBannerManager::Stop(code);
85     ASSERT_FALSE(banner_shown_.get());
86     banner_shown_.reset(new bool(false));
87     install_source_.reset(new WebappInstallSource(WebappInstallSource::COUNT));
88     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
89                                                   std::move(on_done_));
90   }
91 
ShowBannerUi(WebappInstallSource install_source)92   void ShowBannerUi(WebappInstallSource install_source) override {
93     // Fake the call to ReportStatus here - this is usually called in
94     // platform-specific code which is not exposed here.
95     ReportStatus(SHOWING_WEB_APP_BANNER);
96     RecordDidShowBanner();
97 
98     ASSERT_FALSE(banner_shown_.get());
99     banner_shown_.reset(new bool(true));
100     install_source_.reset(new WebappInstallSource(install_source));
101     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
102                                                   std::move(on_done_));
103   }
104 
UpdateState(AppBannerManager::State state)105   void UpdateState(AppBannerManager::State state) override {
106     AppBannerManager::UpdateState(state);
107 
108     if (state == AppBannerManager::State::PENDING_ENGAGEMENT ||
109         state == AppBannerManager::State::PENDING_PROMPT) {
110       base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
111                                                     std::move(on_done_));
112     }
113   }
114 
OnBannerPromptReply(mojo::Remote<blink::mojom::AppBannerController> controller,blink::mojom::AppBannerPromptReply reply)115   void OnBannerPromptReply(
116       mojo::Remote<blink::mojom::AppBannerController> controller,
117       blink::mojom::AppBannerPromptReply reply) override {
118     AppBannerManager::OnBannerPromptReply(std::move(controller), reply);
119     if (on_banner_prompt_reply_) {
120       base::ThreadTaskRunnerHandle::Get()->PostTask(
121           FROM_HERE, std::move(on_banner_prompt_reply_));
122     }
123   }
124 
GetWeakPtr()125   base::WeakPtr<AppBannerManager> GetWeakPtr() override {
126     return weak_factory_.GetWeakPtr();
127   }
128 
InvalidateWeakPtrs()129   void InvalidateWeakPtrs() override { weak_factory_.InvalidateWeakPtrs(); }
130 
IsSupportedNonWebAppPlatform(const base::string16 & platform) const131   bool IsSupportedNonWebAppPlatform(
132       const base::string16& platform) const override {
133     return base::EqualsASCII(platform, "chrome_web_store");
134   }
135 
IsRelatedNonWebAppInstalled(const blink::Manifest::RelatedApplication & related_app) const136   bool IsRelatedNonWebAppInstalled(
137       const blink::Manifest::RelatedApplication& related_app) const override {
138     // Corresponds to the id listed in manifest_listing_related_chrome_app.json.
139     return base::EqualsASCII(related_app.platform.value_or(base::string16()),
140                              "chrome_web_store") &&
141            base::EqualsASCII(related_app.id.value_or(base::string16()),
142                              "installed-extension-id");
143   }
144 
145  private:
146   base::OnceClosure on_done_;
147 
148   // If non-null, |on_banner_prompt_reply_| will be invoked from
149   // OnBannerPromptReply.
150   base::OnceClosure on_banner_prompt_reply_;
151 
152   std::unique_ptr<bool> banner_shown_;
153   std::unique_ptr<WebappInstallSource> install_source_;
154 
155   base::WeakPtrFactory<AppBannerManagerTest> weak_factory_{this};
156 
157   DISALLOW_COPY_AND_ASSIGN(AppBannerManagerTest);
158 };
159 
160 class AppBannerManagerBrowserTest : public AppBannerManagerBrowserTestBase {
161  public:
162   AppBannerManagerBrowserTest() = default;
163 
SetUpOnMainThread()164   void SetUpOnMainThread() override {
165     AppBannerSettingsHelper::SetTotalEngagementToTrigger(10);
166     SiteEngagementScore::SetParamValuesForTesting();
167 
168     // Make sure app banners are disabled in the browser, otherwise they will
169     // interfere with the test.
170     AppBannerManagerDesktop::DisableTriggeringForTesting();
171     AppBannerManagerBrowserTestBase::SetUpOnMainThread();
172   }
173 
174  protected:
CreateAppBannerManager(Browser * browser)175   std::unique_ptr<AppBannerManagerTest> CreateAppBannerManager(
176       Browser* browser) {
177     content::WebContents* web_contents =
178         browser->tab_strip_model()->GetActiveWebContents();
179     return std::make_unique<AppBannerManagerTest>(web_contents);
180   }
181 
RunBannerTest(Browser * browser,AppBannerManagerTest * manager,const GURL & url,base::Optional<InstallableStatusCode> expected_code_for_histogram)182   void RunBannerTest(
183       Browser* browser,
184       AppBannerManagerTest* manager,
185       const GURL& url,
186       base::Optional<InstallableStatusCode> expected_code_for_histogram) {
187     base::HistogramTester histograms;
188 
189     SiteEngagementService* service =
190         SiteEngagementService::Get(browser->profile());
191     service->ResetBaseScoreForURL(url, 10);
192 
193     // Spin the run loop and wait for the manager to finish.
194     base::RunLoop run_loop;
195     manager->clear_will_show();
196     manager->PrepareDone(run_loop.QuitClosure());
197     NavigateParams nav_params(browser, url, ui::PAGE_TRANSITION_LINK);
198     ui_test_utils::NavigateToURL(&nav_params);
199     run_loop.Run();
200 
201     EXPECT_EQ(expected_code_for_histogram.value_or(MAX_ERROR_CODE) ==
202                   SHOWING_WEB_APP_BANNER,
203               manager->banner_shown());
204     EXPECT_EQ(WebappInstallSource::COUNT, manager->install_source());
205 
206     // Generally the manager will be in the complete state, however some test
207     // cases navigate the page, causing the state to go back to INACTIVE.
208     EXPECT_TRUE(manager->state() == State::COMPLETE ||
209                 manager->state() == State::PENDING_PROMPT ||
210                 manager->state() == State::INACTIVE);
211 
212     // If in incognito, ensure that nothing is recorded.
213     histograms.ExpectTotalCount(banners::kMinutesHistogram, 0);
214     if (browser->profile()->IsOffTheRecord() || !expected_code_for_histogram) {
215       histograms.ExpectTotalCount(banners::kInstallableStatusCodeHistogram, 0);
216     } else {
217       histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
218                                     *expected_code_for_histogram, 1);
219     }
220   }
221 
TriggerBannerFlowWithNavigation(Browser * browser,AppBannerManagerTest * manager,const GURL & url,bool expected_will_show,State expected_state)222   void TriggerBannerFlowWithNavigation(Browser* browser,
223                                        AppBannerManagerTest* manager,
224                                        const GURL& url,
225                                        bool expected_will_show,
226                                        State expected_state) {
227     // Use NavigateToURLWithDisposition as it isn't overloaded, so can be used
228     // with Bind.
229     TriggerBannerFlow(
230         browser, manager,
231         base::BindOnce(
232             base::IgnoreResult(&ui_test_utils::NavigateToURLWithDisposition),
233             browser, url, WindowOpenDisposition::CURRENT_TAB,
234             ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP),
235         expected_will_show, expected_state);
236   }
237 
TriggerBannerFlow(Browser * browser,AppBannerManagerTest * manager,base::OnceClosure trigger_task,bool expected_will_show,base::Optional<State> expected_state)238   void TriggerBannerFlow(Browser* browser,
239                          AppBannerManagerTest* manager,
240                          base::OnceClosure trigger_task,
241                          bool expected_will_show,
242                          base::Optional<State> expected_state) {
243     base::RunLoop run_loop;
244     manager->clear_will_show();
245     manager->PrepareDone(run_loop.QuitClosure());
246     std::move(trigger_task).Run();
247     run_loop.Run();
248 
249     EXPECT_EQ(expected_will_show, manager->banner_shown());
250     if (expected_state)
251       EXPECT_EQ(expected_state, manager->state());
252   }
253 
254  private:
255   DISALLOW_COPY_AND_ASSIGN(AppBannerManagerBrowserTest);
256 };
257 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerNoTypeInManifest)258 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,
259                        WebAppBannerNoTypeInManifest) {
260   std::unique_ptr<AppBannerManagerTest> manager(
261       CreateAppBannerManager(browser()));
262   RunBannerTest(browser(), manager.get(),
263                 GetBannerURLWithManifest("/banners/manifest_no_type.json"),
264                 base::nullopt);
265 }
266 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerNoTypeInManifestCapsExtension)267 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,
268                        WebAppBannerNoTypeInManifestCapsExtension) {
269   std::unique_ptr<AppBannerManagerTest> manager(
270       CreateAppBannerManager(browser()));
271   RunBannerTest(browser(), manager.get(),
272                 GetBannerURLWithManifest("/banners/manifest_no_type_caps.json"),
273                 base::nullopt);
274 }
275 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerSvgIcon)276 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, WebAppBannerSvgIcon) {
277   std::unique_ptr<AppBannerManagerTest> manager(
278       CreateAppBannerManager(browser()));
279   RunBannerTest(browser(), manager.get(),
280                 GetBannerURLWithManifest("/banners/manifest_svg_icon.json"),
281                 base::nullopt);
282 }
283 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerWebPIcon)284 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, WebAppBannerWebPIcon) {
285   std::unique_ptr<AppBannerManagerTest> manager(
286       CreateAppBannerManager(browser()));
287   RunBannerTest(browser(), manager.get(),
288                 GetBannerURLWithManifest("/banners/manifest_webp_icon.json"),
289                 base::nullopt);
290 }
291 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,DelayedManifestTriggersPipeline)292 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,
293                        DelayedManifestTriggersPipeline) {
294   std::unique_ptr<AppBannerManagerTest> manager(
295       CreateAppBannerManager(browser()));
296   RunBannerTest(
297       browser(), manager.get(),
298       embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"),
299       NO_MANIFEST);
300 
301   // Dynamically add the manifest.
302   base::HistogramTester histograms;
303   TriggerBannerFlow(browser(), manager.get(), base::BindLambdaForTesting([&]() {
304                       EXPECT_TRUE(content::ExecJs(
305                           browser()->tab_strip_model()->GetActiveWebContents(),
306                           "addManifestLinkTag()"));
307                     }),
308                     false, AppBannerManager::State::PENDING_PROMPT);
309   histograms.ExpectTotalCount(banners::kInstallableStatusCodeHistogram, 0);
310 }
311 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,RemovingManifestStopsPipeline)312 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,
313                        RemovingManifestStopsPipeline) {
314   std::unique_ptr<AppBannerManagerTest> manager(
315       CreateAppBannerManager(browser()));
316   RunBannerTest(
317       browser(), manager.get(),
318       embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
319       base::nullopt);
320   EXPECT_EQ(manager->state(), AppBannerManager::State::PENDING_PROMPT);
321 
322   // Dynamically remove the manifest.
323   base::HistogramTester histograms;
324   TriggerBannerFlow(browser(), manager.get(), base::BindLambdaForTesting([&]() {
325                       EXPECT_TRUE(content::ExecJs(
326                           browser()->tab_strip_model()->GetActiveWebContents(),
327                           "removeAllManifestTags()"));
328                     }),
329                     false, AppBannerManager::State::COMPLETE);
330   histograms.ExpectTotalCount(banners::kInstallableStatusCodeHistogram, 1);
331   histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
332                                 RENDERER_CANCELLED, 1);
333 }
334 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,ManifestChangeTriggersPipeline)335 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,
336                        ManifestChangeTriggersPipeline) {
337   std::unique_ptr<AppBannerManagerTest> manager(
338       CreateAppBannerManager(browser()));
339 
340   // Cause the manifest test page to reach the PENDING_PROMPT stage of the
341   // app banner pipeline.
342   RunBannerTest(
343       browser(), manager.get(),
344       embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
345       base::nullopt);
346   EXPECT_EQ(manager->state(), AppBannerManager::State::PENDING_PROMPT);
347 
348   // Dynamically change the manifest, which results in a
349   // Stop(RENDERER_CANCELLED), and a restart of the pipeline.
350   {
351     base::HistogramTester histograms;
352     // Note - The state of the appbannermanager here will be racy, so don't
353     // check for that.
354     TriggerBannerFlow(
355         browser(), manager.get(), base::BindLambdaForTesting([&]() {
356           EXPECT_TRUE(content::ExecJs(
357               browser()->tab_strip_model()->GetActiveWebContents(),
358               "addManifestLinkTag('/banners/manifest_one_icon.json')"));
359         }),
360         false, base::nullopt);
361     histograms.ExpectTotalCount(banners::kInstallableStatusCodeHistogram, 1);
362     histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
363                                   RENDERER_CANCELLED, 1);
364   }
365   // The pipeline should either have completed, or it is scheduled in the
366   // background. Wait for the next prompt request if so.
367   if (manager->state() != AppBannerManager::State::PENDING_PROMPT) {
368     base::HistogramTester histograms;
369     base::RunLoop run_loop;
370     manager->PrepareDone(run_loop.QuitClosure());
371     run_loop.Run();
372     histograms.ExpectTotalCount(banners::kInstallableStatusCodeHistogram, 0);
373   }
374   EXPECT_EQ(manager->state(), AppBannerManager::State::PENDING_PROMPT);
375 }
376 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,NoManifest)377 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, NoManifest) {
378   std::unique_ptr<AppBannerManagerTest> manager(
379       CreateAppBannerManager(browser()));
380   RunBannerTest(
381       browser(), manager.get(),
382       embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"),
383       NO_MANIFEST);
384 }
385 
386 // TODO(crbug.com/1146526): Test is flaky on Mac.
387 #if defined(OS_MAC)
388 #define MAYBE_MissingManifest DISABLED_MissingManifest
389 #else
390 #define MAYBE_MissingManifest MissingManifest
391 #endif
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,MAYBE_MissingManifest)392 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, MAYBE_MissingManifest) {
393   std::unique_ptr<AppBannerManagerTest> manager(
394       CreateAppBannerManager(browser()));
395   RunBannerTest(browser(), manager.get(),
396                 GetBannerURLWithManifest("/banners/manifest_missing.json"),
397                 MANIFEST_EMPTY);
398 }
399 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerInIFrame)400 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, WebAppBannerInIFrame) {
401   std::unique_ptr<AppBannerManagerTest> manager(
402       CreateAppBannerManager(browser()));
403   RunBannerTest(
404       browser(), manager.get(),
405       embedded_test_server()->GetURL("/banners/iframe_test_page.html"),
406       NO_MANIFEST);
407 }
408 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,DoesNotShowInIncognito)409 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, DoesNotShowInIncognito) {
410   Browser* incognito_browser =
411       OpenURLOffTheRecord(browser()->profile(), GURL("about:blank"));
412   std::unique_ptr<AppBannerManagerTest> manager(
413       CreateAppBannerManager(incognito_browser));
414   RunBannerTest(incognito_browser, manager.get(), GetBannerURL(), IN_INCOGNITO);
415 }
416 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerInsufficientEngagement)417 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,
418                        WebAppBannerInsufficientEngagement) {
419   std::unique_ptr<AppBannerManagerTest> manager(
420       CreateAppBannerManager(browser()));
421 
422   base::HistogramTester histograms;
423   GURL test_url = GetBannerURL();
424 
425   // First run through: expect the manager to end up stopped in the pending
426   // state, without showing a banner.
427   TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
428                                   false /* expected_will_show */,
429                                   State::PENDING_ENGAGEMENT);
430 
431   // Navigate and expect Stop() to be called.
432   TriggerBannerFlowWithNavigation(browser(), manager.get(), GURL("about:blank"),
433                                   false /* expected_will_show */,
434                                   State::INACTIVE);
435 
436   histograms.ExpectTotalCount(banners::kMinutesHistogram, 0);
437   histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
438                                 INSUFFICIENT_ENGAGEMENT, 1);
439 }
440 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerNotCreated)441 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, WebAppBannerNotCreated) {
442   std::unique_ptr<AppBannerManagerTest> manager(
443       CreateAppBannerManager(browser()));
444   base::HistogramTester histograms;
445 
446   SiteEngagementService* service =
447       SiteEngagementService::Get(browser()->profile());
448   GURL test_url = GetBannerURL();
449   service->ResetBaseScoreForURL(test_url, 10);
450 
451   // Navigate and expect the manager to end up waiting for prompt to be called.
452   TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
453                                   false /* expected_will_show */,
454                                   State::PENDING_PROMPT);
455 
456   // Navigate and expect Stop() to be called.
457   TriggerBannerFlowWithNavigation(browser(), manager.get(), GURL("about:blank"),
458                                   false /* expected_will_show */,
459                                   State::INACTIVE);
460 
461   histograms.ExpectTotalCount(banners::kMinutesHistogram, 0);
462   histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
463                                 RENDERER_CANCELLED, 1);
464 }
465 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerCancelled)466 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, WebAppBannerCancelled) {
467   std::unique_ptr<AppBannerManagerTest> manager(
468       CreateAppBannerManager(browser()));
469   base::HistogramTester histograms;
470 
471   SiteEngagementService* service =
472       SiteEngagementService::Get(browser()->profile());
473 
474   // Explicitly call preventDefault(), but don't call prompt().
475   GURL test_url = GetBannerURLWithAction("cancel_prompt");
476   service->ResetBaseScoreForURL(test_url, 10);
477 
478   // Navigate and expect the manager to end up waiting for prompt() to be
479   // called.
480   TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
481                                   false /* expected_will_show */,
482                                   State::PENDING_PROMPT);
483 
484   // Navigate to about:blank and expect Stop() to be called.
485   TriggerBannerFlowWithNavigation(browser(), manager.get(), GURL("about:blank"),
486                                   false /* expected_will_show */,
487                                   State::INACTIVE);
488 
489   histograms.ExpectTotalCount(banners::kMinutesHistogram, 0);
490   histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
491                                 RENDERER_CANCELLED, 1);
492 }
493 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerPromptWithGesture)494 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,
495                        WebAppBannerPromptWithGesture) {
496   std::unique_ptr<AppBannerManagerTest> manager(
497       CreateAppBannerManager(browser()));
498   base::HistogramTester histograms;
499 
500   SiteEngagementService* service =
501       SiteEngagementService::Get(browser()->profile());
502   GURL test_url = GetBannerURLWithAction("stash_event");
503   service->ResetBaseScoreForURL(test_url, 10);
504 
505   // Navigate to page and get the pipeline started.
506   TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
507                                   false /* expected_will_show */,
508                                   State::PENDING_PROMPT);
509 
510   // Now let the page call prompt with a gesture. The banner should be shown.
511   TriggerBannerFlow(
512       browser(), manager.get(),
513       base::BindOnce(&AppBannerManagerBrowserTest::ExecuteScript, browser(),
514                      "callStashedPrompt();", true /* with_gesture */),
515       true /* expected_will_show */, State::COMPLETE);
516 
517   histograms.ExpectTotalCount(banners::kMinutesHistogram, 1);
518   histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
519                                 SHOWING_WEB_APP_BANNER, 1);
520 }
521 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerNeedsEngagement)522 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,
523                        WebAppBannerNeedsEngagement) {
524   AppBannerSettingsHelper::SetTotalEngagementToTrigger(1);
525   std::unique_ptr<AppBannerManagerTest> manager(
526       CreateAppBannerManager(browser()));
527   base::HistogramTester histograms;
528 
529   SiteEngagementService* service =
530       SiteEngagementService::Get(browser()->profile());
531   GURL test_url = GetBannerURLWithAction("stash_event");
532   service->ResetBaseScoreForURL(test_url, 0);
533 
534   // Navigate and expect the manager to end up waiting for sufficient
535   // engagement.
536   TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
537                                   false /* expected_will_show */,
538                                   State::PENDING_ENGAGEMENT);
539 
540   // Trigger an engagement increase that signals observers and expect the
541   // manager to end up waiting for prompt to be called.
542   TriggerBannerFlow(
543       browser(), manager.get(),
544       base::BindOnce(&SiteEngagementService::HandleNavigation,
545                      base::Unretained(service),
546                      browser()->tab_strip_model()->GetActiveWebContents(),
547                      ui::PageTransition::PAGE_TRANSITION_TYPED),
548       false /* expected_will_show */, State::PENDING_PROMPT);
549 
550   // Trigger prompt() and expect the banner to be shown.
551   TriggerBannerFlow(
552       browser(), manager.get(),
553       base::BindOnce(&AppBannerManagerBrowserTest::ExecuteScript, browser(),
554                      "callStashedPrompt();", true /* with_gesture */),
555       true /* expected_will_show */, State::COMPLETE);
556 
557   histograms.ExpectTotalCount(banners::kMinutesHistogram, 1);
558   histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
559                                 SHOWING_WEB_APP_BANNER, 1);
560 }
561 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,WebAppBannerReprompt)562 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, WebAppBannerReprompt) {
563   std::unique_ptr<AppBannerManagerTest> manager(
564       CreateAppBannerManager(browser()));
565   base::HistogramTester histograms;
566 
567   SiteEngagementService* service =
568       SiteEngagementService::Get(browser()->profile());
569   GURL test_url = GetBannerURLWithAction("stash_event");
570   service->ResetBaseScoreForURL(test_url, 10);
571 
572   // Navigate to page and get the pipeline started.
573   TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
574                                   false /* expected_will_show */,
575                                   State::PENDING_PROMPT);
576 
577   // Call prompt to show the banner.
578   TriggerBannerFlow(
579       browser(), manager.get(),
580       base::BindOnce(&AppBannerManagerBrowserTest::ExecuteScript, browser(),
581                      "callStashedPrompt();", true /* with_gesture */),
582       true /* expected_will_show */, State::COMPLETE);
583 
584   // Dismiss the banner.
585   base::RunLoop run_loop;
586   manager->PrepareDone(base::DoNothing());
587   manager->PrepareBannerPromptReply(run_loop.QuitClosure());
588   manager->SendBannerDismissed();
589   // Wait for OnBannerPromptReply event.
590   run_loop.Run();
591 
592   // Call prompt again to show the banner again.
593   TriggerBannerFlow(
594       browser(), manager.get(),
595       base::BindOnce(&AppBannerManagerBrowserTest::ExecuteScript, browser(),
596                      "callStashedPrompt();", true /* with_gesture */),
597       true /* expected_will_show */, State::COMPLETE);
598 
599   histograms.ExpectTotalCount(banners::kMinutesHistogram, 1);
600   histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
601                                 SHOWING_WEB_APP_BANNER, 1);
602 }
603 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,PreferRelatedAppUnknown)604 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, PreferRelatedAppUnknown) {
605   std::unique_ptr<AppBannerManagerTest> manager(
606       CreateAppBannerManager(browser()));
607 
608   GURL test_url = embedded_test_server()->GetURL(
609       "/banners/manifest_test_page.html?manifest="
610       "manifest_prefer_related_apps_unknown.json");
611   TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
612                                   false /* expected_will_show */,
613                                   State::PENDING_ENGAGEMENT);
614 }
615 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,PreferRelatedChromeApp)616 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest, PreferRelatedChromeApp) {
617   std::unique_ptr<AppBannerManagerTest> manager(
618       CreateAppBannerManager(browser()));
619   base::HistogramTester histograms;
620 
621   GURL test_url = embedded_test_server()->GetURL(
622       "/banners/manifest_test_page.html?manifest="
623       "manifest_prefer_related_chrome_app.json");
624   TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
625                                   false /* expected_will_show */,
626                                   State::COMPLETE);
627   histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
628                                 PREFER_RELATED_APPLICATIONS, 1);
629 }
630 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,ListedRelatedChromeAppInstalled)631 IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTest,
632                        ListedRelatedChromeAppInstalled) {
633   std::unique_ptr<AppBannerManagerTest> manager(
634       CreateAppBannerManager(browser()));
635   base::HistogramTester histograms;
636 
637   GURL test_url = embedded_test_server()->GetURL(
638       "/banners/manifest_test_page.html?manifest="
639       "manifest_listing_related_chrome_app.json");
640   TriggerBannerFlowWithNavigation(browser(), manager.get(), test_url,
641                                   false /* expected_will_show */,
642                                   State::COMPLETE);
643   histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
644                                 PREFER_RELATED_APPLICATIONS, 1);
645 }
646 
647 namespace {
648 class FailingInstallableManager : public InstallableManager {
649  public:
FailingInstallableManager(content::WebContents * web_contents)650   explicit FailingInstallableManager(content::WebContents* web_contents)
651       : InstallableManager(web_contents) {}
652 
FailNext(std::unique_ptr<InstallableData> installable_data)653   void FailNext(std::unique_ptr<InstallableData> installable_data) {
654     failure_data_ = std::move(installable_data);
655   }
656 
GetData(const InstallableParams & params,InstallableCallback callback)657   void GetData(const InstallableParams& params,
658                InstallableCallback callback) override {
659     if (failure_data_) {
660       auto temp_data = std::move(failure_data_);
661       std::move(callback).Run(*temp_data);
662       return;
663     }
664     InstallableManager::GetData(params, std::move(callback));
665   }
666 
667  private:
668   std::unique_ptr<InstallableData> failure_data_;
669 };
670 
671 class AppBannerManagerBrowserTestWithFailableInstallableManager
672     : public AppBannerManagerBrowserTest {
673  public:
674   AppBannerManagerBrowserTestWithFailableInstallableManager() = default;
675   ~AppBannerManagerBrowserTestWithFailableInstallableManager() override =
676       default;
677 
SetUpOnMainThread()678   void SetUpOnMainThread() override {
679     // Manually inject the FailingInstallableManager as a "InstallableManager"
680     // WebContentsUserData. We can't directly call ::CreateForWebContents due to
681     // typing issues since FailingInstallableManager doesn't directly inherit
682     // from WebContentsUserData.
683     browser()->tab_strip_model()->GetActiveWebContents()->SetUserData(
684         FailingInstallableManager::UserDataKey(),
685         base::WrapUnique(new FailingInstallableManager(
686             browser()->tab_strip_model()->GetActiveWebContents())));
687     installable_manager_ = static_cast<FailingInstallableManager*>(
688         browser()->tab_strip_model()->GetActiveWebContents()->GetUserData(
689             FailingInstallableManager::UserDataKey()));
690 
691     AppBannerManagerBrowserTest::SetUpOnMainThread();
692   }
693 
694  protected:
695   FailingInstallableManager* installable_manager_ = nullptr;
696 };
697 
IN_PROC_BROWSER_TEST_F(AppBannerManagerBrowserTestWithFailableInstallableManager,AppBannerManagerRetriesPipeline)698 IN_PROC_BROWSER_TEST_F(
699     AppBannerManagerBrowserTestWithFailableInstallableManager,
700     AppBannerManagerRetriesPipeline) {
701   std::unique_ptr<AppBannerManagerTest> manager(
702       CreateAppBannerManager(browser()));
703 
704   SiteEngagementService* service =
705       SiteEngagementService::Get(browser()->profile());
706   GURL test_url = GetBannerURLWithAction("stash_event");
707   service->ResetBaseScoreForURL(test_url, 10);
708 
709   installable_manager_->FailNext(base::WrapUnique(
710       new InstallableData({MANIFEST_URL_CHANGED}, GURL(), nullptr, GURL(),
711                           nullptr, false, GURL(), nullptr, false, false)));
712 
713   // The page should record one failure of MANIFEST_URL_CHANGED, but it should
714   // still successfully get to the PENDING_PROMPT state of the pipeline, as it
715   // should retry the call to GetData on the InstallableManager.
716   RunBannerTest(browser(), manager.get(), test_url, MANIFEST_URL_CHANGED);
717   EXPECT_EQ(manager->state(), AppBannerManager::State::PENDING_PROMPT);
718 
719   {
720     base::HistogramTester histograms;
721     // Now let the page call prompt with a gesture. The banner should be shown.
722     TriggerBannerFlow(
723         browser(), manager.get(),
724         base::BindOnce(&AppBannerManagerBrowserTest::ExecuteScript, browser(),
725                        "callStashedPrompt();", true /* with_gesture */),
726         true /* expected_will_show */, State::COMPLETE);
727 
728     histograms.ExpectTotalCount(banners::kMinutesHistogram, 1);
729     histograms.ExpectUniqueSample(banners::kInstallableStatusCodeHistogram,
730                                   SHOWING_WEB_APP_BANNER, 1);
731   }
732 }
733 
734 }  // namespace
735 }  // namespace banners
736