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