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