Commit 0ebb9509 authored by Alan Cutter's avatar Alan Cutter Committed by Commit Bot

Support offline manifest install for default web app configs

This CL adds a new "offline_manifest" field to default web app JSON
configs.

Example:
{
  "app_url": "https://example.org/",
  "launch_container": "window",
  "user_type": ["unmanaged"],
  "offline_manifest": {
    "name": "Example",
    "start_url": "https://example.org/",
    "scope": "https://example.org/",
    "display": "standalone",
    "icon_any_pngs": ["icon.png"],
    "theme_color_argb_hex": "FFFFFFFF"
  }
}

This is to support installing default web apps on Chrome OS without
spawning a WebContents and fetching the install URL of every default app.
Additionally this is to avoid overwhelming the default web apps when
every Chrome user is migrated over to default web apps instead of
the current default Chrome apps.

Design doc:
https://docs.google.com/document/d/1pk8aLnp24AVsWwC6OaCgYZHGGqvyNxekB04rzuTGDVM/edit#heading=h.9f9lhtrsvs1k

Bug: 1119710
Change-Id: Ibe36631e4dbb540165cd67944b5ec6c8a47e8a2e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2352450
Commit-Queue: Alan Cutter <alancutter@chromium.org>
Reviewed-by: default avatarGlen Robertson <glenrob@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800066}
parent e63e3c19
......@@ -19,6 +19,7 @@
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/app_menu_model.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/web_applications/components/app_icon_manager.h"
#include "chrome/browser/web_applications/components/app_registrar.h"
#include "chrome/browser/web_applications/components/external_install_options.h"
#include "chrome/browser/web_applications/components/install_finalizer.h"
......@@ -28,7 +29,6 @@
#include "chrome/browser/web_applications/components/web_app_provider_base.h"
#include "chrome/browser/web_applications/components/web_app_tab_helper_base.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/web_application_info.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
#include "content/public/browser/notification_service.h"
......@@ -217,4 +217,22 @@ void UninstallWebApp(Profile* profile, const AppId& app_id) {
base::DoNothing());
}
SkColor ReadAppIconPixel(Profile* profile,
const AppId& app_id,
SquareSizePx size,
int x,
int y) {
SkColor result;
base::RunLoop run_loop;
WebAppProviderBase::GetProviderBase(profile)->icon_manager().ReadIcons(
app_id, IconPurpose::ANY, {size},
base::BindLambdaForTesting(
[&](std::map<SquareSizePx, SkBitmap> icon_bitmaps) {
run_loop.Quit();
result = icon_bitmaps.at(size).getColor(x, y);
}));
run_loop.Run();
return result;
}
} // namespace web_app
......@@ -8,6 +8,7 @@
#include <memory>
#include "chrome/browser/web_applications/components/web_app_id.h"
#include "chrome/common/web_application_info.h"
#include "url/gurl.h"
class Browser;
......@@ -64,6 +65,13 @@ bool IsBrowserOpen(const Browser* test_browser);
// Launches the app for |app| in |profile|.
void UninstallWebApp(Profile* profile, const AppId& app_id);
// Synchronous read of an app icon pixel.
SkColor ReadAppIconPixel(Profile* profile,
const AppId& app_id,
SquareSizePx size,
int x,
int y);
} // namespace web_app
#endif // CHROME_BROWSER_UI_WEB_APPLICATIONS_TEST_WEB_APP_BROWSERTEST_UTIL_H_
......@@ -59,6 +59,8 @@ source_set("web_applications") {
sources = [
"external_web_app_manager.cc",
"external_web_app_manager.h",
"external_web_app_utils.cc",
"external_web_app_utils.h",
"file_utils_wrapper.cc",
"file_utils_wrapper.h",
"pending_app_install_task.cc",
......@@ -194,6 +196,7 @@ source_set("web_applications_unit_tests") {
sources = [
"daily_metrics_helper_unittest.cc",
"external_web_app_manager_unittest.cc",
"external_web_app_utils_unittest.cc",
"pending_app_manager_impl_unittest.cc",
"system_web_app_manager_unittest.cc",
"web_app_database_unittest.cc",
......
......@@ -12,6 +12,7 @@
#include "base/memory/weak_ptr.h"
#include "chrome/browser/web_applications/components/external_install_options.h"
#include "chrome/browser/web_applications/components/pending_app_manager.h"
#include "chrome/browser/web_applications/file_utils_wrapper.h"
namespace base {
class FilePath;
......@@ -38,8 +39,10 @@ class ExternalWebAppManager {
//
// This function performs file I/O, and must not be scheduled on UI threads.
static std::vector<ExternalInstallOptions>
ScanDirForExternalWebAppsForTesting(const base::FilePath& dir,
Profile* profile);
ScanDirForExternalWebAppsForTesting(
std::unique_ptr<FileUtilsWrapper> file_utils,
const base::FilePath& dir,
Profile* profile);
using ScanCallback =
base::OnceCallback<void(std::vector<ExternalInstallOptions>)>;
......@@ -49,11 +52,14 @@ class ExternalWebAppManager {
static void SkipStartupScanForTesting();
void SynchronizeAppsForTesting(
std::unique_ptr<FileUtilsWrapper> file_utils,
std::vector<std::string> app_configs,
PendingAppManager::SynchronizeCallback callback);
private:
void OnScanForExternalWebApps(std::vector<ExternalInstallOptions>);
void SynchronizeExternalInstallOptions(
PendingAppManager::SynchronizeCallback callback,
std::vector<ExternalInstallOptions>);
PendingAppManager* pending_app_manager_ = nullptr;
Profile* const profile_;
......
......@@ -4,9 +4,14 @@
#include "chrome/browser/web_applications/external_web_app_manager.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/test/bind_test_util.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h"
#include "chrome/browser/web_applications/test/test_file_utils.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
......@@ -15,13 +20,6 @@
#include "extensions/browser/test_extension_registry_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kChromeAppDirectory[] = "app";
const char kChromeAppName[] = "App Test";
} // namespace
namespace web_app {
class ExternalWebAppManagerBrowserTest
......@@ -35,6 +33,10 @@ class ExternalWebAppManagerBrowserTest
return embedded_test_server()->GetURL("/web_apps/basic.html");
}
const AppRegistrar& registrar() {
return WebAppProvider::Get(browser()->profile())->registrar();
}
~ExternalWebAppManagerBrowserTest() override = default;
};
......@@ -43,6 +45,8 @@ IN_PROC_BROWSER_TEST_F(ExternalWebAppManagerBrowserTest, UninstallAndReplace) {
Profile* profile = browser()->profile();
// Install Chrome app to be replaced.
const char kChromeAppDirectory[] = "app";
const char kChromeAppName[] = "App Test";
const extensions::Extension* app = InstallExtensionWithSourceAndFlags(
test_data_dir_.AppendASCII(kChromeAppDirectory), 1,
extensions::Manifest::INTERNAL, extensions::Extension::NO_FLAGS);
......@@ -57,6 +61,7 @@ IN_PROC_BROWSER_TEST_F(ExternalWebAppManagerBrowserTest, UninstallAndReplace) {
WebAppProvider::Get(profile)
->external_web_app_manager_for_testing()
.SynchronizeAppsForTesting(
std::make_unique<FileUtilsWrapper>(),
{base::ReplaceStringPlaceholders(
R"({
"app_url": "$1",
......@@ -80,4 +85,71 @@ IN_PROC_BROWSER_TEST_F(ExternalWebAppManagerBrowserTest, UninstallAndReplace) {
EXPECT_EQ(app, uninstalled_app.get());
}
// TODO(crbug.com/1119710): Loading icon.png is flaky on Windows.
#if defined(OS_WIN)
#define MAYBE_OfflineManifest DISABLED_OfflineManifest
#else
#define MAYBE_OfflineManifest OfflineManifest
#endif
IN_PROC_BROWSER_TEST_F(ExternalWebAppManagerBrowserTest,
MAYBE_OfflineManifest) {
ASSERT_TRUE(embedded_test_server()->Start());
Profile* profile = browser()->profile();
constexpr char kAppInstallUrl[] = "https://test.org/install.html";
constexpr char kAppName[] = "Offline app name";
constexpr char kAppUrl[] = "https://test.org/start.html";
constexpr char kAppScope[] = "https://test.org/";
AppId app_id = GenerateAppIdFromURL(GURL(kAppUrl));
base::FilePath source_root_dir;
CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir));
base::FilePath test_icon_path = source_root_dir.Append(GetChromeTestDataDir())
.AppendASCII("web_apps/blue-192.png");
EXPECT_FALSE(registrar().IsInstalled(app_id));
// Sync default web apps.
base::RunLoop sync_run_loop;
WebAppProvider::Get(profile)
->external_web_app_manager_for_testing()
.SynchronizeAppsForTesting(
TestFileUtils::Create(
{{base::FilePath(FILE_PATH_LITERAL("test_dir/icon.png")),
test_icon_path}}),
{base::ReplaceStringPlaceholders(
R"({
"app_url": "$1",
"launch_container": "window",
"user_type": ["unmanaged"],
"offline_manifest": {
"name": "$2",
"start_url": "$3",
"scope": "$4",
"display": "minimal-ui",
"theme_color_argb_hex": "AABBCCDD",
"icon_any_pngs": ["icon.png"]
}
})",
{kAppInstallUrl, kAppName, kAppUrl, kAppScope}, nullptr)},
base::BindLambdaForTesting(
[&](std::map<GURL, InstallResultCode> install_results,
std::map<GURL, bool> uninstall_results) {
EXPECT_EQ(install_results.at(GURL(kAppInstallUrl)),
InstallResultCode::kSuccessNewInstall);
sync_run_loop.Quit();
}));
sync_run_loop.Run();
EXPECT_TRUE(registrar().IsInstalled(app_id));
EXPECT_EQ(registrar().GetAppShortName(app_id), kAppName);
EXPECT_EQ(registrar().GetAppLaunchURL(app_id).spec(), kAppUrl);
EXPECT_EQ(registrar().GetAppScope(app_id).spec(), kAppScope);
// theme_color must be installed opaque.
EXPECT_EQ(registrar().GetAppThemeColor(app_id),
SkColorSetARGB(0xFF, 0xBB, 0xCC, 0xDD));
EXPECT_EQ(ReadAppIconPixel(profile, app_id, /*size=*/192, /*x=*/0, /*y=*/0),
SK_ColorBLUE);
}
} // namespace web_app
......@@ -117,7 +117,8 @@ class ScanDirForExternalWebAppsTest : public testing::Test {
std::vector<ExternalInstallOptions> ScanTestDirForExternalWebApps(
const std::string& dir) {
return ExternalWebAppManager::ScanDirForExternalWebAppsForTesting(
GetTestDir(dir), CreateProfile().get());
std::make_unique<FileUtilsWrapper>(), GetTestDir(dir),
CreateProfile().get());
}
// Helper that creates simple test profile.
......
......@@ -185,7 +185,8 @@ class ExternalWebAppMigrationBrowserTest : public InProcessBrowserTest {
WebAppProvider::Get(profile())
->external_web_app_manager_for_testing()
.SynchronizeAppsForTesting({external_web_app_config},
.SynchronizeAppsForTesting(std::make_unique<FileUtilsWrapper>(),
{external_web_app_config},
std::move(callback));
run_loop.Run();
......
This diff is collapsed.
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WEB_APPLICATIONS_EXTERNAL_WEB_APP_UTILS_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_EXTERNAL_WEB_APP_UTILS_H_
#include <string>
#include "base/optional.h"
#include "chrome/browser/web_applications/components/external_install_options.h"
namespace base {
class FilePath;
class Value;
} // namespace base
namespace web_app {
class FileUtilsWrapper;
base::Optional<ExternalInstallOptions> ParseConfig(
FileUtilsWrapper& file_utils,
const base::FilePath& dir,
const base::FilePath& file,
const std::string& user_type,
const base::Value& app_config);
WebApplicationInfoFactory ParseOfflineManifest(
FileUtilsWrapper& file_utils,
const base::FilePath& dir,
const base::FilePath& file,
const base::Value& offline_manifest);
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_EXTERNAL_WEB_APP_UTILS_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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/external_web_app_utils.h"
#include "base/files/file_path.h"
#include "base/json/json_reader.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/web_applications/test/test_file_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace web_app {
class ExternalWebAppUtilsTest : public testing::Test {
public:
ExternalWebAppUtilsTest() = default;
~ExternalWebAppUtilsTest() override = default;
// testing::Test:
void SetUp() override {
testing::Test::SetUp();
base::FilePath source_root_dir;
CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir));
file_utils_ = TestFileUtils::Create({
{base::FilePath(FILE_PATH_LITERAL("test_dir/icon.png")),
source_root_dir.AppendASCII("chrome/test/data/web_apps/blue-192.png")},
{base::FilePath(FILE_PATH_LITERAL("test_dir/basic.html")),
source_root_dir.AppendASCII("chrome/test/data/web_apps/basic.html")},
});
}
WebApplicationInfoFactory ParseOfflineManifest(
const char* offline_manifest_string) {
base::Optional<base::Value> offline_manifest =
base::JSONReader::Read(offline_manifest_string);
DCHECK(offline_manifest);
return ::web_app::ParseOfflineManifest(
*file_utils_, base::FilePath(FILE_PATH_LITERAL("test_dir")),
base::FilePath(FILE_PATH_LITERAL("test_dir/test.json")),
*offline_manifest);
}
protected:
std::unique_ptr<TestFileUtils> file_utils_;
};
// ParseConfig() is tested by ScanDirForExternalWebAppsTest.
// TODO(crbug.com/1119710): Loading icon.png is flaky on Windows.
#if defined(OS_WIN)
#define MAYBE_OfflineManifestValid DISABLED_OfflineManifestValid
#else
#define MAYBE_OfflineManifestValid OfflineManifestValid
#endif
TEST_F(ExternalWebAppUtilsTest, MAYBE_OfflineManifestValid) {
std::unique_ptr<WebApplicationInfo> app_info = ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"],
"theme_color_argb_hex": "AABBCCDD"
}
)")
.Run();
EXPECT_TRUE(app_info);
EXPECT_EQ(app_info->title, base::UTF8ToUTF16("Test App"));
EXPECT_EQ(app_info->app_url, GURL("https://test.org/start.html"));
EXPECT_EQ(app_info->scope, GURL("https://test.org/"));
EXPECT_EQ(app_info->display_mode, DisplayMode::kStandalone);
EXPECT_EQ(app_info->icon_bitmaps_any.size(), 1u);
EXPECT_EQ(app_info->icon_bitmaps_any.at(192).getColor(0, 0), SK_ColorBLUE);
EXPECT_EQ(app_info->theme_color, SkColorSetARGB(0xFF, 0xBB, 0xCC, 0xDD));
}
TEST_F(ExternalWebAppUtilsTest, OfflineManifestName) {
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"]
}
)")) << "name is required";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": 400,
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"]
}
)")) << "name is string";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"]
}
)")) << "name is non-empty";
}
TEST_F(ExternalWebAppUtilsTest, OfflineManifestStartUrl) {
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"]
}
)")) << "start_url is required";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "not a url",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"]
}
)")) << "start_url is valid";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/inner/",
"display": "standalone",
"icon_any_pngs": ["icon.png"]
}
)")) << "start_url is within scope";
}
TEST_F(ExternalWebAppUtilsTest, OfflineManifestScope) {
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"display": "standalone",
"icon_any_pngs": ["icon.png"]
}
)")) << "scope is required";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "not a url",
"display": "standalone",
"icon_any_pngs": ["icon.png"]
}
)")) << "start_url is valid";
}
// TODO(crbug.com/1119710): Loading icon.png is flaky on Windows.
#if defined(OS_WIN)
#define MAYBE_OfflineManifestDisplay DISABLED_OfflineManifestDisplay
#else
#define MAYBE_OfflineManifestDisplay OfflineManifestDisplay
#endif
TEST_F(ExternalWebAppUtilsTest, MAYBE_OfflineManifestDisplay) {
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"icon_any_pngs": ["icon.png"]
}
)")) << "display is required";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "tab",
"icon_any_pngs": ["icon.png"]
}
)")) << "display is valid";
EXPECT_TRUE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"]
}
)")) << "display can be standalone";
EXPECT_TRUE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "browser",
"icon_any_pngs": ["icon.png"]
}
)")) << "display can be browser";
EXPECT_TRUE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "minimal-ui",
"icon_any_pngs": ["icon.png"]
}
)")) << "display can be minimal-ui";
EXPECT_TRUE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "fullscreen",
"icon_any_pngs": ["icon.png"]
}
)")) << "display can be fullscreen";
}
TEST_F(ExternalWebAppUtilsTest, OfflineManifestIconAnyPngs) {
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone"
}
)")) << "icon_any_pngs is required";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": "icon.png"
}
)")) << "icon_any_pngs is valid";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": [{
"src": "https://test.org/icon.png",
"sizes": "144x144",
"type": "image/png"
}]
}
)")) << "icon_any_pngs is valid";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["does-not-exist.png"]
}
)")) << "icon_any_pngs exists";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["basic.html"]
}
)")) << "icon_any_pngs is a PNG";
}
TEST_F(ExternalWebAppUtilsTest, OfflineManifestThemeColorArgbHex) {
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"],
"theme_color_argb_hex": 12345
}
)")) << "theme_color_argb_hex is valid";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"],
"theme_color_argb_hex": "blue"
}
)")) << "theme_color_argb_hex is valid";
EXPECT_FALSE(ParseOfflineManifest(R"(
{
"name": "Test App",
"start_url": "https://test.org/start.html",
"scope": "https://test.org/",
"display": "standalone",
"icon_any_pngs": ["icon.png"],
"theme_color_argb_hex": "#ff0000"
}
)")) << "theme_color_argb_hex is valid";
}
} // namespace web_app
......@@ -54,7 +54,8 @@ class FileUtilsWrapper {
bool IsDirectoryEmpty(const base::FilePath& dir_path);
bool ReadFileToString(const base::FilePath& path, std::string* contents);
virtual bool ReadFileToString(const base::FilePath& path,
std::string* contents);
bool DeleteFile(const base::FilePath& path, bool recursive);
......
......@@ -19,6 +19,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/web_applications/components/app_icon_manager.h"
#include "chrome/browser/web_applications/components/app_registry_controller.h"
#include "chrome/browser/web_applications/components/app_shortcut_manager.h"
......@@ -1051,16 +1052,9 @@ IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest,
ManifestUpdateResult::kAppUpdated, 1);
AwaitShortcutsUpdated(SK_ColorBLUE);
// Check that the installed icon is now blue.
base::RunLoop run_loop;
GetProvider().icon_manager().ReadIcons(
app_id, IconPurpose::ANY, {192},
base::BindLambdaForTesting(
[&run_loop](std::map<SquareSizePx, SkBitmap> icon_bitmaps) {
run_loop.Quit();
EXPECT_EQ(icon_bitmaps.at(192).getColor(0, 0), SK_ColorBLUE);
}));
run_loop.Run();
EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/192,
/*x=*/0, /*y=*/0),
SK_ColorBLUE);
}
IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest,
......@@ -1114,17 +1108,12 @@ IN_PROC_BROWSER_TEST_P(ManifestUpdateManagerBrowserTest,
histogram_tester_.ExpectBucketCount(
kUpdateHistogramName, ManifestUpdateResult::kIconDownloadFailed, 1);
// Check that the installed icon is still black.
base::RunLoop run_loop;
GetProvider().icon_manager().ReadIcons(
app_id, IconPurpose::ANY, {48, 192},
base::BindLambdaForTesting(
[&run_loop](std::map<SquareSizePx, SkBitmap> icon_bitmaps) {
run_loop.Quit();
EXPECT_EQ(icon_bitmaps.at(48).getColor(0, 0), SK_ColorBLACK);
EXPECT_EQ(icon_bitmaps.at(192).getColor(0, 0), SK_ColorBLACK);
}));
run_loop.Run();
EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/48, /*x=*/0,
/*y=*/0),
SK_ColorBLACK);
EXPECT_EQ(ReadAppIconPixel(browser()->profile(), app_id, /*size=*/192,
/*x=*/0, /*y=*/0),
SK_ColorBLACK);
}
class ManifestUpdateManagerSystemAppBrowserTest
......
......@@ -281,7 +281,13 @@ void PendingAppManagerImpl::CurrentInstallationFinished(
bool is_local_resource =
launch_url.scheme() == content::kChromeUIScheme ||
launch_url.scheme() == content::kChromeUIUntrustedScheme;
if (!launch_url.is_empty() && !is_local_resource)
// TODO(crbug.com/809304): Call CreateWebContentsIfNecessary() instead of
// checking web_contents_ once major migration of default hosted apps to web
// apps has completed.
// Temporarily using offline manifest migrations (in which |web_contents_|
// is nullptr) in order to avoid overwhelming migrated-to web apps with hits
// for service worker registrations.
if (!launch_url.is_empty() && !is_local_resource && web_contents_)
pending_registrations_.push_back(launch_url);
}
......
......@@ -2,13 +2,22 @@
// 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/test/test_file_utils.h"
#include <utility>
#include "chrome/browser/web_applications/test/test_file_utils.h"
#include "base/files/file_path.h"
namespace web_app {
TestFileUtils::TestFileUtils() = default;
std::unique_ptr<TestFileUtils> TestFileUtils::Create(
std::map<base::FilePath, base::FilePath> read_file_rerouting) {
return std::make_unique<TestFileUtils>(read_file_rerouting);
}
TestFileUtils::TestFileUtils(
std::map<base::FilePath, base::FilePath> read_file_rerouting)
: read_file_rerouting_(read_file_rerouting) {}
TestFileUtils::TestFileUtils(const TestFileUtils&) = default;
......@@ -46,6 +55,16 @@ int TestFileUtils::WriteFile(const base::FilePath& filename,
return FileUtilsWrapper::WriteFile(filename, data, size);
}
bool TestFileUtils::ReadFileToString(const base::FilePath& path,
std::string* contents) {
for (const std::pair<const base::FilePath, base::FilePath>& route :
read_file_rerouting_) {
if (route.first == path)
return FileUtilsWrapper::ReadFileToString(route.second, contents);
}
return FileUtilsWrapper::ReadFileToString(path, contents);
}
bool TestFileUtils::DeleteFileRecursively(const base::FilePath& path) {
return delete_file_recursively_result_
? *delete_file_recursively_result_
......
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_WEB_APPLICATIONS_TEST_TEST_FILE_UTILS_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_TEST_TEST_FILE_UTILS_H_
#include <map>
#include <memory>
#include "base/macros.h"
......@@ -16,7 +17,13 @@ namespace web_app {
// A testing implementation to intercept calls to the file system.
class TestFileUtils : public FileUtilsWrapper {
public:
TestFileUtils();
// Initializer list type deduction does not work through std::make_unique so
// provide this helper function.
static std::unique_ptr<TestFileUtils> Create(
std::map<base::FilePath, base::FilePath> read_file_rerouting);
explicit TestFileUtils(
std::map<base::FilePath, base::FilePath> read_file_rerouting = {});
TestFileUtils(const TestFileUtils&);
~TestFileUtils() override;
......@@ -25,6 +32,8 @@ class TestFileUtils : public FileUtilsWrapper {
int WriteFile(const base::FilePath& filename,
const char* data,
int size) override;
bool ReadFileToString(const base::FilePath& path,
std::string* contents) override;
bool DeleteFileRecursively(const base::FilePath& path) override;
static constexpr int kNoLimit = -1;
......@@ -35,6 +44,7 @@ class TestFileUtils : public FileUtilsWrapper {
void SetNextDeleteFileRecursivelyResult(base::Optional<bool> delete_result);
private:
std::map<base::FilePath, base::FilePath> read_file_rerouting_;
base::Optional<bool> delete_file_recursively_result_;
int remaining_disk_space_ = kNoLimit;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment