#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation_traits.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/app_service_test.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/externally_managed_app_manager.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
#include "chrome/browser/web_applications/test/test_web_app_url_loader.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/test/base/testing_profile.h"
#include "components/account_id/account_id.h"
#include "components/app_constants/constants.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/features.h"
#include "components/services/app_service/public/cpp/icon_types.h"
#include "components/services/app_service/public/cpp/intent_util.h"
#include "components/services/app_service/public/cpp/permission.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/components/arc/test/fake_app_instance.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "base/test/scoped_command_line.h"
#include "chrome/browser/apps/app_service/publishers/arc_apps.h"
#include "chrome/browser/apps/app_service/publishers/arc_apps_factory.h"
#include "chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h"
#include "chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps_factory.h"
#include "chrome/browser/apps/app_service/publishers/web_apps_crosapi.h"
#include "chrome/browser/apps/app_service/publishers/web_apps_crosapi_factory.h"
#include "chrome/browser/ash/app_list/arc/arc_app_test.h"
#include "chrome/browser/ash/app_list/internal_app/internal_app_metadata.h"
#include "chrome/browser/ash/borealis/borealis_util.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/crosapi/fake_browser_manager.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/branded_strings.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/ash/components/standalone_browser/feature_refs.h"
#include "chromeos/ash/components/standalone_browser/standalone_browser_features.h"
#include "components/services/app_service/public/cpp/app_capability_access_cache.h"
#include "components/services/app_service/public/cpp/capability_access_update.h"
#include "components/user_manager/scoped_user_manager.h"
#include "ui/base/l10n/l10n_util.h"
#endif
namespace {
const base::Time kLastLaunchTime = …;
const base::Time kInstallTime = …;
const char kUrl[] = …;
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
scoped_refptr<extensions::Extension> MakeExtensionApp(
const std::string& name,
const std::string& version,
const std::string& url,
const std::string& id) { … }
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
const char kLegacyPackagedAppId[] = "mblemkccghnfkjignlmgngmopopifacf";
scoped_refptr<extensions::Extension> MakeLegacyPackagedApp(
const std::string& name,
const std::string& version,
const std::string& url,
const std::string& id) {
std::string err;
base::Value::Dict value;
value.Set("name", name);
value.Set("version", version);
value.SetByDottedPath("app.launch.local_path", "index.html");
scoped_refptr<extensions::Extension> app = extensions::Extension::Create(
base::FilePath(), extensions::mojom::ManifestLocation::kInternal, value,
extensions::Extension::WAS_INSTALLED_BY_DEFAULT, id, &err);
EXPECT_EQ(err, "");
return app;
}
void AddArcPackage(ArcAppTest& arc_test,
const std::vector<arc::mojom::AppInfoPtr>& fake_apps) {
for (const auto& fake_app : fake_apps) {
base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
permissions;
permissions.emplace(arc::mojom::AppPermission::CAMERA,
arc::mojom::PermissionState::New(false,
false));
permissions.emplace(arc::mojom::AppPermission::LOCATION,
arc::mojom::PermissionState::New(true,
false));
arc::mojom::ArcPackageInfoPtr package = arc::mojom::ArcPackageInfo::New(
fake_app->package_name, 1,
1,
1, true, false,
false, nullptr, std::nullopt,
std::move(permissions));
arc_test.AddPackage(package->Clone());
arc_test.app_instance()->SendPackageAdded(package->Clone());
}
}
apps::AppPtr MakeApp(apps::AppType app_type,
const std::string& app_id,
const std::string& name,
apps::Readiness readiness) {
auto app = std::make_unique<apps::App>(app_type, app_id);
app->readiness = readiness;
app->name = name;
app->short_name = name;
app->install_reason = apps::InstallReason::kUser;
app->install_source = apps::InstallSource::kSync;
app->icon_key = apps::IconKey();
return app;
}
apps::Permissions MakeFakePermissions() {
apps::Permissions permissions;
permissions.push_back(std::make_unique<apps::Permission>(
apps::PermissionType::kCamera, apps::TriState::kBlock,
false));
permissions.push_back(std::make_unique<apps::Permission>(
apps::PermissionType::kLocation, apps::TriState::kAllow,
false));
return permissions;
}
apps::CapabilityAccessPtr MakeCapabilityAccess(const std::string& app_id,
std::optional<bool> camera,
std::optional<bool> microphone) {
apps::CapabilityAccessPtr access =
std::make_unique<apps::CapabilityAccess>(app_id);
access->camera = std::move(camera);
access->microphone = std::move(microphone);
return access;
}
#endif
apps::IntentFilters CreateIntentFilters() { … }
MATCHER(Ready, "App has readiness=\"kReady\"") { … }
MATCHER_P(ShownInShelf, shown, "App shown on the shelf") { … }
MATCHER_P(ShownInLauncher, shown, "App shown in the launcher") { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
arc::mojom::PrivacyItemPtr CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup permission,
const std::string& package_name) {
arc::mojom::PrivacyItemPtr item = arc::mojom::PrivacyItem::New();
item->permission_group = permission;
item->privacy_application = arc::mojom::PrivacyApplication::New();
item->privacy_application->package_name = package_name;
return item;
}
#endif
class AppRegistryCacheObserver : public apps::AppRegistryCache::Observer { … };
}
namespace apps {
class PublisherTest : public extensions::ExtensionServiceTestBase { … };
#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(PublisherTest, ArcAppsOnApps) {
ArcAppTest arc_test;
arc_test.SetUp(profile());
arc_test.app_instance()->SendRefreshAppList(arc_test.fake_apps());
AddArcPackage(arc_test, arc_test.fake_apps());
ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile());
ASSERT_TRUE(prefs);
for (const auto& app_id : prefs->GetAppIds()) {
std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
if (app_info) {
VerifyApp(
AppType::kArc, app_id, app_info->name, Readiness::kReady,
app_info->sticky ? InstallReason::kSystem : InstallReason::kUser,
app_info->sticky ? InstallSource::kSystem : InstallSource::kPlayStore,
{}, app_info->last_launch_time, app_info->install_time,
apps::Permissions(),
false,
true, true,
true, true,
true, true,
true,
app_info->ready && !app_info->sticky,
true,
false, false,
std::nullopt);
RemoveArcApp(app_id);
VerifyAppIsRemoved(app_id);
}
}
VerifyAppTypeIsInitialized(AppType::kArc);
std::unique_ptr<ArcApps> arc_apps = std::make_unique<ArcApps>(
AppServiceProxyFactory::GetForProfile(profile()));
ASSERT_TRUE(arc_apps.get());
arc_apps->Initialize();
for (const auto& app_id : prefs->GetAppIds()) {
std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
if (app_info) {
VerifyApp(
AppType::kArc, app_id, app_info->name, Readiness::kReady,
app_info->sticky ? InstallReason::kSystem : InstallReason::kUser,
app_info->sticky ? InstallSource::kSystem : InstallSource::kPlayStore,
{}, app_info->last_launch_time, app_info->install_time,
MakeFakePermissions(),
false,
true, true,
true, true,
true, true,
true,
app_info->ready && !app_info->sticky,
true,
false, false,
std::nullopt);
const base::Time before_time = base::Time::Now();
prefs->SetLastLaunchTime(app_id);
app_info = prefs->GetApp(app_id);
EXPECT_GE(app_info->last_launch_time, before_time);
VerifyApp(
AppType::kArc, app_id, app_info->name, Readiness::kReady,
app_info->sticky ? InstallReason::kSystem : InstallReason::kUser,
app_info->sticky ? InstallSource::kSystem : InstallSource::kPlayStore,
{}, app_info->last_launch_time, app_info->install_time,
MakeFakePermissions());
}
}
arc_apps->Shutdown();
}
TEST_F(PublisherTest, ArcApps_CapabilityAccess) {
ArcAppTest arc_test;
arc_test.SetUp(profile());
ArcApps* arc_apps = apps::ArcAppsFactory::GetForProfile(profile());
ASSERT_TRUE(arc_apps);
const auto& fake_apps = arc_test.fake_apps();
std::string package_name1 = fake_apps[0]->package_name;
std::string package_name2 = fake_apps[1]->package_name;
arc_test.app_instance()->SendRefreshAppList(arc_test.fake_apps());
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name1));
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
VerifyCapabilityAccess(ArcAppTest::GetAppId(*fake_apps[0]),
true,
std::nullopt);
}
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
VerifyCapabilityAccess(ArcAppTest::GetAppId(*fake_apps[0]),
false,
false);
}
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name1));
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::MICROPHONE, package_name1));
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name2));
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
VerifyCapabilityAccess(ArcAppTest::GetAppId(*fake_apps[0]),
true,
true);
VerifyCapabilityAccess(ArcAppTest::GetAppId(*fake_apps[1]),
true,
std::nullopt);
}
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name1));
privacy_items.push_back(CreateArcPrivacyItem(
arc::mojom::AppPermissionGroup::CAMERA, package_name2));
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
VerifyCapabilityAccess(ArcAppTest::GetAppId(*fake_apps[0]),
true,
false);
VerifyCapabilityAccess(ArcAppTest::GetAppId(*fake_apps[1]),
true,
false);
}
{
std::vector<arc::mojom::PrivacyItemPtr> privacy_items;
arc_apps->OnPrivacyItemsChanged(std::move(privacy_items));
VerifyCapabilityAccess(ArcAppTest::GetAppId(*fake_apps[0]),
false,
false);
VerifyCapabilityAccess(ArcAppTest::GetAppId(*fake_apps[1]),
false,
false);
}
arc_apps->Shutdown();
}
TEST_F(PublisherTest, BuiltinAppsOnApps) {
for (const auto& internal_app : app_list::GetInternalAppList(profile())) {
if ((internal_app.app_id == nullptr) ||
(internal_app.name_string_resource_id == 0) ||
(internal_app.icon_resource_id <= 0)) {
continue;
}
std::vector<std::string> additional_search_terms;
if (internal_app.searchable_string_resource_id != 0) {
additional_search_terms.push_back(
l10n_util::GetStringUTF8(internal_app.searchable_string_resource_id));
}
VerifyApp(AppType::kBuiltIn, internal_app.app_id,
l10n_util::GetStringUTF8(internal_app.name_string_resource_id),
Readiness::kReady, InstallReason::kSystem, InstallSource::kSystem,
additional_search_terms, base::Time(), base::Time(),
apps::Permissions(),
false, internal_app.recommendable,
internal_app.searchable, internal_app.show_in_launcher,
internal_app.searchable, internal_app.searchable,
false, internal_app.show_in_launcher,
false,
true,
std::nullopt);
}
VerifyAppTypeIsInitialized(AppType::kBuiltIn);
}
class LegacyPackagedAppLacrosPrimaryPublisherTest : public PublisherTest {
public:
LegacyPackagedAppLacrosPrimaryPublisherTest() {
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeatures(
ash::standalone_browser::GetFeatureRefs(), {});
scoped_command_line_.GetProcessCommandLine()->AppendSwitch(
ash::switches::kEnableLacrosForTesting);
}
LegacyPackagedAppLacrosPrimaryPublisherTest(
const LegacyPackagedAppLacrosPrimaryPublisherTest&) = delete;
LegacyPackagedAppLacrosPrimaryPublisherTest& operator=(
const LegacyPackagedAppLacrosPrimaryPublisherTest&) = delete;
~LegacyPackagedAppLacrosPrimaryPublisherTest() override = default;
void SetUp() override {
auto user_manager = std::make_unique<ash::FakeChromeUserManager>();
auto* fake_user_manager = user_manager.get();
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
std::move(user_manager));
const AccountId account_id = AccountId::FromUserEmail("testing_profile");
fake_user_manager->AddUser(account_id);
fake_user_manager->LoginUser(account_id);
PublisherTest::SetUp();
ASSERT_TRUE(crosapi::browser_util::IsLacrosEnabled());
}
private:
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
};
TEST_F(LegacyPackagedAppLacrosPrimaryPublisherTest, LegacyPackagedAppsOnApps) {
AppServiceTest app_service_test;
app_service_test.SetUp(profile());
scoped_refptr<extensions::Extension> legacy_app =
MakeLegacyPackagedApp("legacy_app", "0.0", "http://google.com",
std::string(kLegacyPackagedAppId));
ASSERT_TRUE(legacy_app->is_legacy_packaged_app());
service_->AddExtension(legacy_app.get());
VerifyNoApp(legacy_app->id());
}
class StandaloneBrowserPublisherTest : public PublisherTest {
public:
StandaloneBrowserPublisherTest() {
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeatures(
ash::standalone_browser::GetFeatureRefs(), {});
scoped_command_line_.GetProcessCommandLine()->AppendSwitch(
ash::switches::kEnableLacrosForTesting);
}
StandaloneBrowserPublisherTest(const StandaloneBrowserPublisherTest&) =
delete;
StandaloneBrowserPublisherTest& operator=(
const StandaloneBrowserPublisherTest&) = delete;
~StandaloneBrowserPublisherTest() override = default;
void SetUp() override {
auto user_manager = std::make_unique<ash::FakeChromeUserManager>();
auto* fake_user_manager = user_manager.get();
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
std::move(user_manager));
const AccountId account_id = AccountId::FromUserEmail("testing_profile");
fake_user_manager->AddUser(account_id);
fake_user_manager->LoginUser(account_id);
PublisherTest::SetUp();
ASSERT_EQ(base::FeatureList::IsEnabled(
ash::standalone_browser::features::kLacrosOnly),
crosapi::browser_util::IsLacrosEnabled());
}
void ExtensionAppsOnApps() {
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote =
pending_receiver.InitWithNewPipeAndPassRemote();
StandaloneBrowserExtensionApps* chrome_apps =
StandaloneBrowserExtensionAppsFactoryForApp::GetForProfile(profile());
chrome_apps->RegisterAppController(std::move(pending_remote));
std::vector<AppPtr> apps;
auto app = MakeApp(AppType::kStandaloneBrowserChromeApp,
"a",
"TestApp", Readiness::kReady);
app->is_platform_app = true;
app->recommendable = false;
app->searchable = false;
app->show_in_launcher = false;
app->show_in_shelf = false;
app->show_in_search = false;
app->show_in_management = false;
app->handles_intents = false;
app->allow_uninstall = false;
app->allow_close = true;
app->has_badge = false;
app->paused = false;
app->allow_window_mode_selection = std::nullopt;
apps.push_back(std::move(app));
chrome_apps->OnApps(std::move(apps));
}
void WebAppsCrosapiOnApps() {
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote =
pending_receiver.InitWithNewPipeAndPassRemote();
WebAppsCrosapi* web_apps_crosapi =
WebAppsCrosapiFactory::GetForProfile(profile());
web_apps_crosapi->RegisterAppController(std::move(pending_remote));
std::vector<AppPtr> apps;
auto app = MakeApp(AppType::kWeb,
"a",
"TestApp", Readiness::kReady);
app->additional_search_terms.push_back("TestApp");
app->last_launch_time = kLastLaunchTime;
app->install_time = kInstallTime;
app->permissions = MakeFakePermissions();
app->recommendable = true;
app->searchable = true;
app->show_in_launcher = true;
app->show_in_shelf = true;
app->show_in_search = true;
app->show_in_management = true;
app->handles_intents = true;
app->allow_uninstall = true;
app->allow_close = true;
app->has_badge = true;
app->paused = true;
app->allow_window_mode_selection = true;
app->window_mode = WindowMode::kBrowser;
apps.push_back(std::move(app));
web_apps_crosapi->OnApps(std::move(apps));
}
private:
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
};
TEST_F(StandaloneBrowserPublisherTest, StandaloneBrowserAppsOnApps) {
std::string lacros_app_name = l10n_util::GetStringUTF8(IDS_PRODUCT_NAME);
VerifyApp(AppType::kStandaloneBrowser, app_constants::kLacrosAppId,
lacros_app_name, Readiness::kReady, InstallReason::kSystem,
InstallSource::kSystem, {}, base::Time(), base::Time(),
apps::Permissions(),
false,
true, true,
true, true,
true, true,
true, false,
true, std::nullopt);
VerifyAppTypeIsInitialized(AppType::kStandaloneBrowser);
}
TEST_F(StandaloneBrowserPublisherTest, StandaloneBrowserExtensionAppsOnApps) {
ExtensionAppsOnApps();
VerifyApp(AppType::kStandaloneBrowserChromeApp, "a", "TestApp",
Readiness::kReady, InstallReason::kUser, InstallSource::kSync, {},
base::Time(), base::Time(), apps::Permissions(),
true, false,
false,
false, false,
false, false,
false, false,
true,
false, false,
std::nullopt);
}
TEST_F(StandaloneBrowserPublisherTest,
StandaloneBrowserExtensionAppsNotUpdated) {
StandaloneBrowserExtensionApps* chrome_apps =
StandaloneBrowserExtensionAppsFactoryForApp::GetForProfile(profile());
AppRegistryCache& cache =
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
AppRegistryCacheObserver observer(&cache);
std::vector<AppPtr> apps;
std::string app_id = "a";
apps.push_back(MakeApp(AppType::kStandaloneBrowserChromeApp, app_id,
"TestApp", Readiness::kReady));
chrome_apps->OnApps(std::move(apps));
EXPECT_EQ(AppType::kUnknown, cache.GetAppType(app_id));
EXPECT_TRUE(observer.app_types().empty());
EXPECT_TRUE(observer.updated_ids().empty());
}
TEST_F(StandaloneBrowserPublisherTest, StandaloneBrowserExtensionAppsUpdated) {
StandaloneBrowserExtensionApps* chrome_apps =
StandaloneBrowserExtensionAppsFactoryForApp::GetForProfile(profile());
AppRegistryCache& cache =
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
AppRegistryCacheObserver observer(&cache);
std::vector<AppPtr> apps1;
std::string app_id1 = "a";
std::string app_id2 = "b";
apps1.push_back(MakeApp(AppType::kStandaloneBrowserChromeApp, app_id1,
"TestApp", Readiness::kReady));
apps1.push_back(MakeApp(AppType::kStandaloneBrowserChromeApp, app_id2,
"TestApp", Readiness::kReady));
chrome_apps->OnApps(std::move(apps1));
std::vector<AppPtr> apps2;
std::string app_id3 = "c";
apps2.push_back(MakeApp(AppType::kStandaloneBrowserChromeApp, app_id3,
"TestApp", Readiness::kReady));
chrome_apps->OnApps(std::move(apps2));
EXPECT_EQ(AppType::kUnknown, cache.GetAppType(app_id1));
EXPECT_EQ(AppType::kUnknown, cache.GetAppType(app_id2));
EXPECT_EQ(AppType::kUnknown, cache.GetAppType(app_id3));
EXPECT_TRUE(observer.app_types().empty());
EXPECT_TRUE(observer.updated_ids().empty());
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver1;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote1 =
pending_receiver1.InitWithNewPipeAndPassRemote();
chrome_apps->RegisterAppController(std::move(pending_remote1));
EXPECT_EQ(AppType::kStandaloneBrowserChromeApp, cache.GetAppType(app_id1));
EXPECT_EQ(AppType::kStandaloneBrowserChromeApp, cache.GetAppType(app_id2));
EXPECT_EQ(AppType::kStandaloneBrowserChromeApp, cache.GetAppType(app_id3));
ASSERT_EQ(1u, observer.app_types().size());
EXPECT_EQ(AppType::kStandaloneBrowserChromeApp, observer.app_types()[0]);
ASSERT_EQ(3u, observer.updated_ids().size());
EXPECT_EQ(app_id1, observer.updated_ids()[0]);
EXPECT_EQ(app_id2, observer.updated_ids()[1]);
EXPECT_EQ(app_id3, observer.updated_ids()[2]);
std::vector<AppPtr> apps3;
std::string app_id4 = "d";
apps3.push_back(MakeApp(AppType::kStandaloneBrowserChromeApp, app_id4,
"TestApp", Readiness::kReady));
chrome_apps->OnApps(std::move(apps3));
EXPECT_EQ(AppType::kStandaloneBrowserChromeApp, cache.GetAppType(app_id4));
ASSERT_EQ(4u, observer.updated_ids().size());
EXPECT_EQ(app_id4, observer.updated_ids()[3]);
chrome_apps->OnControllerDisconnected();
std::vector<AppPtr> apps4;
std::string app_id5 = "e";
apps4.push_back(MakeApp(AppType::kStandaloneBrowserChromeApp, app_id5,
"TestApp", Readiness::kReady));
std::string app_id6 = "f";
apps4.push_back(MakeApp(AppType::kStandaloneBrowserChromeApp, app_id6,
"TestApp", Readiness::kReady));
chrome_apps->OnApps(std::move(apps4));
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver2;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote2 =
pending_receiver2.InitWithNewPipeAndPassRemote();
chrome_apps->RegisterAppController(std::move(pending_remote2));
EXPECT_EQ(AppType::kStandaloneBrowserChromeApp, cache.GetAppType(app_id5));
EXPECT_EQ(AppType::kStandaloneBrowserChromeApp, cache.GetAppType(app_id6));
ASSERT_EQ(1u, observer.app_types().size());
ASSERT_EQ(6u, observer.updated_ids().size());
EXPECT_EQ(app_id5, observer.updated_ids()[4]);
EXPECT_EQ(app_id6, observer.updated_ids()[5]);
}
TEST_F(StandaloneBrowserPublisherTest, WebAppsCrosapiOnApps) {
WebAppsCrosapiOnApps();
VerifyApp(AppType::kWeb, "a", "TestApp", Readiness::kReady,
InstallReason::kUser, InstallSource::kSync, {"TestApp"},
kLastLaunchTime, kInstallTime, MakeFakePermissions(),
std::nullopt, true,
true,
true, true,
true, true,
true, true,
true,
true, true,
true, WindowMode::kBrowser);
}
TEST_F(StandaloneBrowserPublisherTest, WebAppsCrosapiNotUpdated) {
WebAppsCrosapi* web_apps_crosapi =
WebAppsCrosapiFactory::GetForProfile(profile());
AppRegistryCache& cache =
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
AppRegistryCacheObserver observer(&cache);
std::vector<AppPtr> apps;
std::string app_id = "a";
apps.push_back(MakeApp(AppType::kWeb, app_id,
"TestApp", Readiness::kReady));
web_apps_crosapi->OnApps(std::move(apps));
EXPECT_EQ(AppType::kUnknown, cache.GetAppType(app_id));
EXPECT_TRUE(observer.app_types().empty());
EXPECT_TRUE(observer.updated_ids().empty());
}
TEST_F(StandaloneBrowserPublisherTest, WebAppsCrosapiUpdated) {
WebAppsCrosapi* web_apps_crosapi =
WebAppsCrosapiFactory::GetForProfile(profile());
AppRegistryCache& cache =
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
AppRegistryCacheObserver observer(&cache);
std::string app_id1 = "a";
std::string app_id2 = "b";
{
std::vector<AppPtr> apps1;
apps1.push_back(MakeApp(AppType::kWeb, app_id1,
"TestApp", Readiness::kReady));
apps1.push_back(MakeApp(AppType::kWeb, app_id2,
"TestApp", Readiness::kReady));
web_apps_crosapi->OnApps(std::move(apps1));
std::vector<CapabilityAccessPtr> capability_access1;
capability_access1.push_back(MakeCapabilityAccess(app_id1,
std::nullopt,
true));
capability_access1.push_back(
MakeCapabilityAccess(app_id2,
true,
std::nullopt));
web_apps_crosapi->OnCapabilityAccesses(std::move(capability_access1));
}
std::string app_id3 = "c";
{
std::vector<AppPtr> apps2;
apps2.push_back(MakeApp(AppType::kWeb, app_id3,
"TestApp", Readiness::kReady));
web_apps_crosapi->OnApps(std::move(apps2));
std::vector<CapabilityAccessPtr> capability_access2;
capability_access2.push_back(MakeCapabilityAccess(app_id3,
true,
true));
web_apps_crosapi->OnCapabilityAccesses(std::move(capability_access2));
}
EXPECT_EQ(AppType::kUnknown, cache.GetAppType(app_id1));
EXPECT_EQ(AppType::kUnknown, cache.GetAppType(app_id2));
EXPECT_EQ(AppType::kUnknown, cache.GetAppType(app_id3));
EXPECT_TRUE(observer.app_types().empty());
EXPECT_TRUE(observer.updated_ids().empty());
VerifyNoCapabilityAccess(app_id1);
VerifyNoCapabilityAccess(app_id2);
VerifyNoCapabilityAccess(app_id3);
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver1;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote1 =
pending_receiver1.InitWithNewPipeAndPassRemote();
web_apps_crosapi->RegisterAppController(std::move(pending_remote1));
EXPECT_EQ(AppType::kWeb, cache.GetAppType(app_id1));
EXPECT_EQ(AppType::kWeb, cache.GetAppType(app_id2));
EXPECT_EQ(AppType::kWeb, cache.GetAppType(app_id3));
ASSERT_EQ(1u, observer.app_types().size());
EXPECT_EQ(AppType::kWeb, observer.app_types()[0]);
ASSERT_EQ(3u, observer.updated_ids().size());
EXPECT_EQ(app_id1, observer.updated_ids()[0]);
EXPECT_EQ(app_id2, observer.updated_ids()[1]);
EXPECT_EQ(app_id3, observer.updated_ids()[2]);
VerifyCapabilityAccess(app_id1,
std::nullopt,
true);
VerifyCapabilityAccess(app_id2,
true,
std::nullopt);
VerifyCapabilityAccess(app_id3,
true,
true);
std::string app_id4 = "d";
{
std::vector<AppPtr> apps3;
apps3.push_back(MakeApp(AppType::kWeb, app_id4,
"TestApp", Readiness::kReady));
web_apps_crosapi->OnApps(std::move(apps3));
std::vector<CapabilityAccessPtr> capability_access3;
capability_access3.push_back(
MakeCapabilityAccess(app_id4,
true,
std::nullopt));
web_apps_crosapi->OnCapabilityAccesses(std::move(capability_access3));
}
EXPECT_EQ(AppType::kWeb, cache.GetAppType(app_id4));
ASSERT_EQ(4u, observer.updated_ids().size());
EXPECT_EQ(app_id4, observer.updated_ids()[3]);
VerifyCapabilityAccess(app_id4,
true,
std::nullopt);
web_apps_crosapi->OnControllerDisconnected();
std::string app_id5 = "e";
std::string app_id6 = "f";
{
std::vector<AppPtr> apps4;
apps4.push_back(MakeApp(AppType::kWeb, app_id5,
"TestApp", Readiness::kReady));
apps4.push_back(MakeApp(AppType::kWeb, app_id6,
"TestApp", Readiness::kReady));
web_apps_crosapi->OnApps(std::move(apps4));
}
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver2;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote2 =
pending_receiver2.InitWithNewPipeAndPassRemote();
web_apps_crosapi->RegisterAppController(std::move(pending_remote2));
EXPECT_EQ(AppType::kWeb, cache.GetAppType(app_id5));
EXPECT_EQ(AppType::kWeb, cache.GetAppType(app_id6));
ASSERT_EQ(1u, observer.app_types().size());
ASSERT_EQ(6u, observer.updated_ids().size());
EXPECT_EQ(app_id5, observer.updated_ids()[4]);
EXPECT_EQ(app_id6, observer.updated_ids()[5]);
VerifyNoCapabilityAccess(app_id5);
VerifyNoCapabilityAccess(app_id6);
}
TEST_F(StandaloneBrowserPublisherTest, WebAppsCrosapiUpdatedCapability) {
WebAppsCrosapi* web_apps_crosapi =
WebAppsCrosapiFactory::GetForProfile(profile());
std::string app_id1 = "a";
std::string app_id2 = "b";
{
std::vector<CapabilityAccessPtr> capability_access1;
capability_access1.push_back(MakeCapabilityAccess(app_id1,
std::nullopt,
true));
capability_access1.push_back(
MakeCapabilityAccess(app_id2,
true,
std::nullopt));
web_apps_crosapi->OnCapabilityAccesses(std::move(capability_access1));
}
VerifyNoCapabilityAccess(app_id1);
VerifyNoCapabilityAccess(app_id2);
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver1;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote1 =
pending_receiver1.InitWithNewPipeAndPassRemote();
web_apps_crosapi->RegisterAppController(std::move(pending_remote1));
VerifyCapabilityAccess(app_id1,
std::nullopt,
true);
VerifyCapabilityAccess(app_id2,
true,
std::nullopt);
std::string app_id3 = "c";
{
std::vector<CapabilityAccessPtr> capability_access2;
capability_access2.push_back(MakeCapabilityAccess(app_id3,
true,
true));
web_apps_crosapi->OnCapabilityAccesses(std::move(capability_access2));
}
VerifyCapabilityAccess(app_id3,
true,
true);
web_apps_crosapi->OnControllerDisconnected();
}
TEST_F(StandaloneBrowserPublisherTest, WebAppsCrosapiCapabilityReset) {
WebAppsCrosapi* web_apps_crosapi =
WebAppsCrosapiFactory::GetForProfile(profile());
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver1;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote1 =
pending_receiver1.InitWithNewPipeAndPassRemote();
web_apps_crosapi->RegisterAppController(std::move(pending_remote1));
std::string app_id1 = "a";
std::string app_id2 = "b";
std::vector<AppPtr> apps1;
apps1.push_back(MakeApp(AppType::kWeb, app_id1,
"TestApp", Readiness::kReady));
apps1.push_back(MakeApp(AppType::kWeb, app_id2,
"TestApp", Readiness::kReady));
web_apps_crosapi->OnApps(std::move(apps1));
std::vector<CapabilityAccessPtr> capability_access;
capability_access.push_back(MakeCapabilityAccess(app_id1,
std::nullopt,
true));
capability_access.push_back(
MakeCapabilityAccess(app_id2,
true,
std::nullopt));
web_apps_crosapi->OnCapabilityAccesses(std::move(capability_access));
VerifyCapabilityAccess(app_id1,
std::nullopt,
true);
VerifyCapabilityAccess(app_id2,
true,
std::nullopt);
web_apps_crosapi->OnControllerDisconnected();
VerifyCapabilityAccess(app_id1, false, false);
VerifyCapabilityAccess(app_id2, false, false);
}
TEST_F(StandaloneBrowserPublisherTest, WebAppsNotInitializedIfRegisterFirst) {
WebAppsCrosapi* web_apps_crosapi =
WebAppsCrosapiFactory::GetForProfile(profile());
AppRegistryCache& cache =
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
AppRegistryCacheObserver observer(&cache);
EXPECT_TRUE(observer.app_types().empty());
EXPECT_TRUE(observer.updated_ids().empty());
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver1;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote1 =
pending_receiver1.InitWithNewPipeAndPassRemote();
web_apps_crosapi->RegisterAppController(std::move(pending_remote1));
EXPECT_TRUE(observer.app_types().empty());
EXPECT_TRUE(observer.updated_ids().empty());
std::vector<AppPtr> apps1;
std::string app_id1 = "a";
std::string app_id2 = "b";
apps1.push_back(MakeApp(AppType::kWeb, app_id1,
"TestApp", Readiness::kReady));
apps1.push_back(MakeApp(AppType::kWeb, app_id2,
"TestApp", Readiness::kReady));
web_apps_crosapi->OnApps(std::move(apps1));
std::vector<AppPtr> apps2;
std::string app_id3 = "c";
apps2.push_back(MakeApp(AppType::kWeb, app_id3,
"TestApp", Readiness::kReady));
web_apps_crosapi->OnApps(std::move(apps2));
EXPECT_EQ(AppType::kWeb, cache.GetAppType(app_id1));
EXPECT_EQ(AppType::kWeb, cache.GetAppType(app_id2));
EXPECT_EQ(AppType::kWeb, cache.GetAppType(app_id3));
ASSERT_EQ(1u, observer.app_types().size());
EXPECT_EQ(AppType::kWeb, observer.app_types()[0]);
ASSERT_EQ(3u, observer.updated_ids().size());
EXPECT_EQ(app_id1, observer.updated_ids()[0]);
EXPECT_EQ(app_id2, observer.updated_ids()[1]);
EXPECT_EQ(app_id3, observer.updated_ids()[2]);
}
TEST_F(StandaloneBrowserPublisherTest, WebAppsInitializedForEmptyList) {
WebAppsCrosapi* web_apps_crosapi =
WebAppsCrosapiFactory::GetForProfile(profile());
AppRegistryCache& cache =
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
AppRegistryCacheObserver observer(&cache);
web_apps_crosapi->OnApps(std::vector<AppPtr>{});
EXPECT_TRUE(observer.app_types().empty());
EXPECT_TRUE(observer.updated_ids().empty());
mojo::PendingReceiver<crosapi::mojom::AppController> pending_receiver1;
mojo::PendingRemote<crosapi::mojom::AppController> pending_remote1 =
pending_receiver1.InitWithNewPipeAndPassRemote();
web_apps_crosapi->RegisterAppController(std::move(pending_remote1));
ASSERT_EQ(1u, observer.app_types().size());
EXPECT_EQ(AppType::kWeb, observer.app_types()[0]);
EXPECT_TRUE(observer.updated_ids().empty());
}
TEST_F(StandaloneBrowserPublisherTest, ExtensionAppsNotPublished) {
scoped_refptr<extensions::Extension> store =
MakeExtensionApp("webstore", "0.0", "http://google.com",
std::string(extensions::kWebStoreAppId));
service_->AddExtension(store.get());
AppRegistryCache& cache =
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
EXPECT_EQ(AppType::kUnknown, cache.GetAppType(store->id()));
}
using NonBorealisPublisherTest = StandaloneBrowserPublisherTest;
TEST_F(NonBorealisPublisherTest, BorealisAppsNotAllowed) {
EXPECT_THAT(*GetApp(borealis::kInstallerAppId), testing::Not(Ready()));
}
class BorealisPublisherTest : public StandaloneBrowserPublisherTest {
public:
BorealisPublisherTest() {
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeatures(
{features::kBorealis, ash::features::kBorealisPermitted}, {});
}
};
TEST_F(BorealisPublisherTest, BorealisAppsAllowed) {
EXPECT_THAT(
*GetApp(borealis::kInstallerAppId),
testing::AllOf(Ready(), ShownInShelf(true), ShownInLauncher(false)));
}
#endif
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
TEST_F(PublisherTest, ExtensionAppsOnApps) { … }
TEST_F(PublisherTest, WebAppsOnApps) { … }
#endif
}