blob: 4a861653b6ebb4e3906532324f16a8e501d3f259 [file] [log] [blame]
// Copyright 2013 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.
#include "chrome/browser/extensions/extension_util.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_sync_service.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/extensions/shared_module_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/sync_helper.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/site_instance.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_icon_set.h"
#include "extensions/common/feature_switch.h"
#include "extensions/common/features/behavior_feature.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/features/feature_provider.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_handlers/app_isolation_info.h"
#include "extensions/common/manifest_handlers/incognito_info.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/grit/extensions_browser_resources.h"
#include "ui/base/resource/resource_bundle.h"
namespace extensions {
namespace util {
namespace {
const char kSupervisedUserExtensionPermissionIncreaseFieldTrialName[] =
"SupervisedUserExtensionPermissionIncrease";
// The entry into the ExtensionPrefs for allowing an extension to script on
// all urls without explicit permission.
const char kExtensionAllowedOnAllUrlsPrefName[] =
"extension_can_script_all_urls";
// The entry into the prefs for when a user has explicitly set the "extension
// allowed on all urls" pref.
const char kHasSetScriptOnAllUrlsPrefName[] = "has_set_script_all_urls";
// Returns true if |extension| should always be enabled in incognito mode.
bool IsWhitelistedForIncognito(const Extension* extension) {
const Feature* feature = FeatureProvider::GetBehaviorFeature(
BehaviorFeature::kWhitelistedForIncognito);
return feature && feature->IsAvailableToExtension(extension).is_available();
}
// Returns |extension_id|. See note below.
std::string ReloadExtensionIfEnabled(const std::string& extension_id,
content::BrowserContext* context) {
ExtensionRegistry* registry = ExtensionRegistry::Get(context);
bool extension_is_enabled =
registry->enabled_extensions().Contains(extension_id);
if (!extension_is_enabled)
return extension_id;
// When we reload the extension the ID may be invalidated if we've passed it
// by const ref everywhere. Make a copy to be safe. http://crbug.com/103762
std::string id = extension_id;
ExtensionService* service =
ExtensionSystem::Get(context)->extension_service();
CHECK(service);
service->ReloadExtension(id);
return id;
}
// Sets the preference for scripting on all urls to |allowed|, optionally
// updating the extension's active permissions (based on |update_permissions|).
void SetAllowedScriptingOnAllUrlsHelper(
content::BrowserContext* context,
const std::string& extension_id,
bool allowed,
bool update_permissions) {
// TODO(devlin): Right now, we always need to have a value for this pref.
// Once the scripts-require-action feature launches, we can change the set
// to be null if false.
ExtensionPrefs::Get(context)->UpdateExtensionPref(
extension_id,
kExtensionAllowedOnAllUrlsPrefName,
new base::FundamentalValue(allowed));
if (update_permissions) {
const Extension* extension =
ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
extension_id);
if (extension) {
ScriptingPermissionsModifier modifier(context, extension);
if (allowed)
modifier.GrantWithheldImpliedAllHosts();
else
modifier.WithholdImpliedAllHosts();
// If this was an update to permissions, we also need to sync the change.
ExtensionSyncService* sync_service = ExtensionSyncService::Get(context);
if (sync_service) // sync_service can be null in unittests.
sync_service->SyncExtensionChangeIfNeeded(*extension);
}
}
}
} // namespace
bool IsIncognitoEnabled(const std::string& extension_id,
content::BrowserContext* context) {
const Extension* extension = ExtensionRegistry::Get(context)->
GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
if (extension) {
if (!util::CanBeIncognitoEnabled(extension))
return false;
// If this is an existing component extension we always allow it to
// work in incognito mode.
if (extension->location() == Manifest::COMPONENT)
return true;
if (IsWhitelistedForIncognito(extension))
return true;
}
return ExtensionPrefs::Get(context)->IsIncognitoEnabled(extension_id);
}
void SetIsIncognitoEnabled(const std::string& extension_id,
content::BrowserContext* context,
bool enabled) {
ExtensionRegistry* registry = ExtensionRegistry::Get(context);
const Extension* extension =
registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
if (extension) {
if (!util::CanBeIncognitoEnabled(extension))
return;
// TODO(treib,kalman): Should this be Manifest::IsComponentLocation(..)?
// (which also checks for EXTERNAL_COMPONENT).
if (extension->location() == Manifest::COMPONENT) {
// This shouldn't be called for component extensions unless it is called
// by sync, for syncable component extensions.
// See http://crbug.com/112290 and associated CLs for the sordid history.
DCHECK(sync_helper::IsSyncableComponentExtension(extension));
// If we are here, make sure the we aren't trying to change the value.
DCHECK_EQ(enabled, IsIncognitoEnabled(extension_id, context));
return;
}
}
ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(context);
// Broadcast unloaded and loaded events to update browser state. Only bother
// if the value changed and the extension is actually enabled, since there is
// no UI otherwise.
bool old_enabled = extension_prefs->IsIncognitoEnabled(extension_id);
if (enabled == old_enabled)
return;
extension_prefs->SetIsIncognitoEnabled(extension_id, enabled);
std::string id = ReloadExtensionIfEnabled(extension_id, context);
// Reloading the extension invalidates the |extension| pointer.
extension = registry->GetExtensionById(id, ExtensionRegistry::EVERYTHING);
if (extension) {
Profile* profile = Profile::FromBrowserContext(context);
ExtensionSyncService::Get(profile)->SyncExtensionChangeIfNeeded(*extension);
}
}
bool CanCrossIncognito(const Extension* extension,
content::BrowserContext* context) {
// We allow the extension to see events and data from another profile iff it
// uses "spanning" behavior and it has incognito access. "split" mode
// extensions only see events for a matching profile.
CHECK(extension);
return IsIncognitoEnabled(extension->id(), context) &&
!IncognitoInfo::IsSplitMode(extension);
}
bool CanLoadInIncognito(const Extension* extension,
content::BrowserContext* context) {
CHECK(extension);
if (extension->is_hosted_app())
return true;
// Packaged apps and regular extensions need to be enabled specifically for
// incognito (and split mode should be set).
return IncognitoInfo::IsSplitMode(extension) &&
IsIncognitoEnabled(extension->id(), context);
}
bool AllowFileAccess(const std::string& extension_id,
content::BrowserContext* context) {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableExtensionsFileAccessCheck) ||
ExtensionPrefs::Get(context)->AllowFileAccess(extension_id);
}
void SetAllowFileAccess(const std::string& extension_id,
content::BrowserContext* context,
bool allow) {
// Reload to update browser state. Only bother if the value changed and the
// extension is actually enabled, since there is no UI otherwise.
if (allow == AllowFileAccess(extension_id, context))
return;
ExtensionPrefs::Get(context)->SetAllowFileAccess(extension_id, allow);
ReloadExtensionIfEnabled(extension_id, context);
}
bool AllowedScriptingOnAllUrls(const std::string& extension_id,
content::BrowserContext* context) {
bool allowed = false;
ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
if (!prefs->ReadPrefAsBoolean(extension_id,
kExtensionAllowedOnAllUrlsPrefName,
&allowed)) {
// If there is no value present, we make one, defaulting it to the value of
// the 'scripts require action' flag. If the flag is on, then the extension
// does not have permission to script on all urls by default.
allowed = DefaultAllowedScriptingOnAllUrls();
SetAllowedScriptingOnAllUrlsHelper(context, extension_id, allowed, false);
}
return allowed;
}
void SetAllowedScriptingOnAllUrls(const std::string& extension_id,
content::BrowserContext* context,
bool allowed) {
if (allowed != AllowedScriptingOnAllUrls(extension_id, context)) {
ExtensionPrefs::Get(context)->UpdateExtensionPref(
extension_id,
kHasSetScriptOnAllUrlsPrefName,
new base::FundamentalValue(true));
SetAllowedScriptingOnAllUrlsHelper(context, extension_id, allowed, true);
}
}
bool HasSetAllowedScriptingOnAllUrls(const std::string& extension_id,
content::BrowserContext* context) {
bool did_set = false;
return ExtensionPrefs::Get(context)->ReadPrefAsBoolean(
extension_id,
kHasSetScriptOnAllUrlsPrefName,
&did_set) && did_set;
}
bool DefaultAllowedScriptingOnAllUrls() {
return !FeatureSwitch::scripts_require_action()->IsEnabled();
}
bool IsAppLaunchable(const std::string& extension_id,
content::BrowserContext* context) {
int reason = ExtensionPrefs::Get(context)->GetDisableReasons(extension_id);
return !((reason & Extension::DISABLE_UNSUPPORTED_REQUIREMENT) ||
(reason & Extension::DISABLE_CORRUPTED));
}
bool IsAppLaunchableWithoutEnabling(const std::string& extension_id,
content::BrowserContext* context) {
return ExtensionRegistry::Get(context)->GetExtensionById(
extension_id, ExtensionRegistry::ENABLED) != NULL;
}
bool ShouldSync(const Extension* extension,
content::BrowserContext* context) {
return sync_helper::IsSyncable(extension) &&
!ExtensionPrefs::Get(context)->DoNotSync(extension->id());
}
bool IsExtensionIdle(const std::string& extension_id,
content::BrowserContext* context) {
std::vector<std::string> ids_to_check;
ids_to_check.push_back(extension_id);
const Extension* extension =
ExtensionRegistry::Get(context)
->GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
if (extension && extension->is_shared_module()) {
// We have to check all the extensions that use this shared module for idle
// to tell whether it is really 'idle'.
SharedModuleService* service = ExtensionSystem::Get(context)
->extension_service()
->shared_module_service();
std::unique_ptr<ExtensionSet> dependents =
service->GetDependentExtensions(extension);
for (ExtensionSet::const_iterator i = dependents->begin();
i != dependents->end();
i++) {
ids_to_check.push_back((*i)->id());
}
}
ProcessManager* process_manager = ProcessManager::Get(context);
for (std::vector<std::string>::const_iterator i = ids_to_check.begin();
i != ids_to_check.end();
i++) {
const std::string id = (*i);
ExtensionHost* host = process_manager->GetBackgroundHostForExtension(id);
if (host)
return false;
scoped_refptr<content::SiteInstance> site_instance =
process_manager->GetSiteInstanceForURL(
Extension::GetBaseURLFromExtensionId(id));
if (site_instance && site_instance->HasProcess())
return false;
if (!process_manager->GetRenderFrameHostsForExtension(id).empty())
return false;
}
return true;
}
GURL GetSiteForExtensionId(const std::string& extension_id,
content::BrowserContext* context) {
return content::SiteInstance::GetSiteForURL(
context, Extension::GetBaseURLFromExtensionId(extension_id));
}
std::unique_ptr<base::DictionaryValue> GetExtensionInfo(
const Extension* extension) {
DCHECK(extension);
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
dict->SetString("id", extension->id());
dict->SetString("name", extension->name());
GURL icon = extensions::ExtensionIconSource::GetIconURL(
extension,
extension_misc::EXTENSION_ICON_SMALLISH,
ExtensionIconSet::MATCH_BIGGER,
false, // Not grayscale.
NULL); // Don't set bool if exists.
dict->SetString("icon", icon.spec());
return dict;
}
const gfx::ImageSkia& GetDefaultAppIcon() {
return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
IDR_APP_DEFAULT_ICON);
}
const gfx::ImageSkia& GetDefaultExtensionIcon() {
return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
IDR_EXTENSION_DEFAULT_ICON);
}
bool IsNewBookmarkAppsEnabled() {
#if defined(OS_MACOSX)
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableNewBookmarkApps);
#else
return !base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableNewBookmarkApps);
#endif
}
bool CanHostedAppsOpenInWindows() {
#if defined(OS_MACOSX)
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableHostedAppsInWindows);
#else
return true;
#endif
}
bool IsExtensionSupervised(const Extension* extension, const Profile* profile) {
return extension->was_installed_by_custodian() && profile->IsSupervised();
}
bool NeedCustodianApprovalForPermissionIncrease(const Profile* profile) {
if (!profile->IsSupervised())
return false;
// Query the trial group name first, to make sure it's properly initialized.
base::FieldTrialList::FindFullName(
kSupervisedUserExtensionPermissionIncreaseFieldTrialName);
std::string value = variations::GetVariationParamValue(
kSupervisedUserExtensionPermissionIncreaseFieldTrialName,
profile->IsChild() ? "child_account" : "legacy_supervised_user");
return value == "true";
}
} // namespace util
} // namespace extensions