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