installed_loader.cc 17.1 KB
Newer Older
1 2 3 4 5 6
// Copyright (c) 2012 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/installed_loader.h"

7
#include "base/files/file_path.h"
8
#include "base/metrics/histogram.h"
9 10
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
11 12 13 14 15 16 17 18
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/runtime/runtime_api.h"
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
19
#include "chrome/browser/extensions/management_policy.h"
20 21
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_switches.h"
22
#include "chrome/common/extensions/api/managed_mode_private/managed_mode_handler.h"
23
#include "chrome/common/extensions/background_info.h"
24 25 26 27 28
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_file_util.h"
#include "chrome/common/extensions/extension_l10n_util.h"
#include "chrome/common/extensions/extension_manifest_constants.h"
#include "chrome/common/extensions/manifest.h"
29
#include "chrome/common/extensions/manifest_url_handler.h"
30 31 32 33 34 35 36 37
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/user_metrics.h"

using content::BrowserThread;
using content::UserMetricsAction;
using extensions::Extension;
using extensions::ExtensionInfo;
38
using extensions::Manifest;
39 40 41

namespace errors = extension_manifest_errors;

42 43
namespace extensions {

44 45 46 47 48 49 50 51 52 53 54 55
namespace {

// The following enumeration is used in histograms matching
// Extensions.ManifestReload* .  Values may be added, as long as existing
// values are not changed.
enum ManifestReloadReason {
  NOT_NEEDED = 0,  // Reload not needed.
  UNPACKED_DIR,  // Unpacked directory.
  NEEDS_RELOCALIZATION,  // The locale has changed since we read this extension.
  NUM_MANIFEST_RELOAD_REASONS
};

56 57 58 59 60 61 62 63
// Used in histogram Extension.BackgroundPageType. Values may be added, as
// long as existing values are not changed.
enum BackgroundPageType {
  NO_BACKGROUND_PAGE = 0,
  BACKGROUND_PAGE_PERSISTENT = 1,
  EVENT_PAGE = 2,
};

64 65 66 67 68 69 70 71 72 73 74 75
// Used in histogram Extensions.ExternalItemState. Values may be added, as
// long as existing values are not changed.
enum ExternalItemState {
  DEPRECATED_EXTERNAL_ITEM_DISABLED = 0,
  DEPRECATED_EXTERNAL_ITEM_ENABLED = 1,
  EXTERNAL_ITEM_WEBSTORE_DISABLED = 2,
  EXTERNAL_ITEM_WEBSTORE_ENABLED = 3,
  EXTERNAL_ITEM_NONWEBSTORE_DISABLED = 4,
  EXTERNAL_ITEM_NONWEBSTORE_ENABLED = 5,
  EXTERNAL_ITEM_MAX_ITEMS = 6
};

76 77 78
ManifestReloadReason ShouldReloadExtensionManifest(const ExtensionInfo& info) {
  // Always reload manifests of unpacked extensions, because they can change
  // on disk independent of the manifest in our prefs.
79
  if (Manifest::IsUnpackedLocation(info.extension_location))
80 81 82
    return UNPACKED_DIR;

  // Reload the manifest if it needs to be relocalized.
83 84
  if (extension_l10n_util::ShouldRelocalizeManifest(
          info.extension_manifest.get()))
85 86 87 88 89
    return NEEDS_RELOCALIZATION;

  return NOT_NEEDED;
}

90 91 92 93 94 95 96 97
BackgroundPageType GetBackgroundPageType(const Extension* extension) {
  if (!BackgroundInfo::HasBackgroundPage(extension))
    return NO_BACKGROUND_PAGE;
  if (BackgroundInfo::HasPersistentBackgroundPage(extension))
    return BACKGROUND_PAGE_PERSISTENT;
  return EVENT_PAGE;
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
void DispatchOnInstalledEvent(
    Profile* profile,
    const std::string& extension_id,
    const Version& old_version,
    bool chrome_updated) {
  // profile manager can be NULL in unit tests.
  if (!g_browser_process->profile_manager())
    return;
  if (!g_browser_process->profile_manager()->IsValidProfile(profile))
    return;

  extensions::RuntimeEventRouter::DispatchOnInstalledEvent(
      profile, extension_id, old_version, chrome_updated);
}

}  // namespace

InstalledLoader::InstalledLoader(ExtensionService* extension_service)
    : extension_service_(extension_service),
      extension_prefs_(extension_service->extension_prefs()) {
}

InstalledLoader::~InstalledLoader() {
}

void InstalledLoader::Load(const ExtensionInfo& info, bool write_to_prefs) {
  std::string error;
  scoped_refptr<const Extension> extension(NULL);
126
  if (info.extension_manifest) {
127 128 129 130 131 132 133 134 135 136 137 138 139
    extension = Extension::Create(
        info.extension_path,
        info.extension_location,
        *info.extension_manifest,
        GetCreationFlags(&info),
        &error);
  } else {
    error = errors::kManifestUnreadable;
  }

  // Once installed, non-unpacked extensions cannot change their IDs (e.g., by
  // updating the 'key' field in their manifest).
  // TODO(jstritar): migrate preferences when unpacked extensions change IDs.
140
  if (extension.get() && !Manifest::IsUnpackedLocation(extension->location()) &&
141 142 143 144 145 146 147 148 149 150
      info.extension_id != extension->id()) {
    error = errors::kCannotChangeExtensionID;
    extension = NULL;
    content::RecordAction(UserMetricsAction("Extensions.IDChangedError"));
  }

  // Check policy on every load in case an extension was blacklisted while
  // Chrome was not running.
  const ManagementPolicy* policy = extensions::ExtensionSystem::Get(
      extension_service_->profile())->management_policy();
151
  if (extension.get() && !policy->UserMayLoad(extension.get(), NULL)) {
152 153 154 155 156 157
    // The error message from UserMayInstall() often contains the extension ID
    // and is therefore not well suited to this UI.
    error = errors::kDisabledByPolicy;
    extension = NULL;
  }

158 159 160
  if (!extension.get()) {
    extension_service_->ReportExtensionLoadError(
        info.extension_path, error, false);
161 162 163 164
    return;
  }

  if (write_to_prefs)
165
    extension_prefs_->UpdateManifest(extension.get());
166

167
  extension_service_->AddExtension(extension.get());
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
}

void InstalledLoader::LoadAllExtensions() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  base::TimeTicks start_time = base::TimeTicks::Now();

  scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
      extension_prefs_->GetInstalledExtensionsInfo());

  std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0);
  bool should_write_prefs = false;

  for (size_t i = 0; i < extensions_info->size(); ++i) {
    ExtensionInfo* info = extensions_info->at(i).get();

184 185 186 187 188
    // Skip extensions that were loaded from the command-line because we don't
    // want those to persist across browser restart.
    if (info->extension_location == Manifest::COMMAND_LINE)
      continue;

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
    ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info);
    ++reload_reason_counts[reload_reason];
    UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestReloadEnumValue",
                              reload_reason, 100);

    if (reload_reason != NOT_NEEDED) {
      // Reloading an extension reads files from disk.  We do this on the
      // UI thread because reloads should be very rare, and the complexity
      // added by delaying the time when the extensions service knows about
      // all extensions is significant.  See crbug.com/37548 for details.
      // |allow_io| disables tests that file operations run on the file
      // thread.
      base::ThreadRestrictions::ScopedAllowIO allow_io;

      std::string error;
      scoped_refptr<const Extension> extension(
          extension_file_util::LoadExtension(
              info->extension_path,
              info->extension_location,
              GetCreationFlags(info),
              &error));

211 212 213
      if (!extension.get()) {
        extension_service_->ReportExtensionLoadError(
            info->extension_path, error, false);
214 215 216 217 218 219 220 221 222 223 224
        continue;
      }

      extensions_info->at(i)->extension_manifest.reset(
          static_cast<DictionaryValue*>(
              extension->manifest()->value()->DeepCopy()));
      should_write_prefs = true;
    }
  }

  for (size_t i = 0; i < extensions_info->size(); ++i) {
225 226
    if (extensions_info->at(i)->extension_location == Manifest::COMMAND_LINE)
      continue;
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
    Load(*extensions_info->at(i), should_write_prefs);
  }

  extension_service_->OnLoadedInstalledExtensions();

  // The histograms Extensions.ManifestReload* allow us to validate
  // the assumption that reloading manifest is a rare event.
  UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNotNeeded",
                           reload_reason_counts[NOT_NEEDED]);
  UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadUnpackedDir",
                           reload_reason_counts[UNPACKED_DIR]);
  UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNeedsRelocalization",
                           reload_reason_counts[NEEDS_RELOCALIZATION]);

  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll",
                           extension_service_->extensions()->size());
  UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled",
                           extension_service_->disabled_extensions()->size());

  UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
                      base::TimeTicks::Now() - start_time);

  int app_user_count = 0;
  int app_external_count = 0;
  int hosted_app_count = 0;
  int legacy_packaged_app_count = 0;
