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 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/lazy_instance.h"
16 #include "base/macros.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/threading/thread_restrictions.h"
22 #include "base/time/time.h"
23 #include "base/version.h"
24 #include "build/build_config.h"
25 #include "chrome/browser/extensions/blocklist_check.h"
26 #include "chrome/browser/extensions/convert_user_script.h"
27 #include "chrome/browser/extensions/convert_web_app.h"
28 #include "chrome/browser/extensions/extension_assets_manager.h"
29 #include "chrome/browser/extensions/extension_service.h"
30 #include "chrome/browser/extensions/forced_extensions/install_stage_tracker.h"
31 #include "chrome/browser/extensions/install_tracker.h"
32 #include "chrome/browser/extensions/install_tracker_factory.h"
33 #include "chrome/browser/extensions/load_error_reporter.h"
34 #include "chrome/browser/extensions/permissions_updater.h"
35 #include "chrome/browser/extensions/webstore_installer.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/web_applications/components/web_application_info.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/extensions/extension_constants.h"
41 #include "chrome/grit/generated_resources.h"
42 #include "components/crx_file/crx_verifier.h"
43 #include "content/public/browser/browser_task_traits.h"
44 #include "content/public/browser/browser_thread.h"
45 #include "content/public/browser/notification_service.h"
46 #include "extensions/browser/content_verifier.h"
47 #include "extensions/browser/extension_file_task_runner.h"
48 #include "extensions/browser/extension_prefs.h"
49 #include "extensions/browser/extension_registry.h"
50 #include "extensions/browser/extension_system.h"
51 #include "extensions/browser/install/crx_install_error.h"
52 #include "extensions/browser/install/extension_install_ui.h"
53 #include "extensions/browser/install_flag.h"
54 #include "extensions/browser/install_stage.h"
55 #include "extensions/browser/notification_types.h"
56 #include "extensions/browser/policy_check.h"
57 #include "extensions/browser/preload_check_group.h"
58 #include "extensions/browser/requirements_checker.h"
59 #include "extensions/common/extension_icon_set.h"
60 #include "extensions/common/file_util.h"
61 #include "extensions/common/manifest.h"
62 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
63 #include "extensions/common/manifest_handlers/shared_module_info.h"
64 #include "extensions/common/manifest_url_handlers.h"
65 #include "extensions/common/permissions/permission_message_provider.h"
66 #include "extensions/common/permissions/permission_set.h"
67 #include "extensions/common/permissions/permissions_data.h"
68 #include "extensions/common/user_script.h"
69 #include "extensions/common/verifier_formats.h"
70 #include "extensions/strings/grit/extensions_strings.h"
71 #include "third_party/skia/include/core/SkBitmap.h"
72 #include "ui/base/l10n/l10n_util.h"
73
74 #if defined(OS_CHROMEOS)
75 #include "components/user_manager/user_manager.h"
76 #endif
77
78 using content::BrowserThread;
79
80 namespace extensions {
81
82 // static
CreateSilent(ExtensionService * frontend)83 scoped_refptr<CrxInstaller> CrxInstaller::CreateSilent(
84 ExtensionService* frontend) {
85 return new CrxInstaller(frontend->AsWeakPtr(),
86 std::unique_ptr<ExtensionInstallPrompt>(), NULL);
87 }
88
89 // static
Create(ExtensionService * frontend,std::unique_ptr<ExtensionInstallPrompt> client)90 scoped_refptr<CrxInstaller> CrxInstaller::Create(
91 ExtensionService* frontend,
92 std::unique_ptr<ExtensionInstallPrompt> client) {
93 return new CrxInstaller(frontend->AsWeakPtr(), std::move(client), NULL);
94 }
95
96 // static
Create(ExtensionService * service,std::unique_ptr<ExtensionInstallPrompt> client,const WebstoreInstaller::Approval * approval)97 scoped_refptr<CrxInstaller> CrxInstaller::Create(
98 ExtensionService* service,
99 std::unique_ptr<ExtensionInstallPrompt> client,
100 const WebstoreInstaller::Approval* approval) {
101 return new CrxInstaller(service->AsWeakPtr(), std::move(client), approval);
102 }
103
CrxInstaller(base::WeakPtr<ExtensionService> service_weak,std::unique_ptr<ExtensionInstallPrompt> client,const WebstoreInstaller::Approval * approval)104 CrxInstaller::CrxInstaller(base::WeakPtr<ExtensionService> service_weak,
105 std::unique_ptr<ExtensionInstallPrompt> client,
106 const WebstoreInstaller::Approval* approval)
107 : profile_(service_weak->profile()),
108 install_directory_(service_weak->install_directory()),
109 install_source_(Manifest::INTERNAL),
110 approved_(false),
111 verification_check_failed_(false),
112 expected_manifest_check_level_(
113 WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT),
114 fail_install_if_unexpected_version_(false),
115 extensions_enabled_(service_weak->extensions_enabled()),
116 delete_source_(false),
117 service_weak_(service_weak),
118 // See header file comment on |client_| for why we use a raw pointer here.
119 client_(client.release()),
120 apps_require_extension_mime_type_(false),
121 allow_silent_install_(false),
122 grant_permissions_(true),
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 shared_file_task_runner_(GetExtensionFileTaskRunner()),
129 update_from_settings_page_(false),
130 install_flags_(kInstallFlagNone) {
131 if (!approval)
132 return;
133
134 CHECK(profile()->IsSameOrParent(approval->profile));
135 if (client_) {
136 client_->install_ui()->SetUseAppInstalledBubble(
137 approval->use_app_installed_bubble);
138 client_->install_ui()->SetSkipPostInstallUI(approval->skip_post_install_ui);
139 }
140
141 if (approval->skip_install_dialog) {
142 // Mark the extension as approved, but save the expected manifest and ID
143 // so we can check that they match the CRX's.
144 approved_ = true;
145 expected_manifest_check_level_ = approval->manifest_check_level;
146 if (expected_manifest_check_level_ !=
147 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE) {
148 expected_manifest_ = approval->manifest->CreateDeepCopy();
149 }
150 expected_id_ = approval->extension_id;
151 }
152 if (approval->minimum_version.get())
153 minimum_version_ = base::Version(*approval->minimum_version);
154
155 show_dialog_callback_ = approval->show_dialog_callback;
156 }
157
~CrxInstaller()158 CrxInstaller::~CrxInstaller() {
159 DCHECK(!service_weak_ || service_weak_->browser_terminating() ||
160 installer_callback_.is_null());
161 DCHECK_CURRENTLY_ON(BrowserThread::UI);
162 // Ensure |client_| and |install_checker_| data members are destroyed on the
163 // UI thread. The |client_| dialog has a weak reference as |this| is its
164 // delegate, and |install_checker_| owns WeakPtrs, so must be destroyed on the
165 // same thread that created it.
166 }
167
InstallCrx(const base::FilePath & source_file)168 void CrxInstaller::InstallCrx(const base::FilePath& source_file) {
169 crx_file::VerifierFormat format =
170 off_store_install_allow_reason_ == OffStoreInstallDisallowed
171 ? GetWebstoreVerifierFormat(
172 base::CommandLine::ForCurrentProcess()->HasSwitch(
173 ::switches::kAppsGalleryURL))
174 : GetExternalVerifierFormat();
175 InstallCrxFile(CRXFileInfo(source_file, format));
176 }
177
InstallCrxFile(const CRXFileInfo & source_file)178 void CrxInstaller::InstallCrxFile(const CRXFileInfo& source_file) {
179 ExtensionService* service = service_weak_.get();
180 if (!service || service->browser_terminating())
181 return;
182
183 NotifyCrxInstallBegin();
184
185 source_file_ = source_file.path;
186
187 auto unpacker = base::MakeRefCounted<SandboxedUnpacker>(
188 install_source_, creation_flags_, install_directory_,
189 GetUnpackerTaskRunner(), this);
190
191 if (!GetUnpackerTaskRunner()->PostTask(
192 FROM_HERE, base::BindOnce(&SandboxedUnpacker::StartWithCrx, unpacker,
193 source_file))) {
194 NOTREACHED();
195 }
196 }
197
InstallUnpackedCrx(const std::string & extension_id,const std::string & public_key,const base::FilePath & unpacked_dir)198 void CrxInstaller::InstallUnpackedCrx(const std::string& extension_id,
199 const std::string& public_key,
200 const base::FilePath& unpacked_dir) {
201 ExtensionService* service = service_weak_.get();
202 if (!service || service->browser_terminating())
203 return;
204
205 NotifyCrxInstallBegin();
206
207 source_file_ = unpacked_dir;
208
209 auto unpacker = base::MakeRefCounted<SandboxedUnpacker>(
210 install_source_, creation_flags_, install_directory_,
211 GetUnpackerTaskRunner(), this);
212
213 if (!GetUnpackerTaskRunner()->PostTask(
214 FROM_HERE,
215 base::BindOnce(&SandboxedUnpacker::StartWithDirectory, unpacker,
216 extension_id, public_key, unpacked_dir))) {
217 NOTREACHED();
218 }
219 }
220
InstallUserScript(const base::FilePath & source_file,const GURL & download_url)221 void CrxInstaller::InstallUserScript(const base::FilePath& source_file,
222 const GURL& download_url) {
223 DCHECK(!download_url.is_empty());
224
225 NotifyCrxInstallBegin();
226
227 source_file_ = source_file;
228 download_url_ = download_url;
229
230 if (!shared_file_task_runner_->PostTask(
231 FROM_HERE,
232 base::BindOnce(&CrxInstaller::ConvertUserScriptOnSharedFileThread,
233 this)))
234 NOTREACHED();
235 }
236
ConvertUserScriptOnSharedFileThread()237 void CrxInstaller::ConvertUserScriptOnSharedFileThread() {
238 base::string16 error;
239 scoped_refptr<Extension> extension = ConvertUserScriptToExtension(
240 source_file_, download_url_, install_directory_, &error);
241 if (!extension.get()) {
242 ReportFailureFromSharedFileThread(CrxInstallError(
243 CrxInstallErrorType::OTHER,
244 CrxInstallErrorDetail::CONVERT_USER_SCRIPT_TO_EXTENSION_FAILED, error));
245 return;
246 }
247
248 OnUnpackSuccessOnSharedFileThread(extension->path(), extension->path(),
249 nullptr, extension, SkBitmap(),
250 {} /* ruleset_install_prefs */);
251 }
252
InstallWebApp(const WebApplicationInfo & web_app)253 void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
254 NotifyCrxInstallBegin();
255
256 if (!shared_file_task_runner_->PostTask(
257 FROM_HERE,
258 base::BindOnce(&CrxInstaller::ConvertWebAppOnSharedFileThread, this,
259 web_app)))
260 NOTREACHED();
261 }
262
UpdateExtensionFromUnpackedCrx(const std::string & extension_id,const std::string & public_key,const base::FilePath & unpacked_dir)263 void CrxInstaller::UpdateExtensionFromUnpackedCrx(
264 const std::string& extension_id,
265 const std::string& public_key,
266 const base::FilePath& unpacked_dir) {
267 ExtensionService* service = service_weak_.get();
268 if (!service || service->browser_terminating())
269 return;
270
271 const Extension* extension = ExtensionRegistry::Get(service->profile())
272 ->GetInstalledExtension(extension_id);
273 if (!extension) {
274 LOG(WARNING) << "Will not update extension " << extension_id
275 << " because it is not installed";
276 if (delete_source_)
277 temp_dir_ = unpacked_dir;
278 if (installer_callback_.is_null()) {
279 shared_file_task_runner_->PostTask(
280 FROM_HERE, base::BindOnce(&CrxInstaller::CleanupTempFiles, this));
281 } else {
282 shared_file_task_runner_->PostTaskAndReply(
283 FROM_HERE, base::BindOnce(&CrxInstaller::CleanupTempFiles, this),
284 base::BindOnce(
285 std::move(installer_callback_),
286 CrxInstallError(
287 CrxInstallErrorType::OTHER,
288 CrxInstallErrorDetail::UPDATE_NON_EXISTING_EXTENSION)));
289 }
290 return;
291 }
292
293 expected_id_ = extension_id;
294 install_source_ = extension->location();
295 install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE;
296 InitializeCreationFlagsForUpdate(extension, Extension::NO_FLAGS);
297
298 const ExtensionPrefs* extension_prefs =
299 ExtensionPrefs::Get(service->GetBrowserContext());
300 DCHECK(extension_prefs);
301 set_do_not_sync(extension_prefs->DoNotSync(extension_id));
302
303 InstallUnpackedCrx(extension_id, public_key, unpacked_dir);
304 }
305
ConvertWebAppOnSharedFileThread(const WebApplicationInfo & web_app)306 void CrxInstaller::ConvertWebAppOnSharedFileThread(
307 const WebApplicationInfo& web_app) {
308 scoped_refptr<Extension> extension(
309 ConvertWebAppToExtension(web_app, base::Time::Now(), install_directory_,
310 creation_flags_, install_source_));
311 if (!extension.get()) {
312 // Validation should have stopped any potential errors before getting here.
313 NOTREACHED() << "Could not convert web app to extension.";
314 return;
315 }
316
317 // TODO(aa): conversion data gets lost here :(
318
319 OnUnpackSuccessOnSharedFileThread(extension->path(), extension->path(),
320 nullptr, extension, SkBitmap(),
321 {} /* ruleset_install_prefs */);
322 }
323
CheckExpectations(const Extension * extension)324 base::Optional<CrxInstallError> CrxInstaller::CheckExpectations(
325 const Extension* extension) {
326 DCHECK(shared_file_task_runner_->RunsTasksInCurrentSequence());
327
328 // Make sure the expected ID matches if one was supplied or if we want to
329 // bypass the prompt.
330 if ((approved_ || !expected_id_.empty()) &&
331 expected_id_ != extension->id()) {
332 return CrxInstallError(
333 CrxInstallErrorType::OTHER, CrxInstallErrorDetail::UNEXPECTED_ID,
334 l10n_util::GetStringFUTF16(IDS_EXTENSION_INSTALL_UNEXPECTED_ID,
335 base::ASCIIToUTF16(expected_id_),
336 base::ASCIIToUTF16(extension->id())));
337 }
338
339 if (expected_version_.IsValid() && fail_install_if_unexpected_version_ &&
340 expected_version_ != extension->version()) {
341 return CrxInstallError(
342 CrxInstallErrorType::OTHER, CrxInstallErrorDetail::MISMATCHED_VERSION,
343 l10n_util::GetStringFUTF16(
344 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
345 base::ASCIIToUTF16(expected_version_.GetString()),
346 base::ASCIIToUTF16(extension->version().GetString())));
347 }
348
349 return base::nullopt;
350 }
351
AllowInstall(const Extension * extension)352 base::Optional<CrxInstallError> CrxInstaller::AllowInstall(
353 const Extension* extension) {
354 DCHECK(shared_file_task_runner_->RunsTasksInCurrentSequence());
355
356 if (minimum_version_.IsValid() &&
357 extension->version().CompareTo(minimum_version_) < 0) {
358 return CrxInstallError(
359 CrxInstallErrorType::OTHER, CrxInstallErrorDetail::UNEXPECTED_VERSION,
360 l10n_util::GetStringFUTF16(
361 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
362 base::ASCIIToUTF16(minimum_version_.GetString() + "+"),
363 base::ASCIIToUTF16(extension->version().GetString())));
364 }
365
366 // Make sure the manifests match if we want to bypass the prompt.
367 if (approved_) {
368 bool valid = false;
369 if (expected_manifest_check_level_ ==
370 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE) {
371 // To skip manifest checking, the extension must be a shared module
372 // and not request any permissions.
373 if (SharedModuleInfo::IsSharedModule(extension) &&
374 extension->permissions_data()->active_permissions().IsEmpty()) {
375 valid = true;
376 }
377 } else {
378 valid = expected_manifest_->Equals(original_manifest_.get());
379 if (!valid && expected_manifest_check_level_ ==
380 WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE) {
381 std::string error;
382 scoped_refptr<Extension> dummy_extension =
383 Extension::Create(base::FilePath(),
384 install_source_,
385 *expected_manifest_->value(),
386 creation_flags_,
387 extension->id(),
388 &error);
389 if (error.empty()) {
390 valid = !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
391 dummy_extension->permissions_data()->active_permissions(),
392 extension->permissions_data()->active_permissions(),
393 extension->GetType()));
394 }
395 }
396 }
397
398 if (!valid)
399 return CrxInstallError(
400 CrxInstallErrorType::OTHER, CrxInstallErrorDetail::MANIFEST_INVALID,
401 l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID));
402 }
403
404 // The checks below are skipped for themes, external installs, and bookmark
405 // apps.
406 // TODO(pamg): After ManagementPolicy refactoring is complete, remove this
407 // and other uses of install_source_ that are no longer needed now that the
408 // SandboxedUnpacker sets extension->location.
409 if (extension->is_theme() || extension->from_bookmark() ||
410 Manifest::IsExternalLocation(install_source_)) {
411 return base::nullopt;
412 }
413
414 if (!extensions_enabled_) {
415 return CrxInstallError(
416 CrxInstallErrorType::DECLINED,
417 CrxInstallErrorDetail::INSTALL_NOT_ENABLED,
418 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_NOT_ENABLED));
419 }
420
421 if (install_cause_ == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD &&
422 !is_gallery_install() &&
423 off_store_install_allow_reason_ == OffStoreInstallDisallowed) {
424 // Don't delete source in this case so that the user can install
425 // manually if they want.
426 delete_source_ = false;
427 did_handle_successfully_ = false;
428
429 return CrxInstallError(
430 CrxInstallErrorType::OTHER,
431 CrxInstallErrorDetail::OFFSTORE_INSTALL_DISALLOWED,
432 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE));
433 }
434
435 if (extension_->is_app()) {
436 // If the app was downloaded, apps_require_extension_mime_type_
437 // will be set. In this case, check that it was served with the
438 // right mime type. Make an exception for file URLs, which come
439 // from the users computer and have no headers.
440 if (!download_url_.SchemeIsFile() &&
441 apps_require_extension_mime_type_ &&
442 original_mime_type_ != Extension::kMimeType) {
443 return CrxInstallError(
444 CrxInstallErrorType::OTHER,
445 CrxInstallErrorDetail::INCORRECT_APP_CONTENT_TYPE,
446 l10n_util::GetStringFUTF16(
447 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE,
448 base::ASCIIToUTF16(Extension::kMimeType)));
449 }
450
451 // If the client_ is NULL, then the app is either being installed via
452 // an internal mechanism like sync, external_extensions, or default apps.
453 // In that case, we don't want to enforce things like the install origin.
454 if (!is_gallery_install() && client_) {
455 // For apps with a gallery update URL, require that they be installed
456 // from the gallery.
457 // TODO(erikkay) Apply this rule for paid extensions and themes as well.
458 if (ManifestURL::UpdatesFromGallery(extension)) {
459 return CrxInstallError(
460 CrxInstallErrorType::OTHER,
461 CrxInstallErrorDetail::NOT_INSTALLED_FROM_GALLERY,
462 l10n_util::GetStringFUTF16(
463 IDS_EXTENSION_INSTALL_GALLERY_ONLY,
464 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
465 }
466
467 // For self-hosted apps, verify that the entire extent is on the same
468 // host (or a subdomain of the host) the download happened from. There's
469 // no way for us to verify that the app controls any other hosts.
470 URLPattern pattern(UserScript::ValidUserScriptSchemes());
471 pattern.SetHost(download_url_.host());
472 pattern.SetMatchSubdomains(true);
473
474 const URLPatternSet& patterns = extension_->web_extent();
475 for (auto i = patterns.begin(); i != patterns.end(); ++i) {
476 if (!pattern.MatchesHost(i->host())) {
477 return CrxInstallError(
478 CrxInstallErrorType::OTHER,
479 CrxInstallErrorDetail::INCORRECT_INSTALL_HOST,
480 l10n_util::GetStringUTF16(
481 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST));
482 }
483 }
484 }
485 }
486
487 return base::nullopt;
488 }
489
ShouldComputeHashesOnUI(scoped_refptr<const Extension> extension,base::OnceCallback<void (bool)> callback)490 void CrxInstaller::ShouldComputeHashesOnUI(
491 scoped_refptr<const Extension> extension,
492 base::OnceCallback<void(bool)> callback) {
493 DCHECK_CURRENTLY_ON(BrowserThread::UI);
494 ExtensionService* service = service_weak_.get();
495 if (!service || service->browser_terminating())
496 return;
497
498 extensions::ContentVerifier* content_verifier =
499 extensions::ExtensionSystem::Get(profile_)->content_verifier();
500 bool result = content_verifier &&
501 content_verifier->ShouldComputeHashesOnInstall(*extension);
502 GetUnpackerTaskRunner()->PostTask(
503 FROM_HERE, base::BindOnce(std::move(callback), result));
504 }
505
ShouldComputeHashesForOffWebstoreExtension(scoped_refptr<const Extension> extension,base::OnceCallback<void (bool)> callback)506 void CrxInstaller::ShouldComputeHashesForOffWebstoreExtension(
507 scoped_refptr<const Extension> extension,
508 base::OnceCallback<void(bool)> callback) {
509 DCHECK(GetUnpackerTaskRunner()->RunsTasksInCurrentSequence());
510 if (!content::GetUIThreadTaskRunner({})->PostTask(
511 FROM_HERE,
512 base::BindOnce(&CrxInstaller::ShouldComputeHashesOnUI, this,
513 std::move(extension), std::move(callback)))) {
514 NOTREACHED();
515 }
516 }
517
OnUnpackFailure(const CrxInstallError & error)518 void CrxInstaller::OnUnpackFailure(const CrxInstallError& error) {
519 DCHECK(GetUnpackerTaskRunner()->RunsTasksInCurrentSequence());
520 if (!content::GetUIThreadTaskRunner({})->PostTask(
521 FROM_HERE, base::BindOnce(&CrxInstaller::ReportFailureFromUIThread,
522 this, error))) {
523 NOTREACHED();
524 }
525 }
526
OnUnpackSuccess(const base::FilePath & temp_dir,const base::FilePath & extension_dir,std::unique_ptr<base::DictionaryValue> original_manifest,const Extension * extension,const SkBitmap & install_icon,declarative_net_request::RulesetInstallPrefs ruleset_install_prefs)527 void CrxInstaller::OnUnpackSuccess(
528 const base::FilePath& temp_dir,
529 const base::FilePath& extension_dir,
530 std::unique_ptr<base::DictionaryValue> original_manifest,
531 const Extension* extension,
532 const SkBitmap& install_icon,
533 declarative_net_request::RulesetInstallPrefs ruleset_install_prefs) {
534 DCHECK(GetUnpackerTaskRunner()->RunsTasksInCurrentSequence());
535 shared_file_task_runner_->PostTask(
536 FROM_HERE,
537 base::BindOnce(&CrxInstaller::OnUnpackSuccessOnSharedFileThread, this,
538 temp_dir, extension_dir, std::move(original_manifest),
539 scoped_refptr<const Extension>(extension), install_icon,
540 std::move(ruleset_install_prefs)));
541 }
542
OnUnpackSuccessOnSharedFileThread(base::FilePath temp_dir,base::FilePath extension_dir,std::unique_ptr<base::DictionaryValue> original_manifest,scoped_refptr<const Extension> extension,SkBitmap install_icon,declarative_net_request::RulesetInstallPrefs ruleset_install_prefs)543 void CrxInstaller::OnUnpackSuccessOnSharedFileThread(
544 base::FilePath temp_dir,
545 base::FilePath extension_dir,
546 std::unique_ptr<base::DictionaryValue> original_manifest,
547 scoped_refptr<const Extension> extension,
548 SkBitmap install_icon,
549 declarative_net_request::RulesetInstallPrefs ruleset_install_prefs) {
550 DCHECK(shared_file_task_runner_->RunsTasksInCurrentSequence());
551
552 extension_ = extension;
553 temp_dir_ = temp_dir;
554 ruleset_install_prefs_ = std::move(ruleset_install_prefs);
555 ReportInstallationStage(InstallationStage::kCheckingExpectations);
556
557 if (!install_icon.empty())
558 install_icon_ = std::make_unique<SkBitmap>(install_icon);
559
560 if (original_manifest) {
561 original_manifest_ = std::make_unique<Manifest>(
562 Manifest::INVALID_LOCATION, std::move(original_manifest));
563 }
564
565 // We don't have to delete the unpack dir explicity since it is a child of
566 // the temp dir.
567 unpacked_extension_root_ = extension_dir;
568
569 // Check whether the crx matches the set expectations.
570 base::Optional<CrxInstallError> expectations_error =
571 CheckExpectations(extension.get());
572 if (expectations_error) {
573 DCHECK_NE(CrxInstallErrorType::NONE, expectations_error->type());
574 ReportFailureFromSharedFileThread(*expectations_error);
575 return;
576 }
577
578 // The |expectations_error| could be non-null in case of version mismatch if
579 // |fail_install_if_unexpected_version_| is set to false.
580 // If |expectations_passed_callback_| is set, the installer owns the crx file,
581 // and there is no version mismatch, invoke the callback and transfer the
582 // ownership. The responsibility to delete the crx file now lies with the
583 // callback.
584 if (!expectations_verified_callback_.is_null() && delete_source_ &&
585 (!expected_version_.IsValid() ||
586 expected_version_ == extension->version())) {
587 delete_source_ = false;
588 if (!content::GetUIThreadTaskRunner({})->PostTask(
589 FROM_HERE,
590 base::BindOnce(std::move(expectations_verified_callback_)))) {
591 NOTREACHED();
592 }
593 }
594
595 base::Optional<CrxInstallError> error = AllowInstall(extension.get());
596 if (error) {
597 DCHECK_NE(CrxInstallErrorType::NONE, error->type());
598 ReportFailureFromSharedFileThread(*error);
599 return;
600 }
601
602 if (!content::GetUIThreadTaskRunner({})->PostTask(
603 FROM_HERE, base::BindOnce(&CrxInstaller::CheckInstall, this)))
604 NOTREACHED();
605 }
606
OnStageChanged(InstallationStage stage)607 void CrxInstaller::OnStageChanged(InstallationStage stage) {
608 ReportInstallationStage(stage);
609 }
610
CheckInstall()611 void CrxInstaller::CheckInstall() {
612 DCHECK_CURRENTLY_ON(BrowserThread::UI);
613 ExtensionService* service = service_weak_.get();
614 if (!service || service->browser_terminating())
615 return;
616
617 // TODO(crbug.com/420147): Move this code to a utility class to avoid
618 // duplication of SharedModuleService::CheckImports code.
619 if (SharedModuleInfo::ImportsModules(extension())) {
620 const std::vector<SharedModuleInfo::ImportInfo>& imports =
621 SharedModuleInfo::GetImports(extension());
622 ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile());
623 for (const auto& import : imports) {
624 const Extension* imported_module = registry->GetExtensionById(
625 import.extension_id, ExtensionRegistry::EVERYTHING);
626 if (!imported_module)
627 continue;
628
629 if (!SharedModuleInfo::IsSharedModule(imported_module)) {
630 ReportFailureFromUIThread(CrxInstallError(
631 CrxInstallErrorType::DECLINED,
632 CrxInstallErrorDetail::DEPENDENCY_NOT_SHARED_MODULE,
633 l10n_util::GetStringFUTF16(
634 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE,
635 base::UTF8ToUTF16(imported_module->name()))));
636 return;
637 }
638 base::Version version_required(import.minimum_version);
639 if (version_required.IsValid() &&
640 imported_module->version().CompareTo(version_required) < 0) {
641 ReportFailureFromUIThread(CrxInstallError(
642 CrxInstallErrorType::DECLINED,
643 CrxInstallErrorDetail::DEPENDENCY_OLD_VERSION,
644 l10n_util::GetStringFUTF16(
645 IDS_EXTENSION_INSTALL_DEPENDENCY_OLD_VERSION,
646 base::UTF8ToUTF16(imported_module->name()),
647 base::ASCIIToUTF16(import.minimum_version),
648 base::ASCIIToUTF16(imported_module->version().GetString()))));
649 return;
650 }
651 if (!SharedModuleInfo::IsExportAllowedByAllowlist(imported_module,
652 extension()->id())) {
653 ReportFailureFromUIThread(CrxInstallError(
654 CrxInstallErrorType::DECLINED,
655 CrxInstallErrorDetail::DEPENDENCY_NOT_ALLOWLISTED,
656 l10n_util::GetStringFUTF16(
657 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_ALLOWLISTED,
658 base::UTF8ToUTF16(extension()->name()),
659 base::UTF8ToUTF16(imported_module->name()))));
660 return;
661 }
662 }
663 }
664
665 // Skip the checks if the extension is a bookmark app.
666 if (extension()->from_bookmark()) {
667 ConfirmInstall();
668 return;
669 }
670
671 // Run the policy, requirements and blocklist checks in parallel.
672 check_group_ = std::make_unique<PreloadCheckGroup>();
673
674 policy_check_ = std::make_unique<PolicyCheck>(profile_, extension());
675 requirements_check_ = std::make_unique<RequirementsChecker>(extension());
676 blocklist_check_ =
677 std::make_unique<BlocklistCheck>(Blocklist::Get(profile_), extension_);
678
679 check_group_->AddCheck(policy_check_.get());
680 check_group_->AddCheck(requirements_check_.get());
681 check_group_->AddCheck(blocklist_check_.get());
682
683 check_group_->Start(
684 base::BindOnce(&CrxInstaller::OnInstallChecksComplete, this));
685 }
686
OnInstallChecksComplete(const PreloadCheck::Errors & errors)687 void CrxInstaller::OnInstallChecksComplete(const PreloadCheck::Errors& errors) {
688 DCHECK_CURRENTLY_ON(BrowserThread::UI);
689 if (!service_weak_)
690 return;
691
692 if (errors.empty()) {
693 ConfirmInstall();
694 return;
695 }
696
697 // Check for requirement errors.
698 if (!requirements_check_->GetErrorMessage().empty()) {
699 if (error_on_unsupported_requirements_) {
700 ReportFailureFromUIThread(
701 CrxInstallError(CrxInstallErrorType::DECLINED,
702 CrxInstallErrorDetail::UNSUPPORTED_REQUIREMENTS,
703 requirements_check_->GetErrorMessage()));
704 return;
705 }
706 install_flags_ |= kInstallFlagHasRequirementErrors;
707 }
708
709 // Check the blocklist state.
710 if (errors.count(PreloadCheck::BLOCKLISTED_ID) ||
711 errors.count(PreloadCheck::BLOCKLISTED_UNKNOWN)) {
712 if (allow_silent_install_) {
713 // NOTE: extension may still be blocklisted, but we're forced to silently
714 // install it. In this case, ExtensionService::OnExtensionInstalled needs
715 // to deal with it.
716 if (errors.count(PreloadCheck::BLOCKLISTED_ID))
717 install_flags_ |= kInstallFlagIsBlocklistedForMalware;
718 } else {
719 // User tried to install a blocklisted extension. Show an error and
720 // refuse to install it.
721 ReportFailureFromUIThread(CrxInstallError(
722 CrxInstallErrorType::DECLINED,
723 CrxInstallErrorDetail::EXTENSION_IS_BLOCKLISTED,
724 l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLOCKLISTED,
725 base::UTF8ToUTF16(extension()->name()))));
726 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
727 extension()->location(),
728 Manifest::NUM_LOCATIONS);
729 return;
730 }
731 }
732
733 // Check for policy errors.
734 if (errors.count(PreloadCheck::DISALLOWED_BY_POLICY)) {
735 // We don't want to show the error infobar for installs from the WebStore,
736 // because the WebStore already shows an error dialog itself.
737 // Note: |client_| can be NULL in unit_tests!
738 if (extension()->from_webstore() && client_)
739 client_->install_ui()->SetSkipPostInstallUI(true);
740
741 ReportFailureFromUIThread(
742 CrxInstallError(CrxInstallErrorType::DECLINED,
743 CrxInstallErrorDetail::DISALLOWED_BY_POLICY,
744 policy_check_->GetErrorMessage()));
745 return;
746 }
747
748 ConfirmInstall();
749 }
750
ConfirmInstall()751 void CrxInstaller::ConfirmInstall() {
752 DCHECK_CURRENTLY_ON(BrowserThread::UI);
753 ReportInstallationStage(InstallationStage::kFinalizing);
754 ExtensionService* service = service_weak_.get();
755 if (!service || service->browser_terminating())
756 return;
757
758 if (KioskModeInfo::IsKioskOnly(extension())) {
759 bool in_kiosk_mode = false;
760 #if defined(OS_CHROMEOS)
761 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
762 in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
763 #endif
764 if (!in_kiosk_mode) {
765 ReportFailureFromUIThread(CrxInstallError(
766 CrxInstallErrorType::DECLINED, CrxInstallErrorDetail::KIOSK_MODE_ONLY,
767 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY)));
768 return;
769 }
770 }
771
772 // Check whether this install is initiated from the settings page to
773 // update an existing extension or app.
774 CheckUpdateFromSettingsPage();
775
776 GURL overlapping_url;
777 ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile());
778 const Extension* overlapping_extension =
779 registry->enabled_extensions().GetHostedAppByOverlappingWebExtent(
780 extension()->web_extent());
781 if (overlapping_extension &&
782 overlapping_extension->id() != extension()->id()) {
783 ReportFailureFromUIThread(
784 CrxInstallError(CrxInstallErrorType::OTHER,
785 CrxInstallErrorDetail::OVERLAPPING_WEB_EXTENT,
786 l10n_util::GetStringFUTF16(
787 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
788 base::UTF8ToUTF16(extension()->name()),
789 base::UTF8ToUTF16(overlapping_extension->name()))));
790 return;
791 }
792
793 current_version_ = base::Version(ExtensionPrefs::Get(service->profile())
794 ->GetVersionString(extension()->id()));
795
796 if (client_ &&
797 (!allow_silent_install_ || !approved_) &&
798 !update_from_settings_page_) {
799 AddRef(); // Balanced in OnInstallPromptDone().
800 client_->ShowDialog(base::Bind(&CrxInstaller::OnInstallPromptDone, this),
801 extension(), nullptr, show_dialog_callback_);
802 } else {
803 UpdateCreationFlagsAndCompleteInstall(kDontWithholdPermissions);
804 }
805 }
806
OnInstallPromptDone(ExtensionInstallPrompt::Result result)807 void CrxInstaller::OnInstallPromptDone(ExtensionInstallPrompt::Result result) {
808 DCHECK_CURRENTLY_ON(BrowserThread::UI);
809
810 // If update_from_settings_page_ boolean is true, this functions is
811 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
812 // and if it is false, this function is called in response to
813 // ExtensionInstallPrompt::ShowDialog().
814
815 ExtensionService* service = service_weak_.get();
816 switch (result) {
817 case ExtensionInstallPrompt::Result::ACCEPTED:
818 if (!service || service->browser_terminating())
819 return;
820
821 // Install (or re-enable) the extension with full permissions.
822 if (update_from_settings_page_)
823 service->GrantPermissionsAndEnableExtension(extension());
824 else
825 UpdateCreationFlagsAndCompleteInstall(kDontWithholdPermissions);
826 break;
827 case ExtensionInstallPrompt::Result::ACCEPTED_AND_OPTION_CHECKED:
828 if (!service || service->browser_terminating())
829 return;
830
831 // TODO(tjudkins): Add support for withholding permissions on the
832 // re-enable prompt here once we know how that should be handled.
833 DCHECK(!update_from_settings_page_);
834 // Install the extension with permissions withheld.
835 UpdateCreationFlagsAndCompleteInstall(kWithholdPermissions);
836 break;
837 case ExtensionInstallPrompt::Result::USER_CANCELED:
838 if (!update_from_settings_page_) {
839 ExtensionService::RecordPermissionMessagesHistogram(extension(),
840 "InstallCancel");
841 NotifyCrxInstallComplete(CrxInstallError(
842 CrxInstallErrorType::OTHER, CrxInstallErrorDetail::USER_CANCELED));
843 }
844 break;
845 case ExtensionInstallPrompt::Result::ABORTED:
846 if (!update_from_settings_page_) {
847 ExtensionService::RecordPermissionMessagesHistogram(extension(),
848 "InstallAbort");
849 NotifyCrxInstallComplete(CrxInstallError(
850 CrxInstallErrorType::OTHER, CrxInstallErrorDetail::USER_ABORTED));
851 }
852 break;
853 }
854
855 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
856 }
857
InitializeCreationFlagsForUpdate(const Extension * extension,const int initial_flags)858 void CrxInstaller::InitializeCreationFlagsForUpdate(const Extension* extension,
859 const int initial_flags) {
860 DCHECK(extension);
861
862 creation_flags_ = initial_flags;
863
864 // If the extension was installed from or has migrated to the webstore, or
865 // its auto-update URL is from the webstore, treat it as a webstore install.
866 // Note that we ignore some older extensions with blank auto-update URLs
867 // because we are mostly concerned with restrictions on NaCl extensions,
868 // which are newer.
869 if (extension->from_webstore() || ManifestURL::UpdatesFromGallery(extension))
870 creation_flags_ |= Extension::FROM_WEBSTORE;
871
872 // Bookmark apps being updated is kind of a contradiction, but that's because
873 // we mark the default apps as bookmark apps, and they're hosted in the web
874 // store, thus they can get updated. See http://crbug.com/101605 for more
875 // details.
876 if (extension->from_bookmark())
877 creation_flags_ |= Extension::FROM_BOOKMARK;
878
879 if (extension->was_installed_by_default())
880 creation_flags_ |= Extension::WAS_INSTALLED_BY_DEFAULT;
881
882 if (extension->was_installed_by_oem())
883 creation_flags_ |= Extension::WAS_INSTALLED_BY_OEM;
884 }
885
UpdateCreationFlagsAndCompleteInstall(WithholdingBehavior withholding_behavior)886 void CrxInstaller::UpdateCreationFlagsAndCompleteInstall(
887 WithholdingBehavior withholding_behavior) {
888 creation_flags_ = extension()->creation_flags() | Extension::REQUIRE_KEY;
889 // If the extension was already installed and had file access, also grant file
890 // access to the updated extension.
891 if (ExtensionPrefs::Get(profile())->AllowFileAccess(extension()->id()))
892 creation_flags_ |= Extension::ALLOW_FILE_ACCESS;
893
894 if (withholding_behavior == kWithholdPermissions)
895 creation_flags_ |= Extension::WITHHOLD_PERMISSIONS;
896
897 if (!shared_file_task_runner_->PostTask(
898 FROM_HERE, base::BindOnce(&CrxInstaller::CompleteInstall, this))) {
899 NOTREACHED();
900 }
901 }
902
CompleteInstall()903 void CrxInstaller::CompleteInstall() {
904 DCHECK(shared_file_task_runner_->RunsTasksInCurrentSequence());
905
906 if (current_version_.IsValid() &&
907 current_version_.CompareTo(extension()->version()) > 0) {
908 ReportFailureFromSharedFileThread(CrxInstallError(
909 CrxInstallErrorType::DECLINED,
910 CrxInstallErrorDetail::CANT_DOWNGRADE_VERSION,
911 l10n_util::GetStringUTF16(extension()->is_app()
912 ? IDS_APP_CANT_DOWNGRADE_VERSION
913 : IDS_EXTENSION_CANT_DOWNGRADE_VERSION)));
914 return;
915 }
916
917 ExtensionAssetsManager* assets_manager =
918 ExtensionAssetsManager::GetInstance();
919 assets_manager->InstallExtension(
920 extension(),
921 unpacked_extension_root_,
922 install_directory_,
923 profile(),
924 base::Bind(&CrxInstaller::ReloadExtensionAfterInstall, this));
925 }
926
ReloadExtensionAfterInstall(const base::FilePath & version_dir)927 void CrxInstaller::ReloadExtensionAfterInstall(
928 const base::FilePath& version_dir) {
929 DCHECK(shared_file_task_runner_->RunsTasksInCurrentSequence());
930
931 if (version_dir.empty()) {
932 ReportFailureFromSharedFileThread(
933 CrxInstallError(CrxInstallErrorType::OTHER,
934 CrxInstallErrorDetail::MOVE_DIRECTORY_TO_PROFILE_FAILED,
935 l10n_util::GetStringUTF16(
936 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)));
937 return;
938 }
939
940 // This is lame, but we must reload the extension because absolute paths
941 // inside the content scripts are established inside InitFromValue() and we
942 // just moved the extension.
943 // TODO(aa): All paths to resources inside extensions should be created
944 // lazily and based on the Extension's root path at that moment.
945 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
946 // with base::string16
947 std::string extension_id = extension()->id();
948 std::string error;
949 extension_ = file_util::LoadExtension(
950 version_dir, install_source_,
951 // Note: modified by UpdateCreationFlagsAndCompleteInstall.
952 creation_flags_, &error);
953
954 if (extension()) {
955 ReportSuccessFromSharedFileThread();
956 } else {
957 LOG(ERROR) << error << " " << extension_id << " " << download_url_;
958 ReportFailureFromSharedFileThread(CrxInstallError(
959 CrxInstallErrorType::OTHER, CrxInstallErrorDetail::CANT_LOAD_EXTENSION,
960 base::UTF8ToUTF16(error)));
961 }
962 }
963
ReportFailureFromSharedFileThread(const CrxInstallError & error)964 void CrxInstaller::ReportFailureFromSharedFileThread(
965 const CrxInstallError& error) {
966 DCHECK(shared_file_task_runner_->RunsTasksInCurrentSequence());
967 if (!content::GetUIThreadTaskRunner({})->PostTask(
968 FROM_HERE, base::BindOnce(&CrxInstaller::ReportFailureFromUIThread,
969 this, error))) {
970 NOTREACHED();
971 }
972 }
973
ReportFailureFromUIThread(const CrxInstallError & error)974 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallError& error) {
975 DCHECK_CURRENTLY_ON(BrowserThread::UI);
976 DCHECK_NE(CrxInstallErrorType::NONE, error.type());
977
978 if (!service_weak_.get() || service_weak_->browser_terminating())
979 return;
980
981 content::NotificationService* service =
982 content::NotificationService::current();
983 service->Notify(NOTIFICATION_EXTENSION_INSTALL_ERROR,
984 content::Source<CrxInstaller>(this),
985 content::Details<const CrxInstallError>(&error));
986
987 // This isn't really necessary, it is only used because unit tests expect to
988 // see errors get reported via this interface.
989 //
990 // TODO(aa): Need to go through unit tests and clean them up too, probably get
991 // rid of this line.
992 LoadErrorReporter::GetInstance()->ReportError(error.message(),
993 false); // Be quiet.
994
995 if (client_)
996 client_->OnInstallFailure(error);
997
998 NotifyCrxInstallComplete(error);
999
1000 // Delete temporary files.
1001 CleanupTempFiles();
1002 }
1003
ReportSuccessFromSharedFileThread()1004 void CrxInstaller::ReportSuccessFromSharedFileThread() {
1005 DCHECK(shared_file_task_runner_->RunsTasksInCurrentSequence());
1006
1007 // Tracking number of extensions installed by users
1008 if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD)
1009 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
1010
1011 if (!content::GetUIThreadTaskRunner({})->PostTask(
1012 FROM_HERE,
1013 base::BindOnce(&CrxInstaller::ReportSuccessFromUIThread, this)))
1014 NOTREACHED();
1015
1016 // Delete temporary files.
1017 CleanupTempFiles();
1018 }
1019
ReportSuccessFromUIThread()1020 void CrxInstaller::ReportSuccessFromUIThread() {
1021 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1022
1023 if (!service_weak_.get() || service_weak_->browser_terminating())
1024 return;
1025
1026 extension()->permissions_data()->BindToCurrentThread();
1027
1028 if (!update_from_settings_page_) {
1029 // If there is a client, tell the client about installation.
1030 if (client_)
1031 client_->OnInstallSuccess(extension_, install_icon_.get());
1032
1033 // We update the extension's granted permissions if the user already
1034 // approved the install (client_ is non NULL), or we are allowed to install
1035 // this silently.
1036 if ((client_ || allow_silent_install_) && grant_permissions_ &&
1037 (!expected_version_.IsValid() ||
1038 expected_version_ == extension()->version())) {
1039 PermissionsUpdater perms_updater(profile());
1040 perms_updater.InitializePermissions(extension());
1041 perms_updater.GrantActivePermissions(extension());
1042 }
1043 }
1044
1045 service_weak_->OnExtensionInstalled(extension(), page_ordinal_,
1046 install_flags_, ruleset_install_prefs_);
1047 NotifyCrxInstallComplete(base::nullopt);
1048 }
1049
ReportInstallationStage(InstallationStage stage)1050 void CrxInstaller::ReportInstallationStage(InstallationStage stage) {
1051 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
1052 DCHECK(GetUnpackerTaskRunner()->RunsTasksInCurrentSequence() ||
1053 shared_file_task_runner_->RunsTasksInCurrentSequence());
1054 if (!content::GetUIThreadTaskRunner({})->PostTask(
1055 FROM_HERE, base::BindOnce(&CrxInstaller::ReportInstallationStage,
1056 this, stage))) {
1057 NOTREACHED();
1058 }
1059 return;
1060 }
1061
1062 if (!service_weak_.get() || service_weak_->browser_terminating())
1063 return;
1064 // In case of force installed extensions, expected_id_ should always be set.
1065 // We do not want to report in case of other extensions.
1066 if (expected_id_.empty())
1067 return;
1068 InstallStageTracker* install_stage_tracker =
1069 InstallStageTracker::Get(profile_);
1070 install_stage_tracker->ReportCRXInstallationStage(expected_id_, stage);
1071 }
1072
NotifyCrxInstallBegin()1073 void CrxInstaller::NotifyCrxInstallBegin() {
1074 InstallTrackerFactory::GetForBrowserContext(profile())
1075 ->OnBeginCrxInstall(expected_id_);
1076 }
1077
NotifyCrxInstallComplete(const base::Optional<CrxInstallError> & error)1078 void CrxInstaller::NotifyCrxInstallComplete(
1079 const base::Optional<CrxInstallError>& error) {
1080 ReportInstallationStage(InstallationStage::kComplete);
1081 const std::string extension_id =
1082 expected_id_.empty() && extension() ? extension()->id() : expected_id_;
1083 InstallStageTracker* install_stage_tracker =
1084 InstallStageTracker::Get(profile_);
1085 install_stage_tracker->ReportInstallationStage(
1086 extension_id, InstallStageTracker::Stage::COMPLETE);
1087 const bool success = !error.has_value();
1088
1089 if (extension()) {
1090 install_stage_tracker->ReportExtensionType(extension_id,
1091 extension()->GetType());
1092 }
1093
1094 if (!success && (!expected_id_.empty() || extension())) {
1095 switch (error->type()) {
1096 case CrxInstallErrorType::DECLINED:
1097 install_stage_tracker->ReportCrxInstallError(
1098 extension_id,
1099 InstallStageTracker::FailureReason::CRX_INSTALL_ERROR_DECLINED,
1100 error->detail());
1101 break;
1102 case CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE:
1103 install_stage_tracker->ReportSandboxedUnpackerFailureReason(
1104 extension_id, error->sandbox_failure_detail());
1105 break;
1106 case CrxInstallErrorType::OTHER:
1107 install_stage_tracker->ReportCrxInstallError(
1108 extension_id,
1109 InstallStageTracker::FailureReason::CRX_INSTALL_ERROR_OTHER,
1110 error->detail());
1111 break;
1112 case CrxInstallErrorType::NONE:
1113 NOTREACHED();
1114 break;
1115 }
1116 }
1117
1118 // Some users (such as the download shelf) need to know when a
1119 // CRXInstaller is done. Listening for the EXTENSION_* events
1120 // is problematic because they don't know anything about the
1121 // extension before it is unpacked, so they cannot filter based
1122 // on the extension.
1123 content::NotificationService::current()->Notify(
1124 NOTIFICATION_CRX_INSTALLER_DONE, content::Source<CrxInstaller>(this),
1125 content::Details<const Extension>(success ? extension() : NULL));
1126
1127 InstallTrackerFactory::GetForBrowserContext(profile())
1128 ->OnFinishCrxInstall(success ? extension()->id() : expected_id_, success);
1129
1130 if (success)
1131 ConfirmReEnable();
1132
1133 if (!installer_callback_.is_null() &&
1134 !content::GetUIThreadTaskRunner({})->PostTask(
1135 FROM_HERE, base::BindOnce(std::move(installer_callback_), error))) {
1136 NOTREACHED();
1137 }
1138 }
1139
CleanupTempFiles()1140 void CrxInstaller::CleanupTempFiles() {
1141 if (!shared_file_task_runner_->RunsTasksInCurrentSequence()) {
1142 if (!shared_file_task_runner_->PostTask(
1143 FROM_HERE, base::BindOnce(&CrxInstaller::CleanupTempFiles, this))) {
1144 NOTREACHED();
1145 }
1146 return;
1147 }
1148
1149 // Delete the temp directory and crx file as necessary.
1150 if (!temp_dir_.value().empty()) {
1151 base::DeletePathRecursively(temp_dir_);
1152 temp_dir_ = base::FilePath();
1153 }
1154
1155 if (delete_source_ && !source_file_.value().empty()) {
1156 base::DeleteFile(source_file_);
1157 source_file_ = base::FilePath();
1158 }
1159 }
1160
CheckUpdateFromSettingsPage()1161 void CrxInstaller::CheckUpdateFromSettingsPage() {
1162 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1163
1164 ExtensionService* service = service_weak_.get();
1165 if (!service || service->browser_terminating())
1166 return;
1167
1168 if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage)
1169 return;
1170
1171 const Extension* installed_extension =
1172 ExtensionRegistry::Get(service->profile())
1173 ->GetInstalledExtension(extension()->id());
1174 if (installed_extension) {
1175 // Previous version of the extension exists.
1176 update_from_settings_page_ = true;
1177 expected_id_ = installed_extension->id();
1178 install_source_ = installed_extension->location();
1179 install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE;
1180 }
1181 }
1182
ConfirmReEnable()1183 void CrxInstaller::ConfirmReEnable() {
1184 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1185
1186 ExtensionService* service = service_weak_.get();
1187 if (!service || service->browser_terminating())
1188 return;
1189
1190 if (!update_from_settings_page_)
1191 return;
1192
1193 ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile());
1194 if (!prefs->DidExtensionEscalatePermissions(extension()->id()))
1195 return;
1196
1197 if (client_) {
1198 AddRef(); // Balanced in OnInstallPromptDone().
1199 ExtensionInstallPrompt::PromptType type =
1200 ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
1201 service->profile(), extension());
1202 client_->ShowDialog(base::Bind(&CrxInstaller::OnInstallPromptDone, this),
1203 extension(), nullptr,
1204 std::make_unique<ExtensionInstallPrompt::Prompt>(type),
1205 ExtensionInstallPrompt::GetDefaultShowDialogCallback());
1206 }
1207 }
1208
GetUnpackerTaskRunner()1209 base::SequencedTaskRunner* CrxInstaller::GetUnpackerTaskRunner() {
1210 if (!unpacker_task_runner_) {
1211 bool low_priority =
1212 (creation_flags_ & Extension::WAS_INSTALLED_BY_DEFAULT) &&
1213 !(creation_flags_ & Extension::WAS_INSTALLED_BY_OEM);
1214 unpacker_task_runner_ = GetOneShotFileTaskRunner(
1215 low_priority ? base::TaskPriority::BEST_EFFORT
1216 : base::TaskPriority::USER_VISIBLE);
1217 }
1218 return unpacker_task_runner_.get();
1219 }
1220
set_installer_callback(InstallerResultCallback callback)1221 void CrxInstaller::set_installer_callback(InstallerResultCallback callback) {
1222 installer_callback_ = std::move(callback);
1223 }
1224
set_expectations_verified_callback(ExpectationsVerifiedCallback callback)1225 void CrxInstaller::set_expectations_verified_callback(
1226 ExpectationsVerifiedCallback callback) {
1227 expectations_verified_callback_ = std::move(callback);
1228 }
1229
1230 } // namespace extensions
1231