blob: 4406eb9afc5a106bd5248baf60630f6398c126a9 [file] [log] [blame]
[email protected]a7ff4b72013-10-17 20:56:021// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/extension_util.h"
6
7#include "base/command_line.h"
[email protected]bb2feea2014-03-18 22:08:138#include "base/logging.h"
treib3202d592015-07-31 08:33:169#include "base/metrics/field_trial.h"
[email protected]bb2feea2014-03-18 22:08:1310#include "base/values.h"
avia2f4804a2015-12-24 23:11:1311#include "build/build_config.h"
[email protected]a7ff4b72013-10-17 20:56:0212#include "chrome/browser/extensions/extension_service.h"
[email protected]f8aefb132013-10-30 09:29:5213#include "chrome/browser/extensions/extension_sync_service.h"
[email protected]23a85362014-07-07 23:26:1914#include "chrome/browser/extensions/permissions_updater.h"
rdevlin.cronincb9f86e2015-10-15 15:13:4215#include "chrome/browser/extensions/scripting_permissions_modifier.h"
elijahtaylor0def4432014-10-06 18:15:1116#include "chrome/browser/extensions/shared_module_service.h"
[email protected]1d5cf4142014-01-24 18:25:2217#include "chrome/browser/profiles/profile.h"
[email protected]bb2feea2014-03-18 22:08:1318#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
[email protected]a7ff4b72013-10-17 20:56:0219#include "chrome/common/chrome_switches.h"
[email protected]a7ff4b72013-10-17 20:56:0220#include "chrome/common/extensions/sync_helper.h"
treibb6af28cd2015-12-01 11:19:4621#include "components/variations/variations_associated_data.h"
[email protected]617342a42013-12-18 23:34:0322#include "content/public/browser/site_instance.h"
[email protected]489db0842014-01-22 18:20:0323#include "extensions/browser/extension_prefs.h"
[email protected]599539802014-01-07 23:06:0024#include "extensions/browser/extension_registry.h"
[email protected]59b0e602014-01-30 00:41:2425#include "extensions/browser/extension_system.h"
[email protected]30e190f82014-05-26 16:44:3926#include "extensions/browser/extension_util.h"
[email protected]e4452d32013-11-15 23:07:4127#include "extensions/common/extension.h"
[email protected]4b7908842014-04-07 23:50:2228#include "extensions/common/extension_icon_set.h"
[email protected]4d67e9d2014-08-18 22:03:5429#include "extensions/common/feature_switch.h"
kalman642103282014-12-15 03:59:0930#include "extensions/common/features/behavior_feature.h"
31#include "extensions/common/features/feature_provider.h"
[email protected]a7ff4b72013-10-17 20:56:0232#include "extensions/common/manifest.h"
tfarina0bcdf362015-06-29 22:19:2633#include "extensions/common/manifest_handlers/app_isolation_info.h"
[email protected]1f7de252013-11-06 22:02:0034#include "extensions/common/manifest_handlers/incognito_info.h"
[email protected]4d67e9d2014-08-18 22:03:5435#include "extensions/common/permissions/permissions_data.h"
mukai4245dfe82014-09-05 17:40:5136#include "extensions/grit/extensions_browser_resources.h"
[email protected]7eb20e32014-04-30 08:50:5637#include "ui/base/resource/resource_bundle.h"
[email protected]a7ff4b72013-10-17 20:56:0238
[email protected]1d5cf4142014-01-24 18:25:2239namespace extensions {
40namespace util {
[email protected]a7ff4b72013-10-17 20:56:0241
[email protected]b33c8c22014-05-29 19:51:0842namespace {
thestig7b4bd932014-09-09 22:44:3143
treib3202d592015-07-31 08:33:1644const char kSupervisedUserExtensionPermissionIncreaseFieldTrialName[] =
45 "SupervisedUserExtensionPermissionIncrease";
46
[email protected]b33c8c22014-05-29 19:51:0847// The entry into the ExtensionPrefs for allowing an extension to script on
48// all urls without explicit permission.
49const char kExtensionAllowedOnAllUrlsPrefName[] =
50 "extension_can_script_all_urls";
[email protected]277c4142014-06-19 20:08:5451
rdevlin.croninbb213582015-02-10 00:25:5552// The entry into the prefs for when a user has explicitly set the "extension
53// allowed on all urls" pref.
54const char kHasSetScriptOnAllUrlsPrefName[] = "has_set_script_all_urls";
55
kalman642103282014-12-15 03:59:0956// Returns true if |extension| should always be enabled in incognito mode.
57bool IsWhitelistedForIncognito(const Extension* extension) {
kalman38ec4882014-12-18 09:40:3858 return FeatureProvider::GetBehaviorFeature(
59 BehaviorFeature::kWhitelistedForIncognito)
kalman642103282014-12-15 03:59:0960 ->IsAvailableToExtension(extension)
61 .is_available();
[email protected]b33c8c22014-05-29 19:51:0862}
thestig7b4bd932014-09-09 22:44:3163
64// Returns |extension_id|. See note below.
65std::string ReloadExtensionIfEnabled(const std::string& extension_id,
66 content::BrowserContext* context) {
67 ExtensionRegistry* registry = ExtensionRegistry::Get(context);
68 bool extension_is_enabled =
69 registry->enabled_extensions().Contains(extension_id);
70
71 if (!extension_is_enabled)
72 return extension_id;
73
74 // When we reload the extension the ID may be invalidated if we've passed it
75 // by const ref everywhere. Make a copy to be safe. http://crbug.com/103762
76 std::string id = extension_id;
77 ExtensionService* service =
78 ExtensionSystem::Get(context)->extension_service();
79 CHECK(service);
80 service->ReloadExtension(id);
81 return id;
82}
83
rdevlin.croninb8dffe52015-02-07 00:58:0184// Sets the preference for scripting on all urls to |allowed|, optionally
85// updating the extension's active permissions (based on |update_permissions|).
86void SetAllowedScriptingOnAllUrlsHelper(
87 content::BrowserContext* context,
88 const std::string& extension_id,
89 bool allowed,
90 bool update_permissions) {
91 // TODO(devlin): Right now, we always need to have a value for this pref.
92 // Once the scripts-require-action feature launches, we can change the set
93 // to be null if false.
94 ExtensionPrefs::Get(context)->UpdateExtensionPref(
95 extension_id,
96 kExtensionAllowedOnAllUrlsPrefName,
97 new base::FundamentalValue(allowed));
98
99 if (update_permissions) {
100 const Extension* extension =
101 ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
102 extension_id);
103 if (extension) {
rdevlin.cronincb9f86e2015-10-15 15:13:42104 ScriptingPermissionsModifier modifier(context, extension);
rdevlin.croninb8dffe52015-02-07 00:58:01105 if (allowed)
rdevlin.cronincb9f86e2015-10-15 15:13:42106 modifier.GrantWithheldImpliedAllHosts();
rdevlin.croninb8dffe52015-02-07 00:58:01107 else
rdevlin.cronincb9f86e2015-10-15 15:13:42108 modifier.WithholdImpliedAllHosts();
rdevlin.cronind1aa8522015-02-13 00:25:57109
110 // If this was an update to permissions, we also need to sync the change.
111 ExtensionSyncService* sync_service = ExtensionSyncService::Get(context);
112 if (sync_service) // sync_service can be null in unittests.
113 sync_service->SyncExtensionChangeIfNeeded(*extension);
rdevlin.croninb8dffe52015-02-07 00:58:01114 }
115 }
116}
117
[email protected]277c4142014-06-19 20:08:54118} // namespace
[email protected]b33c8c22014-05-29 19:51:08119
[email protected]a7ff4b72013-10-17 20:56:02120bool IsIncognitoEnabled(const std::string& extension_id,
[email protected]1d5cf4142014-01-24 18:25:22121 content::BrowserContext* context) {
122 const Extension* extension = ExtensionRegistry::Get(context)->
123 GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
124 if (extension) {
kundajie548e7442015-09-18 23:19:09125 if (!util::CanBeIncognitoEnabled(extension))
[email protected]1d5cf4142014-01-24 18:25:22126 return false;
127 // If this is an existing component extension we always allow it to
128 // work in incognito mode.
129 if (extension->location() == Manifest::COMPONENT)
130 return true;
kalman642103282014-12-15 03:59:09131 if (IsWhitelistedForIncognito(extension))
[email protected]277c4142014-06-19 20:08:54132 return true;
[email protected]1d5cf4142014-01-24 18:25:22133 }
[email protected]1d5cf4142014-01-24 18:25:22134 return ExtensionPrefs::Get(context)->IsIncognitoEnabled(extension_id);
[email protected]a7ff4b72013-10-17 20:56:02135}
136
137void SetIsIncognitoEnabled(const std::string& extension_id,
[email protected]1d5cf4142014-01-24 18:25:22138 content::BrowserContext* context,
[email protected]a7ff4b72013-10-17 20:56:02139 bool enabled) {
thestig7b4bd932014-09-09 22:44:31140 ExtensionRegistry* registry = ExtensionRegistry::Get(context);
141 const Extension* extension =
142 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
[email protected]a7ff4b72013-10-17 20:56:02143
[email protected]1d5cf4142014-01-24 18:25:22144 if (extension) {
kundajie548e7442015-09-18 23:19:09145 if (!util::CanBeIncognitoEnabled(extension))
[email protected]1d5cf4142014-01-24 18:25:22146 return;
147
treibc644a1c2015-07-13 08:37:04148 // TODO(treib,kalman): Should this be Manifest::IsComponentLocation(..)?
149 // (which also checks for EXTERNAL_COMPONENT).
[email protected]1d5cf4142014-01-24 18:25:22150 if (extension->location() == Manifest::COMPONENT) {
151 // This shouldn't be called for component extensions unless it is called
152 // by sync, for syncable component extensions.
153 // See http://crbug.com/112290 and associated CLs for the sordid history.
treibc644a1c2015-07-13 08:37:04154 DCHECK(sync_helper::IsSyncableComponentExtension(extension));
[email protected]1d5cf4142014-01-24 18:25:22155
156 // If we are here, make sure the we aren't trying to change the value.
thestig7b4bd932014-09-09 22:44:31157 DCHECK_EQ(enabled, IsIncognitoEnabled(extension_id, context));
[email protected]1d5cf4142014-01-24 18:25:22158 return;
159 }
[email protected]a7ff4b72013-10-17 20:56:02160 }
161
thestig7b4bd932014-09-09 22:44:31162 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(context);
[email protected]a7ff4b72013-10-17 20:56:02163 // Broadcast unloaded and loaded events to update browser state. Only bother
164 // if the value changed and the extension is actually enabled, since there is
165 // no UI otherwise.
166 bool old_enabled = extension_prefs->IsIncognitoEnabled(extension_id);
167 if (enabled == old_enabled)
168 return;
169
170 extension_prefs->SetIsIncognitoEnabled(extension_id, enabled);
171
thestig7b4bd932014-09-09 22:44:31172 std::string id = ReloadExtensionIfEnabled(extension_id, context);
[email protected]a7ff4b72013-10-17 20:56:02173
174 // Reloading the extension invalidates the |extension| pointer.
thestig7b4bd932014-09-09 22:44:31175 extension = registry->GetExtensionById(id, ExtensionRegistry::EVERYTHING);
[email protected]f8aefb132013-10-30 09:29:52176 if (extension) {
thestig7b4bd932014-09-09 22:44:31177 Profile* profile = Profile::FromBrowserContext(context);
178 ExtensionSyncService::Get(profile)->SyncExtensionChangeIfNeeded(*extension);
[email protected]f8aefb132013-10-30 09:29:52179 }
[email protected]a7ff4b72013-10-17 20:56:02180}
181
182bool CanCrossIncognito(const Extension* extension,
[email protected]1d5cf4142014-01-24 18:25:22183 content::BrowserContext* context) {
[email protected]a7ff4b72013-10-17 20:56:02184 // We allow the extension to see events and data from another profile iff it
185 // uses "spanning" behavior and it has incognito access. "split" mode
186 // extensions only see events for a matching profile.
187 CHECK(extension);
[email protected]1d5cf4142014-01-24 18:25:22188 return IsIncognitoEnabled(extension->id(), context) &&
189 !IncognitoInfo::IsSplitMode(extension);
[email protected]a7ff4b72013-10-17 20:56:02190}
191
192bool CanLoadInIncognito(const Extension* extension,
[email protected]1d5cf4142014-01-24 18:25:22193 content::BrowserContext* context) {
194 CHECK(extension);
[email protected]a7ff4b72013-10-17 20:56:02195 if (extension->is_hosted_app())
196 return true;
197 // Packaged apps and regular extensions need to be enabled specifically for
198 // incognito (and split mode should be set).
[email protected]1d5cf4142014-01-24 18:25:22199 return IncognitoInfo::IsSplitMode(extension) &&
200 IsIncognitoEnabled(extension->id(), context);
[email protected]a7ff4b72013-10-17 20:56:02201}
202
[email protected]1d5cf4142014-01-24 18:25:22203bool AllowFileAccess(const std::string& extension_id,
204 content::BrowserContext* context) {
avi3ef9ec9e2014-12-22 22:50:17205 return base::CommandLine::ForCurrentProcess()->HasSwitch(
[email protected]1d5cf4142014-01-24 18:25:22206 switches::kDisableExtensionsFileAccessCheck) ||
207 ExtensionPrefs::Get(context)->AllowFileAccess(extension_id);
[email protected]a7ff4b72013-10-17 20:56:02208}
209
[email protected]1d5cf4142014-01-24 18:25:22210void SetAllowFileAccess(const std::string& extension_id,
211 content::BrowserContext* context,
[email protected]a7ff4b72013-10-17 20:56:02212 bool allow) {
213 // Reload to update browser state. Only bother if the value changed and the
214 // extension is actually enabled, since there is no UI otherwise.
[email protected]1d5cf4142014-01-24 18:25:22215 if (allow == AllowFileAccess(extension_id, context))
[email protected]a7ff4b72013-10-17 20:56:02216 return;
217
[email protected]7c82539c2014-02-19 06:09:17218 ExtensionPrefs::Get(context)->SetAllowFileAccess(extension_id, allow);
[email protected]a7ff4b72013-10-17 20:56:02219
thestig7b4bd932014-09-09 22:44:31220 ReloadExtensionIfEnabled(extension_id, context);
[email protected]a7ff4b72013-10-17 20:56:02221}
222
[email protected]b33c8c22014-05-29 19:51:08223bool AllowedScriptingOnAllUrls(const std::string& extension_id,
224 content::BrowserContext* context) {
225 bool allowed = false;
rdevlin.croninb8dffe52015-02-07 00:58:01226 ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
227 if (!prefs->ReadPrefAsBoolean(extension_id,
228 kExtensionAllowedOnAllUrlsPrefName,
229 &allowed)) {
230 // If there is no value present, we make one, defaulting it to the value of
231 // the 'scripts require action' flag. If the flag is on, then the extension
232 // does not have permission to script on all urls by default.
233 allowed = DefaultAllowedScriptingOnAllUrls();
234 SetAllowedScriptingOnAllUrlsHelper(context, extension_id, allowed, false);
235 }
236 return allowed;
[email protected]b33c8c22014-05-29 19:51:08237}
238
239void SetAllowedScriptingOnAllUrls(const std::string& extension_id,
240 content::BrowserContext* context,
241 bool allowed) {
rdevlin.croninbb213582015-02-10 00:25:55242 if (allowed != AllowedScriptingOnAllUrls(extension_id, context)) {
rdevlin.croninbb213582015-02-10 00:25:55243 ExtensionPrefs::Get(context)->UpdateExtensionPref(
244 extension_id,
245 kHasSetScriptOnAllUrlsPrefName,
246 new base::FundamentalValue(true));
rdevlin.cronin738501d2015-11-05 21:51:36247 SetAllowedScriptingOnAllUrlsHelper(context, extension_id, allowed, true);
rdevlin.croninbb213582015-02-10 00:25:55248 }
249}
250
251bool HasSetAllowedScriptingOnAllUrls(const std::string& extension_id,
252 content::BrowserContext* context) {
253 bool did_set = false;
254 return ExtensionPrefs::Get(context)->ReadPrefAsBoolean(
255 extension_id,
256 kHasSetScriptOnAllUrlsPrefName,
257 &did_set) && did_set;
[email protected]b33c8c22014-05-29 19:51:08258}
259
rdevlin.croninb8dffe52015-02-07 00:58:01260bool DefaultAllowedScriptingOnAllUrls() {
261 return !FeatureSwitch::scripts_require_action()->IsEnabled();
262}
263
[email protected]f5ea0962013-11-22 09:20:47264bool IsAppLaunchable(const std::string& extension_id,
[email protected]1d5cf4142014-01-24 18:25:22265 content::BrowserContext* context) {
[email protected]47e19402014-06-27 09:01:14266 int reason = ExtensionPrefs::Get(context)->GetDisableReasons(extension_id);
267 return !((reason & Extension::DISABLE_UNSUPPORTED_REQUIREMENT) ||
268 (reason & Extension::DISABLE_CORRUPTED));
[email protected]f5ea0962013-11-22 09:20:47269}
270
271bool IsAppLaunchableWithoutEnabling(const std::string& extension_id,
[email protected]1d5cf4142014-01-24 18:25:22272 content::BrowserContext* context) {
273 return ExtensionRegistry::Get(context)->GetExtensionById(
274 extension_id, ExtensionRegistry::ENABLED) != NULL;
[email protected]f5ea0962013-11-22 09:20:47275}
276
treibc644a1c2015-07-13 08:37:04277bool ShouldSync(const Extension* extension,
278 content::BrowserContext* context) {
279 return sync_helper::IsSyncable(extension) &&
[email protected]658eae52014-06-14 20:28:05280 !ExtensionPrefs::Get(context)->DoNotSync(extension->id());
281}
282
[email protected]617342a42013-12-18 23:34:03283bool IsExtensionIdle(const std::string& extension_id,
[email protected]1d5cf4142014-01-24 18:25:22284 content::BrowserContext* context) {
elijahtaylor0def4432014-10-06 18:15:11285 std::vector<std::string> ids_to_check;
286 ids_to_check.push_back(extension_id);
[email protected]617342a42013-12-18 23:34:03287
elijahtaylor0def4432014-10-06 18:15:11288 const Extension* extension =
289 ExtensionRegistry::Get(context)
290 ->GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
291 if (extension && extension->is_shared_module()) {
292 // We have to check all the extensions that use this shared module for idle
293 // to tell whether it is really 'idle'.
294 SharedModuleService* service = ExtensionSystem::Get(context)
295 ->extension_service()
296 ->shared_module_service();
297 scoped_ptr<ExtensionSet> dependents =
298 service->GetDependentExtensions(extension);
299 for (ExtensionSet::const_iterator i = dependents->begin();
300 i != dependents->end();
301 i++) {
302 ids_to_check.push_back((*i)->id());
303 }
304 }
305
reillyg0ea3fa902014-10-28 15:30:23306 ProcessManager* process_manager = ProcessManager::Get(context);
elijahtaylor0def4432014-10-06 18:15:11307 for (std::vector<std::string>::const_iterator i = ids_to_check.begin();
308 i != ids_to_check.end();
309 i++) {
310 const std::string id = (*i);
311 ExtensionHost* host = process_manager->GetBackgroundHostForExtension(id);
312 if (host)
313 return false;
314
rdevlin.cronin3d4261522015-02-10 00:48:15315 scoped_refptr<content::SiteInstance> site_instance =
elijahtaylor0def4432014-10-06 18:15:11316 process_manager->GetSiteInstanceForURL(
317 Extension::GetBaseURLFromExtensionId(id));
318 if (site_instance && site_instance->HasProcess())
319 return false;
320
rdevlin.cronin6ae04a012015-04-03 20:19:40321 if (!process_manager->GetRenderFrameHostsForExtension(id).empty())
elijahtaylor0def4432014-10-06 18:15:11322 return false;
323 }
324 return true;
[email protected]617342a42013-12-18 23:34:03325}
326
[email protected]3a746ec2014-03-15 05:30:56327GURL GetSiteForExtensionId(const std::string& extension_id,
328 content::BrowserContext* context) {
329 return content::SiteInstance::GetSiteForURL(
330 context, Extension::GetBaseURLFromExtensionId(extension_id));
331}
332
[email protected]bb2feea2014-03-18 22:08:13333scoped_ptr<base::DictionaryValue> GetExtensionInfo(const Extension* extension) {
334 DCHECK(extension);
335 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
336
337 dict->SetString("id", extension->id());
338 dict->SetString("name", extension->name());
339
340 GURL icon = extensions::ExtensionIconSource::GetIconURL(
341 extension,
342 extension_misc::EXTENSION_ICON_SMALLISH,
343 ExtensionIconSet::MATCH_BIGGER,
344 false, // Not grayscale.
345 NULL); // Don't set bool if exists.
346 dict->SetString("icon", icon.spec());
347
dcheng1fc00f12015-12-26 22:18:03348 return dict;
[email protected]bb2feea2014-03-18 22:08:13349}
350
[email protected]7eb20e32014-04-30 08:50:56351const gfx::ImageSkia& GetDefaultAppIcon() {
352 return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
353 IDR_APP_DEFAULT_ICON);
354}
355
356const gfx::ImageSkia& GetDefaultExtensionIcon() {
357 return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
358 IDR_EXTENSION_DEFAULT_ICON);
359}
360
benwellsc431c0ae2015-01-27 22:04:06361bool IsNewBookmarkAppsEnabled() {
benwellscb7bea0e2015-04-30 23:21:13362 return !base::CommandLine::ForCurrentProcess()->HasSwitch(
363 switches::kDisableNewBookmarkApps);
benwells39f23ae2014-08-27 08:01:52364}
365
dominickn2b10cbd2015-08-20 02:09:18366bool CanHostedAppsOpenInWindows() {
367#if defined(OS_MACOSX)
368 return base::CommandLine::ForCurrentProcess()->HasSwitch(
369 switches::kEnableHostedAppsInWindows);
370#else
371 return true;
372#endif
373}
374
treibb6af28cd2015-12-01 11:19:46375bool IsExtensionSupervised(const Extension* extension, const Profile* profile) {
treibbb9a1962015-02-25 13:40:59376 return extension->was_installed_by_custodian() && profile->IsSupervised();
377}
378
treibb6af28cd2015-12-01 11:19:46379bool NeedCustodianApprovalForPermissionIncrease(const Profile* profile) {
380 if (!profile->IsSupervised())
381 return false;
382 // Query the trial group name first, to make sure it's properly initialized.
383 base::FieldTrialList::FindFullName(
treib3202d592015-07-31 08:33:16384 kSupervisedUserExtensionPermissionIncreaseFieldTrialName);
treibb6af28cd2015-12-01 11:19:46385 std::string value = variations::GetVariationParamValue(
386 kSupervisedUserExtensionPermissionIncreaseFieldTrialName,
387 profile->IsChild() ? "child_account" : "legacy_supervised_user");
388 return value == "true";
treib3202d592015-07-31 08:33:16389}
390
[email protected]1d5cf4142014-01-24 18:25:22391} // namespace util
392} // namespace extensions