Commit f82a08ba authored by Jit Yao Yap's avatar Jit Yao Yap Committed by Commit Bot

Add Remote Apps Mojo service

This CL adds a Mojo service for Remote Apps. The service is a thin layer
which mainly forwards calls to the RemoteAppsManager.

Only extensions under the imprivata_in_session_extension behavior feature
have access to the API.

Bug: 1101208
Change-Id: Ieaf9f6839ba22d86cb3fba8fa64afe49ade8e6ce
DD: go/remote_apps_mojo
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2332615
Commit-Queue: Jit Yao Yap <jityao@google.com>
Reviewed-by: default avatarDenis Kuznetsov [CET] <antrim@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatarSam McNally <sammc@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798551}
parent 10dc2479
......@@ -118,6 +118,7 @@ source_set("chromeos") {
"//chromeos/components/print_management/mojom",
"//chromeos/components/proximity_auth",
"//chromeos/components/quick_answers/public/cpp:prefs",
"//chromeos/components/remote_apps/mojom",
"//chromeos/components/scanning",
"//chromeos/components/smbfs",
"//chromeos/components/smbfs/mojom",
......@@ -2418,12 +2419,15 @@ source_set("chromeos") {
"release_notes/release_notes_storage.h",
"remote_apps/id_generator.cc",
"remote_apps/id_generator.h",
"remote_apps/remote_apps_impl.cc",
"remote_apps/remote_apps_impl.h",
"remote_apps/remote_apps_manager.cc",
"remote_apps/remote_apps_manager.h",
"remote_apps/remote_apps_manager_factory.cc",
"remote_apps/remote_apps_manager_factory.h",
"remote_apps/remote_apps_model.cc",
"remote_apps/remote_apps_model.h",
"remote_apps/remote_apps_types.h",
"reset/metrics.h",
"scanning/lorgnette_scanner_manager.cc",
"scanning/lorgnette_scanner_manager.h",
......
specific_include_rules = {
"remote_apps_manager_browsertest\.cc": [
".*_browsertest\.cc": [
"+ash/app_list",
"+ash/shell.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/chromeos/remote_apps/remote_apps_impl.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/optional.h"
#include "base/stl_util.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_manager.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/render_frame_host.h"
#include "extensions/common/extension.h"
#include "extensions/common/features/behavior_feature.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/features/feature_provider.h"
namespace chromeos {
namespace {
constexpr char kErrNotReady[] = "Manager for remote apps is not ready";
constexpr char kErrFolderIdDoesNotExist[] = "Folder ID provided does not exist";
static bool g_bypass_checks_for_testing_ = false;
} // namespace
// static
bool RemoteAppsImpl::IsAllowed(content::RenderFrameHost* render_frame_host,
const extensions::Extension* extension) {
if (!render_frame_host || !extension)
return false;
Profile* profile =
Profile::FromBrowserContext(render_frame_host->GetBrowserContext());
DCHECK(profile);
// RemoteApps are not available for non-managed guest sessions.
if (!RemoteAppsManagerFactory::GetForProfile(profile))
return false;
if (g_bypass_checks_for_testing_)
return true;
const extensions::Feature* feature =
extensions::FeatureProvider::GetBehaviorFeature(
extensions::behavior_feature::kKeyImprivataInSessionExtension);
DCHECK(feature);
return feature->IsAvailableToExtension(extension).is_available();
}
// static
void RemoteAppsImpl::SetBypassChecksForTesting(bool bypass_checks_for_testing) {
g_bypass_checks_for_testing_ = bypass_checks_for_testing;
}
RemoteAppsImpl::RemoteAppsImpl(RemoteAppsManager* manager) : manager_(manager) {
DCHECK(manager);
}
RemoteAppsImpl::~RemoteAppsImpl() = default;
void RemoteAppsImpl::Bind(
mojo::PendingReceiver<remote_apps::mojom::RemoteApps> pending_remote_apps,
mojo::PendingRemote<remote_apps::mojom::RemoteAppLaunchObserver>
pending_observer) {
receivers_.Add(this, std::move(pending_remote_apps));
app_launch_observers_.Add(
mojo::Remote<remote_apps::mojom::RemoteAppLaunchObserver>(
std::move(pending_observer)));
}
void RemoteAppsImpl::AddFolder(const std::string& name,
AddFolderCallback callback) {
const std::string& folder_id = manager_->AddFolder(name);
std::move(callback).Run(folder_id, base::nullopt);
}
void RemoteAppsImpl::AddApp(const std::string& name,
const std::string& folder_id,
const GURL& icon_url,
AddAppCallback callback) {
manager_->AddApp(
name, folder_id, icon_url,
base::BindOnce(&RemoteAppsImpl::OnAppAdded, weak_factory_.GetWeakPtr(),
std::move(callback)));
}
void RemoteAppsImpl::OnAppLaunched(const std::string& id) {
if (!base::Contains(app_ids_, id))
return;
for (auto& observer : app_launch_observers_)
observer->OnRemoteAppLaunched(id);
}
void RemoteAppsImpl::OnAppAdded(AddAppCallback callback,
const std::string& id,
RemoteAppsError error) {
switch (error) {
case RemoteAppsError::kNotReady:
std::move(callback).Run(base::nullopt, kErrNotReady);
return;
case RemoteAppsError::kFolderIdDoesNotExist:
std::move(callback).Run(base::nullopt, kErrFolderIdDoesNotExist);
return;
case RemoteAppsError::kNone:
app_ids_.insert(id);
std::move(callback).Run(id, base::nullopt);
return;
case RemoteAppsError::kAppIdDoesNotExist:
// Only occurs when deleting an app, which is not yet implemented in the
// API.
DCHECK(false);
}
}
} // namespace chromeos
// 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_CHROMEOS_REMOTE_APPS_REMOTE_APPS_IMPL_H_
#define CHROME_BROWSER_CHROMEOS_REMOTE_APPS_REMOTE_APPS_IMPL_H_
#include <set>
#include <string>
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_types.h"
#include "chromeos/components/remote_apps/mojom/remote_apps.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "url/gurl.h"
namespace content {
class RenderFrameHost;
}
namespace extensions {
class Extension;
}
namespace chromeos {
class RemoteAppsManager;
// Forwards calls to RemoteAppsManager via mojo. Also keeps track of apps added
// by the extension for |OnAppLaunched()|.
class RemoteAppsImpl : public remote_apps::mojom::RemoteApps {
public:
static bool IsAllowed(content::RenderFrameHost* render_frame_host,
const extensions::Extension* extension);
static void SetBypassChecksForTesting(bool bypass_checks_for_testing);
explicit RemoteAppsImpl(RemoteAppsManager* manager);
RemoteAppsImpl(const RemoteAppsImpl&) = delete;
RemoteAppsImpl& operator=(const RemoteAppsImpl&) = delete;
~RemoteAppsImpl() override;
void Bind(
mojo::PendingReceiver<remote_apps::mojom::RemoteApps> pending_remote_apps,
mojo::PendingRemote<remote_apps::mojom::RemoteAppLaunchObserver>
pending_observer);
// remote_apps::mojom::RemoteApps:
void AddFolder(const std::string& name, AddFolderCallback callback) override;
void AddApp(const std::string& name,
const std::string& folder_id,
const GURL& icon_url,
AddAppCallback callback) override;
void OnAppLaunched(const std::string& id);
private:
void OnAppAdded(AddAppCallback callback,
const std::string& id,
RemoteAppsError error);
RemoteAppsManager* manager_ = nullptr;
std::set<std::string> app_ids_;
mojo::ReceiverSet<remote_apps::mojom::RemoteApps> receivers_;
mojo::RemoteSet<remote_apps::mojom::RemoteAppLaunchObserver>
app_launch_observers_;
base::WeakPtrFactory<RemoteAppsImpl> weak_factory_{this};
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_REMOTE_APPS_REMOTE_APPS_IMPL_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/chromeos/remote_apps/remote_apps_impl.h"
#include <string>
#include <vector>
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/app_list/model/app_list_item.h"
#include "ash/app_list/model/app_list_model.h"
#include "ash/shell.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
#include "chrome/browser/chromeos/login/test/session_manager_state_waiter.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/remote_apps/id_generator.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_manager.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_manager_factory.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_model.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/constants/chromeos_switches.h"
#include "components/policy/proto/chrome_device_policy.pb.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/api/test/test_api.h"
#include "extensions/common/manifest.h"
#include "extensions/common/switches.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace {
// ID of extension found at chrome/test/data/remote_apps.
constexpr char kExtensionId[] = "ceddkihciiemhnpnhbndbinppokgoidh";
constexpr char kId1[] = "Id 1";
constexpr char kId2[] = "Id 2";
constexpr char kId3[] = "Id 3";
} // namespace
class RemoteAppsImplBrowsertest : public policy::DevicePolicyCrosBrowserTest {
public:
RemoteAppsImplBrowsertest() : policy::DevicePolicyCrosBrowserTest() {}
// DevicePolicyCrosBrowserTest:
void SetUp() override {
app_list::AppListSyncableServiceFactory::SetUseInTesting(true);
RemoteAppsImpl::SetBypassChecksForTesting(true);
DevicePolicyCrosBrowserTest::SetUp();
}
// DevicePolicyCrosBrowserTest:
void SetUpCommandLine(base::CommandLine* command_line) override {
DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(chromeos::switches::kLoginManager);
command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
command_line->AppendSwitchASCII(
extensions::switches::kAllowlistedExtensionID, kExtensionId);
}
// DevicePolicyCrosBrowserTest:
void SetUpOnMainThread() override {
policy::DevicePolicyCrosBrowserTest::SetUpOnMainThread();
SetUpDeviceLocalAccountPolicy();
WizardController::SkipPostLoginScreensForTesting();
SessionStateWaiter(session_manager::SessionState::ACTIVE).Wait();
}
void SetUpDeviceLocalAccountPolicy() {
enterprise_management::DeviceLocalAccountsProto* const
device_local_accounts =
device_policy()->payload().mutable_device_local_accounts();
enterprise_management::DeviceLocalAccountInfoProto* const account =
device_local_accounts->add_account();
account->set_account_id("user@test");
account->set_type(enterprise_management::DeviceLocalAccountInfoProto::
ACCOUNT_TYPE_PUBLIC_SESSION);
device_local_accounts->set_auto_login_id("user@test");
device_local_accounts->set_auto_login_delay(0);
RefreshDevicePolicy();
}
void LoadExtensionAndRunTest(const std::string& test_name) {
config_.SetKey("customArg", base::Value(test_name));
extensions::TestGetConfigFunction::set_test_config_state(&config_);
base::FilePath test_dir_path;
base::PathService::Get(chrome::DIR_TEST_DATA, &test_dir_path);
base::FilePath extension_path =
test_dir_path.AppendASCII("extensions/remote_apps/extension");
base::FilePath pem_path =
test_dir_path.AppendASCII("extensions/remote_apps/remote_apps.pem");
user_manager::User* user =
user_manager::UserManager::Get()->GetActiveUser();
Profile* profile = ProfileHelper::Get()->GetProfileByUser(user);
std::unique_ptr<FakeIdGenerator> id_generator =
std::make_unique<FakeIdGenerator>(
std::vector<std::string>{kId1, kId2, kId3});
RemoteAppsManagerFactory::GetForProfile(profile)
->GetModelForTesting()
->SetIdGeneratorForTesting(std::move(id_generator));
extensions::ChromeTestExtensionLoader loader(profile);
loader.set_location(extensions::Manifest::EXTERNAL_POLICY);
loader.set_pack_extension(true);
loader.set_pem_path(pem_path);
ASSERT_TRUE(loader.LoadExtension(extension_path));
}
ash::AppListItem* GetAppListItem(const std::string& id) {
ash::AppListControllerImpl* controller =
ash::Shell::Get()->app_list_controller();
ash::AppListModel* model = controller->GetModel();
return model->FindItem(id);
}
private:
base::DictionaryValue config_;
chromeos::LocalPolicyTestServerMixin local_policy_mixin_{&mixin_host_};
};
IN_PROC_BROWSER_TEST_F(RemoteAppsImplBrowsertest, AddApp) {
extensions::ResultCatcher catcher;
LoadExtensionAndRunTest("AddApp");
ASSERT_TRUE(catcher.GetNextResult());
ash::AppListItem* app = GetAppListItem(kId1);
EXPECT_FALSE(app->is_folder());
EXPECT_EQ("App 1", app->name());
}
IN_PROC_BROWSER_TEST_F(RemoteAppsImplBrowsertest, AddFolderAndApps) {
extensions::ResultCatcher catcher;
LoadExtensionAndRunTest("AddFolderAndApps");
ASSERT_TRUE(catcher.GetNextResult());
ash::AppListItem* folder = GetAppListItem(kId1);
EXPECT_TRUE(folder->is_folder());
EXPECT_EQ("Folder 1", folder->name());
EXPECT_EQ(2u, folder->ChildItemCount());
EXPECT_TRUE(folder->FindChildItem(kId2));
EXPECT_TRUE(folder->FindChildItem(kId3));
ash::AppListItem* app1 = GetAppListItem(kId2);
EXPECT_EQ(kId1, app1->folder_id());
ash::AppListItem* app2 = GetAppListItem(kId3);
EXPECT_EQ(kId1, app2->folder_id());
}
IN_PROC_BROWSER_TEST_F(RemoteAppsImplBrowsertest, OnRemoteAppLaunched) {
extensions::ResultCatcher catcher;
ExtensionTestMessageListener listener("Remote app added",
/*will_reply=*/false);
listener.set_extension_id(kExtensionId);
LoadExtensionAndRunTest("OnRemoteAppLaunched");
ASSERT_TRUE(listener.WaitUntilSatisfied());
ChromeLauncherController::instance()->LaunchApp(
ash::ShelfID(kId1), ash::ShelfLaunchSource::LAUNCH_FROM_APP_LIST,
/*event_flags=*/0, /*display_id=*/0);
ASSERT_TRUE(catcher.GetNextResult());
}
} // namespace chromeos
......@@ -10,6 +10,7 @@
#include "ash/public/cpp/image_downloader.h"
#include "base/bind.h"
#include "chrome/browser/apps/app_service/menu_util.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_impl.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/app_list_model_updater.h"
#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
......@@ -73,12 +74,13 @@ void RemoteAppsManager::AddApp(const std::string& name,
const GURL& icon_url,
AddAppCallback callback) {
if (!is_initialized_) {
std::move(callback).Run(std::string(), Error::kNotReady);
std::move(callback).Run(std::string(), RemoteAppsError::kNotReady);
return;
}
if (!folder_id.empty() && !model_->HasFolder(folder_id)) {
std::move(callback).Run(std::string(), Error::kFolderIdDoesNotExist);
std::move(callback).Run(std::string(),
RemoteAppsError::kFolderIdDoesNotExist);
return;
}
......@@ -88,15 +90,15 @@ void RemoteAppsManager::AddApp(const std::string& name,
remote_apps_->AddApp(info);
}
RemoteAppsManager::Error RemoteAppsManager::DeleteApp(const std::string& id) {
RemoteAppsError RemoteAppsManager::DeleteApp(const std::string& id) {
// Check if app was added but |HandleOnAppAdded| has not been called.
if (!model_->HasApp(id) ||
add_app_callback_map_.find(id) != add_app_callback_map_.end())
return Error::kAppIdDoesNotExist;
return RemoteAppsError::kAppIdDoesNotExist;
model_->DeleteApp(id);
remote_apps_->DeleteApp(id);
return Error::kNone;
return RemoteAppsError::kNone;
}
std::string RemoteAppsManager::AddFolder(const std::string& folder_name) {
......@@ -105,17 +107,16 @@ std::string RemoteAppsManager::AddFolder(const std::string& folder_name) {
return folder_info.id;
}
RemoteAppsManager::Error RemoteAppsManager::DeleteFolder(
const std::string& folder_id) {
RemoteAppsError RemoteAppsManager::DeleteFolder(const std::string& folder_id) {
if (!model_->HasFolder(folder_id))
return Error::kFolderIdDoesNotExist;
return RemoteAppsError::kFolderIdDoesNotExist;
// Move all items out of the folder. Empty folders are automatically deleted.
RemoteAppsModel::FolderInfo& folder_info = model_->GetFolderInfo(folder_id);
for (const auto& app : folder_info.items)
model_updater_->MoveItemToFolder(app, std::string());
model_->DeleteFolder(folder_id);
return Error::kNone;
return RemoteAppsError::kNone;
}
void RemoteAppsManager::AddObserver(Observer* observer) {
......@@ -126,8 +127,22 @@ void RemoteAppsManager::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void RemoteAppsManager::BindInterface(
mojo::PendingReceiver<remote_apps::mojom::RemoteAppsFactory>
pending_remote_apps_factory) {
receivers_.Add(this, std::move(pending_remote_apps_factory));
}
void RemoteAppsManager::Shutdown() {}
void RemoteAppsManager::Create(
mojo::PendingReceiver<remote_apps::mojom::RemoteApps> pending_remote_apps,
mojo::PendingRemote<remote_apps::mojom::RemoteAppLaunchObserver>
pending_observer) {
remote_apps_impl_.Bind(std::move(pending_remote_apps),
std::move(pending_observer));
}
const std::map<std::string, RemoteAppsModel::AppInfo>&
RemoteAppsManager::GetApps() {
return model_->GetAllAppInfo();
......@@ -136,6 +151,7 @@ RemoteAppsManager::GetApps() {
void RemoteAppsManager::LaunchApp(const std::string& id) {
for (Observer& observer : observer_list_)
observer.OnAppLaunched(id);
remote_apps_impl_.OnAppLaunched(id);
}
gfx::ImageSkia RemoteAppsManager::GetIcon(const std::string& id) {
......@@ -214,7 +230,7 @@ void RemoteAppsManager::HandleOnAppAdded(const std::string& id) {
auto it = add_app_callback_map_.find(id);
DCHECK(it != add_app_callback_map_.end())
<< "Missing callback for id: " << id;
std::move(it->second).Run(id, Error::kNone);
std::move(it->second).Run(id, RemoteAppsError::kNone);
add_app_callback_map_.erase(it);
}
......
......@@ -13,11 +13,16 @@
#include "base/observer_list.h"
#include "base/scoped_observer.h"
#include "chrome/browser/apps/app_service/remote_apps.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_impl.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_model.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_types.h"
#include "chrome/browser/ui/app_list/app_list_model_updater_observer.h"
#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
#include "chrome/browser/ui/app_list/chrome_app_list_model_updater.h"
#include "chromeos/components/remote_apps/mojom/remote_apps.mojom.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
class AppListModelUpdater;
class ChromeAppListItem;
......@@ -33,6 +38,8 @@ class ImageSkia;
namespace chromeos {
class RemoteAppsImpl;
// KeyedService which manages the logic for |AppType::kRemote| in AppService.
// This service is only created for Managed Guest Sessions.
// The IDs of the added apps and folders are GUIDs generated using
......@@ -40,16 +47,9 @@ namespace chromeos {
class RemoteAppsManager : public KeyedService,
public apps::RemoteApps::Delegate,
public app_list::AppListSyncableService::Observer,
public AppListModelUpdaterObserver {
public AppListModelUpdaterObserver,
public remote_apps::mojom::RemoteAppsFactory {
public:
enum class Error {
kNone = 0,
kAppIdDoesNotExist,
kFolderIdDoesNotExist,
// Manager has not been initialized.
kNotReady,
};
class Observer : public base::CheckedObserver {
public:
~Observer() override = default;
......@@ -73,8 +73,12 @@ class RemoteAppsManager : public KeyedService,
bool is_initialized() const { return is_initialized_; }
void BindInterface(
mojo::PendingReceiver<remote_apps::mojom::RemoteAppsFactory>
pending_remote_apps_factory);
using AddAppCallback =
base::OnceCallback<void(const std::string& id, Error error)>;
base::OnceCallback<void(const std::string& id, RemoteAppsError error)>;
// Adds a app with the given |name|. If |folder_id| is non-empty, the app is
// added to the folder with the given ID. The icon of the app is an image
......@@ -92,7 +96,7 @@ class RemoteAppsManager : public KeyedService,
// Deletes the app with id |id|.
// Deleting a non-existent app will result in an error.
Error DeleteApp(const std::string& id);
RemoteAppsError DeleteApp(const std::string& id);
// Adds a folder with |folder_name|. Note that empty folders are not
// shown in the launcher. Returns the ID for the added folder.
......@@ -101,7 +105,7 @@ class RemoteAppsManager : public KeyedService,
// Deletes the folder with id |folder_id|. All items in the folder are moved
// to the top-level in the launcher.
// Deleting a non-existent folder will result in an error.
Error DeleteFolder(const std::string& folder_id);
RemoteAppsError DeleteFolder(const std::string& folder_id);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
......@@ -109,6 +113,12 @@ class RemoteAppsManager : public KeyedService,
// KeyedService:
void Shutdown() override;
// remote_apps::mojom::RemoteAppsFactory:
void Create(
mojo::PendingReceiver<remote_apps::mojom::RemoteApps> pending_remote_apps,
mojo::PendingRemote<remote_apps::mojom::RemoteAppLaunchObserver>
pending_observer) override;
// apps::RemoteApps::Delegate:
const std::map<std::string, RemoteAppsModel::AppInfo>& GetApps() override;
void LaunchApp(const std::string& id) override;
......@@ -146,9 +156,11 @@ class RemoteAppsManager : public KeyedService,
app_list::AppListSyncableService* app_list_syncable_service_ = nullptr;
AppListModelUpdater* model_updater_ = nullptr;
std::unique_ptr<apps::RemoteApps> remote_apps_;
RemoteAppsImpl remote_apps_impl_{this};
std::unique_ptr<RemoteAppsModel> model_;
std::unique_ptr<ImageDownloader> image_downloader_;
base::ObserverList<Observer> observer_list_;
mojo::ReceiverSet<remote_apps::mojom::RemoteAppsFactory> receivers_;
// Map from id to callback. The callback is run after |OnAppUpdate| for the
// app has been observed.
std::map<std::string, AddAppCallback> add_app_callback_map_;
......
......@@ -213,35 +213,34 @@ class RemoteAppsManagerBrowsertest
const GURL& icon_url) {
base::RunLoop run_loop;
std::string id;
manager_->AddApp(
name, folder_id, icon_url,
base::BindOnce(
[](base::RepeatingClosure closure, std::string* id_arg,
const std::string& id, RemoteAppsManager::Error error) {
ASSERT_EQ(RemoteAppsManager::Error::kNone, error);
ash::AppListControllerImpl* controller =
ash::Shell::Get()->app_list_controller();
ash::AppListModel* model = controller->GetModel();
ASSERT_TRUE(model->FindItem(id));
*id_arg = id;
closure.Run();
},
run_loop.QuitClosure(), &id));
manager_->AddApp(name, folder_id, icon_url,
base::BindOnce(
[](base::RepeatingClosure closure, std::string* id_arg,
const std::string& id, RemoteAppsError error) {
ASSERT_EQ(RemoteAppsError::kNone, error);
ash::AppListControllerImpl* controller =
ash::Shell::Get()->app_list_controller();
ash::AppListModel* model = controller->GetModel();
ASSERT_TRUE(model->FindItem(id));
*id_arg = id;
closure.Run();
},
run_loop.QuitClosure(), &id));
run_loop.Run();
return id;
}
RemoteAppsManager::Error DeleteApp(const std::string& id) {
RemoteAppsManager::Error error = manager_->DeleteApp(id);
RemoteAppsError DeleteApp(const std::string& id) {
RemoteAppsError error = manager_->DeleteApp(id);
// Allow updates to propagate to AppList.
base::RunLoop().RunUntilIdle();
return error;
}
RemoteAppsManager::Error DeleteFolder(const std::string& id) {
RemoteAppsManager::Error error = manager_->DeleteFolder(id);
RemoteAppsError DeleteFolder(const std::string& id) {
RemoteAppsError error = manager_->DeleteFolder(id);
// Allow updates to propagate to AppList.
base::RunLoop().RunUntilIdle();
return error;
......@@ -258,7 +257,7 @@ class RemoteAppsManagerBrowsertest
waiter.Wait();
}
void AddAppAssertError(RemoteAppsManager::Error error,
void AddAppAssertError(RemoteAppsError error,
const std::string& name,
const std::string& folder_id,
const GURL& icon_url) {
......@@ -266,9 +265,8 @@ class RemoteAppsManagerBrowsertest
manager_->AddApp(
name, folder_id, icon_url,
base::BindOnce(
[](base::RepeatingClosure closure,
RemoteAppsManager::Error expected_error, const std::string& id,
RemoteAppsManager::Error error) {
[](base::RepeatingClosure closure, RemoteAppsError expected_error,
const std::string& id, RemoteAppsError error) {
ASSERT_EQ(expected_error, error);
closure.Run();
},
......@@ -308,8 +306,8 @@ IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, AddAppError) {
GURL icon_url("icon_url");
gfx::ImageSkia icon = CreateTestIcon(32, SK_ColorRED);
AddAppAssertError(RemoteAppsManager::Error::kFolderIdDoesNotExist, name,
kMissingId, icon_url);
AddAppAssertError(RemoteAppsError::kFolderIdDoesNotExist, name, kMissingId,
icon_url);
}
IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, AddAppErrorNotReady) {
......@@ -318,8 +316,7 @@ IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, AddAppErrorNotReady) {
gfx::ImageSkia icon = CreateTestIcon(32, SK_ColorRED);
manager_->SetIsInitializedForTesting(false);
AddAppAssertError(RemoteAppsManager::Error::kNotReady, name, std::string(),
icon_url);
AddAppAssertError(RemoteAppsError::kNotReady, name, std::string(), icon_url);
}
IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, DeleteApp) {
......@@ -327,15 +324,14 @@ IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, DeleteApp) {
AddAppAndWaitForIconChange(kId1, "name", std::string(), GURL("icon_url"),
CreateTestIcon(32, SK_ColorRED));
RemoteAppsManager::Error error = DeleteApp(kId1);
RemoteAppsError error = DeleteApp(kId1);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(RemoteAppsManager::Error::kNone, error);
EXPECT_EQ(RemoteAppsError::kNone, error);
EXPECT_FALSE(GetAppListItem(kId1));
}
IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, DeleteAppError) {
EXPECT_EQ(RemoteAppsManager::Error::kAppIdDoesNotExist,
DeleteApp(kMissingId));
EXPECT_EQ(RemoteAppsError::kAppIdDoesNotExist, DeleteApp(kMissingId));
}
IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, AddAndDeleteFolder) {
......@@ -343,12 +339,11 @@ IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, AddAndDeleteFolder) {
// Empty folder has no AppListItem.
EXPECT_FALSE(GetAppListItem(kId1));
EXPECT_EQ(RemoteAppsManager::Error::kNone, DeleteFolder(kId1));
EXPECT_EQ(RemoteAppsError::kNone, DeleteFolder(kId1));
}
IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, DeleteFolderError) {
EXPECT_EQ(RemoteAppsManager::Error::kFolderIdDoesNotExist,
DeleteFolder(kMissingId));
EXPECT_EQ(RemoteAppsError::kFolderIdDoesNotExist, DeleteFolder(kMissingId));
}
IN_PROC_BROWSER_TEST_F(RemoteAppsManagerBrowsertest, AddFolderAndApp) {
......
// 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_CHROMEOS_REMOTE_APPS_REMOTE_APPS_TYPES_H_
#define CHROME_BROWSER_CHROMEOS_REMOTE_APPS_REMOTE_APPS_TYPES_H_
namespace chromeos {
enum class RemoteAppsError {
kNone = 0,
kAppIdDoesNotExist,
kFolderIdDoesNotExist,
// Manager has not been initialized.
kNotReady,
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_REMOTE_APPS_REMOTE_APPS_TYPES_H_
......@@ -1028,6 +1028,7 @@ static_library("extensions") {
"//chromeos/components/camera_app_ui:mojo_bindings",
"//chromeos/components/camera_app_ui/resources:chrome_camera_app",
"//chromeos/components/proximity_auth",
"//chromeos/components/remote_apps/mojom",
"//chromeos/constants",
"//chromeos/cryptohome",
"//chromeos/dbus",
......
......@@ -18,10 +18,14 @@
#include "extensions/common/permissions/permissions_data.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/remote_apps/remote_apps_impl.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_manager.h"
#include "chrome/browser/chromeos/remote_apps/remote_apps_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chromeos/components/camera_app_ui/camera_app_ui.h"
#include "chromeos/components/remote_apps/mojom/remote_apps.mojom.h"
#include "chromeos/services/cfm/public/buildflags/buildflags.h"
#include "chromeos/services/media_perception/public/mojom/media_perception.mojom.h"
#include "chromeos/services/tts/public/mojom/tts_service.mojom.h"
......@@ -80,7 +84,20 @@ void BindTtsStream(
->BindTtsStream(std::move(receiver));
}
#endif
void BindRemoteAppsFactory(
content::RenderFrameHost* render_frame_host,
mojo::PendingReceiver<chromeos::remote_apps::mojom::RemoteAppsFactory>
pending_receiver) {
// |remote_apps_manager| will be null in non-managed guest sessions, but this
// is already checked in |RemoteAppsImpl::IsAllowed()|.
chromeos::RemoteAppsManager* remote_apps_manager =
chromeos::RemoteAppsManagerFactory::GetForProfile(
Profile::FromBrowserContext(render_frame_host->GetBrowserContext()));
DCHECK(remote_apps_manager);
remote_apps_manager->BindInterface(std::move(pending_receiver));
}
#endif // defined(OS_CHROMEOS)
} // namespace
void PopulateChromeFrameBindersForExtension(
......@@ -155,7 +172,12 @@ void PopulateChromeFrameBindersForExtension(
binder_map->Add<chromeos::tts::mojom::TtsStream>(
base::BindRepeating(&BindTtsStream));
}
#endif
if (chromeos::RemoteAppsImpl::IsAllowed(render_frame_host, extension)) {
binder_map->Add<chromeos::remote_apps::mojom::RemoteAppsFactory>(
base::BindRepeating(&BindRemoteAppsFactory));
}
#endif // defined(OS_CHROMEOS)
}
} // namespace extensions
......@@ -38,6 +38,7 @@ grit("resources") {
]
if (is_chromeos) {
deps += [
"//chromeos/components/remote_apps/mojom:mojom_js",
"//chromeos/services/ime/public/mojom:mojom_js",
"//chromeos/services/tts/public/mojom:mojom_js",
]
......
......@@ -178,6 +178,14 @@ void ChromeExtensionsDispatcherDelegate::PopulateSourceMap(
source_map->RegisterSource("chromeos.tts.mojom.tts_stream.mojom",
IDR_TTS_STREAM_MOJOM_JS);
source_map->RegisterSource("chromeos.tts.stream", IDR_TTS_STREAM_BINDINGS_JS);
// Imprivata API.
source_map->RegisterSource("chromeos.remote_apps.mojom-lite",
IDR_REMOTE_APPS_MOJOM_LITE_JS);
source_map->RegisterSource("chromeos.remote_apps",
IDR_REMOTE_APPS_BINDINGS_JS);
source_map->RegisterSource("url/mojom/url.mojom-lite",
IDR_MOJO_URL_MOJOM_LITE_JS);
#endif // defined(OS_CHROMEOS)
source_map->RegisterSource(
......
hendrich@chromium.org
jityao@google.com
// 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.
'use strict';
if ((typeof mojo === 'undefined') || !mojo.bindingsLibraryInitialized) {
loadScript('mojo_bindings_lite');
}
loadScript('url/mojom/url.mojom-lite');
loadScript('chromeos.remote_apps.mojom-lite');
class RemoteAppsAdapter {
constructor() {
const factory = chromeos.remoteApps.mojom.RemoteAppsFactory.getRemote();
this.remoteApps_ = new chromeos.remoteApps.mojom.RemoteAppsRemote();
this.callbackRouter_ =
new chromeos.remoteApps.mojom.RemoteAppLaunchObserverCallbackRouter();
factory.create(
this.remoteApps_.$.bindNewPipeAndPassReceiver(),
this.callbackRouter_.$.bindNewPipeAndPassRemote());
}
/**
* Adds a folder to the launcher. Note that empty folders are not shown in
* the launcher.
* @param {string} name name of the added folder
* @return {!Promise<!{folderId: string, error: string}>} ID for the added
* folder
*/
addFolder(name) {
return this.remoteApps_.addFolder(name);
}
/**
* Adds an app to the launcher.
* @param {string} name name of the added app
* @param {string} folderId Id of the parent folder. An empty string
* indicates the app does not have a parent folder.
* @param {string} iconUrl URL to an image representing the app's icon
* @return {!Promise<!{appId: string, error: string}>} ID for the
* added app.
*/
addApp(name, folderId, iconUrl) {
return this.remoteApps_.addApp(name, folderId, {url: iconUrl});
}
/**
* Adds a callback for remote app launch events.
* @param {function(string)} callback called when a remote app is launched
* with the app ID as argument.
* @return {!Promise<void>}
*/
addRemoteAppLaunchObserver(callback) {
return this.callbackRouter_.onRemoteAppLaunched.addListener(callback);
}
}
exports.$set('returnValue', new RemoteAppsAdapter());
......@@ -63,10 +63,15 @@
<!-- ChromeOS IME Mojo service and bindings. -->
<include name="IDR_IME_SERVICE_BINDINGS_JS" file="extensions\chromeos_ime_service_bindings.js" type="BINDATA" />
<include name="IDR_IME_SERVICE_MOJOM_JS" file="${mojom_root}\chromeos/services/ime/public/mojom/input_engine.mojom.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_IME_SERVICE_MOJOM_JS" file="${mojom_root}\chromeos\services\ime\public\mojom\input_engine.mojom.js" use_base_dir="false" type="BINDATA" />
<!-- Remote Apps bindings. -->
<include name="IDR_REMOTE_APPS_BINDINGS_JS" file="extensions\remote_apps\remote_apps_bindings.js" type="BINDATA" />
<include name="IDR_REMOTE_APPS_MOJOM_LITE_JS" file="${mojom_root}\chromeos\components\remote_apps\mojom\remote_apps.mojom-lite.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_MOJO_URL_MOJOM_LITE_JS" file="${mojom_root}\url\mojom\url.mojom-lite.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_TTS_STREAM_BINDINGS_JS" file="extensions\chromeos_tts_stream_bindings.js" type="BINDATA" />
<include name="IDR_TTS_STREAM_MOJOM_JS" file="${mojom_root}\chromeos/services/tts/public/mojom/tts_service.mojom.js" use_base_dir="false" type="BINDATA" />
<include name="IDR_TTS_STREAM_MOJOM_JS" file="${mojom_root}\chromeos\services\tts\public\mojom\tts_service.mojom.js" use_base_dir="false" type="BINDATA" />
</if>
<!-- Media Router Mojo service and bindings. -->
<include name="IDR_MEDIA_CONTROLLER_MOJOM_JS" file="${mojom_root}\chrome\common\media_router\mojom\media_controller.mojom.js" use_base_dir="false" type="BINDATA" />
......
......@@ -2490,6 +2490,7 @@ if (!is_android) {
"../browser/chromeos/printing/test_printer_configurer.cc",
"../browser/chromeos/printing/test_printer_configurer.h",
"../browser/chromeos/profiles/profile_helper_browsertest.cc",
"../browser/chromeos/remote_apps/remote_apps_impl_browsertest.cc",
"../browser/chromeos/remote_apps/remote_apps_manager_browsertest.cc",
"../browser/chromeos/shutdown_policy_browsertest.cc",
"../browser/chromeos/startup_settings_cache_browsertest.cc",
......
{
"name": "Remote Apps",
"version": "0.1",
"manifest_version": 2,
"description": "Browser test for Remote Apps Mojo Private API",
"background": {
"scripts": ["test.js"]
}
}
// 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.
let api;
const testCases = [
async function AddApp() {
const result1 = await api.addApp('App 1', '', '');
chrome.test.assertFalse(!!result1.error);
chrome.test.assertEq('Id 1', result1.appId);
const result2 = await api.addApp('App 2', 'missing', '');
chrome.test.assertEq('Folder ID provided does not exist', result2.error);
chrome.test.succeed();
},
async function AddFolderAndApps() {
const result1 = await api.addFolder('Folder 1');
const folderId = result1.folderId;
chrome.test.assertFalse(!!result1.error);
chrome.test.assertEq('Id 1', folderId);
const result2 = await api.addApp('App 1', folderId, '');
chrome.test.assertFalse(!!result2.error);
chrome.test.assertEq('Id 2', result2.appId);
const result3 = await api.addApp('App 2', folderId, '');
chrome.test.assertFalse(!!result3.error);
chrome.test.assertEq('Id 3', result3.appId);
chrome.test.succeed();
},
async function OnRemoteAppLaunched() {
let actualId = '';
await new Promise(async (resolve) => {
await api.addRemoteAppLaunchObserver(id => {
actualId = id;
resolve();
});
await api.addApp('App 1', '', '');
chrome.test.sendMessage('Remote app added');
});
chrome.test.assertEq('Id 1', actualId);
chrome.test.succeed();
},
];
chrome.test.getConfig(async (config) => {
try {
api = await chrome.mojoPrivate.requireAsync('chromeos.remote_apps');
} catch (e) {
chrome.test.notifyFail('Could not get mojoPrivate bindings: ' + e.message);
return;
}
const testName = config.customArg;
const testCase = testCases.find((f) => f.name === testName);
if (!testCase) {
chrome.test.notifyFail('Test case \'' + testName + '\' not found');
return;
}
chrome.test.runTests([testCase]);
});
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDrhtzVny69zwz3
F0n517RrPaeuiXF9f7TXf+K+xrL3p0NDTrP6pj5A9tV2rbhCrMqEd6ub5WRhl7Y8
JlnWcAs6UikeJaQU7uSSVXxw2/ZJdEgTrcjL6DvfyyGpWf33TQn8yHVHpqiRxXr5
NV2ciA0Vv8abNgbi5EUQrM+yKXi3FrCGBewXNoffA8tUV3jYEdHEJirE3y1s65r9
xD9RMfPlGt6lV63EOGy+Ti88YldKPB6HiRHD9GmIH3BPkan9YGlmb4RBWLfoUNZS
jrrUAlQzkUaWLyqTYHZqtpNHaKClCAX6DNAodgchzd+YNWW/Ysev6et6IkAO5dbl
v4gxEkAPAgMBAAECggEAKiKv6kW2mn1qr9/KO7jDzbWzhG2RUKbipvT5jzDD/rs9
NNLlLu/DzmJ6UOeGQeNgva8dE+BHg5AdKYig5NSZpZ7iPUL1pksQuD8z6orndj+n
z2F1PUl4QLK5/G6dmTr+kOsZ1C40FRQTynaqHyFV2fC7qrPRKpE06+VGqPRzZKmC
HjeLUTevBgOYmVB/bRn1uc7yZN2YpuDoJ9BtU1HopyqVFVxTvJorsCAYD+94hS6I
+lKzW+X1U0JDGjTRaRZqHaOc7kgKJswD1B6xVKv58ZO4mRhmKbKtaKRF3IVLz9vb
RyoClY2Qrn57hOMSgKyh+jxN8jpiW76bTHOi2GofVQKBgQD6PhSHzqpDAVVg+ivR
JykRjfVtG5mOAYPB4PAZX+8KuKXxtFuh9xN98zJiB/EaqJNP0Kcnd09enkqHN4Cm
D8WEBzjokIxJQf55+DzeVtC1DkCAzuWdfXeJDjOQ/e447L9xeo0ms9ivjsWpl2T+
p6jfx7nk1N1cPZUGyIso0m1oVQKBgQDw8huFWKEM4K5TXMF9Z/keoS/q6uVoeSkB
335S/iZMEmnv8eG46y4ID30Mg4E4gye9U4Jc6wauorQYm1lOpfVlYxsVh7TaMFLk
mQ+jUWttIeURye3qn3t9YCSsIdKydNUrkkJd4nCCr31rPSQhRWa5PQUVjtOqI4B/
siafBDo60wKBgQCpC6by1zlNamkyyc0vzTSBF1TkD/D7bSqEnl+TxKrGo1X2odAE
6dPREajHcHX/fEGHeXxxvLdxQ501GtldVOoo9ngLIxqhomM2Iet8h0kWBjqsyRdz
/H3zqBRNrjxvV/87uX4A1x1Z+yisGAmxvbDm+xUo8GNZHIC/xFm9iek+wQKBgDQj
0DzM7x0ASfkUK3Ld2xT7wIjPiBFRlsQm/wkqolL38SDRcQ05J17rKx5YHtCB4Umh
FqbQ3UNRRjPE+lCArVfhWG0STtqgdm+th6rJ5btaCF4PGoMZO/nnokf1kci4a6Dg
J6h1Ze+B1lwsgPMKN66CO+VsYPWCdT4s6RqkKY2tAoGBAOiyRyW3RfpdGO36mv7p
r7qZ++tH+ZnTZ6CAp/+MTcx0XpCUnmSOGt0fU83TI9ujBeVJSoafg2MB/Th5UX11
RkN67zkTqeyej5scAzoTB+FD4+/zcrCfLze30dLCvtGp/IdwyLv9X+Yd4uxPIugm
CGqT0AcAo852wwzRPS/7eB6J
-----END PRIVATE KEY-----
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [ "remote_apps.mojom" ]
public_deps = [
"//mojo/public/mojom/base",
"//url/mojom:url_mojom_gurl",
]
}
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
// 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.
module chromeos.remote_apps.mojom;
import "url/mojom/url.mojom";
// Interface for communication between an extension and the Remote Apps
// Manager.
interface RemoteApps {
// Adds a Remote Apps folder to the launcher. Empty folders are not shown in
// the launcher.
//
// Input parameters:
// - |name|: the name of the folder.
//
// Output parameters:
// - |folder_id|: the ID of the newly added folder.
// - |error|: A string describing the error if any.
AddFolder(string name) => (string? folder_id, string? error);
// Adds a Remote Apps app to the launcher.
//
// Input parameters:
// - |name|: the name of the app.
// - |folder_id|: the ID of the parent folder. An empty string indicates the
// app has no parent folder.
// - |icon_url|: a URL pointing to an image which represents the app's icon.
//
// Output parameters:
// - |app_id|: the ID of the newly added app.
// - |error|: A string describing the error if any.
AddApp(string name, string folder_id, url.mojom.Url icon_url) =>
(string? app_id, string? error);
};
// Factory for creating an instance of RemoteApps.
interface RemoteAppsFactory {
// Creates an instance of RemoteApps.
Create(pending_receiver<RemoteApps> remote_apps,
pending_remote<RemoteAppLaunchObserver> observer);
};
// A RemoteAppLaunchObserver gets notifications when a remote app is launched.
interface RemoteAppLaunchObserver {
// Invoked when a remote app is launched. |app_id| is the ID of the app which
// was launched.
OnRemoteAppLaunched(string app_id);
};
......@@ -368,7 +368,7 @@
"dependencies": ["manifest:mime_types_handler"],
"contexts": ["blessed_extension"]
},
"mojoPrivate": {
"mojoPrivate": [{
"contexts": ["blessed_extension"],
"channel": "stable",
"extension_types": ["platform_app", "extension"],
......@@ -378,7 +378,13 @@
"B41E7F08E1179CC03CBD1F49E57CF353A40ADE07", // Chrome Camera App Dev
"A3E3DE9E9F16B41D4A2FAD106BD6CA76B94A0C94" // Chrome Camera App Stable
]
},
}, {
"contexts": ["blessed_extension"],
"dependencies": ["behavior:imprivata_in_session_extension"],
"extension_types": ["extension"],
"location": "policy",
"platforms": ["chromeos"]
}],
"networking.config": {
"dependencies": ["permission:networking.config"],
"contexts": ["blessed_extension"]
......
......@@ -752,6 +752,7 @@ std::vector<Dispatcher::JsResourceInfo> Dispatcher::GetJsResources() {
{"keep_alive", IDR_KEEP_ALIVE_JS},
{"mojo_bindings", IDR_MOJO_MOJO_BINDINGS_JS},
{"mojo_bindings_lite", IDR_MOJO_MOJO_BINDINGS_LITE_JS},
{"extensions/common/mojom/keep_alive.mojom", IDR_KEEP_ALIVE_MOJOM_JS},
// Custom bindings.
......
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