| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/webapps/browser/features.h" |
| #include "components/webapps/browser/installable/installable_manager.h" |
| |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "chrome/browser/banners/app_banner_manager_desktop.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/web_applications/test/service_worker_registration_waiter.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/webapps/browser/installable/installable_logging.h" |
| #include "components/webapps/browser/installable/installable_metrics.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/prerender_test_util.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/manifest/manifest_util.h" |
| #include "third_party/blink/public/mojom/manifest/manifest.mojom.h" |
| |
| namespace webapps { |
| |
| namespace { |
| |
| const char kInsecureOrigin[] = "http://www.google.com"; |
| const char kOtherInsecureOrigin[] = "http://maps.google.com"; |
| const char kUnsafeSecureOriginFlag[] = |
| "unsafely-treat-insecure-origin-as-secure"; |
| |
| InstallableParams GetManifestParams() { |
| InstallableParams params; |
| params.check_eligibility = true; |
| params.wait_for_worker = true; |
| return params; |
| } |
| |
| InstallableParams GetWebAppParams() { |
| InstallableParams params = GetManifestParams(); |
| params.valid_manifest = true; |
| params.has_worker = true; |
| params.valid_primary_icon = true; |
| params.wait_for_worker = true; |
| return params; |
| } |
| |
| InstallableParams GetPrimaryIconParams() { |
| InstallableParams params = GetManifestParams(); |
| params.valid_primary_icon = true; |
| params.wait_for_worker = true; |
| return params; |
| } |
| |
| InstallableParams GetPrimaryIconAndSplashIconParams() { |
| InstallableParams params = GetManifestParams(); |
| params.valid_primary_icon = true; |
| params.valid_splash_icon = true; |
| params.wait_for_worker = true; |
| return params; |
| } |
| |
| InstallableParams GetPrimaryIconPreferMaskableParams() { |
| InstallableParams params = GetManifestParams(); |
| params.valid_primary_icon = true; |
| params.prefer_maskable_icon = true; |
| params.wait_for_worker = true; |
| return params; |
| } |
| |
| InstallableParams GetPreferMaskablePrimaryAndSplashIconParams() { |
| InstallableParams params = GetManifestParams(); |
| params.valid_primary_icon = true; |
| params.prefer_maskable_icon = true; |
| params.valid_splash_icon = true; |
| params.wait_for_worker = true; |
| return params; |
| } |
| |
| } // anonymous namespace |
| |
| // Used only for testing pages with no service workers. This class will dispatch |
| // a RunLoop::QuitClosure when it begins waiting for a service worker to be |
| // registered. |
| class LazyWorkerInstallableManager : public InstallableManager { |
| public: |
| LazyWorkerInstallableManager(content::WebContents* web_contents, |
| base::OnceClosure quit_closure) |
| : InstallableManager(web_contents), |
| quit_closure_(std::move(quit_closure)) {} |
| ~LazyWorkerInstallableManager() override = default; |
| |
| protected: |
| void OnWaitingForServiceWorker() override { std::move(quit_closure_).Run(); } |
| |
| private: |
| base::OnceClosure quit_closure_; |
| }; |
| |
| // Used only for testing pages where the manifest URL is changed. This class |
| // will dispatch a RunLoop::QuitClosure when internal state is reset. |
| class ResetDataInstallableManager : public InstallableManager { |
| public: |
| explicit ResetDataInstallableManager(content::WebContents* web_contents) |
| : InstallableManager(web_contents) {} |
| ~ResetDataInstallableManager() override {} |
| |
| void SetQuitClosure(base::RepeatingClosure quit_closure) { |
| quit_closure_ = quit_closure; |
| } |
| |
| bool GetOnResetData() { return is_reset_data_; } |
| void ClearOnResetData() { is_reset_data_ = false; } |
| |
| protected: |
| void OnResetData() override { |
| is_reset_data_ = true; |
| if (quit_closure_) |
| quit_closure_.Run(); |
| } |
| |
| private: |
| base::RepeatingClosure quit_closure_; |
| bool is_reset_data_ = false; |
| }; |
| |
| class CallbackTester { |
| public: |
| explicit CallbackTester(base::RepeatingClosure quit_closure) |
| : quit_closure_(quit_closure) {} |
| |
| void OnDidFinishInstallableCheck(const InstallableData& data) { |
| errors_ = data.errors; |
| manifest_url_ = data.manifest_url; |
| manifest_ = data.manifest.Clone(); |
| primary_icon_url_ = data.primary_icon_url; |
| if (data.primary_icon) |
| primary_icon_ = std::make_unique<SkBitmap>(*data.primary_icon); |
| has_maskable_primary_icon_ = data.has_maskable_primary_icon; |
| splash_icon_url_ = data.splash_icon_url; |
| if (data.splash_icon) |
| splash_icon_ = std::make_unique<SkBitmap>(*data.splash_icon); |
| has_maskable_splash_icon_ = data.has_maskable_splash_icon; |
| valid_manifest_ = data.valid_manifest; |
| worker_check_passed_ = data.worker_check_passed; |
| screenshots_ = data.screenshots; |
| base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, quit_closure_); |
| } |
| |
| const std::vector<InstallableStatusCode>& errors() const { return errors_; } |
| const GURL& manifest_url() const { return manifest_url_; } |
| const blink::mojom::Manifest& manifest() const { |
| DCHECK(manifest_); |
| return *manifest_; |
| } |
| const GURL& primary_icon_url() const { return primary_icon_url_; } |
| const SkBitmap* primary_icon() const { return primary_icon_.get(); } |
| bool has_maskable_primary_icon() const { return has_maskable_primary_icon_; } |
| const GURL& splash_icon_url() const { return splash_icon_url_; } |
| bool has_maskable_splash_icon() const { return has_maskable_splash_icon_; } |
| const SkBitmap* splash_icon() const { return splash_icon_.get(); } |
| const std::vector<SkBitmap>& screenshots() const { return screenshots_; } |
| bool valid_manifest() const { return valid_manifest_; } |
| bool worker_check_passed() const { return worker_check_passed_; } |
| |
| private: |
| base::RepeatingClosure quit_closure_; |
| std::vector<InstallableStatusCode> errors_; |
| GURL manifest_url_; |
| blink::mojom::ManifestPtr manifest_ = blink::mojom::Manifest::New(); |
| GURL primary_icon_url_; |
| std::unique_ptr<SkBitmap> primary_icon_; |
| bool has_maskable_primary_icon_; |
| GURL splash_icon_url_; |
| std::unique_ptr<SkBitmap> splash_icon_; |
| std::vector<SkBitmap> screenshots_; |
| bool has_maskable_splash_icon_; |
| bool valid_manifest_; |
| bool worker_check_passed_; |
| }; |
| |
| class NestedCallbackTester { |
| public: |
| NestedCallbackTester(InstallableManager* manager, |
| const InstallableParams& params, |
| base::OnceClosure quit_closure) |
| : manager_(manager), |
| params_(params), |
| quit_closure_(std::move(quit_closure)) {} |
| |
| void Run() { |
| manager_->GetData( |
| params_, base::BindOnce(&NestedCallbackTester::OnDidFinishFirstCheck, |
| base::Unretained(this))); |
| } |
| |
| void OnDidFinishFirstCheck(const InstallableData& data) { |
| errors_ = data.errors; |
| manifest_url_ = data.manifest_url; |
| manifest_ = data.manifest.Clone(); |
| primary_icon_url_ = data.primary_icon_url; |
| if (data.primary_icon) |
| primary_icon_ = std::make_unique<SkBitmap>(*data.primary_icon); |
| valid_manifest_ = data.valid_manifest; |
| worker_check_passed_ = data.worker_check_passed; |
| |
| manager_->GetData( |
| params_, base::BindOnce(&NestedCallbackTester::OnDidFinishSecondCheck, |
| base::Unretained(this))); |
| } |
| |
| void OnDidFinishSecondCheck(const InstallableData& data) { |
| EXPECT_EQ(errors_, data.errors); |
| EXPECT_EQ(manifest_url_, data.manifest_url); |
| EXPECT_EQ(primary_icon_url_, data.primary_icon_url); |
| EXPECT_EQ(primary_icon_.get(), data.primary_icon); |
| EXPECT_EQ(valid_manifest_, data.valid_manifest); |
| EXPECT_EQ(worker_check_passed_, data.worker_check_passed); |
| EXPECT_EQ(blink::IsEmptyManifest(*manifest_), |
| blink::IsEmptyManifest(data.manifest)); |
| EXPECT_EQ(manifest_->start_url, data.manifest.start_url); |
| EXPECT_EQ(manifest_->display, data.manifest.display); |
| EXPECT_EQ(manifest_->name, data.manifest.name); |
| EXPECT_EQ(manifest_->short_name, data.manifest.short_name); |
| EXPECT_EQ(manifest_->display_override, data.manifest.display_override); |
| |
| std::move(quit_closure_).Run(); |
| } |
| |
| private: |
| raw_ptr<InstallableManager> manager_; |
| InstallableParams params_; |
| base::OnceClosure quit_closure_; |
| std::vector<InstallableStatusCode> errors_; |
| GURL manifest_url_; |
| blink::mojom::ManifestPtr manifest_; |
| GURL primary_icon_url_; |
| std::unique_ptr<SkBitmap> primary_icon_; |
| bool valid_manifest_; |
| bool worker_check_passed_; |
| }; |
| |
| class InstallableManagerBrowserTest : public InProcessBrowserTest { |
| public: |
| InstallableManagerBrowserTest() { |
| scoped_feature_list_.InitAndEnableFeature( |
| webapps::features::kDesktopPWAsDetailedInstallDialog); |
| } |
| |
| void SetUpOnMainThread() override { |
| embedded_test_server()->ServeFilesFromSourceDirectory( |
| "chrome/test/data/banners"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Make sure app banners are disabled in the browser so they do not |
| // interfere with the test. |
| AppBannerManagerDesktop::DisableTriggeringForTesting(); |
| } |
| |
| // Returns a test server URL to a page controlled by a service worker with |
| // |manifest_url| injected as the manifest tag. |
| std::string GetURLOfPageWithServiceWorkerAndManifest( |
| const std::string& manifest_url) { |
| return "/banners/manifest_test_page.html?manifest=" + |
| embedded_test_server()->GetURL(manifest_url).spec(); |
| } |
| |
| void NavigateAndMaybeWaitForWorker(Browser* browser, |
| const std::string& path, |
| bool wait_for_worker = true) { |
| GURL test_url = embedded_test_server()->GetURL(path); |
| if (wait_for_worker) { |
| web_app::ServiceWorkerRegistrationWaiter registration_waiter( |
| browser->profile(), test_url); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, test_url)); |
| registration_waiter.AwaitRegistration(); |
| } else { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, test_url)); |
| } |
| } |
| |
| void NavigateAndRunInstallableManager(Browser* browser, |
| CallbackTester* tester, |
| const InstallableParams& params, |
| const std::string& url, |
| bool wait_for_worker = true) { |
| NavigateAndMaybeWaitForWorker(browser, url, wait_for_worker); |
| RunInstallableManager(browser, tester, params); |
| } |
| |
| std::vector<content::InstallabilityError> |
| NavigateAndGetAllInstallabilityErrors(Browser* browser, |
| const std::string& url) { |
| NavigateAndMaybeWaitForWorker(browser, url); |
| return GetAllInstallabilityErrors(browser); |
| } |
| |
| void RunInstallableManager(Browser* browser, |
| CallbackTester* tester, |
| const InstallableParams& params) { |
| InstallableManager* manager = GetManager(browser); |
| manager->GetData( |
| params, base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester))); |
| } |
| |
| InstallableManager* GetManager(Browser* browser) { |
| content::WebContents* web_contents = |
| browser->tab_strip_model()->GetActiveWebContents(); |
| InstallableManager::CreateForWebContents(web_contents); |
| InstallableManager* manager = |
| InstallableManager::FromWebContents(web_contents); |
| CHECK(manager); |
| |
| return manager; |
| } |
| |
| std::vector<content::InstallabilityError> GetAllInstallabilityErrors( |
| Browser* browser) { |
| InstallableManager* manager = GetManager(browser); |
| |
| base::RunLoop run_loop; |
| std::vector<content::InstallabilityError> result; |
| |
| manager->GetAllErrors(base::BindLambdaForTesting( |
| [&](std::vector<content::InstallabilityError> installability_errors) { |
| result = std::move(installability_errors); |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| return result; |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| class InstallableManagerAllowlistOriginBrowserTest |
| : public InstallableManagerBrowserTest { |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitchASCII(kUnsafeSecureOriginFlag, kInsecureOrigin); |
| } |
| }; |
| |
| enum class CheckOfflineCapabilityMode { NONE = 0, WARN_ONLY = 1, ENFORCE = 2 }; |
| |
| class InstallableManagerOfflineCapabilityBrowserTest |
| : public InstallableManagerBrowserTest, |
| public testing::WithParamInterface< |
| std::tuple<CheckOfflineCapabilityMode, bool>> { |
| public: |
| InstallableManagerOfflineCapabilityBrowserTest() |
| : offline_capability_type_(std::get<0>(GetParam())), |
| is_service_worker_offline_supported_(std::get<1>(GetParam())) { |
| switch (offline_capability_type_) { |
| case CheckOfflineCapabilityMode::NONE: |
| scoped_feature_list_.InitAndDisableFeature( |
| blink::features::kCheckOfflineCapability); |
| break; |
| case CheckOfflineCapabilityMode::WARN_ONLY: |
| scoped_feature_list_.InitAndEnableFeatureWithParameters( |
| blink::features::kCheckOfflineCapability, |
| {{"check_mode", "warn_only"}}); |
| break; |
| case CheckOfflineCapabilityMode::ENFORCE: |
| scoped_feature_list_.InitAndEnableFeatureWithParameters( |
| blink::features::kCheckOfflineCapability, |
| {{"check_mode", "enforce"}}); |
| break; |
| } |
| } |
| ~InstallableManagerOfflineCapabilityBrowserTest() override = default; |
| |
| bool IsServiceWorkerOfflineSupported() { |
| return is_service_worker_offline_supported_; |
| } |
| |
| bool IsCheckOfflineCapableFeatureEnabled() { |
| return offline_capability_type_ == CheckOfflineCapabilityMode::WARN_ONLY || |
| offline_capability_type_ == CheckOfflineCapabilityMode::ENFORCE; |
| } |
| |
| // Check the result of `tester` depending on whether or not a service worker |
| // supports offline pages and the CheckOfflineCapability feature. |
| void CheckServiceWorkerForTester(CallbackTester* tester) { |
| if (is_service_worker_offline_supported_ || |
| offline_capability_type_ == CheckOfflineCapabilityMode::NONE) { |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } else if (offline_capability_type_ == |
| CheckOfflineCapabilityMode::WARN_ONLY) { |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{WARN_NOT_OFFLINE_CAPABLE}, |
| tester->errors()); |
| } else { |
| EXPECT_FALSE(tester->worker_check_passed()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE}, |
| tester->errors()); |
| } |
| } |
| |
| // Check the result of `manager` depending on whether or not a service worker |
| // supports offline pages and the CheckOfflineCapability feature. |
| void CheckServiceWorkerForInstallableManager(InstallableManager* manager) { |
| if (is_service_worker_offline_supported_ || |
| offline_capability_type_ == CheckOfflineCapabilityMode::NONE) { |
| EXPECT_TRUE(manager->has_worker()); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error()); |
| } else if (offline_capability_type_ == |
| CheckOfflineCapabilityMode::WARN_ONLY) { |
| EXPECT_TRUE(manager->has_worker()); |
| EXPECT_EQ(WARN_NOT_OFFLINE_CAPABLE, manager->worker_error()); |
| } else { |
| EXPECT_FALSE(manager->has_worker()); |
| EXPECT_EQ(NOT_OFFLINE_CAPABLE, manager->worker_error()); |
| } |
| } |
| |
| // Assume that the name of HTML files using a service worker with an empty |
| // fetch event handler includes "_empty_fetch_handler" suffix. |
| const std::string GetPath(std::string base) { |
| if (is_service_worker_offline_supported_) |
| return base + ".html"; |
| return base + "_empty_fetch_handler.html"; |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| |
| const CheckOfflineCapabilityMode offline_capability_type_; |
| const bool is_service_worker_offline_supported_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| InstallableManagerOfflineCapabilityBrowserTest, |
| testing::Combine(testing::Values(CheckOfflineCapabilityMode::NONE, |
| CheckOfflineCapabilityMode::WARN_ONLY, |
| CheckOfflineCapabilityMode::ENFORCE), |
| testing::Bool())); |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| ManagerBeginsInEmptyState) { |
| // Ensure that the InstallableManager starts off with everything null. |
| InstallableManager* manager = GetManager(browser()); |
| |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_TRUE(manager->manifest_url().is_empty()); |
| EXPECT_TRUE(manager->icons_.empty()); |
| EXPECT_FALSE(manager->valid_manifest()); |
| EXPECT_FALSE(manager->has_worker()); |
| |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error()); |
| EXPECT_TRUE(!manager->task_queue_.HasCurrent()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, ManagerInIncognito) { |
| // Ensure that the InstallableManager returns an error if called in an |
| // incognito profile. |
| Browser* incognito_browser = |
| OpenURLOffTheRecord(browser()->profile(), GURL("about:blank")); |
| InstallableManager* manager = GetManager(incognito_browser); |
| |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndMaybeWaitForWorker(incognito_browser, |
| "/banners/manifest_test_page.html"); |
| |
| RunInstallableManager(incognito_browser, tester.get(), GetManifestParams()); |
| run_loop.Run(); |
| |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_TRUE(manager->manifest_url().is_empty()); |
| EXPECT_TRUE(manager->icons_.empty()); |
| EXPECT_FALSE(manager->valid_manifest()); |
| EXPECT_FALSE(manager->has_worker()); |
| |
| EXPECT_EQ(IN_INCOGNITO, manager->eligibility_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error()); |
| EXPECT_TRUE(!manager->task_queue_.HasCurrent()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckNoManifest) { |
| // Ensure that a page with no manifest returns the appropriate error and with |
| // null fields for everything. |
| base::HistogramTester histograms; |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| // Navigating resets histogram state, so do it before recording a histogram. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"))); |
| RunInstallableManager(browser(), tester.get(), GetManifestParams()); |
| run_loop.Run(); |
| |
| // If there is no manifest, everything should be empty. |
| EXPECT_TRUE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_TRUE(tester->manifest_url().is_empty()); |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MANIFEST}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckManifest404) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), GetManifestParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_missing.json")); |
| run_loop.Run(); |
| |
| // The installable manager should return a manifest URL even if it 404s. |
| // However, the check should fail with a ManifestEmpty error. |
| EXPECT_TRUE(blink::IsEmptyManifest(tester->manifest())); |
| |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{MANIFEST_EMPTY}, |
| tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckManifestOnly) { |
| // Verify that asking for just the manifest works as expected. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), GetManifestParams(), |
| "/banners/manifest_test_page.html"); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckInstallableParamsDefaultConstructor) { |
| // Verify that using InstallableParams' default constructor is equivalent to |
| // just asking for the manifest alone. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params; |
| NavigateAndRunInstallableManager(browser(), tester.get(), params, |
| "/banners/manifest_test_page.html"); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckManifestWithIconThatIsTooSmall) { |
| // This page has a manifest with only a 48x48 icon which is too small to be |
| // installable. Asking for a primary icon should fail with NO_ACCEPTABLE_ICON. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetPrimaryIconParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_too_small_icon.json")); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON}, |
| tester->errors()); |
| } |
| |
| // Ask for everything except splash icon. This should fail with |
| // NO_ACCEPTABLE_ICON - the primary icon fetch has already failed, so that |
| // cached error stops the installable check from being performed. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| RunInstallableManager(browser(), tester.get(), GetWebAppParams()); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_FALSE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON}, |
| tester->errors()); |
| } |
| |
| // Ask for a splash icon. This should fail to get a splash icon but not record |
| // an error. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetPrimaryIconAndSplashIconParams(); |
| params.valid_primary_icon = false; |
| RunInstallableManager(browser(), tester.get(), params); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckManifestWithOnlyRelatedApplications) { |
| // This page has a manifest with only related applications specified. Asking |
| // for just the manifest should succeed. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), |
| GetManifestParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/play_app_manifest.json")); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_TRUE(tester->manifest().prefer_related_applications); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| // Ask for a primary icon (but don't navigate). This should fail with |
| // NO_ACCEPTABLE_ICON. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| RunInstallableManager(browser(), tester.get(), GetPrimaryIconParams()); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_TRUE(tester->manifest().prefer_related_applications); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON}, |
| tester->errors()); |
| } |
| |
| // Ask for everything except splash icon. This should fail with |
| // NO_ACCEPTABLE_ICON - the primary icon fetch has already failed, so that |
| // cached error stops the installable check from being performed. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| auto params = GetWebAppParams(); |
| params.valid_manifest = false; |
| RunInstallableManager(browser(), tester.get(), params); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_TRUE(tester->manifest().prefer_related_applications); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_FALSE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON}, |
| tester->errors()); |
| } |
| |
| // Do not ask for primary icon. This should fail with several validity |
| // errors. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetWebAppParams(); |
| params.valid_primary_icon = false; |
| RunInstallableManager(browser(), tester.get(), params); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_TRUE(tester->manifest().prefer_related_applications); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_FALSE(tester->worker_check_passed()); |
| EXPECT_EQ( |
| std::vector<InstallableStatusCode>( |
| {START_URL_NOT_VALID, MANIFEST_MISSING_NAME_OR_SHORT_NAME, |
| MANIFEST_DISPLAY_NOT_SUPPORTED, MANIFEST_MISSING_SUITABLE_ICON}), |
| tester->errors()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckManifestAndIcon) { |
| // Add to homescreen checks for manifest + primary icon. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), |
| GetPrimaryIconParams(), |
| "/banners/manifest_test_page.html"); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| // Add to homescreen checks for manifest + primary icon + splash icon. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| RunInstallableManager(browser(), tester.get(), |
| GetPrimaryIconAndSplashIconParams()); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_FALSE(tester->splash_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| // Navigate to a page with a good maskable icon and a bad any |
| // icon. The maskable icon is fetched for both primary and splash icon. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetPreferMaskablePrimaryAndSplashIconParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_bad_non_maskable_icon.json")); |
| run_loop.Run(); |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->has_maskable_primary_icon()); |
| EXPECT_FALSE(tester->splash_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->splash_icon()); |
| EXPECT_TRUE(tester->has_maskable_splash_icon()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest, |
| CheckWebapp) { |
| // Request everything except splash icon. |
| { |
| base::HistogramTester histograms; |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| // Navigating resets histogram state, so do it before recording a histogram. |
| NavigateAndMaybeWaitForWorker(browser(), |
| GetPath("/banners/manifest_test_page")); |
| RunInstallableManager(browser(), tester.get(), GetWebAppParams()); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| CheckServiceWorkerForTester(tester.get()); |
| |
| // Verify that the returned state matches manager internal state. |
| InstallableManager* manager = GetManager(browser()); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_FALSE(manager->manifest_url().is_empty()); |
| EXPECT_EQ(1u, manager->icons_.size()); |
| EXPECT_FALSE(manager->valid_manifest()); |
| EXPECT_FALSE(( |
| manager->icon_url(InstallableManager::IconUsage::kPrimary).is_empty())); |
| EXPECT_NE(nullptr, |
| (manager->icon(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, |
| (manager->icon_error(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_TRUE(!manager->task_queue_.HasCurrent()); |
| CheckServiceWorkerForInstallableManager(manager); |
| } |
| |
| // Request everything except splash icon again without navigating away. This |
| // should work fine. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| auto params = GetWebAppParams(); |
| // Make sure valid_manifest check is run. |
| params.is_debug_mode = true; |
| RunInstallableManager(browser(), tester.get(), params); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| CheckServiceWorkerForTester(tester.get()); |
| |
| // Verify that the returned state matches manager internal state. |
| InstallableManager* manager = GetManager(browser()); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_FALSE(manager->manifest_url().is_empty()); |
| EXPECT_EQ(1u, manager->icons_.size()); |
| EXPECT_FALSE(manager->valid_manifest()); |
| EXPECT_FALSE(( |
| manager->icon_url(InstallableManager::IconUsage::kPrimary).is_empty())); |
| EXPECT_NE(nullptr, |
| (manager->icon(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, |
| (manager->icon_error(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_TRUE(!manager->task_queue_.HasCurrent()); |
| CheckServiceWorkerForInstallableManager(manager); |
| } |
| |
| { |
| // Check that a subsequent navigation resets state. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank"))); |
| InstallableManager* manager = GetManager(browser()); |
| |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_TRUE(manager->manifest_url().is_empty()); |
| EXPECT_FALSE(manager->valid_manifest()); |
| EXPECT_FALSE(manager->has_worker()); |
| EXPECT_TRUE(manager->icons_.empty()); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error()); |
| EXPECT_TRUE(!manager->task_queue_.HasCurrent()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckMaskableIcon) { |
| // Checks that InstallableManager chooses the correct primary icon when the |
| // manifest contains maskable icons. |
| |
| // Checks that if a MASKABLE icon is specified, it is used as primary icon. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetPreferMaskablePrimaryAndSplashIconParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_maskable.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->has_maskable_primary_icon()); |
| |
| EXPECT_FALSE(tester->splash_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->splash_icon()); |
| EXPECT_TRUE(tester->has_maskable_splash_icon()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| // Checks that we don't pick a MASKABLE icon if it was not requested. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), |
| GetPrimaryIconAndSplashIconParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_maskable.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| |
| EXPECT_FALSE(tester->splash_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| // Checks that we fall back to using an ANY icon if a MASKABLE icon is |
| // requested but not in the manifest. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetPreferMaskablePrimaryAndSplashIconParams(), |
| "/banners/manifest_test_page.html"); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| |
| EXPECT_FALSE(tester->splash_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| // Checks that we fall back to using an ANY icon if a MASKABLE icon is |
| // requested but the maskable icon is bad. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetPreferMaskablePrimaryAndSplashIconParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_bad_maskable.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| |
| EXPECT_FALSE(tester->splash_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckNavigationWithoutRunning) { |
| { |
| // Expect the call to ManifestAndIconTimeout to kick off an installable |
| // check and fail it on a not installable page. |
| base::HistogramTester histograms; |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"))); |
| |
| InstallableManager* manager = GetManager(browser()); |
| |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| // Set up a GetData call which will not record an installable metric to |
| // ensure we wait until the previous check has finished. |
| manager->GetData( |
| GetManifestParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank"))); |
| } |
| |
| { |
| // Expect the call to ManifestAndIconTimeout to kick off an installable |
| // check and pass it on an installable page. |
| base::HistogramTester histograms; |
| NavigateAndMaybeWaitForWorker(browser(), |
| "/banners/manifest_test_page.html"); |
| |
| InstallableManager* manager = GetManager(browser()); |
| |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| // Set up a GetData call which will not record an installable metric to |
| // ensure we wait until the previous check has finished. |
| manager->GetData( |
| GetManifestParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank"))); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckWebappInIframe) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), GetWebAppParams(), |
| "/banners/iframe_test_page.html"); |
| run_loop.Run(); |
| |
| // The installable manager should only retrieve items in the main frame; |
| // everything should be empty here. |
| EXPECT_TRUE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_TRUE(tester->manifest_url().is_empty()); |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_FALSE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MANIFEST}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckPageWithManifestAndNoServiceWorker) { |
| // Just fetch the manifest. This should have no error. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetManifestParams(), |
| "/banners/manifest_no_service_worker.html", /*wait_for_worker=*/false); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| // Fetching the full criteria should fail if we don't wait for the worker. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetWebAppParams(); |
| params.wait_for_worker = false; |
| RunInstallableManager(browser(), tester.get(), params); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_FALSE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MATCHING_SERVICE_WORKER}, |
| tester->errors()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest, |
| CheckLazyServiceWorkerPassesWhenWaiting) { |
| base::RunLoop tester_run_loop, sw_run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(tester_run_loop.QuitClosure())); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| auto manager = std::make_unique<LazyWorkerInstallableManager>( |
| web_contents, sw_run_loop.QuitClosure()); |
| |
| { |
| // Load a URL with no service worker. |
| GURL test_url = embedded_test_server()->GetURL( |
| "/banners/manifest_no_service_worker.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); |
| |
| // Kick off fetching the data. This should block on waiting for a worker. |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| sw_run_loop.Run(); |
| } |
| |
| // We should now be waiting for the service worker. |
| EXPECT_TRUE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_FALSE(manager->manifest_url().is_empty()); |
| EXPECT_FALSE(manager->has_worker()); |
| EXPECT_EQ(1u, manager->icons_.size()); |
| EXPECT_TRUE(manager->valid_manifest()); |
| EXPECT_FALSE( |
| (manager->icon_url(InstallableManager::IconUsage::kPrimary).is_empty())); |
| EXPECT_NE(nullptr, (manager->icon(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->worker_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, |
| (manager->icon_error(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_TRUE(!manager->task_queue_.HasCurrent()); |
| EXPECT_TRUE(!manager->task_queue_.paused_tasks_.empty()); |
| |
| { |
| // Fetching just the manifest and icons should not hang while the other call |
| // is waiting for a worker. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> nested_tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| manager->GetData( |
| GetPrimaryIconParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(nested_tester.get()))); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(nested_tester->manifest())); |
| EXPECT_FALSE(nested_tester->manifest_url().is_empty()); |
| EXPECT_FALSE(nested_tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, nested_tester->primary_icon()); |
| EXPECT_TRUE(nested_tester->valid_manifest()); |
| EXPECT_TRUE(nested_tester->worker_check_passed()); |
| EXPECT_TRUE(nested_tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, nested_tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, nested_tester->errors()); |
| } |
| |
| // Load the service worker. |
| if (IsServiceWorkerOfflineSupported()) { |
| EXPECT_TRUE(content::ExecuteScript( |
| web_contents, |
| "navigator.serviceWorker.register('service_worker.js');" |
| )); |
| } else { |
| EXPECT_TRUE(content::ExecuteScript( |
| web_contents, |
| "navigator.serviceWorker.register(" |
| "'service_worker_empty_fetch_handler.js');")); |
| } |
| tester_run_loop.Run(); |
| |
| // We should have passed now. |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| CheckServiceWorkerForTester(tester.get()); |
| |
| // Verify internal state. |
| EXPECT_FALSE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_FALSE(manager->manifest_url().is_empty()); |
| EXPECT_FALSE(manager->valid_manifest()); |
| EXPECT_EQ(1u, manager->icons_.size()); |
| EXPECT_FALSE( |
| (manager->icon_url(InstallableManager::IconUsage::kPrimary).is_empty())); |
| EXPECT_NE(nullptr, (manager->icon(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, |
| (manager->icon_error(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_TRUE(!manager->task_queue_.HasCurrent()); |
| EXPECT_FALSE(!manager->task_queue_.paused_tasks_.empty()); |
| CheckServiceWorkerForInstallableManager(manager.get()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckLazyServiceWorkerNoFetchHandlerFails) { |
| base::RunLoop tester_run_loop, sw_run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(tester_run_loop.QuitClosure())); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| auto manager = std::make_unique<LazyWorkerInstallableManager>( |
| web_contents, sw_run_loop.QuitClosure()); |
| |
| // Load a URL with no service worker. |
| GURL test_url = embedded_test_server()->GetURL( |
| "/banners/manifest_no_service_worker.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); |
| |
| // Kick off fetching the data. This should block on waiting for a worker. |
| manager->GetData(GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| sw_run_loop.Run(); |
| |
| // We should now be waiting for the service worker. |
| EXPECT_TRUE(!manager->task_queue_.HasCurrent()); |
| EXPECT_TRUE(!manager->task_queue_.paused_tasks_.empty()); |
| |
| // Load the service worker with no fetch handler. |
| EXPECT_TRUE(content::ExecuteScript(web_contents, |
| "navigator.serviceWorker.register('" |
| "service_worker_no_fetch_handler.js');")); |
| tester_run_loop.Run(); |
| |
| // We should fail the check. |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_FALSE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE}, |
| tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest, |
| CheckServiceWorkerErrorIsNotCached) { |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| base::RunLoop sw_run_loop; |
| auto manager = std::make_unique<LazyWorkerInstallableManager>( |
| web_contents, sw_run_loop.QuitClosure()); |
| |
| // Load a URL with no service worker. |
| GURL test_url = embedded_test_server()->GetURL( |
| "/banners/manifest_no_service_worker.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); |
| |
| { |
| base::RunLoop tester_run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(tester_run_loop.QuitClosure())); |
| |
| InstallableParams params = GetWebAppParams(); |
| params.wait_for_worker = false; |
| manager->GetData( |
| params, base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| tester_run_loop.Run(); |
| |
| // We should have returned with an error. |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_FALSE(tester->worker_check_passed()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MATCHING_SERVICE_WORKER}, |
| tester->errors()); |
| } |
| |
| { |
| base::RunLoop tester_run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(tester_run_loop.QuitClosure())); |
| |
| InstallableParams params = GetWebAppParams(); |
| manager->GetData( |
| params, base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| sw_run_loop.Run(); |
| |
| if (IsServiceWorkerOfflineSupported()) { |
| EXPECT_TRUE(content::ExecuteScript( |
| web_contents, |
| "navigator.serviceWorker.register('service_worker.js');")); |
| } else { |
| EXPECT_TRUE(content::ExecuteScript( |
| web_contents, |
| "navigator.serviceWorker.register(" |
| "'service_worker_empty_fetch_handler.js');")); |
| } |
| tester_run_loop.Run(); |
| |
| // The callback result will depend on the state of offline support. |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_TRUE(tester->valid_manifest()); |
| CheckServiceWorkerForTester(tester.get()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckPageWithNoServiceWorkerFetchHandler) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetWebAppParams(), |
| "/banners/no_sw_fetch_handler_test_page.html"); |
| |
| RunInstallableManager(browser(), tester.get(), GetWebAppParams()); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_FALSE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NOT_OFFLINE_CAPABLE}, |
| tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest, |
| CheckPageWithNestedServiceWorkerCanBeInstalled) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), GetWebAppParams(), |
| GetPath("/banners/nested_sw_test_page")); |
| |
| auto params = GetWebAppParams(); |
| // Make sure valid_manifest check is run. |
| params.is_debug_mode = true; |
| RunInstallableManager(browser(), tester.get(), params); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| CheckServiceWorkerForTester(tester.get()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckDataUrlIcon) { |
| // Verify that InstallableManager can handle data URL icons. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), GetWebAppParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_data_url_icon.json")); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_EQ(144, tester->primary_icon()->width()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckManifestCorruptedIcon) { |
| // Verify that the returned InstallableData::primary_icon is null if the web |
| // manifest points to a corrupt primary icon. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), |
| GetPrimaryIconParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_bad_icon.json")); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ICON_AVAILABLE}, |
| tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest, |
| CheckChangeInIconDimensions) { |
| // Verify that a follow-up request for a primary icon with a different size |
| // works. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), GetWebAppParams(), |
| GetPath("/banners/manifest_test_page")); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| CheckServiceWorkerForTester(tester.get()); |
| } |
| |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| auto params = GetWebAppParams(); |
| // Make sure valid_manifest check is run. |
| params.is_debug_mode = true; |
| RunInstallableManager(browser(), tester.get(), params); |
| |
| run_loop.Run(); |
| |
| // The smaller primary icon requirements should allow this to pass. |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| CheckServiceWorkerForTester(tester.get()); |
| } |
| } |
| |
| // The case that a service worker doesn't return an offline response for the |
| // start_url, but does for the manifest scope. |
| // - manifest's scope: /banners/ |
| // - manifest's start_url: /banners/manifest_test_page.html?ignore |
| // - service worker's scope: /banners/ |
| IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest, |
| CheckNotOfflineCapableStartUrl) { |
| // This test wants to check the service worker that doesn't support offline |
| // pages, so ignore the cases when `is_service_worker_offline_supported_` is |
| // true. |
| if (IsServiceWorkerOfflineSupported()) |
| return; |
| |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetWebAppParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_not_offline_capable_url.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| CheckServiceWorkerForTester(tester.get()); |
| |
| InstallableManager* manager = GetManager(browser()); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_FALSE(manager->manifest_url().is_empty()); |
| EXPECT_FALSE(manager->valid_manifest()); |
| EXPECT_EQ(1u, manager->icons_.size()); |
| EXPECT_FALSE( |
| (manager->icon_url(InstallableManager::IconUsage::kPrimary).is_empty())); |
| EXPECT_NE(nullptr, (manager->icon(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| EXPECT_EQ(NO_ERROR_DETECTED, |
| (manager->icon_error(InstallableManager::IconUsage::kPrimary))); |
| EXPECT_TRUE(!manager->task_queue_.HasCurrent()); |
| CheckServiceWorkerForInstallableManager(manager); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckNestedCallsToGetData) { |
| // Verify that we can call GetData while in a callback from GetData. |
| base::RunLoop run_loop; |
| InstallableParams params = GetWebAppParams(); |
| std::unique_ptr<NestedCallbackTester> tester(new NestedCallbackTester( |
| GetManager(browser()), params, run_loop.QuitClosure())); |
| |
| tester->Run(); |
| run_loop.Run(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| ManifestUrlChangeFlushesState) { |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| auto manager = std::make_unique<ResetDataInstallableManager>(web_contents); |
| |
| // Start on a page with no manifest. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"))); |
| |
| { |
| // Fetch the data. This should return an empty manifest. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| |
| EXPECT_TRUE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_EQ(NO_MANIFEST, manager->manifest_error()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MANIFEST}, |
| tester->errors()); |
| } |
| |
| { |
| // Injecting a manifest URL but not navigating should flush the state. |
| base::RunLoop run_loop; |
| manager->SetQuitClosure(run_loop.QuitClosure()); |
| EXPECT_TRUE(content::ExecuteScript(web_contents, "addManifestLinkTag()")); |
| run_loop.Run(); |
| |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| } |
| |
| { |
| // Fetch the data again. This should succeed. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| EXPECT_EQ(u"Manifest test app", tester->manifest().name); |
| EXPECT_EQ(std::u16string(), |
| tester->manifest().short_name.value_or(std::u16string())); |
| } |
| |
| { |
| // Flush the state again by changing the manifest URL. |
| base::RunLoop run_loop; |
| manager->SetQuitClosure(run_loop.QuitClosure()); |
| |
| GURL manifest_url = embedded_test_server()->GetURL( |
| "/banners/manifest_short_name_only.json"); |
| EXPECT_TRUE(content::ExecuteScript( |
| web_contents, "changeManifestUrl('" + manifest_url.spec() + "');")); |
| run_loop.Run(); |
| |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| } |
| |
| { |
| // Fetch again. This should return the data from the new manifest. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_EQ(std::u16string(), |
| tester->manifest().name.value_or(std::u16string())); |
| EXPECT_EQ(u"Manifest", tester->manifest().short_name); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, DebugModeWithNoManifest) { |
| // Ensure that a page with no manifest stops with NO_MANIFEST in debug mode. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetWebAppParams(); |
| params.is_debug_mode = true; |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"))); |
| RunInstallableManager(browser(), tester.get(), params); |
| run_loop.Run(); |
| |
| EXPECT_EQ(std::vector<InstallableStatusCode>({NO_MANIFEST}), |
| tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest, |
| DebugModeAccumulatesErrorsWithManifest) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetWebAppParams(); |
| params.is_debug_mode = true; |
| NavigateAndRunInstallableManager(browser(), tester.get(), params, |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/play_app_manifest.json")); |
| run_loop.Run(); |
| |
| if (IsCheckOfflineCapableFeatureEnabled()) { |
| EXPECT_EQ( |
| std::vector<InstallableStatusCode>( |
| {START_URL_NOT_VALID, MANIFEST_MISSING_NAME_OR_SHORT_NAME, |
| MANIFEST_DISPLAY_NOT_SUPPORTED, MANIFEST_MISSING_SUITABLE_ICON, |
| NO_URL_FOR_SERVICE_WORKER, NO_ACCEPTABLE_ICON}), |
| tester->errors()); |
| } else { |
| EXPECT_EQ(std::vector<InstallableStatusCode>( |
| {START_URL_NOT_VALID, MANIFEST_MISSING_NAME_OR_SHORT_NAME, |
| MANIFEST_DISPLAY_NOT_SUPPORTED, |
| MANIFEST_MISSING_SUITABLE_ICON, NO_ACCEPTABLE_ICON}), |
| tester->errors()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| DebugModeBadFallbackMaskable) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetPrimaryIconPreferMaskableParams(); |
| params.is_debug_mode = true; |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), params, |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_one_bad_maskable.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_ACCEPTABLE_ICON}, |
| tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| GetAllInstallabilityErrorsNoErrors) { |
| EXPECT_EQ(std::vector<content::InstallabilityError>{}, |
| NavigateAndGetAllInstallabilityErrors( |
| browser(), "/banners/manifest_test_page.html")); |
| |
| // Should pass a second time with no issues. |
| EXPECT_EQ(std::vector<content::InstallabilityError>{}, |
| GetAllInstallabilityErrors(browser())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| GetAllInstallabilityErrorsWithNoManifest) { |
| EXPECT_EQ(std::vector<content::InstallabilityError>{GetInstallabilityError( |
| NO_MANIFEST)}, |
| NavigateAndGetAllInstallabilityErrors( |
| browser(), "/banners/no_manifest_test_page.html")); |
| |
| // Should pass a second time with no issues. |
| EXPECT_EQ(std::vector<content::InstallabilityError>{GetInstallabilityError( |
| NO_MANIFEST)}, |
| GetAllInstallabilityErrors(browser())); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(InstallableManagerOfflineCapabilityBrowserTest, |
| GetAllInstallabilityErrorsWithPlayAppManifest) { |
| auto errors = std::vector<content::InstallabilityError>( |
| {GetInstallabilityError(START_URL_NOT_VALID), |
| GetInstallabilityError(MANIFEST_MISSING_NAME_OR_SHORT_NAME), |
| GetInstallabilityError(MANIFEST_DISPLAY_NOT_SUPPORTED), |
| GetInstallabilityError(MANIFEST_MISSING_SUITABLE_ICON)}); |
| if (IsCheckOfflineCapableFeatureEnabled()) { |
| errors.push_back(GetInstallabilityError(NO_URL_FOR_SERVICE_WORKER)); |
| } |
| errors.push_back(GetInstallabilityError(NO_ACCEPTABLE_ICON)); |
| EXPECT_EQ(errors, NavigateAndGetAllInstallabilityErrors( |
| browser(), GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/play_app_manifest.json"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerAllowlistOriginBrowserTest, |
| SecureOriginCheckRespectsUnsafeFlag) { |
| // The allowlisted origin should be regarded as secure. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kInsecureOrigin))); |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_TRUE(InstallableManager::IsContentSecure(contents)); |
| |
| // While a non-allowlisted origin should not. |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), GURL(kOtherInsecureOrigin))); |
| EXPECT_FALSE(InstallableManager::IsContentSecure(contents)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, NarrowServiceWorker) { |
| NavigateAndMaybeWaitForWorker(browser(), "/banners/scope_c/scope_c.html"); |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetWebAppParams(); |
| params.wait_for_worker = false; |
| |
| RunInstallableManager(browser(), tester.get(), params); |
| run_loop.Run(); |
| |
| EXPECT_EQ(std::vector<InstallableStatusCode>({NO_MATCHING_SERVICE_WORKER}), |
| tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckSplashIcon) { |
| // Checks that InstallableManager chooses the correct splash icon. |
| |
| // Test page has a manifest with only one icon, primary icon and splash icon |
| // should be the same one. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), |
| GetPrimaryIconAndSplashIconParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_one_icon.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_FALSE(tester->splash_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| |
| EXPECT_EQ(tester->primary_icon_url(), tester->splash_icon_url()); |
| } |
| |
| // Test page has a manifest with only one maskable icon. This should fail to |
| // get a splash icon but not record an error. |
| { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetPreferMaskablePrimaryAndSplashIconParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_one_maskable.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_TRUE(tester->has_maskable_primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_FALSE(tester->splash_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->splash_icon()); |
| EXPECT_TRUE(tester->has_maskable_splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckScreenshots) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetManifestParams(); |
| params.fetch_screenshots = true; |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), params, |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_with_screenshots.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_EQ(1u, tester->screenshots().size()); |
| // Corresponding form_factor should filter out the screenshot with mismatched |
| // form_factor. |
| #if BUILDFLAG(IS_ANDROID) |
| EXPECT_LT(tester->screenshots()[0].width(), |
| tester->screenshots()[0].height()); |
| #else |
| EXPECT_GT(tester->screenshots()[0].width(), |
| tester->screenshots()[0].height()); |
| #endif |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckScreenshotsPlatform) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetManifestParams(); |
| params.fetch_screenshots = true; |
| |
| // Check if only screenshots with mismatched form_factor are available, they |
| // are still used. |
| #if BUILDFLAG(IS_ANDROID) |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), params, |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_with_only_wide_screenshots.json")); |
| #else |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), params, |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_with_only_narrow_screenshots.json")); |
| #endif |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_EQ(1u, tester->screenshots().size()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckScreenshotsNumber) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetManifestParams(); |
| params.fetch_screenshots = true; |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), params, |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_with_too_many_screenshots.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_EQ(8u, tester->screenshots().size()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckLargeScreenshotsFilteredOut) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| InstallableParams params = GetManifestParams(); |
| params.fetch_screenshots = true; |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), params, |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_large_screenshot.json")); |
| |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_EQ(1u, tester->screenshots().size()); |
| EXPECT_EQ(551, tester->screenshots()[0].width()); |
| EXPECT_EQ(541, tester->screenshots()[0].height()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| ManifestLinkChangeReportsError) { |
| InstallableManager* manager = GetManager(browser()); |
| |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager(browser(), tester.get(), GetManifestParams(), |
| "/banners/manifest_test_page.html"); |
| // Simulate a manifest URL update by just calling the observer function. |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| static_cast<content::WebContentsObserver*>(manager)->DidUpdateWebManifestURL( |
| web_contents->GetPrimaryMainFrame(), GURL()); |
| run_loop.Run(); |
| |
| ASSERT_EQ(tester->errors().size(), 1u); |
| EXPECT_EQ(tester->errors()[0], MANIFEST_URL_CHANGED); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| CheckManifestOnly_DisplayOverride) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetManifestParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_display_override.json")); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| ASSERT_EQ(2u, tester->manifest().display_override.size()); |
| EXPECT_EQ(blink::mojom::DisplayMode::kMinimalUi, |
| tester->manifest().display_override[0]); |
| EXPECT_EQ(blink::mojom::DisplayMode::kStandalone, |
| tester->manifest().display_override[1]); |
| |
| EXPECT_TRUE(tester->primary_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->primary_icon()); |
| EXPECT_FALSE(tester->has_maskable_primary_icon()); |
| EXPECT_FALSE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_FALSE(tester->has_maskable_splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| ManifestDisplayOverrideReportsError_DisplayOverride) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetWebAppParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_display_override_contains_browser.json")); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| ASSERT_EQ(3u, tester->manifest().display_override.size()); |
| EXPECT_EQ(blink::mojom::DisplayMode::kBrowser, |
| tester->manifest().display_override[0]); |
| EXPECT_EQ(blink::mojom::DisplayMode::kMinimalUi, |
| tester->manifest().display_override[1]); |
| EXPECT_EQ(blink::mojom::DisplayMode::kStandalone, |
| tester->manifest().display_override[2]); |
| EXPECT_EQ( |
| std::vector<InstallableStatusCode>{ |
| MANIFEST_DISPLAY_OVERRIDE_NOT_SUPPORTED}, |
| tester->errors()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, |
| FallbackToDisplayBrowser_DisplayOverride) { |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| NavigateAndRunInstallableManager( |
| browser(), tester.get(), GetWebAppParams(), |
| GetURLOfPageWithServiceWorkerAndManifest( |
| "/banners/manifest_display_override_display_is_browser.json")); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_FALSE(tester->manifest_url().is_empty()); |
| ASSERT_EQ(1u, tester->manifest().display_override.size()); |
| EXPECT_EQ(blink::mojom::DisplayMode::kStandalone, |
| tester->manifest().display_override[0]); |
| |
| EXPECT_FALSE(tester->primary_icon_url().is_empty()); |
| EXPECT_NE(nullptr, tester->primary_icon()); |
| EXPECT_EQ(144, tester->primary_icon()->width()); |
| EXPECT_TRUE(tester->valid_manifest()); |
| EXPECT_TRUE(tester->worker_check_passed()); |
| EXPECT_TRUE(tester->splash_icon_url().is_empty()); |
| EXPECT_EQ(nullptr, tester->splash_icon()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| } |
| |
| class InstallableManagerInPrerenderingBrowserTest |
| : public InstallableManagerBrowserTest { |
| public: |
| InstallableManagerInPrerenderingBrowserTest() |
| : prerender_helper_(base::BindRepeating( |
| &InstallableManagerInPrerenderingBrowserTest::web_contents, |
| base::Unretained(this))) {} |
| ~InstallableManagerInPrerenderingBrowserTest() override = default; |
| |
| content::test::PrerenderTestHelper* prerender_helper() { |
| return &prerender_helper_; |
| } |
| |
| content::WebContents* web_contents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| private: |
| content::test::PrerenderTestHelper prerender_helper_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(InstallableManagerInPrerenderingBrowserTest, |
| InstallableManagerInPrerendering) { |
| auto manager = std::make_unique<ResetDataInstallableManager>(web_contents()); |
| GURL url = embedded_test_server()->GetURL("/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| manager->ClearOnResetData(); |
| |
| // Loads a page in the prerendering. |
| const std::string path = "/banners/manifest_test_page.html"; |
| auto prerender_url = embedded_test_server()->GetURL(path); |
| int host_id = prerender_helper()->AddPrerender(prerender_url); |
| content::test::PrerenderHostObserver host_observer(*web_contents(), host_id); |
| |
| // The prerendering should not affect the current data. |
| EXPECT_FALSE(manager->GetOnResetData()); |
| |
| { |
| // Fetches the data. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| } |
| // It should have no data since manifest_test_page.html is loaded in the |
| // prerendering. |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_EQ(NO_MANIFEST, manager->manifest_error()); |
| |
| { |
| // If the page is activated from the prerendering and the data should be |
| // reset. |
| base::RunLoop run_loop; |
| manager->SetQuitClosure(run_loop.QuitClosure()); |
| NavigateAndMaybeWaitForWorker(browser(), path); |
| run_loop.Run(); |
| } |
| |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| |
| { |
| // Fetch the data again. This should succeed. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| EXPECT_EQ(u"Manifest test app", tester->manifest().name); |
| EXPECT_EQ(std::u16string(), |
| tester->manifest().short_name.value_or(std::u16string())); |
| } |
| } |
| |
| class MockInstallableManager : public InstallableManager { |
| public: |
| explicit MockInstallableManager(content::WebContents* web_contents) |
| : InstallableManager(web_contents) {} |
| ~MockInstallableManager() override = default; |
| |
| MOCK_METHOD(void, OnResetData, (), (override)); |
| MOCK_METHOD(void, |
| DidUpdateWebManifestURL, |
| (content::RenderFrameHost * rfh, const GURL& manifest_url), |
| (override)); |
| }; |
| |
| MATCHER_P(IsManifestURL, file_name, std::string()) { |
| return arg.ExtractFileName() == file_name; |
| } |
| |
| MATCHER_P(IsPrerenderedRFH, render_frame_host, std::string()) { |
| return arg->GetGlobalId() == render_frame_host->GetGlobalId(); |
| } |
| |
| // Tests that NotifyManifestUrlChanged is called on the page that has manifest |
| // after the activation from the prerendering. |
| IN_PROC_BROWSER_TEST_F(InstallableManagerInPrerenderingBrowserTest, |
| NotifyManifestUrlChangedInActivation) { |
| auto manager = std::make_unique<MockInstallableManager>(web_contents()); |
| GURL url = embedded_test_server()->GetURL("/empty.html"); |
| // OnResetData() is called when a navigation is finished. |
| EXPECT_CALL(*manager.get(), OnResetData()).Times(1); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Loads a page in the prerendering. |
| auto prerender_url = |
| embedded_test_server()->GetURL("/banners/manifest_test_page.html"); |
| // OnResetData() should not be called on the prerendering. |
| EXPECT_CALL(*manager.get(), OnResetData()).Times(0); |
| int host_id = prerender_helper()->AddPrerender(prerender_url); |
| |
| content::test::PrerenderHostObserver host_observer(*web_contents(), host_id); |
| content::RenderFrameHost* render_frame_host = |
| prerender_helper()->GetPrerenderedMainFrameHost(host_id); |
| |
| { |
| // Fetches the data. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| } |
| // It should have no data since manifest_test_page.html is loaded in the |
| // prerendering. |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_EQ(NO_MANIFEST, manager->manifest_error()); |
| |
| { |
| // If the page is activated from the prerendering and the data should be |
| // reset and notify the updated manifest url. |
| EXPECT_CALL(*manager.get(), OnResetData()).Times(1); |
| EXPECT_CALL(*manager.get(), |
| DidUpdateWebManifestURL(IsPrerenderedRFH(render_frame_host), |
| IsManifestURL("manifest.json"))); |
| prerender_helper()->NavigatePrimaryPage(prerender_url); |
| } |
| |
| EXPECT_TRUE(host_observer.was_activated()); |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| |
| { |
| // Fetch the data again. This should succeed. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| EXPECT_FALSE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{}, tester->errors()); |
| EXPECT_EQ(u"Manifest test app", tester->manifest().name); |
| EXPECT_EQ(std::u16string(), |
| tester->manifest().short_name.value_or(std::u16string())); |
| } |
| } |
| |
| // Tests that NotifyManifestUrlChanged is not called without manifest after |
| // the activation from the prerendering. |
| IN_PROC_BROWSER_TEST_F(InstallableManagerInPrerenderingBrowserTest, |
| NotNotifyManifestUrlChangedInActivation) { |
| auto manager = std::make_unique<MockInstallableManager>(web_contents()); |
| GURL url = embedded_test_server()->GetURL("/empty.html"); |
| // OnResetData() is called when a navigation is finished. |
| EXPECT_CALL(*manager.get(), OnResetData()).Times(1); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Loads a page in the prerendering. |
| auto prerender_url = |
| embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"); |
| // OnResetData() should not be called on the prerendering. |
| EXPECT_CALL(*manager.get(), OnResetData()).Times(0); |
| int host_id = prerender_helper()->AddPrerender(prerender_url); |
| |
| content::test::PrerenderHostObserver host_observer(*web_contents(), host_id); |
| { |
| // Fetches the data. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| } |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_EQ(NO_MANIFEST, manager->manifest_error()); |
| |
| // OnResetData() is called when a navigation is finished. |
| EXPECT_CALL(*manager.get(), OnResetData()).Times(1); |
| // OnResetData() should not be called when a page doesn't have a manifest. |
| EXPECT_CALL(*manager.get(), DidUpdateWebManifestURL(testing::_, testing::_)) |
| .Times(0); |
| prerender_helper()->NavigatePrimaryPage(prerender_url); |
| |
| EXPECT_TRUE(host_observer.was_activated()); |
| EXPECT_TRUE(blink::IsEmptyManifest(manager->manifest())); |
| EXPECT_EQ(NO_ERROR_DETECTED, manager->manifest_error()); |
| |
| { |
| // Fetch the data again. This should return the same empty result as |
| // earlier. |
| base::RunLoop run_loop; |
| std::unique_ptr<CallbackTester> tester( |
| new CallbackTester(run_loop.QuitClosure())); |
| |
| manager->GetData( |
| GetWebAppParams(), |
| base::BindOnce(&CallbackTester::OnDidFinishInstallableCheck, |
| base::Unretained(tester.get()))); |
| run_loop.Run(); |
| EXPECT_TRUE(blink::IsEmptyManifest(tester->manifest())); |
| EXPECT_EQ(NO_MANIFEST, manager->manifest_error()); |
| EXPECT_EQ(std::vector<InstallableStatusCode>{NO_MANIFEST}, |
| tester->errors()); |
| } |
| } |
| |
| } // namespace webapps |