Commit 65320143 authored by Devlin Cronin's avatar Devlin Cronin Committed by Commit Bot

[Web App Migration] Provide a flow for migrating to hard-coded web apps

Currently, the ExternalWebAppManager provides a flow for migrating from
an extension-app to a web app specified in a known directory through a
JSON config. This will be used on ChromeOS.

On other platforms, we'll simply hard-code the web app data, and use it
directly. This CL provides an entry point to do so, allowing for the
specification of default apps that are directly included in the binary.

Bug: 809304, 1118195
Change-Id: I0bb83de21ac1f59bdf62fcf313e98db3889bd01f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2417535
Commit-Queue: Devlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarAlan Cutter <alancutter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#809081}
parent b1f7558f
...@@ -68,6 +68,8 @@ source_set("web_applications") { ...@@ -68,6 +68,8 @@ source_set("web_applications") {
"pending_app_manager_impl.h", "pending_app_manager_impl.h",
"pending_app_registration_task.cc", "pending_app_registration_task.cc",
"pending_app_registration_task.h", "pending_app_registration_task.h",
"preinstalled_web_apps.cc",
"preinstalled_web_apps.h",
"web_app.cc", "web_app.cc",
"web_app.h", "web_app.h",
"web_app_database.cc", "web_app_database.cc",
...@@ -205,6 +207,7 @@ source_set("web_applications_unit_tests") { ...@@ -205,6 +207,7 @@ source_set("web_applications_unit_tests") {
"external_web_app_manager_unittest.cc", "external_web_app_manager_unittest.cc",
"external_web_app_utils_unittest.cc", "external_web_app_utils_unittest.cc",
"pending_app_manager_impl_unittest.cc", "pending_app_manager_impl_unittest.cc",
"preinstalled_web_apps_unittest.cc",
"system_web_app_manager_unittest.cc", "system_web_app_manager_unittest.cc",
"web_app_database_unittest.cc", "web_app_database_unittest.cc",
"web_app_icon_manager_unittest.cc", "web_app_icon_manager_unittest.cc",
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/browser/web_applications/external_web_app_manager.h" #include "chrome/browser/web_applications/external_web_app_manager.h"
#include <iterator>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
...@@ -29,6 +30,7 @@ ...@@ -29,6 +30,7 @@
#include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/components/web_app_install_utils.h" #include "chrome/browser/web_applications/components/web_app_install_utils.h"
#include "chrome/browser/web_applications/external_web_app_utils.h" #include "chrome/browser/web_applications/external_web_app_utils.h"
#include "chrome/browser/web_applications/preinstalled_web_apps.h"
#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
...@@ -219,6 +221,14 @@ void ExternalWebAppManager::SynchronizeExternalInstallOptions( ...@@ -219,6 +221,14 @@ void ExternalWebAppManager::SynchronizeExternalInstallOptions(
PendingAppManager::SynchronizeCallback callback, PendingAppManager::SynchronizeCallback callback,
std::vector<ExternalInstallOptions> desired_apps_install_options) { std::vector<ExternalInstallOptions> desired_apps_install_options) {
DCHECK(pending_app_manager_); DCHECK(pending_app_manager_);
// Add in any web apps that should be pre-installed.
std::vector<ExternalInstallOptions> default_apps = GetPreinstalledWebApps();
desired_apps_install_options.insert(
desired_apps_install_options.end(),
std::make_move_iterator(default_apps.begin()),
std::make_move_iterator(default_apps.end()));
pending_app_manager_->SynchronizeInstalledApps( pending_app_manager_->SynchronizeInstalledApps(
std::move(desired_apps_install_options), std::move(desired_apps_install_options),
ExternalInstallSource::kExternalDefault, std::move(callback)); ExternalInstallSource::kExternalDefault, std::move(callback));
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "chrome/browser/web_applications/components/external_app_install_features.h" #include "chrome/browser/web_applications/components/external_app_install_features.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h" #include "chrome/browser/web_applications/components/web_app_helpers.h"
#include "chrome/browser/web_applications/external_web_app_manager.h" #include "chrome/browser/web_applications/external_web_app_manager.h"
#include "chrome/browser/web_applications/preinstalled_web_apps.h"
#include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
...@@ -156,7 +157,9 @@ class ExternalWebAppMigrationBrowserTest : public InProcessBrowserTest { ...@@ -156,7 +157,9 @@ class ExternalWebAppMigrationBrowserTest : public InProcessBrowserTest {
run_loop.Run(); run_loop.Run();
} }
void SyncExternalWebApps(bool expect_install, bool expect_uninstall) { void SyncExternalWebApps(bool expect_install,
bool expect_uninstall,
bool pass_config = true) {
base::RunLoop run_loop; base::RunLoop run_loop;
auto callback = base::BindLambdaForTesting( auto callback = base::BindLambdaForTesting(
...@@ -173,21 +176,24 @@ class ExternalWebAppMigrationBrowserTest : public InProcessBrowserTest { ...@@ -173,21 +176,24 @@ class ExternalWebAppMigrationBrowserTest : public InProcessBrowserTest {
run_loop.Quit(); run_loop.Quit();
}); });
std::string external_web_app_config = base::ReplaceStringPlaceholders( std::vector<std::string> configs;
R"({ if (pass_config) {
"app_url": "$1", std::string external_web_app_config = base::ReplaceStringPlaceholders(
"launch_container": "window", R"({
"user_type": ["unmanaged"], "app_url": "$1",
"feature_name": "$2", "launch_container": "window",
"uninstall_and_replace": ["$3"] "user_type": ["unmanaged"],
})", "feature_name": "$2",
{GetWebAppUrl().spec(), kMigrationFlag, kExtensionId}, nullptr); "uninstall_and_replace": ["$3"]
})",
{GetWebAppUrl().spec(), kMigrationFlag, kExtensionId}, nullptr);
configs.push_back(std::move(external_web_app_config));
}
WebAppProvider::Get(profile()) WebAppProvider::Get(profile())
->external_web_app_manager_for_testing() ->external_web_app_manager_for_testing()
.SynchronizeAppsForTesting(std::make_unique<FileUtilsWrapper>(), .SynchronizeAppsForTesting(std::make_unique<FileUtilsWrapper>(),
{external_web_app_config}, configs, std::move(callback));
std::move(callback));
run_loop.Run(); run_loop.Run();
} }
...@@ -416,4 +422,55 @@ IN_PROC_BROWSER_TEST_F(ExternalWebAppMigrationBrowserTest, ...@@ -416,4 +422,55 @@ IN_PROC_BROWSER_TEST_F(ExternalWebAppMigrationBrowserTest,
} }
} }
// Tests the migration from an extension-app to a preinstalled web app provided
// by the preinstalled apps (rather than an external config).
IN_PROC_BROWSER_TEST_F(ExternalWebAppMigrationBrowserTest,
MigrateToPreinstalledWebApp) {
// Set up pre-migration state.
{
// Override the preinstalled apps to be empty.
ScopedTestingPreinstalledAppData empty_preinstalled_apps;
EXPECT_EQ(0u, GetPreinstalledWebApps().size());
ASSERT_FALSE(IsExternalAppInstallFeatureEnabled(kMigrationFlag));
SyncExternalExtensions();
SyncExternalWebApps(/*expect_install=*/false, /*expect_uninstall=*/false,
/*pass_config=*/false);
EXPECT_FALSE(IsWebAppInstalled());
EXPECT_TRUE(IsExtensionAppInstalled());
}
// Migrate extension app to web app.
{
base::AutoReset<bool> testing_scope =
SetExternalAppInstallFeatureAlwaysEnabledForTesting();
ASSERT_TRUE(IsExternalAppInstallFeatureEnabled(kMigrationFlag));
SyncExternalExtensions();
// Extension sticks around to be uninstalled by the replacement web app.
EXPECT_TRUE(IsExtensionAppInstalled());
{
extensions::TestExtensionRegistryObserver uninstall_observer(
extensions::ExtensionRegistry::Get(profile()));
ScopedTestingPreinstalledAppData preinstalled_apps;
preinstalled_apps.apps.push_back(
{GetWebAppUrl(), kMigrationFlag, kExtensionId});
EXPECT_EQ(1u, GetPreinstalledWebApps().size());
SyncExternalWebApps(/*expect_install=*/true, /*expect_uninstall=*/false,
/*pass_config=*/false);
EXPECT_TRUE(IsWebAppInstalled());
scoped_refptr<const extensions::Extension> uninstalled_app =
uninstall_observer.WaitForExtensionUninstalled();
EXPECT_EQ(uninstalled_app->id(), kExtensionId);
EXPECT_FALSE(IsExtensionAppInstalled());
}
}
}
} // namespace web_app } // namespace web_app
// 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/preinstalled_web_apps.h"
#include "base/feature_list.h"
#include "chrome/browser/web_applications/components/external_app_install_features.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/common/chrome_features.h"
namespace web_app {
namespace {
std::vector<PreinstalledAppData>* g_preinstalled_app_data_for_testing = nullptr;
std::vector<PreinstalledAppData> GetPreinstalledAppData() {
if (g_preinstalled_app_data_for_testing)
return *g_preinstalled_app_data_for_testing;
std::vector<PreinstalledAppData> preinstalled_app_data = {
// TODO(devlin): Add the web apps that should come preinstalled, gated
// by OS.
};
return preinstalled_app_data;
}
} // namespace
ScopedTestingPreinstalledAppData::ScopedTestingPreinstalledAppData() {
DCHECK_EQ(nullptr, g_preinstalled_app_data_for_testing);
g_preinstalled_app_data_for_testing = &apps;
}
ScopedTestingPreinstalledAppData::~ScopedTestingPreinstalledAppData() {
DCHECK_EQ(&apps, g_preinstalled_app_data_for_testing);
g_preinstalled_app_data_for_testing = nullptr;
}
std::vector<ExternalInstallOptions> GetPreinstalledWebApps() {
std::vector<ExternalInstallOptions> install_options_list;
if (!base::FeatureList::IsEnabled(features::kDefaultWebAppInstallation))
return install_options_list;
std::vector<PreinstalledAppData> preinstalled_app_data =
GetPreinstalledAppData();
for (const auto& app_data : preinstalled_app_data) {
if (!IsExternalAppInstallFeatureEnabled(app_data.feature_name))
continue;
ExternalInstallOptions options(app_data.install_url, DisplayMode::kBrowser,
ExternalInstallSource::kExternalDefault);
// Preinstalled web apps should not have OS shortcuts of any kind.
options.add_to_applications_menu = false;
options.add_to_desktop = false;
options.add_to_quick_launch_bar = false;
options.add_to_search = false;
options.add_to_management = false;
options.require_manifest = true;
options.uninstall_and_replace = {app_data.app_id_to_replace};
install_options_list.push_back(std::move(options));
}
return install_options_list;
}
} // namespace web_app
// 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_PREINSTALLED_WEB_APPS_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APPS_H_
#include <string>
#include <vector>
#include "chrome/browser/web_applications/components/external_install_options.h"
#include "url/gurl.h"
namespace web_app {
struct ExternalInstallOptions;
// A simple struct that contains the relevant data for web apps that come
// preinstalled. These are used to generate the ExternalInstallOptions, which
// in turn are used to install the apps.
struct PreinstalledAppData {
// The install url for the app.
GURL install_url;
// The name of a feature which must be enabled for the app to be installed.
const char* feature_name = nullptr;
// The ID of an existing app to uninstall when this app is installed.
const char* app_id_to_replace = nullptr;
};
// A scoped helper to provide a testing set of preinstalled app data. This will
// replace the default set.
struct ScopedTestingPreinstalledAppData {
ScopedTestingPreinstalledAppData();
ScopedTestingPreinstalledAppData(const ScopedTestingPreinstalledAppData&) =
delete;
ScopedTestingPreinstalledAppData& operator=(
const ScopedTestingPreinstalledAppData&) = delete;
~ScopedTestingPreinstalledAppData();
std::vector<PreinstalledAppData> apps;
};
// Returns the list of web apps that should be pre-installed on new profiles.
std::vector<ExternalInstallOptions> GetPreinstalledWebApps();
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APPS_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/preinstalled_web_apps.h"
#include <memory>
#include <vector>
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/web_applications/components/external_app_install_features.h"
#include "chrome/common/chrome_features.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace web_app {
namespace {
constexpr char kTestAppId1[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
constexpr char kTestAppId2[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
std::unique_ptr<ScopedTestingPreinstalledAppData> CreateStubPreinstalledApps() {
auto app_data = std::make_unique<ScopedTestingPreinstalledAppData>();
app_data->apps.push_back({GURL("https://one.example"),
kMigrateDefaultChromeAppToWebAppsGSuite.name,
kTestAppId1});
app_data->apps.push_back({GURL("https://two.example"),
kMigrateDefaultChromeAppToWebAppsNonGSuite.name,
kTestAppId2});
return app_data;
}
} // namespace
using PreinstalledWebAppsTest = testing::Test;
TEST(PreinstalledWebAppsTest, NoAppsWithoutDefaultWebAppInstallation) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(features::kDefaultWebAppInstallation);
base::AutoReset<bool> testing_scope =
SetExternalAppInstallFeatureAlwaysEnabledForTesting();
auto scoped_preinstalled_apps = CreateStubPreinstalledApps();
EXPECT_EQ(0u, GetPreinstalledWebApps().size());
}
TEST(PreinstalledWebAppsTest, AppsOnlyReturnedIfSpecificFeatureEnabled) {
auto scoped_preinstalled_apps = CreateStubPreinstalledApps();
// The preinstalled apps depend on two different features (in addition to
// default web app installation).
{
// With both features disabled, no apps are included.
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({features::kDefaultWebAppInstallation},
{kMigrateDefaultChromeAppToWebAppsGSuite,
kMigrateDefaultChromeAppToWebAppsNonGSuite});
EXPECT_EQ(0u, GetPreinstalledWebApps().size());
}
{
// Enable a single feature; only the corresponding app should be returned.
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({features::kDefaultWebAppInstallation,
kMigrateDefaultChromeAppToWebAppsGSuite},
{kMigrateDefaultChromeAppToWebAppsNonGSuite});
std::vector<ExternalInstallOptions> install_options =
GetPreinstalledWebApps();
ASSERT_EQ(1u, install_options.size());
ASSERT_EQ(1u, install_options[0].uninstall_and_replace.size());
EXPECT_EQ(kTestAppId1, install_options[0].uninstall_and_replace[0]);
}
{
// Enable the second feature; the corresponding app should be returned.
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({features::kDefaultWebAppInstallation,
kMigrateDefaultChromeAppToWebAppsNonGSuite},
{kMigrateDefaultChromeAppToWebAppsGSuite});
std::vector<ExternalInstallOptions> install_options =
GetPreinstalledWebApps();
ASSERT_EQ(1u, install_options.size());
ASSERT_EQ(1u, install_options[0].uninstall_and_replace.size());
EXPECT_EQ(kTestAppId2, install_options[0].uninstall_and_replace[0]);
}
}
} // namespace web_app
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