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/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/chrome_notification_types.h"
     25 #include "chrome/browser/extensions/convert_user_script.h"
     26 #include "chrome/browser/extensions/convert_web_app.h"
     27 #include "chrome/browser/extensions/crx_installer_error.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/extension_system.h"
     32 #include "chrome/browser/extensions/permissions_updater.h"
     33 #include "chrome/browser/extensions/webstore_installer.h"
     34 #include "chrome/browser/profiles/profile.h"
     35 #include "chrome/browser/web_applications/web_app.h"
     36 #include "chrome/common/chrome_paths.h"
     37 #include "chrome/common/extensions/extension_constants.h"
     38 #include "chrome/common/extensions/extension_file_util.h"
     39 #include "chrome/common/extensions/extension_icon_set.h"
     40 #include "chrome/common/extensions/manifest_url_handler.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/common/feature_switch.h"
     46 #include "extensions/common/manifest.h"
     47 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
     48 #include "extensions/common/manifest_handlers/shared_module_info.h"
     49 #include "extensions/common/permissions/permission_message_provider.h"
     50 #include "extensions/common/permissions/permission_set.h"
     51 #include "extensions/common/permissions/permissions_data.h"
     52 #include "extensions/common/user_script.h"
     53 #include "grit/chromium_strings.h"
     54 #include "grit/generated_resources.h"
     55 #include "grit/theme_resources.h"
     56 #include "third_party/skia/include/core/SkBitmap.h"
     57 #include "ui/base/l10n/l10n_util.h"
     58 #include "ui/base/resource/resource_bundle.h"
     59 
     60 #if defined(OS_CHROMEOS)
     61 #include "chrome/browser/chromeos/login/user_manager.h"
     62 #endif
     63 
     64 using content::BrowserThread;
     65 using content::UserMetricsAction;
     66 using extensions::SharedModuleInfo;
     67 
     68 namespace extensions {
     69 
     70 namespace {
     71 
     72 // Used in histograms; do not change order.
     73 enum OffStoreInstallDecision {
     74   OnStoreInstall,
     75   OffStoreInstallAllowed,
     76   OffStoreInstallDisallowed,
     77   NumOffStoreInstallDecision
     78 };
     79 
     80 }  // namespace
     81 
     82 // static
     83 scoped_refptr<CrxInstaller> CrxInstaller::CreateSilent(
     84     ExtensionService* frontend) {
     85   return new CrxInstaller(frontend->AsWeakPtr(),
     86                           scoped_ptr<ExtensionInstallPrompt>(),
     87                           NULL);
     88 }
     89 
     90 // static
     91 scoped_refptr<CrxInstaller> CrxInstaller::Create(
     92     ExtensionService* frontend,
     93     scoped_ptr<ExtensionInstallPrompt> client) {
     94   return new CrxInstaller(frontend->AsWeakPtr(), client.Pass(), NULL);
     95 }
     96 
     97 // static
     98 scoped_refptr<CrxInstaller> CrxInstaller::Create(
     99     ExtensionService* service,
    100     scoped_ptr<ExtensionInstallPrompt> client,
    101     const WebstoreInstaller::Approval* approval) {
    102   return new CrxInstaller(service->AsWeakPtr(), client.Pass(), approval);
    103 }
    104 
    105 CrxInstaller::CrxInstaller(
    106     base::WeakPtr<ExtensionService> service_weak,
    107     scoped_ptr<ExtensionInstallPrompt> client,
    108     const WebstoreInstaller::Approval* approval)
    109     : install_directory_(service_weak->install_directory()),
    110       install_source_(Manifest::INTERNAL),
    111       approved_(false),
    112       expected_manifest_check_level_(
    113           WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT),
    114       expected_version_strict_checking_(false),
    115       extensions_enabled_(service_weak->extensions_enabled()),
    116       delete_source_(false),
    117       create_app_shortcut_(false),
    118       service_weak_(service_weak),
    119       // See header file comment on |client_| for why we use a raw pointer here.
    120       client_(client.release()),
    121       apps_require_extension_mime_type_(false),
    122       allow_silent_install_(false),
    123       install_cause_(extension_misc::INSTALL_CAUSE_UNSET),
    124       creation_flags_(Extension::NO_FLAGS),
    125       off_store_install_allow_reason_(OffStoreInstallDisallowed),
    126       did_handle_successfully_(true),
    127       error_on_unsupported_requirements_(false),
    128       has_requirement_errors_(false),
    129       blacklist_state_(extensions::Blacklist::NOT_BLACKLISTED),
    130       install_wait_for_idle_(true),
    131       update_from_settings_page_(false),
    132       installer_(service_weak->profile()) {
    133   installer_task_runner_ = service_weak->GetFileTaskRunner();
    134   if (!approval)
    135     return;
    136 
    137   CHECK(profile()->IsSameProfile(approval->profile));
    138   if (client_) {
    139     client_->install_ui()->SetUseAppInstalledBubble(
    140         approval->use_app_installed_bubble);
    141     client_->install_ui()->set_skip_post_install_ui(
    142         approval->skip_post_install_ui);
    143   }
    144 
    145   if (approval->skip_install_dialog) {
    146     // Mark the extension as approved, but save the expected manifest and ID
    147     // so we can check that they match the CRX's.
    148     approved_ = true;
    149     expected_manifest_check_level_ = approval->manifest_check_level;
    150     if (expected_manifest_check_level_ !=
    151         WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE)
    152       expected_manifest_.reset(approval->manifest->DeepCopy());
    153     expected_id_ = approval->extension_id;
    154   }
    155   if (approval->minimum_version.get()) {
    156     expected_version_.reset(new Version(*approval->minimum_version));
    157     expected_version_strict_checking_ = false;
    158   }
    159 
    160   show_dialog_callback_ = approval->show_dialog_callback;
    161 
    162   if (approval->is_ephemeral)
    163     creation_flags_ |= Extension::IS_EPHEMERAL;
    164 }
    165 
    166 CrxInstaller::~CrxInstaller() {
    167   // Make sure the UI is deleted on the ui thread.
    168   if (client_) {
    169     BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_);
    170     client_ = NULL;
    171   }
    172 }
    173 
    174 void CrxInstaller::InstallCrx(const base::FilePath& source_file) {
    175   ExtensionService* service = service_weak_.get();
    176   if (!service || service->browser_terminating())
    177     return;
    178 
    179   source_file_ = source_file;
    180 
    181   scoped_refptr<SandboxedUnpacker> unpacker(
    182       new SandboxedUnpacker(source_file,
    183                             install_source_,
    184                             creation_flags_,
    185                             install_directory_,
    186                             installer_task_runner_.get(),
    187                             this));
    188 
    189   if (!installer_task_runner_->PostTask(
    190           FROM_HERE,
    191           base::Bind(&SandboxedUnpacker::Start, unpacker.get())))
    192     NOTREACHED();
    193 }
    194 
    195 void CrxInstaller::InstallUserScript(const base::FilePath& source_file,
    196                                      const GURL& download_url) {
    197   DCHECK(!download_url.is_empty());
    198 
    199   source_file_ = source_file;
    200   download_url_ = download_url;
    201 
    202   if (!installer_task_runner_->PostTask(
    203           FROM_HERE,
    204           base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread, this)))
    205     NOTREACHED();
    206 }
    207 
    208 void CrxInstaller::ConvertUserScriptOnFileThread() {
    209   base::string16 error;
    210   scoped_refptr<Extension> extension = ConvertUserScriptToExtension(
    211       source_file_, download_url_, install_directory_, &error);
    212   if (!extension.get()) {
    213     ReportFailureFromFileThread(CrxInstallerError(error));
    214     return;
    215   }
    216 
    217   OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
    218                   SkBitmap());
    219 }
    220 
    221 void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
    222   if (!installer_task_runner_->PostTask(
    223           FROM_HERE,
    224           base::Bind(&CrxInstaller::ConvertWebAppOnFileThread,
    225                      this,
    226                      web_app,
    227                      install_directory_)))
    228     NOTREACHED();
    229 }
    230 
    231 void CrxInstaller::ConvertWebAppOnFileThread(
    232     const WebApplicationInfo& web_app,
    233     const base::FilePath& install_directory) {
    234   base::string16 error;
    235   scoped_refptr<Extension> extension(
    236       ConvertWebAppToExtension(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                                    ASCIIToUTF16(expected_id_),
    259                                    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               ASCIIToUTF16(expected_version_->GetString()),
    269               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               ASCIIToUTF16(expected_version_->GetString() + "+"),
    277               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             PermissionsData::GetActivePermissions(extension)->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               PermissionsData::GetActivePermissions(dummy_extension.get());
    307           valid = !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
    308                         expected_permissions,
    309                         PermissionsData::GetActivePermissions(extension),
    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 (installer_.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               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 = installer_.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(const base::FilePath& temp_dir,
    433                                    const base::FilePath& extension_dir,
    434                                    const DictionaryValue* original_manifest,
    435                                    const Extension* extension,
    436                                    const SkBitmap& install_icon) {
    437   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    438 
    439   UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
    440                             install_source(), Manifest::NUM_LOCATIONS);
    441 
    442 
    443   UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
    444                             install_cause(),
    445                             extension_misc::NUM_INSTALL_CAUSES);
    446 
    447   installer_.set_extension(extension);
    448   temp_dir_ = temp_dir;
    449   if (!install_icon.empty())
    450     install_icon_.reset(new SkBitmap(install_icon));
    451 
    452   if (original_manifest)
    453     original_manifest_.reset(new Manifest(
    454         Manifest::INVALID_LOCATION,
    455         scoped_ptr<DictionaryValue>(original_manifest->DeepCopy())));
    456 
    457   // We don't have to delete the unpack dir explicity since it is a child of
    458   // the temp dir.
    459   unpacked_extension_root_ = extension_dir;
    460 
    461   CrxInstallerError error = AllowInstall(extension);
    462   if (error.type() != CrxInstallerError::ERROR_NONE) {
    463     ReportFailureFromFileThread(error);
    464     return;
    465   }
    466 
    467   if (!BrowserThread::PostTask(
    468         BrowserThread::UI, FROM_HERE,
    469         base::Bind(&CrxInstaller::CheckImportsAndRequirements, this)))
    470     NOTREACHED();
    471 }
    472 
    473 void CrxInstaller::CheckImportsAndRequirements() {
    474   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    475   ExtensionService* service = service_weak_.get();
    476   if (!service || service->browser_terminating())
    477     return;
    478 
    479   if (SharedModuleInfo::ImportsModules(extension())) {
    480     const std::vector<SharedModuleInfo::ImportInfo>& imports =
    481         SharedModuleInfo::GetImports(extension());
    482     std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
    483     for (i = imports.begin(); i != imports.end(); ++i) {
    484       Version version_required(i->minimum_version);
    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                 ASCIIToUTF16(i->extension_id))));
    493         return;
    494       }
    495     }
    496   }
    497   installer_.CheckRequirements(base::Bind(&CrxInstaller::OnRequirementsChecked,
    498                                           this));
    499 }
    500 
    501 void CrxInstaller::OnRequirementsChecked(
    502     std::vector<std::string> requirement_errors) {
    503   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    504   if (!service_weak_)
    505     return;
    506 
    507   if (!requirement_errors.empty()) {
    508     if (error_on_unsupported_requirements_) {
    509       ReportFailureFromUIThread(CrxInstallerError(
    510           UTF8ToUTF16(JoinString(requirement_errors, ' '))));
    511       return;
    512     }
    513     has_requirement_errors_ = true;
    514   }
    515 
    516   ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted(
    517       extension()->id(),
    518       base::Bind(&CrxInstaller::OnBlacklistChecked, this));
    519 }
    520 
    521 void CrxInstaller::OnBlacklistChecked(
    522     extensions::Blacklist::BlacklistState blacklist_state) {
    523   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    524   if (!service_weak_)
    525     return;
    526 
    527   blacklist_state_ = blacklist_state;
    528 
    529   if (blacklist_state_ == extensions::Blacklist::BLACKLISTED_MALWARE &&
    530       !allow_silent_install_) {
    531     // User tried to install a blacklisted extension. Show an error and
    532     // refuse to install it.
    533     ReportFailureFromUIThread(extensions::CrxInstallerError(
    534         l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED,
    535                                    UTF8ToUTF16(extension()->name()))));
    536     UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
    537                               extension()->location(),
    538                               Manifest::NUM_LOCATIONS);
    539     return;
    540   }
    541 
    542   // NOTE: extension may still be blacklisted, but we're forced to silently
    543   // install it. In this case, ExtensionService::OnExtensionInstalled needs to
    544   // deal with it.
    545   ConfirmInstall();
    546 }
    547 
    548 void CrxInstaller::ConfirmInstall() {
    549   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    550   ExtensionService* service = service_weak_.get();
    551   if (!service || service->browser_terminating())
    552     return;
    553 
    554   if (KioskModeInfo::IsKioskOnly(installer_.extension())) {
    555     bool in_kiosk_mode = false;
    556 #if defined(OS_CHROMEOS)
    557     chromeos::UserManager* user_manager = chromeos::UserManager::Get();
    558     in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
    559 #endif
    560     if (!in_kiosk_mode) {
    561       ReportFailureFromUIThread(CrxInstallerError(
    562           l10n_util::GetStringUTF16(
    563               IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY)));
    564     }
    565   }
    566 
    567   base::string16 error = installer_.CheckManagementPolicy();
    568   if (!error.empty()) {
    569     // We don't want to show the error infobar for installs from the WebStore,
    570     // because the WebStore already shows an error dialog itself.
    571     // Note: |client_| can be NULL in unit_tests!
    572     if (extension()->from_webstore() && client_)
    573       client_->install_ui()->set_skip_post_install_ui(true);
    574     ReportFailureFromUIThread(CrxInstallerError(error));
    575     return;
    576   }
    577 
    578   // Check whether this install is initiated from the settings page to
    579   // update an existing extension or app.
    580   CheckUpdateFromSettingsPage();
    581 
    582   GURL overlapping_url;
    583   const Extension* overlapping_extension =
    584       service->extensions()->GetHostedAppByOverlappingWebExtent(
    585           extension()->web_extent());
    586   if (overlapping_extension &&
    587       overlapping_extension->id() != extension()->id()) {
    588     ReportFailureFromUIThread(
    589         CrxInstallerError(
    590             l10n_util::GetStringFUTF16(
    591                 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
    592                 UTF8ToUTF16(overlapping_extension->name()))));
    593     return;
    594   }
    595 
    596   current_version_ =
    597       service->extension_prefs()->GetVersionString(extension()->id());
    598 
    599   if (client_ &&
    600       (!allow_silent_install_ || !approved_) &&
    601       !update_from_settings_page_) {
    602     AddRef();  // Balanced in InstallUIProceed() and InstallUIAbort().
    603     client_->ConfirmInstall(this, extension(), show_dialog_callback_);
    604   } else {
    605     if (!installer_task_runner_->PostTask(
    606             FROM_HERE,
    607             base::Bind(&CrxInstaller::CompleteInstall, this)))
    608       NOTREACHED();
    609   }
    610   return;
    611 }
    612 
    613 void CrxInstaller::InstallUIProceed() {
    614   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    615 
    616   ExtensionService* service = service_weak_.get();
    617   if (!service || service->browser_terminating())
    618     return;
    619 
    620   // If update_from_settings_page_ boolean is true, this functions is
    621   // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
    622   // and if it is false, this function is called in response to
    623   // ExtensionInstallPrompt::ConfirmInstall().
    624   if (update_from_settings_page_) {
    625     service->GrantPermissionsAndEnableExtension(extension());
    626   } else {
    627     if (!installer_task_runner_->PostTask(
    628             FROM_HERE,
    629             base::Bind(&CrxInstaller::CompleteInstall, this)))
    630       NOTREACHED();
    631   }
    632 
    633   Release();  // balanced in ConfirmInstall() or ConfirmReEnable().
    634 }
    635 
    636 void CrxInstaller::InstallUIAbort(bool user_initiated) {
    637   // If update_from_settings_page_ boolean is true, this functions is
    638   // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
    639   // and if it is false, this function is called in response to
    640   // ExtensionInstallPrompt::ConfirmInstall().
    641   if (!update_from_settings_page_) {
    642     std::string histogram_name = user_initiated ?
    643         "Extensions.Permissions_InstallCancel" :
    644         "Extensions.Permissions_InstallAbort";
    645     ExtensionService::RecordPermissionMessagesHistogram(
    646         extension(), histogram_name.c_str());
    647 
    648     NotifyCrxInstallComplete(false);
    649   }
    650 
    651   Release();  // balanced in ConfirmInstall() or ConfirmReEnable().
    652 
    653   // We're done. Since we don't post any more tasks to ourself, our ref count
    654   // should go to zero and we die. The destructor will clean up the temp dir.
    655 }
    656 
    657 void CrxInstaller::CompleteInstall() {
    658   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    659 
    660   if (!current_version_.empty()) {
    661     Version current_version(current_version_);
    662     if (current_version.CompareTo(*(extension()->version())) > 0) {
    663       ReportFailureFromFileThread(
    664           CrxInstallerError(
    665               l10n_util::GetStringUTF16(extension()->is_app() ?
    666                   IDS_APP_CANT_DOWNGRADE_VERSION :
    667                   IDS_EXTENSION_CANT_DOWNGRADE_VERSION)));
    668       return;
    669     }
    670   }
    671 
    672   // See how long extension install paths are.  This is important on
    673   // windows, because file operations may fail if the path to a file
    674   // exceeds a small constant.  See crbug.com/69693 .
    675   UMA_HISTOGRAM_CUSTOM_COUNTS(
    676     "Extensions.CrxInstallDirPathLength",
    677         install_directory_.value().length(), 0, 500, 100);
    678 
    679   base::FilePath version_dir = extension_file_util::InstallExtension(
    680       unpacked_extension_root_,
    681       extension()->id(),
    682       extension()->VersionString(),
    683       install_directory_);
    684   if (version_dir.empty()) {
    685     ReportFailureFromFileThread(
    686         CrxInstallerError(
    687             l10n_util::GetStringUTF16(
    688                 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)));
    689     return;
    690   }
    691 
    692   // This is lame, but we must reload the extension because absolute paths
    693   // inside the content scripts are established inside InitFromValue() and we
    694   // just moved the extension.
    695   // TODO(aa): All paths to resources inside extensions should be created
    696   // lazily and based on the Extension's root path at that moment.
    697   // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
    698   // with base::string16
    699   std::string extension_id = extension()->id();
    700   std::string error;
    701   installer_.set_extension(extension_file_util::LoadExtension(
    702       version_dir,
    703       install_source_,
    704       extension()->creation_flags() | Extension::REQUIRE_KEY,
    705       &error).get());
    706 
    707   if (extension()) {
    708     ReportSuccessFromFileThread();
    709   } else {
    710     LOG(ERROR) << error << " " << extension_id << " " << download_url_;
    711     ReportFailureFromFileThread(CrxInstallerError(UTF8ToUTF16(error)));
    712   }
    713 
    714 }
    715 
    716 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError& error) {
    717   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    718   if (!BrowserThread::PostTask(
    719           BrowserThread::UI, FROM_HERE,
    720           base::Bind(&CrxInstaller::ReportFailureFromUIThread, this, error))) {
    721     NOTREACHED();
    722   }
    723 }
    724 
    725 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError& error) {
    726   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    727 
    728   content::NotificationService* service =
    729       content::NotificationService::current();
    730   service->Notify(chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
    731                   content::Source<CrxInstaller>(this),
    732                   content::Details<const base::string16>(&error.message()));
    733 
    734   // This isn't really necessary, it is only used because unit tests expect to
    735   // see errors get reported via this interface.
    736   //
    737   // TODO(aa): Need to go through unit tests and clean them up too, probably get
    738   // rid of this line.
    739   ExtensionErrorReporter::GetInstance()->ReportError(
    740       error.message(), false);  // quiet
    741 
    742   if (client_)
    743     client_->OnInstallFailure(error);
    744 
    745   NotifyCrxInstallComplete(false);
    746 
    747   // Delete temporary files.
    748   CleanupTempFiles();
    749 }
    750 
    751 void CrxInstaller::ReportSuccessFromFileThread() {
    752   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
    753 
    754   // Tracking number of extensions installed by users
    755   if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD)
    756     UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
    757 
    758   if (!BrowserThread::PostTask(
    759           BrowserThread::UI, FROM_HERE,
    760           base::Bind(&CrxInstaller::ReportSuccessFromUIThread, this)))
    761     NOTREACHED();
    762 
    763   // Delete temporary files.
    764   CleanupTempFiles();
    765 }
    766 
    767 void CrxInstaller::ReportSuccessFromUIThread() {
    768   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    769 
    770   if (!service_weak_.get() || service_weak_->browser_terminating())
    771     return;
    772 
    773   if (!update_from_settings_page_) {
    774     // If there is a client, tell the client about installation.
    775     if (client_)
    776       client_->OnInstallSuccess(extension(), install_icon_.get());
    777 
    778     // We update the extension's granted permissions if the user already
    779     // approved the install (client_ is non NULL), or we are allowed to install
    780     // this silently.
    781     if (client_ || allow_silent_install_) {
    782       PermissionsUpdater perms_updater(profile());
    783       perms_updater.GrantActivePermissions(extension());
    784     }
    785   }
    786 
    787   service_weak_->OnExtensionInstalled(extension(),
    788                                       page_ordinal_,
    789                                       has_requirement_errors_,
    790                                       blacklist_state_,
    791                                       install_wait_for_idle_);
    792   NotifyCrxInstallComplete(true);
    793 }
    794 
    795 void CrxInstaller::NotifyCrxInstallComplete(bool success) {
    796   // Some users (such as the download shelf) need to know when a
    797   // CRXInstaller is done.  Listening for the EXTENSION_* events
    798   // is problematic because they don't know anything about the
    799   // extension before it is unpacked, so they cannot filter based
    800   // on the extension.
    801   content::NotificationService::current()->Notify(
    802       chrome::NOTIFICATION_CRX_INSTALLER_DONE,
    803       content::Source<CrxInstaller>(this),
    804       content::Details<const Extension>(
    805           success ? extension() : NULL));
    806 
    807   if (success)
    808     ConfirmReEnable();
    809 }
    810 
    811 void CrxInstaller::CleanupTempFiles() {
    812   if (!installer_task_runner_->RunsTasksOnCurrentThread()) {
    813     if (!installer_task_runner_->PostTask(
    814             FROM_HERE,
    815             base::Bind(&CrxInstaller::CleanupTempFiles, this))) {
    816       NOTREACHED();
    817     }
    818     return;
    819   }
    820 
    821   // Delete the temp directory and crx file as necessary.
    822   if (!temp_dir_.value().empty()) {
    823     extension_file_util::DeleteFile(temp_dir_, true);
    824     temp_dir_ = base::FilePath();
    825   }
    826 
    827   if (delete_source_ && !source_file_.value().empty()) {
    828     extension_file_util::DeleteFile(source_file_, false);
    829     source_file_ = base::FilePath();
    830   }
    831 }
    832 
    833 void CrxInstaller::CheckUpdateFromSettingsPage() {
    834   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    835 
    836   ExtensionService* service = service_weak_.get();
    837   if (!service || service->browser_terminating())
    838     return;
    839 
    840   if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage)
    841     return;
    842 
    843   const Extension* installed_extension =
    844       service->GetInstalledExtension(extension()->id());
    845   if (installed_extension) {
    846     // Previous version of the extension exists.
    847     update_from_settings_page_ = true;
    848     expected_id_ = installed_extension->id();
    849     install_source_ = installed_extension->location();
    850     install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE;
    851   }
    852 }
    853 
    854 void CrxInstaller::ConfirmReEnable() {
    855   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    856 
    857   ExtensionService* service = service_weak_.get();
    858   if (!service || service->browser_terminating())
    859     return;
    860 
    861   if (!update_from_settings_page_)
    862     return;
    863 
    864   ExtensionPrefs* prefs = service->extension_prefs();
    865   if (!prefs->DidExtensionEscalatePermissions(extension()->id()))
    866     return;
    867 
    868   if (client_) {
    869     AddRef();  // Balanced in InstallUIProceed() and InstallUIAbort().
    870     client_->ConfirmReEnable(this, extension());
    871   }
    872 }
    873 
    874 }  // namespace extensions
    875