253
  int platform_app_count = 0;
254
  int user_script_count = 0;
255
  int content_pack_count = 0;
256 257 258 259 260 261 262
  int extension_user_count = 0;
  int extension_external_count = 0;
  int theme_count = 0;
  int page_action_count = 0;
  int browser_action_count = 0;
  int disabled_for_permissions_count = 0;
  int item_user_count = 0;
263
  int non_webstore_ntp_override_count = 0;
264 265 266
  const ExtensionSet* extensions = extension_service_->extensions();
  ExtensionSet::const_iterator ex;
  for (ex = extensions->begin(); ex != extensions->end(); ++ex) {
267 268
    Manifest::Location location = (*ex)->location();
    Manifest::Type type = (*ex)->GetType();
269 270 271
    if ((*ex)->is_app()) {
      UMA_HISTOGRAM_ENUMERATION("Extensions.AppLocation",
                                location, 100);
272
    } else if (type == Manifest::TYPE_EXTENSION) {
273 274 275
      UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionLocation",
                                location, 100);
    }
276 277 278 279 280 281
    if (!ManifestURL::UpdatesFromGallery(*ex)) {
      UMA_HISTOGRAM_ENUMERATION("Extensions.NonWebstoreLocation",
                                location, 100);
    }
    if (Manifest::IsExternalLocation(location)) {
      // See loop below for DISABLED.
282 283 284 285 286 287 288 289 290
      if (ManifestURL::UpdatesFromGallery(*ex)) {
        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
                                  EXTERNAL_ITEM_WEBSTORE_ENABLED,
                                  EXTERNAL_ITEM_MAX_ITEMS);
      } else {
        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
                                  EXTERNAL_ITEM_NONWEBSTORE_ENABLED,
                                  EXTERNAL_ITEM_MAX_ITEMS);
      }
291 292 293 294 295 296
    }
    if ((*ex)->from_webstore()) {
      // Check for inconsistencies if the extension was supposedly installed
      // from the webstore.
      enum {
        BAD_UPDATE_URL = 0,
297 298 299
        // This value was a mistake. Turns out sideloaded extensions can
        // have the from_webstore bit if they update from the webstore.
        DEPRECATED_IS_EXTERNAL = 1,
300 301 302 303 304 305
      };
      if (!ManifestURL::UpdatesFromGallery(*ex)) {
        UMA_HISTOGRAM_ENUMERATION("Extensions.FromWebstoreInconsistency",
                                  BAD_UPDATE_URL, 2);
      }
    }
306 307 308

    // Don't count component extensions, since they are only extensions as an
    // implementation detail.
309
    if (location == Manifest::COMPONENT)
310
      continue;
311 312 313 314 315 316 317 318 319
    // Histogram for non-webstore extensions overriding new tab page should
    // include unpacked extensions.
    if (!(*ex)->from_webstore()) {
      const extensions::URLOverrides::URLOverrideMap& override_map =
          extensions::URLOverrides::GetChromeURLOverrides(ex->get());
      if (override_map.find("newtab") != override_map.end()) {
        ++non_webstore_ntp_override_count;
      }
    }
320 321 322

    // Don't count unpacked extensions, since they're a developer-specific
    // feature.
323
    if (Manifest::IsUnpackedLocation(location))
324 325
      continue;

326 327 328 329
    UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestVersion",
                              (*ex)->manifest_version(), 10);

    if (type == Manifest::TYPE_EXTENSION) {
330 331 332 333
      BackgroundPageType background_page_type =
          GetBackgroundPageType(ex->get());
      UMA_HISTOGRAM_ENUMERATION(
          "Extensions.BackgroundPageType", background_page_type, 10);
334 335
    }

