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