1 // Copyright (c) 2013 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.
5 #include "chrome/browser/extensions/unpacked_installer.h"
8 #include "base/callback.h"
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "chrome/browser/extensions/extension_error_reporter.h"
15 #include "chrome/browser/extensions/extension_install_prompt.h"
16 #include "chrome/browser/extensions/extension_install_ui.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/permissions_updater.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_l10n_util.h"
27 #include "extensions/common/file_util.h"
28 #include "extensions/common/id_util.h"
29 #include "extensions/common/manifest.h"
30 #include "sync/api/string_ordinal.h"
32 using content::BrowserThread
;
33 using extensions::Extension
;
37 const char kUnpackedExtensionsBlacklistedError
[] =
38 "Loading of unpacked extensions is disabled by the administrator.";
40 // Manages an ExtensionInstallPrompt for a particular extension.
41 class SimpleExtensionLoadPrompt
: public ExtensionInstallPrompt::Delegate
{
43 SimpleExtensionLoadPrompt(const Extension
* extension
,
45 const base::Closure
& callback
);
46 virtual ~SimpleExtensionLoadPrompt();
50 // ExtensionInstallUI::Delegate
51 virtual void InstallUIProceed() OVERRIDE
;
52 virtual void InstallUIAbort(bool user_initiated
) OVERRIDE
;
55 scoped_ptr
<ExtensionInstallPrompt
> install_ui_
;
56 scoped_refptr
<const Extension
> extension_
;
57 base::Closure callback_
;
60 SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt(
61 const Extension
* extension
,
63 const base::Closure
& callback
)
64 : install_ui_(ExtensionInstallUI::CreateInstallPromptWithProfile(
66 extension_(extension
),
70 SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() {
73 void SimpleExtensionLoadPrompt::ShowPrompt() {
74 std::string confirm
= CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
75 switches::kAppsGalleryInstallAutoConfirmForTests
);
76 if (confirm
== "accept") {
80 if (confirm
== "cancel") {
81 InstallUIAbort(false);
85 install_ui_
->ConfirmInstall(
88 ExtensionInstallPrompt::GetDefaultShowDialogCallback());
91 void SimpleExtensionLoadPrompt::InstallUIProceed() {
96 void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated
) {
102 namespace extensions
{
105 scoped_refptr
<UnpackedInstaller
> UnpackedInstaller::Create(
106 ExtensionService
* extension_service
) {
107 DCHECK(extension_service
);
108 return scoped_refptr
<UnpackedInstaller
>(
109 new UnpackedInstaller(extension_service
));
112 UnpackedInstaller::UnpackedInstaller(ExtensionService
* extension_service
)
113 : service_weak_(extension_service
->AsWeakPtr()),
114 prompt_for_plugins_(true),
115 require_modern_manifest_version_(true),
116 be_noisy_on_failure_(true),
117 installer_(extension_service
->profile()) {
118 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
121 UnpackedInstaller::~UnpackedInstaller() {
122 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
) ||
123 BrowserThread::CurrentlyOn(BrowserThread::FILE));
126 void UnpackedInstaller::Load(const base::FilePath
& path_in
) {
127 DCHECK(extension_path_
.empty());
128 extension_path_
= path_in
;
129 BrowserThread::PostTask(
132 base::Bind(&UnpackedInstaller::GetAbsolutePath
, this));
135 bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath
& path_in
,
136 std::string
* extension_id
) {
137 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
138 DCHECK(extension_path_
.empty());
140 if (!service_weak_
.get())
142 // Load extensions from the command line synchronously to avoid a race
143 // between extension loading and loading an URL from the command line.
144 base::ThreadRestrictions::ScopedAllowIO allow_io
;
146 extension_path_
= base::MakeAbsoluteFilePath(path_in
);
148 if (!IsLoadingUnpackedAllowed()) {
149 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError
);
154 installer_
.set_extension(
155 file_util::LoadExtension(
156 extension_path_
, Manifest::COMMAND_LINE
, GetFlags(), &error
).get());
158 if (!installer_
.extension().get() ||
159 !extension_l10n_util::ValidateExtensionLocales(
161 installer_
.extension()->manifest()->value(),
163 ReportExtensionLoadError(error
);
169 *extension_id
= installer_
.extension()->id();
173 void UnpackedInstaller::ShowInstallPrompt() {
174 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
175 if (!service_weak_
.get())
178 const ExtensionSet
& disabled_extensions
=
179 ExtensionRegistry::Get(service_weak_
->profile())->disabled_extensions();
180 if (service_weak_
->show_extensions_prompts() && prompt_for_plugins_
&&
181 PluginInfo::HasPlugins(installer_
.extension().get()) &&
182 !disabled_extensions
.Contains(installer_
.extension()->id())) {
183 SimpleExtensionLoadPrompt
* prompt
= new SimpleExtensionLoadPrompt(
184 installer_
.extension().get(),
185 installer_
.profile(),
186 base::Bind(&UnpackedInstaller::CallCheckRequirements
, this));
187 prompt
->ShowPrompt();
190 CallCheckRequirements();
193 void UnpackedInstaller::CallCheckRequirements() {
194 installer_
.CheckRequirements(
195 base::Bind(&UnpackedInstaller::OnRequirementsChecked
, this));
198 void UnpackedInstaller::OnRequirementsChecked(
199 std::vector
<std::string
> requirement_errors
) {
200 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
202 if (!requirement_errors
.empty()) {
203 ReportExtensionLoadError(JoinString(requirement_errors
, ' '));
210 int UnpackedInstaller::GetFlags() {
211 std::string id
= id_util::GenerateIdForPath(extension_path_
);
212 bool allow_file_access
=
213 Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED
);
214 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(service_weak_
->profile());
215 if (prefs
->HasAllowFileAccessSetting(id
))
216 allow_file_access
= prefs
->AllowFileAccess(id
);
218 int result
= Extension::FOLLOW_SYMLINKS_ANYWHERE
;
219 if (allow_file_access
)
220 result
|= Extension::ALLOW_FILE_ACCESS
;
221 if (require_modern_manifest_version_
)
222 result
|= Extension::REQUIRE_MODERN_MANIFEST_VERSION
;
227 bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
228 if (!service_weak_
.get())
230 // If there is a "*" in the extension blacklist, then no extensions should be
231 // allowed at all (except explicitly whitelisted extensions).
232 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(service_weak_
->profile());
233 return !prefs
->ExtensionsBlacklistedByDefault();
236 void UnpackedInstaller::GetAbsolutePath() {
237 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
239 extension_path_
= base::MakeAbsoluteFilePath(extension_path_
);
242 if (!file_util::CheckForIllegalFilenames(extension_path_
, &error
)) {
243 BrowserThread::PostTask(
246 base::Bind(&UnpackedInstaller::ReportExtensionLoadError
, this, error
));
249 BrowserThread::PostTask(
250 BrowserThread::UI
, FROM_HERE
,
251 base::Bind(&UnpackedInstaller::CheckExtensionFileAccess
, this));
254 void UnpackedInstaller::CheckExtensionFileAccess() {
255 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
256 if (!service_weak_
.get())
259 if (!IsLoadingUnpackedAllowed()) {
260 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError
);
264 BrowserThread::PostTask(
267 base::Bind(&UnpackedInstaller::LoadWithFileAccess
, this, GetFlags()));
270 void UnpackedInstaller::LoadWithFileAccess(int flags
) {
271 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
274 installer_
.set_extension(
275 file_util::LoadExtension(
276 extension_path_
, Manifest::UNPACKED
, flags
, &error
).get());
278 if (!installer_
.extension().get() ||
279 !extension_l10n_util::ValidateExtensionLocales(
281 installer_
.extension()->manifest()->value(),
283 BrowserThread::PostTask(
286 base::Bind(&UnpackedInstaller::ReportExtensionLoadError
, this, error
));
290 BrowserThread::PostTask(
293 base::Bind(&UnpackedInstaller::ShowInstallPrompt
, this));
296 void UnpackedInstaller::ReportExtensionLoadError(const std::string
&error
) {
297 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
298 if (!on_failure_callback_
.is_null())
299 on_failure_callback_
.Run(extension_path_
, error
);
301 if (service_weak_
.get()) {
302 ExtensionErrorReporter::GetInstance()->ReportLoadError(
305 service_weak_
->profile(),
306 be_noisy_on_failure_
);
310 void UnpackedInstaller::ConfirmInstall() {
311 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
312 base::string16 error
= installer_
.CheckManagementPolicy();
313 if (!error
.empty()) {
314 ReportExtensionLoadError(base::UTF16ToUTF8(error
));
318 PermissionsUpdater
perms_updater(service_weak_
->profile());
319 perms_updater
.GrantActivePermissions(installer_
.extension().get());
321 service_weak_
->OnExtensionInstalled(
322 installer_
.extension().get(),
323 syncer::StringOrdinal(),
324 false /* no requirement errors */,
326 false /* don't wait for idle */);
329 } // namespace extensions