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