extension_util.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
1// 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"
8#include "base/logging.h"
9#include "base/values.h"
10#include "chrome/browser/extensions/extension_service.h"
11#include "chrome/browser/extensions/extension_sync_service.h"
12#include "chrome/browser/extensions/permissions_updater.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
15#include "chrome/common/chrome_switches.h"
16#include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
17#include "chrome/common/extensions/sync_helper.h"
18#include "content/public/browser/site_instance.h"
19#include "extensions/browser/extension_prefs.h"
20#include "extensions/browser/extension_registry.h"
21#include "extensions/browser/extension_system.h"
22#include "extensions/browser/extension_util.h"
23#include "extensions/common/extension.h"
24#include "extensions/common/extension_icon_set.h"
25#include "extensions/common/feature_switch.h"
26#include "extensions/common/features/simple_feature.h"
27#include "extensions/common/manifest.h"
28#include "extensions/common/manifest_handlers/incognito_info.h"
29#include "extensions/common/permissions/permissions_data.h"
30#include "grit/theme_resources.h"
31#include "ui/base/resource/resource_bundle.h"
32
33namespace extensions {
34namespace util {
35
36namespace {
37// The entry into the ExtensionPrefs for allowing an extension to script on
38// all urls without explicit permission.
39const char kExtensionAllowedOnAllUrlsPrefName[] =
40    "extension_can_script_all_urls";
41
42// Returns true if |extension_id| for an external component extension should
43// always be enabled in incognito windows.
44bool IsWhitelistedForIncognito(const std::string& extension_id) {
45  static const char* kExtensionWhitelist[] = {
46    "D5736E4B5CF695CB93A2FB57E4FDC6E5AFAB6FE2",  // http://crbug.com/312900
47    "D57DE394F36DC1C3220E7604C575D29C51A6C495",  // http://crbug.com/319444
48    "3F65507A3B39259B38C8173C6FFA3D12DF64CCE9"   // http://crbug.com/371562
49  };
50
51  return extensions::SimpleFeature::IsIdInList(
52      extension_id,
53      std::set<std::string>(
54          kExtensionWhitelist,
55          kExtensionWhitelist + arraysize(kExtensionWhitelist)));
56}
57}  // namespace
58
59bool IsIncognitoEnabled(const std::string& extension_id,
60                        content::BrowserContext* context) {
61  const Extension* extension = ExtensionRegistry::Get(context)->
62      GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
63  if (extension) {
64    if (!extension->can_be_incognito_enabled())
65      return false;
66    // If this is an existing component extension we always allow it to
67    // work in incognito mode.
68    if (extension->location() == Manifest::COMPONENT)
69      return true;
70    if (extension->location() == Manifest::EXTERNAL_COMPONENT &&
71        IsWhitelistedForIncognito(extension_id)) {
72      return true;
73    }
74  }
75
76  return ExtensionPrefs::Get(context)->IsIncognitoEnabled(extension_id);
77}
78
79void SetIsIncognitoEnabled(const std::string& extension_id,
80                           content::BrowserContext* context,
81                           bool enabled) {
82  ExtensionService* service =
83      ExtensionSystem::Get(context)->extension_service();
84  CHECK(service);
85  const Extension* extension = service->GetInstalledExtension(extension_id);
86
87  if (extension) {
88    if (!extension->can_be_incognito_enabled())
89      return;
90
91    if (extension->location() == Manifest::COMPONENT) {
92      // This shouldn't be called for component extensions unless it is called
93      // by sync, for syncable component extensions.
94      // See http://crbug.com/112290 and associated CLs for the sordid history.
95      DCHECK(sync_helper::IsSyncable(extension));
96
97      // If we are here, make sure the we aren't trying to change the value.
98      DCHECK_EQ(enabled, IsIncognitoEnabled(extension_id, service->profile()));
99      return;
100    }
101  }
102
103  ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(service->profile());
104  // Broadcast unloaded and loaded events to update browser state. Only bother
105  // if the value changed and the extension is actually enabled, since there is
106  // no UI otherwise.
107  bool old_enabled = extension_prefs->IsIncognitoEnabled(extension_id);
108  if (enabled == old_enabled)
109    return;
110
111  extension_prefs->SetIsIncognitoEnabled(extension_id, enabled);
112
113  bool extension_is_enabled = service->extensions()->Contains(extension_id);
114
115  // When we reload the extension the ID may be invalidated if we've passed it
116  // by const ref everywhere. Make a copy to be safe.
117  std::string id = extension_id;
118  if (extension_is_enabled)
119    service->ReloadExtension(id);
120
121  // Reloading the extension invalidates the |extension| pointer.
122  extension = service->GetInstalledExtension(id);
123  if (extension) {
124    ExtensionSyncService::Get(service->profile())->
125        SyncExtensionChangeIfNeeded(*extension);
126  }
127}
128
129bool CanCrossIncognito(const Extension* extension,
130                       content::BrowserContext* context) {
131  // We allow the extension to see events and data from another profile iff it
132  // uses "spanning" behavior and it has incognito access. "split" mode
133  // extensions only see events for a matching profile.
134  CHECK(extension);
135  return IsIncognitoEnabled(extension->id(), context) &&
136         !IncognitoInfo::IsSplitMode(extension);
137}
138
139bool CanLoadInIncognito(const Extension* extension,
140                        content::BrowserContext* context) {
141  CHECK(extension);
142  if (extension->is_hosted_app())
143    return true;
144  // Packaged apps and regular extensions need to be enabled specifically for
145  // incognito (and split mode should be set).
146  return IncognitoInfo::IsSplitMode(extension) &&
147         IsIncognitoEnabled(extension->id(), context);
148}
149
150bool AllowFileAccess(const std::string& extension_id,
151                     content::BrowserContext* context) {
152  return CommandLine::ForCurrentProcess()->HasSwitch(
153             switches::kDisableExtensionsFileAccessCheck) ||
154         ExtensionPrefs::Get(context)->AllowFileAccess(extension_id);
155}
156
157void SetAllowFileAccess(const std::string& extension_id,
158                        content::BrowserContext* context,
159                        bool allow) {
160  ExtensionService* service =
161      ExtensionSystem::Get(context)->extension_service();
162  CHECK(service);
163
164  // Reload to update browser state. Only bother if the value changed and the
165  // extension is actually enabled, since there is no UI otherwise.
166  if (allow == AllowFileAccess(extension_id, context))
167    return;
168
169  ExtensionPrefs::Get(context)->SetAllowFileAccess(extension_id, allow);
170
171  bool extension_is_enabled = service->extensions()->Contains(extension_id);
172  if (extension_is_enabled)
173    service->ReloadExtension(extension_id);
174}
175
176bool AllowedScriptingOnAllUrls(const std::string& extension_id,
177                               content::BrowserContext* context) {
178  bool allowed = false;
179  return ExtensionPrefs::Get(context)->ReadPrefAsBoolean(
180             extension_id,
181             kExtensionAllowedOnAllUrlsPrefName,
182             &allowed) &&
183         allowed;
184}
185
186void SetAllowedScriptingOnAllUrls(const std::string& extension_id,
187                                  content::BrowserContext* context,
188                                  bool allowed) {
189  if (allowed == AllowedScriptingOnAllUrls(extension_id, context))
190    return;  // Nothing to do here.
191
192  ExtensionPrefs::Get(context)->UpdateExtensionPref(
193      extension_id,
194      kExtensionAllowedOnAllUrlsPrefName,
195      allowed ? new base::FundamentalValue(true) : NULL);
196
197  const Extension* extension =
198      ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
199          extension_id);
200  if (extension) {
201    PermissionsUpdater updater(context);
202    if (allowed)
203      updater.GrantWithheldImpliedAllHosts(extension);
204    else
205      updater.WithholdImpliedAllHosts(extension);
206  }
207}
208
209bool ScriptsMayRequireActionForExtension(const Extension* extension) {
210  // An extension requires user action to execute scripts iff the switch to do
211  // so is enabled, the extension shows up in chrome:extensions (so the user can
212  // grant withheld permissions), the extension is not part of chrome or
213  // corporate policy, and also not on the scripting whitelist.
214  return FeatureSwitch::scripts_require_action()->IsEnabled() &&
215      extension->ShouldDisplayInExtensionSettings() &&
216      !Manifest::IsPolicyLocation(extension->location()) &&
217      !Manifest::IsComponentLocation(extension->location()) &&
218      !PermissionsData::CanExecuteScriptEverywhere(extension);
219}
220
221bool IsAppLaunchable(const std::string& extension_id,
222                     content::BrowserContext* context) {
223  int reason = ExtensionPrefs::Get(context)->GetDisableReasons(extension_id);
224  return !((reason & Extension::DISABLE_UNSUPPORTED_REQUIREMENT) ||
225           (reason & Extension::DISABLE_CORRUPTED));
226}
227
228bool IsAppLaunchableWithoutEnabling(const std::string& extension_id,
229                                    content::BrowserContext* context) {
230  return ExtensionRegistry::Get(context)->GetExtensionById(
231      extension_id, ExtensionRegistry::ENABLED) != NULL;
232}
233
234bool ShouldSyncExtension(const Extension* extension,
235                         content::BrowserContext* context) {
236  return sync_helper::IsSyncableExtension(extension) &&
237         !ExtensionPrefs::Get(context)->DoNotSync(extension->id());
238}
239
240bool ShouldSyncApp(const Extension* app, content::BrowserContext* context) {
241  return sync_helper::IsSyncableApp(app) &&
242         !util::IsEphemeralApp(app->id(), context) &&
243         !ExtensionPrefs::Get(context)->DoNotSync(app->id());
244}
245
246bool IsExtensionIdle(const std::string& extension_id,
247                     content::BrowserContext* context) {
248  ProcessManager* process_manager =
249      ExtensionSystem::Get(context)->process_manager();
250  DCHECK(process_manager);
251  ExtensionHost* host =
252      process_manager->GetBackgroundHostForExtension(extension_id);
253  if (host)
254    return false;
255
256  content::SiteInstance* site_instance = process_manager->GetSiteInstanceForURL(
257      Extension::GetBaseURLFromExtensionId(extension_id));
258  if (site_instance && site_instance->HasProcess())
259    return false;
260
261  return process_manager->GetRenderViewHostsForExtension(extension_id).empty();
262}
263
264GURL GetSiteForExtensionId(const std::string& extension_id,
265                           content::BrowserContext* context) {
266  return content::SiteInstance::GetSiteForURL(
267      context, Extension::GetBaseURLFromExtensionId(extension_id));
268}
269
270scoped_ptr<base::DictionaryValue> GetExtensionInfo(const Extension* extension) {
271  DCHECK(extension);
272  scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
273
274  dict->SetString("id", extension->id());
275  dict->SetString("name", extension->name());
276
277  GURL icon = extensions::ExtensionIconSource::GetIconURL(
278      extension,
279      extension_misc::EXTENSION_ICON_SMALLISH,
280      ExtensionIconSet::MATCH_BIGGER,
281      false,  // Not grayscale.
282      NULL);  // Don't set bool if exists.
283  dict->SetString("icon", icon.spec());
284
285  return dict.Pass();
286}
287
288bool HasIsolatedStorage(const ExtensionInfo& info) {
289  if (!info.extension_manifest.get())
290    return false;
291
292  std::string error;
293  scoped_refptr<const Extension> extension(Extension::Create(
294      info.extension_path,
295      info.extension_location,
296      *info.extension_manifest,
297      Extension::NO_FLAGS,
298      info.extension_id,
299      &error));
300  if (!extension.get())
301    return false;
302
303  return AppIsolationInfo::HasIsolatedStorage(extension.get());
304}
305
306bool SiteHasIsolatedStorage(const GURL& extension_site_url,
307                            content::BrowserContext* context) {
308  const Extension* extension = ExtensionRegistry::Get(context)->
309      enabled_extensions().GetExtensionOrAppByURL(extension_site_url);
310  if (!extension)
311    return false;
312
313  return AppIsolationInfo::HasIsolatedStorage(extension);
314}
315
316const gfx::ImageSkia& GetDefaultAppIcon() {
317  return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
318      IDR_APP_DEFAULT_ICON);
319}
320
321const gfx::ImageSkia& GetDefaultExtensionIcon() {
322  return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
323      IDR_EXTENSION_DEFAULT_ICON);
324}
325
326}  // namespace util
327}  // namespace extensions
328