Commit 5c654f86 authored by treib's avatar treib Committed by Commit bot

Update chrome://extensions for supervised users

This includes:
- new types of controlled-setting-indicator for children/supervised users
- corresponding new "controlled-by" strings
- passing the strings through the developerPrivate API, and removing the now-unnecessary installedByCustodian field

BUG=397951

Review URL: https://codereview.chromium.org/1051273002

Cr-Commit-Position: refs/heads/master@{#330928}
parent 72c35134
......@@ -5074,6 +5074,12 @@ Even if you have downloaded files from this website before, the website might ha
<message name="IDS_EXTENSIONS_LOCKED_SUPERVISED_USER" desc="The error message (either shown in the extensions UI or logged) informing a supervised user that extensions cannot be changed.">
Applications and extensions cannot be modified by supervised users.
</message>
<message name="IDS_EXTENSIONS_INSTALLED_BY_CHILD_CUSTODIAN" desc="The text in the extensions UI informing a child user that an extension was installed by their parent and cannot be changed.">
Installed by your parent.
</message>
<message name="IDS_EXTENSIONS_INSTALLED_BY_SUPERVISED_USER_CUSTODIAN" desc="The text in the extensions UI informing a supervised user that an extension was installed by their custodian and cannot be changed">
Installed by your custodian.
</message>
<message name="IDS_GET_MORE_EXTENSIONS" desc="The text for getting more extensions. Displayed at bottom of extension management page when there is at least one extension installed.">
Get more extensions
</message>
......@@ -179,12 +179,14 @@
<structure type="chrome_scaled_image" name="IDR_CONTENT_TOP_LEFT_CORNER_MASK" file="common/content_top_left_corner_mask.png" />
<structure type="chrome_scaled_image" name="IDR_CONTENT_TOP_RIGHT_CORNER" file="common/content_top_right_corner.png" />
<structure type="chrome_scaled_image" name="IDR_CONTENT_TOP_RIGHT_CORNER_MASK" file="common/content_top_right_corner_mask.png" />
<structure type="chrome_scaled_image" name="IDR_CONTROLLED_SETTING_CHILD" file="common/controlled_setting_child.png" />
<structure type="chrome_scaled_image" name="IDR_CONTROLLED_SETTING_EXTENSION" file="common/controlled_setting_extension.png" />
<structure type="chrome_scaled_image" name="IDR_CONTROLLED_SETTING_MANDATORY" file="common/controlled_setting_mandatory.png" />
<structure type="chrome_scaled_image" name="IDR_CONTROLLED_SETTING_OWNER" file="common/controlled_setting_owner.png" />
<if expr="chromeos">
<structure type="chrome_scaled_image" name="IDR_CONTROLLED_SETTING_SHARED" file="cros/controlled_setting_shared.png" />
</if>
<structure type="chrome_scaled_image" name="IDR_CONTROLLED_SETTING_SUPERVISED" file="common/controlled_setting_supervised.png" />
<structure type="chrome_scaled_image" name="IDR_COOKIE_ICON" file="common/cookie.png" />
<structure type="chrome_scaled_image" name="IDR_COOKIE_STORAGE_ICON" file="common/cookie_storage.png" />
<if expr="chromeos">
......
......@@ -257,6 +257,27 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper(
new std::string(l10n_util::GetStringUTF8(blacklist_text)));
}
Profile* profile = Profile::FromBrowserContext(browser_context_);
bool is_policy_location = Manifest::IsPolicyLocation(extension.location());
if (is_policy_location || util::IsExtensionSupervised(&extension, profile)) {
info->controlled_info.reset(new developer::ControlledInfo());
if (is_policy_location) {
info->controlled_info->type = developer::CONTROLLER_TYPE_POLICY;
info->controlled_info->text =
l10n_util::GetStringUTF8(IDS_OPTIONS_INSTALL_LOCATION_ENTERPRISE);
} else if (profile->IsChild()) {
info->controlled_info->type = developer::CONTROLLER_TYPE_CHILD_CUSTODIAN;
info->controlled_info->text = l10n_util::GetStringUTF8(
IDS_EXTENSIONS_INSTALLED_BY_CHILD_CUSTODIAN);
} else {
info->controlled_info->type =
developer::CONTROLLER_TYPE_SUPERVISED_USER_CUSTODIAN;
info->controlled_info->text = l10n_util::GetStringUTF8(
IDS_EXTENSIONS_INSTALLED_BY_SUPERVISED_USER_CUSTODIAN);
}
}
// Dependent extensions.
if (extension.is_shared_module()) {
scoped_ptr<ExtensionSet> dependent_extensions =
......@@ -302,10 +323,6 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper(
info->incognito_access.is_active =
util::IsIncognitoEnabled(extension.id(), browser_context_);
Profile* profile = Profile::FromBrowserContext(browser_context_);
info->installed_by_custodian =
util::IsExtensionSupervised(&extension, profile);
// Install warnings (only if unpacked and no error console).
if (!error_console_enabled &&
Manifest::IsUnpackedLocation(extension.location())) {
......@@ -391,11 +408,6 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper(
extensions::path_util::PrettifyPath(extension.path()).AsUTF8Unsafe()));
}
if (Manifest::IsPolicyLocation(extension.location())) {
info->policy_text.reset(new std::string(
l10n_util::GetStringUTF8(IDS_OPTIONS_INSTALL_LOCATION_ENTERPRISE)));
}
// Runs on all urls.
info->run_on_all_urls.is_enabled =
(FeatureSwitch::scripts_require_action()->IsEnabled() &&
......
......@@ -639,7 +639,6 @@ cr.define('extensions', function() {
var trashTemplate = $('template-collection').querySelector('.trash');
var trash = trashTemplate.cloneNode(true);
trash.title = loadTimeData.getString('extensionUninstall');
trash.hidden = !extension.userMayModify;
trash.setAttribute('column-type', 'trash');
trash.addEventListener('click', function(e) {
trash.classList.add('open');
......@@ -702,19 +701,14 @@ cr.define('extensions', function() {
// Hack to keep the closure compiler happy about |remove|.
// TODO(hcarmona): Remove this hack when the closure compiler is updated.
var node = /** @type {Element} */ (row);
node.classList.remove('policy-controlled', 'may-not-modify',
'may-not-remove');
node.classList.remove('controlled', 'may-not-remove');
var classes = [];
if (!extension.userMayModify) {
classes.push('policy-controlled', 'may-not-modify');
} else if (extension.dependentExtensions.length > 0) {
classes.push('may-not-remove', 'may-not-modify');
} else if (extension.mustRemainInstalled) {
if (extension.controlledInfo) {
classes.push('controlled');
} else if (!extension.userMayModify ||
extension.mustRemainInstalled ||
extension.dependentExtensions.length > 0) {
classes.push('may-not-remove');
} else if (extension.disableReasons.suspiciousInstall ||
extension.disableReasons.corruptInstall ||
extension.disableReasons.updateRequired) {
classes.push('may-not-modify');
}
row.classList.add.apply(row.classList, classes);
......@@ -850,32 +844,37 @@ cr.define('extensions', function() {
extension.disableReasons.suspiciousInstall ||
extension.disableReasons.corruptInstall ||
extension.disableReasons.updateRequired ||
extension.installedByCustodian ||
extension.dependentExtensions.length > 0;
item.querySelector('input').disabled = enableCheckboxDisabled;
item.querySelector('input').checked = isActive;
});
// Button for extensions controlled by policy.
// Indicator for extensions controlled by policy.
var controlNode = row.querySelector('.enable-controls');
var indicator =
controlNode.querySelector('.controlled-extension-indicator');
var needsIndicator = isOK &&
!extension.userMayModify &&
extension.policyText;
// TODO(treib): If userMayModify is false, but policyText is empty, that
// indicates this extension is controlled by something else than
// enterprise policy (such as the profile being supervised). For now, just
// don't show the indicator in this case. We should really handle this
// better though (ie use a different text and icon).
var needsIndicator = isOK && extension.controlledInfo;
if (needsIndicator && !indicator) {
indicator = new cr.ui.ControlledIndicator();
indicator.classList.add('controlled-extension-indicator');
indicator.setAttribute('controlled-by', 'policy');
var textPolicy = extension.policyText || '';
indicator.setAttribute('textpolicy', textPolicy);
indicator.image.setAttribute('aria-label', textPolicy);
var ControllerType = chrome.developerPrivate.ControllerType;
var controlledByStr = '';
switch (extension.controlledInfo.type) {
case ControllerType.POLICY:
controlledByStr = 'policy';
break;
case ControllerType.CHILD_CUSTODIAN:
controlledByStr = 'child-custodian';
break;
case ControllerType.SUPERVISED_USER_CUSTODIAN:
controlledByStr = 'supervised-user-custodian';
break;
}
indicator.setAttribute('controlled-by', controlledByStr);
var text = extension.controlledInfo.text;
indicator.setAttribute('text' + controlledByStr, text);
indicator.image.setAttribute('aria-label', text);
controlNode.appendChild(indicator);
indicator.querySelector('div').setAttribute('column-type',
'enterprise');
......
......@@ -401,7 +401,7 @@ html[dir='rtl'] #extension-settings .trash {
* remove it from the layout and make space for the controlled indicator.
* TODO(cschuet): rather than hide always replace it with something meaningful.
*/
.extension-list-item-wrapper.policy-controlled .trash {
.extension-list-item-wrapper.controlled .trash {
display: none;
}
......
......@@ -150,9 +150,21 @@ namespace developerPrivate {
ViewType type;
};
enum ControllerType {
POLICY,
CHILD_CUSTODIAN,
SUPERVISED_USER_CUSTODIAN
};
dictionary ControlledInfo {
ControllerType type;
DOMString text;
};
dictionary ExtensionInfo {
boolean actionButtonHidden;
DOMString? blacklistText;
ControlledInfo? controlledInfo;
DOMString[] dependentExtensions;
DOMString description;
DisableReasons disableReasons;
......@@ -162,7 +174,6 @@ namespace developerPrivate {
DOMString iconUrl;
DOMString id;
AccessModifier incognitoAccess;
boolean installedByCustodian;
DOMString[] installWarnings;
DOMString? launchUrl;
Location location;
......@@ -173,7 +184,6 @@ namespace developerPrivate {
boolean offlineEnabled;
OptionsPage? optionsPage;
DOMString? path;
DOMString? policyText;
DOMString? prettifiedPath;
AccessModifier runOnAllUrls;
RuntimeError[] runtimeErrors;
......
......@@ -25,7 +25,6 @@
"isEnabled": true
},
"installWarnings": [ ],
"installedByCustodian": false,
"location": "UNKNOWN",
"locationText": "Not from Chrome Web Store.",
"manifestErrors": [ ],
......
......@@ -25,7 +25,6 @@
"isEnabled": true
},
"installWarnings": [ ],
"installedByCustodian": false,
"location": "UNKNOWN",
"locationText": "Not from Chrome Web Store.",
"manifestErrors": [ ],
......
......@@ -25,7 +25,6 @@
"isEnabled": true
},
"installWarnings": [ ],
"installedByCustodian": false,
"location": "UNKNOWN",
"locationText": "Not from Chrome Web Store.",
"manifestErrors": [ ],
......
......@@ -216,10 +216,28 @@ var HomePage;
*/
var ExtensionView;
/**
* @enum {string}
*/
chrome.developerPrivate.ControllerType = {
POLICY: 'POLICY',
CHILD_CUSTODIAN: 'CHILD_CUSTODIAN',
SUPERVISED_USER_CUSTODIAN: 'SUPERVISED_USER_CUSTODIAN',
};
/**
* @typedef {{
* type: !chrome.developerPrivate.ControllerType,
* text: string
* }}
*/
var ControlledInfo;
/**
* @typedef {{
* actionButtonHidden: boolean,
* blacklistText: (string|undefined),
* controlledInfo: (ControlledInfo|undefined),
* dependentExtensions: !Array<string>,
* description: string,
* disableReasons: DisableReasons,
......@@ -229,7 +247,6 @@ var ExtensionView;
* iconUrl: string,
* id: string,
* incognitoAccess: AccessModifier,
* installedByCustodian: boolean,
* installWarnings: !Array<string>,
* launchUrl: (string|undefined),
* location: !chrome.developerPrivate.Location,
......@@ -240,7 +257,6 @@ var ExtensionView;
* offlineEnabled: boolean,
* optionsPage: (OptionsPage|undefined),
* path: (string|undefined),
* policyText: (string|undefined),
* prettifiedPath: (string|undefined),
* runOnAllUrls: AccessModifier,
* runtimeErrors: !Array<RuntimeError>,
......
......@@ -32,6 +32,14 @@
background-image: url(chrome://theme/IDR_CONTROLLED_SETTING_SHARED);
}
.controlled-setting-indicator[controlled-by='child-custodian'] > div {
background-image: url(chrome://theme/IDR_CONTROLLED_SETTING_CHILD);
}
.controlled-setting-indicator[controlled-by='supervised-user-custodian'] > div {
background-image: url(chrome://theme/IDR_CONTROLLED_SETTING_SUPERVISED);
}
.controlled-setting-indicator:-webkit-any([controlled-by='recommended'],
[controlled-by='hasRecommendation']) > div {
background-image: url(chrome://theme/IDR_CONTROLLED_SETTING_MANDATORY);
......
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