Commit 5db8550d authored by danielng's avatar danielng Committed by Commit Bot

plugin_vm: Adding UI for microphone and camera permissions

Adding UI features required for static microphone and camera
permissions. When permissions are shifted to a dynamic model, much of
this code will be changed/removed.

Screenshots: https://drive.google.com/drive/folders/1m66pUVmnhGa1BRc1JABzYpgsvJk8OCKj?usp=sharing
Testing: Implemented and ran browser-tests, tested UI locally

Bug: 1071872
Change-Id: Ib882acc57896a3c273e5484d5ce242e08667febc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2264175
Commit-Queue: Daniel Ng <danielng@google.com>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarNancy Wang <nancylingwang@chromium.org>
Reviewed-by: default avatarJoel Hockey <joelhockey@chromium.org>
Reviewed-by: default avatarJimmy Gong <jimmyxgong@chromium.org>
Reviewed-by: default avatarJeevan Shikaram <jshikaram@chromium.org>
Cr-Commit-Position: refs/heads/master@{#783809}
parent 24952667
...@@ -2801,6 +2801,14 @@ ...@@ -2801,6 +2801,14 @@
App details App details
</message> </message>
<!-- Apps > Manage your apps > Parallels -->
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL" desc="Label for the Parallels permissions dialog when camera permissions are being adjusted">
The change in camera setting requires Parallels Desktop to relaunch
</message>
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_MICROPHONE_LABEL" desc="Label for the Parallels permissions dialog when microphone permissions are being adjusted">
The change in microphone setting requires Parallels Desktop to relaunch
</message>
<!-- Apps > Manage your apps > Parallels > Shared folders --> <!-- Apps > Manage your apps > Parallels > Shared folders -->
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS" desc="Label for managing shared folders for Parallels."> <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS" desc="Label for managing shared folders for Parallels.">
Manage shared folders Manage shared folders
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include "chrome/browser/apps/app_service/menu_util.h" #include "chrome/browser/apps/app_service/menu_util.h"
#include "chrome/browser/chromeos/guest_os/guest_os_registry_service.h" #include "chrome/browser/chromeos/guest_os/guest_os_registry_service.h"
#include "chrome/browser/chromeos/guest_os/guest_os_registry_service_factory.h" #include "chrome/browser/chromeos/guest_os/guest_os_registry_service_factory.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager_factory.h" #include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager_factory.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h" #include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h" #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
...@@ -34,13 +33,21 @@ struct PermissionInfo { ...@@ -34,13 +33,21 @@ struct PermissionInfo {
const char* pref_name; const char* pref_name;
}; };
struct PluginVmHandledPermissionInfo {
app_management::mojom::PluginVmPermissionType permission;
plugin_vm::PermissionType permission_type;
};
constexpr PermissionInfo permission_infos[] = { constexpr PermissionInfo permission_infos[] = {
{app_management::mojom::PluginVmPermissionType::PRINTING, {app_management::mojom::PluginVmPermissionType::PRINTING,
plugin_vm::prefs::kPluginVmPrintersAllowed}, plugin_vm::prefs::kPluginVmPrintersAllowed},
};
constexpr PluginVmHandledPermissionInfo plugin_vm_handled_permission_infos[] = {
{app_management::mojom::PluginVmPermissionType::CAMERA, {app_management::mojom::PluginVmPermissionType::CAMERA,
plugin_vm::prefs::kPluginVmCameraAllowed}, plugin_vm::PermissionType::kCamera},
{app_management::mojom::PluginVmPermissionType::MICROPHONE, {app_management::mojom::PluginVmPermissionType::MICROPHONE,
plugin_vm::prefs::kPluginVmMicrophoneAllowed}, plugin_vm::PermissionType::kMicrophone},
}; };
const char* PermissionToPrefName( const char* PermissionToPrefName(
...@@ -75,7 +82,9 @@ void SetShowInAppManagement(apps::mojom::App* app, bool installed) { ...@@ -75,7 +82,9 @@ void SetShowInAppManagement(apps::mojom::App* app, bool installed) {
: apps::mojom::OptionalBool::kFalse; : apps::mojom::OptionalBool::kFalse;
} }
void PopulatePermissions(apps::mojom::App* app, Profile* profile) { void PopulatePermissions(apps::mojom::App* app,
Profile* profile,
bool allowed) {
for (const PermissionInfo& info : permission_infos) { for (const PermissionInfo& info : permission_infos) {
auto permission = apps::mojom::Permission::New(); auto permission = apps::mojom::Permission::New();
permission->permission_id = static_cast<uint32_t>(info.permission); permission->permission_id = static_cast<uint32_t>(info.permission);
...@@ -85,6 +94,21 @@ void PopulatePermissions(apps::mojom::App* app, Profile* profile) { ...@@ -85,6 +94,21 @@ void PopulatePermissions(apps::mojom::App* app, Profile* profile) {
permission->is_managed = false; permission->is_managed = false;
app->permissions.push_back(std::move(permission)); app->permissions.push_back(std::move(permission));
} }
for (const PluginVmHandledPermissionInfo& info :
plugin_vm_handled_permission_infos) {
auto permission = apps::mojom::Permission::New();
permission->permission_id = static_cast<uint32_t>(info.permission);
permission->value_type = apps::mojom::PermissionValueType::kBool;
if (allowed) {
permission->value = static_cast<uint32_t>(
plugin_vm::PluginVmManagerFactory::GetForProfile(profile)
->GetPermission(info.permission_type));
} else {
permission->value = static_cast<uint32_t>(false);
}
permission->is_managed = false;
app->permissions.push_back(std::move(permission));
}
} }
apps::mojom::AppPtr GetPluginVmApp(Profile* profile, bool allowed) { apps::mojom::AppPtr GetPluginVmApp(Profile* profile, bool allowed) {
...@@ -100,7 +124,7 @@ apps::mojom::AppPtr GetPluginVmApp(Profile* profile, bool allowed) { ...@@ -100,7 +124,7 @@ apps::mojom::AppPtr GetPluginVmApp(Profile* profile, bool allowed) {
IDR_LOGO_PLUGIN_VM_DEFAULT_192, apps::IconEffects::kNone); IDR_LOGO_PLUGIN_VM_DEFAULT_192, apps::IconEffects::kNone);
SetShowInAppManagement(app.get(), plugin_vm::IsPluginVmConfigured(profile)); SetShowInAppManagement(app.get(), plugin_vm::IsPluginVmConfigured(profile));
PopulatePermissions(app.get(), profile); PopulatePermissions(app.get(), profile, allowed);
SetAppAllowed(app.get(), allowed); SetAppAllowed(app.get(), allowed);
return app; return app;
...@@ -113,7 +137,7 @@ namespace apps { ...@@ -113,7 +137,7 @@ namespace apps {
PluginVmApps::PluginVmApps( PluginVmApps::PluginVmApps(
const mojo::Remote<apps::mojom::AppService>& app_service, const mojo::Remote<apps::mojom::AppService>& app_service,
Profile* profile) Profile* profile)
: profile_(profile) { : profile_(profile), permissions_observer_(this) {
PublisherBase::Initialize(app_service, apps::mojom::AppType::kPluginVm); PublisherBase::Initialize(app_service, apps::mojom::AppType::kPluginVm);
// Register for Plugin VM changes to policy and installed state, so that we // Register for Plugin VM changes to policy and installed state, so that we
...@@ -130,6 +154,10 @@ PluginVmApps::PluginVmApps( ...@@ -130,6 +154,10 @@ PluginVmApps::PluginVmApps(
base::Unretained(this))); base::Unretained(this)));
is_allowed_ = plugin_vm::IsPluginVmAllowedForProfile(profile_); is_allowed_ = plugin_vm::IsPluginVmAllowedForProfile(profile_);
if (is_allowed_) {
permissions_observer_.Add(
plugin_vm::PluginVmManagerFactory::GetForProfile(profile_));
}
} }
PluginVmApps::~PluginVmApps() = default; PluginVmApps::~PluginVmApps() = default;
...@@ -191,7 +219,7 @@ void PluginVmApps::SetPermission(const std::string& app_id, ...@@ -191,7 +219,7 @@ void PluginVmApps::SetPermission(const std::string& app_id,
apps::mojom::AppPtr app = apps::mojom::App::New(); apps::mojom::AppPtr app = apps::mojom::App::New();
app->app_type = apps::mojom::AppType::kPluginVm; app->app_type = apps::mojom::AppType::kPluginVm;
app->app_id = plugin_vm::kPluginVmShelfAppId; app->app_id = plugin_vm::kPluginVmShelfAppId;
PopulatePermissions(app.get(), profile_); PopulatePermissions(app.get(), profile_, is_allowed_);
Publish(std::move(app), subscribers_); Publish(std::move(app), subscribers_);
} }
...@@ -251,4 +279,14 @@ void PluginVmApps::OnPluginVmConfiguredChanged() { ...@@ -251,4 +279,14 @@ void PluginVmApps::OnPluginVmConfiguredChanged() {
Publish(std::move(app), subscribers_); Publish(std::move(app), subscribers_);
} }
void PluginVmApps::OnPluginVmPermissionsChanged(
plugin_vm::PermissionType permission_type,
bool allowed) {
apps::mojom::AppPtr app = apps::mojom::App::New();
app->app_type = apps::mojom::AppType::kPluginVm;
app->app_id = plugin_vm::kPluginVmShelfAppId;
PopulatePermissions(app.get(), profile_, is_allowed_);
Publish(std::move(app), subscribers_);
}
} // namespace apps } // namespace apps
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "base/scoped_observer.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h" #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
#include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_change_registrar.h"
#include "components/services/app_service/public/cpp/publisher_base.h" #include "components/services/app_service/public/cpp/publisher_base.h"
...@@ -24,7 +26,8 @@ namespace apps { ...@@ -24,7 +26,8 @@ namespace apps {
// An app publisher (in the App Service sense) of Plugin VM apps. // An app publisher (in the App Service sense) of Plugin VM apps.
// //
// See components/services/app_service/README.md. // See components/services/app_service/README.md.
class PluginVmApps : public apps::PublisherBase { class PluginVmApps : public apps::PublisherBase,
public plugin_vm::PluginVmPermissionsObserver {
public: public:
PluginVmApps(const mojo::Remote<apps::mojom::AppService>& app_service, PluginVmApps(const mojo::Remote<apps::mojom::AppService>& app_service,
Profile* profile); Profile* profile);
...@@ -59,6 +62,9 @@ class PluginVmApps : public apps::PublisherBase { ...@@ -59,6 +62,9 @@ class PluginVmApps : public apps::PublisherBase {
void OnPluginVmAllowedChanged(bool is_allowed); void OnPluginVmAllowedChanged(bool is_allowed);
void OnPluginVmConfiguredChanged(); void OnPluginVmConfiguredChanged();
// plugin_vm::PluginVmPermissionsObserver
void OnPluginVmPermissionsChanged(plugin_vm::PermissionType permission_type,
bool allowed) override;
mojo::RemoteSet<apps::mojom::Subscriber> subscribers_; mojo::RemoteSet<apps::mojom::Subscriber> subscribers_;
...@@ -67,6 +73,12 @@ class PluginVmApps : public apps::PublisherBase { ...@@ -67,6 +73,12 @@ class PluginVmApps : public apps::PublisherBase {
// Whether the Plugin VM app is allowed by policy. // Whether the Plugin VM app is allowed by policy.
bool is_allowed_; bool is_allowed_;
ScopedObserver<plugin_vm::PluginVmManager,
plugin_vm::PluginVmPermissionsObserver,
&plugin_vm::PluginVmManager::AddPluginVmPermissionsObserver,
&plugin_vm::PluginVmManager::RemovePluginVmPermissionsObserver>
permissions_observer_;
std::unique_ptr<plugin_vm::PluginVmPolicySubscription> policy_subscription_; std::unique_ptr<plugin_vm::PluginVmPolicySubscription> policy_subscription_;
PrefChangeRegistrar pref_registrar_; PrefChangeRegistrar pref_registrar_;
}; };
......
...@@ -10,4 +10,31 @@ PluginVmManager::PluginVmManager() = default; ...@@ -10,4 +10,31 @@ PluginVmManager::PluginVmManager() = default;
PluginVmManager::~PluginVmManager() = default; PluginVmManager::~PluginVmManager() = default;
bool PluginVmManager::GetPermission(PermissionType permission_type) {
auto it = permissions_.find(permission_type);
DCHECK(it != permissions_.end());
return it->second;
}
void PluginVmManager::SetPermission(PermissionType permission_type,
bool value) {
auto it = permissions_.find(permission_type);
DCHECK(it != permissions_.end());
if (it->second == value) {
return;
}
it->second = value;
for (auto& observer : plugin_vm_permissions_observers_) {
observer.OnPluginVmPermissionsChanged(permission_type, value);
}
}
void PluginVmManager::AddPluginVmPermissionsObserver(
PluginVmPermissionsObserver* observer) {
plugin_vm_permissions_observers_.AddObserver(observer);
}
void PluginVmManager::RemovePluginVmPermissionsObserver(
PluginVmPermissionsObserver* observer) {
plugin_vm_permissions_observers_.RemoveObserver(observer);
}
} // namespace plugin_vm } // namespace plugin_vm
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <string> #include <string>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/containers/flat_map.h"
#include "base/observer_list.h"
#include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher.pb.h" #include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher.pb.h"
#include "components/keyed_service/core/keyed_service.h" #include "components/keyed_service/core/keyed_service.h"
...@@ -17,6 +19,15 @@ class VmStartingObserver; ...@@ -17,6 +19,15 @@ class VmStartingObserver;
namespace plugin_vm { namespace plugin_vm {
enum class PermissionType { kCamera = 0, kMicrophone = 1 };
class PluginVmPermissionsObserver : public base::CheckedObserver {
public:
virtual void OnPluginVmPermissionsChanged(
plugin_vm::PermissionType permission_type,
bool allowed) = 0;
};
class PluginVmManager : public KeyedService { class PluginVmManager : public KeyedService {
public: public:
using LaunchPluginVmCallback = base::OnceCallback<void(bool success)>; using LaunchPluginVmCallback = base::OnceCallback<void(bool success)>;
...@@ -49,10 +60,24 @@ class PluginVmManager : public KeyedService { ...@@ -49,10 +60,24 @@ class PluginVmManager : public KeyedService {
virtual void RemoveVmStartingObserver( virtual void RemoveVmStartingObserver(
chromeos::VmStartingObserver* observer) = 0; chromeos::VmStartingObserver* observer) = 0;
// Add/remove permissions observers
void AddPluginVmPermissionsObserver(PluginVmPermissionsObserver* observer);
void RemovePluginVmPermissionsObserver(PluginVmPermissionsObserver* observer);
virtual vm_tools::plugin_dispatcher::VmState vm_state() const = 0; virtual vm_tools::plugin_dispatcher::VmState vm_state() const = 0;
bool GetPermission(PermissionType permission_type);
void SetPermission(PermissionType permission_type, bool value);
protected: protected:
PluginVmManager(); PluginVmManager();
private:
base::flat_map<PermissionType, bool> permissions_ = {
{PermissionType::kCamera, false},
{PermissionType::kMicrophone, false}};
base::ObserverList<PluginVmPermissionsObserver>
plugin_vm_permissions_observers_;
}; };
} // namespace plugin_vm } // namespace plugin_vm
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
#include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/tpm/stub_install_attributes.h" #include "chromeos/tpm/stub_install_attributes.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h" #include "components/prefs/scoped_user_pref_update.h"
...@@ -27,6 +28,11 @@ class PluginVmUtilTest : public testing::Test { ...@@ -27,6 +28,11 @@ class PluginVmUtilTest : public testing::Test {
MOCK_METHOD(void, OnPolicyChanged, (bool)); MOCK_METHOD(void, OnPolicyChanged, (bool));
protected: protected:
struct ScopedDBusThreadManager {
ScopedDBusThreadManager() { chromeos::DBusThreadManager::Initialize(); }
~ScopedDBusThreadManager() { chromeos::DBusThreadManager::Shutdown(); }
} dbus_thread_manager_;
content::BrowserTaskEnvironment task_environment_; content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestingProfile> testing_profile_; std::unique_ptr<TestingProfile> testing_profile_;
std::unique_ptr<PluginVmTestHelper> test_helper_; std::unique_ptr<PluginVmTestHelper> test_helper_;
......
...@@ -113,6 +113,11 @@ Polymer({ ...@@ -113,6 +113,11 @@ Polymer({
return app_management.util.getPermissionValueBool(app, permissionType); return app_management.util.getPermissionValueBool(app, permissionType);
}, },
resetToggle() {
const currentValue = this.getValue_(this.app_, this.permissionType);
this.$$('#toggle-row').setToggle(currentValue);
},
/** /**
* @private * @private
*/ */
...@@ -125,6 +130,14 @@ Polymer({ ...@@ -125,6 +130,14 @@ Polymer({
*/ */
togglePermission_() { togglePermission_() {
assert(this.app_); assert(this.app_);
// Plugin VM handles microphone and camera permissions manually.
// TODO(crbug:1071872): remove in m86 when plugin_vm permissions are
// updated.
if (this.app_.type == AppType.kPluginVm &&
(this.permissionType == 'MICROPHONE' ||
this.permissionType == 'CAMERA')) {
return;
}
/** @type {!Permission} */ /** @type {!Permission} */
let newPermission; let newPermission;
......
...@@ -18,9 +18,13 @@ js_library("plugin_vm_browser_proxy") { ...@@ -18,9 +18,13 @@ js_library("plugin_vm_browser_proxy") {
js_library("plugin_vm_detail_view") { js_library("plugin_vm_detail_view") {
deps = [ deps = [
":plugin_vm_browser_proxy",
":plugin_vm_permission_dialog",
"../:constants", "../:constants",
"../:permission_item",
"../:store_client", "../:store_client",
"../:util", "../:util",
"//ui/webui/resources/js:web_ui_listener_behavior",
] ]
} }
...@@ -31,6 +35,10 @@ js_library("plugin_vm_shared_paths") { ...@@ -31,6 +35,10 @@ js_library("plugin_vm_shared_paths") {
] ]
} }
js_library("plugin_vm_permission_dialog") {
deps = [ ":plugin_vm_browser_proxy" ]
}
# TODO: Uncomment as the Polymer3 migration makes progress. # TODO: Uncomment as the Polymer3 migration makes progress.
#js_type_check("closure_compile_module") { #js_type_check("closure_compile_module") {
# is_polymer3 = true # is_polymer3 = true
...@@ -71,6 +79,7 @@ group("polymer3_elements") { ...@@ -71,6 +79,7 @@ group("polymer3_elements") {
public_deps = [ public_deps = [
":modulize", ":modulize",
":plugin_vm_detail_view_module", ":plugin_vm_detail_view_module",
":plugin_vm_permission_dialog_module",
":plugin_vm_shared_paths_module", ":plugin_vm_shared_paths_module",
] ]
} }
...@@ -81,6 +90,12 @@ polymer_modulizer("plugin_vm_detail_view") { ...@@ -81,6 +90,12 @@ polymer_modulizer("plugin_vm_detail_view") {
html_type = "dom-module" html_type = "dom-module"
} }
polymer_modulizer("plugin_vm_permission_dialog") {
js_file = "plugin_vm_permission_dialog.js"
html_file = "plugin_vm_permission_dialog.html"
html_type = "dom-module"
}
polymer_modulizer("plugin_vm_shared_paths") { polymer_modulizer("plugin_vm_shared_paths") {
js_file = "plugin_vm_shared_paths.js" js_file = "plugin_vm_shared_paths.js"
html_file = "plugin_vm_shared_paths.html" html_file = "plugin_vm_shared_paths.html"
......
...@@ -2,6 +2,22 @@ ...@@ -2,6 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
/**
* These values should remain consistent with their C++ counterpart
* (chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h).
* @enum {number}
*/
const PermissionType = {
CAMERA: 0,
MICROPHONE: 1,
};
/**
* @typedef {{permissionType: !PermissionType,
* proposedValue: boolean}}
*/
let PermissionSetting;
/** /**
* @fileoverview A helper object used by the Plugin VM section * @fileoverview A helper object used by the Plugin VM section
* to manage the Plugin VM. * to manage the Plugin VM.
...@@ -20,6 +36,20 @@ cr.define('settings', function() { ...@@ -20,6 +36,20 @@ cr.define('settings', function() {
* @param {string} path Path to stop sharing. * @param {string} path Path to stop sharing.
*/ */
removePluginVmSharedPath(vmName, path) {} removePluginVmSharedPath(vmName, path) {}
/**
* @param {!PermissionSetting} permissionSetting The proposed change to
* permissions
* @return {!Promise<boolean>} Whether Plugin VM needs to be relaunched for
* permissions to take effect.
*/
wouldPermissionChangeRequireRelaunch(permissionSetting) {}
/**
* @param {!PermissionSetting} permissionSetting The change to make to the
* permissions
*/
setPluginVmPermission(permissionSetting) {}
} }
/** @implements {settings.PluginVmBrowserProxy} */ /** @implements {settings.PluginVmBrowserProxy} */
...@@ -33,6 +63,20 @@ cr.define('settings', function() { ...@@ -33,6 +63,20 @@ cr.define('settings', function() {
removePluginVmSharedPath(vmName, path) { removePluginVmSharedPath(vmName, path) {
chrome.send('removePluginVmSharedPath', [vmName, path]); chrome.send('removePluginVmSharedPath', [vmName, path]);
} }
/** @override */
wouldPermissionChangeRequireRelaunch(permissionSetting) {
return cr.sendWithPromise(
'wouldPermissionChangeRequireRelaunch',
permissionSetting.permissionType, permissionSetting.proposedValue);
}
/** @override */
setPluginVmPermission(permissionSetting) {
chrome.send(
'setPluginVmPermission',
[permissionSetting.permissionType, permissionSetting.proposedValue]);
}
} }
// The singleton instance_ can be replaced with a test version of this wrapper // The singleton instance_ can be replaced with a test version of this wrapper
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
<link rel="import" href="../store_client.html"> <link rel="import" href="../store_client.html">
<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="plugin_vm_permission_dialog.html">
<dom-module id="app-management-plugin-vm-detail-view"> <dom-module id="app-management-plugin-vm-detail-view">
<template> <template>
...@@ -27,16 +28,20 @@ ...@@ -27,16 +28,20 @@
<div class="permission-list indented-permission-block"> <div class="permission-list indented-permission-block">
<template is="dom-if" if="[[showCameraPermissions_]]"> <template is="dom-if" if="[[showCameraPermissions_]]">
<app-management-permission-item <app-management-permission-item
id="camera-permission"
class="subpermission-row" icon="app-management:camera" class="subpermission-row" icon="app-management:camera"
permission-label="$i18n{appManagementCameraPermissionLabel}" permission-label="$i18n{appManagementCameraPermissionLabel}"
permission-type="CAMERA"> permission-type="CAMERA"
on-change="onPermissionChanged_">
</app-management-permission-item> </app-management-permission-item>
</template> </template>
<template is="dom-if" if="[[showMicrophonePermissions_]]"> <template is="dom-if" if="[[showMicrophonePermissions_]]">
<app-management-permission-item <app-management-permission-item
id="microphone-permission"
class="subpermission-row" icon="app-management:microphone" class="subpermission-row" icon="app-management:microphone"
permission-label="$i18n{appManagementMicrophonePermissionLabel}" permission-label="$i18n{appManagementMicrophonePermissionLabel}"
permission-type="MICROPHONE"> permission-type="MICROPHONE"
on-change="onPermissionChanged_">
</app-management-permission-item> </app-management-permission-item>
</template> </template>
<app-management-permission-item <app-management-permission-item
...@@ -59,6 +64,14 @@ ...@@ -59,6 +64,14 @@
</div> </div>
</div> </div>
</div> </div>
<template is="dom-if" if="[[showPluginVmPermissionDialog_]]" restamp>
<app-management-plugin-vm-permission-dialog
on-close="onPluginVmPermissionDialogClose_"
pending-permission-change="[[pendingPermissionChange_]]"
reset-permission-change="{{resetPermissionChange}}">
</app-management-plugin-vm-permission-dialog>
</template>
</template> </template>
<script src="plugin_vm_detail_view.js"></script> <script src="plugin_vm_detail_view.js"></script>
</dom-module> </dom-module>
...@@ -7,6 +7,7 @@ Polymer({ ...@@ -7,6 +7,7 @@ Polymer({
behaviors: [ behaviors: [
app_management.StoreClient, app_management.StoreClient,
WebUIListenerBehavior,
], ],
properties: { properties: {
...@@ -36,6 +37,31 @@ Polymer({ ...@@ -36,6 +37,31 @@ Polymer({
return loadTimeData.getBoolean('showPluginVmMicrophonePermissions'); return loadTimeData.getBoolean('showPluginVmMicrophonePermissions');
}, },
}, },
/** @private {boolean} */
showPluginVmPermissionDialog_: {
type: Boolean,
value: false,
},
/**
* The current permssion type that is being changed
* and its proposed value.
* @private {?PermissionSetting}
*/
pendingPermissionChange_: {
type: Object,
value: null,
},
/**
* If the last permission change should be reset.
* {boolean}
*/
resetPermissionChange: {
type: Boolean,
value: false,
},
}, },
attached() { attached() {
...@@ -45,11 +71,68 @@ Polymer({ ...@@ -45,11 +71,68 @@ Polymer({
this.updateFromStore(); this.updateFromStore();
}, },
/** /** @private */
* @private
*/
onSharedPathsClick_() { onSharedPathsClick_() {
settings.Router.getInstance().navigateTo( settings.Router.getInstance().navigateTo(
settings.routes.APP_MANAGEMENT_PLUGIN_VM_SHARED_PATHS); settings.routes.APP_MANAGEMENT_PLUGIN_VM_SHARED_PATHS);
}, },
/**
* @param {!Event} e
* @private
*/
onPermissionChanged_: function(e) {
const permissionTypeString =
/** @type {{permissionType:string}} */ (e.target).permissionType;
let permissionType;
switch (permissionTypeString) {
case 'CAMERA':
permissionType = PermissionType.CAMERA;
break;
case 'MICROPHONE':
permissionType = PermissionType.MICROPHONE;
break;
default:
assertNotReached();
}
const permissionSetting = /** @type {!PermissionSetting} */ ({
permissionType: permissionType,
proposedValue: !app_management.util.getPermissionValueBool(
this.app_, permissionTypeString)
});
settings.PluginVmBrowserProxyImpl.getInstance()
.wouldPermissionChangeRequireRelaunch(permissionSetting)
.then(requiresRestart => {
if (requiresRestart) {
this.pendingPermissionChange_ = permissionSetting;
this.showPluginVmPermissionDialog_ = true;
} else {
settings.PluginVmBrowserProxyImpl.getInstance()
.setPluginVmPermission(permissionSetting);
}
});
},
/** @private */
onPluginVmPermissionDialogClose_: function() {
if (this.resetPermissionChange) {
switch (this.pendingPermissionChange_.permissionType) {
case PermissionType.CAMERA:
/* @type {!AppManagementPermissionItem} */ (
this.$$('#camera-permission'))
.resetToggle();
break;
case PermissionType.MICROPHONE:
/* @type @{!AppManagementPermissionItem} */ (
this.$$('#microphone-permission'))
.resetToggle();
break;
default:
assertNotReached();
}
}
this.resetPermissionChange = false;
this.showPluginVmPermissionDialog_ = false;
this.pendingPermissionChange_ = null;
},
}); });
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
<link rel="import" href="chrome://resources/html/i18n_behavior.html">
<link rel="import" href="../browser_proxy.html">
<link rel="import" href="../../../../settings_shared_css.html">
<dom-module id="app-management-plugin-vm-permission-dialog">
<template>
<style include="settings-shared"></style>
<cr-dialog id="dialog" close-text="$i18n{close}">
<div id="dialog-text" slot="body">[[dialogText_]]</div>
<div slot="button-container">
<cr-button id="dialogCancelButton" class="cancel-button"
on-click="onCancelTap_">
$i18n{cancel}
</cr-button>
<cr-button id="dialogOkButton" class="action-button"
on-click="onOkTap_">
$i18n{ok}
</cr-button>
</div>
</cr-dialog>
</template>
<script src="plugin_vm_permission_dialog.js"></script>
</dom-module>
// 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.
/**
* @fileoverview 'app-management-plugin-vm-permission-dialog' is a component
* that alerts the user when Plugin Vm needs to be relaunched in order for
* changes to the permission settings to take effect.
*/
Polymer({
is: 'app-management-plugin-vm-permission-dialog',
properties: {
/**
* An attribute indicating the type of permission that is being
* changed and what value to change it to.
* {!PermissionSetting}
*/
pendingPermissionChange: Object,
/**
* If the last permission change should be reset.
* {boolean}
*/
resetPermissionChange: {
type: Boolean,
notify: true,
},
/** @private {string} */
dialogText_: {
type: String,
computed: 'getDialogText_()',
},
},
/** @override */
attached() {
this.$.dialog.showModal();
},
/** @private */
onCancelTap_() {
this.resetPermissionChange = true;
this.$.dialog.close();
},
/** @private */
onOkTap_() {
// Reinitializing PermissionSetting Object to keep the closure compiler
// happy.
settings.PluginVmBrowserProxyImpl.getInstance().setPluginVmPermission({
permissionType: this.pendingPermissionChange.permissionType,
proposedValue: this.pendingPermissionChange.proposedValue
});
// TODO(1071872): Restart Plugin Vm.
this.$.dialog.close();
},
/** @private */
getDialogText_() {
switch (this.pendingPermissionChange.permissionType) {
case PermissionType.CAMERA:
return loadTimeData.getString('pluginVmPermissionDialogCameraLabel');
case PermissionType.MICROPHONE:
return loadTimeData.getString(
'pluginVmPermissionDialogMicrophoneLabel');
default:
assertNotReached();
}
},
});
...@@ -34,6 +34,13 @@ Polymer({ ...@@ -34,6 +34,13 @@ Polymer({
return this.$.toggle.checked; return this.$.toggle.checked;
}, },
/**
* @param {boolean} value What to set the toggle to.
*/
setToggle(value) {
this.$.toggle.checked = value;
},
/** /**
* @param {MouseEvent} event * @param {MouseEvent} event
* @private * @private
......
...@@ -240,6 +240,12 @@ ...@@ -240,6 +240,12 @@
<structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PLUGIN_VM_BROWSER_PROXY_HTML" <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PLUGIN_VM_BROWSER_PROXY_HTML"
file="chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.html" file="chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.html"
compress="false" type="chrome_html" /> compress="false" type="chrome_html" />
<structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PLUGIN_VM_PERMISSION_DIALOG_JS"
file="chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.js"
compress="false" type="chrome_html" />
<structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PLUGIN_VM_PERMISSION_DIALOG_HTML"
file="chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.html"
compress="false" type="chrome_html" />
<structure name="IDR_OS_SETTINGS_CAPTIONS_SUBPAGE_JS" <structure name="IDR_OS_SETTINGS_CAPTIONS_SUBPAGE_JS"
file="a11y_page/captions_subpage.js" file="a11y_page/captions_subpage.js"
compress="false" type="chrome_html" /> compress="false" type="chrome_html" />
......
...@@ -745,6 +745,13 @@ class PluginVmAppTest : public ::testing::TestWithParam<ProviderType> { ...@@ -745,6 +745,13 @@ class PluginVmAppTest : public ::testing::TestWithParam<ProviderType> {
void TearDown() override { ResetBuilder(); } void TearDown() override { ResetBuilder(); }
protected: protected:
// Required to ensure that the Plugin VM manager can be accessed in order to
// retrieve permissions.
struct ScopedDBusThreadManager {
ScopedDBusThreadManager() { chromeos::DBusThreadManager::Initialize(); }
~ScopedDBusThreadManager() { chromeos::DBusThreadManager::Shutdown(); }
} dbus_thread_manager_;
// Destroys any existing builder in the correct order. // Destroys any existing builder in the correct order.
void ResetBuilder() { void ResetBuilder() {
builder_.reset(); builder_.reset();
......
...@@ -314,6 +314,10 @@ void AppsSection::AddPluginVmLoadTimeData( ...@@ -314,6 +314,10 @@ void AppsSection::AddPluginVmLoadTimeData(
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING}, IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING},
{"pluginVmSharedPathsListEmptyMessage", {"pluginVmSharedPathsListEmptyMessage",
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE}, IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE},
{"pluginVmPermissionDialogCameraLabel",
IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL},
{"pluginVmPermissionDialogMicrophoneLabel",
IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_MICROPHONE_LABEL},
}; };
AddLocalizedStringsBulk(html_source, kLocalizedStrings); AddLocalizedStringsBulk(html_source, kLocalizedStrings);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "chrome/browser/chromeos/file_manager/path_util.h" #include "chrome/browser/chromeos/file_manager/path_util.h"
#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h" #include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
...@@ -32,6 +33,15 @@ void PluginVmHandler::RegisterMessages() { ...@@ -32,6 +33,15 @@ void PluginVmHandler::RegisterMessages() {
"removePluginVmSharedPath", "removePluginVmSharedPath",
base::BindRepeating(&PluginVmHandler::HandleRemovePluginVmSharedPath, base::BindRepeating(&PluginVmHandler::HandleRemovePluginVmSharedPath,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
"wouldPermissionChangeRequireRelaunch",
base::BindRepeating(
&PluginVmHandler::HandleWouldPermissionChangeRequireRelaunch,
weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
"setPluginVmPermission",
base::BindRepeating(&PluginVmHandler::HandleSetPluginVmPermission,
weak_ptr_factory_.GetWeakPtr()));
} }
void PluginVmHandler::HandleGetPluginVmSharedPathsDisplayText( void PluginVmHandler::HandleGetPluginVmSharedPathsDisplayText(
...@@ -68,5 +78,38 @@ void PluginVmHandler::HandleRemovePluginVmSharedPath( ...@@ -68,5 +78,38 @@ void PluginVmHandler::HandleRemovePluginVmSharedPath(
path)); path));
} }
void PluginVmHandler::HandleWouldPermissionChangeRequireRelaunch(
const base::ListValue* args) {
AllowJavascript();
CHECK_EQ(3U, args->GetSize());
std::string callback_id = args->GetList()[0].GetString();
plugin_vm::PermissionType permission_type =
static_cast<plugin_vm::PermissionType>(args->GetList()[1].GetInt());
DCHECK(permission_type == plugin_vm::PermissionType::kCamera ||
permission_type == plugin_vm::PermissionType::kMicrophone);
bool proposed_value = args->GetList()[2].GetBool();
bool current_value =
plugin_vm::PluginVmManagerFactory::GetForProfile(profile_)->GetPermission(
permission_type);
// This can probably incorrectly return false in a small window during
// startup.
bool requires_relaunch =
plugin_vm::IsPluginVmRunning(profile_) && proposed_value != current_value;
ResolveJavascriptCallback(base::Value(callback_id),
base::Value(requires_relaunch));
}
void PluginVmHandler::HandleSetPluginVmPermission(const base::ListValue* args) {
CHECK_EQ(2U, args->GetSize());
plugin_vm::PermissionType permission_type =
static_cast<plugin_vm::PermissionType>(args->GetList()[0].GetInt());
bool proposed_value = args->GetList()[1].GetBool();
DCHECK(permission_type == plugin_vm::PermissionType::kCamera ||
permission_type == plugin_vm::PermissionType::kMicrophone);
plugin_vm::PluginVmManagerFactory::GetForProfile(profile_)->SetPermission(
permission_type, proposed_value);
}
} // namespace settings } // namespace settings
} // namespace chromeos } // namespace chromeos
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <vector> #include <vector>
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager_factory.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
class Profile; class Profile;
...@@ -32,8 +34,14 @@ class PluginVmHandler : public ::settings::SettingsPageUIHandler { ...@@ -32,8 +34,14 @@ class PluginVmHandler : public ::settings::SettingsPageUIHandler {
void HandleGetPluginVmSharedPathsDisplayText(const base::ListValue* args); void HandleGetPluginVmSharedPathsDisplayText(const base::ListValue* args);
// Remove a specified path from being shared. // Remove a specified path from being shared.
void HandleRemovePluginVmSharedPath(const base::ListValue* args); void HandleRemovePluginVmSharedPath(const base::ListValue* args);
// Checks if Plugin VM would need to be relaunched if the proposed changes are
// made.
void HandleWouldPermissionChangeRequireRelaunch(const base::ListValue* args);
// Sets the specified permission to the value proposed.
void HandleSetPluginVmPermission(const base::ListValue* args);
Profile* profile_; Profile* profile_;
// weak_ptr_factory_ should always be last member. // weak_ptr_factory_ should always be last member.
base::WeakPtrFactory<PluginVmHandler> weak_ptr_factory_{this}; base::WeakPtrFactory<PluginVmHandler> weak_ptr_factory_{this};
......
...@@ -5,8 +5,17 @@ ...@@ -5,8 +5,17 @@
'use strict'; 'use strict';
suite('<app-management-plugin-vm-detail-view>', function() { suite('<app-management-plugin-vm-detail-view>', function() {
/** @enum {number} */
const Permissions = {
CAMERA: 0,
MICROPHONE: 1,
};
let pluginVmDetailView; let pluginVmDetailView;
let fakeHandler; let fakeHandler;
/** @type {?TestPluginVmBrowserProxy} */
let pluginVmBrowserProxy = null;
let appId;
function getPermissionBoolByType(permissionType) { function getPermissionBoolByType(permissionType) {
return app_management.util.getPermissionValueBool( return app_management.util.getPermissionValueBool(
...@@ -23,7 +32,60 @@ suite('<app-management-plugin-vm-detail-view>', function() { ...@@ -23,7 +32,60 @@ suite('<app-management-plugin-vm-detail-view>', function() {
return storeData.apps[storeData.selectedAppId]; return storeData.apps[storeData.selectedAppId];
} }
/**
* @param {boolean} expectedCameraState
* @param {!HTMLElement} cameraToggle
* @param {boolean} expectedMicrophoneState
* @param {!HTMLElement} microphoneToggle
*/
function assertToggleStates(
expectedCameraState, cameraToggle, expectedMicrophoneState,
microphoneToggle) {
assertEquals(expectedCameraState, cameraToggle.checked);
assertEquals(expectedMicrophoneState, microphoneToggle.checked);
}
/**
* @param {boolean} expectedCameraState
* @param {boolean} expectedMicrophoneState
*/
function assertPermissions(expectedCameraState, expectedMicrophoneState) {
assertEquals(
expectedCameraState,
pluginVmBrowserProxy.permissions[Permissions.CAMERA]);
assertEquals(
expectedMicrophoneState,
pluginVmBrowserProxy.permissions[Permissions.MICROPHONE]);
}
/**
* @param {boolean} booleanToChange
* @return {OptionalBool}
*/
function booleanToOptionalBool(booleanToChange) {
return booleanToChange ? Bool.kTrue : Bool.kFalse;
}
// Syncs the permissions stored in pluginVmBrowserProxy with app management.
function syncPermissions() {
const app = app_management.Store.getInstance().data.apps[appId];
app.permissions[PluginVmPermissionType.CAMERA] =
app_management.util.createPermission(
PluginVmPermissionType.CAMERA, PermissionValueType.kBool,
booleanToOptionalBool(
pluginVmBrowserProxy.permissions[Permissions.CAMERA]),
/*is_managed=*/ false);
app.permissions[PluginVmPermissionType.MICROPHONE] =
app_management.util.createPermission(
PluginVmPermissionType.MICROPHONE, PermissionValueType.kBool,
booleanToOptionalBool(
pluginVmBrowserProxy.permissions[Permissions.MICROPHONE]),
/*is_managed=*/ false);
}
setup(async function() { setup(async function() {
pluginVmBrowserProxy = new TestPluginVmBrowserProxy();
settings.PluginVmBrowserProxyImpl.instance_ = pluginVmBrowserProxy;
fakeHandler = setupFakeHandler(); fakeHandler = setupFakeHandler();
replaceStore(); replaceStore();
...@@ -44,18 +106,25 @@ suite('<app-management-plugin-vm-detail-view>', function() { ...@@ -44,18 +106,25 @@ suite('<app-management-plugin-vm-detail-view>', function() {
false /*is_managed*/); false /*is_managed*/);
} }
pluginVmBrowserProxy.pluginVmRunning = false;
pluginVmBrowserProxy.permissions =
[/*isCameraEnabled=*/ true, /*isMicrophoneEnabled=*/ true];
// Add an app, and make it the currently selected app. // Add an app, and make it the currently selected app.
const options = { const options = {
type: apps.mojom.AppType.kPluginVm, type: apps.mojom.AppType.kPluginVm,
permissions: permissions permissions: permissions
}; };
const app = await fakeHandler.addApp(null, options); const app = await fakeHandler.addApp(null, options);
appId = app.id;
app_management.Store.getInstance().dispatch( app_management.Store.getInstance().dispatch(
app_management.actions.updateSelectedAppId(app.id)); app_management.actions.updateSelectedAppId(appId));
syncPermissions();
pluginVmDetailView = pluginVmDetailView =
document.createElement('app-management-plugin-vm-detail-view'); document.createElement('app-management-plugin-vm-detail-view');
replaceBody(pluginVmDetailView); replaceBody(pluginVmDetailView);
await fakeHandler.flushPipesForTesting();
}); });
test('App is rendered correctly', function() { test('App is rendered correctly', function() {
...@@ -64,7 +133,7 @@ suite('<app-management-plugin-vm-detail-view>', function() { ...@@ -64,7 +133,7 @@ suite('<app-management-plugin-vm-detail-view>', function() {
pluginVmDetailView.app_.id); pluginVmDetailView.app_.id);
}); });
test('Toggle permissions', async function() { test('Toggle permissions handled by app settings', async function() {
const checkToggle = async (permissionType) => { const checkToggle = async (permissionType) => {
assertTrue(getPermissionBoolByType(permissionType)); assertTrue(getPermissionBoolByType(permissionType));
assertTrue(getPermissionCrToggleByType(pluginVmDetailView, permissionType) assertTrue(getPermissionCrToggleByType(pluginVmDetailView, permissionType)
...@@ -85,8 +154,6 @@ suite('<app-management-plugin-vm-detail-view>', function() { ...@@ -85,8 +154,6 @@ suite('<app-management-plugin-vm-detail-view>', function() {
}; };
await checkToggle('PRINTING'); await checkToggle('PRINTING');
await checkToggle('CAMERA');
await checkToggle('MICROPHONE');
}); });
test('Pin to shelf toggle', async function() { test('Pin to shelf toggle', async function() {
...@@ -113,4 +180,256 @@ suite('<app-management-plugin-vm-detail-view>', function() { ...@@ -113,4 +180,256 @@ suite('<app-management-plugin-vm-detail-view>', function() {
app_management.util.convertOptionalBoolToBool( app_management.util.convertOptionalBoolToBool(
getSelectedAppFromStore().isPinned)); getSelectedAppFromStore().isPinned));
}); });
test('Toggle permissions handled by Plugin Vm', async function() {
const camera_toggle = pluginVmDetailView.$$('#camera-permission')
.$$('#toggle-row')
.$$('#toggle');
const microphone_toggle = pluginVmDetailView.$$('#microphone-permission')
.$$('#toggle-row')
.$$('#toggle');
assertToggleStates(
/*expectedCameraState=*/ true, camera_toggle,
/*expectedMicrophoneState=*/ true, microphone_toggle);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
assertEquals(
0,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
camera_toggle.click();
await fakeHandler.flushPipesForTesting();
assertToggleStates(
/*expectedCameraState=*/ false, camera_toggle,
/*expectedMicrophoneState=*/ true, microphone_toggle);
assertPermissions(
/*expectedCameraState=*/ false, /*expectedMicrophoneState=*/ true);
syncPermissions();
camera_toggle.click();
await fakeHandler.flushPipesForTesting();
assertToggleStates(
/*expectedCameraState=*/ true, camera_toggle,
/*expectedMicrophoneState=*/ true, microphone_toggle);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
syncPermissions();
microphone_toggle.click();
await fakeHandler.flushPipesForTesting();
assertToggleStates(
/*expectedCameraState=*/ true, camera_toggle,
/*expectedMicrophoneState=*/ false, microphone_toggle);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ false);
syncPermissions();
microphone_toggle.click();
await fakeHandler.flushPipesForTesting();
assertToggleStates(
/*expectedCameraState=*/ true, camera_toggle,
/*expectedMicrophoneState=*/ true, microphone_toggle);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
assertEquals(
4,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(4, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
});
test('Cancel camera permission dialog', async function() {
const camera_toggle = pluginVmDetailView.$$('#camera-permission')
.$$('#toggle-row')
.$$('#toggle');
assertFalse(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
assertTrue(camera_toggle.checked);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
assertEquals(
0,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
pluginVmBrowserProxy.pluginVmRunning = true;
camera_toggle.click();
await fakeHandler.flushPipesForTesting();
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
assertTrue(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
const dialog =
pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog');
assertEquals(
dialog.$$('#dialog-text').innerText,
loadTimeData.getString('pluginVmPermissionDialogCameraLabel'));
assertFalse(camera_toggle.checked);
// Assert that the permission did not change.
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
const dialogClosedPromise = test_util.eventToPromise('close', dialog);
dialog.$$('#dialogCancelButton').click();
await Promise.all(
[dialogClosedPromise, fakeHandler.flushPipesForTesting()]);
syncPermissions();
assertEquals(
1,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
assertTrue(camera_toggle.checked);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
assertFalse(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
});
test('Cancel microphone permission dialog', async function() {
const microphone_toggle = pluginVmDetailView.$$('#microphone-permission')
.$$('#toggle-row')
.$$('#toggle');
assertFalse(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
assertTrue(microphone_toggle.checked);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
assertEquals(
0,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
pluginVmBrowserProxy.pluginVmRunning = true;
microphone_toggle.click();
await fakeHandler.flushPipesForTesting();
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
assertTrue(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
const dialog =
pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog');
assertEquals(
loadTimeData.getString('pluginVmPermissionDialogMicrophoneLabel'),
dialog.$$('#dialog-text').innerText);
assertFalse(microphone_toggle.checked);
// Assert that the permission did not change.
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
const dialogClosedPromise = test_util.eventToPromise('close', dialog);
dialog.$$('#dialogCancelButton').click();
await Promise.all(
[dialogClosedPromise, fakeHandler.flushPipesForTesting()]);
syncPermissions();
assertEquals(
1,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
assertTrue(microphone_toggle.checked);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
assertFalse(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
});
test('ok camera permission dialog', async function() {
const camera_toggle = pluginVmDetailView.$$('#camera-permission')
.$$('#toggle-row')
.$$('#toggle');
assertFalse(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
assertTrue(camera_toggle.checked);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
assertEquals(
0,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
pluginVmBrowserProxy.pluginVmRunning = true;
camera_toggle.click();
await fakeHandler.flushPipesForTesting();
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
assertTrue(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
const dialog =
pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog');
assertEquals(
dialog.$$('#dialog-text').innerText,
loadTimeData.getString('pluginVmPermissionDialogCameraLabel'));
assertFalse(camera_toggle.checked);
// Assert that the permission did not change.
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
const dialogClosedPromise = test_util.eventToPromise('close', dialog);
dialog.$$('#dialogOkButton').click();
await Promise.all(
[dialogClosedPromise, fakeHandler.flushPipesForTesting()]);
syncPermissions();
assertEquals(
1,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(1, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
assertFalse(camera_toggle.checked);
assertPermissions(
/*expectedCameraState=*/ false, /*expectedMicrophoneState=*/ true);
assertFalse(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
});
test('ok microphone permission dialog', async function() {
const microphone_toggle = pluginVmDetailView.$$('#microphone-permission')
.$$('#toggle-row')
.$$('#toggle');
assertFalse(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
assertTrue(microphone_toggle.checked);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
assertEquals(
0,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
pluginVmBrowserProxy.pluginVmRunning = true;
microphone_toggle.click();
await fakeHandler.flushPipesForTesting();
assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
assertTrue(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
const dialog =
pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog');
assertEquals(
loadTimeData.getString('pluginVmPermissionDialogMicrophoneLabel'),
dialog.$$('#dialog-text').innerText);
assertFalse(microphone_toggle.checked);
// Assert that the permission did not change.
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
const dialogClosedPromise = test_util.eventToPromise('close', dialog);
dialog.$$('#dialogOkButton').click();
await Promise.all(
[dialogClosedPromise, fakeHandler.flushPipesForTesting()]);
syncPermissions();
assertEquals(
1,
pluginVmBrowserProxy.getCallCount(
'wouldPermissionChangeRequireRelaunch'));
assertEquals(1, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
assertFalse(microphone_toggle.checked);
assertPermissions(
/*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ false);
assertFalse(
!!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
});
}); });
// 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.
/** @implements {settings.PluginVmBrowserProxy} */
class TestPluginVmBrowserProxy extends TestBrowserProxy {
constructor() {
super([
'getPluginVmSharedPathsDisplayText',
'removePluginVmSharedPath',
'wouldPermissionChangeRequireRelaunch',
'setPluginVmPermission',
]);
this.pluginVmRunning = false;
this.permissions = [true, true]; // [0]Camera, [1]Microphone
}
/** @override */
getPluginVmSharedPathsDisplayText(paths) {
this.methodCalled('getPluginVmSharedPathsDisplayText', paths);
return Promise.resolve(true);
}
/** @override */
removePluginVmSharedPath(vmName, path) {
this.methodCalled('removePluginVmSharedPath', vmName, path);
}
/** @override */
wouldPermissionChangeRequireRelaunch(permissionSetting) {
this.methodCalled(
'wouldPermissionChangeRequireRelaunch', permissionSetting);
return Promise.resolve(
permissionSetting.proposedValue !==
this.permissions[permissionSetting.permissionType] &&
this.pluginVmRunning);
}
/** @override */
setPluginVmPermission(permissionSetting) {
this.methodCalled('setPluginVmPermission', permissionSetting);
this.permissions[permissionSetting.permissionType] =
permissionSetting.proposedValue;
}
}
...@@ -403,6 +403,8 @@ var OSSettingsAppManagementPluginVmDetailViewTest = ...@@ -403,6 +403,8 @@ var OSSettingsAppManagementPluginVmDetailViewTest =
/** @override */ /** @override */
get extraLibraries() { get extraLibraries() {
return super.extraLibraries.concat([ return super.extraLibraries.concat([
BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
'app_management/test_plugin_vm_browser_proxy.js',
'app_management/plugin_vm_detail_view_test.js', 'app_management/plugin_vm_detail_view_test.js',
]); ]);
} }
......
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