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/component_loader.h"
6
7 #include <string>
8
9 #include "ash/public/cpp/ash_pref_names.h"
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/command_line.h"
13 #include "base/feature_list.h"
14 #include "base/files/file_util.h"
15 #include "base/json/json_string_value_serializer.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/path_service.h"
18 #include "base/time/time.h"
19 #include "base/trace_event/trace_event.h"
20 #include "build/branding_buildflags.h"
21 #include "build/build_config.h"
22 #include "chrome/browser/extensions/component_extensions_allowlist/allowlist.h"
23 #include "chrome/browser/extensions/data_deleter.h"
24 #include "chrome/browser/extensions/extension_service.h"
25 #include "chrome/browser/extensions/launch_util.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/common/channel_info.h"
28 #include "chrome/common/chrome_paths.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/extensions/extension_constants.h"
31 #include "chrome/grit/browser_resources.h"
32 #include "chrome/grit/chromium_strings.h"
33 #include "chrome/grit/generated_resources.h"
34 #include "components/crx_file/id_util.h"
35 #include "components/nacl/common/buildflags.h"
36 #include "components/version_info/version_info.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/common/content_switches.h"
39 #include "extensions/browser/extension_file_task_runner.h"
40 #include "extensions/browser/extension_prefs.h"
41 #include "extensions/browser/extension_system.h"
42 #include "extensions/common/constants.h"
43 #include "extensions/common/extension.h"
44 #include "extensions/common/extension_l10n_util.h"
45 #include "extensions/common/file_util.h"
46 #include "extensions/common/manifest_constants.h"
47 #include "pdf/buildflags.h"
48 #include "printing/buildflags/buildflags.h"
49 #include "ui/base/l10n/l10n_util.h"
50 #include "ui/base/resource/resource_bundle.h"
51
52 #if defined(OS_CHROMEOS)
53 #include "ash/keyboard/ui/grit/keyboard_resources.h"
54 #include "chromeos/constants/chromeos_features.h"
55 #include "chromeos/constants/chromeos_pref_names.h"
56 #include "chromeos/constants/chromeos_switches.h"
57 #include "components/user_manager/user_manager.h"
58 #include "content/public/browser/site_instance.h"
59 #include "content/public/browser/storage_partition.h"
60 #include "extensions/browser/extensions_browser_client.h"
61 #include "storage/browser/file_system/file_system_context.h"
62 #include "ui/chromeos/devicetype_utils.h"
63 #include "ui/file_manager/grit/file_manager_resources.h"
64 #endif
65
66 #if BUILDFLAG(ENABLE_PDF)
67 #include "chrome/browser/pdf/pdf_extension_util.h"
68 #endif
69
70 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
71 #include "chrome/browser/defaults.h"
72 #endif
73
74 using content::BrowserThread;
75
76 namespace extensions {
77
78 namespace {
79
80 static bool enable_background_extensions_during_testing = false;
81
GenerateId(const base::DictionaryValue * manifest,const base::FilePath & path)82 std::string GenerateId(const base::DictionaryValue* manifest,
83 const base::FilePath& path) {
84 std::string raw_key;
85 std::string id_input;
86 CHECK(manifest->GetString(manifest_keys::kPublicKey, &raw_key));
87 CHECK(Extension::ParsePEMKeyBytes(raw_key, &id_input));
88 std::string id = crx_file::id_util::GenerateId(id_input);
89 return id;
90 }
91
92 #if defined(OS_CHROMEOS)
LoadManifestOnFileThread(const base::FilePath & root_directory,const base::FilePath::CharType * manifest_filename,bool localize_manifest)93 std::unique_ptr<base::DictionaryValue> LoadManifestOnFileThread(
94 const base::FilePath& root_directory,
95 const base::FilePath::CharType* manifest_filename,
96 bool localize_manifest) {
97 DCHECK(GetExtensionFileTaskRunner()->RunsTasksInCurrentSequence());
98 std::string error;
99 std::unique_ptr<base::DictionaryValue> manifest(
100 file_util::LoadManifest(root_directory, manifest_filename, &error));
101 if (!manifest) {
102 LOG(ERROR) << "Can't load "
103 << root_directory.Append(manifest_filename).AsUTF8Unsafe()
104 << ": " << error;
105 return nullptr;
106 }
107
108 if (localize_manifest) {
109 // This is only called for Chrome OS component extensions which are loaded
110 // from a read-only rootfs partition, so it is safe to set
111 // |gzip_permission| to kAllowForTrustedSource.
112 bool localized = extension_l10n_util::LocalizeExtension(
113 root_directory, manifest.get(),
114 extension_l10n_util::GzippedMessagesPermission::kAllowForTrustedSource,
115 &error);
116 CHECK(localized) << error;
117 }
118
119 return manifest;
120 }
121
IsNormalSession()122 bool IsNormalSession() {
123 return !base::CommandLine::ForCurrentProcess()->HasSwitch(
124 chromeos::switches::kGuestSession) &&
125 user_manager::UserManager::IsInitialized() &&
126 user_manager::UserManager::Get()->IsUserLoggedIn();
127 }
128 #endif // defined(OS_CHROMEOS)
129
130 } // namespace
131
ComponentExtensionInfo(std::unique_ptr<base::DictionaryValue> manifest_param,const base::FilePath & directory)132 ComponentLoader::ComponentExtensionInfo::ComponentExtensionInfo(
133 std::unique_ptr<base::DictionaryValue> manifest_param,
134 const base::FilePath& directory)
135 : manifest(std::move(manifest_param)), root_directory(directory) {
136 if (!root_directory.IsAbsolute()) {
137 CHECK(base::PathService::Get(chrome::DIR_RESOURCES, &root_directory));
138 root_directory = root_directory.Append(directory);
139 }
140 extension_id = GenerateId(manifest.get(), root_directory);
141 }
142
ComponentExtensionInfo(ComponentExtensionInfo && other)143 ComponentLoader::ComponentExtensionInfo::ComponentExtensionInfo(
144 ComponentExtensionInfo&& other)
145 : manifest(std::move(other.manifest)),
146 root_directory(std::move(other.root_directory)),
147 extension_id(std::move(other.extension_id)) {}
148
149 ComponentLoader::ComponentExtensionInfo&
operator =(ComponentExtensionInfo && other)150 ComponentLoader::ComponentExtensionInfo::operator=(
151 ComponentExtensionInfo&& other) {
152 manifest = std::move(other.manifest);
153 root_directory = std::move(other.root_directory);
154 extension_id = std::move(other.extension_id);
155 return *this;
156 }
157
158 ComponentLoader::ComponentExtensionInfo::~ComponentExtensionInfo() = default;
159
ComponentLoader(ExtensionSystem * extension_system,Profile * profile)160 ComponentLoader::ComponentLoader(ExtensionSystem* extension_system,
161 Profile* profile)
162 : profile_(profile),
163 extension_system_(extension_system),
164 ignore_allowlist_for_testing_(false) {}
165
166 ComponentLoader::~ComponentLoader() = default;
167
LoadAll()168 void ComponentLoader::LoadAll() {
169 TRACE_EVENT0("browser,startup", "ComponentLoader::LoadAll");
170 SCOPED_UMA_HISTOGRAM_TIMER("Extensions.LoadAllComponentTime");
171
172 for (const auto& component_extension : component_extensions_)
173 Load(component_extension);
174 }
175
ParseManifest(base::StringPiece manifest_contents) const176 std::unique_ptr<base::DictionaryValue> ComponentLoader::ParseManifest(
177 base::StringPiece manifest_contents) const {
178 JSONStringValueDeserializer deserializer(manifest_contents);
179 std::unique_ptr<base::Value> manifest = deserializer.Deserialize(NULL, NULL);
180
181 if (!manifest.get() || !manifest->is_dict()) {
182 LOG(ERROR) << "Failed to parse extension manifest.";
183 return std::unique_ptr<base::DictionaryValue>();
184 }
185 return base::DictionaryValue::From(std::move(manifest));
186 }
187
Add(int manifest_resource_id,const base::FilePath & root_directory)188 std::string ComponentLoader::Add(int manifest_resource_id,
189 const base::FilePath& root_directory) {
190 if (!ignore_allowlist_for_testing_ &&
191 !IsComponentExtensionAllowlisted(manifest_resource_id))
192 return std::string();
193
194 base::StringPiece manifest_contents =
195 ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
196 manifest_resource_id);
197 return Add(manifest_contents, root_directory, true);
198 }
199
Add(const base::StringPiece & manifest_contents,const base::FilePath & root_directory)200 std::string ComponentLoader::Add(const base::StringPiece& manifest_contents,
201 const base::FilePath& root_directory) {
202 return Add(manifest_contents, root_directory, false);
203 }
204
Add(const base::StringPiece & manifest_contents,const base::FilePath & root_directory,bool skip_allowlist)205 std::string ComponentLoader::Add(const base::StringPiece& manifest_contents,
206 const base::FilePath& root_directory,
207 bool skip_allowlist) {
208 // The Value is kept for the lifetime of the ComponentLoader. This is
209 // required in case LoadAll() is called again.
210 std::unique_ptr<base::DictionaryValue> manifest =
211 ParseManifest(manifest_contents);
212 if (manifest)
213 return Add(std::move(manifest), root_directory, skip_allowlist);
214 return std::string();
215 }
216
Add(std::unique_ptr<base::DictionaryValue> parsed_manifest,const base::FilePath & root_directory,bool skip_allowlist)217 std::string ComponentLoader::Add(
218 std::unique_ptr<base::DictionaryValue> parsed_manifest,
219 const base::FilePath& root_directory,
220 bool skip_allowlist) {
221 ComponentExtensionInfo info(std::move(parsed_manifest), root_directory);
222 if (!ignore_allowlist_for_testing_ && !skip_allowlist &&
223 !IsComponentExtensionAllowlisted(info.extension_id))
224 return std::string();
225
226 component_extensions_.push_back(std::move(info));
227 ComponentExtensionInfo& added_info = component_extensions_.back();
228 if (extension_system_->is_ready())
229 Load(added_info);
230 return added_info.extension_id;
231 }
232
AddOrReplace(const base::FilePath & path)233 std::string ComponentLoader::AddOrReplace(const base::FilePath& path) {
234 base::FilePath absolute_path = base::MakeAbsoluteFilePath(path);
235 std::string error;
236 std::unique_ptr<base::DictionaryValue> manifest(
237 file_util::LoadManifest(absolute_path, &error));
238 if (!manifest) {
239 LOG(ERROR) << "Could not load extension from '" << absolute_path.value()
240 << "'. " << error;
241 return std::string();
242 }
243 Remove(GenerateId(manifest.get(), absolute_path));
244
245 // We don't check component extensions loaded by path because this is only
246 // used by developers for testing.
247 return Add(std::move(manifest), absolute_path, true);
248 }
249
Reload(const std::string & extension_id)250 void ComponentLoader::Reload(const std::string& extension_id) {
251 for (const auto& component_extension : component_extensions_) {
252 if (component_extension.extension_id == extension_id) {
253 Load(component_extension);
254 break;
255 }
256 }
257 }
258
Load(const ComponentExtensionInfo & info)259 void ComponentLoader::Load(const ComponentExtensionInfo& info) {
260 std::string error;
261 scoped_refptr<const Extension> extension(CreateExtension(info, &error));
262 if (!extension.get()) {
263 LOG(ERROR) << error;
264 return;
265 }
266
267 CHECK_EQ(info.extension_id, extension->id()) << extension->name();
268 extension_system_->extension_service()->AddComponentExtension(
269 extension.get());
270 }
271
Remove(const base::FilePath & root_directory)272 void ComponentLoader::Remove(const base::FilePath& root_directory) {
273 // Find the ComponentExtensionInfo for the extension.
274 for (const auto& component_extension : component_extensions_) {
275 if (component_extension.root_directory == root_directory) {
276 Remove(GenerateId(component_extension.manifest.get(), root_directory));
277 break;
278 }
279 }
280 }
281
Remove(const std::string & id)282 void ComponentLoader::Remove(const std::string& id) {
283 for (auto it = component_extensions_.begin();
284 it != component_extensions_.end(); ++it) {
285 if (it->extension_id == id) {
286 UnloadComponent(&(*it));
287 component_extensions_.erase(it);
288 break;
289 }
290 }
291 }
292
Exists(const std::string & id) const293 bool ComponentLoader::Exists(const std::string& id) const {
294 for (const auto& component_extension : component_extensions_) {
295 if (component_extension.extension_id == id)
296 return true;
297 }
298 return false;
299 }
300
GetRegisteredComponentExtensionsIds() const301 std::vector<std::string> ComponentLoader::GetRegisteredComponentExtensionsIds()
302 const {
303 std::vector<std::string> result;
304 for (const auto& el : component_extensions_) {
305 result.push_back(el.extension_id);
306 }
307 return result;
308 }
309
310 #if BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
AddHangoutServicesExtension()311 void ComponentLoader::AddHangoutServicesExtension() {
312 Add(IDR_HANGOUT_SERVICES_MANIFEST,
313 base::FilePath(FILE_PATH_LITERAL("hangout_services")));
314 }
315 #endif // BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
316
AddNetworkSpeechSynthesisExtension()317 void ComponentLoader::AddNetworkSpeechSynthesisExtension() {
318 Add(IDR_NETWORK_SPEECH_SYNTHESIS_MANIFEST,
319 base::FilePath(FILE_PATH_LITERAL("network_speech_synthesis")));
320 }
321
AddWithNameAndDescription(int manifest_resource_id,const base::FilePath & root_directory,const std::string & name_string,const std::string & description_string)322 void ComponentLoader::AddWithNameAndDescription(
323 int manifest_resource_id,
324 const base::FilePath& root_directory,
325 const std::string& name_string,
326 const std::string& description_string) {
327 if (!ignore_allowlist_for_testing_ &&
328 !IsComponentExtensionAllowlisted(manifest_resource_id))
329 return;
330
331 base::StringPiece manifest_contents =
332 ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
333 manifest_resource_id);
334
335 // The Value is kept for the lifetime of the ComponentLoader. This is
336 // required in case LoadAll() is called again.
337 std::unique_ptr<base::DictionaryValue> manifest =
338 ParseManifest(manifest_contents);
339
340 if (manifest) {
341 manifest->SetString(manifest_keys::kName, name_string);
342 manifest->SetString(manifest_keys::kDescription, description_string);
343 Add(std::move(manifest), root_directory, true);
344 }
345 }
346
AddWebStoreApp()347 void ComponentLoader::AddWebStoreApp() {
348 #if defined(OS_CHROMEOS)
349 if (!IsNormalSession())
350 return;
351 #endif
352
353 AddWithNameAndDescription(
354 IDR_WEBSTORE_MANIFEST, base::FilePath(FILE_PATH_LITERAL("web_store")),
355 l10n_util::GetStringUTF8(IDS_WEBSTORE_NAME_STORE),
356 l10n_util::GetStringUTF8(IDS_WEBSTORE_APP_DESCRIPTION));
357 }
358
359 #if defined(OS_CHROMEOS)
AddChromeApp()360 void ComponentLoader::AddChromeApp() {
361 AddWithNameAndDescription(
362 IDR_CHROME_APP_MANIFEST, base::FilePath(FILE_PATH_LITERAL("chrome_app")),
363 l10n_util::GetStringUTF8(IDS_SHORT_PRODUCT_NAME),
364 l10n_util::GetStringUTF8(IDS_CHROME_SHORTCUT_DESCRIPTION));
365 }
366
AddFileManagerExtension()367 void ComponentLoader::AddFileManagerExtension() {
368 AddWithNameAndDescription(
369 IDR_FILEMANAGER_MANIFEST,
370 base::FilePath(FILE_PATH_LITERAL("file_manager")),
371 l10n_util::GetStringUTF8(IDS_FILEMANAGER_APP_NAME),
372 l10n_util::GetStringUTF8(IDS_FILEMANAGER_APP_DESCRIPTION));
373 }
374
AddVideoPlayerExtension()375 void ComponentLoader::AddVideoPlayerExtension() {
376 Add(IDR_VIDEO_PLAYER_MANIFEST,
377 base::FilePath(FILE_PATH_LITERAL("video_player")));
378 }
379
AddAudioPlayerExtension()380 void ComponentLoader::AddAudioPlayerExtension() {
381 Add(IDR_AUDIO_PLAYER_MANIFEST,
382 base::FilePath(FILE_PATH_LITERAL("audio_player")));
383 }
384
AddGalleryExtension()385 void ComponentLoader::AddGalleryExtension() {
386 Add(IDR_GALLERY_MANIFEST, base::FilePath(FILE_PATH_LITERAL("gallery")));
387 }
388
AddImageLoaderExtension()389 void ComponentLoader::AddImageLoaderExtension() {
390 Add(IDR_IMAGE_LOADER_MANIFEST,
391 base::FilePath(FILE_PATH_LITERAL("image_loader")));
392 }
393
AddKeyboardApp()394 void ComponentLoader::AddKeyboardApp() {
395 Add(IDR_KEYBOARD_MANIFEST, base::FilePath(FILE_PATH_LITERAL("keyboard")));
396 }
397
AddChromeCameraApp()398 void ComponentLoader::AddChromeCameraApp() {
399 // TODO(crbug.com/1135280): Remove all the logic here once CCA is fully
400 // migrated to SWA.
401
402 // If users should use the SWA version of CCA and the status from the platform
403 // app version is already migrated, there is no need to install the platform
404 // version of CCA.
405 if (base::FeatureList::IsEnabled(chromeos::features::kCameraSystemWebApp) &&
406 profile_->GetPrefs()->GetBoolean(
407 chromeos::prefs::kHasCameraAppMigratedToSWA)) {
408 return;
409 }
410
411 base::FilePath resources_path;
412 if (base::PathService::Get(chrome::DIR_RESOURCES, &resources_path)) {
413 AddComponentFromDir(resources_path.Append(extension_misc::kCameraAppPath),
414 extension_misc::kCameraAppId, base::RepeatingClosure());
415 }
416 }
417
AddZipArchiverExtension()418 void ComponentLoader::AddZipArchiverExtension() {
419 base::FilePath resources_path;
420 if (base::PathService::Get(chrome::DIR_RESOURCES, &resources_path)) {
421 AddWithNameAndDescriptionFromDir(
422 resources_path.Append(extension_misc::kZipArchiverExtensionPath),
423 extension_misc::kZipArchiverExtensionId,
424 l10n_util::GetStringUTF8(IDS_ZIP_ARCHIVER_NAME),
425 l10n_util::GetStringUTF8(IDS_ZIP_ARCHIVER_DESCRIPTION));
426 }
427 }
428 #endif // defined(OS_CHROMEOS)
429
CreateExtension(const ComponentExtensionInfo & info,std::string * utf8_error)430 scoped_refptr<const Extension> ComponentLoader::CreateExtension(
431 const ComponentExtensionInfo& info,
432 std::string* utf8_error) {
433 // TODO(abarth): We should REQUIRE_MODERN_MANIFEST_VERSION once we've updated
434 // our component extensions to the new manifest version.
435 int flags = Extension::REQUIRE_KEY;
436 return Extension::Create(info.root_directory, Manifest::COMPONENT,
437 *info.manifest, flags, utf8_error);
438 }
439
440 // static
EnableBackgroundExtensionsForTesting()441 void ComponentLoader::EnableBackgroundExtensionsForTesting() {
442 enable_background_extensions_during_testing = true;
443 }
444
AddDefaultComponentExtensions(bool skip_session_components)445 void ComponentLoader::AddDefaultComponentExtensions(
446 bool skip_session_components) {
447 // Do not add component extensions that have background pages here -- add them
448 // to AddDefaultComponentExtensionsWithBackgroundPages.
449 #if defined(OS_CHROMEOS)
450 Add(IDR_MOBILE_MANIFEST,
451 base::FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile")));
452
453 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
454 if (browser_defaults::enable_help_app) {
455 Add(IDR_HELP_MANIFEST, base::FilePath(FILE_PATH_LITERAL(
456 "/usr/share/chromeos-assets/helpapp")));
457 }
458 #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
459
460 AddKeyboardApp();
461 #else // defined(OS_CHROMEOS)
462 DCHECK(!skip_session_components);
463 #if BUILDFLAG(ENABLE_PRINTING)
464 // Cloud Print component app. Not required on Chrome OS.
465 Add(IDR_CLOUDPRINT_MANIFEST,
466 base::FilePath(FILE_PATH_LITERAL("cloud_print")));
467 #endif // BUILDFLAG(ENABLE_PRINTING)
468 #endif // defined(OS_CHROMEOS)
469
470 if (!skip_session_components) {
471 AddWebStoreApp();
472 #if defined(OS_CHROMEOS)
473 AddChromeApp();
474 #endif // defined(OS_CHROMEOS)
475 #if BUILDFLAG(ENABLE_PDF)
476 Add(pdf_extension_util::GetManifest(),
477 base::FilePath(FILE_PATH_LITERAL("pdf")));
478 #endif // BUILDFLAG(ENABLE_PDF)
479 }
480
481 AddDefaultComponentExtensionsWithBackgroundPages(skip_session_components);
482 }
483
AddDefaultComponentExtensionsForKioskMode(bool skip_session_components)484 void ComponentLoader::AddDefaultComponentExtensionsForKioskMode(
485 bool skip_session_components) {
486 // Do not add component extensions that have background pages here -- add them
487 // to AddDefaultComponentExtensionsWithBackgroundPagesForKioskMode.
488
489 // No component extension for kiosk app launch splash screen.
490 if (skip_session_components)
491 return;
492
493 #if defined(OS_CHROMEOS)
494 // Component extensions needed for kiosk apps.
495 AddFileManagerExtension();
496
497 // Add virtual keyboard.
498 AddKeyboardApp();
499 #endif // defined(OS_CHROMEOS)
500
501 AddDefaultComponentExtensionsWithBackgroundPagesForKioskMode();
502
503 #if BUILDFLAG(ENABLE_PDF)
504 Add(pdf_extension_util::GetManifest(),
505 base::FilePath(FILE_PATH_LITERAL("pdf")));
506 #endif
507 }
508
AddDefaultComponentExtensionsWithBackgroundPages(bool skip_session_components)509 void ComponentLoader::AddDefaultComponentExtensionsWithBackgroundPages(
510 bool skip_session_components) {
511 const base::CommandLine* command_line =
512 base::CommandLine::ForCurrentProcess();
513
514 // Component extensions with background pages are not enabled during tests
515 // because they generate a lot of background behavior that can interfere.
516 if (!enable_background_extensions_during_testing &&
517 (command_line->HasSwitch(::switches::kTestType) ||
518 command_line->HasSwitch(
519 ::switches::kDisableComponentExtensionsWithBackgroundPages))) {
520 return;
521 }
522
523 if (!skip_session_components) {
524 #if BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
525 AddHangoutServicesExtension();
526 #endif // BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
527
528 bool install_feedback = enable_background_extensions_during_testing;
529 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
530 install_feedback = true;
531 #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
532 if (install_feedback) {
533 AddWithNameAndDescription(
534 IDR_FEEDBACK_MANIFEST, base::FilePath(FILE_PATH_LITERAL("feedback")),
535 l10n_util::GetStringUTF8(IDS_FEEDBACK_REPORT_APP_TITLE),
536 // Description string
537 l10n_util::GetStringUTF8(IDS_FEEDBACK_REPORT_PAGE_TITLE));
538 }
539
540 #if defined(OS_CHROMEOS)
541 AddChromeCameraApp();
542 AddVideoPlayerExtension();
543 AddAudioPlayerExtension();
544 AddFileManagerExtension();
545 AddGalleryExtension();
546 AddImageLoaderExtension();
547
548 #if BUILDFLAG(ENABLE_NACL)
549 AddZipArchiverExtension();
550 #endif // BUILDFLAG(ENABLE_NACL)
551
552 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
553 if (!base::FeatureList::IsEnabled(
554 chromeos::features::kDisableOfficeEditingComponentApp)) {
555 Add(IDR_QUICKOFFICE_MANIFEST,
556 base::FilePath(
557 FILE_PATH_LITERAL("/usr/share/chromeos-assets/quickoffice")));
558 }
559
560 // TODO(https://crbug.com/1005083): Force the off the record profile to be
561 // created to allow the virtual keyboard to work in guest mode.
562 if (!IsNormalSession())
563 ExtensionsBrowserClient::Get()->GetOffTheRecordContext(profile_);
564 #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
565
566 Add(IDR_ECHO_MANIFEST,
567 base::FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/echo")));
568
569 if (!command_line->HasSwitch(chromeos::switches::kGuestSession)) {
570 Add(IDR_WALLPAPERMANAGER_MANIFEST,
571 base::FilePath(FILE_PATH_LITERAL("chromeos/wallpaper_manager")));
572 }
573
574 Add(IDR_FIRST_RUN_DIALOG_MANIFEST,
575 base::FilePath(FILE_PATH_LITERAL("chromeos/first_run/app")));
576
577 Add(IDR_CONNECTIVITY_DIAGNOSTICS_MANIFEST,
578 base::FilePath(extension_misc::kConnectivityDiagnosticsPath));
579 Add(IDR_CONNECTIVITY_DIAGNOSTICS_LAUNCHER_MANIFEST,
580 base::FilePath(extension_misc::kConnectivityDiagnosticsLauncherPath));
581
582 Add(IDR_ARC_SUPPORT_MANIFEST,
583 base::FilePath(FILE_PATH_LITERAL("chromeos/arc_support")));
584 #endif // defined(OS_CHROMEOS)
585 }
586
587 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
588 #if !defined(OS_CHROMEOS) // http://crbug.com/314799
589 AddNetworkSpeechSynthesisExtension();
590 #endif
591
592 #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
593
594 Add(IDR_CRYPTOTOKEN_MANIFEST,
595 base::FilePath(FILE_PATH_LITERAL("cryptotoken")));
596 }
597
598 void ComponentLoader::
AddDefaultComponentExtensionsWithBackgroundPagesForKioskMode()599 AddDefaultComponentExtensionsWithBackgroundPagesForKioskMode() {
600 const base::CommandLine* command_line =
601 base::CommandLine::ForCurrentProcess();
602
603 // Component extensions with background pages are not enabled during tests
604 // because they generate a lot of background behavior that can interfere.
605 if (!enable_background_extensions_during_testing &&
606 (command_line->HasSwitch(::switches::kTestType) ||
607 command_line->HasSwitch(
608 ::switches::kDisableComponentExtensionsWithBackgroundPages))) {
609 return;
610 }
611
612 #if BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
613 AddHangoutServicesExtension();
614 #endif // BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
615 }
616
UnloadComponent(ComponentExtensionInfo * component)617 void ComponentLoader::UnloadComponent(ComponentExtensionInfo* component) {
618 if (extension_system_->is_ready()) {
619 extension_system_->extension_service()->RemoveComponentExtension(
620 component->extension_id);
621 }
622 }
623
624 #if defined(OS_CHROMEOS)
AddComponentFromDir(const base::FilePath & root_directory,const char * extension_id,const base::Closure & done_cb)625 void ComponentLoader::AddComponentFromDir(const base::FilePath& root_directory,
626 const char* extension_id,
627 const base::Closure& done_cb) {
628 AddComponentFromDirWithManifestFilename(
629 root_directory, extension_id, extensions::kManifestFilename,
630 extension_misc::kGuestManifestFilename, done_cb);
631 }
632
AddComponentFromDirWithManifestFilename(const base::FilePath & root_directory,const char * extension_id,const base::FilePath::CharType * manifest_file_name,const base::FilePath::CharType * guest_manifest_file_name,const base::Closure & done_cb)633 void ComponentLoader::AddComponentFromDirWithManifestFilename(
634 const base::FilePath& root_directory,
635 const char* extension_id,
636 const base::FilePath::CharType* manifest_file_name,
637 const base::FilePath::CharType* guest_manifest_file_name,
638 const base::Closure& done_cb) {
639 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
640 const base::FilePath::CharType* manifest_filename =
641 IsNormalSession() ? manifest_file_name : guest_manifest_file_name;
642 base::PostTaskAndReplyWithResult(
643 GetExtensionFileTaskRunner().get(), FROM_HERE,
644 base::BindOnce(&LoadManifestOnFileThread, root_directory,
645 manifest_filename, true),
646 base::BindOnce(&ComponentLoader::FinishAddComponentFromDir,
647 weak_factory_.GetWeakPtr(), root_directory, extension_id,
648 base::nullopt, base::nullopt, done_cb));
649 }
650
AddWithNameAndDescriptionFromDir(const base::FilePath & root_directory,const char * extension_id,const std::string & name_string,const std::string & description_string)651 void ComponentLoader::AddWithNameAndDescriptionFromDir(
652 const base::FilePath& root_directory,
653 const char* extension_id,
654 const std::string& name_string,
655 const std::string& description_string) {
656 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
657 base::PostTaskAndReplyWithResult(
658 GetExtensionFileTaskRunner().get(), FROM_HERE,
659 base::BindOnce(&LoadManifestOnFileThread, root_directory,
660 extensions::kManifestFilename, false),
661 base::BindOnce(&ComponentLoader::FinishAddComponentFromDir,
662 weak_factory_.GetWeakPtr(), root_directory, extension_id,
663 name_string, description_string, base::Closure()));
664 }
665
AddChromeOsSpeechSynthesisExtensions()666 void ComponentLoader::AddChromeOsSpeechSynthesisExtensions() {
667 if (!Exists(extension_misc::kGoogleSpeechSynthesisExtensionId)) {
668 AddComponentFromDir(
669 base::FilePath(extension_misc::kGoogleSpeechSynthesisExtensionPath),
670 extension_misc::kGoogleSpeechSynthesisExtensionId,
671 base::BindRepeating(
672 &ComponentLoader::FinishLoadSpeechSynthesisExtension,
673 weak_factory_.GetWeakPtr(),
674 extension_misc::kGoogleSpeechSynthesisExtensionId));
675 }
676
677 if (!Exists(extension_misc::kEspeakSpeechSynthesisExtensionId)) {
678 AddComponentFromDir(
679 base::FilePath(extension_misc::kEspeakSpeechSynthesisExtensionPath),
680 extension_misc::kEspeakSpeechSynthesisExtensionId,
681 base::BindRepeating(
682 &ComponentLoader::FinishLoadSpeechSynthesisExtension,
683 weak_factory_.GetWeakPtr(),
684 extension_misc::kEspeakSpeechSynthesisExtensionId));
685 }
686 }
687
FinishAddComponentFromDir(const base::FilePath & root_directory,const char * extension_id,const base::Optional<std::string> & name_string,const base::Optional<std::string> & description_string,const base::Closure & done_cb,std::unique_ptr<base::DictionaryValue> manifest)688 void ComponentLoader::FinishAddComponentFromDir(
689 const base::FilePath& root_directory,
690 const char* extension_id,
691 const base::Optional<std::string>& name_string,
692 const base::Optional<std::string>& description_string,
693 const base::Closure& done_cb,
694 std::unique_ptr<base::DictionaryValue> manifest) {
695 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
696 if (!manifest)
697 return; // Error already logged.
698
699 if (name_string)
700 manifest->SetString(manifest_keys::kName, name_string.value());
701
702 if (description_string) {
703 manifest->SetString(manifest_keys::kDescription,
704 description_string.value());
705 }
706
707 std::string actual_extension_id =
708 Add(std::move(manifest), root_directory, false);
709 CHECK_EQ(extension_id, actual_extension_id);
710 if (!done_cb.is_null())
711 done_cb.Run();
712 }
713
FinishLoadSpeechSynthesisExtension(const char * extension_id)714 void ComponentLoader::FinishLoadSpeechSynthesisExtension(
715 const char* extension_id) {
716 // TODO(https://crbug.com/947305): mitigation for extension not awake after
717 // load.
718 extensions::ProcessManager::Get(profile_)->WakeEventPage(extension_id,
719 base::DoNothing());
720 }
721 #endif
722
723 } // namespace extensions
724