Improve messaging for shared modules on chrome://extensions

List the dependent extensions under the shared module and change message
from generic policy message to be more specific for shared modules
to explain why the extension cannot be disabled or uninstalled.

BUG=393670

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284010 0039d316-1c4b-4281-b951-d872f2087c98
parent 047c3d69
......@@ -4901,6 +4901,9 @@ Make sure you do not expose any sensitive information.
<message name="IDS_EXTENSIONS_POLICY_CONTROLLED" desc="The text in the extensions UI informing the user that an extension is policy controlled">
(This extension is managed and cannot be removed or disabled.)
</message>
<message name="IDS_EXTENSIONS_DEPENDENT_EXTENSIONS" desc="The text in the extensions UI informing the user that an extension is depended on by on other installed extensions">
The following extensions depend on this extension:
</message>
<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>
......@@ -10214,6 +10217,9 @@ Chrome ran out of memory.
<message name="IDS_OPTIONS_INSTALL_LOCATION_ENTERPRISE" desc="The text explaining the the installation of the extension came from an enterprise policy.">
Installed by enterprise policy.
</message>
<message name="IDS_OPTIONS_INSTALL_LOCATION_SHARED_MODULE" desc="The text explaining the the installation of the extension was because of extensions that depend on this shared module">
Installed because of dependent extension(s).
</message>
<!-- Extension blacklist state -->
<message name="IDS_OPTIONS_BLACKLISTED_SECURITY_VULNERABILITY" desc="The text explaining the reason for disabling extension or app. The extension in question has a security vulnerability.">
......
......@@ -112,34 +112,6 @@ namespace {
// Wait this many seconds after an extensions becomes idle before updating it.
const int kUpdateIdleDelay = 5;
bool IsCWSSharedModule(const Extension* extension) {
return extension->from_webstore() &&
SharedModuleInfo::IsSharedModule(extension);
}
class SharedModuleProvider : public extensions::ManagementPolicy::Provider {
public:
SharedModuleProvider() {}
virtual ~SharedModuleProvider() {}
virtual std::string GetDebugPolicyProviderName() const OVERRIDE {
return "SharedModuleProvider";
}
virtual bool UserMayModifySettings(const Extension* extension,
base::string16* error) const OVERRIDE {
return !IsCWSSharedModule(extension);
}
virtual bool MustRemainEnabled(const Extension* extension,
base::string16* error) const OVERRIDE {
return IsCWSSharedModule(extension);
}
private:
DISALLOW_COPY_AND_ASSIGN(SharedModuleProvider);
};
} // namespace
// ExtensionService.
......@@ -355,8 +327,6 @@ ExtensionService::ExtensionService(Profile* profile,
new extensions::ExtensionActionStorageManager(profile_));
#endif
shared_module_policy_provider_.reset(new SharedModuleProvider);
// How long is the path to the Extensions directory?
UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ExtensionRootPathLength",
install_directory_.value().length(), 0, 500, 100);
......@@ -472,9 +442,6 @@ void ExtensionService::Init() {
// rather than running immediately at startup.
CheckForExternalUpdates();
system_->management_policy()->RegisterProvider(
shared_module_policy_provider_.get());
LoadGreylistFromPrefs();
}
......
......@@ -85,7 +85,8 @@ cr.define('options', function() {
if (!extension.enabled || extension.terminated)
node.classList.add('inactive-extension');
if (extension.managedInstall) {
if (extension.managedInstall ||
extension.dependentExtensions.length > 0) {
node.classList.add('may-not-modify');
node.classList.add('may-not-remove');
} else if (extension.suspiciousInstall || extension.corruptInstall) {
......@@ -236,12 +237,13 @@ cr.define('options', function() {
// The 'Enabled' checkbox.
var enable = node.querySelector('.enable-checkbox');
enable.hidden = false;
var managedOrHosedExtension = extension.managedInstall ||
extension.suspiciousInstall ||
extension.corruptInstall;
enable.querySelector('input').disabled = managedOrHosedExtension;
var enableCheckboxDisabled = extension.managedInstall ||
extension.suspiciousInstall ||
extension.corruptInstall ||
extension.dependentExtensions.length > 0;
enable.querySelector('input').disabled = enableCheckboxDisabled;
if (!managedOrHosedExtension) {
if (!enableCheckboxDisabled) {
enable.addEventListener('click', function(e) {
// When e.target is the label instead of the checkbox, it doesn't
// have the checked property and the state of the checkbox is
......@@ -312,6 +314,18 @@ cr.define('options', function() {
}
}
if (extension.dependentExtensions.length > 0) {
var dependentMessage =
node.querySelector('.dependent-extensions-message');
dependentMessage.hidden = false;
var dependentList = dependentMessage.querySelector('ul');
extension.dependentExtensions.forEach(function(id) {
var li = document.createElement('li');
li.innerText = id;
dependentList.appendChild(li);
});
}
// Then active views.
if (extension.views.length > 0) {
var activeViews = node.querySelector('.active-views');
......
......@@ -161,6 +161,10 @@
</a>
</div>
</div>
<div class="dependent-extensions-message" hidden>
<span i18n-content="extensionSettingsDependentExtensions"></span>
<ul></ul>
</div>
<div class="developer-extras">
<div>
<span i18n-content="extensionSettingsExtensionId"></span>
......
......@@ -41,6 +41,7 @@
#include "chrome/browser/extensions/extension_warning_set.h"
#include "chrome/browser/extensions/install_verifier.h"
#include "chrome/browser/extensions/path_util.h"
#include "chrome/browser/extensions/shared_module_service.h"
#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
......@@ -264,6 +265,20 @@ base::DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue(
extension_data->SetBoolean("homepageProvided",
ManifestURL::GetHomepageURL(extension).is_valid());
// Add dependent extensions.
base::ListValue* dependents_list = new base::ListValue;
if (extension->is_shared_module()) {
scoped_ptr<ExtensionSet> dependent_extensions =
extension_service_->shared_module_service()->GetDependentExtensions(
extension);
for (ExtensionSet::const_iterator i = dependent_extensions->begin();
i != dependent_extensions->end();
i++) {
dependents_list->Append(new base::StringValue((*i)->id()));
}
}
extension_data->Set("dependentExtensions", dependents_list);
// Extensions only want all URL access if:
// - The feature is enabled.
// - The extension has access to enough urls that we can't just let it run
......@@ -288,6 +303,9 @@ base::DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue(
} else if (extension->location() == Manifest::EXTERNAL_REGISTRY) {
location_text = l10n_util::GetStringUTF16(
IDS_OPTIONS_INSTALL_LOCATION_3RD_PARTY);
} else if (extension->is_shared_module()) {
location_text = l10n_util::GetStringUTF16(
IDS_OPTIONS_INSTALL_LOCATION_SHARED_MODULE);
}
extension_data->SetString("locationText", location_text);
......@@ -491,6 +509,8 @@ void ExtensionSettingsHandler::GetLocalizedValues(
l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSTORE));
source->AddString("extensionSettingsPolicyControlled",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED));
source->AddString("extensionSettingsDependentExtensions",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEPENDENT_EXTENSIONS));
source->AddString("extensionSettingsManagedMode",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOCKED_SUPERVISED_USER));
source->AddString("extensionSettingsCorruptInstall",
......
......@@ -411,11 +411,11 @@ void Extension::AddInstallWarnings(
}
bool Extension::is_app() const {
return manifest_->is_app();
return manifest()->is_app();
}
bool Extension::is_platform_app() const {
return manifest_->is_platform_app();
return manifest()->is_platform_app();
}
bool Extension::is_hosted_app() const {
......@@ -430,6 +430,14 @@ bool Extension::is_extension() const {
return manifest()->is_extension();
}
bool Extension::is_shared_module() const {
return manifest()->is_shared_module();
}
bool Extension::is_theme() const {
return manifest()->is_theme();
}
bool Extension::can_be_incognito_enabled() const {
// Only component platform apps are supported in incognito.
return !is_platform_app() || location() == Manifest::COMPONENT;
......@@ -443,10 +451,6 @@ void Extension::AddWebExtentPattern(const URLPattern& pattern) {
extent_.AddPattern(pattern);
}
bool Extension::is_theme() const {
return manifest()->is_theme();
}
// static
bool Extension::InitExtensionID(extensions::Manifest* manifest,
const base::FilePath& path,
......
......@@ -342,20 +342,20 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
return (creation_flags_ & WAS_INSTALLED_BY_CUSTODIAN) != 0;
}
// App-related.
// Type-related queries.
bool is_app() const;
bool is_platform_app() const;
bool is_hosted_app() const;
bool is_legacy_packaged_app() const;
bool is_extension() const;
bool is_shared_module() const;
bool is_theme() const;
bool can_be_incognito_enabled() const;
void AddWebExtentPattern(const URLPattern& pattern);
const URLPatternSet& web_extent() const { return extent_; }
// Theme-related.
bool is_theme() const;
private:
friend class base::RefCountedThreadSafe<Extension>;
......
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