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