Home | History | Annotate | Download | only in extensions
      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/crx_installer.h"
      6 
      7 #include <map>
      8 #include <set>
      9 
     10 #include "base/bind.h"
     11 #include "base/files/file_util.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/lazy_instance.h"
     14 #include "base/metrics/histogram.h"
     15 #include "base/path_service.h"
     16 #include "base/sequenced_task_runner.h"
     17 #include "base/strings/string_util.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "base/threading/sequenced_worker_pool.h"
     21 #include "base/threading/thread_restrictions.h"
     22 #include "base/time/time.h"
     23 #include "base/version.h"
     24 #include "chrome/browser/extensions/convert_user_script.h"
     25 #include "chrome/browser/extensions/convert_web_app.h"
     26 #include "chrome/browser/extensions/crx_installer_error.h"
     27 #include "chrome/browser/extensions/extension_assets_manager.h"
     28 #include "chrome/browser/extensions/extension_error_reporter.h"
     29 #include "chrome/browser/extensions/extension_install_ui.h"
     30 #include "chrome/browser/extensions/extension_service.h"
     31 #include "chrome/browser/extensions/install_tracker.h"
     32 #include "chrome/browser/extensions/install_tracker_factory.h"
     33 #include "chrome/browser/extensions/permissions_updater.h"
     34 #include "chrome/browser/extensions/webstore_installer.h"
     35 #include "chrome/browser/profiles/profile.h"
     36 #include "chrome/browser/web_applications/web_app.h"
     37 #include "chrome/common/chrome_paths.h"
     38 #include "chrome/common/extensions/extension_constants.h"
     39 #include "chrome/common/extensions/manifest_url_handler.h"
     40 #include "chrome/grit/generated_resources.h"
     41 #include "content/public/browser/browser_thread.h"
     42 #include "content/public/browser/notification_service.h"
     43 #include "content/public/browser/resource_dispatcher_host.h"
     44 #include "content/public/browser/user_metrics.h"
     45 #include "extensions/browser/extension_prefs.h"
     46 #include "extensions/browser/extension_system.h"
     47 #include "extensions/browser/install_flag.h"
     48 #include "extensions/browser/notification_types.h"
     49 #include "extensions/common/extension_icon_set.h"
     50 #include "extensions/common/feature_switch.h"
     51 #include "extensions/common/file_util.h"
     52 #include "extensions/common/manifest.h"
     53 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
     54 #include "extensions/common/manifest_handlers/shared_module_info.h"
     55 #include "extensions/common/permissions/permission_message_provider.h"
     56 #include "extensions/common/permissions/permission_set.h"
     57 #include "extensions/common/permissions/permissions_data.h"
     58 #include "extensions/common/user_script.h"
     59 #include "grit/extensions_strings.h"
     60 #include "third_party/skia/include/core/SkBitmap.h"
     61 #include "ui/base/l10n/l10n_util.h"
     62 
     63 #if defined(OS_CHROMEOS)
     64 #include "components/user_manager/user_manager.h"
     65 #endif
     66 
     67 using base::UserMetricsAction;
     68 using content::BrowserThread;
     69 using extensions::SharedModuleInfo;
     70 
     71 namespace extensions {
     72 
     73 namespace {
     74 
     75 // Used in histograms; do not change order.
     76 enum OffStoreInstallDecision {
     77   OnStoreInstall,
     78   OffStoreInstallAllowed,
     79   OffStoreInstallDisallowed,
     80   NumOffStoreInstallDecision
     81 };
     82 
     83 }  // namespace
     84 
     85 // static
     86 scoped_refptr<CrxInstaller> CrxInstaller::CreateSilent(
     87     ExtensionService* frontend) {
     88   return new CrxInstaller(frontend->AsWeakPtr(),
     89                           scoped_ptr<ExtensionInstallPrompt>(),
     90                           NULL);
     91 }
     92 
     93 // static
     94 scoped_refptr<CrxInstaller> CrxInstaller::Create(
     95     ExtensionService* frontend,
     96     scoped_ptr<ExtensionInstallPrompt> client) {
     97   return new CrxInstaller(frontend->AsWeakPtr(), client.Pass(), NULL);
     98 }
     99 
    100 // static
    101 scoped_refptr<CrxInstaller> CrxInstaller::Create(
    102     ExtensionService* service,
    103     scoped_ptr<ExtensionInstallPrompt> client,
    104     const WebstoreInstaller::Approval* approval) {
    105   return new CrxInstaller(service->AsWeakPtr(), client.Pass(), approval);
    106 }
    107 
    108 CrxInstaller::CrxInstaller(base::WeakPtr<ExtensionService> service_weak,
    109                            scoped_ptr<ExtensionInstallPrompt> client,
    110                            const WebstoreInstaller::Approval* approval)
    111     : install_directory_(service_weak->install_directory()),
    112       install_source_(Manifest::INTERNAL),
    113       approved_(false),
    114       expected_manifest_check_level_(
    115           WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT),
    116       expected_version_strict_checking_(false),
    117       extensions_enabled_(service_weak->extensions_enabled()),
    118       delete_source_(false),
    119       create_app_shortcut_(false),
    120       service_weak_(service_weak),
    121       // See header file comment on |client_| for why we use a raw pointer here.
    122       client_(client.release()),
    123       apps_require_extension_mime_type_(false),
    124       allow_silent_install_(false),
    125       grant_permissions_(true),
    126       install_cause_(extension_misc::INSTALL_CAUSE_UNSET),
    127       creation_flags_(Extension::NO_FLAGS),
    128       off_store_install_allow_reason_(OffStoreInstallDisallowed),
    129       did_handle_successfully_(true),
    130       error_on_unsupported_requirements_(false),
    131       update_from_settings_page_(false),
    132       install_flags_(kInstallFlagNone),
    133       install_checker_(service_weak->profile()) {
    134   installer_task_runner_ = service_weak->GetFileTaskRunner();
    135   if (!approval)
    136     return;
    137 
    138   CHECK(profile()->IsSameProfile(approval->profile));
    139   if (client_) {
    140     client_->install_ui()->SetUseAppInstalledBubble(
    141         approval->use_app_installed_bubble);
    142     client_->install_ui()->set_skip_post_install_ui(
    143         approval->skip_post_install_ui);
    144   }
    145 
    146   if (approval->skip_install_dialog) {
    147     // Mark the extension as approved, but save the expected manifest and ID
    148     // so we can check that they match the CRX's.
    149     approved_ = true;
    150     expected_manifest_check_level_ = approval->manifest_check_level;
    151     if (expected_manifest_check_level_ !=
    152         WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE)
    153       expected_manifest_.reset(approval->manifest->DeepCopy());
    154     expected_id_ = approval->extension_id;
    155   }
    156   if (approval->minimum_version.get()) {
    157     expected_version_.reset(new Version(*approval->minimum_version));
    158     expected_version_strict_checking_ = false;
    159   }
    160 
    161   show_dialog_callback_ = approval->show_dialog_callback;
    162   set_is_ephemeral(approval->is_ephemeral);
    163 }
    164 
    165 CrxInstaller::~CrxInstaller() {
    166   // Make sure the UI is deleted on the ui thread.
    167   if (client_) {
    168     BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_);
    169     client_ = NULL;
    170   }
    171 }
    172 
    173 void CrxInstaller::InstallCrx(const base::FilePath& source_file) {
    174   ExtensionService* service = service_weak_.get();
    175   if (!service || service->browser_terminating())
    176     return;
    177 
    178   NotifyCrxInstallBegin();
    179 
    180   source_file_ = source_file;
    181 
    182   scoped_refptr<SandboxedUnpacker> unpacker(
    183       new SandboxedUnpacker(source_file,
    184                             install_source_,
    185                             creation_flags_,
    186                             install_directory_,
    187                             installer_task_runner_.get(),
    188                             this));
    189 
    190   if (!installer_task_runner_->PostTask(
    191           FROM_HERE,
    192           base::Bind(&SandboxedUnpacker::Start, unpacker.get())))
    193     NOTREACHED();
    194 }
    195 
    196 void CrxInstaller::InstallUserScript(const base::FilePath& source_file,
    197                                      const GURL& download_url) {
    198   DCHECK(!download_url.is_empty());
    199 
    200   NotifyCrxInstallBegin();
    201 
    202   source_file_ = source_file;
    203   download_url_ = download_url;
    204 
    205   if (!installer_task_runner_->PostTask(
    206           FROM_HERE,
    207           base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread, this)))
    208     NOTREACHED();
    209 }
    210 
    211 void CrxInstaller::ConvertUserScriptOnFileThread() {
    212   base::string16 error;
    213   scoped_refptr<Extension> extension = ConvertUserScriptToExtension(
    214       source_file_, download_url_, install_directory_, &error);
    215   if (!extension.get()) {
    216     ReportFailureFromFileThread(CrxInstallerError(error));
    217     return;
    218   }
    219 
    220   OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
    221                   SkBitmap());
    222 }
    223 
    224 void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
    225   NotifyCrxInstallBegin();
    226 
    227   if (!installer_task_runner_->PostTask(
    228           FROM_HERE,
    229           base::Bind(&CrxInstaller::ConvertWebAppOnFileThread, this, web_app)))
    230     NOTREACHED();
    231 }
    232 
    233 void CrxInstaller::ConvertWebAppOnFileThread(
    234     const WebApplicationInfo& web_app) {
    235   scoped_refptr<Extension> extension(ConvertWebAppToExtension(
    236       web_app, base::Time::Now(), install_directory_));
    237   if (!extension.get()) {
    238     // Validation should have stopped any potential errors before getting here.
    239     NOTREACHED() << "Could not convert web app to extension.";
    240     return;
    241   }
    242 
    243   // TODO(aa): conversion data gets lost here :(
    244 
    245   OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
    246                   SkBitmap());
    247 }
    248 
    249 CrxInstallerError CrxInstaller::AllowInstall(const Extension* extension) {
    250   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    251 
    252   // Make sure the expected ID matches if one was supplied or if we want to
    253   // bypass the prompt.
    254   if ((approved_ || !expected_id_.empty()) &&
    255       expected_id_ != extension->id()) {
    256     return CrxInstallerError(
    257         l10n_util::GetStringFUTF16(IDS_EXTENSION_INSTALL_UNEXPECTED_ID,
    258                                    base::ASCIIToUTF16(expected_id_),
    259                                    base::ASCIIToUTF16(extension->id())));
    260   }
    261 
    262   if (expected_version_.get()) {
    263     if (expected_version_strict_checking_) {
    264       if (!expected_version_->Equals(*extension->version())) {
    265         return CrxInstallerError(
    266             l10n_util::GetStringFUTF16(
    267               IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
    268               base::ASCIIToUTF16(expected_version_->GetString()),
    269               base::ASCIIToUTF16(extension->version()->GetString())));
    270       }
    271     } else {
    272       if (extension->version()->CompareTo(*expected_version_) < 0) {
    273         return CrxInstallerError(
    274             l10n_util::GetStringFUTF16(
    275               IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
    276               base::ASCIIToUTF16(expected_version_->GetString() + "+"),
    277               base::ASCIIToUTF16(extension->version()->GetString())));
    278       }
    279     }
    280   }
    281 
    282   // Make sure the manifests match if we want to bypass the prompt.
    283   if (approved_) {
    284     bool valid = false;
    285     if (expected_manifest_check_level_ ==
    286         WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE) {
    287         // To skip manifest checking, the extension must be a shared module
    288         // and not request any permissions.
    289       if (SharedModuleInfo::IsSharedModule(extension) &&
    290           extension->permissions_data()->active_permissions()->IsEmpty()) {
    291           valid = true;
    292         }
    293     } else {
    294       valid = expected_manifest_->Equals(original_manifest_.get());
    295       if (!valid && expected_manifest_check_level_ ==
    296           WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE) {
    297         std::string error;
    298         scoped_refptr<Extension> dummy_extension =
    299             Extension::Create(base::FilePath(),
    300                               install_source_,
    301                               *expected_manifest_->value(),
    302                               creation_flags_,
    303                               &error);
    304         if (error.empty()) {
    305           scoped_refptr<const PermissionSet> expected_permissions =
    306               dummy_extension->permissions_data()->active_permissions();
    307           valid = !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
    308               expected_permissions.get(),
    309               extension->permissions_data()->active_permissions().get(),
    310               extension->GetType()));
    311         }
    312       }
    313     }
    314 
    315     if (!valid)
    316       return CrxInstallerError(
    317           l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID));
    318   }
    319 
    320   // The checks below are skipped for themes and external installs.
    321   // TODO(pamg): After ManagementPolicy refactoring is complete, remove this
    322   // and other uses of install_source_ that are no longer needed now that the
    323   // SandboxedUnpacker sets extension->location.
    324   if (extension->is_theme() || Manifest::IsExternalLocation(install_source_))
    325     return CrxInstallerError();
    326 
    327   if (!extensions_enabled_) {
    328     return CrxInstallerError(
    329         l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_NOT_ENABLED));
    330   }
    331 
    332   if (install_cause_ == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD) {
    333     if (FeatureSwitch::easy_off_store_install()->IsEnabled()) {
    334       const char* kHistogramName = "Extensions.OffStoreInstallDecisionEasy";
    335       if (is_gallery_install()) {
    336         UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
    337                                   NumOffStoreInstallDecision);
    338       } else {
    339         UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
    340                                   NumOffStoreInstallDecision);
    341       }
    342     } else {
    343       const char* kHistogramName = "Extensions.OffStoreInstallDecisionHard";
    344       if (is_gallery_install()) {
    345         UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
    346                                   NumOffStoreInstallDecision);
    347       } else if (off_store_install_allow_reason_ != OffStoreInstallDisallowed) {
    348         UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
    349                                   NumOffStoreInstallDecision);
    350         UMA_HISTOGRAM_ENUMERATION("Extensions.OffStoreInstallAllowReason",
    351                                   off_store_install_allow_reason_,
    352                                   NumOffStoreInstallAllowReasons);
    353       } else {
    354         UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallDisallowed,
    355                                   NumOffStoreInstallDecision);
    356         // Don't delete source in this case so that the user can install
    357         // manually if they want.
    358         delete_source_ = false;
    359         did_handle_successfully_ = false;
    360 
    361         return CrxInstallerError(
    362             CrxInstallerError::ERROR_OFF_STORE,
    363             l10n_util::GetStringUTF16(
    364                 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE));
    365       }
    366     }
    367   }
    368 
    369   if (install_checker_.extension()->is_app()) {
    370     // If the app was downloaded, apps_require_extension_mime_type_
    371     // will be set.  In this case, check that it was served with the
    372     // right mime type.  Make an exception for file URLs, which come
    373     // from the users computer and have no headers.
    374     if (!download_url_.SchemeIsFile() &&
    375         apps_require_extension_mime_type_ &&
    376         original_mime_type_ != Extension::kMimeType) {
    377       return CrxInstallerError(
    378           l10n_util::GetStringFUTF16(
    379               IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE,
    380               base::ASCIIToUTF16(Extension::kMimeType)));
    381     }
    382 
    383     // If the client_ is NULL, then the app is either being installed via
    384     // an internal mechanism like sync, external_extensions, or default apps.
    385     // In that case, we don't want to enforce things like the install origin.
    386     if (!is_gallery_install() && client_) {
    387       // For apps with a gallery update URL, require that they be installed
    388       // from the gallery.
    389       // TODO(erikkay) Apply this rule for paid extensions and themes as well.
    390       if (ManifestURL::UpdatesFromGallery(extension)) {
    391         return CrxInstallerError(
    392             l10n_util::GetStringFUTF16(
    393                 IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS,
    394                 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
    395       }
    396 
    397       // For self-hosted apps, verify that the entire extent is on the same
    398       // host (or a subdomain of the host) the download happened from.  There's
    399       // no way for us to verify that the app controls any other hosts.
    400       URLPattern pattern(UserScript::ValidUserScriptSchemes());
    401       pattern.SetHost(download_url_.host());
    402       pattern.SetMatchSubdomains(true);
    403 
    404       URLPatternSet patterns = install_checker_.extension()->web_extent();
    405       for (URLPatternSet::const_iterator i = patterns.begin();
    406            i != patterns.end(); ++i) {
    407         if (!pattern.MatchesHost(i->host())) {
    408           return CrxInstallerError(
    409               l10n_util::GetStringUTF16(
    410                   IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST));
    411         }
    412       }
    413     }
    414   }
    415 
    416   return CrxInstallerError();
    417 }
    418 
    419 void CrxInstaller::OnUnpackFailure(const base::string16& error_message) {
    420   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    421 
    422   UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallSource",
    423                             install_source(), Manifest::NUM_LOCATIONS);
    424 
    425   UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallCause",
    426                             install_cause(),
    427                             extension_misc::NUM_INSTALL_CAUSES);
    428 
    429   ReportFailureFromFileThread(CrxInstallerError(error_message));
    430 }
    431 
    432 void CrxInstaller::OnUnpackSuccess(
    433     const base::FilePath& temp_dir,
    434     const base::FilePath& extension_dir,
    435     const base::DictionaryValue* original_manifest,
    436     const Extension* extension,
    437     const SkBitmap& install_icon) {
    438   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    439 
    440   UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
    441                             install_source(), Manifest::NUM_LOCATIONS);
    442 
    443 
    444   UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
    445                             install_cause(),
    446                             extension_misc::NUM_INSTALL_CAUSES);
    447 
    448   install_checker_.set_extension(extension);
    449   temp_dir_ = temp_dir;
    450   if (!install_icon.empty())
    451     install_icon_.reset(new SkBitmap(install_icon));
    452 
    453   if (original_manifest)
    454     original_manifest_.reset(new Manifest(
    455         Manifest::INVALID_LOCATION,
    456         scoped_ptr<base::DictionaryValue>(original_manifest->DeepCopy())));
    457 
    458   // We don't have to delete the unpack dir explicity since it is a child of
    459   // the temp dir.
    460   unpacked_extension_root_ = extension_dir;
    461 
    462   CrxInstallerError error = AllowInstall(extension);
    463   if (error.type() != CrxInstallerError::ERROR_NONE) {
    464     ReportFailureFromFileThread(error);
    465     return;
    466   }
    467 
    468   if (!BrowserThread::PostTask(BrowserThread::UI,
    469                                FROM_HERE,
    470                                base::Bind(&CrxInstaller::CheckInstall, this)))
    471     NOTREACHED();
    472 }
    473 
    474 void CrxInstaller::CheckInstall() {
    475   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    476   ExtensionService* service = service_weak_.get();
    477   if (!service || service->browser_terminating())
    478     return;
    479 
    480   if (SharedModuleInfo::ImportsModules(extension())) {
    481     const std::vector<SharedModuleInfo::ImportInfo>& imports =
    482         SharedModuleInfo::GetImports(extension());
    483     std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
    484     for (i = imports.begin(); i != imports.end(); ++i) {
    485       const Extension* imported_module =
    486           service->GetExtensionById(i->extension_id, true);
    487       if (imported_module &&
    488           !SharedModuleInfo::IsSharedModule(imported_module)) {
    489         ReportFailureFromUIThread(
    490             CrxInstallerError(l10n_util::GetStringFUTF16(
    491                 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE,
    492                 base::ASCIIToUTF16(i->extension_id))));
    493         return;
    494       } else if (imported_module &&
    495           !SharedModuleInfo::IsExportAllowedByWhitelist(imported_module,
    496                                                         extension()->id())) {
    497         ReportFailureFromUIThread(CrxInstallerError(l10n_util::GetStringFUTF16(
    498             IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED,
    499             base::UTF8ToUTF16(extension()->name()),
    500             base::UTF8ToUTF16(imported_module->name()))));
    501         return;
    502       }
    503     }
    504   }
    505 
    506   // Run the policy, requirements and blacklist checks in parallel.
    507   install_checker_.Start(
    508       ExtensionInstallChecker::CHECK_ALL,
    509       false /* fail fast */,
    510       base::Bind(&CrxInstaller::OnInstallChecksComplete, this));
    511 }
    512 
    513 void CrxInstaller::OnInstallChecksComplete(int failed_checks) {
    514   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    515   if (!service_weak_)
    516     return;
    517 
    518   // Check for requirement errors.
    519   if (!install_checker_.requirement_errors().empty()) {
    520     if (error_on_unsupported_requirements_) {
    521       ReportFailureFromUIThread(CrxInstallerError(base::UTF8ToUTF16(
    522           JoinString(install_checker_.requirement_errors(), ' '))));
    523       return;
    524     }
    525     install_flags_ |= kInstallFlagHasRequirementErrors;
    526   }
    527 
    528   // Check the blacklist state.
    529   if (install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE) {
    530     install_flags_ |= kInstallFlagIsBlacklistedForMalware;
    531   }
    532 
    533   if ((install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE ||
    534        install_checker_.blacklist_state() == extensions::BLACKLISTED_UNKNOWN) &&
    535       !allow_silent_install_) {
    536     // User tried to install a blacklisted extension. Show an error and
    537     // refuse to install it.
    538     ReportFailureFromUIThread(extensions::CrxInstallerError(
    539         l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED,
    540                                    base::UTF8ToUTF16(extension()->name()))));
    541     UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
    542                               extension()->location(),
    543                               Manifest::NUM_LOCATIONS);
    544     return;
    545   }
    546 
    547   // NOTE: extension may still be blacklisted, but we're forced to silently
    548   // install it. In this case, ExtensionService::OnExtensionInstalled needs to
    549   // deal with it.
    550 
    551   // Check for policy errors.
    552   if (!install_checker_.policy_error().empty()) {
    553     // We don't want to show the error infobar for installs from the WebStore,
    554     // because the WebStore already shows an error dialog itself.
    555     // Note: |client_| can be NULL in unit_tests!
    556     if (extension()->from_webstore() && client_)
    557       client_->install_ui()->set_skip_post_install_ui(true);
    558     ReportFailureFromUIThread(
    559         CrxInstallerError(base::UTF8ToUTF16(install_checker_.policy_error())));
    560     return;
    561   }
    562 
    563   ConfirmInstall();
    564 }
    565 
    566 void CrxInstaller::ConfirmInstall() {
    567   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    568   ExtensionService* service = service_weak_.get();
    569   if (!service || service->browser_terminating())
    570     return;
    571 
    572   if (KioskModeInfo::IsKioskOnly(install_checker_.extension().get())) {
    573     bool in_kiosk_mode = false;
    574 #if defined(OS_CHROMEOS)
    575     user_manager::UserManager* user_manager = user_manager::UserManager::Get();
    576     in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
    577 #endif
    578     if (!in_kiosk_mode) {
    579       ReportFailureFromUIThread(CrxInstallerError(
    580           l10n_util::GetStringUTF16(
    581               IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY)));
    582       return;
    583     }
    584   }
    585 
    586   // Check whether this install is initiated from the settings page to
    587   // update an existing extension or app.
    588   CheckUpdateFromSettingsPage();
    589 
    590   GURL overlapping_url;
    591   const Extension* overlapping_extension =
    592       service->extensions()->GetHostedAppByOverlappingWebExtent(
    593           extension()->web_extent());
    594   if (overlapping_extension &&
    595       overlapping_extension->id() != extension()->id()) {
    596     ReportFailureFromUIThread(
    597         CrxInstallerError(
    598             l10n_util::GetStringFUTF16(
    599                 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
    600                 base::UTF8ToUTF16(overlapping_extension->name()))));
    601     return;
    602   }
    603 
    604   current_version_ = ExtensionPrefs::Get(service->profile())
    605                          ->GetVersionString(extension()->id());
    606 
    607   if (client_ &&
    608       (!allow_silent_install_ || !approved_) &&
    609       !update_from_settings_page_) {
    610     AddRef();  // Balanced in InstallUIProceed() and InstallUIAbort().
    611     client_->ConfirmInstall(this, extension(), show_dialog_callback_);
    612   } else {
    613     if (!installer_task_runner_->PostTask(
    614             FROM_HERE,
    615             base::Bind(&CrxInstaller::CompleteInstall, this)))
    616       NOTREACHED();
    617   }
    618   return;
    619 }
    620 
    621 void CrxInstaller::InstallUIProceed() {
    622   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    623 
    624   ExtensionService* service = service_weak_.get();
    625   if (!service || service->browser_terminating())
    626     return;
    627 
    628   // If update_from_settings_page_ boolean is true, this functions is
    629   // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
    630   // and if it is false, this function is called in response to
    631   // ExtensionInstallPrompt::ConfirmInstall().
    632   if (update_from_settings_page_) {
    633     service->GrantPermissionsAndEnableExtension(extension());
    634   } else {
    635     if (!installer_task_runner_->PostTask(
    636             FROM_HERE,
    637             base::Bind(&CrxInstaller::CompleteInstall, this)))
    638       NOTREACHED();
    639   }
    640 
    641   Release();  // balanced in ConfirmInstall() or ConfirmReEnable().
    642 }
    643 
    644 void CrxInstaller::InstallUIAbort(bool user_initiated) {
    645   // If update_from_settings_page_ boolean is true, this functions is
    646   // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
    647   // and if it is false, this function is called in response to
    648   // ExtensionInstallPrompt::ConfirmInstall().
    649   if (!update_from_settings_page_) {
    650     std::string histogram_name = user_initiated
    651                                      ? "Extensions.Permissions_InstallCancel2"
    652                                      : "Extensions.Permissions_InstallAbort2";
    653     ExtensionService::RecordPermissionMessagesHistogram(
    654         extension(), histogram_name.c_str());
    655 
    656     NotifyCrxInstallComplete(false);
    657   }
    658 
    659   Release();  // balanced in ConfirmInstall() or ConfirmReEnable().
    660 
    661   // We're done. Since we don't post any more tasks to ourself, our ref count
    662   // should go to zero and we die. The destructor will clean up the temp dir.
    663 }
    664 
    665 void CrxInstaller::CompleteInstall() {
    666   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    667 
    668   if (!current_version_.empty()) {
    669     Version current_version(current_version_);
    670     if (current_version.CompareTo(*(extension()->version())) > 0) {
    671       ReportFailureFromFileThread(
    672           CrxInstallerError(
    673               l10n_util::GetStringUTF16(extension()->is_app() ?
    674                   IDS_APP_CANT_DOWNGRADE_VERSION :
    675                   IDS_EXTENSION_CANT_DOWNGRADE_VERSION)));
    676       return;
    677     }
    678   }
    679 
    680   // See how long extension install paths are.  This is important on
    681   // windows, because file operations may fail if the path to a file
    682   // exceeds a small constant.  See crbug.com/69693 .
    683   UMA_HISTOGRAM_CUSTOM_COUNTS(
    684     "Extensions.CrxInstallDirPathLength",
    685         install_directory_.value().length(), 0, 500, 100);
    686 
    687   ExtensionAssetsManager* assets_manager =
    688       ExtensionAssetsManager::GetInstance();
    689   assets_manager->InstallExtension(
    690       extension(),
    691       unpacked_extension_root_,
    692       install_directory_,
    693       profile(),
    694       base::Bind(&CrxInstaller::ReloadExtensionAfterInstall, this));
    695 }
    696 
    697 void CrxInstaller::ReloadExtensionAfterInstall(
    698     const base::FilePath& version_dir) {
    699   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    700 
    701   if (version_dir.empty()) {
    702     ReportFailureFromFileThread(
    703         CrxInstallerError(
    704             l10n_util::GetStringUTF16(
    705                 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)));
    706     return;
    707   }
    708 
    709   // This is lame, but we must reload the extension because absolute paths
    710   // inside the content scripts are established inside InitFromValue() and we
    711   // just moved the extension.
    712   // TODO(aa): All paths to resources inside extensions should be created
    713   // lazily and based on the Extension's root path at that moment.
    714   // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
    715   // with base::string16
    716   std::string extension_id = extension()->id();
    717   std::string error;
    718   install_checker_.set_extension(
    719       file_util::LoadExtension(
    720           version_dir,
    721           install_source_,
    722           extension()->creation_flags() | Extension::REQUIRE_KEY,
    723           &error).get());
    724 
    725   if (extension()) {
    726     ReportSuccessFromFileThread();
    727   } else {
    728     LOG(ERROR) << error << " " << extension_id << " " << download_url_;
    729     ReportFailureFromFileThread(CrxInstallerError(base::UTF8ToUTF16(error)));
    730   }
    731 }
    732 
    733 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError& error) {
    734   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    735   if (!BrowserThread::PostTask(
    736           BrowserThread::UI, FROM_HERE,
    737           base::Bind(&CrxInstaller::ReportFailureFromUIThread, this, error))) {
    738     NOTREACHED();
    739   }
    740 }
    741 
    742 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError& error) {
    743   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    744 
    745   if (!service_weak_.get() || service_weak_->browser_terminating())
    746     return;
    747 
    748   content::NotificationService* service =
    749       content::NotificationService::current();
    750   service->Notify(extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
    751                   content::Source<CrxInstaller>(this),
    752                   content::Details<const base::string16>(&error.message()));
    753 
    754   // This isn't really necessary, it is only used because unit tests expect to
    755   // see errors get reported via this interface.
    756   //
    757   // TODO(aa): Need to go through unit tests and clean them up too, probably get
    758   // rid of this line.
    759   ExtensionErrorReporter::GetInstance()->ReportError(
    760       error.message(),
    761       false);  // Be quiet.
    762 
    763   if (client_)
    764     client_->OnInstallFailure(error);
    765 
    766   NotifyCrxInstallComplete(false);
    767 
    768   // Delete temporary files.
    769   CleanupTempFiles();
    770 }
    771 
    772 void CrxInstaller::ReportSuccessFromFileThread() {
    773   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    774 
    775   // Tracking number of extensions installed by users
    776   if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD)
    777     UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
    778 
    779   if (!BrowserThread::PostTask(
    780           BrowserThread::UI, FROM_HERE,
    781           base::Bind(&CrxInstaller::ReportSuccessFromUIThread, this)))
    782     NOTREACHED();
    783 
    784   // Delete temporary files.
    785   CleanupTempFiles();
    786 }
    787 
    788 void CrxInstaller::ReportSuccessFromUIThread() {
    789   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    790 
    791   if (!service_weak_.get() || service_weak_->browser_terminating())
    792     return;
    793 
    794   if (!update_from_settings_page_) {
    795     // If there is a client, tell the client about installation.
    796     if (client_)
    797       client_->OnInstallSuccess(extension(), install_icon_.get());
    798 
    799     // We update the extension's granted permissions if the user already
    800     // approved the install (client_ is non NULL), or we are allowed to install
    801     // this silently.
    802     if ((client_ || allow_silent_install_) && grant_permissions_) {
    803       PermissionsUpdater perms_updater(profile());
    804       perms_updater.InitializePermissions(extension());
    805       perms_updater.GrantActivePermissions(extension());
    806     }
    807   }
    808 
    809   service_weak_->OnExtensionInstalled(
    810       extension(), page_ordinal_, install_flags_);
    811   NotifyCrxInstallComplete(true);
    812 }
    813 
    814 void CrxInstaller::NotifyCrxInstallBegin() {
    815   InstallTrackerFactory::GetForBrowserContext(profile())
    816       ->OnBeginCrxInstall(expected_id_);
    817 }
    818 
    819 void CrxInstaller::NotifyCrxInstallComplete(bool success) {
    820   // Some users (such as the download shelf) need to know when a
    821   // CRXInstaller is done.  Listening for the EXTENSION_* events
    822   // is problematic because they don't know anything about the
    823   // extension before it is unpacked, so they cannot filter based
    824   // on the extension.
    825   content::NotificationService::current()->Notify(
    826       extensions::NOTIFICATION_CRX_INSTALLER_DONE,
    827       content::Source<CrxInstaller>(this),
    828       content::Details<const Extension>(success ? extension() : NULL));
    829 
    830   InstallTrackerFactory::GetForBrowserContext(profile())
    831       ->OnFinishCrxInstall(success ? extension()->id() : expected_id_, success);
    832 
    833   if (success)
    834     ConfirmReEnable();
    835 }
    836 
    837 void CrxInstaller::CleanupTempFiles() {
    838   if (!installer_task_runner_->RunsTasksOnCurrentThread()) {
    839     if (!installer_task_runner_->PostTask(
    840             FROM_HERE,
    841             base::Bind(&CrxInstaller::CleanupTempFiles, this))) {
    842       NOTREACHED();
    843     }
    844     return;
    845   }
    846 
    847   // Delete the temp directory and crx file as necessary.
    848   if (!temp_dir_.value().empty()) {
    849     file_util::DeleteFile(temp_dir_, true);
    850     temp_dir_ = base::FilePath();
    851   }
    852 
    853   if (delete_source_ && !source_file_.value().empty()) {
    854     file_util::DeleteFile(source_file_, false);
    855     source_file_ = base::FilePath();
    856   }
    857 }
    858 
    859 void CrxInstaller::CheckUpdateFromSettingsPage() {
    860   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    861 
    862   ExtensionService* service = service_weak_.get();
    863   if (!service || service->browser_terminating())
    864     return;
    865 
    866   if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage)
    867     return;
    868 
    869   const Extension* installed_extension =
    870       service->GetInstalledExtension(extension()->id());
    871   if (installed_extension) {
    872     // Previous version of the extension exists.
    873     update_from_settings_page_ = true;
    874     expected_id_ = installed_extension->id();
    875     install_source_ = installed_extension->location();
    876     install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE;
    877   }
    878 }
    879 
    880 void CrxInstaller::ConfirmReEnable() {
    881   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    882 
    883   ExtensionService* service = service_weak_.get();
    884   if (!service || service->browser_terminating())
    885     return;
    886 
    887   if (!update_from_settings_page_)
    888     return;
    889 
    890   ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile());
    891   if (!prefs->DidExtensionEscalatePermissions(extension()->id()))
    892     return;
    893 
    894   if (client_) {
    895     AddRef();  // Balanced in InstallUIProceed() and InstallUIAbort().
    896     client_->ConfirmReEnable(this, extension());
    897   }
    898 }
    899 
    900 }  // namespace extensions
    901