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 @@
App details
</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 -->
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS" desc="Label for managing shared folders for Parallels.">
Manage shared folders
......
......@@ -15,7 +15,6 @@
#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_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_pref_names.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
......@@ -34,13 +33,21 @@ struct PermissionInfo {
const char* pref_name;
};
struct PluginVmHandledPermissionInfo {
app_management::mojom::PluginVmPermissionType permission;
plugin_vm::PermissionType permission_type;
};
constexpr PermissionInfo permission_infos[] = {
{app_management::mojom::PluginVmPermissionType::PRINTING,
plugin_vm::prefs::kPluginVmPrintersAllowed},
};
constexpr PluginVmHandledPermissionInfo plugin_vm_handled_permission_infos[] = {
{app_management::mojom::PluginVmPermissionType::CAMERA,
plugin_vm::prefs::kPluginVmCameraAllowed},
plugin_vm::PermissionType::kCamera},
{app_management::mojom::PluginVmPermissionType::MICROPHONE,
plugin_vm::prefs::kPluginVmMicrophoneAllowed},
plugin_vm::PermissionType::kMicrophone},
};
const char* PermissionToPrefName(
......@@ -75,7 +82,9 @@ void SetShowInAppManagement(apps::mojom::App* app, bool installed) {
: 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) {
auto permission = apps::mojom::Permission::New();
permission->permission_id = static_cast<uint32_t>(info.permission);
......@@ -85,6 +94,21 @@ void PopulatePermissions(apps::mojom::App* app, Profile* profile) {
permission->is_managed = false;
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) {
......@@ -100,7 +124,7 @@ apps::mojom::AppPtr GetPluginVmApp(Profile* profile, bool allowed) {
IDR_LOGO_PLUGIN_VM_DEFAULT_192, apps::IconEffects::kNone);
SetShowInAppManagement(app.get(), plugin_vm::IsPluginVmConfigured(profile));
PopulatePermissions(app.get(), profile);
PopulatePermissions(app.get(), profile, allowed);
SetAppAllowed(app.get(), allowed);
return app;
......@@ -113,7 +137,7 @@ namespace apps {
PluginVmApps::PluginVmApps(
const mojo::Remote<apps::mojom::AppService>& app_service,
Profile* profile)
: profile_(profile) {
: profile_(profile), permissions_observer_(this) {
PublisherBase::Initialize(app_service, apps::mojom::AppType::kPluginVm);
// Register for Plugin VM changes to policy and installed state, so that we
......@@ -130,6 +154,10 @@ PluginVmApps::PluginVmApps(
base::Unretained(this)));
is_allowed_ = plugin_vm::IsPluginVmAllowedForProfile(profile_);
if (is_allowed_) {
permissions_observer_.Add(
plugin_vm::PluginVmManagerFactory::GetForProfile(profile_));
}
}
PluginVmApps::~PluginVmApps() = default;
......@@ -191,7 +219,7 @@ void PluginVmApps::SetPermission(const std::string& app_id,
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_);
PopulatePermissions(app.get(), profile_, is_allowed_);
Publish(std::move(app), subscribers_);
}
......@@ -251,4 +279,14 @@ void PluginVmApps::OnPluginVmConfiguredChanged() {
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
......@@ -8,6 +8,8 @@
#include <memory>
#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 "components/prefs/pref_change_registrar.h"
#include "components/services/app_service/public/cpp/publisher_base.h"
......@@ -24,7 +26,8 @@ namespace apps {
// An app publisher (in the App Service sense) of Plugin VM apps.
//
// See components/services/app_service/README.md.
class PluginVmApps : public apps::PublisherBase {
class PluginVmApps : public apps::PublisherBase,
public plugin_vm::PluginVmPermissionsObserver {
public:
PluginVmApps(const mojo::Remote<apps::mojom::AppService>& app_service,
Profile* profile);
......@@ -59,6 +62,9 @@ class PluginVmApps : public apps::PublisherBase {
void OnPluginVmAllowedChanged(bool is_allowed);
void OnPluginVmConfiguredChanged();
// plugin_vm::PluginVmPermissionsObserver
void OnPluginVmPermissionsChanged(plugin_vm::PermissionType permission_type,
bool allowed) override;
mojo::RemoteSet<apps::mojom::Subscriber> subscribers_;
......@@ -67,6 +73,12 @@ class PluginVmApps : public apps::PublisherBase {
// Whether the Plugin VM app is allowed by policy.
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_;
PrefChangeRegistrar pref_registrar_;
};
......
......@@ -10,4 +10,31 @@ 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
......@@ -8,6 +8,8 @@
#include <string>
#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 "components/keyed_service/core/keyed_service.h"
......@@ -17,6 +19,15 @@ class VmStartingObserver;
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 {
public:
using LaunchPluginVmCallback = base::OnceCallback<void(bool success)>;
......@@ -49,10 +60,24 @@ class PluginVmManager : public KeyedService {
virtual void RemoveVmStartingObserver(
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;
bool GetPermission(PermissionType permission_type);
void SetPermission(PermissionType permission_type, bool value);
protected:
PluginVmManager();
private:
base::flat_map<PermissionType, bool> permissions_ = {
{PermissionType::kCamera, false},
{PermissionType::kMicrophone, false}};
base::ObserverList<PluginVmPermissionsObserver>
plugin_vm_permissions_observers_;
};
} // namespace plugin_vm
......
......@@ -10,6 +10,7 @@
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/tpm/stub_install_attributes.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
......@@ -27,6 +28,11 @@ class PluginVmUtilTest : public testing::Test {
MOCK_METHOD(void, OnPolicyChanged, (bool));
protected:
struct ScopedDBusThreadManager {
ScopedDBusThreadManager() { chromeos::DBusThreadManager::Initialize(); }
~ScopedDBusThreadManager() { chromeos::DBusThreadManager::Shutdown(); }
} dbus_thread_manager_;
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestingProfile> testing_profile_;
std::unique_ptr<PluginVmTestHelper> test_helper_;
......
......@@ -113,6 +113,11 @@ Polymer({
return app_management.util.getPermissionValueBool(app, permissionType);
},
resetToggle() {
const currentValue = this.getValue_(this.app_, this.permissionType);
this.$$('#toggle-row').setToggle(currentValue);
},
/**
* @private
*/
......@@ -125,6 +130,14 @@ Polymer({
*/
togglePermission_() {
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} */
let newPermission;
......
......@@ -18,9 +18,13 @@ js_library("plugin_vm_browser_proxy") {
js_library("plugin_vm_detail_view") {
deps = [
":plugin_vm_browser_proxy",
":plugin_vm_permission_dialog",
"../:constants",
"../:permission_item",
"../:store_client",
"../:util",
"//ui/webui/resources/js:web_ui_listener_behavior",
]
}
......@@ -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.
#js_type_check("closure_compile_module") {
# is_polymer3 = true
......@@ -71,6 +79,7 @@ group("polymer3_elements") {
public_deps = [
":modulize",
":plugin_vm_detail_view_module",
":plugin_vm_permission_dialog_module",
":plugin_vm_shared_paths_module",
]
}
......@@ -81,6 +90,12 @@ polymer_modulizer("plugin_vm_detail_view") {
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") {
js_file = "plugin_vm_shared_paths.js"
html_file = "plugin_vm_shared_paths.html"
......
......@@ -2,6 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// 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
* to manage the Plugin VM.
......@@ -20,6 +36,20 @@ cr.define('settings', function() {
* @param {string} path Path to stop sharing.
*/
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} */
......@@ -33,6 +63,20 @@ cr.define('settings', function() {
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
......
......@@ -8,6 +8,7 @@
<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/icons.html">
<link rel="import" href="plugin_vm_permission_dialog.html">
<dom-module id="app-management-plugin-vm-detail-view">
<template>
......@@ -27,16 +28,20 @@
<div class="permission-list indented-permission-block">
<template is="dom-if" if="[[showCameraPermissions_]]">
<app-management-permission-item
id="camera-permission"
class="subpermission-row" icon="app-management:camera"
permission-label="$i18n{appManagementCameraPermissionLabel}"
permission-type="CAMERA">
permission-type="CAMERA"
on-change="onPermissionChanged_">
</app-management-permission-item>
</template>
<template is="dom-if" if="[[showMicrophonePermissions_]]">
<app-management-permission-item
id="microphone-permission"
class="subpermission-row" icon="app-management:microphone"
permission-label="$i18n{appManagementMicrophonePermissionLabel}"
permission-type="MICROPHONE">
permission-type="MICROPHONE"
on-change="onPermissionChanged_">
</app-management-permission-item>
</template>
<app-management-permission-item
......@@ -59,6 +64,14 @@
</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>
<script src="plugin_vm_detail_view.js"></script>
</dom-module>
......@@ -7,6 +7,7 @@ Polymer({
behaviors: [
app_management.StoreClient,
WebUIListenerBehavior,
],
properties: {
......@@ -36,6 +37,31 @@ Polymer({
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() {
......@@ -45,11 +71,68 @@ Polymer({
this.updateFromStore();
},
/**
* @private
*/
/** @private */
onSharedPathsClick_() {
settings.Router.getInstance().navigateTo(
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({
return this.$.toggle.checked;
},
/**
* @param {boolean} value What to set the toggle to.
*/
setToggle(value) {
this.$.toggle.checked = value;
},
/**
* @param {MouseEvent} event
* @private
......
......@@ -240,6 +240,12 @@
<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"
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"
file="a11y_page/captions_subpage.js"
compress="false" type="chrome_html" />
......
......@@ -745,6 +745,13 @@ class PluginVmAppTest : public ::testing::TestWithParam<ProviderType> {
void TearDown() override { ResetBuilder(); }
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.
void ResetBuilder() {
builder_.reset();
......
......@@ -314,6 +314,10 @@ void AppsSection::AddPluginVmLoadTimeData(
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING},
{"pluginVmSharedPathsListEmptyMessage",
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);
......
......@@ -12,6 +12,7 @@
#include "base/bind_helpers.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/plugin_vm/plugin_vm_util.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_thread.h"
......@@ -32,6 +33,15 @@ void PluginVmHandler::RegisterMessages() {
"removePluginVmSharedPath",
base::BindRepeating(&PluginVmHandler::HandleRemovePluginVmSharedPath,
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(
......@@ -68,5 +78,38 @@ void PluginVmHandler::HandleRemovePluginVmSharedPath(
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 chromeos
......@@ -8,6 +8,8 @@
#include <vector>
#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"
class Profile;
......@@ -32,8 +34,14 @@ class PluginVmHandler : public ::settings::SettingsPageUIHandler {
void HandleGetPluginVmSharedPathsDisplayText(const base::ListValue* args);
// Remove a specified path from being shared.
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_;
// weak_ptr_factory_ should always be last member.
base::WeakPtrFactory<PluginVmHandler> weak_ptr_factory_{this};
......
// 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 =
/** @override */
get extraLibraries() {
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',
]);
}
......
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