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