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