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.
5 #include "chrome/browser/extensions/component_loader.h"
9 #include "base/command_line.h"
10 #include "base/files/file_util.h"
11 #include "base/json/json_string_value_serializer.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/path_service.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/search/hotword_service_factory.h"
16 #include "chrome/common/chrome_paths.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/chrome_version_info.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "components/crx_file/id_util.h"
22 #include "content/public/browser/browser_context.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/plugin_service.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/file_util.h"
27 #include "extensions/common/manifest_constants.h"
28 #include "grit/browser_resources.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/resource/resource_bundle.h"
32 #if defined(OS_CHROMEOS)
33 #include "grit/keyboard_resources.h"
34 #include "ui/file_manager/grit/file_manager_resources.h"
35 #include "ui/keyboard/keyboard_util.h"
38 #if defined(GOOGLE_CHROME_BUILD)
39 #include "chrome/browser/defaults.h"
42 #if defined(OS_CHROMEOS)
43 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
44 #include "chromeos/chromeos_switches.h"
45 #include "content/public/browser/site_instance.h"
46 #include "content/public/browser/storage_partition.h"
47 #include "extensions/browser/extensions_browser_client.h"
48 #include "storage/browser/fileapi/file_system_context.h"
51 #if defined(ENABLE_APP_LIST)
52 #include "chrome/grit/chromium_strings.h"
55 using content::BrowserThread;
57 namespace extensions {
61 static bool enable_background_extensions_during_testing = false;
63 std::string GenerateId(const base::DictionaryValue* manifest,
64 const base::FilePath& path) {
67 CHECK(manifest->GetString(manifest_keys::kPublicKey, &raw_key));
68 CHECK(Extension::ParsePEMKeyBytes(raw_key, &id_input));
69 std::string id = crx_file::id_util::GenerateId(id_input);
73 #if defined(OS_CHROMEOS)
74 scoped_ptr<base::DictionaryValue>
75 LoadManifestOnFileThread(
76 const base::FilePath& chromevox_path, const char* manifest_filename) {
77 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
79 scoped_ptr<base::DictionaryValue> manifest(
80 file_util::LoadManifest(chromevox_path, manifest_filename, &error));
81 CHECK(manifest) << error;
82 return manifest.Pass();
84 #endif // defined(OS_CHROMEOS)
88 ComponentLoader::ComponentExtensionInfo::ComponentExtensionInfo(
89 const base::DictionaryValue* manifest, const base::FilePath& directory)
91 root_directory(directory) {
92 if (!root_directory.IsAbsolute()) {
93 CHECK(PathService::Get(chrome::DIR_RESOURCES, &root_directory));
94 root_directory = root_directory.Append(directory);
96 extension_id = GenerateId(manifest, root_directory);
99 ComponentLoader::ComponentLoader(ExtensionServiceInterface* extension_service,
100 PrefService* profile_prefs,
101 PrefService* local_state,
102 content::BrowserContext* browser_context)
103 : profile_prefs_(profile_prefs),
104 local_state_(local_state),
105 browser_context_(browser_context),
106 extension_service_(extension_service),
107 weak_factory_(this) {}
109 ComponentLoader::~ComponentLoader() {
110 ClearAllRegistered();
113 void ComponentLoader::LoadAll() {
114 for (RegisteredComponentExtensions::iterator it =
115 component_extensions_.begin();
116 it != component_extensions_.end(); ++it) {
121 base::DictionaryValue* ComponentLoader::ParseManifest(
122 const std::string& manifest_contents) const {
123 JSONStringValueSerializer serializer(manifest_contents);
124 scoped_ptr<base::Value> manifest(serializer.Deserialize(NULL, NULL));
126 if (!manifest.get() || !manifest->IsType(base::Value::TYPE_DICTIONARY)) {
127 LOG(ERROR) << "Failed to parse extension manifest.";
130 // Transfer ownership to the caller.
131 return static_cast<base::DictionaryValue*>(manifest.release());
134 void ComponentLoader::ClearAllRegistered() {
135 for (RegisteredComponentExtensions::iterator it =
136 component_extensions_.begin();
137 it != component_extensions_.end(); ++it) {
141 component_extensions_.clear();
144 std::string ComponentLoader::GetExtensionID(
145 int manifest_resource_id,
146 const base::FilePath& root_directory) {
147 std::string manifest_contents = ResourceBundle::GetSharedInstance().
148 GetRawDataResource(manifest_resource_id).as_string();
149 base::DictionaryValue* manifest = ParseManifest(manifest_contents);
151 return std::string();
153 ComponentExtensionInfo info(manifest, root_directory);
154 return info.extension_id;
157 std::string ComponentLoader::Add(int manifest_resource_id,
158 const base::FilePath& root_directory) {
159 std::string manifest_contents =
160 ResourceBundle::GetSharedInstance().GetRawDataResource(
161 manifest_resource_id).as_string();
162 return Add(manifest_contents, root_directory);
165 std::string ComponentLoader::Add(const std::string& manifest_contents,
166 const base::FilePath& root_directory) {
167 // The Value is kept for the lifetime of the ComponentLoader. This is
168 // required in case LoadAll() is called again.
169 base::DictionaryValue* manifest = ParseManifest(manifest_contents);
171 return Add(manifest, root_directory);
172 return std::string();
175 std::string ComponentLoader::Add(const base::DictionaryValue* parsed_manifest,
176 const base::FilePath& root_directory) {
177 ComponentExtensionInfo info(parsed_manifest, root_directory);
178 component_extensions_.push_back(info);
179 if (extension_service_->is_ready())
181 return info.extension_id;
184 std::string ComponentLoader::AddOrReplace(const base::FilePath& path) {
185 base::FilePath absolute_path = base::MakeAbsoluteFilePath(path);
187 scoped_ptr<base::DictionaryValue> manifest(
188 file_util::LoadManifest(absolute_path, &error));
190 LOG(ERROR) << "Could not load extension from '" <<
191 absolute_path.value() << "'. " << error;
192 return std::string();
194 Remove(GenerateId(manifest.get(), absolute_path));
196 return Add(manifest.release(), absolute_path);
199 void ComponentLoader::Reload(const std::string& extension_id) {
200 for (RegisteredComponentExtensions::iterator it =
201 component_extensions_.begin(); it != component_extensions_.end();
203 if (it->extension_id == extension_id) {
210 void ComponentLoader::Load(const ComponentExtensionInfo& info) {
211 // TODO(abarth): We should REQUIRE_MODERN_MANIFEST_VERSION once we've updated
212 // our component extensions to the new manifest version.
213 int flags = Extension::REQUIRE_KEY;
217 scoped_refptr<const Extension> extension(Extension::Create(
223 if (!extension.get()) {
228 CHECK_EQ(info.extension_id, extension->id()) << extension->name();
229 extension_service_->AddComponentExtension(extension.get());
232 void ComponentLoader::Remove(const base::FilePath& root_directory) {
233 // Find the ComponentExtensionInfo for the extension.
234 RegisteredComponentExtensions::iterator it = component_extensions_.begin();
235 for (; it != component_extensions_.end(); ++it) {
236 if (it->root_directory == root_directory) {
237 Remove(GenerateId(it->manifest, root_directory));
243 void ComponentLoader::Remove(const std::string& id) {
244 RegisteredComponentExtensions::iterator it = component_extensions_.begin();
245 for (; it != component_extensions_.end(); ++it) {
246 if (it->extension_id == id) {
247 UnloadComponent(&(*it));
248 it = component_extensions_.erase(it);
254 bool ComponentLoader::Exists(const std::string& id) const {
255 RegisteredComponentExtensions::const_iterator it =
256 component_extensions_.begin();
257 for (; it != component_extensions_.end(); ++it)
258 if (it->extension_id == id)
263 void ComponentLoader::AddFileManagerExtension() {
264 #if defined(OS_CHROMEOS)
266 const CommandLine* command_line = CommandLine::ForCurrentProcess();
267 if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) {
268 base::FilePath filemgr_extension_path(
269 command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath));
270 AddWithNameAndDescription(IDR_FILEMANAGER_MANIFEST,
271 filemgr_extension_path,
272 IDS_FILEMANAGER_APP_NAME,
273 IDS_FILEMANAGER_APP_DESCRIPTION);
277 AddWithNameAndDescription(IDR_FILEMANAGER_MANIFEST,
278 base::FilePath(FILE_PATH_LITERAL("file_manager")),
279 IDS_FILEMANAGER_APP_NAME,
280 IDS_FILEMANAGER_APP_DESCRIPTION);
281 #endif // defined(OS_CHROMEOS)
284 void ComponentLoader::AddVideoPlayerExtension() {
285 #if defined(OS_CHROMEOS)
286 Add(IDR_VIDEO_PLAYER_MANIFEST,
287 base::FilePath(FILE_PATH_LITERAL("video_player")));
288 #endif // defined(OS_CHROMEOS)
291 void ComponentLoader::AddGalleryExtension() {
292 #if defined(OS_CHROMEOS)
293 Add(IDR_GALLERY_MANIFEST, base::FilePath(FILE_PATH_LITERAL("gallery")));
297 void ComponentLoader::AddHangoutServicesExtension() {
298 #if defined(GOOGLE_CHROME_BUILD) || defined(ENABLE_HANGOUT_SERVICES_EXTENSION)
299 Add(IDR_HANGOUT_SERVICES_MANIFEST,
300 base::FilePath(FILE_PATH_LITERAL("hangout_services")));
304 void ComponentLoader::AddHotwordAudioVerificationApp() {
305 CommandLine* command_line = CommandLine::ForCurrentProcess();
306 if (command_line->HasSwitch(switches::kEnableExperimentalHotwording)) {
307 Add(IDR_HOTWORD_AUDIO_VERIFICATION_MANIFEST,
308 base::FilePath(FILE_PATH_LITERAL("hotword_audio_verification")));
312 void ComponentLoader::AddHotwordHelperExtension() {
313 if (HotwordServiceFactory::IsHotwordAllowed(browser_context_)) {
314 CommandLine* command_line = CommandLine::ForCurrentProcess();
315 if (command_line->HasSwitch(switches::kEnableExperimentalHotwording)) {
316 Add(IDR_HOTWORD_MANIFEST,
317 base::FilePath(FILE_PATH_LITERAL("hotword")));
319 Add(IDR_HOTWORD_HELPER_MANIFEST,
320 base::FilePath(FILE_PATH_LITERAL("hotword_helper")));
325 void ComponentLoader::AddImageLoaderExtension() {
326 #if defined(IMAGE_LOADER_EXTENSION)
327 Add(IDR_IMAGE_LOADER_MANIFEST,
328 base::FilePath(FILE_PATH_LITERAL("image_loader")));
329 #endif // defined(IMAGE_LOADER_EXTENSION)
332 void ComponentLoader::AddNetworkSpeechSynthesisExtension() {
333 Add(IDR_NETWORK_SPEECH_SYNTHESIS_MANIFEST,
334 base::FilePath(FILE_PATH_LITERAL("network_speech_synthesis")));
337 #if defined(OS_CHROMEOS)
338 void ComponentLoader::AddChromeVoxExtension(
339 const base::Closure& done_cb) {
340 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
341 base::FilePath resources_path;
342 PathService::Get(chrome::DIR_RESOURCES, &resources_path);
344 base::FilePath chromevox_path =
345 resources_path.Append(extension_misc::kChromeVoxExtensionPath);
347 const CommandLine* command_line = CommandLine::ForCurrentProcess();
348 bool is_chromevox_next =
349 command_line->HasSwitch(chromeos::switches::kEnableChromeVoxNext);
350 bool is_guest = command_line->HasSwitch(chromeos::switches::kGuestSession);
351 const char* manifest_filename;
352 if (is_chromevox_next) {
354 is_guest ? extension_misc::kChromeVoxNextGuestManifestFilename
355 : extension_misc::kChromeVoxNextManifestFilename;
358 is_guest ? extension_misc::kChromeVoxGuestManifestFilename
359 : extension_misc::kChromeVoxManifestFilename;
361 BrowserThread::PostTaskAndReplyWithResult(
364 base::Bind(&LoadManifestOnFileThread, chromevox_path, manifest_filename),
365 base::Bind(&ComponentLoader::AddChromeVoxExtensionWithManifest,
366 weak_factory_.GetWeakPtr(),
371 void ComponentLoader::AddChromeVoxExtensionWithManifest(
372 const base::FilePath& chromevox_path,
373 const base::Closure& done_cb,
374 scoped_ptr<base::DictionaryValue> manifest) {
375 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
376 std::string extension_id = Add(manifest.release(), chromevox_path);
377 CHECK_EQ(extension_misc::kChromeVoxExtensionId, extension_id);
378 if (!done_cb.is_null())
382 std::string ComponentLoader::AddChromeOsSpeechSynthesisExtension() {
383 const CommandLine* command_line = CommandLine::ForCurrentProcess();
384 int idr = command_line->HasSwitch(chromeos::switches::kGuestSession) ?
385 IDR_SPEECH_SYNTHESIS_GUEST_MANIFEST : IDR_SPEECH_SYNTHESIS_MANIFEST;
386 std::string id = Add(idr,
387 base::FilePath(extension_misc::kSpeechSynthesisExtensionPath));
388 EnableFileSystemInGuestMode(id);
393 void ComponentLoader::AddWithNameAndDescription(
394 int manifest_resource_id,
395 const base::FilePath& root_directory,
397 int description_string_id) {
398 std::string manifest_contents =
399 ResourceBundle::GetSharedInstance().GetRawDataResource(
400 manifest_resource_id).as_string();
402 // The Value is kept for the lifetime of the ComponentLoader. This is
403 // required in case LoadAll() is called again.
404 base::DictionaryValue* manifest = ParseManifest(manifest_contents);
407 manifest->SetString(manifest_keys::kName,
408 l10n_util::GetStringUTF8(name_string_id));
409 manifest->SetString(manifest_keys::kDescription,
410 l10n_util::GetStringUTF8(description_string_id));
411 Add(manifest, root_directory);
415 void ComponentLoader::AddChromeApp() {
416 #if defined(ENABLE_APP_LIST)
417 AddWithNameAndDescription(IDR_CHROME_APP_MANIFEST,
418 base::FilePath(FILE_PATH_LITERAL("chrome_app")),
419 IDS_SHORT_PRODUCT_NAME,
420 IDS_CHROME_SHORTCUT_DESCRIPTION);
424 void ComponentLoader::AddKeyboardApp() {
425 #if defined(OS_CHROMEOS)
426 Add(IDR_KEYBOARD_MANIFEST, base::FilePath(FILE_PATH_LITERAL("keyboard")));
430 void ComponentLoader::AddWebStoreApp() {
431 AddWithNameAndDescription(IDR_WEBSTORE_MANIFEST,
432 base::FilePath(FILE_PATH_LITERAL("web_store")),
433 IDS_WEBSTORE_NAME_STORE,
434 IDS_WEBSTORE_APP_DESCRIPTION);
438 void ComponentLoader::EnableBackgroundExtensionsForTesting() {
439 enable_background_extensions_during_testing = true;
442 void ComponentLoader::AddDefaultComponentExtensions(
443 bool skip_session_components) {
444 // Do not add component extensions that have background pages here -- add them
445 // to AddDefaultComponentExtensionsWithBackgroundPages.
446 #if defined(OS_CHROMEOS)
447 Add(IDR_MOBILE_MANIFEST,
448 base::FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile")));
450 #if defined(GOOGLE_CHROME_BUILD)
451 if (browser_defaults::enable_help_app) {
452 Add(IDR_HELP_MANIFEST, base::FilePath(FILE_PATH_LITERAL(
453 "/usr/share/chromeos-assets/helpapp")));
457 // Skip all other extensions that require user session presence.
458 if (!skip_session_components) {
459 const CommandLine* command_line = CommandLine::ForCurrentProcess();
460 if (!command_line->HasSwitch(chromeos::switches::kGuestSession))
461 Add(IDR_BOOKMARKS_MANIFEST,
462 base::FilePath(FILE_PATH_LITERAL("bookmark_manager")));
464 Add(IDR_CROSH_BUILTIN_MANIFEST, base::FilePath(FILE_PATH_LITERAL(
465 "/usr/share/chromeos-assets/crosh_builtin")));
467 #else // !defined(OS_CHROMEOS)
468 DCHECK(!skip_session_components);
469 Add(IDR_BOOKMARKS_MANIFEST,
470 base::FilePath(FILE_PATH_LITERAL("bookmark_manager")));
471 // Cloud Print component app. Not required on Chrome OS.
472 Add(IDR_CLOUDPRINT_MANIFEST,
473 base::FilePath(FILE_PATH_LITERAL("cloud_print")));
476 if (!skip_session_components) {
483 AddDefaultComponentExtensionsWithBackgroundPages(skip_session_components);
486 void ComponentLoader::AddDefaultComponentExtensionsForKioskMode(
487 bool skip_session_components) {
488 // No component extension for kiosk app launch splash screen.
489 if (skip_session_components)
492 // Component extensions needed for kiosk apps.
493 AddFileManagerExtension();
495 // Add virtual keyboard.
499 void ComponentLoader::AddDefaultComponentExtensionsWithBackgroundPages(
500 bool skip_session_components) {
501 const CommandLine* command_line = CommandLine::ForCurrentProcess();
503 // Component extensions with background pages are not enabled during tests
504 // because they generate a lot of background behavior that can interfere.
505 if (!enable_background_extensions_during_testing &&
506 (command_line->HasSwitch(switches::kTestType) ||
507 command_line->HasSwitch(
508 switches::kDisableComponentExtensionsWithBackgroundPages))) {
512 #if defined(OS_CHROMEOS) && defined(GOOGLE_CHROME_BUILD)
513 // Since this is a v2 app it has a background page.
514 AddWithNameAndDescription(IDR_GENIUS_APP_MANIFEST,
515 base::FilePath(FILE_PATH_LITERAL(
516 "/usr/share/chromeos-assets/genius_app")),
518 IDS_GENIUS_APP_DESCRIPTION);
521 if (!skip_session_components) {
522 AddVideoPlayerExtension();
523 AddFileManagerExtension();
524 AddGalleryExtension();
526 AddHangoutServicesExtension();
527 AddHotwordAudioVerificationApp();
528 AddHotwordHelperExtension();
529 AddImageLoaderExtension();
531 #if defined(ENABLE_SETTINGS_APP)
532 Add(IDR_SETTINGS_APP_MANIFEST,
533 base::FilePath(FILE_PATH_LITERAL("settings_app")));
537 // If (!enable_background_extensions_during_testing || this isn't a test)
538 // install_feedback = false;
539 bool install_feedback = enable_background_extensions_during_testing;
540 #if defined(GOOGLE_CHROME_BUILD)
541 install_feedback = true;
542 #endif // defined(GOOGLE_CHROME_BUILD)
543 if (install_feedback)
544 Add(IDR_FEEDBACK_MANIFEST, base::FilePath(FILE_PATH_LITERAL("feedback")));
546 #if defined(OS_CHROMEOS)
547 if (!skip_session_components) {
548 #if defined(GOOGLE_CHROME_BUILD)
549 if (!command_line->HasSwitch(
550 chromeos::switches::kDisableOfficeEditingComponentApp)) {
551 std::string id = Add(IDR_QUICKOFFICE_MANIFEST, base::FilePath(
552 FILE_PATH_LITERAL("/usr/share/chromeos-assets/quickoffice")));
553 EnableFileSystemInGuestMode(id);
555 #endif // defined(GOOGLE_CHROME_BUILD)
557 Add(IDR_ECHO_MANIFEST,
558 base::FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/echo")));
560 if (!command_line->HasSwitch(chromeos::switches::kGuestSession)) {
561 Add(IDR_WALLPAPERMANAGER_MANIFEST,
562 base::FilePath(FILE_PATH_LITERAL("chromeos/wallpaper_manager")));
565 Add(IDR_FIRST_RUN_DIALOG_MANIFEST,
566 base::FilePath(FILE_PATH_LITERAL("chromeos/first_run/app")));
568 Add(IDR_NETWORK_CONFIGURATION_MANIFEST,
569 base::FilePath(FILE_PATH_LITERAL("chromeos/network_configuration")));
571 Add(IDR_CONNECTIVITY_DIAGNOSTICS_MANIFEST,
572 base::FilePath(extension_misc::kConnectivityDiagnosticsPath));
573 Add(IDR_CONNECTIVITY_DIAGNOSTICS_LAUNCHER_MANIFEST,
574 base::FilePath(extension_misc::kConnectivityDiagnosticsLauncherPath));
577 // Load ChromeVox extension now if spoken feedback is enabled.
578 if (chromeos::AccessibilityManager::Get() &&
579 chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) {
580 AddChromeVoxExtension(base::Closure());
582 #endif // defined(OS_CHROMEOS)
584 #if defined(ENABLE_GOOGLE_NOW)
585 const char kEnablePrefix[] = "Enable";
586 const char kFieldTrialName[] = "GoogleNow";
587 std::string enable_prefix(kEnablePrefix);
588 std::string field_trial_result =
589 base::FieldTrialList::FindFullName(kFieldTrialName);
591 bool enabled_via_field_trial =
592 field_trial_result.compare(0, enable_prefix.length(), enable_prefix) == 0;
594 // Enable the feature on trybots and trunk builds.
595 bool enabled_via_trunk_build =
596 chrome::VersionInfo::GetChannel() == chrome::VersionInfo::CHANNEL_UNKNOWN;
598 bool enabled = enabled_via_field_trial || enabled_via_trunk_build;
600 if (!skip_session_components && enabled) {
601 Add(IDR_GOOGLE_NOW_MANIFEST,
602 base::FilePath(FILE_PATH_LITERAL("google_now")));
606 #if defined(GOOGLE_CHROME_BUILD)
607 #if !defined(OS_CHROMEOS) // http://crbug.com/314799
608 AddNetworkSpeechSynthesisExtension();
611 #endif // defined(GOOGLE_CHROME_BUILD)
613 #if defined(ENABLE_PLUGINS)
614 base::FilePath pdf_path;
615 content::PluginService* plugin_service =
616 content::PluginService::GetInstance();
617 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kOutOfProcessPdf) &&
618 PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path) &&
619 plugin_service->GetRegisteredPpapiPluginInfo(pdf_path)) {
620 Add(IDR_PDF_MANIFEST, base::FilePath(FILE_PATH_LITERAL("pdf")));
624 Add(IDR_CRYPTOTOKEN_MANIFEST,
625 base::FilePath(FILE_PATH_LITERAL("cryptotoken")));
628 void ComponentLoader::UnloadComponent(ComponentExtensionInfo* component) {
629 delete component->manifest;
630 if (extension_service_->is_ready()) {
632 RemoveComponentExtension(component->extension_id);
636 void ComponentLoader::EnableFileSystemInGuestMode(const std::string& id) {
637 #if defined(OS_CHROMEOS)
638 const CommandLine* command_line = CommandLine::ForCurrentProcess();
639 if (command_line->HasSwitch(chromeos::switches::kGuestSession)) {
640 // TODO(dpolukhin): Hack to enable HTML5 temporary file system for
641 // the extension. Some component extensions don't work without temporary
642 // file system access. Make sure temporary file system is enabled in the off
643 // the record browser context (as that is the one used in guest session).
644 content::BrowserContext* off_the_record_context =
645 ExtensionsBrowserClient::Get()->GetOffTheRecordContext(
647 GURL site = content::SiteInstance::GetSiteForURL(
648 off_the_record_context, Extension::GetBaseURLFromExtensionId(id));
649 storage::FileSystemContext* file_system_context =
650 content::BrowserContext::GetStoragePartitionForSite(
651 off_the_record_context, site)->GetFileSystemContext();
652 file_system_context->EnableTemporaryFileSystemInIncognito();
657 } // namespace extensions