1// Copyright (c) 2012 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/installed_loader.h"
6
7#include "base/files/file_path.h"
8#include "base/metrics/histogram.h"
9#include "base/metrics/sparse_histogram.h"
10#include "base/strings/stringprintf.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/threading/thread_restrictions.h"
13#include "base/values.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/extensions/extension_action_manager.h"
16#include "chrome/browser/extensions/extension_error_reporter.h"
17#include "chrome/browser/extensions/extension_service.h"
18#include "chrome/browser/extensions/extension_util.h"
19#include "chrome/browser/profiles/profile_manager.h"
20#include "chrome/common/chrome_switches.h"
21#include "chrome/common/extensions/api/supervised_user_private/supervised_user_handler.h"
22#include "chrome/common/extensions/manifest_url_handler.h"
23#include "chrome/common/pref_names.h"
24#include "content/public/browser/browser_thread.h"
25#include "content/public/browser/notification_service.h"
26#include "content/public/browser/user_metrics.h"
27#include "extensions/browser/extension_prefs.h"
28#include "extensions/browser/extension_registry.h"
29#include "extensions/browser/extension_system.h"
30#include "extensions/browser/management_policy.h"
31#include "extensions/common/extension.h"
32#include "extensions/common/extension_l10n_util.h"
33#include "extensions/common/extension_set.h"
34#include "extensions/common/extension_urls.h"
35#include "extensions/common/file_util.h"
36#include "extensions/common/manifest.h"
37#include "extensions/common/manifest_constants.h"
38#include "extensions/common/manifest_handlers/background_info.h"
39
40using base::UserMetricsAction;
41using content::BrowserThread;
42
43namespace extensions {
44
45namespace errors = manifest_errors;
46
47namespace {
48
49// The following enumeration is used in histograms matching
50// Extensions.ManifestReload*.
51enum ManifestReloadReason {
52  NOT_NEEDED = 0,        // Reload not needed.
53  UNPACKED_DIR,          // Unpacked directory.
54  NEEDS_RELOCALIZATION,  // The locale has changed since we read this extension.
55  CORRUPT_PREFERENCES,   // The manifest in the preferences is corrupt.
56
57  // New enum values must go above here.
58  NUM_MANIFEST_RELOAD_REASONS
59};
60
61// Used in histogram Extension.BackgroundPageType.
62enum BackgroundPageType {
63  NO_BACKGROUND_PAGE = 0,
64  BACKGROUND_PAGE_PERSISTENT,
65  EVENT_PAGE,
66
67  // New enum values must go above here.
68  NUM_BACKGROUND_PAGE_TYPES
69};
70
71// Used in histogram Extensions.ExternalItemState.
72enum ExternalItemState {
73  DEPRECATED_EXTERNAL_ITEM_DISABLED = 0,
74  DEPRECATED_EXTERNAL_ITEM_ENABLED,
75  EXTERNAL_ITEM_WEBSTORE_DISABLED,
76  EXTERNAL_ITEM_WEBSTORE_ENABLED,
77  EXTERNAL_ITEM_NONWEBSTORE_DISABLED,
78  EXTERNAL_ITEM_NONWEBSTORE_ENABLED,
79  EXTERNAL_ITEM_WEBSTORE_UNINSTALLED,
80  EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED,
81
82  // New enum values must go above here.
83  EXTERNAL_ITEM_MAX_ITEMS
84};
85
86bool IsManifestCorrupt(const base::DictionaryValue* manifest) {
87  if (!manifest)
88    return false;
89
90  // Because of bug #272524 sometimes manifests got mangled in the preferences
91  // file, one particularly bad case resulting in having both a background page
92  // and background scripts values. In those situations we want to reload the
93  // manifest from the extension to fix this.
94  const base::Value* background_page;
95  const base::Value* background_scripts;
96  return manifest->Get(manifest_keys::kBackgroundPage, &background_page) &&
97      manifest->Get(manifest_keys::kBackgroundScripts, &background_scripts);
98}
99
100ManifestReloadReason ShouldReloadExtensionManifest(const ExtensionInfo& info) {
101  // Always reload manifests of unpacked extensions, because they can change
102  // on disk independent of the manifest in our prefs.
103  if (Manifest::IsUnpackedLocation(info.extension_location))
104    return UNPACKED_DIR;
105
106  // Reload the manifest if it needs to be relocalized.
107  if (extension_l10n_util::ShouldRelocalizeManifest(
108          info.extension_manifest.get()))
109    return NEEDS_RELOCALIZATION;
110
111  // Reload if the copy of the manifest in the preferences is corrupt.
112  if (IsManifestCorrupt(info.extension_manifest.get()))
113    return CORRUPT_PREFERENCES;
114
115  return NOT_NEEDED;
116}
117
118BackgroundPageType GetBackgroundPageType(const Extension* extension) {
119  if (!BackgroundInfo::HasBackgroundPage(extension))
120    return NO_BACKGROUND_PAGE;
121  if (BackgroundInfo::HasPersistentBackgroundPage(extension))
122    return BACKGROUND_PAGE_PERSISTENT;
123  return EVENT_PAGE;
124}
125
126// Records the creation flags of an extension grouped by
127// Extension::InitFromValueFlags.
128void RecordCreationFlags(const Extension* extension) {
129  for (int i = 0; i < Extension::kInitFromValueFlagBits; ++i) {
130    int flag = 1 << i;
131    if (extension->creation_flags() & flag) {
132      UMA_HISTOGRAM_ENUMERATION(
133          "Extensions.LoadCreationFlags", i, Extension::kInitFromValueFlagBits);
134    }
135  }
136}
137
138// Helper to record a single disable reason histogram value (see
139// RecordDisableReasons below).
140void RecordDisbleReasonHistogram(int reason) {
141  UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.DisableReason", reason);
142}
143
144// Records the disable reasons for a single extension grouped by
145// Extension::DisableReason.
146void RecordDisableReasons(int reasons) {
147  // |reasons| is a bitmask with values from Extension::DisabledReason
148  // which are increasing powers of 2.
149  if (reasons == Extension::DISABLE_NONE) {
150    RecordDisbleReasonHistogram(Extension::DISABLE_NONE);
151    return;
152  }
153  for (int reason = 1; reason < Extension::DISABLE_REASON_LAST; reason <<= 1) {
154    if (reasons & reason)
155      RecordDisbleReasonHistogram(reason);
156  }
157}
158
159}  // namespace
160
161InstalledLoader::InstalledLoader(ExtensionService* extension_service)
162    : extension_service_(extension_service),
163      extension_registry_(ExtensionRegistry::Get(extension_service->profile())),
164      extension_prefs_(ExtensionPrefs::Get(extension_service->profile())) {}
165
166InstalledLoader::~InstalledLoader() {
167}
168
169void InstalledLoader::Load(const ExtensionInfo& info, bool write_to_prefs) {
170  std::string error;
171  scoped_refptr<const Extension> extension(NULL);
172  if (info.extension_manifest) {
173    extension = Extension::Create(
174        info.extension_path,
175        info.extension_location,
176        *info.extension_manifest,
177        GetCreationFlags(&info),
178        &error);
179  } else {
180    error = errors::kManifestUnreadable;
181  }
182
183  // Once installed, non-unpacked extensions cannot change their IDs (e.g., by
184  // updating the 'key' field in their manifest).
185  // TODO(jstritar): migrate preferences when unpacked extensions change IDs.
186  if (extension.get() && !Manifest::IsUnpackedLocation(extension->location()) &&
187      info.extension_id != extension->id()) {
188    error = errors::kCannotChangeExtensionID;
189    extension = NULL;
190  }
191
192  // Check policy on every load in case an extension was blacklisted while
193  // Chrome was not running.
194  const ManagementPolicy* policy = extensions::ExtensionSystem::Get(
195      extension_service_->profile())->management_policy();
196  if (extension.get()) {
197    Extension::DisableReason disable_reason = Extension::DISABLE_NONE;
198    bool force_disabled = false;
199    if (!policy->UserMayLoad(extension.get(), NULL)) {
200      // The error message from UserMayInstall() often contains the extension ID
201      // and is therefore not well suited to this UI.
202      error = errors::kDisabledByPolicy;
203      extension = NULL;
204    } else if (!extension_prefs_->IsExtensionDisabled(extension->id()) &&
205               policy->MustRemainDisabled(
206                   extension.get(), &disable_reason, NULL)) {
207      extension_prefs_->SetExtensionState(extension->id(), Extension::DISABLED);
208      extension_prefs_->AddDisableReason(extension->id(), disable_reason);
209      force_disabled = true;
210    }
211    UMA_HISTOGRAM_BOOLEAN("ExtensionInstalledLoader.ForceDisabled",
212                          force_disabled);
213  }
214
215  if (!extension.get()) {
216    ExtensionErrorReporter::GetInstance()->ReportLoadError(
217        info.extension_path,
218        error,
219        extension_service_->profile(),
220        false);  // Be quiet.
221    return;
222  }
223
224  if (write_to_prefs)
225    extension_prefs_->UpdateManifest(extension.get());
226
227  extension_service_->AddExtension(extension.get());
228}
229
230void InstalledLoader::LoadAllExtensions() {
231  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
232
233  base::TimeTicks start_time = base::TimeTicks::Now();
234
235  Profile* profile = extension_service_->profile();
236  scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
237      extension_prefs_->GetInstalledExtensionsInfo());
238
239  std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0);
240  bool should_write_prefs = false;
241
242  for (size_t i = 0; i < extensions_info->size(); ++i) {
243    ExtensionInfo* info = extensions_info->at(i).get();
244
245    // Skip extensions that were loaded from the command-line because we don't
246    // want those to persist across browser restart.
247    if (info->extension_location == Manifest::COMMAND_LINE)
248      continue;
249
250    ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info);
251    ++reload_reason_counts[reload_reason];
252
253    if (reload_reason != NOT_NEEDED) {
254      // Reloading an extension reads files from disk.  We do this on the
255      // UI thread because reloads should be very rare, and the complexity
256      // added by delaying the time when the extensions service knows about
257      // all extensions is significant.  See crbug.com/37548 for details.
258      // |allow_io| disables tests that file operations run on the file
259      // thread.
260      base::ThreadRestrictions::ScopedAllowIO allow_io;
261
262      std::string error;
263      scoped_refptr<const Extension> extension(
264          file_util::LoadExtension(info->extension_path,
265                                   info->extension_location,
266                                   GetCreationFlags(info),
267                                   &error));
268
269      if (!extension.get()) {
270        ExtensionErrorReporter::GetInstance()->ReportLoadError(
271            info->extension_path,
272            error,
273            profile,
274            false);  // Be quiet.
275        continue;
276      }
277
278      extensions_info->at(i)->extension_manifest.reset(
279          static_cast<base::DictionaryValue*>(
280              extension->manifest()->value()->DeepCopy()));
281      should_write_prefs = true;
282    }
283  }
284
285  for (size_t i = 0; i < extensions_info->size(); ++i) {
286    if (extensions_info->at(i)->extension_location != Manifest::COMMAND_LINE)
287      Load(*extensions_info->at(i), should_write_prefs);
288  }
289
290  extension_service_->OnLoadedInstalledExtensions();
291
292  // The histograms Extensions.ManifestReload* allow us to validate
293  // the assumption that reloading manifest is a rare event.
294  UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNotNeeded",
295                           reload_reason_counts[NOT_NEEDED]);
296  UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadUnpackedDir",
297                           reload_reason_counts[UNPACKED_DIR]);
298  UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNeedsRelocalization",
299                           reload_reason_counts[NEEDS_RELOCALIZATION]);
300
301  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll",
302                           extension_registry_->enabled_extensions().size());
303  UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled",
304                           extension_registry_->disabled_extensions().size());
305
306  UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
307                      base::TimeTicks::Now() - start_time);
308
309  int app_user_count = 0;
310  int app_external_count = 0;
311  int hosted_app_count = 0;
312  int legacy_packaged_app_count = 0;
313  int platform_app_count = 0;
314  int user_script_count = 0;
315  int content_pack_count = 0;
316  int extension_user_count = 0;
317  int extension_external_count = 0;
318  int theme_count = 0;
319  int page_action_count = 0;
320  int browser_action_count = 0;
321  int disabled_for_permissions_count = 0;
322  int non_webstore_ntp_override_count = 0;
323  int incognito_allowed_count = 0;
324  int incognito_not_allowed_count = 0;
325  int file_access_allowed_count = 0;
326  int file_access_not_allowed_count = 0;
327
328  const ExtensionSet& extensions = extension_registry_->enabled_extensions();
329  ExtensionActionManager* extension_action_manager =
330      ExtensionActionManager::Get(profile);
331  for (ExtensionSet::const_iterator iter = extensions.begin();
332       iter != extensions.end();
333       ++iter) {
334    const Extension* extension = iter->get();
335    Manifest::Location location = extension->location();
336    Manifest::Type type = extension->GetType();
337
338    // For the first few metrics, include all extensions and apps (component,
339    // unpacked, etc). It's good to know these locations, and it doesn't
340    // muck up any of the stats. Later, though, we want to omit component and
341    // unpacked, as they are less interesting.
342    if (extension->is_app())
343      UMA_HISTOGRAM_ENUMERATION(
344          "Extensions.AppLocation", location, Manifest::NUM_LOCATIONS);
345    else if (extension->is_extension())
346      UMA_HISTOGRAM_ENUMERATION(
347          "Extensions.ExtensionLocation", location, Manifest::NUM_LOCATIONS);
348
349    if (!ManifestURL::UpdatesFromGallery(extension)) {
350      UMA_HISTOGRAM_ENUMERATION(
351          "Extensions.NonWebstoreLocation", location, Manifest::NUM_LOCATIONS);
352
353      // Check for inconsistencies if the extension was supposedly installed
354      // from the webstore.
355      enum {
356        BAD_UPDATE_URL = 0,
357        // This value was a mistake. Turns out sideloaded extensions can
358        // have the from_webstore bit if they update from the webstore.
359        DEPRECATED_IS_EXTERNAL = 1,
360      };
361      if (extension->from_webstore()) {
362        UMA_HISTOGRAM_ENUMERATION(
363            "Extensions.FromWebstoreInconsistency", BAD_UPDATE_URL, 2);
364      }
365    }
366
367    if (Manifest::IsExternalLocation(location)) {
368      // See loop below for DISABLED.
369      if (ManifestURL::UpdatesFromGallery(extension)) {
370        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
371                                  EXTERNAL_ITEM_WEBSTORE_ENABLED,
372                                  EXTERNAL_ITEM_MAX_ITEMS);
373      } else {
374        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
375                                  EXTERNAL_ITEM_NONWEBSTORE_ENABLED,
376                                  EXTERNAL_ITEM_MAX_ITEMS);
377      }
378    }
379
380    // From now on, don't count component extensions, since they are only
381    // extensions as an implementation detail. Continue to count unpacked
382    // extensions for a few metrics.
383    if (location == Manifest::COMPONENT)
384      continue;
385
386    // Histogram for non-webstore extensions overriding new tab page should
387    // include unpacked extensions.
388    if (!extension->from_webstore() &&
389        URLOverrides::GetChromeURLOverrides(extension).count("newtab")) {
390      ++non_webstore_ntp_override_count;
391    }
392
393    // Don't count unpacked extensions anymore, either.
394    if (Manifest::IsUnpackedLocation(location))
395      continue;
396
397    UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestVersion",
398                              extension->manifest_version(),
399                              10);  // TODO(kalman): Why 10 manifest versions?
400
401    // We might have wanted to count legacy packaged apps here, too, since they
402    // are effectively extensions. Unfortunately, it's too late, as we don't
403    // want to mess up the existing stats.
404    if (type == Manifest::TYPE_EXTENSION) {
405      UMA_HISTOGRAM_ENUMERATION("Extensions.BackgroundPageType",
406                                GetBackgroundPageType(extension),
407                                NUM_BACKGROUND_PAGE_TYPES);
408    }
409
410    // Using an enumeration shows us the total installed ratio across all users.
411    // Using the totals per user at each startup tells us the distribution of
412    // usage for each user (e.g. 40% of users have at least one app installed).
413    UMA_HISTOGRAM_ENUMERATION(
414        "Extensions.LoadType", type, Manifest::NUM_LOAD_TYPES);
415    switch (type) {
416      case Manifest::TYPE_THEME:
417        ++theme_count;
418        break;
419      case Manifest::TYPE_USER_SCRIPT:
420        ++user_script_count;
421        break;
422      case Manifest::TYPE_HOSTED_APP:
423        ++hosted_app_count;
424        if (Manifest::IsExternalLocation(location)) {
425          ++app_external_count;
426        } else {
427          ++app_user_count;
428        }
429        break;
430      case Manifest::TYPE_LEGACY_PACKAGED_APP:
431        ++legacy_packaged_app_count;
432        if (Manifest::IsExternalLocation(location)) {
433          ++app_external_count;
434        } else {
435          ++app_user_count;
436        }
437        break;
438      case Manifest::TYPE_PLATFORM_APP:
439        ++platform_app_count;
440        if (Manifest::IsExternalLocation(location)) {
441          ++app_external_count;
442        } else {
443          ++app_user_count;
444        }
445        break;
446      case Manifest::TYPE_EXTENSION:
447      default:
448        if (Manifest::IsExternalLocation(location)) {
449          ++extension_external_count;
450        } else {
451          ++extension_user_count;
452        }
453        break;
454    }
455
456    if (extension_action_manager->GetPageAction(*extension))
457      ++page_action_count;
458
459    if (extension_action_manager->GetBrowserAction(*extension))
460      ++browser_action_count;
461
462    if (SupervisedUserInfo::IsContentPack(extension))
463      ++content_pack_count;
464
465    RecordCreationFlags(extension);
466
467    ExtensionService::RecordPermissionMessagesHistogram(
468        extension, "Extensions.Permissions_Load2");
469
470    // For incognito and file access, skip anything that doesn't appear in
471    // settings. Also, policy-installed (and unpacked of course, checked above)
472    // extensions are boring.
473    if (extension->ShouldDisplayInExtensionSettings() &&
474        !Manifest::IsPolicyLocation(extension->location())) {
475      if (extension->can_be_incognito_enabled()) {
476        if (util::IsIncognitoEnabled(extension->id(), profile))
477          ++incognito_allowed_count;
478        else
479          ++incognito_not_allowed_count;
480      }
481      if (extension->wants_file_access()) {
482        if (util::AllowFileAccess(extension->id(), profile))
483          ++file_access_allowed_count;
484        else
485          ++file_access_not_allowed_count;
486      }
487    }
488  }
489
490  const ExtensionSet& disabled_extensions =
491      extension_registry_->disabled_extensions();
492
493  for (ExtensionSet::const_iterator ex = disabled_extensions.begin();
494       ex != disabled_extensions.end();
495       ++ex) {
496    if (extension_prefs_->DidExtensionEscalatePermissions((*ex)->id())) {
497      ++disabled_for_permissions_count;
498    }
499    RecordDisableReasons(extension_prefs_->GetDisableReasons((*ex)->id()));
500    if (Manifest::IsExternalLocation((*ex)->location())) {
501      // See loop above for ENABLED.
502      if (ManifestURL::UpdatesFromGallery(ex->get())) {
503        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
504                                  EXTERNAL_ITEM_WEBSTORE_DISABLED,
505                                  EXTERNAL_ITEM_MAX_ITEMS);
506      } else {
507        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
508                                  EXTERNAL_ITEM_NONWEBSTORE_DISABLED,
509                                  EXTERNAL_ITEM_MAX_ITEMS);
510      }
511    }
512  }
513
514  scoped_ptr<ExtensionPrefs::ExtensionsInfo> uninstalled_extensions_info(
515      extension_prefs_->GetUninstalledExtensionsInfo());
516  for (size_t i = 0; i < uninstalled_extensions_info->size(); ++i) {
517    ExtensionInfo* info = uninstalled_extensions_info->at(i).get();
518    if (Manifest::IsExternalLocation(info->extension_location)) {
519      std::string update_url;
520      if (info->extension_manifest->GetString("update_url", &update_url) &&
521          extension_urls::IsWebstoreUpdateUrl(GURL(update_url))) {
522        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
523                                  EXTERNAL_ITEM_WEBSTORE_UNINSTALLED,
524                                  EXTERNAL_ITEM_MAX_ITEMS);
525      } else {
526        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
527                                  EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED,
528                                  EXTERNAL_ITEM_MAX_ITEMS);
529      }
530    }
531  }
532
533  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp",
534                           app_user_count + app_external_count);
535  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppUser", app_user_count);
536  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppExternal", app_external_count);
537  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count);
538  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPackagedApp",
539                           legacy_packaged_app_count);
540  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPlatformApp", platform_app_count);
541  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension",
542                           extension_user_count + extension_external_count);
543  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionUser",
544                           extension_user_count);
545  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionExternal",
546                           extension_external_count);
547  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
548  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
549  // Histogram name different for legacy reasons.
550  UMA_HISTOGRAM_COUNTS_100("PageActionController.ExtensionsWithPageActions",
551                           page_action_count);
552  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
553                           browser_action_count);
554  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadContentPack", content_pack_count);
555  UMA_HISTOGRAM_COUNTS_100("Extensions.DisabledForPermissions",
556                           disabled_for_permissions_count);
557  UMA_HISTOGRAM_COUNTS_100("Extensions.NonWebStoreNewTabPageOverrides",
558                           non_webstore_ntp_override_count);
559  if (incognito_allowed_count + incognito_not_allowed_count > 0) {
560    UMA_HISTOGRAM_COUNTS_100("Extensions.IncognitoAllowed",
561                             incognito_allowed_count);
562    UMA_HISTOGRAM_COUNTS_100("Extensions.IncognitoNotAllowed",
563                             incognito_not_allowed_count);
564  }
565  if (file_access_allowed_count + file_access_not_allowed_count > 0) {
566    UMA_HISTOGRAM_COUNTS_100("Extensions.FileAccessAllowed",
567                             file_access_allowed_count);
568    UMA_HISTOGRAM_COUNTS_100("Extensions.FileAccessNotAllowed",
569                             file_access_not_allowed_count);
570  }
571  UMA_HISTOGRAM_COUNTS_100("Extensions.CorruptExtensionTotalDisables",
572                           extension_prefs_->GetCorruptedDisableCount());
573}
574
575int InstalledLoader::GetCreationFlags(const ExtensionInfo* info) {
576  int flags = extension_prefs_->GetCreationFlags(info->extension_id);
577  if (!Manifest::IsUnpackedLocation(info->extension_location))
578    flags |= Extension::REQUIRE_KEY;
579  if (extension_prefs_->AllowFileAccess(info->extension_id))
580    flags |= Extension::ALLOW_FILE_ACCESS;
581  return flags;
582}
583
584}  // namespace extensions
585