Commit 61600282 authored by Jeevan Shikaram's avatar Jeevan Shikaram Committed by Commit Bot

[App Management] Add support to display managed badging for toggles.

Add support to detect managed pinning and managed permissions
and display badges next to the toggles.

Screenshot:
https://bugs.chromium.org/p/chromium/issues/detail?id=953580#c4

TBR: raymes@chromium.org
Bug: 953580
Change-Id: Ia7f56f10243bfffe99c229e1255052c882c01262
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1605442
Commit-Queue: Jeevan Shikaram <jshikaram@chromium.org>
Reviewed-by: default avatarcalamity <calamity@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#660344}
parent 5282e7d2
......@@ -76,4 +76,7 @@
<message name="IDS_APP_MANAGEMENT_VERSION" desc="Label for version of an app in the app settings page.">
Version: <ph name="VERSION">$1<ex>4.0</ex></ph>
</message>
<message name="IDS_APP_MANAGEMENT_PIN_ENFORCED_BY_POLICY" desc="Short text for the pin/unpin context menu to tell user the setting is enforced by policy of an app.">
Pinned by administrator
</message>
</grit-part>
......@@ -138,13 +138,15 @@ void LoadIcon0(apps::mojom::IconCompression icon_compression,
}
void UpdateAppPermissions(
const base::flat_map<arc::mojom::AppPermission, bool>& new_permissions,
const base::flat_map<arc::mojom::AppPermission,
arc::mojom::PermissionStatePtr>& new_permissions,
std::vector<apps::mojom::PermissionPtr>* permissions) {
for (const auto& new_permission : new_permissions) {
auto permission = apps::mojom::Permission::New();
permission->permission_id = static_cast<uint32_t>(new_permission.first);
permission->value_type = apps::mojom::PermissionValueType::kBool;
permission->value = static_cast<uint32_t>(new_permission.second);
permission->value = static_cast<uint32_t>(new_permission.second->granted);
permission->is_managed = new_permission.second->managed;
permissions->push_back(std::move(permission));
}
......
......@@ -28,6 +28,7 @@
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/services/app_service/public/mojom/types.mojom.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "extensions/browser/extension_prefs.h"
......@@ -466,10 +467,16 @@ void ExtensionApps::PopulatePermissions(
setting_val = apps::mojom::TriState::kAsk;
}
content_settings::SettingInfo setting_info;
host_content_settings_map->GetWebsiteSetting(url, url, type, std::string(),
&setting_info);
auto permission = apps::mojom::Permission::New();
permission->permission_id = static_cast<uint32_t>(type);
permission->value_type = apps::mojom::PermissionValueType::kTriState;
permission->value = static_cast<uint32_t>(setting_val);
permission->is_managed =
setting_info.source == content_settings::SETTING_SOURCE_POLICY;
target->push_back(std::move(permission));
}
......
......@@ -18,6 +18,18 @@ cr.define('app_management', function() {
this.handler = new app_management.FakePageHandler(
this.callbackRouter.createProxy());
const permissionOptions = {};
permissionOptions[PwaPermissionType.CONTENT_SETTINGS_TYPE_GEOLOCATION] =
{
permissionValue: TriState.kAllow,
isManaged: true,
};
permissionOptions[PwaPermissionType
.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA] = {
permissionValue: TriState.kBlock,
isManaged: true
};
const /** @type {!Array<App>}*/ appList = [
app_management.FakePageHandler.createApp(
'blpcfgokakmgnkcojhhkbfblekacnbeo',
......@@ -68,9 +80,14 @@ cr.define('app_management', function() {
app_management.FakePageHandler.createApp(
'aapocclcgogkmnckokdopfmhonfmgok',
{
title: 'Web App, policy installed',
title: 'Web App, policy applied',
type: AppType.kWeb,
installSource: InstallSource.kPolicy,
isPinned: apps.mojom.OptionalBool.kTrue,
isPolicyPinned: apps.mojom.OptionalBool.kTrue,
installSource: apps.mojom.InstallSource.kPolicy,
permissions:
app_management.FakePageHandler.createWebPermissions(
permissionOptions),
},
),
];
......
......@@ -8,9 +8,10 @@ cr.define('app_management', function() {
*/
class FakePageHandler {
/**
* @param {Object=} options
* @return {!Object<number, Permission>}
*/
static createWebPermissions() {
static createWebPermissions(options) {
const permissionIds = [
PwaPermissionType.CONTENT_SETTINGS_TYPE_GEOLOCATION,
PwaPermissionType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
......@@ -21,8 +22,17 @@ cr.define('app_management', function() {
const permissions = {};
for (const permissionId of permissionIds) {
let permissionValue = TriState.kAllow;
let isManaged = false;
if (options && options[permissionId]) {
const opts = options[permissionId];
permissionValue = opts.permissionValue || permissionValue;
isManaged = opts.isManaged || isManaged;
}
permissions[permissionId] = app_management.util.createPermission(
permissionId, PermissionValueType.kTriState, TriState.kAllow);
permissionId, PermissionValueType.kTriState, permissionValue,
isManaged);
}
return permissions;
......@@ -44,7 +54,8 @@ cr.define('app_management', function() {
for (const permissionId of permissionIds) {
permissions[permissionId] = app_management.util.createPermission(
permissionId, PermissionValueType.kBool, Bool.kTrue);
permissionId, PermissionValueType.kBool, Bool.kTrue,
false /*is_managed*/);
}
return permissions;
......@@ -79,6 +90,7 @@ cr.define('app_management', function() {
version: '5.1',
size: '9.0MB',
isPinned: apps.mojom.OptionalBool.kFalse,
isPolicyPinned: apps.mojom.OptionalBool.kFalse,
installSource: apps.mojom.InstallSource.kUser,
permissions: {},
};
......
......@@ -15,6 +15,7 @@
}
#shelf-switch {
align-items: center;
display: flex;
justify-content: space-between;
}
......@@ -36,13 +37,27 @@
display: flex;
justify-content: space-around;
}
#policy-indicator {
fill: var(--google-grey-refresh-700);
margin-inline-end: 12px;
}
</style>
<template is="dom-if" if="[[pinToShelfToggleVisible_(app_)]]">
<div id="shelf-switch-row">
<span id="shelf-switch" class="header-text">
<template is="dom-if" if="[[isPolicyPinned_(app_)]]">
<iron-icon id="policy-indicator" icon="cr:domain" tabindex="0"
aria-describedby="tooltip">
</iron-icon>
<paper-tooltip id="tooltip" for="policy-indicator"
position="top" fit-to-visible-bounds>
$i18n{pinControlledByPolicy}
</paper-tooltip>
</template>
$i18n{pinToShelf}
<cr-toggle id="pin-to-shelf-toggle" checked="[[isPinned_(app_)]]"
on-change="togglePinned_">
on-change="togglePinned_" disabled$="[[isPolicyPinned_(app_)]]">
</cr-toggle>
</span>
</div>
......@@ -51,7 +66,6 @@
<div id="metadata-overview" class="secondary-text">
<span>[[versionString_(app_)]]</span>
<span>[[sizeString_(app_)]]</span>
<!--TODO(ceciliani): Placeholder for legal declaration-->
</div>
</template>
<script src="metadata_view.js"></script>
......
......@@ -41,6 +41,10 @@ Polymer({
return app.isPinned === OptionalBool.kTrue;
},
isPolicyPinned_: function(app) {
return app.isPolicyPinned === OptionalBool.kTrue;
},
/** @private */
togglePinned_: function() {
let newPinnedValue;
......
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/policy/cr_tooltip_icon.html">
<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
<dom-module id="app-management-permission-toggle">
<template>
<style>
:host {
align-items: center;
display: flex;
}
#policy-indicator {
fill: var(--google-grey-refresh-700);
padding-inline-end: 12px;
}
</style>
<template is="dom-if"
if="[[isPermissionManaged_(app, permissionType)]]">
<iron-icon id="policy-indicator" icon="cr:domain" tabindex="0"
aria-describedby="tooltip">
</iron-icon>
<paper-tooltip id="tooltip" for="policy-indicator"
position="top" fit-to-visible-bounds>
$i18n{controlledByPolicy}
</paper-tooltip>
</template>
<cr-toggle checked="[[getPermissionValueBool_(app, permissionType)]]"
on-change="togglePermission_">
on-change="togglePermission_"
disabled$="[[isPermissionManaged_(app, permissionType)]]">
</cr-toggle>
</template>
<script src="permission_toggle.js"></script>
......
......@@ -34,6 +34,22 @@ Polymer({
return app_management.util.getPermissionValueBool(app, permissionType);
},
/**
* @param {App} app
* @param {string} permissionType
* @return {boolean}
*/
isPermissionManaged_: function(app, permissionType) {
if (app === undefined || permissionType === undefined) {
return false;
}
assert(app);
const permission = app_management.util.getPermission(app, permissionType);
assert(permission);
return permission.isManaged;
},
togglePermission_: function() {
assert(this.app);
......@@ -67,8 +83,10 @@ Polymer({
*/
getNewPermissionBoolean_: function(app, permissionType) {
let newPermissionValue;
const currentPermission =
app_management.util.getPermission(app, permissionType);
switch (app_management.util.getPermission(app, permissionType).value) {
switch (currentPermission.value) {
case Bool.kFalse:
newPermissionValue = Bool.kTrue;
break;
......@@ -82,7 +100,8 @@ Polymer({
assert(newPermissionValue !== undefined);
return app_management.util.createPermission(
app_management.util.permissionTypeHandle(app, permissionType),
PermissionValueType.kBool, newPermissionValue);
PermissionValueType.kBool, newPermissionValue,
currentPermission.isManaged);
},
/**
......@@ -93,8 +112,10 @@ Polymer({
*/
getNewPermissionTriState_: function(app, permissionType) {
let newPermissionValue;
const currentPermission =
app_management.util.getPermission(app, permissionType);
switch (app_management.util.getPermission(app, permissionType).value) {
switch (currentPermission.value) {
case TriState.kBlock:
newPermissionValue = TriState.kAllow;
break;
......@@ -115,6 +136,7 @@ Polymer({
assert(newPermissionValue !== undefined);
return app_management.util.createPermission(
app_management.util.permissionTypeHandle(app, permissionType),
PermissionValueType.kTriState, newPermissionValue);
PermissionValueType.kTriState, newPermissionValue,
currentPermission.isManaged);
},
});
......@@ -39,8 +39,8 @@
}
#policy-indicator {
fill: var(--google-grey-refresh-500);
padding-inline-end: 10px;
fill: var(--google-grey-refresh-700);
margin-inline-end: 12px;
}
</style>
<cr-icon-button class="icon-arrow-back" id="backButton"
......
......@@ -58,13 +58,15 @@ cr.define('app_management.util', function() {
* @param {number} permissionId
* @param {!PermissionValueType} valueType
* @param {number} value
* @param {boolean} isManaged
* @return {!Permission}
*/
function createPermission(permissionId, valueType, value) {
function createPermission(permissionId, valueType, value, isManaged) {
return {
permissionId: permissionId,
valueType: valueType,
value: value,
permissionId,
valueType,
value,
isManaged,
};
}
......
......@@ -73,6 +73,8 @@ constexpr char kSuspended[] = "suspended";
constexpr char kSystem[] = "system";
constexpr char kUninstalled[] = "uninstalled";
constexpr char kVPNProvider[] = "vpnprovider";
constexpr char kPermissionStateGranted[] = "granted";
constexpr char kPermissionStateManaged[] = "managed";
// Defines current version for app icons. This is used for invalidation icons in
// case we change how app icons are produced on Android side. Can be updated in
......@@ -610,7 +612,8 @@ std::unique_ptr<ArcAppListPrefs::PackageInfo> ArcAppListPrefs::GetPackage(
bool should_sync = false;
bool system = false;
bool vpn_provider = false;
base::flat_map<arc::mojom::AppPermission, bool> permissions;
base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
permissions;
GetInt64FromPref(package, kLastBackupAndroidId, &last_backup_android_id);
GetInt64FromPref(package, kLastBackupTime, &last_backup_time);
......@@ -618,6 +621,7 @@ std::unique_ptr<ArcAppListPrefs::PackageInfo> ArcAppListPrefs::GetPackage(
package->GetBoolean(kShouldSync, &should_sync);
package->GetBoolean(kSystem, &system);
package->GetBoolean(kVPNProvider, &vpn_provider);
const base::Value* permission_val = package->FindKey(kPermissions);
if (permission_val) {
const base::DictionaryValue* permission_dict = nullptr;
......@@ -630,18 +634,33 @@ std::unique_ptr<ArcAppListPrefs::PackageInfo> ArcAppListPrefs::GetPackage(
base::StringToInt64(iter.key(), &permission_type);
DCHECK_NE(-1, permission_type);
bool value = false;
iter.value().GetAsBoolean(&value);
bool granted = false;
bool managed = false;
const base::Value& permission_state = iter.value();
// Handle old pref structure.
if (permission_state.is_bool()) {
permission_state.GetAsBoolean(&granted);
}
// Handle new pref structure.
if (permission_state.is_dict()) {
const base::DictionaryValue* permission_state_dict = nullptr;
permission_state.GetAsDictionary(&permission_state_dict);
permission_state_dict->GetBoolean(kPermissionStateGranted, &granted);
permission_state_dict->GetBoolean(kPermissionStateManaged, &managed);
}
auto state = arc::mojom::PermissionState::New(granted, managed);
arc::mojom::AppPermission permission =
static_cast<arc::mojom::AppPermission>(permission_type);
permissions.insert(std::make_pair(permission, value));
permissions.emplace(permission, std::move(state));
}
}
return std::make_unique<PackageInfo>(
package_name, package_version, last_backup_android_id, last_backup_time,
should_sync, system, vpn_provider, permissions);
should_sync, system, vpn_provider, std::move(permissions));
}
std::vector<std::string> ArcAppListPrefs::GetAppIds() const {
......@@ -1249,13 +1268,18 @@ void ArcAppListPrefs::AddOrUpdatePackagePrefs(
package_dict->SetBoolean(kUninstalled, false);
package_dict->SetBoolean(kVPNProvider, package.vpn_provider);
if (package.permissions.has_value()) {
base::DictionaryValue permission_dict;
base::DictionaryValue permissions_dict;
for (const auto& permission : package.permissions.value()) {
permission_dict.SetBoolean(
base::DictionaryValue permission_dict;
permission_dict.SetBoolean(kPermissionStateGranted,
permission.second->granted);
permission_dict.SetBoolean(kPermissionStateManaged,
permission.second->managed);
permissions_dict.SetKey(
base::NumberToString(static_cast<int64_t>(permission.first)),
permission.second);
std::move(permission_dict));
}
package_dict->SetKey(kPermissions, std::move(permission_dict));
package_dict->SetKey(kPermissions, std::move(permissions_dict));
} else {
// Remove kPermissions from dict if there are no permissions.
package_dict->RemoveKey(kPermissions);
......@@ -1892,7 +1916,8 @@ ArcAppListPrefs::PackageInfo::PackageInfo(
bool should_sync,
bool system,
bool vpn_provider,
const base::flat_map<arc::mojom::AppPermission, bool>& permissions)
base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
permissions)
: package_name(package_name),
package_version(package_version),
last_backup_android_id(last_backup_android_id),
......@@ -1900,7 +1925,7 @@ ArcAppListPrefs::PackageInfo::PackageInfo(
should_sync(should_sync),
system(system),
vpn_provider(vpn_provider),
permissions(permissions) {}
permissions(std::move(permissions)) {}
// Need to add explicit destructor for chromium style checker error:
// Complex class/struct needs an explicit out-of-line destructor
......
......@@ -116,15 +116,15 @@ class ArcAppListPrefs : public KeyedService,
};
struct PackageInfo {
PackageInfo(
const std::string& package_name,
int32_t package_version,
int64_t last_backup_android_id,
int64_t last_backup_time,
bool should_sync,
bool system,
bool vpn_provider,
const base::flat_map<arc::mojom::AppPermission, bool>& permissions);
PackageInfo(const std::string& package_name,
int32_t package_version,
int64_t last_backup_android_id,
int64_t last_backup_time,
bool should_sync,
bool system,
bool vpn_provider,
base::flat_map<arc::mojom::AppPermission,
arc::mojom::PermissionStatePtr> permissions);
~PackageInfo();
std::string package_name;
......@@ -134,8 +134,9 @@ class ArcAppListPrefs : public KeyedService,
bool should_sync;
bool system;
bool vpn_provider;
// Maps app permission to boolean values
base::flat_map<arc::mojom::AppPermission, bool> permissions;
// Maps app permission to permission states
base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
permissions;
};
class Observer {
......
......@@ -172,27 +172,47 @@ void ArcAppTest::CreateFakeAppsAndPackages() {
app.sticky = true;
fake_default_apps_.push_back(app);
base::flat_map<arc::mojom::AppPermission, bool> permissions;
permissions.insert(std::make_pair(arc::mojom::AppPermission::CAMERA, 0));
base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
permissions1;
permissions1.emplace(arc::mojom::AppPermission::CAMERA,
arc::mojom::PermissionState::New(false /* granted */,
false /* managed */));
fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
kPackageName1 /* package_name */, 1 /* package_version */,
1 /* last_backup_android_id */, 1 /* last_backup_time */,
false /* sync */, false /* system */, false /* vpn_provider */,
nullptr /* web_app_info */, permissions));
permissions.insert(std::make_pair(arc::mojom::AppPermission::MICROPHONE, 0));
nullptr /* web_app_info */, std::move(permissions1)));
base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
permissions2;
permissions2.emplace(arc::mojom::AppPermission::CAMERA,
arc::mojom::PermissionState::New(false /* granted */,
false /* managed */));
permissions2.emplace(arc::mojom::AppPermission::MICROPHONE,
arc::mojom::PermissionState::New(false /* granted */,
false /* managed */));
fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
kPackageName2 /* package_name */, 2 /* package_version */,
2 /* last_backup_android_id */, 2 /* last_backup_time */, true /* sync */,
false /* system */, false /* vpn_provider */, nullptr /* web_app_info */,
permissions));
permissions.insert(std::make_pair(arc::mojom::AppPermission::LOCATION, 1));
std::move(permissions2)));
base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
permissions3;
permissions3.emplace(arc::mojom::AppPermission::CAMERA,
arc::mojom::PermissionState::New(false /* granted */,
false /* managed */));
permissions3.emplace(arc::mojom::AppPermission::MICROPHONE,
arc::mojom::PermissionState::New(false /* granted */,
false /* managed */));
permissions3.emplace(arc::mojom::AppPermission::LOCATION,
arc::mojom::PermissionState::New(true /* granted */,
false /* managed */));
fake_packages_.emplace_back(arc::mojom::ArcPackageInfo::New(
kPackageName3 /* package_name */, 3 /* package_version */,
3 /* last_backup_android_id */, 3 /* last_backup_time */,
false /* sync */, false /* system */, false /* vpn_provider */,
nullptr /* web_app_info */, permissions));
nullptr /* web_app_info */, std::move(permissions3)));
for (int i = 0; i < 3; ++i) {
arc::mojom::ShortcutInfo shortcut_info;
......
......@@ -465,13 +465,19 @@ class ArcAppModelBuilderTest : public extensions::ExtensionServiceTestBase,
arc::mojom::ArcPackageInfoPtr CreatePackageWithVersion(
const std::string& package_name,
int package_version) {
base::flat_map<arc::mojom::AppPermission, bool> permissions;
permissions.insert(std::make_pair(arc::mojom::AppPermission::CAMERA, 0));
permissions.insert(std::make_pair(arc::mojom::AppPermission::LOCATION, 1));
base::flat_map<arc::mojom::AppPermission, arc::mojom::PermissionStatePtr>
permissions;
permissions.emplace(arc::mojom::AppPermission::CAMERA,
arc::mojom::PermissionState::New(false /* granted */,
false /* managed */));
permissions.emplace(arc::mojom::AppPermission::LOCATION,
arc::mojom::PermissionState::New(true /* granted */,
false /* managed */));
return arc::mojom::ArcPackageInfo::New(
package_name, package_version, 1 /* last_backup_android_id */,
1 /* last_backup_time */, true /* sync */, false /* system */,
false /* vpn_provider */, nullptr /* web_app_info */, permissions);
false /* vpn_provider */, nullptr /* web_app_info */,
std::move(permissions));
}
void AddPackage(const arc::mojom::ArcPackageInfoPtr& package) {
......
......@@ -17,6 +17,7 @@ struct App {
string? description;
apps.mojom.OptionalBool is_pinned;
apps.mojom.OptionalBool is_policy_pinned;
string? version;
string? size;
map<uint32, apps.mojom.Permission> permissions;
......
......@@ -197,6 +197,9 @@ app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
app->is_pinned = shelf_delegate_.IsPinned(update.AppId())
? OptionalBool::kTrue
: OptionalBool::kFalse;
app->is_policy_pinned = shelf_delegate_.IsPolicyPinned(update.AppId())
? OptionalBool::kTrue
: OptionalBool::kFalse;
#endif
return app;
......
......@@ -8,6 +8,7 @@
#include "ash/public/cpp/shelf_model.h"
#include "ash/public/cpp/shelf_types.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
#include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
#include "chrome/services/app_service/public/mojom/types.mojom.h"
......@@ -27,6 +28,14 @@ bool AppManagementShelfDelegate::IsPinned(const std::string& app_id) {
return ChromeLauncherController::instance()->IsAppPinned(app_id);
}
bool AppManagementShelfDelegate::IsPolicyPinned(const std::string& app_id) {
auto* shelf_item =
ChromeLauncherController::instance()->GetItem(ash::ShelfID(app_id));
// If the app does not exist on the launcher, it has not been pinned by
// policy.
return shelf_item == nullptr ? false : shelf_item->pinned_by_policy;
}
void AppManagementShelfDelegate::SetPinned(const std::string& app_id,
OptionalBool pinned) {
if (pinned == OptionalBool::kTrue) {
......
......@@ -23,6 +23,8 @@ class AppManagementShelfDelegate : public ash::ShelfModelObserver {
bool IsPinned(const std::string& app_id);
void SetPinned(const std::string& app_id, apps::mojom::OptionalBool pinned);
bool IsPolicyPinned(const std::string& app_id);
private:
// ash::ShelfModelObserver:
void ShelfItemAdded(int index) override;
......
......@@ -57,6 +57,10 @@ content::WebUIDataSource* CreateAppManagementUIHTMLSource(Profile* profile) {
source->AddLocalizedString("title", IDS_APP_MANAGEMENT_TITLE);
source->AddLocalizedString("uninstall", IDS_APP_MANAGEMENT_UNINSTALL);
source->AddLocalizedString("version", IDS_APP_MANAGEMENT_VERSION);
source->AddLocalizedString("controlledByPolicy",
IDS_CONTROLLED_SETTING_POLICY);
source->AddLocalizedString("pinControlledByPolicy",
IDS_APP_MANAGEMENT_PIN_ENFORCED_BY_POLICY);
source->AddResourcePath("app_management.mojom-lite.js",
IDR_APP_MANAGEMENT_MOJO_LITE_JS);
......
......@@ -57,6 +57,8 @@ struct Permission {
PermissionValueType value_type;
// The semantics of value depends on the value_type.
uint32 value;
// If the permission is managed by an enterprise policy.
bool is_managed;
};
// The types of apps available in the registry.
......
......@@ -139,3 +139,17 @@ AppManagementArcPermissionViewTest.prototype = {
TEST_F('AppManagementArcPermissionViewTest', 'All', function() {
mocha.run();
});
function AppManagementManagedAppsTest() {}
AppManagementManagedAppsTest.prototype = {
__proto__: AppManagementBrowserTest.prototype,
extraLibraries: AppManagementBrowserTest.prototype.extraLibraries.concat([
'managed_apps_test.js',
]),
};
TEST_F('AppManagementManagedAppsTest', 'All', function() {
mocha.run();
});
......@@ -8,11 +8,6 @@ suite('<app-management-arc-permission-view>', () => {
let arcPermissionView;
let fakeHandler;
function getPermissionItemByPermissionType(permissionType) {
return arcPermissionView.root.querySelector(
'[permission-type=' + permissionType + ']');
}
function expandPermissions() {
arcPermissionView.root.querySelector('#subpermission-expand-row').click();
}
......@@ -22,20 +17,13 @@ suite('<app-management-arc-permission-view>', () => {
arcPermissionView.app_, permissionType);
}
function getPermissionToggleByType(permissionType) {
return arcPermissionView.root
.querySelector('[permission-type=' + permissionType + ']')
.root.querySelector('app-management-permission-toggle')
.root.querySelector('cr-toggle');
}
async function clickPermissionToggle(permissionType) {
getPermissionToggleByType(permissionType).click();
getPermissionCrToggleByType(arcPermissionView, permissionType).click();
await fakeHandler.$.flushForTesting();
}
async function clickPermissionItem(permissionType) {
getPermissionItemByPermissionType(permissionType).click();
getPermissionItemByType(arcPermissionView, permissionType).click();
await fakeHandler.$.flushForTesting();
}
......@@ -71,25 +59,30 @@ suite('<app-management-arc-permission-view>', () => {
test('Permissions are hidden correctly', () => {
expandPermissions();
assertTrue(isHidden(getPermissionItemByPermissionType('MICROPHONE')));
assertFalse(isHidden(getPermissionItemByPermissionType('LOCATION')));
assertFalse(isHidden(getPermissionItemByPermissionType('CAMERA')));
assertTrue(
isHidden(getPermissionItemByType(arcPermissionView, 'MICROPHONE')));
assertFalse(
isHidden(getPermissionItemByType(arcPermissionView, 'LOCATION')));
assertFalse(isHidden(getPermissionItemByType(arcPermissionView, 'CAMERA')));
});
test('Toggle works correctly', async () => {
const checkPermissionToggle = async (permissionType) => {
assertTrue(getPermissionBoolByType(permissionType));
assertTrue(getPermissionToggleByType(permissionType).checked);
assertTrue(getPermissionCrToggleByType(arcPermissionView, permissionType)
.checked);
// Toggle Off.
await clickPermissionToggle(permissionType);
assertFalse(getPermissionBoolByType(permissionType));
assertFalse(getPermissionToggleByType(permissionType).checked);
assertFalse(getPermissionCrToggleByType(arcPermissionView, permissionType)
.checked);
// Toggle On.
await clickPermissionToggle(permissionType);
assertTrue(getPermissionBoolByType(permissionType));
assertTrue(getPermissionToggleByType(permissionType).checked);
assertTrue(getPermissionCrToggleByType(arcPermissionView, permissionType)
.checked);
};
expandPermissions();
......@@ -102,17 +95,20 @@ suite('<app-management-arc-permission-view>', () => {
test('OnClick handler for permission item works correctly', async () => {
const checkPermissionItemOnClick = async (permissionType) => {
assertTrue(getPermissionBoolByType(permissionType));
assertTrue(getPermissionToggleByType(permissionType).checked);
assertTrue(getPermissionCrToggleByType(arcPermissionView, permissionType)
.checked);
// Toggle Off.
await clickPermissionItem(permissionType);
assertFalse(getPermissionBoolByType(permissionType));
assertFalse(getPermissionToggleByType(permissionType).checked);
assertFalse(getPermissionCrToggleByType(arcPermissionView, permissionType)
.checked);
// Toggle On.
await clickPermissionItem(permissionType);
assertTrue(getPermissionBoolByType(permissionType));
assertTrue(getPermissionToggleByType(permissionType).checked);
assertTrue(getPermissionCrToggleByType(arcPermissionView, permissionType)
.checked);
};
expandPermissions();
......
// Copyright 2019 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';
suite('<app-management-managed-apps>', () => {
let appDetailView;
let fakeHandler;
setup(async () => {
fakeHandler = setupFakeHandler();
replaceStore();
// Create a Web app which is installed and pinned by policy
// and has location set to on and camera set to off by policy.
const permissionOptions = {};
permissionOptions[PwaPermissionType.CONTENT_SETTINGS_TYPE_GEOLOCATION] = {
permissionValue: TriState.kAllow,
isManaged: true,
};
permissionOptions[PwaPermissionType
.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA] = {
permissionValue: TriState.kBlock,
isManaged: true
};
const policyAppOptions = {
type: apps.mojom.AppType.kWeb,
isPinned: apps.mojom.OptionalBool.kTrue,
isPolicyPinned: apps.mojom.OptionalBool.kTrue,
installSource: apps.mojom.InstallSource.kPolicy,
permissions: app_management.FakePageHandler.createWebPermissions(
permissionOptions),
};
const app = await fakeHandler.addApp(null, policyAppOptions);
// Select created app.
app_management.Store.getInstance().dispatch(
app_management.actions.changePage(PageType.DETAIL, app.id));
appDetailView =
document.createElement('app-management-pwa-permission-view');
replaceBody(appDetailView);
await PolymerTest.flushTasks();
});
test('Uninstall button affected by policy', () => {
const uninstallWrapper =
appDetailView.$$('app-management-permission-view-header')
.$$('#uninstall-wrapper');
expectTrue(!!uninstallWrapper.querySelector('#policy-indicator'));
});
test('Permission toggles affected by policy', () => {
function checkToggle(permissionType, policyAffected) {
const permissionToggle =
getPermissionToggleByType(appDetailView, permissionType);
expectTrue(permissionToggle.$$('cr-toggle').disabled === policyAffected);
expectTrue(!!permissionToggle.$$('#policy-indicator') === policyAffected);
}
checkToggle('CONTENT_SETTINGS_TYPE_NOTIFICATIONS', false);
checkToggle('CONTENT_SETTINGS_TYPE_GEOLOCATION', true);
checkToggle('CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA', true);
checkToggle('CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC', false);
});
test('Pin to shelf toggle effected by policy', () => {
const shelfSwitch =
appDetailView.$$('app-management-metadata-view').$$('#shelf-switch');
expectTrue(!!shelfSwitch.querySelector('#policy-indicator'));
expectTrue(shelfSwitch.querySelector('#pin-to-shelf-toggle').disabled);
});
});
......@@ -79,3 +79,32 @@ async function navigateTo(route) {
window.dispatchEvent(new CustomEvent('location-changed'));
await PolymerTest.flushTasks();
}
/**
* @param {Element} element
* @param {Object} permissionType
* @return {Element}
*/
function getPermissionItemByType(view, permissionType) {
return view.root.querySelector('[permission-type=' + permissionType + ']');
}
/**
* @param {Element} element
* @param {Object} permissionType
* @return {Element}
*/
function getPermissionToggleByType(view, permissionType) {
return getPermissionItemByType(view, permissionType)
.root.querySelector('app-management-permission-toggle');
}
/**
* @param {Element} element
* @param {Object} permissionType
* @return {Element}
*/
function getPermissionCrToggleByType(view, permissionType) {
return getPermissionToggleByType(view, permissionType)
.root.querySelector('cr-toggle');
}
......@@ -56,7 +56,7 @@ struct ArcPackageInfo {
// If non-null, signifies this package represents a web app that should be
// installed on the browser side.
[MinVersion=38] WebAppInfo? web_app_info;
[MinVersion=41] map<AppPermission, bool>? permissions;
[MinVersion=42] map<AppPermission, PermissionState>? permissions;
};
// Describes ARC app shortcut.
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Next MinVersion: 1
// Next MinVersion: 2
module arc.mojom;
......@@ -16,6 +16,11 @@ enum AppPermission {
NOTIFICATIONS = 3, // Not a manifest permission
};
struct PermissionState {
bool granted; // If the permission has been granted
bool managed; // If the permission is managed by an enterprise policy
};
// An interface for Chrome to manipulate app permissions in ARC.
// Next method ID: 2
interface AppPermissionsInstance {
......
......@@ -135,7 +135,7 @@ class HostContentSettingsMap : public content_settings::Observer,
// the |SETTING_SOURCE_WHITELIST| and the |primary_pattern| and
// |secondary_pattern| are set to a wildcard pattern. If there is no content
// setting, NULL is returned and the |source| field of |info| is set to
// |SETTING_SOURCE_NONE|. The pattern fiels of |info| are set to empty
// |SETTING_SOURCE_NONE|. The pattern fields of |info| are set to empty
// patterns.
// May be called on any thread.
std::unique_ptr<base::Value> GetWebsiteSetting(
......
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