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