336 337 338 339 340
    // Using an enumeration shows us the total installed ratio across all users.
    // Using the totals per user at each startup tells us the distribution of
    // usage for each user (e.g. 40% of users have at least one app installed).
    UMA_HISTOGRAM_ENUMERATION("Extensions.LoadType", type, 100);
    switch (type) {
341
      case Manifest::TYPE_THEME:
342 343
        ++theme_count;
        break;
344
      case Manifest::TYPE_USER_SCRIPT:
345 346
        ++user_script_count;
        break;
347
      case Manifest::TYPE_HOSTED_APP:
348
        ++hosted_app_count;
349
        if (Manifest::IsExternalLocation(location)) {
350 351 352 353 354
          ++app_external_count;
        } else {
          ++app_user_count;
        }
        break;
355
      case Manifest::TYPE_LEGACY_PACKAGED_APP:
356
        ++legacy_packaged_app_count;
357
        if (Manifest::IsExternalLocation(location)) {
358 359 360 361 362
          ++app_external_count;
        } else {
          ++app_user_count;
        }
        break;
363 364 365 366 367 368 369 370 371
      case Manifest::TYPE_PLATFORM_APP:
        ++platform_app_count;
        if (Manifest::IsExternalLocation(location)) {
          ++app_external_count;
        } else {
          ++app_user_count;
        }
        break;
      case Manifest::TYPE_EXTENSION:
372
      default:
373
        if (Manifest::IsExternalLocation(location)) {
374 375 376 377 378 379
          ++extension_external_count;
        } else {
          ++extension_user_count;
        }
        break;
    }
380
    if (!Manifest::IsExternalLocation((*ex)->location()))
381 382 383
      ++item_user_count;
    ExtensionActionManager* extension_action_manager =
        ExtensionActionManager::Get(extension_service_->profile());
384
    if (extension_action_manager->GetPageAction(*ex->get()))
385
      ++page_action_count;
386
    if (extension_action_manager->GetBrowserAction(*ex->get()))
387 388
      ++browser_action_count;

389
    if (extensions::ManagedModeInfo::IsContentPack(ex->get()))
390 391
      ++content_pack_count;

392
    extension_service_->RecordPermissionMessagesHistogram(
393
        ex->get(), "Extensions.Permissions_Load");
394 395 396 397 398 399 400 401 402
  }
  const ExtensionSet* disabled_extensions =
      extension_service_->disabled_extensions();
  for (ex = disabled_extensions->begin();
       ex != disabled_extensions->end(); ++ex) {
    if (extension_service_->extension_prefs()->
        DidExtensionEscalatePermissions((*ex)->id())) {
      ++disabled_for_permissions_count;
    }
403 404
    if (Manifest::IsExternalLocation((*ex)->location())) {
      // See loop above for ENABLED.
405 406 407 408 409 410 411 412 413
      if (ManifestURL::UpdatesFromGallery(*ex)) {
        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
                                  EXTERNAL_ITEM_WEBSTORE_DISABLED,
                                  EXTERNAL_ITEM_MAX_ITEMS);
      } else {
        UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
                                  EXTERNAL_ITEM_NONWEBSTORE_DISABLED,
                                  EXTERNAL_ITEM_MAX_ITEMS);
      }
414
    }
415 416 417 418 419 420 421 422 423 424
  }

  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAllUser", item_user_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp",
                           app_user_count + app_external_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppUser", app_user_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppExternal", app_external_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPackagedApp",
                           legacy_packaged_app_count);
425
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPlatformApp", platform_app_count);
426 427 428 429 430 431 432 433 434 435 436
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension",
                           extension_user_count + extension_external_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionUser",
                           extension_user_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionExternal",
                           extension_external_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
                           browser_action_count);
437
  UMA_HISTOGRAM_COUNTS_100("Extensions.LoadContentPack", content_pack_count);
438 439
  UMA_HISTOGRAM_COUNTS_100("Extensions.DisabledForPermissions",
                           disabled_for_permissions_count);
440 441
  UMA_HISTOGRAM_COUNTS_100("Extensions.NonWebStoreNewTabPageOverrides",
                           non_webstore_ntp_override_count);
442 443 444 445
}

int InstalledLoader::GetCreationFlags(const ExtensionInfo* info) {
  int flags = extension_prefs_->GetCreationFlags(info->extension_id);
446
  if (!Manifest::IsUnpackedLocation(info->extension_location))
447 448 449 450 451 452 453
    flags |= Extension::REQUIRE_KEY;
  if (extension_prefs_->AllowFileAccess(info->extension_id))
    flags |= Extension::ALLOW_FILE_ACCESS;
  return flags;
}

}  // namespace extensions