Commit 2d1e7d10 authored by Dominick Ng's avatar Dominick Ng Committed by Commit Bot

Create a skeleton App Service and App Registry.

This CL creates the App Service as a in-process per-profile service, and
attaches the App Registry to it.

App Service is currently located in chrome/browser as many of its direct
dependencies (which are not yet connected to the service) live there.
Over time, we will refactor the dependencies such that:

1. data which can be made independent of chrome/browser is moved within
   the App Service
2. components which rely on that data access it asynchronously via the
   App Service

This will allow us to move the App Service to chrome/services in the
future.

BUG=826982

Change-Id: Ia7608fbb8c8b235d2b3e69ffa447963b32f2d5f0
Reviewed-on: https://chromium-review.googlesource.com/1127513
Commit-Queue: Dominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarBernhard Bauer <bauerb@chromium.org>
Reviewed-by: default avatarSam McNally <sammc@chromium.org>
Reviewed-by: default avatarTrent Apted <tapted@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Cr-Commit-Position: refs/heads/master@{#575968}
parent e75edbf6
...@@ -460,6 +460,7 @@ service_manifest("chrome_content_packaged_services_manifest_overlay") { ...@@ -460,6 +460,7 @@ service_manifest("chrome_content_packaged_services_manifest_overlay") {
service_manifest("chrome_content_browser_manifest_overlay") { service_manifest("chrome_content_browser_manifest_overlay") {
source = "//chrome/browser/chrome_content_browser_manifest_overlay.json" source = "//chrome/browser/chrome_content_browser_manifest_overlay.json"
packaged_services = [ packaged_services = [
"//chrome/browser/apps/foundation/app_service:manifest",
"//services/identity:manifest", "//services/identity:manifest",
"//services/preferences:manifest", "//services/preferences:manifest",
] ]
......
...@@ -1660,6 +1660,7 @@ jumbo_split_static_library("browser") { ...@@ -1660,6 +1660,7 @@ jumbo_split_static_library("browser") {
"//chrome:strings", "//chrome:strings",
"//chrome/app/resources:platform_locale_settings", "//chrome/app/resources:platform_locale_settings",
"//chrome/app/theme:theme_resources", "//chrome/app/theme:theme_resources",
"//chrome/browser/apps/foundation/app_service:lib",
"//chrome/browser/budget_service:budget_proto", "//chrome/browser/budget_service:budget_proto",
"//chrome/browser/devtools", "//chrome/browser/devtools",
"//chrome/browser/media:media_engagement_preload_proto", "//chrome/browser/media:media_engagement_preload_proto",
......
benwells@chromium.org
dominickn@chromium.org
# Chrome Apps Platform Foundation
Chrome features multiple app platforms, including PWAs (desktop and mobile),
ARC++ (Chrome OS), and Chrome apps (now deprecated). Each of these is based on
different underlying technologies, but UX-wise, it is an explicit goal to
minimize the differences between them and give users the feeling that all apps
have as similar properties, management, and features as possible.
This directory contains generic code to facilitate app platforms on Chrome
communicating with the browser and each other. This layer consists of
app-platform-agnostic code, which each app platform's custom implementation
then plugs into. Example features include:
* `app_service` - a central service for information on installed apps
* `app_registry` - the central registry for installed apps contained within
the app service
Over time, it is intended to reduce the dependencies of this layer on Chrome
browser through refactoring work, with the eventual goal of moving this
directory to chrome/services.
# Copyright 2018 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.
import("//services/service_manager/public/cpp/service.gni")
import("//services/service_manager/public/service_manifest.gni")
# TODO(crbug.com/826982): move to chrome/services.
source_set("lib") {
sources = [
"app_registry/app_registry.cc",
"app_registry/app_registry.h",
"app_service.cc",
"app_service.h",
]
deps = [
"//base",
"//components/prefs",
"//mojo/public/cpp/bindings",
"//services/preferences/public/cpp",
"//services/service_manager/public/cpp",
]
public_deps = [
"//chrome/browser/apps/foundation/app_service/public/mojom",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"app_registry/app_registry_unittest.cc",
]
deps = [
":lib",
"//components/prefs:test_support",
"//testing/gtest",
]
}
service_manifest("manifest") {
name = "apps"
source = "manifest.json"
}
benwells@chromium.org
dominickn@chromium.org
per-file manifest.json=set noparent
per-file manifest.json=file://ipc/SECURITY_OWNERS
// Copyright 2018 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 <utility>
#include <vector>
#include "chrome/browser/apps/foundation/app_service/app_registry/app_registry.h"
#include "base/values.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
namespace {
// Key for synced App Registry prefs. The pref format is a dictionary where the
// key is an app ID and the value is a dictionary of app metadata.
constexpr char kAppRegistryPrefNameSynced[] = "app_registry_synced";
// Key for per-app preferences within the app metadata dictionary. Used to store
// arbitrary data per each app.
constexpr char kAppRegistryPrefNamePrefs[] = "prefs";
// Key for whether an app should be preferred for opening links.
constexpr char kAppRegistryPrefKeyPreferred[] = "preferred";
} // namespace
namespace apps {
// static
void AppRegistry::RegisterPrefs(PrefRegistrySimple* registry) {
// Note: it may be desirable to add a non-synced pref if some future app
// metadata should remain local to the device.
registry->RegisterDictionaryPref(
kAppRegistryPrefNameSynced,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
}
AppRegistry::AppRegistry(std::unique_ptr<PrefService> pref_service)
: pref_service_(std::move(pref_service)) {}
AppRegistry::~AppRegistry() = default;
void AppRegistry::BindRequest(apps::mojom::AppRegistryRequest request) {
bindings_.AddBinding(this, std::move(request));
}
void AppRegistry::GetApps(GetAppsCallback callback) {
std::vector<apps::mojom::AppInfoPtr> app_infos;
const base::DictionaryValue* apps =
pref_service_->GetDictionary(kAppRegistryPrefNameSynced);
for (const auto& item : apps->DictItems()) {
auto state = apps::mojom::AppPreferred::kUnknown;
const base::Value* value = item.second.FindPathOfType(
{kAppRegistryPrefNamePrefs, kAppRegistryPrefKeyPreferred},
base::Value::Type::INTEGER);
if (value)
state = static_cast<apps::mojom::AppPreferred>(value->GetInt());
auto app_info = apps::mojom::AppInfo::New();
app_info->id = item.first;
app_info->type = apps::mojom::AppType::kUnknown;
app_info->preferred = state;
app_infos.push_back(std::move(app_info));
}
std::move(callback).Run(std::move(app_infos));
}
apps::mojom::AppPreferred AppRegistry::GetIfAppPreferredForTesting(
const std::string& app_id) const {
const base::Value* value =
pref_service_->GetDictionary(kAppRegistryPrefNameSynced)
->FindPathOfType(
{app_id, kAppRegistryPrefNamePrefs, kAppRegistryPrefKeyPreferred},
base::Value::Type::INTEGER);
return value ? static_cast<apps::mojom::AppPreferred>(value->GetInt())
: apps::mojom::AppPreferred::kUnknown;
}
void AppRegistry::SetAppPreferred(const std::string& app_id,
apps::mojom::AppPreferred state) {
DictionaryPrefUpdate dict_update(pref_service_.get(),
kAppRegistryPrefNameSynced);
dict_update->SetPath(
{app_id, kAppRegistryPrefNamePrefs, kAppRegistryPrefKeyPreferred},
base::Value(static_cast<int>(state)));
}
} // namespace apps
// Copyright 2018 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_APPS_FOUNDATION_APP_SERVICE_APP_REGISTRY_APP_REGISTRY_H_
#define CHROME_BROWSER_APPS_FOUNDATION_APP_SERVICE_APP_REGISTRY_APP_REGISTRY_H_
#include <memory>
#include <string>
#include "base/macros.h"
#include "chrome/browser/apps/foundation/app_service/public/mojom/app_registry.mojom.h"
#include "mojo/public/cpp/bindings/binding_set.h"
class PrefRegistrySimple;
class PrefService;
namespace apps {
// The app registry maintains metadata on installed apps.
class AppRegistry : public apps::mojom::AppRegistry {
public:
static void RegisterPrefs(PrefRegistrySimple* registry);
explicit AppRegistry(std::unique_ptr<PrefService> pref_service);
~AppRegistry() override;
void BindRequest(apps::mojom::AppRegistryRequest request);
// Returns |app_id|'s preferred state.
apps::mojom::AppPreferred GetIfAppPreferredForTesting(
const std::string& app_id) const;
// mojom::apps::AppRegistry overrides.
void GetApps(GetAppsCallback callback) override;
void SetAppPreferred(const std::string& app_id,
apps::mojom::AppPreferred state) override;
private:
std::unique_ptr<PrefService> pref_service_;
mojo::BindingSet<apps::mojom::AppRegistry> bindings_;
DISALLOW_COPY_AND_ASSIGN(AppRegistry);
};
} // namespace apps
#endif // CHROME_BROWSER_APPS_FOUNDATION_APP_SERVICE_APP_REGISTRY_APP_REGISTRY_H_
// Copyright 2018 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 <algorithm>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "chrome/browser/apps/foundation/app_service/app_registry/app_registry.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kTestAppId1[] = "test.app.id.1";
const char kTestAppId2[] = "test.app.id.2";
} // namespace
namespace apps {
class AppRegistryTest : public testing::Test {
protected:
std::unique_ptr<AppRegistry> CreateAppRegistry() {
auto pref_service = std::make_unique<TestingPrefServiceSimple>();
AppRegistry::RegisterPrefs(pref_service->registry());
return std::make_unique<AppRegistry>(std::move(pref_service));
}
void GetAppInfos(AppRegistry* app_registry) {
base::RunLoop run_loop;
app_registry->GetApps(base::BindOnce(&AppRegistryTest::GetAppsCallback,
base::Unretained(this),
run_loop.QuitClosure()));
run_loop.Run();
}
void GetAppsCallback(base::OnceClosure quit_closure,
std::vector<apps::mojom::AppInfoPtr> app_infos) {
app_infos_ = std::move(app_infos);
std::move(quit_closure).Run();
}
const std::vector<apps::mojom::AppInfoPtr>& app_infos() const {
return app_infos_;
}
const apps::mojom::AppInfoPtr& FindWithId(const std::string& id) const {
return *(std::find_if(
app_infos_.begin(), app_infos_.end(),
[&id](const apps::mojom::AppInfoPtr& info) { return info->id == id; }));
}
private:
// Required for RunLoop waiting.
base::MessageLoop message_loop_;
std::vector<apps::mojom::AppInfoPtr> app_infos_;
};
TEST_F(AppRegistryTest, SetAppPreferred) {
auto registry = CreateAppRegistry();
EXPECT_EQ(apps::mojom::AppPreferred::kUnknown,
registry->GetIfAppPreferredForTesting(kTestAppId1));
EXPECT_EQ(apps::mojom::AppPreferred::kUnknown,
registry->GetIfAppPreferredForTesting(kTestAppId2));
registry->SetAppPreferred(kTestAppId1, apps::mojom::AppPreferred::kPreferred);
EXPECT_EQ(apps::mojom::AppPreferred::kPreferred,
registry->GetIfAppPreferredForTesting(kTestAppId1));
EXPECT_EQ(apps::mojom::AppPreferred::kUnknown,
registry->GetIfAppPreferredForTesting(kTestAppId2));
registry->SetAppPreferred(kTestAppId1,
apps::mojom::AppPreferred::kNotPreferred);
EXPECT_EQ(apps::mojom::AppPreferred::kNotPreferred,
registry->GetIfAppPreferredForTesting(kTestAppId1));
EXPECT_EQ(apps::mojom::AppPreferred::kUnknown,
registry->GetIfAppPreferredForTesting(kTestAppId2));
registry->SetAppPreferred(kTestAppId2, apps::mojom::AppPreferred::kPreferred);
EXPECT_EQ(apps::mojom::AppPreferred::kNotPreferred,
registry->GetIfAppPreferredForTesting(kTestAppId1));
EXPECT_EQ(apps::mojom::AppPreferred::kPreferred,
registry->GetIfAppPreferredForTesting(kTestAppId2));
registry->SetAppPreferred(kTestAppId2,
apps::mojom::AppPreferred::kNotPreferred);
EXPECT_EQ(apps::mojom::AppPreferred::kNotPreferred,
registry->GetIfAppPreferredForTesting(kTestAppId1));
EXPECT_EQ(apps::mojom::AppPreferred::kNotPreferred,
registry->GetIfAppPreferredForTesting(kTestAppId2));
}
TEST_F(AppRegistryTest, GetPreferredApps) {
auto app_registry = CreateAppRegistry();
GetAppInfos(app_registry.get());
EXPECT_EQ(std::vector<apps::mojom::AppInfoPtr>{}, app_infos());
app_registry->SetAppPreferred(kTestAppId1,
apps::mojom::AppPreferred::kPreferred);
GetAppInfos(app_registry.get());
EXPECT_EQ(1u, app_infos().size());
EXPECT_EQ(apps::mojom::AppPreferred::kPreferred, app_infos()[0]->preferred);
app_registry->SetAppPreferred(kTestAppId2,
apps::mojom::AppPreferred::kPreferred);
GetAppInfos(app_registry.get());
EXPECT_EQ(2u, app_infos().size());
EXPECT_EQ(apps::mojom::AppPreferred::kPreferred,
FindWithId(kTestAppId1)->preferred);
EXPECT_EQ(apps::mojom::AppPreferred::kPreferred,
FindWithId(kTestAppId2)->preferred);
app_registry->SetAppPreferred(kTestAppId1,
apps::mojom::AppPreferred::kNotPreferred);
GetAppInfos(app_registry.get());
EXPECT_EQ(2u, app_infos().size());
EXPECT_EQ(apps::mojom::AppPreferred::kNotPreferred,
FindWithId(kTestAppId1)->preferred);
EXPECT_EQ(apps::mojom::AppPreferred::kPreferred,
FindWithId(kTestAppId2)->preferred);
app_registry->SetAppPreferred(kTestAppId2,
apps::mojom::AppPreferred::kNotPreferred);
GetAppInfos(app_registry.get());
EXPECT_EQ(2u, app_infos().size());
EXPECT_EQ(apps::mojom::AppPreferred::kNotPreferred,
FindWithId(kTestAppId1)->preferred);
EXPECT_EQ(apps::mojom::AppPreferred::kNotPreferred,
FindWithId(kTestAppId2)->preferred);
}
} // namespace apps
// Copyright 2018 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 <utility>
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "chrome/browser/apps/foundation/app_service/app_registry/app_registry.h"
#include "chrome/browser/apps/foundation/app_service/app_service.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "services/preferences/public/cpp/pref_service_factory.h"
#include "services/service_manager/public/cpp/service_context.h"
namespace apps {
AppService::AppService() = default;
AppService::~AppService() = default;
void AppService::OnStart() {
binder_registry_.AddInterface<apps::mojom::AppRegistry>(base::BindRepeating(
&AppService::BindAppRegistryRequest, base::Unretained(this)));
auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
AppRegistry::RegisterPrefs(pref_registry.get());
prefs::ConnectToPrefService(
context()->connector(), std::move(pref_registry),
base::BindRepeating(&AppService::OnPrefServiceConnected,
base::Unretained(this)));
}
void AppService::OnBindInterface(const service_manager::BindSourceInfo& source,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
binder_registry_.BindInterface(interface_name, std::move(interface_pipe));
}
void AppService::BindAppRegistryRequest(
apps::mojom::AppRegistryRequest request) {
if (!IsInitializationComplete()) {
pending_app_registry_requests_.push_back(std::move(request));
return;
}
app_registry_->BindRequest(std::move(request));
}
void AppService::OnPrefServiceConnected(
std::unique_ptr<::PrefService> pref_service) {
// Connecting to the pref service is required for this class to function.
DCHECK(pref_service);
app_registry_ = std::make_unique<AppRegistry>(std::move(pref_service));
DCHECK(IsInitializationComplete());
for (auto& request : pending_app_registry_requests_)
BindAppRegistryRequest(std::move(request));
pending_app_registry_requests_.clear();
}
} // namespace apps
// Copyright 2018 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_APPS_FOUNDATION_APP_SERVICE_APP_SERVICE_H_
#define CHROME_BROWSER_APPS_FOUNDATION_APP_SERVICE_APP_SERVICE_H_
#include <memory>
#include <vector>
#include "base/macros.h"
#include "chrome/browser/apps/foundation/app_service/public/mojom/app_registry.mojom.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/service.h"
class PrefService;
namespace apps {
class AppRegistry;
// The App Service is an intermediary between M customers (e.g. app launching
// UI, or sync) and N providers (also known as app platforms, e.g. PWAs, ARC++,
// and Crostini). Its motivation is to abstract over platform-specific
// implementations and allow customers to work with generic actions (e.g.
// querying for app data) that are forwarded to the appropriate provider.
class AppService : public service_manager::Service {
public:
AppService();
~AppService() override;
// service_manager::Service:
void OnStart() override;
void OnBindInterface(const service_manager::BindSourceInfo& source,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override;
private:
void BindAppRegistryRequest(apps::mojom::AppRegistryRequest request);
bool IsInitializationComplete() const { return app_registry_ != nullptr; }
void OnPrefServiceConnected(std::unique_ptr<PrefService> pref_service);
service_manager::BinderRegistry binder_registry_;
std::unique_ptr<AppRegistry> app_registry_;
// Requests which will be serviced once initialization of the App Registry is
// complete.
std::vector<apps::mojom::AppRegistryRequest> pending_app_registry_requests_;
DISALLOW_COPY_AND_ASSIGN(AppService);
};
} // namespace apps
#endif // CHROME_BROWSER_APPS_FOUNDATION_APP_SERVICE_APP_SERVICE_H_
{
"name": "apps",
"display_name": "App Service",
"sandbox_type": "none",
"interface_provider_specs": {
"service_manager:connector": {
"provides": {
"app_registry": [
"apps.mojom.AppRegistry"
]
},
"requires": {
"preferences": [
"pref_client"
]
}
}
}
}
# Copyright 2018 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.
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [
"app_registry.mojom",
]
public_deps = [
":constants",
":types",
"//mojo/public/mojom/base",
]
}
mojom("constants") {
sources = [
"constants.mojom",
]
}
mojom("types") {
sources = [
"types.mojom",
]
}
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
// Copyright 2018 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.
module apps.mojom;
import "chrome/browser/apps/foundation/app_service/public/mojom/types.mojom";
// The interface through which clients of the App Service can query for data on
// installed apps.
interface AppRegistry {
// Returns a list of all apps persisted in the registry.
GetApps() => (array<AppInfo> app_infos);
// Sets the preferred value for the app represented by |app_id| to |state|.
// TODO(crbug.com/826982): mandate that |app_id| must exist in the registry,
// or decide that this method no-ops.
SetAppPreferred(string app_id, AppPreferred state);
};
// Copyright 2018 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.
module apps.mojom;
const string kServiceName = "apps";
// Copyright 2018 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.
module apps.mojom;
// Whether an app is preferred by the user, e.g. for capturing links within
// its scope. Multiple preferred apps with overlapping scope may be set, and
// the best choice will be resolved via the most specific scope.
enum AppPreferred {
// No preference currently saved.
kUnknown,
kPreferred,
kNotPreferred,
};
// The types of apps available in the registry.
enum AppType {
// Used for error scenarios or when the app type has not been set.
kUnknown,
// An ARC++ Android app.
kArc,
// A web app.
kWeb,
// A Linux app.
kCrostini,
};
// A struct representing an app.
struct AppInfo {
// The id of the app. For backwards compabitility, this should currently be
// the same as the extension ID that would have been generated for the app.
string id;
// The type of the app.
AppType type;
// The app's current preferred state.
AppPreferred preferred;
};
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
] ]
}, },
"requires": { "requires": {
"apps": [ "app_registry" ],
"autoclick_app": [ "chromeos:autoclick" ], "autoclick_app": [ "chromeos:autoclick" ],
"ash": [ "system_ui", "test", "display" ], "ash": [ "system_ui", "test", "display" ],
// Only used in classic ash case. // Only used in classic ash case.
......
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "base/version.h" #include "base/version.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/apps/foundation/app_service/app_service.h"
#include "chrome/browser/apps/foundation/app_service/public/mojom/constants.mojom.h"
#include "chrome/browser/background/background_contents_service_factory.h" #include "chrome/browser/background/background_contents_service_factory.h"
#include "chrome/browser/background_fetch/background_fetch_delegate_factory.h" #include "chrome/browser/background_fetch/background_fetch_delegate_factory.h"
#include "chrome/browser/background_fetch/background_fetch_delegate_impl.h" #include "chrome/browser/background_fetch/background_fetch_delegate_impl.h"
...@@ -296,6 +298,11 @@ std::string ExitTypeToSessionTypePrefValue(Profile::ExitType type) { ...@@ -296,6 +298,11 @@ std::string ExitTypeToSessionTypePrefValue(Profile::ExitType type) {
return std::string(); return std::string();
} }
std::unique_ptr<service_manager::Service> CreateAppService(Profile* profile) {
// TODO(crbug.com/826982): use |profile| to fetch existing registries.
return std::make_unique<apps::AppService>();
}
} // namespace } // namespace
// static // static
...@@ -1172,6 +1179,16 @@ void ProfileImpl::RegisterInProcessServices(StaticServiceMap* services) { ...@@ -1172,6 +1179,16 @@ void ProfileImpl::RegisterInProcessServices(StaticServiceMap* services) {
} }
#endif #endif
{
// Binding the App Service here means that its preferences will be stored in
// the primary Preferences file for this profile.
service_manager::EmbeddedServiceInfo info;
info.task_runner = base::ThreadTaskRunnerHandle::Get();
info.factory =
base::BindRepeating(&CreateAppService, base::Unretained(this));
services->emplace(apps::mojom::kServiceName, info);
}
service_manager::EmbeddedServiceInfo identity_service_info; service_manager::EmbeddedServiceInfo identity_service_info;
// The Identity Service must run on the UI thread. // The Identity Service must run on the UI thread.
......
...@@ -2802,6 +2802,7 @@ test("unit_tests") { ...@@ -2802,6 +2802,7 @@ test("unit_tests") {
"//chrome:child_dependencies", "//chrome:child_dependencies",
"//chrome:resources", "//chrome:resources",
"//chrome:strings", "//chrome:strings",
"//chrome/browser/apps/foundation/app_service:unit_tests",
"//chrome/browser/media/router:test_support", "//chrome/browser/media/router:test_support",
"//chrome/common:test_support", "//chrome/common:test_support",
"//chrome/common/media_router:test_support", "//chrome/common/media_router:test_support",
......
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