| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/web_applications/manifest_update_manager.h" |
| |
| #include <ios> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/check_op.h" |
| #include "base/containers/contains.h" |
| #include "base/containers/flat_tree.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ref.h" |
| #include "base/notreached.h" |
| #include "base/numerics/clamped_math.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/run_loop.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/test/bind.h" |
| #include "base/test/gmock_expected_support.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_future.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "build/buildflag.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h" |
| #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" |
| #include "chrome/browser/ui/web_applications/web_app_browser_controller.h" |
| #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h" |
| #include "chrome/browser/ui/web_applications/web_app_dialogs.h" |
| #include "chrome/browser/web_applications/external_install_options.h" |
| #include "chrome/browser/web_applications/externally_managed_app_manager.h" |
| #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h" |
| #include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h" |
| #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h" |
| #include "chrome/browser/web_applications/os_integration/os_integration_manager.h" |
| #include "chrome/browser/web_applications/os_integration/web_app_shortcut.h" |
| #include "chrome/browser/web_applications/proto/web_app_install_state.pb.h" |
| #include "chrome/browser/web_applications/test/web_app_icon_test_utils.h" |
| #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" |
| #include "chrome/browser/web_applications/test/web_app_sync_test_utils.h" |
| #include "chrome/browser/web_applications/test/web_app_test_observers.h" |
| #include "chrome/browser/web_applications/test/web_app_test_utils.h" |
| #include "chrome/browser/web_applications/web_app.h" |
| #include "chrome/browser/web_applications/web_app_callback_app_identity.h" |
| #include "chrome/browser/web_applications/web_app_command_manager.h" |
| #include "chrome/browser/web_applications/web_app_command_scheduler.h" |
| #include "chrome/browser/web_applications/web_app_constants.h" |
| #include "chrome/browser/web_applications/web_app_helpers.h" |
| #include "chrome/browser/web_applications/web_app_icon_generator.h" |
| #include "chrome/browser/web_applications/web_app_icon_manager.h" |
| #include "chrome/browser/web_applications/web_app_install_finalizer.h" |
| #include "chrome/browser/web_applications/web_app_install_info.h" |
| #include "chrome/browser/web_applications/web_app_install_params.h" |
| #include "chrome/browser/web_applications/web_app_management_type.h" |
| #include "chrome/browser/web_applications/web_app_origin_association_manager.h" |
| #include "chrome/browser/web_applications/web_app_proto_utils.h" |
| #include "chrome/browser/web_applications/web_app_provider.h" |
| #include "chrome/browser/web_applications/web_app_registrar.h" |
| #include "chrome/browser/web_applications/web_app_registry_update.h" |
| #include "chrome/browser/web_applications/web_app_sync_bridge.h" |
| #include "chrome/browser/web_applications/web_app_tab_helper.h" |
| #include "chrome/browser/web_applications/web_app_ui_manager.h" |
| #include "chrome/browser/web_applications/web_app_utils.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "chrome/test/base/web_feature_histogram_tester.h" |
| #include "components/services/app_service/public/cpp/file_handler.h" |
| #include "components/services/app_service/public/cpp/icon_info.h" |
| #include "components/services/app_service/public/cpp/protocol_handler_info.h" |
| #include "components/services/app_service/public/cpp/share_target.h" |
| #include "components/webapps/browser/features.h" |
| #include "components/webapps/browser/install_result_code.h" |
| #include "components/webapps/browser/installable/installable_metrics.h" |
| #include "components/webapps/browser/uninstall_result_code.h" |
| #include "components/webapps/common/web_app_id.h" |
| #include "components/webapps/services/web_app_origin_association/test/test_web_app_origin_association_fetcher.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/prerender_test_util.h" |
| #include "content/public/test/url_loader_interceptor.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "net/base/net_errors.h" |
| #include "net/http/http_status_code.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "services/network/public/cpp/resource_request.h" |
| #include "services/network/public/mojom/url_loader.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/gfx/color_utils.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/image/image.h" |
| #include "ui/gfx/image/image_family.h" |
| #include "ui/views/test/dialog_test.h" |
| #include "ui/views/test/widget_test.h" |
| #include "ui/views/widget/any_widget_observer.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| #include "ui/views/window/dialog_delegate.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| #if BUILDFLAG(IS_MAC) |
| #include "base/mac/mac_util.h" |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #include "ash/constants/ash_features.h" |
| #include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.h" |
| #endif |
| |
| #if BUILDFLAG(IS_LINUX) |
| #include "chrome/browser/web_applications/os_integration/web_app_file_handler_registration.h" |
| #endif |
| |
| namespace web_app { |
| |
| namespace { |
| |
| // Note: When adding new tests and any bitmap resources they may require, please |
| // make sure the filename reflects the actual pixel size of the bitmap and that |
| // it includes a reference to the color of the bitmap. Avoid multi-color |
| // images unless they are necessary to test something. For example, if you need |
| // to add a blue square image with edge size 4096, the filename should be |
| // something like 4096x4096-blue.png and the RGB value of the blue color used |
| // should match SK_ColorBLUE. This ensures that the test can be validated just |
| // by reading the code and avoids looking up pixel colors in image editors or |
| // in defined constants with non-descriptive names. |
| |
| constexpr char kUpdateHistogramName[] = "Webapp.Update.ManifestUpdateResult"; |
| |
| // DEPRECATED: Do not use in new tests (see note above). |
| constexpr char kInstallableIconList[] = R"( |
| [ |
| { |
| "src": "launcher-icon-4x.png", |
| "sizes": "192x192", |
| "type": "image/png" |
| } |
| ] |
| )"; |
| constexpr SkColor kInstallableIconTopLeftColor = |
| SkColorSetRGB(0x15, 0x96, 0xE0); |
| |
| // DEPRECATED: Do not use in new tests (see note above). |
| constexpr char kAnotherInstallableIconList[] = R"( |
| [ |
| { |
| "src": "/banners/image-512px.png", |
| "sizes": "512x512", |
| "type": "image/png" |
| } |
| ] |
| )"; |
| constexpr SkColor kAnotherInstallableIconTopLeftColor = |
| SkColorSetRGB(0x5C, 0x5C, 0x5C); |
| |
| constexpr char kAnotherShortcutsItemName[] = "Timeline"; |
| constexpr char16_t kAnotherShortcutsItemName16[] = u"Timeline"; |
| constexpr char kAnotherShortcutsItemUrl[] = "/shortcut"; |
| constexpr char kAnotherShortcutsItemShortName[] = "H"; |
| constexpr char kAnotherShortcutsItemDescription[] = "Navigate home"; |
| constexpr char kAnotherIconSrc[] = "/banners/launcher-icon-4x.png"; |
| constexpr int kAnotherIconSize = 192; |
| |
| constexpr char kShortcutsItem[] = R"( |
| [ |
| { |
| "name": "Home", |
| "short_name": "HM", |
| "description": "Go home", |
| "url": ".", |
| "icons": [ |
| { |
| "src": "/banners/image-512px.png", |
| "sizes": "512x512", |
| "type": "image/png" |
| } |
| ] |
| } |
| ] |
| )"; |
| |
| constexpr char kShortcutsItems[] = R"( |
| [ |
| { |
| "name": "Home", |
| "short_name": "HM", |
| "description": "Go home", |
| "url": ".", |
| "icons": [ |
| { |
| "src": "/banners/image-512px.png", |
| "sizes": "512x512", |
| "type": "image/png" |
| } |
| ] |
| }, |
| { |
| "name": "Settings", |
| "short_name": "ST", |
| "description": "App settings", |
| "url": "/settings", |
| "icons": [ |
| { |
| "src": "launcher-icon-4x.png", |
| "sizes": "192x192", |
| "type": "image/png" |
| } |
| ] |
| } |
| ] |
| )"; |
| |
| // Two 'unimportant' icon sizes, smaller than the smallest generated icon on all |
| // platforms. This simplifies creating test expectations as it avoids having |
| // unimportant icons affecting generated icons, which inherit their bits from |
| // the next size up when left unspecified by the manifest. |
| constexpr int kUnimportantIconSize = 2; |
| constexpr int kUnimportantIconSize2 = 4; |
| |
| // An icon size guaranteed to meet the installability requirements, and on all |
| // platforms is larger than both the install icon and launcher icon. |
| constexpr int kInstallabilityIconSize = 512; |
| // The minimum icon size to meet the installability criteria. |
| constexpr int kInstallMinSize = 192; |
| |
| // Platform definitions for evaluating rules of which size to look for in a |
| // shortcut. |
| constexpr int kAll = 0; |
| constexpr int kWin = 1; // Windows-only rule. |
| constexpr int kMac = 2; // Mac-only rule. |
| constexpr int kNotWin = 3; // All platforms except Windows. |
| constexpr int kNotMac = 4; // All platforms except Mac. |
| |
| ManifestUpdateManager& GetManifestUpdateManager(Profile* profile) { |
| return WebAppProvider::GetForTest(profile)->manifest_update_manager(); |
| } |
| |
| // Utility class to wait for a manifest update to finish and get a |
| // ManifestUpdateResult back. |
| class UpdateCheckResultAwaiter { |
| public: |
| explicit UpdateCheckResultAwaiter(const GURL& url) : url_(url) { |
| SetCallback(); |
| } |
| |
| void SetCallback() { |
| ManifestUpdateManager::SetResultCallbackForTesting(base::BindOnce( |
| &UpdateCheckResultAwaiter::OnResult, base::Unretained(this))); |
| } |
| |
| ManifestUpdateResult AwaitNextResult() && { |
| run_loop_.Run(); |
| return *result_; |
| } |
| |
| void OnResult(const GURL& url, ManifestUpdateResult result) { |
| if (url != url_) { |
| SetCallback(); |
| return; |
| } |
| result_ = result; |
| run_loop_.Quit(); |
| } |
| |
| private: |
| const GURL url_; |
| base::RunLoop run_loop_; |
| std::optional<ManifestUpdateResult> result_; |
| }; |
| |
| // Utility class to wait for WebContentsObserver to trigger a DidFinishLoad. |
| class DidFinishLoadObserver : public content::WebContentsObserver { |
| public: |
| explicit DidFinishLoadObserver(content::WebContents* contents, |
| const GURL& url) |
| : content::WebContentsObserver(contents), expected_url_(url) {} |
| |
| bool AwaitCorrectPageLoaded() { |
| on_load_run_loop_finished_.Run(); |
| base::test::TestFuture<void> future; |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, future.GetCallback()); |
| EXPECT_TRUE(future.Wait()); |
| return correct_page_loaded_; |
| } |
| |
| private: |
| void DidFinishLoad(content::RenderFrameHost* render_frame_host, |
| const GURL& validated_url) override { |
| if (!validated_url.is_valid() || validated_url != expected_url_) { |
| return; |
| } |
| |
| correct_page_loaded_ = true; |
| on_load_run_loop_finished_.Quit(); |
| } |
| |
| base::RunLoop on_load_run_loop_finished_; |
| const GURL expected_url_; |
| bool correct_page_loaded_ = false; |
| }; |
| |
| } // namespace |
| |
| class ManifestUpdateManagerBrowserTest : public WebAppBrowserTestBase { |
| public: |
| ManifestUpdateManagerBrowserTest() |
| : update_dialog_scope_(SetIdentityUpdateDialogActionForTesting( |
| AppIdentityUpdate::kSkipped)) {} |
| ManifestUpdateManagerBrowserTest(const ManifestUpdateManagerBrowserTest&) = |
| delete; |
| ManifestUpdateManagerBrowserTest& operator=( |
| const ManifestUpdateManagerBrowserTest&) = delete; |
| |
| ~ManifestUpdateManagerBrowserTest() override = default; |
| |
| void SetUp() override { |
| http_server_.AddDefaultHandlers(GetChromeTestDataDir()); |
| http_server_.RegisterRequestHandler(base::BindRepeating( |
| &ManifestUpdateManagerBrowserTest::RequestHandlerOverride, |
| base::Unretained(this))); |
| ASSERT_TRUE(http_server_.Start()); |
| // Suppress globally to avoid OS hooks deployed for system web app during |
| // WebAppProvider setup. |
| WebAppBrowserTestBase::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| // Cannot construct RunLoop in constructor due to threading restrictions. |
| shortcut_run_loop_.emplace(); |
| test::WaitUntilWebAppProviderAndSubsystemsReady( |
| WebAppProvider::GetForTest(browser()->profile())); |
| } |
| |
| void OnShortcutInfoRetrieved(std::unique_ptr<ShortcutInfo> shortcut_info) { |
| updated_colors_ = {}; |
| if (shortcut_info) { |
| gfx::ImageFamily::const_iterator it; |
| // Loop through each size in the ImgFamily and add it to the color map. |
| for (it = shortcut_info->favicon.begin(); |
| it != shortcut_info->favicon.end(); ++it) { |
| updated_colors_.emplace_back(it->Size().width(), |
| it->AsBitmap().getColor(0, 0)); |
| } |
| } |
| shortcut_run_loop_->Quit(); |
| } |
| |
| bool RuleAppliesToThisOS(int os, int size) { |
| #if BUILDFLAG(IS_WIN) |
| return os == kWin || os == kNotMac || os == kAll; |
| #elif BUILDFLAG(IS_MAC) |
| // The Mac code in generating these icons doesn't write a size 48 icon. See |
| // chrome/browser/web_applications/web_app_icon_generator.h's |
| // `kInstallIconSize`. Skip it. |
| if (size == icon_size::k48) |
| return false; |
| return os == kMac || os == kNotWin || os == kAll; |
| #else |
| return os == kNotWin || os == kNotMac || os == kAll; |
| #endif |
| } |
| |
| // Confirms that the platform shortcut for this app (with id `app_id`) |
| // contains an icon family that matches exactly the color specified in |
| // `expectations`. The latter is a vector mapping (size, os) to an SK_Color |
| // value. |
| void ConfirmShortcutColors( |
| const webapps::AppId& app_id, |
| const std::vector<std::pair<std::pair<int, int>, SkColor>>& |
| expectations) { |
| GetProvider().os_integration_manager().GetShortcutInfoForAppFromRegistrar( |
| app_id, base::BindOnce( |
| &ManifestUpdateManagerBrowserTest::OnShortcutInfoRetrieved, |
| base::Unretained(this))); |
| shortcut_run_loop_->Run(); |
| |
| std::vector<std::pair<int /* size */, SkColor>>::const_iterator |
| actual_size_to_color_it = updated_colors_.begin(); |
| for (auto expected_size_to_color_it : expectations) { |
| int expected_size = expected_size_to_color_it.first.first; |
| int platform = expected_size_to_color_it.first.second; |
| SkColor expected_color = expected_size_to_color_it.second; |
| |
| if (!RuleAppliesToThisOS(platform, expected_size)) { |
| SCOPED_TRACE(::testing::Message() << "Skipping size " << expected_size |
| << " (wrong os: " << platform << ")"); |
| continue; |
| } |
| |
| int actual_size = actual_size_to_color_it->first; |
| SkColor actual_color = actual_size_to_color_it->second; |
| EXPECT_EQ(expected_size, actual_size); |
| EXPECT_EQ(expected_color, actual_color) |
| << "Size " << expected_size << ": Expecting ARGB " << std::hex |
| << expected_color << " but found " << std::hex << actual_color; |
| ++actual_size_to_color_it; |
| } |
| |
| ASSERT_EQ(updated_colors_.end(), actual_size_to_color_it) |
| << "Unexpected size found in shortcut: " |
| << actual_size_to_color_it->first << ": ARGB " << std::hex |
| << actual_size_to_color_it->second; |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> RequestHandlerOverride( |
| const net::test_server::HttpRequest& request) { |
| if (request_override_) |
| return request_override_.Run(request); |
| return nullptr; |
| } |
| |
| void OverrideManifest(const char* manifest_template, |
| const std::vector<std::string>& substitutions) { |
| std::string content = base::ReplaceStringPlaceholders( |
| manifest_template, substitutions, nullptr); |
| request_override_ = base::BindLambdaForTesting( |
| [this, content = std::move(content)]( |
| const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (request.GetURL() != GetManifestURL()) |
| return nullptr; |
| auto http_response = |
| std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_code(net::HTTP_OK); |
| http_response->set_content(content); |
| return std::move(http_response); |
| }); |
| } |
| |
| GURL GetAppURL() const { |
| return http_server_.GetURL("/banners/manifest_test_page.html"); |
| } |
| |
| GURL GetManifestURL() const { |
| return http_server_.GetURL("/banners/manifest.json"); |
| } |
| |
| GURL GetAppURLWithoutManifest() const { |
| return http_server_.GetURL("/banners/no_manifest_test_page.html"); |
| } |
| |
| // Mimics the `DIY` app install flow from the three dot overflow menu. |
| webapps::AppId InstallWebAppWithoutManifest() { |
| GURL app_url = GetAppURLWithoutManifest(); |
| EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url)); |
| |
| webapps::AppId app_id; |
| base::test::TestFuture<const webapps::AppId&, webapps::InstallResultCode> |
| install_future; |
| GetProvider().scheduler().FetchManifestAndInstall( |
| webapps::WebappInstallSource::MENU_BROWSER_TAB, |
| browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(), |
| base::BindOnce(test::TestAcceptDialogCallback), |
| install_future.GetCallback(), |
| FallbackBehavior::kUseFallbackInfoWhenNotInstallable); |
| EXPECT_EQ(install_future.Get<webapps::InstallResultCode>(), |
| webapps::InstallResultCode::kSuccessNewInstall); |
| return install_future.Get<webapps::AppId>(); |
| } |
| |
| webapps::AppId InstallWebApp() { |
| GURL app_url = GetAppURL(); |
| EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url)); |
| |
| webapps::AppId app_id; |
| base::RunLoop run_loop; |
| GetProvider().scheduler().FetchManifestAndInstall( |
| webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON, |
| browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(), |
| base::BindOnce(test::TestAcceptDialogCallback), |
| base::BindLambdaForTesting([&](const webapps::AppId& new_app_id, |
| webapps::InstallResultCode code) { |
| EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall); |
| app_id = new_app_id; |
| run_loop.Quit(); |
| }), |
| FallbackBehavior::kAllowFallbackDataAlways); |
| |
| run_loop.Run(); |
| return app_id; |
| } |
| |
| webapps::AppId InstallOemWebApp() { |
| const GURL app_url = GetAppURL(); |
| EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url)); |
| |
| webapps::AppId app_id; |
| base::RunLoop run_loop; |
| GetProvider().scheduler().FetchManifestAndInstall( |
| webapps::WebappInstallSource::PRELOADED_OEM, |
| browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(), |
| base::BindOnce(test::TestAcceptDialogCallback), |
| base::BindLambdaForTesting([&](const webapps::AppId& new_app_id, |
| webapps::InstallResultCode code) { |
| EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall); |
| app_id = new_app_id; |
| run_loop.Quit(); |
| }), |
| FallbackBehavior::kAllowFallbackDataAlways); |
| |
| run_loop.Run(); |
| return app_id; |
| } |
| |
| webapps::AppId InstallDefaultApp() { |
| const GURL app_url = GetAppURL(); |
| base::RunLoop run_loop; |
| ExternalInstallOptions install_options( |
| app_url, mojom::UserDisplayMode::kStandalone, |
| ExternalInstallSource::kInternalDefault); |
| install_options.add_to_applications_menu = false; |
| install_options.add_to_desktop = false; |
| install_options.add_to_quick_launch_bar = false; |
| install_options.install_placeholder = true; |
| GetProvider().externally_managed_app_manager().Install( |
| std::move(install_options), |
| base::BindLambdaForTesting( |
| [&](const GURL& installed_app_url, |
| ExternallyManagedAppManager::InstallResult result) { |
| EXPECT_EQ(installed_app_url, app_url); |
| EXPECT_EQ(result.code, |
| webapps::InstallResultCode::kSuccessNewInstall); |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| return GetProvider() |
| .registrar_unsafe() |
| .LookupExternalAppId(app_url) |
| .value(); |
| } |
| |
| webapps::AppId InstallPolicyApp() { |
| const GURL app_url = GetAppURL(); |
| base::RunLoop run_loop; |
| ExternalInstallOptions install_options( |
| app_url, mojom::UserDisplayMode::kStandalone, |
| ExternalInstallSource::kExternalPolicy); |
| install_options.add_to_applications_menu = false; |
| install_options.add_to_desktop = false; |
| install_options.add_to_quick_launch_bar = false; |
| install_options.install_placeholder = true; |
| GetProvider().externally_managed_app_manager().Install( |
| std::move(install_options), |
| base::BindLambdaForTesting( |
| [&](const GURL& installed_app_url, |
| ExternallyManagedAppManager::InstallResult result) { |
| EXPECT_EQ(installed_app_url, app_url); |
| EXPECT_EQ(result.code, |
| webapps::InstallResultCode::kSuccessNewInstall); |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| return GetProvider() |
| .registrar_unsafe() |
| .LookupExternalAppId(app_url) |
| .value(); |
| } |
| |
| webapps::AppId InstallKioskApp() { |
| const GURL app_url = GetAppURL(); |
| base::RunLoop run_loop; |
| ExternalInstallOptions install_options(app_url, |
| mojom::UserDisplayMode::kStandalone, |
| ExternalInstallSource::kKiosk); |
| install_options.install_placeholder = true; |
| GetProvider().externally_managed_app_manager().Install( |
| std::move(install_options), |
| base::BindLambdaForTesting( |
| [&](const GURL& installed_app_url, |
| ExternallyManagedAppManager::InstallResult result) { |
| EXPECT_EQ(installed_app_url, app_url); |
| EXPECT_EQ(result.code, |
| webapps::InstallResultCode::kSuccessNewInstall); |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| return GetProvider() |
| .registrar_unsafe() |
| .LookupExternalAppId(app_url) |
| .value(); |
| } |
| |
| webapps::AppId InstallWebAppFromSync(const GURL& start_url) { |
| const webapps::AppId app_id = |
| GenerateAppId(/*manifest_id=*/std::nullopt, start_url); |
| |
| std::vector<std::unique_ptr<WebApp>> add_synced_apps_data; |
| { |
| auto synced_specifics_data = std::make_unique<WebApp>(app_id); |
| synced_specifics_data->SetStartUrl(start_url); |
| |
| synced_specifics_data->AddSource(WebAppManagement::kSync); |
| synced_specifics_data->SetUserDisplayMode( |
| mojom::UserDisplayMode::kBrowser); |
| synced_specifics_data->SetName("Name From Sync"); |
| |
| sync_pb::WebAppSpecifics sync_proto; |
| sync_proto.set_name("Name From Sync"); |
| sync_proto.set_theme_color(SK_ColorMAGENTA); |
| sync_proto.set_scope(GURL("https://example.com/sync_scope").spec()); |
| |
| apps::IconInfo apps_icon_info = CreateIconInfo( |
| /*icon_base_url=*/start_url, IconPurpose::MONOCHROME, 64); |
| sync_proto.mutable_icon_infos()->Add( |
| AppIconInfoToSyncProto(std::move(apps_icon_info))); |
| |
| synced_specifics_data->SetSyncProto(std::move(sync_proto)); |
| |
| add_synced_apps_data.push_back(std::move(synced_specifics_data)); |
| } |
| |
| WebAppTestInstallObserver observer(browser()->profile()); |
| |
| GetProvider().sync_bridge_unsafe().set_disable_checks_for_testing(true); |
| |
| sync_bridge_test_utils::AddApps(GetProvider().sync_bridge_unsafe(), |
| add_synced_apps_data); |
| |
| return observer.BeginListeningAndWait({app_id}); |
| } |
| |
| // Simulates what AppLauncherHandler::HandleInstallAppLocally() does. |
| void InstallAppLocally(const WebApp* web_app) { |
| base::test::TestFuture<void> future; |
| GetProvider().scheduler().InstallAppLocally(web_app->app_id(), |
| future.GetCallback()); |
| EXPECT_TRUE(future.Wait()); |
| } |
| |
| void SetTimeOverride(base::Time time_override) { |
| GetManifestUpdateManager(browser()->profile()) |
| .set_time_override_for_testing(time_override); |
| } |
| |
| ManifestUpdateResult GetResultAfterPageLoad(const GURL& url) { |
| UpdateCheckResultAwaiter awaiter(url); |
| EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| return std::move(awaiter).AwaitNextResult(); |
| } |
| |
| WebAppProvider& GetProvider() { |
| return *WebAppProvider::GetForTest(browser()->profile()); |
| } |
| |
| SkColor ReadAppIconPixel(const webapps::AppId& app_id, |
| SquareSizePx size, |
| int x = 0, |
| int y = 0) { |
| return IconManagerReadAppIconPixel( |
| WebAppProvider::GetForTest(browser()->profile())->icon_manager(), |
| app_id, size, x, y); |
| } |
| |
| void AcceptAppIdentityUpdateDialogForTesting() { |
| update_dialog_scope_ = |
| SetIdentityUpdateDialogActionForTesting(AppIdentityUpdate::kAllowed); |
| } |
| |
| void ResetAutomatedAppIdentityUpdateDialogBehavior() { |
| update_dialog_scope_ = |
| SetIdentityUpdateDialogActionForTesting(std::nullopt); |
| } |
| |
| protected: |
| net::EmbeddedTestServer::HandleRequestCallback request_override_; |
| |
| base::HistogramTester histogram_tester_; |
| |
| net::EmbeddedTestServer http_server_; |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| std::optional<base::RunLoop> shortcut_run_loop_; |
| // A vector mapping image sizes to shortcut colors. Note that the top left |
| // pixel color for each size is used as the representation color for that |
| // size, even if the image is multi-colored. |
| std::vector<std::pair<int, SkColor>> updated_colors_; |
| base::AutoReset<std::optional<AppIdentityUpdate>> update_dialog_scope_; |
| }; |
| |
| enum class UpdateDialogParam { |
| kDisabled = 0, |
| kEnabled = 1, |
| }; |
| |
| class ManifestUpdateManagerBrowserTest_UpdateDialog |
| : public ManifestUpdateManagerBrowserTest, |
| public testing::WithParamInterface<UpdateDialogParam> { |
| public: |
| ManifestUpdateManagerBrowserTest_UpdateDialog() { |
| std::vector<base::test::FeatureRef> enabled_features; |
| std::vector<base::test::FeatureRef> disabled_features; |
| if (IsUpdateDialogEnabled()) { |
| enabled_features.push_back(features::kPwaUpdateDialogForIcon); |
| } else { |
| disabled_features.push_back(features::kPwaUpdateDialogForIcon); |
| } |
| scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); |
| } |
| |
| bool IsUpdateDialogEnabled() const { |
| return GetParam() == UpdateDialogParam::kEnabled; |
| } |
| |
| static std::string ParamToString( |
| testing::TestParamInfo<UpdateDialogParam> param) { |
| switch (param.param) { |
| case UpdateDialogParam::kDisabled: |
| return "UpdateDialogDisabled"; |
| case UpdateDialogParam::kEnabled: |
| return "UpdateDialogEnabled"; |
| } |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckOutOfScopeNavigation) { |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kNoAppInScope); |
| |
| webapps::AppId app_id = InstallWebApp(); |
| |
| EXPECT_EQ(GetResultAfterPageLoad(GURL("https://example.org")), |
| ManifestUpdateResult::kNoAppInScope); |
| |
| histogram_tester_.ExpectTotalCount(kUpdateHistogramName, 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, CheckIsThrottled) { |
| constexpr base::TimeDelta kDelayBetweenChecks = base::Days(1); |
| base::Time time_override = base::Time::UnixEpoch(); |
| SetTimeOverride(time_override); |
| |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| |
| time_override += kDelayBetweenChecks / 2; |
| SetTimeOverride(time_override); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kThrottled); |
| |
| time_override += kDelayBetweenChecks; |
| SetTimeOverride(time_override); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| |
| time_override += kDelayBetweenChecks / 2; |
| SetTimeOverride(time_override); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kThrottled); |
| |
| time_override += kDelayBetweenChecks * 2; |
| SetTimeOverride(time_override); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kThrottled, 2); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 3); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckCancelledByWebContentsDestroyed) { |
| webapps::AppId app_id = InstallWebApp(); |
| GetManifestUpdateManager(browser()->profile()) |
| .hang_update_checks_for_testing(); |
| |
| GURL url = GetAppURL(); |
| UpdateCheckResultAwaiter awaiter(url); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| chrome::CloseTab(browser()); |
| EXPECT_EQ(std::move(awaiter).AwaitNextResult(), |
| ManifestUpdateResult::kWebContentsDestroyed); |
| histogram_tester_.ExpectBucketCount( |
| kUpdateHistogramName, ManifestUpdateResult::kWebContentsDestroyed, 1); |
| } |
| |
| // TODO(crbug.com/402699278): Fix and re-enable. |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| DISABLED_CheckCancelledByAppUninstalled) { |
| webapps::AppId app_id = InstallWebApp(); |
| GetManifestUpdateManager(browser()->profile()) |
| .hang_update_checks_for_testing(); |
| |
| GURL url = GetAppURL(); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| base::RunLoop run_loop; |
| UpdateCheckResultAwaiter awaiter(url); |
| GetProvider().scheduler().RemoveUserUninstallableManagements( |
| app_id, webapps::WebappUninstallSource::kAppMenu, |
| base::BindLambdaForTesting([&](webapps::UninstallResultCode code) { |
| EXPECT_EQ(code, webapps::UninstallResultCode::kAppRemoved); |
| run_loop.Quit(); |
| })); |
| |
| EXPECT_EQ(std::move(awaiter).AwaitNextResult(), |
| ManifestUpdateResult::kAppUninstalling); |
| |
| run_loop.Run(); |
| histogram_tester_.ExpectBucketCount( |
| kUpdateHistogramName, ManifestUpdateResult::kAppUninstalling, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| TriggersAfterLoadingNewManifestUrl) { |
| // Install an app with no manifest, trigger an update by navigation. |
| GURL no_manifest_url = GetAppURLWithoutManifest(); |
| const webapps::AppId app_id = InstallWebAppWithoutManifest(); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| UpdateCheckResultAwaiter result_awaiter(no_manifest_url); |
| DidFinishLoadObserver load_observer(web_contents, no_manifest_url); |
| EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), no_manifest_url)); |
| EXPECT_TRUE(load_observer.AwaitCorrectPageLoaded()); |
| EXPECT_TRUE(GetManifestUpdateManager(browser()->profile()) |
| .IsAppPendingPageAndManifestUrlLoadForTesting(app_id)); |
| EXPECT_TRUE(GetProvider().registrar_unsafe().IsDiyApp(app_id)); |
| |
| // Inject new manifest into the page once DidFinishLoad() is triggered. This |
| // should start the manifest checking command without the need for a refresh. |
| EXPECT_TRUE(content::ExecJs( |
| web_contents, |
| "addManifestLinkTag('/banners/manifest_for_no_manifest_page.json')")); |
| GURL newly_loaded_manifest_url = |
| http_server_.GetURL("/banners/manifest_for_no_manifest_page.json"); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| std::move(result_awaiter).AwaitNextResult()); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppManifestUrl(app_id), |
| newly_loaded_manifest_url); |
| EXPECT_FALSE(GetProvider().registrar_unsafe().IsDiyApp(app_id)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresWhitespaceDifferences) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, ""}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "\n\n\n\n"}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresNameChange) { |
| WebFeatureHistogramTester web_feature_histogram_tester; |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {"Different app name", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| EXPECT_EQ(0, web_feature_histogram_tester.GetCount( |
| blink::mojom::WebFeature::kWebAppManifestUpdate)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresShortNameChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "short_name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, |
| {"Short test app name", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {"Different short test app name", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| } |
| |
| // TODO(crbug.com/40231087): Test is flaky. |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_CheckNameUpdatesForDefaultApps \ |
| DISABLED_CheckNameUpdatesForDefaultApps |
| #else |
| #define MAYBE_CheckNameUpdatesForDefaultApps CheckNameUpdatesForDefaultApps |
| #endif |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| MAYBE_CheckNameUpdatesForDefaultApps) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList}); |
| webapps::AppId app_id = InstallDefaultApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {"Different app name", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppShortName(app_id), |
| "Different app name"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresStartUrlChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": "$1", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"a.html", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {"b.html", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppIdMismatch); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppIdMismatch, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresNoManifestChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresInvalidManifest) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, ""}); |
| webapps::AppId app_id = InstallWebApp(); |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, |
| "invalid manifest syntax !@#$%^*&()"}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppNotEligible); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppNotEligible, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresNonLocalApps) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "theme_color": "$2" |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // TODO(crbug.com/41490924): Instead of doing this, just install the |
| // app from sync in the first place to have it 'not locally installed' in the |
| // beginning. |
| GetProvider().sync_bridge_unsafe().SetAppNotLocallyInstalledForTesting( |
| app_id); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetInstallState(app_id), |
| proto::SUGGESTED_FROM_ANOTHER_DEVICE); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "red"}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kNoAppInScope); |
| histogram_tester_.ExpectTotalCount(kUpdateHistogramName, 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresPlaceholderApps) { |
| // Set up app URL to redirect to force placeholder app to install. |
| const GURL app_url = GetAppURL(); |
| request_override_ = base::BindLambdaForTesting( |
| [&app_url](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (request.GetURL() != app_url) |
| return nullptr; |
| auto http_response = |
| std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_code(net::HTTP_TEMPORARY_REDIRECT); |
| http_response->AddCustomHeader( |
| "Location", "http://other-origin.com/defaultresponse"); |
| http_response->set_content("redirect page"); |
| return std::move(http_response); |
| }); |
| |
| // Install via ExternallyManagedAppManager, the redirect to a different origin |
| // should cause it to install a placeholder app. |
| webapps::AppId app_id = InstallPolicyApp(); |
| EXPECT_TRUE(GetProvider().registrar_unsafe().IsPlaceholderApp( |
| app_id, WebAppManagement::kPolicy)); |
| |
| // Manifest updating should ignore non-redirect loads for placeholder apps |
| // because the ExternallyManagedAppManager will handle these. |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(app_url), |
| ManifestUpdateResult::kAppIsPlaceholder); |
| histogram_tester_.ExpectBucketCount( |
| kUpdateHistogramName, ManifestUpdateResult::kAppIsPlaceholder, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresPlaceholderAppsForKiosk) { |
| // Set up app URL to redirect to force placeholder app to install. |
| const GURL app_url = GetAppURL(); |
| request_override_ = base::BindLambdaForTesting( |
| [&app_url](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (request.GetURL() != app_url) |
| return nullptr; |
| auto http_response = |
| std::make_unique<net::test_server::BasicHttpResponse>(); |
| http_response->set_code(net::HTTP_TEMPORARY_REDIRECT); |
| http_response->AddCustomHeader( |
| "Location", "http://other-origin.com/defaultresponse"); |
| http_response->set_content("redirect page"); |
| return std::move(http_response); |
| }); |
| |
| // Install via ExternallyManagedAppManager, the redirect to a different origin |
| // should cause it to install a placeholder app. |
| webapps::AppId app_id = InstallKioskApp(); |
| EXPECT_TRUE(GetProvider().registrar_unsafe().IsPlaceholderApp( |
| app_id, WebAppManagement::kKiosk)); |
| |
| // Manifest updating should ignore non-redirect loads for placeholder apps |
| // because the ExternallyManagedAppManager will handle these. |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(app_url), |
| ManifestUpdateResult::kAppIsPlaceholder); |
| histogram_tester_.ExpectBucketCount( |
| kUpdateHistogramName, ManifestUpdateResult::kAppIsPlaceholder, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsThemeColorChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "theme_color": "$2" |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"}); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppThemeColor(app_id), |
| SK_ColorBLUE); |
| |
| // Check that OnWebAppInstalled and OnWebAppWillBeUninstalled are not called |
| // if in-place web app update happens. |
| WebAppInstallManagerObserverAdapter install_observer( |
| &GetProvider().install_manager()); |
| install_observer.SetWebAppInstalledDelegate(base::BindLambdaForTesting( |
| [](const webapps::AppId& app_id) { NOTREACHED(); })); |
| install_observer.SetWebAppUninstalledDelegate(base::BindLambdaForTesting( |
| [](const webapps::AppId& app_id) { NOTREACHED(); })); |
| |
| // CSS #RRGGBBAA syntax. |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "#00FF00F0"}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| |
| // Updated theme_color loses any transparency. |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppThemeColor(app_id), |
| SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsBackgroundColorChange) { |
| WebFeatureHistogramTester web_feature_histogram_tester; |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "background_color": "$2" |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"}); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppBackgroundColor(app_id), |
| SK_ColorBLUE); |
| |
| // CSS #RRGGBBAA syntax. |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "red"}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(1, web_feature_histogram_tester.GetCount( |
| blink::mojom::WebFeature::kWebAppManifestUpdate)); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppBackgroundColor(app_id), |
| SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsManifestUrlChange) { |
| // This matches the content of chrome/test/data/banners/manifest_one_icon.json |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Manifest test app", |
| "icons": [ |
| { |
| "src": "image-512px.png", |
| "sizes": "512x512", |
| "type": "image/png" |
| } |
| ], |
| "start_url": "manifest_test_page.html", |
| "display": "standalone", |
| "orientation": "landscape" |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {}); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppManifestUrl(app_id), |
| GetManifestURL()); |
| |
| // Load a page which contains the same manifest content but at a new manifest |
| // URL. |
| GURL::Replacements replacements; |
| replacements.SetQueryStr("manifest=/banners/manifest_one_icon.json"); |
| GURL app_url_with_new_manifest = GetAppURL().ReplaceComponents(replacements); |
| EXPECT_EQ(GetResultAfterPageLoad(app_url_with_new_manifest), |
| ManifestUpdateResult::kAppUpdated); |
| |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppManifestUrl(app_id), |
| http_server_.GetURL("/banners/manifest_one_icon.json")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, CheckKeepsSameName) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2, |
| "theme_color": "$3" |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, |
| {"App name 1", kInstallableIconList, "blue"}); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppThemeColor(app_id), |
| SK_ColorBLUE); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppShortName(app_id), |
| "App name 1"); |
| |
| OverrideManifest(kManifestTemplate, |
| {"App name 2", kInstallableIconList, "red"}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppThemeColor(app_id), |
| SK_ColorRED); |
| // The app name must not change without user confirmation. |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppShortName(app_id), |
| "App name 1"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckDoesNotFindIconUrlChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 0); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| } |
| |
| // TODO(crbug.com/40231087): Test is flaky. |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) |
| #define MAYBE_CheckDoesFindIconUrlChangeForDefaultApps \ |
| DISABLED_CheckDoesFindIconUrlChangeForDefaultApps |
| #else |
| #define MAYBE_CheckDoesFindIconUrlChangeForDefaultApps \ |
| CheckDoesFindIconUrlChangeForDefaultApps |
| #endif |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| MAYBE_CheckDoesFindIconUrlChangeForDefaultApps) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallDefaultApp(); |
| |
| OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors( |
| app_id, {{{32, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{48, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{64, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{96, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{128, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{256, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{512, kNotWin}, kAnotherInstallableIconTopLeftColor}}); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckUpdatedPolicyAppsNotUninstallable) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "theme_color": "$1", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"blue", kInstallableIconList}); |
| webapps::AppId app_id = InstallPolicyApp(); |
| EXPECT_FALSE(GetProvider().registrar_unsafe().CanUserUninstallWebApp(app_id)); |
| |
| OverrideManifest(kManifestTemplate, {"red", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| |
| // Policy installed apps should continue to be not uninstallable by the user |
| // after updating. |
| EXPECT_FALSE(GetProvider().registrar_unsafe().CanUserUninstallWebApp(app_id)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckUpdatedKioskAppsNotUninstallable) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "theme_color": "$1", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"blue", kInstallableIconList}); |
| webapps::AppId app_id = InstallKioskApp(); |
| EXPECT_FALSE(GetProvider().registrar_unsafe().CanUserUninstallWebApp(app_id)); |
| |
| OverrideManifest(kManifestTemplate, {"red", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| |
| // Kiosk installed apps should continue to be not uninstallable by the user |
| // after updating. |
| EXPECT_FALSE(GetProvider().registrar_unsafe().CanUserUninstallWebApp(app_id)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsScopeChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "$1", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"/banners/", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {"/", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppScope(app_id), |
| http_server_.GetURL("/")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| UninstallDialogClosesAppUpdateDialogUninstallsApp) { |
| ResetAutomatedAppIdentityUpdateDialogBehavior(); |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ("Test app name", |
| GetProvider().registrar_unsafe().GetAppShortName(app_id)); |
| |
| views::NamedWidgetShownWaiter manifest_waiter( |
| views::test::AnyWidgetTestPasskey{}, |
| "WebAppIdentityUpdateConfirmationView"); |
| OverrideManifest(kManifestTemplate, {"Test app name2", kInstallableIconList}); |
| |
| UpdateCheckResultAwaiter awaiter(GetAppURL()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetAppURL())); |
| |
| // Wait for the app identity update dialog to show up. |
| auto* manifest_update_widget = manifest_waiter.WaitIfNeededAndGet(); |
| EXPECT_NE(manifest_update_widget, nullptr); |
| |
| views::NamedWidgetShownWaiter uninstall_waiter( |
| views::test::AnyWidgetTestPasskey{}, "WebAppUninstallDialogDelegateView"); |
| |
| // Wait for the uninstall dialog to show up after cancelling the app identity |
| // update dialog. We cannot use views::test::CancelDialog here because under |
| // the hood, CancelDialog starts running a callback for the update dialog to |
| // be closed, which does not happen until uninstallation is scheduled, and |
| // uninstallation cannot be scheduled because the callback is running, thus |
| // leading to a deadlock. |
| manifest_update_widget->widget_delegate()->AsDialogDelegate()->CancelDialog(); |
| auto* uninstall_widget = uninstall_waiter.WaitIfNeededAndGet(); |
| EXPECT_NE(uninstall_widget, nullptr); |
| |
| // Accept the uninstall dialog, and verify changes are propagated back to the |
| // manifest update manager with the correct result. |
| views::test::AcceptDialog(uninstall_widget); |
| EXPECT_EQ(std::move(awaiter).AwaitNextResult(), |
| ManifestUpdateResult::kAppIdentityUpdateRejectedAndUninstalled); |
| |
| // This is to ensure that the uninstall command that was scheduled also |
| // completes. |
| GetProvider().command_manager().AwaitAllCommandsCompleteForTesting(); |
| EXPECT_FALSE(GetProvider().registrar_unsafe().IsInRegistrar(app_id)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| UninstallDialogCancelStillShowsAppUpdateDialog) { |
| ResetAutomatedAppIdentityUpdateDialogBehavior(); |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ("Test app name", |
| GetProvider().registrar_unsafe().GetAppShortName(app_id)); |
| |
| views::NamedWidgetShownWaiter manifest_waiter( |
| views::test::AnyWidgetTestPasskey{}, |
| "WebAppIdentityUpdateConfirmationView"); |
| OverrideManifest(kManifestTemplate, {"App Name 2", kInstallableIconList}); |
| |
| UpdateCheckResultAwaiter awaiter(GetAppURL()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetAppURL())); |
| |
| auto* manifest_update_widget = manifest_waiter.WaitIfNeededAndGet(); |
| EXPECT_NE(manifest_update_widget, nullptr); |
| |
| views::NamedWidgetShownWaiter uninstall_waiter( |
| views::test::AnyWidgetTestPasskey{}, "WebAppUninstallDialogDelegateView"); |
| manifest_update_widget->widget_delegate()->AsDialogDelegate()->CancelDialog(); |
| auto* uninstall_widget = uninstall_waiter.WaitIfNeededAndGet(); |
| EXPECT_NE(uninstall_widget, nullptr); |
| |
| views::test::WidgetVisibleWaiter update_visible_waiter( |
| manifest_update_widget); |
| |
| // If the uninstall dialog is cancelled, then app identity update dialog |
| // should regain visibility. |
| views::test::CancelDialog(uninstall_widget); |
| update_visible_waiter.Wait(); |
| views::test::AcceptDialog(manifest_update_widget); |
| EXPECT_EQ(std::move(awaiter).AwaitNextResult(), |
| ManifestUpdateResult::kAppUpdated); |
| EXPECT_EQ("App Name 2", |
| GetProvider().registrar_unsafe().GetAppShortName(app_id)); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_UpdateDialog, |
| ScopeChangeWithProductIconChange) { |
| // This test changes the scope and also the icon list. The scope should |
| // update. The icon should update only if identity updates are allowed. |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "$1", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"/banners/", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| if (IsUpdateDialogEnabled()) { |
| AcceptAppIdentityUpdateDialogForTesting(); |
| } |
| |
| OverrideManifest(kManifestTemplate, {"/", kAnotherInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| // The icon should be updated only if product icon updates are allowed. |
| if (IsUpdateDialogEnabled()) { |
| ConfirmShortcutColors( |
| app_id, {{{32, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{48, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{64, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{96, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{128, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{256, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{512, kNotWin}, kAnotherInstallableIconTopLeftColor}}); |
| } else { |
| ConfirmShortcutColors(app_id, |
| {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| } |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppScope(app_id), |
| http_server_.GetURL("/")); |
| } |
| |
| // TODO(crbug.com/40231087): Test is flaky. |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_CheckDoesApplyIconURLChangeForDefaultApps \ |
| DISABLED_CheckDoesApplyIconURLChangeForDefaultApps |
| #else |
| #define MAYBE_CheckDoesApplyIconURLChangeForDefaultApps \ |
| CheckDoesApplyIconURLChangeForDefaultApps |
| #endif |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| MAYBE_CheckDoesApplyIconURLChangeForDefaultApps) { |
| // This test changes the scope and also the icon list. The scope should update |
| // along with the icons. |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "$1", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"/banners/", kInstallableIconList}); |
| webapps::AppId app_id = InstallDefaultApp(); |
| |
| OverrideManifest(kManifestTemplate, {"/", kAnotherInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| // The icon should have updated. |
| ConfirmShortcutColors( |
| app_id, {{{32, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{48, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{64, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{96, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{128, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{256, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{512, kNotWin}, kAnotherInstallableIconTopLeftColor}}); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppScope(app_id), |
| http_server_.GetURL("/")); |
| } |
| |
| class ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate |
| : public ManifestUpdateManagerBrowserTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate() { |
| scoped_feature_list_.InitWithFeatureState( |
| features::kWebAppManifestPolicyAppIdentityUpdate, GetParam()); |
| } |
| |
| bool ExpectUpdateAllowed() { |
| return base::FeatureList::IsEnabled( |
| features::kWebAppManifestPolicyAppIdentityUpdate); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate, |
| CheckDoesApplyIconURLChangeForPolicyAppsWithFlag) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallPolicyApp(); |
| |
| OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList}); |
| |
| if (ExpectUpdateAllowed()) { |
| // The icon should have updated (because the flag is enabled). |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors( |
| app_id, {{{32, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{48, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{64, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{96, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{128, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{256, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{512, kNotWin}, kAnotherInstallableIconTopLeftColor}}); |
| } else { |
| // The icon should not have updated. |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 0); |
| ConfirmShortcutColors(app_id, |
| {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| } |
| } |
| |
| // This test ensures app name cannot be changed for policy apps (without a flag |
| // allowing it). |
| IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate, |
| CheckNameUpdatesForPolicyApps) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList}); |
| webapps::AppId app_id = InstallPolicyApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {"Different app name", kInstallableIconList}); |
| |
| if (ExpectUpdateAllowed()) { |
| // Name should have updated (because the flag is enabled). |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppShortName(app_id), |
| "Different app name"); |
| } else { |
| // Name should not have updated (because the flag is missing). |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 0); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppShortName(app_id), |
| "Test app name"); |
| } |
| } |
| |
| // This test ensures app icon url can always be changed for Kiosk apps. |
| IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate, |
| CheckDoesApplyIconURLChangeForKioskAppsIgnoringFlag) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallKioskApp(); |
| |
| OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList}); |
| |
| // The icon should have updated (regardless of flag status). |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors( |
| app_id, {{{32, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{48, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{64, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{96, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{128, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{256, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{512, kNotWin}, kAnotherInstallableIconTopLeftColor}}); |
| } |
| |
| // This test ensures app name can always be changed for Kiosk apps. |
| IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate, |
| CheckNameUpdatesForKioskApps) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList}); |
| webapps::AppId app_id = InstallKioskApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {"Different app name", kInstallableIconList}); |
| |
| // Name should have updated (regardless of flag status). |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppShortName(app_id), |
| "Different app name"); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(PolicyAppParameterizedTest, |
| ManifestUpdateManagerBrowserTest_PolicyAppsCanUpdate, |
| ::testing::Values(true, false)); |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsDisplayChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "$1", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"minimal-ui", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {"standalone", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppDisplayMode(app_id), |
| DisplayMode::kStandalone); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsDisplayBrowserChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "$1", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"standalone", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| GetProvider().sync_bridge_unsafe().SetAppUserDisplayModeForTesting( |
| app_id, mojom::UserDisplayMode::kStandalone); |
| |
| OverrideManifest(kManifestTemplate, {"browser", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppDisplayMode(app_id), |
| DisplayMode::kBrowser); |
| |
| // We don't touch the user's launch preference even if the app display mode |
| // changes. Instead the effective display mode changes. |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppUserDisplayMode(app_id), |
| mojom::UserDisplayMode::kStandalone); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppEffectiveDisplayMode(app_id), |
| DisplayMode::kMinimalUi); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsDisplayOverrideChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": $1, |
| "icons": $2 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, |
| {R"([ "fullscreen", "standalone" ])", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {R"([ "fullscreen", "minimal-ui" ])", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| |
| std::vector<DisplayMode> app_display_mode_override = |
| GetProvider().registrar_unsafe().GetAppDisplayModeOverride(app_id); |
| |
| ASSERT_EQ(2u, app_display_mode_override.size()); |
| EXPECT_EQ(DisplayMode::kFullscreen, app_display_mode_override[0]); |
| EXPECT_EQ(DisplayMode::kMinimalUi, app_display_mode_override[1]); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsNewDisplayOverride) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| $1 |
| "icons": $2 |
| } |
| )"; |
| |
| // No display_override in manifest |
| OverrideManifest(kManifestTemplate, {"", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // Add display_override field |
| OverrideManifest(kManifestTemplate, |
| {R"("display_override": [ "minimal-ui", "standalone" ],)", |
| kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| |
| std::vector<DisplayMode> app_display_mode_override = |
| GetProvider().registrar_unsafe().GetAppDisplayModeOverride(app_id); |
| |
| ASSERT_EQ(2u, app_display_mode_override.size()); |
| EXPECT_EQ(DisplayMode::kMinimalUi, app_display_mode_override[0]); |
| EXPECT_EQ(DisplayMode::kStandalone, app_display_mode_override[1]); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsDeletedDisplayOverride) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| $1 |
| "icons": $2 |
| } |
| )"; |
| |
| // Ensure display_override exists in initial manifest |
| OverrideManifest(kManifestTemplate, |
| {R"("display_override": [ "fullscreen", "minimal-ui" ],)", |
| kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // Remove display_override from manifest |
| OverrideManifest(kManifestTemplate, {"", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| |
| std::vector<DisplayMode> app_display_mode_override = |
| GetProvider().registrar_unsafe().GetAppDisplayModeOverride(app_id); |
| |
| ASSERT_EQ(0u, app_display_mode_override.size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsInvalidDisplayOverride) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": $1, |
| "icons": $2 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, |
| {R"([ "browser", "fullscreen" ])", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| ASSERT_EQ(2u, GetProvider() |
| .registrar_unsafe() |
| .GetAppDisplayModeOverride(app_id) |
| .size()); |
| |
| // display_override contains only invalid values |
| OverrideManifest(kManifestTemplate, |
| {R"( [ "invalid", 7 ])", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| |
| std::vector<DisplayMode> app_display_mode_override = |
| GetProvider().registrar_unsafe().GetAppDisplayModeOverride(app_id); |
| |
| ASSERT_EQ(0u, app_display_mode_override.size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresDisplayOverrideInvalidChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| $1 |
| "icons": $2 |
| } |
| )"; |
| |
| // No display_override in manifest |
| OverrideManifest(kManifestTemplate, {"", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // display_override contains only invalid values |
| OverrideManifest( |
| kManifestTemplate, |
| {R"("display_override": [ "invalid", 7 ],)", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresDisplayOverrideChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": $1, |
| "icons": $2 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, |
| {R"([ "standard", "fullscreen" ])", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // display_override contains an additional invalid value |
| OverrideManifest( |
| kManifestTemplate, |
| {R"([ "invalid", "standard", "fullscreen" ])", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckDoesNotFindIconContentChange) { |
| constexpr char kManifest[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": [ |
| { |
| "src": "/banners/192x192-green.png?ignore", |
| "sizes": "192x192", |
| "type": "image/png" |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifest, {}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // Replace the contents of 192x192-green.png with 192x192-red.png without |
| // changing the URL. |
| content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting( |
| [this](content::URLLoaderInterceptor::RequestParams* params) |
| -> bool /*intercepted*/ { |
| if (params->url_request.url == |
| http_server_.GetURL("/banners/192x192-green.png?ignore")) { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/banners/192x192-red.png", params->client.get()); |
| return true; |
| } |
| return false; |
| })); |
| |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 0); |
| |
| ConfirmShortcutColors(app_id, {{{32, kAll}, SK_ColorGREEN}, |
| {{48, kAll}, SK_ColorGREEN}, |
| {{64, kWin}, SK_ColorGREEN}, |
| {{96, kWin}, SK_ColorGREEN}, |
| {{128, kAll}, SK_ColorGREEN}, |
| {{256, kAll}, SK_ColorGREEN}}); |
| |
| EXPECT_EQ(ReadAppIconPixel(app_id, /*size=*/192), SK_ColorGREEN); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckDoesNotUpdateGeneratedIcons) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"[]"}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 0); |
| } |
| |
| // TODO(crbug.com/40250635): Flakes on multiple platforms. |
| IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_UpdateDialog, |
| DISABLED_CheckUpdateOfGeneratedIcons_SyncFailure) { |
| // The first "name" character is used to generate icons. Make it like a space |
| // to probe the background color at the center. Spaces are trimmed by the |
| // parser. |
| constexpr char kManifest[] = R"( |
| { |
| "name": "_Test App Name", |
| "start_url": "manifest_test_page.html", |
| "scope": "/", |
| "display": "$1", |
| "icons": [ |
| { |
| "src": "/web_apps/blue-192.png", |
| "sizes": "192x192", |
| "type": "image/png" |
| } |
| ] |
| } |
| )"; |
| |
| OverrideManifest(kManifest, {"standalone"}); |
| |
| webapps::AppId app_id; |
| |
| // Make blue-192.png fail to download for the first sync install. |
| { |
| std::unique_ptr<content::URLLoaderInterceptor> url_interceptor = |
| content::URLLoaderInterceptor::SetupRequestFailForURL( |
| http_server_.GetURL("/web_apps/blue-192.png"), |
| net::Error::ERR_FILE_NOT_FOUND); |
| |
| app_id = InstallWebAppFromSync(GetAppURL()); |
| } |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| ASSERT_TRUE(web_app); |
| EXPECT_TRUE(web_app->is_generated_icon()); |
| |
| // ManifestUpdateManager updates only locally installed apps. Installs web app |
| // locally on Win/Mac/Linux. |
| if (GetProvider().registrar_unsafe().GetInstallState(app_id) == |
| proto::InstallState::SUGGESTED_FROM_ANOTHER_DEVICE) { |
| InstallAppLocally(web_app); |
| } |
| |
| // Autogenerated icons in `ResizeIconsAndGenerateMissing()` use hardcoded dark |
| // gray color as background. |
| EXPECT_EQ(6u, web_app->downloaded_icon_sizes(IconPurpose::ANY).size()); |
| for (SquareSizePx size_px : |
| web_app->downloaded_icon_sizes(IconPurpose::ANY)) { |
| SCOPED_TRACE(size_px); |
| EXPECT_EQ(color_utils::SkColorToRgbaString(ReadAppIconPixel( |
| app_id, size_px, /*x=*/size_px / 2, /*y=*/size_px / 2)), |
| color_utils::SkColorToRgbaString(SK_ColorDKGRAY)); |
| } |
| |
| if (IsUpdateDialogEnabled()) { |
| AcceptAppIdentityUpdateDialogForTesting(); |
| } |
| |
| OverrideManifest(kManifest, {"browser"}); |
| |
| ManifestUpdateResult update_result = GetResultAfterPageLoad(GetAppURL()); |
| |
| ASSERT_EQ(web_app, GetProvider().registrar_unsafe().GetAppById(app_id)); |
| |
| EXPECT_EQ(update_result, ManifestUpdateResult::kAppUpdated); |
| |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| if (IsUpdateDialogEnabled()) { |
| // An actual icon was downloaded, so icon should not be autogenerated. |
| EXPECT_FALSE(web_app->is_generated_icon()); |
| // A non-generated icon was added, so expect 7 instead of 6. |
| EXPECT_EQ(7u, web_app->downloaded_icon_sizes(IconPurpose::ANY).size()); |
| // Icon should have turned blue. |
| for (SquareSizePx size_px : |
| web_app->downloaded_icon_sizes(IconPurpose::ANY)) { |
| SCOPED_TRACE(size_px); |
| EXPECT_EQ(color_utils::SkColorToRgbaString(ReadAppIconPixel( |
| app_id, size_px, /*x=*/size_px / 2, /*y=*/size_px / 2)), |
| color_utils::SkColorToRgbaString(SK_ColorBLUE)); |
| } |
| } else { |
| // Still autogenerated icons, no change. |
| EXPECT_TRUE(web_app->is_generated_icon()); |
| // Not 7u, no non-generated icon added. |
| EXPECT_EQ(6u, web_app->downloaded_icon_sizes(IconPurpose::ANY).size()); |
| // Not SK_ColorBLUE for blue-192.png. |
| for (SquareSizePx size_px : |
| web_app->downloaded_icon_sizes(IconPurpose::ANY)) { |
| SCOPED_TRACE(size_px); |
| EXPECT_EQ(color_utils::SkColorToRgbaString(ReadAppIconPixel( |
| app_id, size_px, /*x=*/size_px / 2, /*y=*/size_px / 2)), |
| color_utils::SkColorToRgbaString(SK_ColorDKGRAY)); |
| } |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ManifestUpdateManagerBrowserTest_UpdateDialog, |
| ::testing::Values(UpdateDialogParam::kEnabled, |
| UpdateDialogParam::kDisabled), |
| ManifestUpdateManagerBrowserTest_UpdateDialog::ParamToString); |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsLaunchHandlerChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "launch_handler": $2 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, R"({ |
| "client_mode": "focus-existing" |
| })"}); |
| webapps::AppId app_id = InstallWebApp(); |
| auto expected_launch_handler = |
| LaunchHandler{LaunchHandler::ClientMode::kFocusExisting}; |
| EXPECT_EQ( |
| expected_launch_handler, |
| GetProvider().registrar_unsafe().GetAppById(app_id)->launch_handler()); |
| EXPECT_TRUE(expected_launch_handler.client_mode_valid_and_specified()); |
| |
| // New launch_handler syntax. |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, R"({ |
| "client_mode": "navigate-existing" |
| })"}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| |
| expected_launch_handler = |
| LaunchHandler{LaunchHandler::ClientMode::kNavigateExisting}; |
| EXPECT_EQ( |
| expected_launch_handler, |
| GetProvider().registrar_unsafe().GetAppById(app_id)->launch_handler()); |
| EXPECT_TRUE(expected_launch_handler.client_mode_valid_and_specified()); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| class ManifestUpdateManagerSystemAppBrowserTest |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| ManifestUpdateManagerSystemAppBrowserTest() |
| : system_app_(ash::TestSystemWebAppInstallation:: |
| SetUpStandaloneSingleWindowApp()) {} |
| |
| void SetUpOnMainThread() override { system_app_->WaitForAppInstall(); } |
| |
| protected: |
| std::unique_ptr<ash::TestSystemWebAppInstallation> system_app_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerSystemAppBrowserTest, |
| CheckUpdateSkipped) { |
| webapps::AppId app_id = system_app_->GetAppId(); |
| EXPECT_EQ(GetResultAfterPageLoad(system_app_->GetAppUrl()), |
| ManifestUpdateResult::kAppIsSystemWebApp); |
| |
| histogram_tester_.ExpectBucketCount( |
| kUpdateHistogramName, ManifestUpdateResult::kAppIsSystemWebApp, 1); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppThemeColor(app_id), |
| SK_ColorGREEN); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| class ManifestUpdateManagerIsolatedWebAppBrowserTest |
| : public IsolatedWebAppBrowserTestHarness { |
| protected: |
| base::HistogramTester histogram_tester_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerIsolatedWebAppBrowserTest, |
| CheckUpdateSkipped) { |
| std::unique_ptr<ScopedBundledIsolatedWebApp> app = |
| IsolatedWebAppBuilder(ManifestBuilder().SetStartUrl("/index.html")) |
| .AddHtml("/index.html", "<html></html>") |
| .BuildBundle(); |
| ASSERT_OK_AND_ASSIGN(IsolatedWebAppUrlInfo url_info, app->Install(profile())); |
| |
| UpdateCheckResultAwaiter awaiter( |
| url_info.origin().GetURL().Resolve("/index.html")); |
| EXPECT_TRUE(OpenApp(url_info.app_id())); |
| EXPECT_EQ(std::move(awaiter).AwaitNextResult(), |
| ManifestUpdateResult::kAppIsIsolatedWebApp); |
| |
| histogram_tester_.ExpectBucketCount( |
| kUpdateHistogramName, ManifestUpdateResult::kAppIsIsolatedWebApp, 1); |
| } |
| |
| using ManifestUpdateManagerWebAppsBrowserTest = |
| ManifestUpdateManagerBrowserTest; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest, |
| CheckFindsAddedShareTarget) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kShareTargetManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "share_target": { |
| "action": "/web_share_target/share.html", |
| "method": "GET", |
| "params": { |
| "url": "link" |
| } |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kShareTargetManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_TRUE(web_app->share_target().has_value()); |
| EXPECT_EQ(web_app->share_target()->method, apps::ShareTarget::Method::kGet); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest, |
| CheckFindsShareTargetChange) { |
| constexpr char kShareTargetManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "share_target": { |
| "action": "/web_share_target/share.html", |
| "method": "$1", |
| "params": { |
| "url": "link" |
| } |
| }, |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kShareTargetManifestTemplate, {"GET", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kShareTargetManifestTemplate, |
| {"POST", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_TRUE(web_app->share_target().has_value()); |
| EXPECT_EQ(web_app->share_target()->method, apps::ShareTarget::Method::kPost); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerWebAppsBrowserTest, |
| CheckFindsDeletedShareTarget) { |
| constexpr char kShareTargetManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "share_target": { |
| "action": "/web_share_target/share.html", |
| "method": "GET", |
| "params": { |
| "url": "link" |
| } |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kShareTargetManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_FALSE(web_app->share_target().has_value()); |
| } |
| |
| // Functional tests. More tests for detecting file handler updates are |
| // available in unit tests at ManifestUpdateDataFetchUtilsTest. |
| class ManifestUpdateManagerBrowserTestWithFileHandling |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| ManifestUpdateManagerBrowserTestWithFileHandling() = default; |
| |
| private: |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling, |
| CheckFindsAddedFileHandler) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kFileHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "file_handlers": [ |
| { |
| "action": "/?plaintext", |
| "name": "Plain Text", |
| "accept": { |
| "text/plain": [".txt"] |
| } |
| } |
| ], |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_FALSE(web_app->file_handlers().empty()); |
| const auto& file_handler = web_app->file_handlers()[0]; |
| EXPECT_EQ("plaintext", file_handler.action.query()); |
| EXPECT_EQ(1u, file_handler.accept.size()); |
| EXPECT_EQ("text/plain", file_handler.accept[0].mime_type); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling, |
| CheckIgnoresUnchangedFileHandler) { |
| constexpr char kFileHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "file_handlers": [ |
| { |
| "action": "/?plaintext", |
| "name": "Plain Text", |
| "accept": { |
| "text/plain": [".txt"] |
| } |
| } |
| ], |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpToDate, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_FALSE(web_app->file_handlers().empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling, |
| CheckFindsChangedFileExtension) { |
| constexpr char kFileHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "file_handlers": [ |
| { |
| "action": "/?plaintext", |
| "name": "Plain Text", |
| "accept": { |
| "text/plain": ["$1"] |
| } |
| } |
| ], |
| "icons": $2 |
| } |
| )"; |
| |
| OverrideManifest(kFileHandlerManifestTemplate, |
| {".txt", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| const auto& old_file_handler = web_app->file_handlers()[0]; |
| EXPECT_EQ(1u, old_file_handler.accept.size()); |
| auto old_extensions = old_file_handler.accept[0].file_extensions; |
| EXPECT_EQ(1u, old_extensions.size()); |
| EXPECT_TRUE(base::Contains(old_extensions, ".txt")); |
| |
| OverrideManifest(kFileHandlerManifestTemplate, {".md", kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| const auto& new_file_handler = web_app->file_handlers()[0]; |
| EXPECT_EQ(1u, new_file_handler.accept.size()); |
| auto new_extensions = new_file_handler.accept[0].file_extensions; |
| EXPECT_EQ(1u, new_extensions.size()); |
| EXPECT_TRUE(base::Contains(new_extensions, ".md")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling, |
| FileHandlingPermissionResetsOnUpdate) { |
| constexpr char kFileHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "file_handlers": [ |
| { |
| "action": "/?plaintext", |
| "name": "Plain Text", |
| "accept": { |
| "text/plain": ["$1"] |
| } |
| } |
| ], |
| "icons": [ |
| { |
| "src": "launcher-icon-4x.png", |
| "sizes": "192x192", |
| "type": "image/png" |
| } |
| ], |
| "theme_color": "$2" |
| } |
| )"; |
| |
| OverrideManifest(kFileHandlerManifestTemplate, {".txt", "red"}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| const auto& old_file_handler = web_app->file_handlers()[0]; |
| auto old_extensions = old_file_handler.accept[0].file_extensions; |
| EXPECT_TRUE(base::Contains(old_extensions, ".txt")); |
| const GURL url = GetAppURL(); |
| const GURL origin = url.DeprecatedGetOriginAsURL(); |
| |
| EXPECT_EQ(ApiApprovalState::kRequiresPrompt, |
| GetProvider().registrar_unsafe().GetAppFileHandlerUserApprovalState( |
| app_id)); |
| GetProvider().sync_bridge_unsafe().SetAppFileHandlerApprovalState( |
| app_id, ApiApprovalState::kAllowed); |
| |
| // Update manifest, adding an extension to the file handler. Permission should |
| // be downgraded to ASK. The time override is necessary to make sure the |
| // manifest update isn't skipped due to throttling. |
| base::Time time_override = base::Time::Now(); |
| SetTimeOverride(time_override); |
| OverrideManifest(kFileHandlerManifestTemplate, {".md\", \".txt", "red"}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, GetResultAfterPageLoad(url)); |
| auto new_extensions = web_app->file_handlers()[0].accept[0].file_extensions; |
| EXPECT_TRUE(base::Contains(new_extensions, ".md")); |
| EXPECT_TRUE(base::Contains(new_extensions, ".txt")); |
| |
| // Set back to allowed. |
| EXPECT_EQ(ApiApprovalState::kRequiresPrompt, |
| GetProvider().registrar_unsafe().GetAppFileHandlerUserApprovalState( |
| app_id)); |
| GetProvider().sync_bridge_unsafe().SetAppFileHandlerApprovalState( |
| app_id, ApiApprovalState::kAllowed); |
| |
| // Update manifest, but keep same file handlers. Permission should be left on |
| // ALLOW. |
| time_override += base::Days(10); |
| SetTimeOverride(time_override); |
| OverrideManifest(kFileHandlerManifestTemplate, {".md\", \".txt", "blue"}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, GetResultAfterPageLoad(url)); |
| new_extensions = web_app->file_handlers()[0].accept[0].file_extensions; |
| EXPECT_TRUE(base::Contains(new_extensions, ".md")); |
| EXPECT_TRUE(base::Contains(new_extensions, ".txt")); |
| |
| EXPECT_EQ(ApiApprovalState::kAllowed, |
| GetProvider().registrar_unsafe().GetAppFileHandlerUserApprovalState( |
| app_id)); |
| |
| // Update manifest, asking for /fewer/ file types. Permission should be left |
| // on ALLOW. |
| time_override += base::Days(10); |
| SetTimeOverride(time_override); |
| OverrideManifest(kFileHandlerManifestTemplate, {".txt", "blue"}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, GetResultAfterPageLoad(url)); |
| new_extensions = web_app->file_handlers()[0].accept[0].file_extensions; |
| EXPECT_FALSE(base::Contains(new_extensions, ".md")); |
| EXPECT_TRUE(base::Contains(new_extensions, ".txt")); |
| EXPECT_EQ(ApiApprovalState::kAllowed, |
| GetProvider().registrar_unsafe().GetAppFileHandlerUserApprovalState( |
| app_id)); |
| |
| #if BUILDFLAG(IS_LINUX) |
| // Make sure that blocking the permission also unregisters the MIME type on |
| // Linux. |
| SetUpdateMimeInfoDatabaseOnLinuxCallbackForTesting(base::BindLambdaForTesting( |
| [](base::FilePath filename, std::string xdg_command, |
| std::string file_contents) { |
| EXPECT_TRUE(file_contents.empty()) << "'" << file_contents << "'"; |
| return true; |
| })); |
| #endif |
| |
| // Block the permission, update manifest, permission should still be block. |
| GetProvider().sync_bridge_unsafe().SetAppFileHandlerApprovalState( |
| app_id, ApiApprovalState::kDisallowed); |
| OverrideManifest(kFileHandlerManifestTemplate, {".txt", "red"}); |
| time_override += base::Days(10); |
| SetTimeOverride(time_override); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, GetResultAfterPageLoad(url)); |
| EXPECT_EQ(ApiApprovalState::kDisallowed, |
| GetProvider().registrar_unsafe().GetAppFileHandlerUserApprovalState( |
| app_id)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling, |
| BlockedPermissionPreservedOnUpdate) { |
| constexpr char kFileHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "file_handlers": [ |
| { |
| "action": "/?plaintext", |
| "name": "Plain Text", |
| "accept": { |
| "text/plain": ["$1"] |
| } |
| } |
| ], |
| "icons": $2 |
| } |
| )"; |
| |
| OverrideManifest(kFileHandlerManifestTemplate, |
| {".txt", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| WebAppRegistrar& registrar = GetProvider().registrar_unsafe(); |
| const WebApp* web_app = registrar.GetAppById(app_id); |
| |
| ASSERT_FALSE(web_app->file_handlers().empty()); |
| const auto& old_file_handler = web_app->file_handlers()[0]; |
| ASSERT_FALSE(old_file_handler.accept.empty()); |
| auto old_extensions = old_file_handler.accept[0].file_extensions; |
| EXPECT_TRUE(base::Contains(old_extensions, ".txt")); |
| const GURL url = GetAppURL(); |
| const GURL origin = url.DeprecatedGetOriginAsURL(); |
| |
| // Disallow the API. |
| EXPECT_EQ(ApiApprovalState::kRequiresPrompt, |
| GetProvider().registrar_unsafe().GetAppFileHandlerUserApprovalState( |
| app_id)); |
| GetProvider().sync_bridge_unsafe().SetAppFileHandlerApprovalState( |
| app_id, ApiApprovalState::kDisallowed); |
| |
| // Update manifest. |
| OverrideManifest(kFileHandlerManifestTemplate, {".md", kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, GetResultAfterPageLoad(url)); |
| |
| // Manifest update task should preserve the permission blocked state. |
| EXPECT_EQ(ApiApprovalState::kDisallowed, |
| registrar.GetAppById(app_id)->file_handler_approval_state()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling, |
| CheckFindsDeletedFileHandler) { |
| constexpr char kFileHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "file_handlers": [ |
| { |
| "action": "/?plaintext", |
| "name": "Plain Text", |
| "accept": { |
| "text/plain": [".txt"] |
| } |
| } |
| ], |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| auto* file_handlers = |
| GetProvider().registrar_unsafe().GetAppFileHandlers(app_id); |
| ASSERT_TRUE(file_handlers); |
| EXPECT_TRUE(file_handlers->empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling, |
| CheckFileExtensionList) { |
| constexpr char kFileHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "file_handlers": [ |
| { |
| "action": "/?plaintext", |
| "name": "Plain Text", |
| "accept": { |
| "text/plain": [".txt"] |
| } |
| } |
| ], |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| auto [associations_list, association_count] = |
| GetFileTypeAssociationsHandledByWebAppForDisplay(browser()->profile(), |
| app_id); |
| EXPECT_EQ(u"TXT", associations_list); |
| EXPECT_EQ(1U, association_count); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling, |
| CheckFileExtensionsList) { |
| constexpr char kFileHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "file_handlers": [ |
| { |
| "action": "/?plaintext", |
| "name": "Plain Text", |
| "accept": { |
| "text/plain": [".txt", ".md"] |
| } |
| } |
| ], |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| auto [associations_list, association_count] = |
| GetFileTypeAssociationsHandledByWebAppForDisplay(browser()->profile(), |
| app_id); |
| EXPECT_EQ(u"MD, TXT", associations_list); |
| EXPECT_EQ(2U, association_count); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling, |
| CheckFileExtensionsListWithTwoFileHandlers) { |
| constexpr char kFileHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "file_handlers": [ |
| { |
| "action": "/?plaintext", |
| "name": "Plain Text", |
| "accept": { |
| "text/plain": [".txt"] |
| } |
| }, |
| { |
| "action": "/?longtype", |
| "name": "Long Custom type", |
| "accept": { |
| "application/long-type": [".longtype"] |
| } |
| } |
| ], |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kFileHandlerManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| auto [associations_list, association_count] = |
| GetFileTypeAssociationsHandledByWebAppForDisplay(browser()->profile(), |
| app_id); |
| EXPECT_EQ(u"LONGTYPE, TXT", associations_list); |
| EXPECT_EQ(2U, association_count); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsShortcutsMenuUpdated) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "shortcuts": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, kShortcutsItem}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, kShortcutsItems}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider() |
| .registrar_unsafe() |
| .GetAppShortcutsMenuItemInfos(app_id) |
| .size(), |
| 2u); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsItemNameUpdated) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "shortcuts": [ |
| { |
| "name": "$2", |
| "short_name": "HM", |
| "description": "Go home", |
| "url": ".", |
| "icons": [ |
| { |
| "src": "/banners/image-512px.png", |
| "sizes": "512x512", |
| "type": "image/png" |
| } |
| ] |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "Home"}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {kInstallableIconList, kAnotherShortcutsItemName}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider() |
| .registrar_unsafe() |
| .GetAppShortcutsMenuItemInfos(app_id)[0] |
| .name, |
| kAnotherShortcutsItemName16); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresShortNameAndDescriptionChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "shortcuts": [ |
| { |
| "name": "Home", |
| "short_name": "$2", |
| "description": "$3", |
| "url": ".", |
| "icons": [ |
| { |
| "src": "/banners/image-512px.png", |
| "sizes": "512x512", |
| "type": "image/png" |
| } |
| ] |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "HM", "Go home"}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {kInstallableIconList, kAnotherShortcutsItemShortName, |
| kAnotherShortcutsItemDescription}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsItemUrlUpdated) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "shortcuts": [ |
| { |
| "name": "Home", |
| "short_name": "HM", |
| "description": "Go home", |
| "url": "$2", |
| "icons": [ |
| { |
| "src": "/banners/image-512px.png", |
| "sizes": "512x512", |
| "type": "image/png" |
| } |
| ] |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "/"}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {kInstallableIconList, kAnotherShortcutsItemUrl}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider() |
| .registrar_unsafe() |
| .GetAppShortcutsMenuItemInfos(app_id)[0] |
| .url, |
| http_server_.GetURL(kAnotherShortcutsItemUrl)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsShortcutIconContentChange) { |
| constexpr char kManifest[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "shortcuts": [ |
| { |
| "name": "Home", |
| "short_name": "HM", |
| "description": "Go home", |
| "url": "/", |
| "icons": [ |
| { |
| "src": "/web_apps/basic-192.png?ignore", |
| "sizes": "192x192", |
| "type": "image/png" |
| } |
| ] |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifest, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // Replace the contents of basic-192.png with blue-192.png without changing |
| // the URL. |
| content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting( |
| [this](content::URLLoaderInterceptor::RequestParams* params) |
| -> bool /*intercepted*/ { |
| if (params->url_request.url == |
| http_server_.GetURL("/web_apps/basic-192.png?ignore")) { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/web_apps/blue-192.png", params->client.get()); |
| return true; |
| } |
| return false; |
| })); |
| |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| // Check that the installed icon is now blue. |
| base::RunLoop run_loop; |
| GetProvider().icon_manager().ReadAllShortcutsMenuIcons( |
| app_id, |
| base::BindLambdaForTesting( |
| [&run_loop](ShortcutsMenuIconBitmaps shortcuts_menu_icon_bitmaps) { |
| run_loop.Quit(); |
| EXPECT_EQ(shortcuts_menu_icon_bitmaps[0].any.at(192).getColor(0, 0), |
| SK_ColorBLUE); |
| })); |
| run_loop.Run(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIconChangeForOemInstallation) { |
| constexpr char kNewName[] = "New app name"; |
| constexpr char kManifest[] = R"( |
| { |
| "name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": [ |
| { |
| "src": "/web_apps/basic-192.png?ignore", |
| "sizes": "192x192", |
| "type": "image/png" |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifest, {kNewName, kInstallableIconList}); |
| webapps::AppId app_id = InstallOemWebApp(); |
| |
| // Replace the contents of basic-192.png with blue-192.png without changing |
| // the URL. |
| content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting( |
| [this](content::URLLoaderInterceptor::RequestParams* params) |
| -> bool /*intercepted*/ { |
| if (params->url_request.url == |
| http_server_.GetURL("/web_apps/basic-192.png?ignore")) { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/web_apps/blue-192.png", params->client.get()); |
| return true; |
| } |
| return false; |
| })); |
| |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| EXPECT_EQ(kNewName, GetProvider().registrar_unsafe().GetAppShortName(app_id)); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| // Check that the installed icon is now blue. |
| EXPECT_EQ(ReadAppIconPixel(app_id, /*size=*/192), SK_ColorBLUE); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_UpdateDialog, |
| ShortcutIconContentChangeWithProductIconChange) { |
| // This test changes the shortuct icon contents and also the product icon |
| // list. The shortcut icons should update. The icon should update only if |
| // identity updates are allowed. |
| constexpr char kManifest[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "shortcuts": [ |
| { |
| "name": "Home", |
| "short_name": "HM", |
| "description": "Go home", |
| "url": "/", |
| "icons": [ |
| { |
| "src": "/web_apps/basic-192.png?ignore", |
| "sizes": "192x192", |
| "type": "image/png" |
| } |
| ] |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifest, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // Replace the contents of basic-192.png with blue-192.png without changing |
| // the URL. |
| content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting( |
| [this](content::URLLoaderInterceptor::RequestParams* params) |
| -> bool /*intercepted*/ { |
| if (params->url_request.url == |
| http_server_.GetURL("/web_apps/basic-192.png?ignore")) { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/web_apps/blue-192.png", params->client.get()); |
| return true; |
| } |
| return false; |
| })); |
| |
| if (IsUpdateDialogEnabled()) { |
| AcceptAppIdentityUpdateDialogForTesting(); |
| } |
| |
| OverrideManifest(kManifest, {kAnotherInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| // The icon should be updated only if product icon updates are allowed. |
| if (IsUpdateDialogEnabled()) { |
| ConfirmShortcutColors( |
| app_id, {{{32, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{48, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{64, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{96, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{128, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{256, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{512, kNotWin}, kAnotherInstallableIconTopLeftColor}}); |
| } else { |
| ConfirmShortcutColors(app_id, |
| {{{32, kAll}, kInstallableIconTopLeftColor}, |
| {{48, kAll}, kInstallableIconTopLeftColor}, |
| {{64, kWin}, kInstallableIconTopLeftColor}, |
| {{96, kWin}, kInstallableIconTopLeftColor}, |
| {{128, kAll}, kInstallableIconTopLeftColor}, |
| {{256, kAll}, kInstallableIconTopLeftColor}}); |
| } |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsShortcutIconSrcUpdated) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "shortcuts": [ |
| { |
| "name": "Home", |
| "short_name": "HM", |
| "description": "Go home", |
| "url": ".", |
| "icons": [ |
| { |
| "src": "$2", |
| "sizes": "512x512", |
| "type": "image/png" |
| } |
| ] |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, |
| {kInstallableIconList, "/banners/image-512px.png"}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, kAnotherIconSrc}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider() |
| .registrar_unsafe() |
| .GetAppShortcutsMenuItemInfos(app_id)[0] |
| .GetShortcutIconInfosForPurpose(IconPurpose::ANY)[0] |
| .url, |
| http_server_.GetURL(kAnotherIconSrc)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsShortcutIconSizesUpdated) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "shortcuts": [ |
| { |
| "name": "Home", |
| "short_name": "HM", |
| "description": "Go home", |
| "url": ".", |
| "icons": [ |
| { |
| "src": "/banners/image-512px.png", |
| "sizes": "$2", |
| "type": "image/png" |
| } |
| ] |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "512x512"}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, |
| {kInstallableIconList, |
| gfx::Size(kAnotherIconSize, kAnotherIconSize).ToString()}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetProvider() |
| .registrar_unsafe() |
| .GetAppShortcutsMenuItemInfos(app_id)[0] |
| .GetShortcutIconInfosForPurpose(IconPurpose::ANY)[0] |
| .square_size_px, |
| kAnotherIconSize); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckUpdateTimeChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "theme_color": "$2" |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "blue"}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| base::Time manifest_update_time = web_app->manifest_update_time(); |
| |
| // CSS #RRGGBBAA syntax. |
| OverrideManifest(kManifestTemplate, {kInstallableIconList, "#00FF00F0"}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| |
| // Update time is updated. |
| EXPECT_LT(manifest_update_time, web_app->manifest_update_time()); |
| } |
| |
| class ManifestUpdateManagerIconUpdatingBrowserTest |
| : public ManifestUpdateManagerBrowserTest { |
| base::test::ScopedFeatureList scoped_feature_list_{ |
| features::kWebAppManifestIconUpdating}; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest_UpdateDialog, |
| CheckFindsIconContentChange) { |
| constexpr char kManifest[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": [ |
| { |
| "src": "/banners/256x256-green.png?ignore", |
| "sizes": "256x256", |
| "type": "image/png" |
| } |
| ] |
| } |
| )"; |
| |
| if (IsUpdateDialogEnabled()) { |
| AcceptAppIdentityUpdateDialogForTesting(); |
| } |
| |
| OverrideManifest(kManifest, {}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // Replace the green icon with a red icon without changing the URL. |
| content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting( |
| [this](content::URLLoaderInterceptor::RequestParams* params) |
| -> bool /*intercepted*/ { |
| if (params->url_request.url == |
| http_server_.GetURL("/banners/256x256-green.png?ignore")) { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/banners/256x256-red.png", params->client.get()); |
| return true; |
| } |
| return false; |
| })); |
| |
| if (IsUpdateDialogEnabled()) { |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| // The icon should have changed, as the file has been updated (but the url |
| // is the same). |
| ConfirmShortcutColors(app_id, {{{32, kAll}, SK_ColorRED}, |
| {{48, kAll}, SK_ColorRED}, |
| {{64, kWin}, SK_ColorRED}, |
| {{96, kWin}, SK_ColorRED}, |
| {{128, kAll}, SK_ColorRED}, |
| {{256, kAll}, SK_ColorRED}}); |
| |
| EXPECT_EQ(ReadAppIconPixel(app_id, /*size=*/256), SK_ColorRED); |
| } else { |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 0); |
| ConfirmShortcutColors(app_id, {{{32, kAll}, SK_ColorGREEN}, |
| {{48, kAll}, SK_ColorGREEN}, |
| {{64, kWin}, SK_ColorGREEN}, |
| {{96, kWin}, SK_ColorGREEN}, |
| {{128, kAll}, SK_ColorGREEN}, |
| {{256, kAll}, SK_ColorGREEN}}); |
| |
| EXPECT_EQ(ReadAppIconPixel(app_id, /*size=*/256), SK_ColorGREEN); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerIconUpdatingBrowserTest, |
| CheckFindsIconUrlChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {kAnotherInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| histogram_tester_.ExpectBucketCount("WebApp.Icon.DownloadedResultOnUpdate", |
| IconsDownloadedResult::kCompleted, 1); |
| |
| histogram_tester_.ExpectBucketCount( |
| "WebApp.Icon.DownloadedHttpStatusCodeOnUpdate", |
| net::HttpStatusCode::HTTP_OK, 1); |
| |
| // The icon should have changed. |
| ConfirmShortcutColors( |
| app_id, {{{32, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{48, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{64, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{96, kWin}, kAnotherInstallableIconTopLeftColor}, |
| {{128, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{256, kAll}, kAnotherInstallableIconTopLeftColor}, |
| {{512, kNotWin}, kAnotherInstallableIconTopLeftColor}}); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerIconUpdatingBrowserTest, |
| CheckIgnoresIconDownloadFail) { |
| constexpr char kManifest[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": [ |
| { |
| "src": "/web_apps/basic-48.png?ignore", |
| "sizes": "48x48", |
| "type": "image/png" |
| }, |
| { |
| "src": "/web_apps/basic-192.png?ignore", |
| "sizes": "192x192", |
| "type": "image/png" |
| } |
| ] |
| } |
| )"; |
| OverrideManifest(kManifest, {}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| histogram_tester_.ExpectBucketCount("WebApp.Icon.DownloadedResultOnCreate", |
| IconsDownloadedResult::kCompleted, 1); |
| |
| histogram_tester_.ExpectBucketCount( |
| "WebApp.Icon.DownloadedHttpStatusCodeOnCreate", |
| net::HttpStatusCode::HTTP_OK, 1); |
| |
| // Make basic-48.png fail to download. |
| // Replace the contents of basic-192.png with blue-192.png without changing |
| // the URL. |
| content::URLLoaderInterceptor url_interceptor(base::BindLambdaForTesting( |
| [this](content::URLLoaderInterceptor::RequestParams* params) |
| -> bool /*intercepted*/ { |
| if (params->url_request.url == |
| http_server_.GetURL("/web_apps/basic-48.png?ignore")) { |
| content::URLLoaderInterceptor::WriteResponse("malformed response", "", |
| params->client.get()); |
| return true; |
| } |
| if (params->url_request.url == |
| http_server_.GetURL("/web_apps/basic-192.png?ignore")) { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/web_apps/blue-192.png", params->client.get()); |
| return true; |
| } |
| return false; |
| })); |
| |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kIconDownloadFailed); |
| histogram_tester_.ExpectBucketCount( |
| kUpdateHistogramName, ManifestUpdateResult::kIconDownloadFailed, 1); |
| |
| // The `url_interceptor` above can't simulate net::HttpStatusCode error |
| // properly, WebApp.Icon.DownloadedHttpStatusCodeOnUpdate left untested here. |
| histogram_tester_.ExpectBucketCount( |
| "WebApp.Icon.DownloadedResultOnUpdate", |
| IconsDownloadedResult::kAbortedDueToFailure, 1); |
| |
| // Since one request failed, none of the icons should be updated. So the '192' |
| // size here is not updated to blue. |
| EXPECT_EQ(ReadAppIconPixel(app_id, /*size=*/48), SK_ColorBLACK); |
| EXPECT_EQ(ReadAppIconPixel(app_id, /*size=*/192), SK_ColorBLACK); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsAddedProtocolHandler) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kProtocolHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "protocol_handlers": [ |
| { |
| "protocol": "mailto", |
| "url": "?mailto=%s" |
| } |
| ], |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kProtocolHandlerManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_FALSE(web_app->protocol_handlers().empty()); |
| const auto& protocol_handler = web_app->protocol_handlers()[0]; |
| EXPECT_EQ("mailto", protocol_handler.protocol); |
| EXPECT_EQ(http_server_.GetURL("/banners/manifest.json?mailto=%s"), |
| protocol_handler.url.spec()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresUnchangedProtocolHandler) { |
| constexpr char kProtocolHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "protocol_handlers": [ |
| { |
| "protocol": "mailto", |
| "url": "?mailto=%s" |
| } |
| ], |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kProtocolHandlerManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kProtocolHandlerManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpToDate, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_FALSE(web_app->protocol_handlers().empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsChangedProtocolHandler) { |
| constexpr char kProtocolHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "protocol_handlers": [ |
| { |
| "protocol": "$1", |
| "url": "?$2=%s" |
| } |
| ], |
| "icons": $3 |
| } |
| )"; |
| |
| OverrideManifest(kProtocolHandlerManifestTemplate, |
| {"mailto", "mailto", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_EQ(1u, web_app->protocol_handlers().size()); |
| const auto& old_protocol_handler = web_app->protocol_handlers()[0]; |
| EXPECT_EQ("mailto", old_protocol_handler.protocol); |
| EXPECT_EQ(http_server_.GetURL("/banners/manifest.json?mailto=%s"), |
| old_protocol_handler.url.spec()); |
| |
| OverrideManifest(kProtocolHandlerManifestTemplate, |
| {"web+mailto", "web+mailto", kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| EXPECT_EQ(1u, web_app->protocol_handlers().size()); |
| const auto& new_protocol_handler = web_app->protocol_handlers()[0]; |
| EXPECT_EQ("web+mailto", new_protocol_handler.protocol); |
| EXPECT_EQ(http_server_.GetURL("/banners/manifest.json?web+mailto=%s"), |
| new_protocol_handler.url.spec()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsDeletedProtocolHandler) { |
| constexpr char kProtocolHandlerManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "protocol_handlers": [ |
| { |
| "protocol": "mailto", |
| "url": "?mailto=%s" |
| } |
| ], |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kProtocolHandlerManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_TRUE(web_app->protocol_handlers().empty()); |
| } |
| |
| class ManifestUpdateManagerBrowserTest_LockScreen |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| ManifestUpdateManagerBrowserTest_LockScreen() { |
| feature_list_.InitWithFeatures({features::kWebLockScreenApi, |
| blink::features::kWebAppManifestLockScreen}, |
| /*disabled_features=*/{}); |
| } |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_LockScreen, |
| CheckFindsAddedLockScreenStartUrl) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kLockScreenStartUrlManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "lock_screen": { |
| "start_url": "/lock-screen-start" |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_TRUE(web_app->lock_screen_start_url().is_empty()); |
| |
| OverrideManifest(kLockScreenStartUrlManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(http_server_.GetURL("/lock-screen-start"), |
| web_app->lock_screen_start_url().spec()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_LockScreen, |
| CheckIgnoresUnchangedLockScreenStartUrl) { |
| constexpr char kLockScreenStartUrlManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "lock_screen": { |
| "start_url": "/lock-screen-start" |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kLockScreenStartUrlManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_EQ(http_server_.GetURL("/lock-screen-start"), |
| web_app->lock_screen_start_url().spec()); |
| |
| OverrideManifest(kLockScreenStartUrlManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpToDate, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| EXPECT_EQ(http_server_.GetURL("/lock-screen-start"), |
| web_app->lock_screen_start_url().spec()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_LockScreen, |
| CheckFindsChangedLockScreenStartUrl) { |
| constexpr char kLockScreenStartUrlManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "lock_screen": { |
| "start_url": "$1" |
| }, |
| "icons": $2 |
| } |
| )"; |
| |
| OverrideManifest(kLockScreenStartUrlManifestTemplate, |
| {"old-relative-url", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| // URL parsed relative to manifest URL, which is in /banners/. |
| EXPECT_EQ(http_server_.GetURL("/banners/old-relative-url"), |
| web_app->lock_screen_start_url().spec()); |
| |
| OverrideManifest(kLockScreenStartUrlManifestTemplate, |
| {"/lock-screen-starter", kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(http_server_.GetURL("/lock-screen-starter"), |
| web_app->lock_screen_start_url().spec()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_LockScreen, |
| CheckFindsDeletedLockScreenStartUrl) { |
| constexpr char kLockScreenStartUrlManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "lock_screen": { |
| "start_url": "/lock-screen-start" |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kLockScreenStartUrlManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_FALSE(web_app->lock_screen_start_url().is_empty()); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_TRUE(web_app->lock_screen_start_url().is_empty()); |
| } |
| |
| class ManifestUpdateManagerBrowserTest_NoLockScreen |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| ManifestUpdateManagerBrowserTest_NoLockScreen() { |
| feature_list_.InitAndDisableFeature( |
| blink::features::kWebAppManifestLockScreen); |
| } |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_NoLockScreen, |
| WithoutLockScreenFlag_CheckIgnoresLockScreenStartUrl) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kLockScreenStartUrlManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "lock_screen": { |
| "start_url": "/lock-screen-start" |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_TRUE(web_app->lock_screen_start_url().is_empty()); |
| |
| OverrideManifest(kLockScreenStartUrlManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpToDate, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| EXPECT_TRUE(web_app->lock_screen_start_url().is_empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsAddedNewNoteUrl) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kNewNoteUrlManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "note_taking": { |
| "new_note_url": "/new" |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_TRUE(web_app->note_taking_new_note_url().is_empty()); |
| |
| OverrideManifest(kNewNoteUrlManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(http_server_.GetURL("/new"), |
| web_app->note_taking_new_note_url().spec()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckIgnoresUnchangedNewNoteUrl) { |
| constexpr char kNewNoteUrlManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "note_taking": { |
| "new_note_url": "/new" |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kNewNoteUrlManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_EQ(http_server_.GetURL("/new"), |
| web_app->note_taking_new_note_url().spec()); |
| |
| OverrideManifest(kNewNoteUrlManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpToDate, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| EXPECT_EQ(http_server_.GetURL("/new"), |
| web_app->note_taking_new_note_url().spec()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsChangedNewNoteUrl) { |
| constexpr char kNewNoteUrlManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "note_taking": { |
| "new_note_url": "$1" |
| }, |
| "icons": $2 |
| } |
| )"; |
| |
| OverrideManifest(kNewNoteUrlManifestTemplate, |
| {"old-relative-url", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| // URL parsed relative to manifest URL, which is in /banners/. |
| EXPECT_EQ(http_server_.GetURL("/banners/old-relative-url"), |
| web_app->note_taking_new_note_url().spec()); |
| |
| OverrideManifest(kNewNoteUrlManifestTemplate, |
| {"/newer", kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(http_server_.GetURL("/newer"), |
| web_app->note_taking_new_note_url().spec()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, |
| CheckFindsDeletedNewNoteUrl) { |
| constexpr char kNewNoteUrlManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "note_taking": { |
| "new_note_url": "/new" |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "minimal-ui", |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kNewNoteUrlManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_FALSE(web_app->note_taking_new_note_url().is_empty()); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_TRUE(web_app->note_taking_new_note_url().is_empty()); |
| } |
| |
| using ManifestUpdateManagerBrowserTest_ManifestId = |
| ManifestUpdateManagerBrowserTest; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ManifestId, |
| AllowStartUrlUpdate) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": "$1", |
| "scope": "/", |
| "display": "minimal-ui", |
| "id": "test", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"/startA", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppStartUrl(app_id).path(), |
| "/startA"); |
| |
| OverrideManifest(kManifestTemplate, {"/startB", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppStartUrl(app_id).path(), |
| "/startB"); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ManifestId, |
| CheckIgnoresIdChange) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "id": "$1", |
| "start_url": "start", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"test", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| OverrideManifest(kManifestTemplate, {"testb", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppIdMismatch); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppIdMismatch, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ManifestId, |
| ChecksSettingIdMatchDefault) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": "/start", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| // manifest_id should default to start_url when it's not provided in manifest. |
| EXPECT_EQ(GetProvider().registrar_unsafe().GetAppById(app_id)->manifest_id(), |
| http_server_.GetURL("/start")); |
| |
| constexpr char kManifestTemplate2[] = R"( |
| { |
| "name": "Test app name", |
| "id": "$1", |
| "start_url": "/start", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| |
| // Setting manifest id to match default value won't trigger update as the |
| // parsed manifest is the same. |
| OverrideManifest(kManifestTemplate2, {"start", kInstallableIconList}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| } |
| |
| class ManifestUpdateManagerBrowserTest_ScopeExtensions |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| static constexpr char kScopeExtensionsManifestTemplate[] = R"( |
| { |
| "name": "test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $1, |
| "scope_extensions": $2 |
| } |
| )"; |
| |
| void SetUpOnMainThread() override { |
| ManifestUpdateManagerBrowserTest::SetUpOnMainThread(); |
| |
| auto origin_association_fetcher = |
| std::make_unique<webapps::TestWebAppOriginAssociationFetcher>(); |
| GetProvider().origin_association_manager().SetFetcherForTest( |
| std::move(origin_association_fetcher)); |
| } |
| |
| ScopeExtensions GetScopeExtensions(const webapps::AppId& app_id) { |
| return GetProvider() |
| .registrar_unsafe() |
| .GetAppById(app_id) |
| ->scope_extensions(); |
| } |
| |
| ScopeExtensions GetValidatedScopeExtensions(const webapps::AppId& app_id) { |
| return GetProvider() |
| .registrar_unsafe() |
| .GetAppById(app_id) |
| ->validated_scope_extensions(); |
| } |
| |
| std::string OriginAssociationFileFromAppIdentity(const GURL& app_identity) { |
| constexpr char kOriginAssociationTemplate[] = R"( |
| { |
| "$1" : {} |
| })"; |
| return base::ReplaceStringPlaceholders(kOriginAssociationTemplate, |
| {app_identity.spec()}, nullptr); |
| } |
| |
| void SetOriginAssociationData( |
| const std::map<url::Origin, std::string>& data) { |
| auto& test_fetcher = |
| static_cast<webapps::TestWebAppOriginAssociationFetcher&>( |
| GetProvider().origin_association_manager().GetFetcherForTest()); |
| test_fetcher.SetData(data); |
| } |
| |
| void OverrideScopeExtensions(const std::string& substitution) { |
| OverrideManifest(kScopeExtensionsManifestTemplate, |
| {kInstallableIconList, substitution}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_{ |
| blink::features::kWebAppEnableScopeExtensions}; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ScopeExtensions, |
| AddedScopeExtensionsWithAssociation) { |
| // Install with no scope_extensions. |
| OverrideScopeExtensions(R"([])"); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(GetScopeExtensions(app_id), ScopeExtensions()); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), ScopeExtensions()); |
| |
| // update with 1 entry in scope_extensions. |
| OverrideScopeExtensions(R"( |
| [ |
| { |
| "type": "origin", "origin": "https://extension.com" |
| } |
| ] |
| )"); |
| // Association file of extension.com confirms association with the installed |
| // app. |
| SetOriginAssociationData({{url::Origin::Create(GURL("https://extension.com")), |
| OriginAssociationFileFromAppIdentity( |
| GetAppURL().GetWithoutFilename())}}); |
| // Check that changes in manifest's scope_extensions causes a successful |
| // update. |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| // Check that origin association validation succeeded with extension.com. |
| GURL scope("https://extension.com"); |
| ScopeExtensions expected_extensions( |
| {ScopeExtensionInfo::CreateForOrigin(url::Origin::Create(scope))}); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| |
| ScopeExtensions expected_validated_extensions( |
| {ScopeExtensionInfo::CreateForOrigin(url::Origin::Create(scope))}); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), expected_validated_extensions); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ScopeExtensions, |
| AddedScopeExtensionsWithoutAssociation) { |
| // Install with no scope_extensions. |
| OverrideScopeExtensions(R"([])"); |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(GetScopeExtensions(app_id), ScopeExtensions()); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), ScopeExtensions()); |
| |
| // Update with 1 entry in scope_extensions. |
| OverrideScopeExtensions(R"( |
| [ |
| { |
| "type": "origin", "origin": "https://extension.com" |
| } |
| ] |
| )"); |
| // Association is not validated by extension.com. |
| SetOriginAssociationData({}); |
| |
| // Check that changes in manifest's scope_extensions causes a successful |
| // update. |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| ScopeExtensions expected_extensions = |
| ScopeExtensions({ScopeExtensionInfo::CreateForOrigin( |
| url::Origin::Create(GURL("https://extension.com")))}); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), ScopeExtensions()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ScopeExtensions, |
| RemovedScopeExtensions) { |
| // Install app with valid scope_extensions. |
| OverrideScopeExtensions(R"( |
| [ |
| { |
| "type": "origin", "origin": "https://extension.com" |
| } |
| ] |
| )"); |
| |
| SetOriginAssociationData({{url::Origin::Create(GURL("https://extension.com")), |
| OriginAssociationFileFromAppIdentity( |
| GetAppURL().GetWithoutFilename())}}); |
| webapps::AppId app_id = InstallWebApp(); |
| ScopeExtensions expected_extensions = |
| ScopeExtensions({ScopeExtensionInfo::CreateForOrigin( |
| url::Origin::Create(GURL("https://extension.com")))}); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| |
| // Update with empty scope_extensions. |
| OverrideScopeExtensions(R"([])"); |
| |
| // Check that changes in manifest's scope_extensions caused successful update. |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| // Check that origin association validation succeeded with extension.com. |
| EXPECT_EQ(GetScopeExtensions(app_id), ScopeExtensions()); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), ScopeExtensions()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ScopeExtensions, |
| AddedAndRemovedScopeExtensions) { |
| // Install app with valid scope_extensions. |
| OverrideScopeExtensions(R"( |
| [ |
| { |
| "type": "origin", "origin": "https://extension_1.com" |
| } |
| ] |
| )"); |
| SetOriginAssociationData( |
| {{url::Origin::Create(GURL("https://extension_1.com")), |
| OriginAssociationFileFromAppIdentity( |
| GetAppURL().GetWithoutFilename())}}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| GURL scope("https://extension_1.com"); |
| ScopeExtensions expected_extensions( |
| {ScopeExtensionInfo::CreateForOrigin(url::Origin::Create(scope))}); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| ScopeExtensions expected_validated_extensions( |
| {ScopeExtensionInfo::CreateForOrigin(url::Origin::Create(scope))}); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), expected_validated_extensions); |
| |
| // Update with empty scope_extensions. |
| OverrideScopeExtensions(R"( |
| [ |
| { |
| "type": "origin", "origin": "https://extension_2.com" |
| } |
| ] |
| )"); |
| GURL scope2("https://extension_2.com"); |
| SetOriginAssociationData( |
| {{url::Origin::Create(scope2), OriginAssociationFileFromAppIdentity( |
| GetAppURL().GetWithoutFilename())}}); |
| // Check that changes in manifest's scope_extensions caused successful update. |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| // Check that origin association validation succeeded with extension.com. |
| expected_extensions = ScopeExtensions( |
| {ScopeExtensionInfo::CreateForOrigin(url::Origin::Create(scope2))}); |
| expected_validated_extensions = ScopeExtensions( |
| {ScopeExtensionInfo::CreateForOrigin(url::Origin::Create(scope2))}); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), expected_validated_extensions); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ScopeExtensions, |
| AssociationFailsToValidateDuringUpdate) { |
| // Install app with valid scope_extensions. |
| OverrideScopeExtensions(R"( |
| [ |
| { |
| "type": "origin", "origin": "https://extension.com" |
| } |
| ] |
| )"); |
| SetOriginAssociationData({{url::Origin::Create(GURL("https://extension.com")), |
| OriginAssociationFileFromAppIdentity( |
| GetAppURL().GetWithoutFilename())}}); |
| |
| webapps::AppId app_id = InstallWebApp(); |
| ScopeExtensions expected_extensions = |
| ScopeExtensions({ScopeExtensionInfo::CreateForOrigin( |
| url::Origin::Create(GURL("https://extension.com")))}); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| |
| // Check that failure to validate origin associations caused successful |
| // update. |
| SetOriginAssociationData({}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| // Check that origin association validation succeeded with extension.com. |
| expected_extensions = ScopeExtensions({ScopeExtensionInfo::CreateForOrigin( |
| url::Origin::Create(GURL("https://extension.com")))}); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), ScopeExtensions()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ScopeExtensions, |
| FailedOriginAssociationValidatationPassesDuringUpdate) { |
| // Install app with valid scope_extensions. |
| OverrideScopeExtensions(R"( |
| [ |
| { |
| "type": "origin", "origin": "https://extension.com" |
| } |
| ] |
| )"); |
| // Validation should fail during initial install. |
| SetOriginAssociationData({}); |
| webapps::AppId app_id = InstallWebApp(); |
| GURL scope("https://extension.com"); |
| ScopeExtensions expected_extensions( |
| {ScopeExtensionInfo::CreateForOrigin(url::Origin::Create(scope))}); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| |
| ScopeExtensions expected_validated_extensions( |
| {ScopeExtensionInfo::CreateForOrigin(url::Origin::Create(scope))}); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), ScopeExtensions()); |
| |
| // Check that successful validation caused successful update. |
| SetOriginAssociationData( |
| {{url::Origin::Create(scope), OriginAssociationFileFromAppIdentity( |
| GetAppURL().GetWithoutFilename())}}); |
| |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), expected_validated_extensions); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ScopeExtensions, |
| UnrelatedAssociationDataDoesNotCauseUpdate) { |
| // Install app with valid scope_extensions. |
| OverrideScopeExtensions(R"( |
| [ |
| { |
| "type": "origin", "origin": "https://extension.com" |
| } |
| ] |
| )"); |
| // Validation should fail during initial install. |
| SetOriginAssociationData({}); |
| webapps::AppId app_id = InstallWebApp(); |
| ScopeExtensions expected_extensions = |
| ScopeExtensions({ScopeExtensionInfo::CreateForOrigin( |
| url::Origin::Create(GURL("https://extension.com")))}); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), ScopeExtensions()); |
| |
| // Recognize another unrelated app identity. |
| SetOriginAssociationData( |
| {{url::Origin::Create(GURL("https://extension.com")), |
| OriginAssociationFileFromAppIdentity(GURL("https://unrelated.app"))}}); |
| EXPECT_EQ(GetResultAfterPageLoad(GetAppURL()), |
| ManifestUpdateResult::kAppUpToDate); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 0); |
| EXPECT_EQ(GetScopeExtensions(app_id), expected_extensions); |
| EXPECT_EQ(GetValidatedScopeExtensions(app_id), ScopeExtensions()); |
| } |
| |
| class ManifestUpdateManagerAppIdentityBrowserTest |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| ManifestUpdateManagerAppIdentityBrowserTest() = default; |
| |
| protected: |
| webapps::AppId InstallShortcutAppForCurrentUrl( |
| Browser* browser, |
| bool open_as_window = false, |
| const char* override_title = nullptr) { |
| SetAutoAcceptWebAppDialogForTesting( |
| /*auto_accept=*/true, |
| /*auto_open_in_window=*/open_as_window); |
| SetOverrideTitleForTesting(override_title); |
| WebAppTestInstallWithOsHooksObserver observer(browser->profile()); |
| observer.BeginListening(); |
| CHECK(chrome::ExecuteCommand(browser, IDC_CREATE_SHORTCUT)); |
| webapps::AppId app_id = observer.Wait(); |
| SetAutoAcceptWebAppDialogForTesting(false, false); |
| SetOverrideTitleForTesting(nullptr); |
| return app_id; |
| } |
| |
| base::test::ScopedFeatureList scoped_feature_list_{ |
| features::kPwaUpdateDialogForIcon}; |
| }; |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // This test verifies that shortcut apps with custom name overrides don't try to |
| // update the name back to the manifest app name. Shortcut apps only exist on |
| // ChromeOS at the moment. |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerAppIdentityBrowserTest, |
| CheckShortcutAppDoesntPromptForUpdates) { |
| constexpr char kAppName[] = "Test app"; |
| constexpr char kOverrideName[] = "Override name"; |
| |
| // Override with a manifest that is missing a few things, so it is not |
| // installable, but we can create a shortcut for it. |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "icons": [{ |
| "src": "$2", |
| "sizes": "256x256", |
| "type": "image/png" |
| }], |
| "scope": "/banners/", |
| "start_url": "/banners/" |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kAppName, "256x256-red.png"}); |
| |
| GURL app_url = GetAppURL(); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url)); |
| |
| // Install a shortcut to the app, but use a different name for it (necessary |
| // to reproduce the bug). |
| webapps::AppId app_id = |
| InstallShortcutAppForCurrentUrl(browser(), false, kOverrideName); |
| |
| // The app installed should be the only app installed. |
| auto app_ids = GetProvider().registrar_unsafe().GetAppIds(); |
| ASSERT_EQ(1u, app_ids.size()); |
| ASSERT_EQ(app_id, app_ids[0]); |
| EXPECT_EQ(kOverrideName, |
| GetProvider().registrar_unsafe().GetAppShortName(app_id)); |
| |
| // Expect no updates with a custom name and out of date icons. |
| OverrideManifest(kManifestTemplate, {kAppName, "256x256-green.png"}); |
| |
| views::AnyWidgetObserver observer(views::test::AnyWidgetTestPasskey{}); |
| observer.set_shown_callback( |
| base::BindLambdaForTesting([&](views::Widget* widget) { |
| // If the App Identity dialog was shown for the shortcut app, then |
| // something is wrong. |
| ASSERT_FALSE(widget->GetName() == |
| "WebAppIdentityUpdateConfirmationView"); |
| })); |
| |
| // Now navigate to the same url and allow the update mechanism to run. |
| UpdateCheckResultAwaiter result_awaiter(app_url); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url)); |
| EXPECT_EQ(std::move(result_awaiter).AwaitNextResult(), |
| ManifestUpdateResult::kAppUpToDate); |
| |
| EXPECT_EQ(kOverrideName, |
| GetProvider().registrar_unsafe().GetAppShortName(app_id)); |
| EXPECT_EQ(SK_ColorRED, ReadAppIconPixel(app_id, /*size=*/256)); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // Test that showing the AppIdentity update confirmation and allowing the update |
| // sends the right signal back. |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerAppIdentityBrowserTest, |
| VerifyCallbackUpgradeAllowed) { |
| base::AutoReset<std::optional<AppIdentityUpdate>> update_dialog_scope = |
| SetIdentityUpdateDialogActionForTesting(AppIdentityUpdate::kAllowed); |
| |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| OverrideManifest(kManifestTemplate, {"Test app name", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| |
| base::RunLoop run_loop; |
| |
| ShowWebAppIdentityUpdateDialog( |
| app_id, |
| /* title_change= */ true, |
| /* icon_change= */ false, u"old_title", u"new_title", |
| /* old_icon= */ SkBitmap(), |
| /* new_icon= */ SkBitmap(), |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| /* callback= */ |
| base::BindLambdaForTesting( |
| [&](AppIdentityUpdate app_identity_update_allowed) { |
| // This verifies that the dialog sends us the signal to update. |
| DCHECK_EQ(AppIdentityUpdate::kAllowed, app_identity_update_allowed); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| class ManifestUpdateManagerImmediateUpdateBrowserTest |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| ManifestUpdateManagerImmediateUpdateBrowserTest() = default; |
| |
| SkColor GetMiddlePixel(gfx::Image image) { |
| SkBitmap bitmap = image.AsBitmap(); |
| return bitmap.getColor(bitmap.width() / 2, bitmap.height() / 2); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| // Test whether web app windows update their UI immediately after a manifest |
| // update gets applied. |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerImmediateUpdateBrowserTest, |
| WebAppWindowsUpdatedImmediately) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": "manifest_test_page.html", |
| "scope": "/", |
| "display": "$2", |
| "icons": [{ |
| "src": "$3", |
| "sizes": "256x256", |
| "type": "image/png" |
| }], |
| "theme_color": "$4" |
| } |
| )"; |
| |
| // Install default web app (so user confirmations aren't required for updating |
| // its identity). |
| OverrideManifest(kManifestTemplate, |
| {"Old name", "standalone", "256x256-red.png", "red"}); |
| webapps::AppId app_id = InstallDefaultApp(); |
| GURL app_url = GetAppURL(); |
| Browser* app_browser = nullptr; |
| |
| // Launch app window and wait for the page to load, the app icon to load and |
| // the manifest update check to complete. |
| { |
| base::RunLoop icon_load; |
| WebAppBrowserController::SetIconLoadCallbackForTesting( |
| icon_load.QuitClosure()); |
| UpdateCheckResultAwaiter result_awaiter(app_url); |
| |
| // Synchronize os integration to ensure that mac app shim is created. |
| GetProvider().scheduler().SynchronizeOsIntegration(app_id, |
| base::DoNothing()); |
| app_browser = LaunchWebAppBrowserAndWait(app_id); |
| |
| icon_load.Run(); |
| EXPECT_EQ(std::move(result_awaiter).AwaitNextResult(), |
| ManifestUpdateResult::kAppUpToDate); |
| } |
| |
| // Update manifest UI elements. |
| OverrideManifest(kManifestTemplate, |
| {"New name", "minimal-ui", "256x256-green.png", "lime"}); |
| |
| // Set up awaiters. |
| base::RunLoop app_window_update; |
| WebAppBrowserController::SetManifestUpdateAppliedCallbackForTesting( |
| app_window_update.QuitClosure()); |
| base::RunLoop second_icon_load; |
| WebAppBrowserController::SetIconLoadCallbackForTesting( |
| second_icon_load.QuitClosure()); |
| UpdateCheckResultAwaiter result_awaiter(app_url); |
| |
| // Reload page to invoke a manifest update check. |
| GetProvider().manifest_update_manager().ResetManifestThrottleForTesting( |
| app_id); |
| chrome::Reload(app_browser, WindowOpenDisposition::CURRENT_TAB); |
| |
| // Check update takes effect on web app database. |
| EXPECT_EQ(std::move(result_awaiter).AwaitNextResult(), |
| ManifestUpdateResult::kAppUpdated); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| ASSERT_TRUE(web_app); |
| EXPECT_EQ(web_app->untranslated_name(), "New name"); |
| EXPECT_EQ(web_app->display_mode(), DisplayMode::kMinimalUi); |
| EXPECT_EQ(web_app->theme_color(), SK_ColorGREEN); |
| |
| // Check update takes effect on live web app window. |
| app_window_update.Run(); |
| AppBrowserController* app_controller = app_browser->app_controller(); |
| EXPECT_EQ(app_controller->GetTitle(), u"New name - Web app banner test page"); |
| EXPECT_EQ(app_controller->GetThemeColor(), SK_ColorGREEN); |
| |
| // Force the app icon to load again and check that it's the new one. |
| app_controller->GetWindowIcon(); |
| second_icon_load.Run(); |
| // Read the middle of the icon since on Chrome OS it gets circlified. |
| EXPECT_EQ(GetMiddlePixel(app_controller->GetWindowIcon().GetImage()), |
| SK_ColorGREEN); |
| } |
| |
| class ManifestUpdateManagerPrerenderingBrowserTest |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| ManifestUpdateManagerPrerenderingBrowserTest() |
| : prerender_helper_(base::BindRepeating( |
| &ManifestUpdateManagerPrerenderingBrowserTest::GetWebContents, |
| base::Unretained(this))) {} |
| |
| ~ManifestUpdateManagerPrerenderingBrowserTest() override = default; |
| |
| content::WebContents* GetWebContents() const { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| content::test::PrerenderTestHelper& prerender_helper() { |
| return prerender_helper_; |
| } |
| |
| private: |
| content::test::PrerenderTestHelper prerender_helper_; |
| }; |
| |
| // Tests that prerendering doesn't change the existing App ID. It also doesn't |
| // call ManifestUpdateManager as a primary page is not changed. |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerPrerenderingBrowserTest, |
| NotUpdateInPrerendering) { |
| webapps::AppId app_id = InstallWebApp(); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpToDate, |
| GetResultAfterPageLoad(GetAppURL())); |
| |
| content::WebContents* web_contents = GetWebContents(); |
| const webapps::AppId* first_app_id = WebAppTabHelper::GetAppId(web_contents); |
| EXPECT_EQ(app_id, *first_app_id); |
| |
| base::HistogramTester histogram_tester; |
| const GURL prerender_url = http_server_.GetURL("/title1.html"); |
| content::FrameTreeNodeId host_id = |
| prerender_helper().AddPrerender(prerender_url); |
| content::test::PrerenderHostObserver host_observer(*web_contents, host_id); |
| // Prerendering doesn't update the existing App ID. |
| const webapps::AppId* app_id_on_prerendering = |
| WebAppTabHelper::GetAppId(web_contents); |
| EXPECT_EQ(app_id, *app_id_on_prerendering); |
| |
| // In prerendering navigation, it doesn't call ManifestUpdateManager. |
| histogram_tester.ExpectTotalCount(kUpdateHistogramName, 0); |
| |
| prerender_helper().NavigatePrimaryPage(prerender_url); |
| EXPECT_TRUE(host_observer.was_activated()); |
| const webapps::AppId* app_id_after_activation = |
| WebAppTabHelper::GetAppId(web_contents); |
| EXPECT_EQ(nullptr, app_id_after_activation); |
| } |
| |
| class ManifestUpdateManagerBrowserTest_TabStrip |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| ManifestUpdateManagerBrowserTest_TabStrip() { |
| feature_list_.InitWithFeatures( |
| {blink::features::kDesktopPWAsTabStripCustomizations, |
| blink::features::kDesktopPWAsTabStrip}, |
| /*disabled_features=*/{}); |
| } |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_TabStrip, |
| CheckFindsAddedTabStripField) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": ["tabbed"], |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kTabStripManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": ["tabbed"], |
| "tab_strip": { |
| "new_tab_button": { |
| "url": "/new-tab-url" |
| }, |
| "home_tab": { |
| "scope_patterns": [ |
| {"pathname": "/foo/*"} |
| ] |
| } |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_FALSE(web_app->tab_strip().has_value()); |
| |
| OverrideManifest(kTabStripManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| |
| EXPECT_TRUE(web_app->tab_strip().has_value()); |
| EXPECT_EQ(http_server_.GetURL("/new-tab-url"), |
| web_app->tab_strip().value().new_tab_button.url); |
| EXPECT_EQ(std::get<blink::Manifest::HomeTabParams>( |
| web_app->tab_strip().value().home_tab) |
| .scope_patterns.size(), |
| 1u); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_TabStrip, |
| CheckIgnoresUnchangedTabStripField) { |
| constexpr char kTabStripManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": ["tabbed"], |
| "tab_strip": { |
| "new_tab_button": { |
| "url": "/new-tab-url" |
| }, |
| "home_tab": {} |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kTabStripManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_TRUE(web_app->tab_strip().has_value()); |
| EXPECT_EQ(http_server_.GetURL("/new-tab-url"), |
| web_app->tab_strip().value().new_tab_button.url); |
| EXPECT_TRUE(std::holds_alternative<blink::Manifest::HomeTabParams>( |
| web_app->tab_strip().value().home_tab)); |
| |
| OverrideManifest(kTabStripManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpToDate, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| EXPECT_TRUE(web_app->tab_strip().has_value()); |
| EXPECT_EQ(http_server_.GetURL("/new-tab-url"), |
| web_app->tab_strip().value().new_tab_button.url); |
| EXPECT_TRUE(std::holds_alternative<blink::Manifest::HomeTabParams>( |
| web_app->tab_strip().value().home_tab)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_TabStrip, |
| CheckFindsChangedTabStripField) { |
| constexpr char kTabStripManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": ["tabbed"], |
| "tab_strip": { |
| "new_tab_button": { |
| "url": "$1" |
| } |
| }, |
| "icons": $2 |
| } |
| )"; |
| |
| OverrideManifest(kTabStripManifestTemplate, |
| {"old-relative-url", kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| // URL parsed relative to manifest URL, which is in /banners/. |
| EXPECT_TRUE(web_app->tab_strip().has_value()); |
| EXPECT_EQ(http_server_.GetURL("/banners/old-relative-url"), |
| web_app->tab_strip().value().new_tab_button.url); |
| |
| OverrideManifest(kTabStripManifestTemplate, |
| {"/new-tab-url", kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_TRUE(web_app->tab_strip().has_value()); |
| EXPECT_EQ(http_server_.GetURL("/new-tab-url"), |
| web_app->tab_strip().value().new_tab_button.url); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_TabStrip, |
| CheckFindsDeletedTabStripField) { |
| constexpr char kTabStripManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": ["tabbed"], |
| "tab_strip": { |
| "new_tab_button": { |
| "url": "/new-tab-url" |
| }, |
| "home_tab": {} |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": ["tabbed"], |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kTabStripManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_TRUE(web_app->tab_strip().has_value()); |
| EXPECT_EQ(http_server_.GetURL("/new-tab-url"), |
| web_app->tab_strip().value().new_tab_button.url); |
| EXPECT_TRUE(std::holds_alternative<blink::Manifest::HomeTabParams>( |
| web_app->tab_strip().value().home_tab)); |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| EXPECT_FALSE(web_app->tab_strip().has_value()); |
| } |
| |
| class ManifestUpdateManagerBrowserTest_NoTabStrip |
| : public ManifestUpdateManagerBrowserTest { |
| public: |
| ManifestUpdateManagerBrowserTest_NoTabStrip() { |
| feature_list_.InitAndDisableFeature( |
| blink::features::kDesktopPWAsTabStripCustomizations); |
| } |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_NoTabStrip, |
| WithoutTabStripFlag_CheckIgnoresTabStripField) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": ["tabbed"], |
| "icons": $1 |
| } |
| )"; |
| |
| constexpr char kTabStripManifestTemplate[] = R"( |
| { |
| "name": "Test app name", |
| "start_url": ".", |
| "scope": "/", |
| "display": "standalone", |
| "display_override": ["tabbed"], |
| "tab_strip": { |
| "new_tab_button": { |
| "url": "/new-tab-url" |
| } |
| }, |
| "icons": $1 |
| } |
| )"; |
| |
| OverrideManifest(kManifestTemplate, {kInstallableIconList}); |
| webapps::AppId app_id = InstallWebApp(); |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| EXPECT_FALSE(web_app->tab_strip().has_value()); |
| |
| OverrideManifest(kTabStripManifestTemplate, {kInstallableIconList}); |
| EXPECT_EQ(ManifestUpdateResult::kAppUpToDate, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpToDate, 1); |
| EXPECT_FALSE(web_app->tab_strip().has_value()); |
| } |
| |
| enum AppIdTestParam { |
| kInvalid = 0, |
| kTypeWebApp = 1 << 1, |
| kTypeDefaultApp = 1 << 2, |
| kTypePolicyApp = 1 << 3, |
| kTypeKioskApp = 1 << 4, |
| kWithFlagNone = 1 << 5, |
| kWithFlagPolicyAppIdentity = 1 << 6, |
| kWithFlagAppIdDialogForIcon = 1 << 7, |
| kActionUpdateTitle = 1 << 8, |
| kActionUpdateTitleAndLauncherIcon = 1 << 9, |
| kActionUpdateLauncherIcon = 1 << 10, |
| kActionUpdateInstallIcon = 1 << 11, |
| kActionUpdateLauncherAndInstallIcon = 1 << 12, |
| kActionUpdateUnimportantIcon = 1 << 13, |
| kActionRemoveLauncherIcon = 1 << 14, |
| kActionRemoveInstallIcon = 1 << 15, |
| kActionRemoveUnimportantIcon = 1 << 16, |
| kActionSwitchFromLauncher = 1 << 17, |
| kActionSwitchToLauncher = 1 << 18, |
| }; |
| |
| class ManifestUpdateManagerBrowserTest_AppIdentityParameterized |
| : public ManifestUpdateManagerBrowserTest, |
| public testing::WithParamInterface< |
| std::tuple<AppIdTestParam, AppIdTestParam, AppIdTestParam>> { |
| public: |
| ManifestUpdateManagerBrowserTest_AppIdentityParameterized() { |
| std::vector<base::test::FeatureRef> enabled_features; |
| std::vector<base::test::FeatureRef> disabled_features; |
| if (IsAppIdentityUpdateDialogForIconEnabled()) { |
| enabled_features.push_back(features::kPwaUpdateDialogForIcon); |
| } else { |
| disabled_features.push_back(features::kPwaUpdateDialogForIcon); |
| } |
| if (IsPolicyAppIdentityOverrideEnabled()) { |
| enabled_features.push_back( |
| features::kWebAppManifestPolicyAppIdentityUpdate); |
| } else { |
| disabled_features.push_back( |
| features::kWebAppManifestPolicyAppIdentityUpdate); |
| } |
| |
| scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); |
| } |
| |
| bool IsWebApp() const { |
| return std::get<1>(GetParam()) & AppIdTestParam::kTypeWebApp; |
| } |
| bool IsDefaultApp() const { |
| return std::get<1>(GetParam()) & AppIdTestParam::kTypeDefaultApp; |
| } |
| bool IsPolicyApp() const { |
| return std::get<1>(GetParam()) & AppIdTestParam::kTypePolicyApp; |
| } |
| bool IsKioskApp() const { |
| return std::get<1>(GetParam()) & AppIdTestParam::kTypeKioskApp; |
| } |
| |
| bool IsAppIdentityUpdateDialogForIconEnabled() const { |
| return std::get<2>(GetParam()) & |
| AppIdTestParam::kWithFlagAppIdDialogForIcon; |
| } |
| bool IsPolicyAppIdentityOverrideEnabled() const { |
| return std::get<2>(GetParam()) & AppIdTestParam::kWithFlagPolicyAppIdentity; |
| } |
| |
| bool TitleUpdate() const { |
| return std::get<0>(GetParam()) & AppIdTestParam::kActionUpdateTitle || |
| std::get<0>(GetParam()) & |
| AppIdTestParam::kActionUpdateTitleAndLauncherIcon; |
| } |
| |
| bool AnyIconUpdate() const { |
| return IdentityIconUpdate() || NonIdentityIconUpdate(); |
| } |
| |
| bool IdentityIconUpdate() const { |
| return LauncherIconUpdate() || LauncherIconRemove() || |
| InstallIconUpdate() || InstallIconRemove() || |
| IconSwitchFromLauncher() || IconSwitchToLauncher(); |
| } |
| |
| bool NonIdentityIconUpdate() const { |
| return UnimportantIconUpdate() || UnimportantIconRemove(); |
| } |
| |
| bool LauncherIconUpdate() const { |
| return std::get<0>(GetParam()) & |
| AppIdTestParam::kActionUpdateLauncherIcon || |
| std::get<0>(GetParam()) & |
| AppIdTestParam::kActionUpdateTitleAndLauncherIcon || |
| std::get<0>(GetParam()) & |
| AppIdTestParam::kActionUpdateLauncherAndInstallIcon; |
| } |
| |
| bool InstallIconUpdate() const { |
| return std::get<0>(GetParam()) & AppIdTestParam::kActionUpdateInstallIcon || |
| std::get<0>(GetParam()) & |
| AppIdTestParam::kActionUpdateLauncherAndInstallIcon; |
| } |
| |
| bool UnimportantIconUpdate() const { |
| return std::get<0>(GetParam()) & |
| AppIdTestParam::kActionUpdateUnimportantIcon; |
| } |
| |
| bool LauncherIconRemove() const { |
| return std::get<0>(GetParam()) & AppIdTestParam::kActionRemoveLauncherIcon; |
| } |
| |
| bool InstallIconRemove() const { |
| return std::get<0>(GetParam()) & AppIdTestParam::kActionRemoveInstallIcon; |
| } |
| |
| bool UnimportantIconRemove() const { |
| return std::get<0>(GetParam()) & |
| AppIdTestParam::kActionRemoveUnimportantIcon; |
| } |
| |
| bool IconSwitchFromLauncher() const { |
| return std::get<0>(GetParam()) & AppIdTestParam::kActionSwitchFromLauncher; |
| } |
| |
| bool IconSwitchToLauncher() const { |
| return std::get<0>(GetParam()) & AppIdTestParam::kActionSwitchToLauncher; |
| } |
| |
| // This function describes in which scenarios the test should expect the title |
| // of an app to change. It should mirror exactly the expectations we have of |
| // the implementation and be simple to read for easy verification. |
| bool ExpectTitleUpdate() const { |
| if (!TitleUpdate()) |
| return false; // Titles should not update without a request to update. |
| |
| if (IsDefaultApp() || IsKioskApp()) |
| return true; |
| if (IsPolicyApp()) |
| return IsPolicyAppIdentityOverrideEnabled(); |
| |
| return true; // App Identity Updates for names have launched. |
| } |
| |
| // This function describes in which scenarios the test should expect the icons |
| // of an app to change. It should mirror exactly the expectations we have of |
| // the implementation and be simple to read for easy verification. |
| bool ExpectIconUpdate() const { |
| return AnyIconUpdate() && IconUpdatesAllowed(); |
| } |
| |
| bool IconUpdatesAllowed() const { |
| if (!IdentityIconUpdate() || IsDefaultApp() || IsKioskApp()) { |
| return true; |
| } |
| |
| if (IsPolicyApp()) { |
| return IsPolicyAppIdentityOverrideEnabled(); |
| } |
| |
| // User-installed apps don't get title updates unless App Id dialog is |
| // enabled for icons. |
| return IsAppIdentityUpdateDialogForIconEnabled(); |
| } |
| |
| static std::string ParamToString( |
| testing::TestParamInfo< |
| std::tuple<AppIdTestParam, AppIdTestParam, AppIdTestParam>> |
| param_info) { |
| std::string result = ""; |
| |
| AppIdTestParam action = std::get<0>(param_info.param); |
| if (action & AppIdTestParam::kActionUpdateTitle) |
| result += "UpdateTitle_"; |
| if (action & AppIdTestParam::kActionUpdateTitleAndLauncherIcon) |
| result += "UpdateTitleAndLauncherIcon_"; |
| if (action & AppIdTestParam::kActionUpdateLauncherIcon) |
| result += "UpdateLauncherIcon_"; |
| if (action & AppIdTestParam::kActionUpdateInstallIcon) |
| result += "UpdateInstallIcon_"; |
| if (action & AppIdTestParam::kActionUpdateLauncherAndInstallIcon) |
| result += "UpdateLauncherAndInstallIcon_"; |
| if (action & AppIdTestParam::kActionUpdateUnimportantIcon) |
| result += "UpdateUnimportantIcon_"; |
| if (action & AppIdTestParam::kActionRemoveLauncherIcon) |
| result += "RemoveLauncherIcon_"; |
| if (action & AppIdTestParam::kActionRemoveInstallIcon) |
| result += "RemoveInstallIcon_"; |
| if (action & AppIdTestParam::kActionRemoveUnimportantIcon) |
| result += "RemoveUnimportantIcon_"; |
| if (action & AppIdTestParam::kActionSwitchFromLauncher) |
| result += "SwitchFromLauncher_"; |
| if (action & AppIdTestParam::kActionSwitchToLauncher) |
| result += "SwitchToLauncher_"; |
| |
| AppIdTestParam type = std::get<1>(param_info.param); |
| if (type & AppIdTestParam::kTypeWebApp) |
| result += "WebApp_"; |
| if (type & AppIdTestParam::kTypeDefaultApp) |
| result += "DefaultApp_"; |
| if (type & AppIdTestParam::kTypePolicyApp) |
| result += "PolicyApp_"; |
| if (type & AppIdTestParam::kTypeKioskApp) |
| result += "KioskApp_"; |
| |
| AppIdTestParam flags = std::get<2>(param_info.param); |
| result += "Flags_"; |
| if (flags & AppIdTestParam::kWithFlagNone) |
| result += "None_"; |
| if (flags & AppIdTestParam::kWithFlagPolicyAppIdentity) |
| result += "PolicyCanUpdate_"; |
| if (flags & AppIdTestParam::kWithFlagAppIdDialogForIcon) |
| result += "WithAppIdDlgForIcon_"; |
| |
| return result; |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| std::string GenerateIconRow(SquareSizePx size, SkColor color) { |
| std::string size_str = base::NumberToString(size); |
| std::string color_str = base::ReplaceStringPlaceholders( |
| R"(rgb($1, $2, $3))", |
| {base::NumberToString(SkColorGetR(color)), |
| base::NumberToString(SkColorGetG(color)), |
| base::NumberToString(SkColorGetB(color))}, |
| nullptr); |
| // Encode a square SVG with a flat colored rect of the requested size and |
| // color in the icon URL. |
| return base::ReplaceStringPlaceholders(R"( { |
| "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='$1' height='$1'><rect fill='$2' width='$1' height='$1' /></svg>", |
| "sizes": "$1x$1", |
| "type": "image/svg+xml" |
| })", |
| {size_str, color_str}, nullptr); |
| } |
| |
| struct SizeColor { |
| SquareSizePx size; |
| SkColor color; |
| }; |
| |
| std::string GenerateColoredIconList(std::vector<SizeColor> size_colors) { |
| bool installable_icon_included = false; |
| |
| std::string icon_list; |
| for (const auto [size, color] : size_colors) { |
| if (!icon_list.empty()) { |
| icon_list += ",\n"; |
| } |
| icon_list += GenerateIconRow(size, color); |
| |
| // Installability requirements mandate at least one large icon. |
| installable_icon_included |= size >= kInstallMinSize; |
| } |
| |
| if (!installable_icon_included) { |
| if (!icon_list.empty()) { |
| icon_list += ",\n"; |
| } |
| icon_list += " { \"error\": \"Installability requirements not met\" }"; |
| } |
| |
| if (!icon_list.empty()) { |
| icon_list += "\n"; |
| } |
| return base::StrCat({"\n [\n", icon_list, " ]\n "}); |
| } |
| |
| // Disabled due to test flakiness: https://crbug.com/1341617 |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) |
| #define MAYBE_CheckCombinations DISABLED_CheckCombinations |
| #else |
| #define MAYBE_CheckCombinations CheckCombinations |
| #endif |
| IN_PROC_BROWSER_TEST_P( |
| ManifestUpdateManagerBrowserTest_AppIdentityParameterized, |
| MAYBE_CheckCombinations) { |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "$1", |
| "start_url": "manifest_test_page.html", |
| "scope": "/", |
| "display": "standalone", |
| "icons": $2 |
| } |
| )"; |
| |
| ManifestUpdateManager::ScopedBypassWindowCloseWaitingForTesting |
| bypass_window_close_waiting; |
| |
| testing::TestParamInfo< |
| std::tuple<AppIdTestParam, AppIdTestParam, AppIdTestParam>> |
| param(GetParam(), 0); |
| |
| std::string trace = "\n---------------------------\nParameterized test: " + |
| ParamToString(param) + "\nType: "; |
| if (IsPolicyApp()) |
| trace += "Policy"; |
| if (IsKioskApp()) |
| trace += "Kiosk"; |
| if (IsDefaultApp()) |
| trace += "Default"; |
| if (IsWebApp()) |
| trace += "WebApp"; |
| trace += (IsAppIdentityUpdateDialogForIconEnabled() |
| ? ", with AppIdDlgForIcon: YES\n" |
| : ", with AppIdDlgForIcon: NO\n"); |
| |
| trace += base::ReplaceStringPlaceholders( |
| "UPDATE: Title: $1 Launcher $2 Install $3 Other $4\n", |
| {base::NumberToString(TitleUpdate()), |
| base::NumberToString(LauncherIconUpdate()), |
| base::NumberToString(InstallIconUpdate()), |
| base::NumberToString(UnimportantIconUpdate())}, |
| nullptr); |
| trace += base::ReplaceStringPlaceholders( |
| "REMOVE: Launcher $1 Install $2 Other $3\n", |
| {base::NumberToString(LauncherIconRemove()), |
| base::NumberToString(InstallIconRemove()), |
| base::NumberToString(UnimportantIconRemove())}, |
| nullptr); |
| trace += base::ReplaceStringPlaceholders( |
| "SWITCH: FromLauncher $1 ToLauncher $2\n", |
| {base::NumberToString(IconSwitchFromLauncher()), |
| base::NumberToString(IconSwitchToLauncher())}, |
| nullptr); |
| trace += base::ReplaceStringPlaceholders( |
| "Should result in: Title update: $1 Icon update $2\n", |
| {base::NumberToString(ExpectTitleUpdate()), |
| base::NumberToString(ExpectIconUpdate())}, |
| nullptr); |
| trace += base::ReplaceStringPlaceholders( |
| "Sizes: InstallIcon $1, LauncherIcon $2, ExtraIcon1 $3, ExtraIcon2 $4 " |
| "Installability $5\n", |
| {base::NumberToString(kInstallIconSize), |
| base::NumberToString(kLauncherIconSize), |
| base::NumberToString(kUnimportantIconSize), |
| base::NumberToString(kUnimportantIconSize2), |
| base::NumberToString(kInstallabilityIconSize)}, |
| nullptr); |
| trace += "---------------------------\n"; |
| |
| // We need to auto-accept the App Identity Update dialog whenever the test |
| // enables icon updates, but also when title updates are requested (because |
| // they are default-enabled). When icon updates become default-enabled also, |
| // we can change this to auto-accept when either icon updates are requested, |
| // or name updates, or both. |
| if (IsAppIdentityUpdateDialogForIconEnabled()) { |
| AcceptAppIdentityUpdateDialogForTesting(); |
| } else if (TitleUpdate()) { |
| AcceptAppIdentityUpdateDialogForTesting(); |
| } |
| |
| std::string app_name = "Test app name"; |
| |
| // The 'before' and 'after' icon lists. |
| std::string starting_stage; |
| std::string ending_stage; |
| |
| // This is the default icon list (all green icons) and is overridden below, |
| // if need be. |
| starting_stage = |
| GenerateColoredIconList({{kUnimportantIconSize, SK_ColorRED}, |
| {kInstallIconSize, SK_ColorYELLOW}, |
| {kLauncherIconSize, SK_ColorGREEN}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| |
| // This is the resulting shortcut colors (per size) for the default icon list |
| // above, and similar to `starting_stage` it is overridden below when needed. |
| // NOTE: When considering which shortcut sizes appear on which platform, the |
| // system creates an intersection between `kDesiredIconSizesForShortcut` |
| // (which is platform-dependent) and `SizesToGenerate()` (which is hard-coded |
| // to { 32, 48, 64, 96, 128, 256 } for all platforms. This can lead to some |
| // discrepancies per platform. For example, Windows specifies more sizes |
| // in`kDesiredIconSizesForShortcut` than other OS', which is why it is common |
| // to find auto-generated icons for size 64 and 96 only on Windows. Similarly, |
| // size 512 is not part of `kDesiredIconSizesForShortcut` on Windows, and |
| // that size therefore does not always feature in the shortcut expectations. |
| std::vector<std::pair<std::pair<int, int>, SkColor>> |
| expected_shortcut_colors_before = { |
| {{32, kAll}, SK_ColorYELLOW}, |
| {{48, kAll}, SK_ColorYELLOW}, |
| // Although sizes 64 and 96 are within the SizesToGenerate() list they |
| // are listed in `kDesiredIconSizesForShortcut` on Windows only. |
| {{64, kWin}, SK_ColorGREEN}, |
| {{96, kWin}, SK_ColorGREEN}, |
| {{128, kAll}, SK_ColorGREEN}, |
| {{256, kMac}, SK_ColorGREEN}, |
| {{256, kNotMac}, SK_ColorBLUE}, |
| // The tests use size 512 as the icon size that guarantees that the |
| // installability requirements are met, but that size is not listed as |
| // a desired shortcut size on Windows. |
| {{512, kNotWin}, SK_ColorBLUE}}; |
| |
| // This needs to be populated for each test below. |
| std::vector<std::pair<std::pair<int, int>, SkColor>> |
| expected_shortcut_colors_if_updated; |
| |
| if (LauncherIconUpdate() && InstallIconUpdate()) { |
| ending_stage = |
| GenerateColoredIconList({{kUnimportantIconSize, SK_ColorRED}, |
| {kInstallIconSize, SK_ColorBLACK}, |
| {kLauncherIconSize, SK_ColorWHITE}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| |
| expected_shortcut_colors_if_updated = { |
| {{32, kAll}, SK_ColorBLACK}, |
| {{48, kAll}, SK_ColorBLACK}, |
| {{64, kWin}, SK_ColorWHITE}, |
| {{96, kWin}, SK_ColorWHITE}, |
| {{128, kAll}, SK_ColorWHITE}, |
| // On Mac, this size is the launcher icon, so white is expected. |
| {{256, kMac}, SK_ColorWHITE}, |
| // On other platforms, there is no size 256 specified, so this is |
| // generated from the installability icon (size 512), which is blue. |
| {{256, kNotMac}, SK_ColorBLUE}, |
| {{512, kNotWin}, SK_ColorBLUE}}; |
| } else if (IconSwitchFromLauncher()) { |
| // Starting stage is with a launcher icon but without an unimportant icon. |
| starting_stage = |
| GenerateColoredIconList({{kInstallIconSize, SK_ColorYELLOW}, |
| {kLauncherIconSize, SK_ColorGREEN}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| |
| expected_shortcut_colors_before = { |
| {{32, kAll}, SK_ColorYELLOW}, {{48, kAll}, SK_ColorYELLOW}, |
| {{64, kWin}, SK_ColorGREEN}, {{96, kWin}, SK_ColorGREEN}, |
| {{128, kAll}, SK_ColorGREEN}, {{256, kMac}, SK_ColorGREEN}, |
| {{256, kNotMac}, SK_ColorBLUE}, {{512, kNotWin}, SK_ColorBLUE}}; |
| |
| // Ending stage is without a launcher icon but with an unimportant icon. |
| ending_stage = |
| GenerateColoredIconList({{kUnimportantIconSize, SK_ColorRED}, |
| {kLauncherIconSize, SK_ColorGREEN}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| |
| expected_shortcut_colors_if_updated = { |
| {{32, kAll}, SK_ColorGREEN}, {{48, kAll}, SK_ColorGREEN}, |
| {{64, kWin}, SK_ColorGREEN}, {{96, kWin}, SK_ColorGREEN}, |
| {{128, kAll}, SK_ColorGREEN}, {{256, kMac}, SK_ColorGREEN}, |
| {{256, kNotMac}, SK_ColorBLUE}, {{512, kNotWin}, SK_ColorBLUE}}; |
| } else if (IconSwitchToLauncher()) { |
| // Starting stage is without a launcher icon but with an unimportant icon. |
| starting_stage = GenerateColoredIconList({ |
| {kUnimportantIconSize, SK_ColorRED}, |
| {kInstallIconSize, SK_ColorYELLOW}, |
| {kInstallabilityIconSize, SK_ColorBLUE}, |
| }); |
| |
| expected_shortcut_colors_before = { |
| {{32, kAll}, SK_ColorYELLOW}, {{48, kAll}, SK_ColorYELLOW}, |
| {{64, kWin}, SK_ColorYELLOW}, {{96, kWin}, SK_ColorYELLOW}, |
| {{128, kAll}, SK_ColorBLUE}, {{256, kMac}, SK_ColorBLUE}, |
| {{256, kNotMac}, SK_ColorBLUE}, {{512, kNotWin}, SK_ColorBLUE}}; |
| |
| // Ending stage is with the launcher icon but without an unimportant icon. |
| ending_stage = |
| GenerateColoredIconList({{kInstallIconSize, SK_ColorYELLOW}, |
| {kLauncherIconSize, SK_ColorGREEN}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| |
| expected_shortcut_colors_if_updated = { |
| {{32, kAll}, SK_ColorYELLOW}, {{48, kAll}, SK_ColorYELLOW}, |
| {{64, kWin}, SK_ColorGREEN}, {{96, kWin}, SK_ColorGREEN}, |
| {{128, kAll}, SK_ColorGREEN}, {{256, kMac}, SK_ColorGREEN}, |
| {{256, kNotMac}, SK_ColorBLUE}, {{512, kNotWin}, SK_ColorBLUE}}; |
| } else if (LauncherIconUpdate()) { |
| ending_stage = GenerateColoredIconList({ |
| {kUnimportantIconSize, SK_ColorRED}, |
| {kInstallIconSize, SK_ColorYELLOW}, |
| {kLauncherIconSize, SK_ColorWHITE}, |
| {kInstallabilityIconSize, SK_ColorBLUE}, |
| }); |
| expected_shortcut_colors_if_updated = { |
| {{32, kAll}, SK_ColorYELLOW}, {{48, kAll}, SK_ColorYELLOW}, |
| {{64, kWin}, SK_ColorWHITE}, {{96, kWin}, SK_ColorWHITE}, |
| {{128, kAll}, SK_ColorWHITE}, {{256, kMac}, SK_ColorWHITE}, |
| {{256, kNotMac}, SK_ColorBLUE}, {{512, kNotWin}, SK_ColorBLUE}}; |
| } else if (InstallIconUpdate()) { |
| ending_stage = |
| GenerateColoredIconList({{kUnimportantIconSize, SK_ColorRED}, |
| {kInstallIconSize, SK_ColorWHITE}, |
| {kLauncherIconSize, SK_ColorGREEN}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| expected_shortcut_colors_if_updated = { |
| {{32, kAll}, SK_ColorWHITE}, {{48, kAll}, SK_ColorWHITE}, |
| {{64, kWin}, SK_ColorGREEN}, {{96, kWin}, SK_ColorGREEN}, |
| {{128, kAll}, SK_ColorGREEN}, {{256, kMac}, SK_ColorGREEN}, |
| {{256, kNotMac}, SK_ColorBLUE}, {{512, kNotWin}, SK_ColorBLUE}}; |
| } else if (UnimportantIconUpdate()) { |
| ending_stage = |
| GenerateColoredIconList({{kUnimportantIconSize, SK_ColorWHITE}, |
| {kInstallIconSize, SK_ColorYELLOW}, |
| {kLauncherIconSize, SK_ColorGREEN}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| |
| // There should be no effect on the shortcut icons when an unimportant icon |
| // updates. |
| expected_shortcut_colors_if_updated = expected_shortcut_colors_before; |
| } else if (LauncherIconRemove()) { |
| ending_stage = |
| GenerateColoredIconList({{kUnimportantIconSize, SK_ColorRED}, |
| {kInstallIconSize, SK_ColorYELLOW}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| |
| expected_shortcut_colors_if_updated = { |
| {{32, kAll}, SK_ColorYELLOW}, {{48, kAll}, SK_ColorYELLOW}, |
| {{64, kWin}, SK_ColorBLUE}, {{96, kWin}, SK_ColorBLUE}, |
| {{128, kAll}, SK_ColorBLUE}, {{256, kMac}, SK_ColorBLUE}, |
| {{256, kNotMac}, SK_ColorBLUE}, {{512, kNotWin}, SK_ColorBLUE}}; |
| } else if (InstallIconRemove()) { |
| ending_stage = |
| GenerateColoredIconList({{kUnimportantIconSize, SK_ColorRED}, |
| {kLauncherIconSize, SK_ColorGREEN}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| expected_shortcut_colors_if_updated = { |
| {{32, kAll}, SK_ColorGREEN}, {{48, kAll}, SK_ColorGREEN}, |
| {{64, kWin}, SK_ColorGREEN}, {{96, kWin}, SK_ColorGREEN}, |
| {{128, kAll}, SK_ColorGREEN}, {{256, kMac}, SK_ColorGREEN}, |
| {{256, kNotMac}, SK_ColorBLUE}, {{512, kNotWin}, SK_ColorBLUE}}; |
| } else if (UnimportantIconRemove()) { |
| starting_stage = |
| GenerateColoredIconList({{kUnimportantIconSize, SK_ColorBLACK}, |
| {kUnimportantIconSize2, SK_ColorRED}, |
| {kInstallIconSize, SK_ColorYELLOW}, |
| {kLauncherIconSize, SK_ColorGREEN}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| expected_shortcut_colors_before = { |
| {{32, kAll}, SK_ColorYELLOW}, {{48, kAll}, SK_ColorYELLOW}, |
| {{64, kWin}, SK_ColorGREEN}, {{96, kWin}, SK_ColorGREEN}, |
| {{128, kAll}, SK_ColorGREEN}, {{256, kMac}, SK_ColorGREEN}, |
| {{256, kNotMac}, SK_ColorBLUE}, {{512, kNotWin}, SK_ColorBLUE}}; |
| |
| // Removing an unimportant icon should have no effect on other icons. |
| ending_stage = |
| GenerateColoredIconList({{kUnimportantIconSize2, SK_ColorRED}, |
| {kInstallIconSize, SK_ColorYELLOW}, |
| {kLauncherIconSize, SK_ColorGREEN}, |
| {kInstallabilityIconSize, SK_ColorBLUE}}); |
| expected_shortcut_colors_if_updated = expected_shortcut_colors_before; |
| } else if (TitleUpdate()) { |
| ending_stage = starting_stage; // No icon change. |
| expected_shortcut_colors_if_updated = expected_shortcut_colors_before; |
| } else { |
| NOTREACHED(); // Unhandled test input. |
| } |
| |
| OverrideManifest(kManifestTemplate, {app_name, starting_stage}); |
| |
| webapps::AppId app_id; |
| if (IsDefaultApp()) { |
| app_id = InstallDefaultApp(); |
| } else if (IsKioskApp()) { |
| app_id = InstallKioskApp(); |
| } else if (IsPolicyApp()) { |
| app_id = InstallPolicyApp(); |
| } else if (IsWebApp()) { |
| app_id = InstallWebApp(); |
| } else { |
| NOTREACHED(); |
| } |
| |
| const WebApp* web_app = GetProvider().registrar_unsafe().GetAppById(app_id); |
| ASSERT_TRUE(web_app); |
| |
| if (TitleUpdate()) |
| app_name = "Different app name"; |
| |
| OverrideManifest(kManifestTemplate, {app_name, ending_stage}); |
| SCOPED_TRACE(trace + "Icons before: \n" + starting_stage + "\n" + |
| "Icons afer (requested): \n" + ending_stage + "\n"); |
| |
| if (ExpectTitleUpdate() || ExpectIconUpdate()) { |
| DCHECK(!ExpectTitleUpdate() || TitleUpdate()); |
| DCHECK(!ExpectIconUpdate() || AnyIconUpdate()); |
| ASSERT_EQ(ManifestUpdateResult::kAppUpdated, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 1); |
| } else { |
| ASSERT_EQ(ManifestUpdateResult::kAppUpToDate, |
| GetResultAfterPageLoad(GetAppURL())); |
| histogram_tester_.ExpectBucketCount(kUpdateHistogramName, |
| ManifestUpdateResult::kAppUpdated, 0); |
| } |
| |
| if (ExpectIconUpdate()) { |
| ConfirmShortcutColors(app_id, expected_shortcut_colors_if_updated); |
| } else { |
| ConfirmShortcutColors(app_id, expected_shortcut_colors_before); |
| } |
| |
| EXPECT_EQ(ExpectTitleUpdate() ? "Different app name" : "Test app name", |
| GetProvider().registrar_unsafe().GetAppShortName(app_id)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ManifestUpdateManagerBrowserTest_AppIdentityParameterized, |
| testing::Combine( |
| testing::Values(AppIdTestParam::kActionUpdateTitle, |
| AppIdTestParam::kActionUpdateTitleAndLauncherIcon, |
| AppIdTestParam::kActionUpdateLauncherIcon, |
| AppIdTestParam::kActionUpdateInstallIcon, |
| AppIdTestParam::kActionUpdateLauncherAndInstallIcon, |
| AppIdTestParam::kActionUpdateUnimportantIcon, |
| AppIdTestParam::kActionRemoveLauncherIcon, |
| AppIdTestParam::kActionRemoveInstallIcon, |
| AppIdTestParam::kActionRemoveUnimportantIcon, |
| AppIdTestParam::kActionSwitchFromLauncher, |
| AppIdTestParam::kActionSwitchToLauncher), |
| testing::Values(AppIdTestParam::kTypeDefaultApp, |
| AppIdTestParam::kTypeKioskApp, |
| AppIdTestParam::kTypePolicyApp, |
| AppIdTestParam::kTypeWebApp), |
| testing::Values(AppIdTestParam::kWithFlagNone, |
| AppIdTestParam::kWithFlagAppIdDialogForIcon, |
| AppIdTestParam::kWithFlagPolicyAppIdentity, |
| AppIdTestParam::kWithFlagPolicyAppIdentity | |
| AppIdTestParam::kWithFlagAppIdDialogForIcon)), |
| ManifestUpdateManagerBrowserTest_AppIdentityParameterized::ParamToString); |
| |
| } // namespace web_app |