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