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
CreateSilent(ExtensionService * frontend)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
Create(ExtensionService * frontend,scoped_ptr<ExtensionInstallPrompt> client)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
Create(ExtensionService * service,scoped_ptr<ExtensionInstallPrompt> client,const WebstoreInstaller::Approval * approval)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
CrxInstaller(base::WeakPtr<ExtensionService> service_weak,scoped_ptr<ExtensionInstallPrompt> client,const WebstoreInstaller::Approval * approval)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
~CrxInstaller()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
InstallCrx(const base::FilePath & source_file)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
InstallUserScript(const base::FilePath & source_file,const GURL & download_url)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
ConvertUserScriptOnFileThread()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
InstallWebApp(const WebApplicationInfo & web_app)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
ConvertWebAppOnFileThread(const WebApplicationInfo & web_app)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
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 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
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 base::DictionaryValue * original_manifest,const Extension * extension,const SkBitmap & install_icon)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
CheckInstall()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
OnInstallChecksComplete(int failed_checks)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
ConfirmInstall()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
InstallUIProceed()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
InstallUIAbort(bool user_initiated)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
CompleteInstall()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
ReloadExtensionAfterInstall(const base::FilePath & version_dir)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
ReportFailureFromFileThread(const CrxInstallerError & error)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
ReportFailureFromUIThread(const CrxInstallerError & error)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
ReportSuccessFromFileThread()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
ReportSuccessFromUIThread()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
NotifyCrxInstallBegin()814 void CrxInstaller::NotifyCrxInstallBegin() {
815 InstallTrackerFactory::GetForBrowserContext(profile())
816 ->OnBeginCrxInstall(expected_id_);
817 }
818
NotifyCrxInstallComplete(bool success)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
CleanupTempFiles()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
CheckUpdateFromSettingsPage()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
ConfirmReEnable()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
901