• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/extension_system.h"
32 #include "chrome/browser/extensions/permissions_updater.h"
33 #include "chrome/browser/extensions/webstore_installer.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/web_applications/web_app.h"
36 #include "chrome/common/chrome_paths.h"
37 #include "chrome/common/extensions/extension_constants.h"
38 #include "chrome/common/extensions/extension_file_util.h"
39 #include "chrome/common/extensions/extension_icon_set.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/common/feature_switch.h"
46 #include "extensions/common/manifest.h"
47 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
48 #include "extensions/common/manifest_handlers/shared_module_info.h"
49 #include "extensions/common/permissions/permission_message_provider.h"
50 #include "extensions/common/permissions/permission_set.h"
51 #include "extensions/common/permissions/permissions_data.h"
52 #include "extensions/common/user_script.h"
53 #include "grit/chromium_strings.h"
54 #include "grit/generated_resources.h"
55 #include "grit/theme_resources.h"
56 #include "third_party/skia/include/core/SkBitmap.h"
57 #include "ui/base/l10n/l10n_util.h"
58 #include "ui/base/resource/resource_bundle.h"
59 
60 #if defined(OS_CHROMEOS)
61 #include "chrome/browser/chromeos/login/user_manager.h"
62 #endif
63 
64 using content::BrowserThread;
65 using content::UserMetricsAction;
66 using extensions::SharedModuleInfo;
67 
68 namespace extensions {
69 
70 namespace {
71 
72 // Used in histograms; do not change order.
73 enum OffStoreInstallDecision {
74   OnStoreInstall,
75   OffStoreInstallAllowed,
76   OffStoreInstallDisallowed,
77   NumOffStoreInstallDecision
78 };
79 
80 }  // namespace
81 
82 // static
CreateSilent(ExtensionService * frontend)83 scoped_refptr<CrxInstaller> CrxInstaller::CreateSilent(
84     ExtensionService* frontend) {
85   return new CrxInstaller(frontend->AsWeakPtr(),
86                           scoped_ptr<ExtensionInstallPrompt>(),
87                           NULL);
88 }
89 
90 // static
Create(ExtensionService * frontend,scoped_ptr<ExtensionInstallPrompt> client)91 scoped_refptr<CrxInstaller> CrxInstaller::Create(
92     ExtensionService* frontend,
93     scoped_ptr<ExtensionInstallPrompt> client) {
94   return new CrxInstaller(frontend->AsWeakPtr(), client.Pass(), NULL);
95 }
96 
97 // static
Create(ExtensionService * service,scoped_ptr<ExtensionInstallPrompt> client,const WebstoreInstaller::Approval * approval)98 scoped_refptr<CrxInstaller> CrxInstaller::Create(
99     ExtensionService* service,
100     scoped_ptr<ExtensionInstallPrompt> client,
101     const WebstoreInstaller::Approval* approval) {
102   return new CrxInstaller(service->AsWeakPtr(), client.Pass(), approval);
103 }
104 
CrxInstaller(base::WeakPtr<ExtensionService> service_weak,scoped_ptr<ExtensionInstallPrompt> client,const WebstoreInstaller::Approval * approval)105 CrxInstaller::CrxInstaller(
106     base::WeakPtr<ExtensionService> service_weak,
107     scoped_ptr<ExtensionInstallPrompt> client,
108     const WebstoreInstaller::Approval* approval)
109     : install_directory_(service_weak->install_directory()),
110       install_source_(Manifest::INTERNAL),
111       approved_(false),
112       expected_manifest_check_level_(
113           WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT),
114       expected_version_strict_checking_(false),
115       extensions_enabled_(service_weak->extensions_enabled()),
116       delete_source_(false),
117       create_app_shortcut_(false),
118       service_weak_(service_weak),
119       // See header file comment on |client_| for why we use a raw pointer here.
120       client_(client.release()),
121       apps_require_extension_mime_type_(false),
122       allow_silent_install_(false),
123       install_cause_(extension_misc::INSTALL_CAUSE_UNSET),
124       creation_flags_(Extension::NO_FLAGS),
125       off_store_install_allow_reason_(OffStoreInstallDisallowed),
126       did_handle_successfully_(true),
127       error_on_unsupported_requirements_(false),
128       has_requirement_errors_(false),
129       blacklist_state_(extensions::Blacklist::NOT_BLACKLISTED),
130       install_wait_for_idle_(true),
131       update_from_settings_page_(false),
132       installer_(service_weak->profile()) {
133   installer_task_runner_ = service_weak->GetFileTaskRunner();
134   if (!approval)
135     return;
136 
137   CHECK(profile()->IsSameProfile(approval->profile));
138   if (client_) {
139     client_->install_ui()->SetUseAppInstalledBubble(
140         approval->use_app_installed_bubble);
141     client_->install_ui()->set_skip_post_install_ui(
142         approval->skip_post_install_ui);
143   }
144 
145   if (approval->skip_install_dialog) {
146     // Mark the extension as approved, but save the expected manifest and ID
147     // so we can check that they match the CRX's.
148     approved_ = true;
149     expected_manifest_check_level_ = approval->manifest_check_level;
150     if (expected_manifest_check_level_ !=
151         WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE)
152       expected_manifest_.reset(approval->manifest->DeepCopy());
153     expected_id_ = approval->extension_id;
154   }
155   if (approval->minimum_version.get()) {
156     expected_version_.reset(new Version(*approval->minimum_version));
157     expected_version_strict_checking_ = false;
158   }
159 
160   show_dialog_callback_ = approval->show_dialog_callback;
161 
162   if (approval->is_ephemeral)
163     creation_flags_ |= Extension::IS_EPHEMERAL;
164 }
165 
~CrxInstaller()166 CrxInstaller::~CrxInstaller() {
167   // Make sure the UI is deleted on the ui thread.
168   if (client_) {
169     BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_);
170     client_ = NULL;
171   }
172 }
173 
InstallCrx(const base::FilePath & source_file)174 void CrxInstaller::InstallCrx(const base::FilePath& source_file) {
175   ExtensionService* service = service_weak_.get();
176   if (!service || service->browser_terminating())
177     return;
178 
179   source_file_ = source_file;
180 
181   scoped_refptr<SandboxedUnpacker> unpacker(
182       new SandboxedUnpacker(source_file,
183                             install_source_,
184                             creation_flags_,
185                             install_directory_,
186                             installer_task_runner_.get(),
187                             this));
188 
189   if (!installer_task_runner_->PostTask(
190           FROM_HERE,
191           base::Bind(&SandboxedUnpacker::Start, unpacker.get())))
192     NOTREACHED();
193 }
194 
InstallUserScript(const base::FilePath & source_file,const GURL & download_url)195 void CrxInstaller::InstallUserScript(const base::FilePath& source_file,
196                                      const GURL& download_url) {
197   DCHECK(!download_url.is_empty());
198 
199   source_file_ = source_file;
200   download_url_ = download_url;
201 
202   if (!installer_task_runner_->PostTask(
203           FROM_HERE,
204           base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread, this)))
205     NOTREACHED();
206 }
207 
ConvertUserScriptOnFileThread()208 void CrxInstaller::ConvertUserScriptOnFileThread() {
209   base::string16 error;
210   scoped_refptr<Extension> extension = ConvertUserScriptToExtension(
211       source_file_, download_url_, install_directory_, &error);
212   if (!extension.get()) {
213     ReportFailureFromFileThread(CrxInstallerError(error));
214     return;
215   }
216 
217   OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
218                   SkBitmap());
219 }
220 
InstallWebApp(const WebApplicationInfo & web_app)221 void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
222   if (!installer_task_runner_->PostTask(
223           FROM_HERE,
224           base::Bind(&CrxInstaller::ConvertWebAppOnFileThread,
225                      this,
226                      web_app,
227                      install_directory_)))
228     NOTREACHED();
229 }
230 
ConvertWebAppOnFileThread(const WebApplicationInfo & web_app,const base::FilePath & install_directory)231 void CrxInstaller::ConvertWebAppOnFileThread(
232     const WebApplicationInfo& web_app,
233     const base::FilePath& install_directory) {
234   base::string16 error;
235   scoped_refptr<Extension> extension(
236       ConvertWebAppToExtension(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 
AllowInstall(const Extension * extension)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                                    ASCIIToUTF16(expected_id_),
259                                    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               ASCIIToUTF16(expected_version_->GetString()),
269               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               ASCIIToUTF16(expected_version_->GetString() + "+"),
277               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             PermissionsData::GetActivePermissions(extension)->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               PermissionsData::GetActivePermissions(dummy_extension.get());
307           valid = !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
308                         expected_permissions,
309                         PermissionsData::GetActivePermissions(extension),
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 (installer_.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               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 = installer_.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 
OnUnpackFailure(const base::string16 & error_message)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 
OnUnpackSuccess(const base::FilePath & temp_dir,const base::FilePath & extension_dir,const DictionaryValue * original_manifest,const Extension * extension,const SkBitmap & install_icon)432 void CrxInstaller::OnUnpackSuccess(const base::FilePath& temp_dir,
433                                    const base::FilePath& extension_dir,
434                                    const DictionaryValue* original_manifest,
435                                    const Extension* extension,
436                                    const SkBitmap& install_icon) {
437   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
438 
439   UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
440                             install_source(), Manifest::NUM_LOCATIONS);
441 
442 
443   UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
444                             install_cause(),
445                             extension_misc::NUM_INSTALL_CAUSES);
446 
447   installer_.set_extension(extension);
448   temp_dir_ = temp_dir;
449   if (!install_icon.empty())
450     install_icon_.reset(new SkBitmap(install_icon));
451 
452   if (original_manifest)
453     original_manifest_.reset(new Manifest(
454         Manifest::INVALID_LOCATION,
455         scoped_ptr<DictionaryValue>(original_manifest->DeepCopy())));
456 
457   // We don't have to delete the unpack dir explicity since it is a child of
458   // the temp dir.
459   unpacked_extension_root_ = extension_dir;
460 
461   CrxInstallerError error = AllowInstall(extension);
462   if (error.type() != CrxInstallerError::ERROR_NONE) {
463     ReportFailureFromFileThread(error);
464     return;
465   }
466 
467   if (!BrowserThread::PostTask(
468         BrowserThread::UI, FROM_HERE,
469         base::Bind(&CrxInstaller::CheckImportsAndRequirements, this)))
470     NOTREACHED();
471 }
472 
CheckImportsAndRequirements()473 void CrxInstaller::CheckImportsAndRequirements() {
474   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
475   ExtensionService* service = service_weak_.get();
476   if (!service || service->browser_terminating())
477     return;
478 
479   if (SharedModuleInfo::ImportsModules(extension())) {
480     const std::vector<SharedModuleInfo::ImportInfo>& imports =
481         SharedModuleInfo::GetImports(extension());
482     std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
483     for (i = imports.begin(); i != imports.end(); ++i) {
484       Version version_required(i->minimum_version);
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                 ASCIIToUTF16(i->extension_id))));
493         return;
494       }
495     }
496   }
497   installer_.CheckRequirements(base::Bind(&CrxInstaller::OnRequirementsChecked,
498                                           this));
499 }
500 
OnRequirementsChecked(std::vector<std::string> requirement_errors)501 void CrxInstaller::OnRequirementsChecked(
502     std::vector<std::string> requirement_errors) {
503   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
504   if (!service_weak_)
505     return;
506 
507   if (!requirement_errors.empty()) {
508     if (error_on_unsupported_requirements_) {
509       ReportFailureFromUIThread(CrxInstallerError(
510           UTF8ToUTF16(JoinString(requirement_errors, ' '))));
511       return;
512     }
513     has_requirement_errors_ = true;
514   }
515 
516   ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted(
517       extension()->id(),
518       base::Bind(&CrxInstaller::OnBlacklistChecked, this));
519 }
520 
OnBlacklistChecked(extensions::Blacklist::BlacklistState blacklist_state)521 void CrxInstaller::OnBlacklistChecked(
522     extensions::Blacklist::BlacklistState blacklist_state) {
523   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
524   if (!service_weak_)
525     return;
526 
527   blacklist_state_ = blacklist_state;
528 
529   if (blacklist_state_ == extensions::Blacklist::BLACKLISTED_MALWARE &&
530       !allow_silent_install_) {
531     // User tried to install a blacklisted extension. Show an error and
532     // refuse to install it.
533     ReportFailureFromUIThread(extensions::CrxInstallerError(
534         l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED,
535                                    UTF8ToUTF16(extension()->name()))));
536     UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
537                               extension()->location(),
538                               Manifest::NUM_LOCATIONS);
539     return;
540   }
541 
542   // NOTE: extension may still be blacklisted, but we're forced to silently
543   // install it. In this case, ExtensionService::OnExtensionInstalled needs to
544   // deal with it.
545   ConfirmInstall();
546 }
547 
ConfirmInstall()548 void CrxInstaller::ConfirmInstall() {
549   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
550   ExtensionService* service = service_weak_.get();
551   if (!service || service->browser_terminating())
552     return;
553 
554   if (KioskModeInfo::IsKioskOnly(installer_.extension())) {
555     bool in_kiosk_mode = false;
556 #if defined(OS_CHROMEOS)
557     chromeos::UserManager* user_manager = chromeos::UserManager::Get();
558     in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
559 #endif
560     if (!in_kiosk_mode) {
561       ReportFailureFromUIThread(CrxInstallerError(
562           l10n_util::GetStringUTF16(
563               IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY)));
564     }
565   }
566 
567   base::string16 error = installer_.CheckManagementPolicy();
568   if (!error.empty()) {
569     // We don't want to show the error infobar for installs from the WebStore,
570     // because the WebStore already shows an error dialog itself.
571     // Note: |client_| can be NULL in unit_tests!
572     if (extension()->from_webstore() && client_)
573       client_->install_ui()->set_skip_post_install_ui(true);
574     ReportFailureFromUIThread(CrxInstallerError(error));
575     return;
576   }
577 
578   // Check whether this install is initiated from the settings page to
579   // update an existing extension or app.
580   CheckUpdateFromSettingsPage();
581 
582   GURL overlapping_url;
583   const Extension* overlapping_extension =
584       service->extensions()->GetHostedAppByOverlappingWebExtent(
585           extension()->web_extent());
586   if (overlapping_extension &&
587       overlapping_extension->id() != extension()->id()) {
588     ReportFailureFromUIThread(
589         CrxInstallerError(
590             l10n_util::GetStringFUTF16(
591                 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
592                 UTF8ToUTF16(overlapping_extension->name()))));
593     return;
594   }
595 
596   current_version_ =
597       service->extension_prefs()->GetVersionString(extension()->id());
598 
599   if (client_ &&
600       (!allow_silent_install_ || !approved_) &&
601       !update_from_settings_page_) {
602     AddRef();  // Balanced in InstallUIProceed() and InstallUIAbort().
603     client_->ConfirmInstall(this, extension(), show_dialog_callback_);
604   } else {
605     if (!installer_task_runner_->PostTask(
606             FROM_HERE,
607             base::Bind(&CrxInstaller::CompleteInstall, this)))
608       NOTREACHED();
609   }
610   return;
611 }
612 
InstallUIProceed()613 void CrxInstaller::InstallUIProceed() {
614   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
615 
616   ExtensionService* service = service_weak_.get();
617   if (!service || service->browser_terminating())
618     return;
619 
620   // If update_from_settings_page_ boolean is true, this functions is
621   // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
622   // and if it is false, this function is called in response to
623   // ExtensionInstallPrompt::ConfirmInstall().
624   if (update_from_settings_page_) {
625     service->GrantPermissionsAndEnableExtension(extension());
626   } else {
627     if (!installer_task_runner_->PostTask(
628             FROM_HERE,
629             base::Bind(&CrxInstaller::CompleteInstall, this)))
630       NOTREACHED();
631   }
632 
633   Release();  // balanced in ConfirmInstall() or ConfirmReEnable().
634 }
635 
InstallUIAbort(bool user_initiated)636 void CrxInstaller::InstallUIAbort(bool user_initiated) {
637   // If update_from_settings_page_ boolean is true, this functions is
638   // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
639   // and if it is false, this function is called in response to
640   // ExtensionInstallPrompt::ConfirmInstall().
641   if (!update_from_settings_page_) {
642     std::string histogram_name = user_initiated ?
643         "Extensions.Permissions_InstallCancel" :
644         "Extensions.Permissions_InstallAbort";
645     ExtensionService::RecordPermissionMessagesHistogram(
646         extension(), histogram_name.c_str());
647 
648     NotifyCrxInstallComplete(false);
649   }
650 
651   Release();  // balanced in ConfirmInstall() or ConfirmReEnable().
652 
653   // We're done. Since we don't post any more tasks to ourself, our ref count
654   // should go to zero and we die. The destructor will clean up the temp dir.
655 }
656 
CompleteInstall()657 void CrxInstaller::CompleteInstall() {
658   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
659 
660   if (!current_version_.empty()) {
661     Version current_version(current_version_);
662     if (current_version.CompareTo(*(extension()->version())) > 0) {
663       ReportFailureFromFileThread(
664           CrxInstallerError(
665               l10n_util::GetStringUTF16(extension()->is_app() ?
666                   IDS_APP_CANT_DOWNGRADE_VERSION :
667                   IDS_EXTENSION_CANT_DOWNGRADE_VERSION)));
668       return;
669     }
670   }
671 
672   // See how long extension install paths are.  This is important on
673   // windows, because file operations may fail if the path to a file
674   // exceeds a small constant.  See crbug.com/69693 .
675   UMA_HISTOGRAM_CUSTOM_COUNTS(
676     "Extensions.CrxInstallDirPathLength",
677         install_directory_.value().length(), 0, 500, 100);
678 
679   base::FilePath version_dir = extension_file_util::InstallExtension(
680       unpacked_extension_root_,
681       extension()->id(),
682       extension()->VersionString(),
683       install_directory_);
684   if (version_dir.empty()) {
685     ReportFailureFromFileThread(
686         CrxInstallerError(
687             l10n_util::GetStringUTF16(
688                 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)));
689     return;
690   }
691 
692   // This is lame, but we must reload the extension because absolute paths
693   // inside the content scripts are established inside InitFromValue() and we
694   // just moved the extension.
695   // TODO(aa): All paths to resources inside extensions should be created
696   // lazily and based on the Extension's root path at that moment.
697   // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
698   // with base::string16
699   std::string extension_id = extension()->id();
700   std::string error;
701   installer_.set_extension(extension_file_util::LoadExtension(
702       version_dir,
703       install_source_,
704       extension()->creation_flags() | Extension::REQUIRE_KEY,
705       &error).get());
706 
707   if (extension()) {
708     ReportSuccessFromFileThread();
709   } else {
710     LOG(ERROR) << error << " " << extension_id << " " << download_url_;
711     ReportFailureFromFileThread(CrxInstallerError(UTF8ToUTF16(error)));
712   }
713 
714 }
715 
ReportFailureFromFileThread(const CrxInstallerError & error)716 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError& error) {
717   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
718   if (!BrowserThread::PostTask(
719           BrowserThread::UI, FROM_HERE,
720           base::Bind(&CrxInstaller::ReportFailureFromUIThread, this, error))) {
721     NOTREACHED();
722   }
723 }
724 
ReportFailureFromUIThread(const CrxInstallerError & error)725 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError& error) {
726   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
727 
728   content::NotificationService* service =
729       content::NotificationService::current();
730   service->Notify(chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
731                   content::Source<CrxInstaller>(this),
732                   content::Details<const base::string16>(&error.message()));
733 
734   // This isn't really necessary, it is only used because unit tests expect to
735   // see errors get reported via this interface.
736   //
737   // TODO(aa): Need to go through unit tests and clean them up too, probably get
738   // rid of this line.
739   ExtensionErrorReporter::GetInstance()->ReportError(
740       error.message(), false);  // quiet
741 
742   if (client_)
743     client_->OnInstallFailure(error);
744 
745   NotifyCrxInstallComplete(false);
746 
747   // Delete temporary files.
748   CleanupTempFiles();
749 }
750 
ReportSuccessFromFileThread()751 void CrxInstaller::ReportSuccessFromFileThread() {
752   DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
753 
754   // Tracking number of extensions installed by users
755   if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD)
756     UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
757 
758   if (!BrowserThread::PostTask(
759           BrowserThread::UI, FROM_HERE,
760           base::Bind(&CrxInstaller::ReportSuccessFromUIThread, this)))
761     NOTREACHED();
762 
763   // Delete temporary files.
764   CleanupTempFiles();
765 }
766 
ReportSuccessFromUIThread()767 void CrxInstaller::ReportSuccessFromUIThread() {
768   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
769 
770   if (!service_weak_.get() || service_weak_->browser_terminating())
771     return;
772 
773   if (!update_from_settings_page_) {
774     // If there is a client, tell the client about installation.
775     if (client_)
776       client_->OnInstallSuccess(extension(), install_icon_.get());
777 
778     // We update the extension's granted permissions if the user already
779     // approved the install (client_ is non NULL), or we are allowed to install
780     // this silently.
781     if (client_ || allow_silent_install_) {
782       PermissionsUpdater perms_updater(profile());
783       perms_updater.GrantActivePermissions(extension());
784     }
785   }
786 
787   service_weak_->OnExtensionInstalled(extension(),
788                                       page_ordinal_,
789                                       has_requirement_errors_,
790                                       blacklist_state_,
791                                       install_wait_for_idle_);
792   NotifyCrxInstallComplete(true);
793 }
794 
NotifyCrxInstallComplete(bool success)795 void CrxInstaller::NotifyCrxInstallComplete(bool success) {
796   // Some users (such as the download shelf) need to know when a
797   // CRXInstaller is done.  Listening for the EXTENSION_* events
798   // is problematic because they don't know anything about the
799   // extension before it is unpacked, so they cannot filter based
800   // on the extension.
801   content::NotificationService::current()->Notify(
802       chrome::NOTIFICATION_CRX_INSTALLER_DONE,
803       content::Source<CrxInstaller>(this),
804       content::Details<const Extension>(
805           success ? extension() : NULL));
806 
807   if (success)
808     ConfirmReEnable();
809 }
810 
CleanupTempFiles()811 void CrxInstaller::CleanupTempFiles() {
812   if (!installer_task_runner_->RunsTasksOnCurrentThread()) {
813     if (!installer_task_runner_->PostTask(
814             FROM_HERE,
815             base::Bind(&CrxInstaller::CleanupTempFiles, this))) {
816       NOTREACHED();
817     }
818     return;
819   }
820 
821   // Delete the temp directory and crx file as necessary.
822   if (!temp_dir_.value().empty()) {
823     extension_file_util::DeleteFile(temp_dir_, true);
824     temp_dir_ = base::FilePath();
825   }
826 
827   if (delete_source_ && !source_file_.value().empty()) {
828     extension_file_util::DeleteFile(source_file_, false);
829     source_file_ = base::FilePath();
830   }
831 }
832 
CheckUpdateFromSettingsPage()833 void CrxInstaller::CheckUpdateFromSettingsPage() {
834   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
835 
836   ExtensionService* service = service_weak_.get();
837   if (!service || service->browser_terminating())
838     return;
839 
840   if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage)
841     return;
842 
843   const Extension* installed_extension =
844       service->GetInstalledExtension(extension()->id());
845   if (installed_extension) {
846     // Previous version of the extension exists.
847     update_from_settings_page_ = true;
848     expected_id_ = installed_extension->id();
849     install_source_ = installed_extension->location();
850     install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE;
851   }
852 }
853 
ConfirmReEnable()854 void CrxInstaller::ConfirmReEnable() {
855   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
856 
857   ExtensionService* service = service_weak_.get();
858   if (!service || service->browser_terminating())
859     return;
860 
861   if (!update_from_settings_page_)
862     return;
863 
864   ExtensionPrefs* prefs = service->extension_prefs();
865   if (!prefs->DidExtensionEscalatePermissions(extension()->id()))
866     return;
867 
868   if (client_) {
869     AddRef();  // Balanced in InstallUIProceed() and InstallUIAbort().
870     client_->ConfirmReEnable(this, extension());
871   }
872 }
873 
874 }  // namespace extensions
875