1 // Copyright 2015 The Chromium Embedded Framework Authors.
2 // Portions copyright 2014 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "libcef/browser/extensions/extension_system.h"
7
8 #include <string>
9
10 #include "libcef/browser/extension_impl.h"
11 #include "libcef/browser/extensions/pdf_extension_util.h"
12 #include "libcef/browser/extensions/value_store/cef_value_store_factory.h"
13 #include "libcef/browser/thread_util.h"
14 #include "libcef/common/extensions/extensions_util.h"
15
16 #include "base/command_line.h"
17 #include "base/files/file_path.h"
18 #include "base/files/file_util.h"
19 #include "base/json/json_string_value_serializer.h"
20 #include "base/path_service.h"
21 #include "base/strings/string_tokenizer.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/common/chrome_paths.h"
26 #include "components/crx_file/id_util.h"
27 #include "content/public/browser/browser_context.h"
28 #include "content/public/browser/browser_task_traits.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/notification_details.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/notification_source.h"
33 #include "content/public/browser/plugin_service.h"
34 #include "content/public/browser/render_process_host.h"
35 #include "extensions/browser/api/app_runtime/app_runtime_api.h"
36 #include "extensions/browser/extension_prefs.h"
37 #include "extensions/browser/extension_registry.h"
38 #include "extensions/browser/info_map.h"
39 #include "extensions/browser/notification_types.h"
40 #include "extensions/browser/null_app_sorting.h"
41 #include "extensions/browser/quota_service.h"
42 #include "extensions/browser/renderer_startup_helper.h"
43 #include "extensions/browser/runtime_data.h"
44 #include "extensions/browser/service_worker_manager.h"
45 #include "extensions/browser/state_store.h"
46 #include "extensions/browser/unloaded_extension_reason.h"
47 #include "extensions/common/constants.h"
48 #include "extensions/common/extension_messages.h"
49 #include "extensions/common/file_util.h"
50 #include "extensions/common/manifest_constants.h"
51 #include "extensions/common/manifest_handlers/mime_types_handler.h"
52 #include "extensions/common/switches.h"
53 #include "net/base/mime_util.h"
54
55 using content::BrowserContext;
56
57 namespace extensions {
58
59 namespace {
60
61 // Implementation based on ComponentLoader::ParseManifest.
ParseManifest(const std::string & manifest_contents)62 std::unique_ptr<base::DictionaryValue> ParseManifest(
63 const std::string& manifest_contents) {
64 JSONStringValueDeserializer deserializer(manifest_contents);
65 std::unique_ptr<base::Value> manifest(
66 deserializer.Deserialize(nullptr, nullptr));
67
68 if (!manifest.get() || !manifest->is_dict()) {
69 LOG(ERROR) << "Failed to parse extension manifest.";
70 return nullptr;
71 }
72 // Transfer ownership to the caller.
73 return base::WrapUnique(
74 static_cast<base::DictionaryValue*>(manifest.release()));
75 }
76
ExecuteLoadFailure(CefRefPtr<CefExtensionHandler> handler,cef_errorcode_t result)77 void ExecuteLoadFailure(CefRefPtr<CefExtensionHandler> handler,
78 cef_errorcode_t result) {
79 if (!handler)
80 return;
81
82 if (!CEF_CURRENTLY_ON_UIT()) {
83 CEF_POST_TASK(CEF_UIT, base::BindOnce(ExecuteLoadFailure, handler, result));
84 return;
85 }
86
87 handler->OnExtensionLoadFailed(result);
88 }
89
LoadExtensionOnUIThread(base::WeakPtr<CefExtensionSystem> context,std::unique_ptr<base::DictionaryValue> manifest,const base::FilePath & root_directory,bool internal,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)90 void LoadExtensionOnUIThread(base::WeakPtr<CefExtensionSystem> context,
91 std::unique_ptr<base::DictionaryValue> manifest,
92 const base::FilePath& root_directory,
93 bool internal,
94 CefRefPtr<CefRequestContext> loader_context,
95 CefRefPtr<CefExtensionHandler> handler) {
96 if (!CEF_CURRENTLY_ON_UIT()) {
97 CEF_POST_TASK(CEF_UIT, base::BindOnce(LoadExtensionOnUIThread, context,
98 std::move(manifest), root_directory,
99 internal, loader_context, handler));
100 return;
101 }
102
103 if (context) {
104 context->LoadExtension(std::move(manifest), root_directory, internal,
105 loader_context, handler);
106 }
107 }
108
LoadExtensionWithManifest(base::WeakPtr<CefExtensionSystem> context,const std::string & manifest_contents,const base::FilePath & root_directory,bool internal,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)109 void LoadExtensionWithManifest(base::WeakPtr<CefExtensionSystem> context,
110 const std::string& manifest_contents,
111 const base::FilePath& root_directory,
112 bool internal,
113 CefRefPtr<CefRequestContext> loader_context,
114 CefRefPtr<CefExtensionHandler> handler) {
115 CEF_REQUIRE_BLOCKING();
116
117 std::unique_ptr<base::DictionaryValue> manifest =
118 ParseManifest(manifest_contents);
119 if (!manifest) {
120 LOG(WARNING) << "Failed to parse extension manifest";
121 ExecuteLoadFailure(handler, ERR_INVALID_ARGUMENT);
122 return;
123 }
124
125 LoadExtensionOnUIThread(context, std::move(manifest), root_directory,
126 internal, loader_context, handler);
127 }
128
LoadExtensionFromDisk(base::WeakPtr<CefExtensionSystem> context,const base::FilePath & root_directory,bool internal,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)129 void LoadExtensionFromDisk(base::WeakPtr<CefExtensionSystem> context,
130 const base::FilePath& root_directory,
131 bool internal,
132 CefRefPtr<CefRequestContext> loader_context,
133 CefRefPtr<CefExtensionHandler> handler) {
134 CEF_REQUIRE_BLOCKING();
135
136 base::FilePath manifest_path = root_directory.AppendASCII("manifest.json");
137 std::string manifest_contents;
138 if (!base::ReadFileToString(manifest_path, &manifest_contents)) {
139 LOG(WARNING) << "Failed to read extension manifest from "
140 << manifest_path.MaybeAsASCII();
141 ExecuteLoadFailure(handler, ERR_FILE_NOT_FOUND);
142 return;
143 }
144
145 LoadExtensionWithManifest(context, manifest_contents, root_directory,
146 internal, loader_context, handler);
147 }
148
149 } // namespace
150
CefExtensionSystem(BrowserContext * browser_context)151 CefExtensionSystem::CefExtensionSystem(BrowserContext* browser_context)
152 : browser_context_(browser_context),
153 initialized_(false),
154 registry_(ExtensionRegistry::Get(browser_context)),
155 renderer_helper_(
156 extensions::RendererStartupHelperFactory::GetForBrowserContext(
157 browser_context)),
158 weak_ptr_factory_(this) {
159 InitPrefs();
160 }
161
~CefExtensionSystem()162 CefExtensionSystem::~CefExtensionSystem() {}
163
Init()164 void CefExtensionSystem::Init() {
165 DCHECK(!initialized_);
166
167 // There's complexity here related to the ordering of message delivery. For
168 // an extension to load correctly both the ExtensionMsg_Loaded and
169 // ExtensionMsg_ActivateExtension messages must be sent. These messages are
170 // currently sent by RendererStartupHelper, ExtensionWebContentsObserver, and
171 // this class. ExtensionMsg_Loaded is handled by Dispatcher::OnLoaded and adds
172 // the extension to |extensions_|. ExtensionMsg_ActivateExtension is handled
173 // by Dispatcher::OnActivateExtension and adds the extension to
174 // |active_extension_ids_|. If these messages are not sent correctly then
175 // ScriptContextSet::Register called from Dispatcher::DidCreateScriptContext
176 // will classify the extension incorrectly and API bindings will not be added.
177
178 // Inform the rest of the extensions system to start.
179 ready_.Signal();
180
181 // Add the internal PDF extension. PDF loading works as follows:
182 // 1. The PDF PPAPI plugin is registered in libcef/common/content_client.cc
183 // ComputeBuiltInPlugins to handle the kPDFPluginOutOfProcessMimeType.
184 // 2. The PDF extension is registered by the below call to AddExtension and
185 // associated with the "application/pdf" mime type.
186 // 3. Web content running in the owner CefBrowser requests to load a PDF file
187 // resource with the "application/pdf" mime type. This can be via a frame
188 // (main frame/iframe) or object/embed tag.
189 // 4. PluginResponseInterceptorURLLoaderThrottle intercepts the PDF resource
190 // load in the browser process and registers the PDF resource as a stream
191 // via MimeHandlerStreamManager::AddStream.
192 // 5. PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse triggers
193 // creation of a MimeHandlerViewEmbedder in the browser process via
194 // MimeHandlerViewAttachHelper::OverrideBodyForInterceptedResponse.
195 // 6. MimeHandlerViewEmbedder::ReadyToCommitNavigation is called and sends a
196 // Mojo message to MimeHandlerViewContainerManager::SetInternalId in the
197 // owner renderer process.
198 // 7. The MimeHandlerViewContainerManager is created in the owner renderer
199 // process via MimeHandlerViewContainerManager::BindReceiver and the
200 // SetInternalId call arrives.
201 // 8. HTMLPlugInElement::RequestObject is called in the owner renderer process
202 // to handle the PDF file frame/object/embed tag. This results in calls to
203 // ContentBrowserClient::GetPluginMimeTypesWithExternalHandlers (browser
204 // process) and ContentRendererClient::IsPluginHandledExternally (owner
205 // renderer process), and determines that the plugin should be handled
206 // externally (handled_externally=true).
207 // 9. MimeHandlerViewContainerManager::IsManagedByContainerManager sends a
208 // Mojo message to MimeHandlerViewEmbedder::ReadyToCreateMimeHandlerView
209 // in the browser process.
210 // 10.MimeHandlerViewEmbedder::RenderFrameCreated triggers creation of a
211 // MimeHandlerViewGuest and CefMimeHandlerViewGuestDelegate in the browser
212 // process.
213 // 11.MimeHandlerViewGuest::CreateWebContents creates a new guest WebContents
214 // (is_guest_view=true) to host the PDF extension and the PDF resource
215 // stream is retrieved via MimeHandlerStreamManager::ReleaseStream.
216 // 12.MimeHandlerViewGuest::DidAttachToEmbedder calls
217 // CefMimeHandlerViewGuestDelegate::OnGuestAttached to associate the guest
218 // WebContents routing IDs with the owner CefBrowser. MimeHandlerViewGuest
219 // then loads the extension URL (index.html) in the guest WebContents.
220 // 13.Creation of the RenderFrame in the guest renderer process triggers a
221 // sync IPC call from AlloyContentRendererClient::MaybeCreateBrowser to
222 // CefBrowserInfoManager::GetBrowserInfo in the browser process to retrieve
223 // the CefBrowser information, which will be immediately available due to
224 // step 12.
225 // 14.The PDF extension begins to load. Extension resource requests are
226 // handled via ExtensionURLLoaderFactory::CreateLoaderAndStart in the
227 // browser process. Access to PDF extension resources is checked by
228 // CefExtensionsBrowserClient::AllowCrossRendererResourceLoad and
229 // PDF extension resources are provided from bundle via
230 // CefExtensionsBrowserClient::LoadResourceFromResourceBundle
231 // and CefComponentExtensionResourceManager. Access to chrome://resources
232 // is granted via CefExtensionWebContentsObserver::RenderViewCreated.
233 // 15.The PDF extension calls chrome.mimeHandlerPrivate.getStreamInfo
234 // (chrome/browser/resources/pdf/browser_api.js) to retrieve the PDF
235 // resource stream. This API is implemented using Mojo as described in
236 // libcef/common/extensions/api/README.txt.
237 // 16.The PDF extension requests the PDF PPAPI plugin to handle
238 // kPDFPluginOutOfProcessMimeType. Approval arrives in the guest renderer
239 // process via ExtensionFrameHelper::OnExtensionResponse which calls
240 // NativeExtensionBindingsSystem::HandleResponse. This triggers creation of
241 // an HTMLPlugInElement via native V8 bindings to host the PDF plugin.
242 // 17.HTMLPlugInElement::RequestObject is called in the guest renderer process
243 // and determines that the PDF PPAPI plugin should be handled internally
244 // (handled_externally=false). A PluginDocument is created and
245 // AlloyContentRendererClient::OverrideCreatePlugin is called to create a
246 // WebPlugin.
247 // 18.The PDF extension and PDF plugin are now loaded. Print commands, if
248 // any, are handled in the guest renderer process by ChromePDFPrintClient
249 // and CefPrintRenderFrameHelperDelegate.
250 // 19.When navigating away from the PDF file or closing the owner CefBrowser
251 // the guest WebContents will be destroyed. This triggers a call to
252 // CefMimeHandlerViewGuestDelegate::OnGuestDetached which removes the
253 // routing ID association with the owner CefBrowser.
254 if (PdfExtensionEnabled()) {
255 LoadExtension(ParseManifest(pdf_extension_util::GetManifest()),
256 base::FilePath(FILE_PATH_LITERAL("pdf")), true /* internal */,
257 nullptr, nullptr);
258 }
259
260 initialized_ = true;
261 }
262
LoadExtension(const base::FilePath & root_directory,bool internal,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)263 void CefExtensionSystem::LoadExtension(
264 const base::FilePath& root_directory,
265 bool internal,
266 CefRefPtr<CefRequestContext> loader_context,
267 CefRefPtr<CefExtensionHandler> handler) {
268 CEF_REQUIRE_UIT();
269 CEF_POST_USER_VISIBLE_TASK(
270 base::BindOnce(LoadExtensionFromDisk, weak_ptr_factory_.GetWeakPtr(),
271 root_directory, internal, loader_context, handler));
272 }
273
LoadExtension(const std::string & manifest_contents,const base::FilePath & root_directory,bool internal,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)274 void CefExtensionSystem::LoadExtension(
275 const std::string& manifest_contents,
276 const base::FilePath& root_directory,
277 bool internal,
278 CefRefPtr<CefRequestContext> loader_context,
279 CefRefPtr<CefExtensionHandler> handler) {
280 CEF_REQUIRE_UIT();
281 CEF_POST_USER_VISIBLE_TASK(base::BindOnce(
282 LoadExtensionWithManifest, weak_ptr_factory_.GetWeakPtr(),
283 manifest_contents, root_directory, internal, loader_context, handler));
284 }
285
286 // Implementation based on ComponentLoader::Add.
LoadExtension(std::unique_ptr<base::DictionaryValue> manifest,const base::FilePath & root_directory,bool internal,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)287 void CefExtensionSystem::LoadExtension(
288 std::unique_ptr<base::DictionaryValue> manifest,
289 const base::FilePath& root_directory,
290 bool internal,
291 CefRefPtr<CefRequestContext> loader_context,
292 CefRefPtr<CefExtensionHandler> handler) {
293 CEF_REQUIRE_UIT();
294
295 // Internal extensions don't have a loader context. External extensions should.
296 #if DCHECK_IS_ON()
297 if (internal) {
298 DCHECK(!loader_context);
299 } else {
300 DCHECK(loader_context);
301 }
302 #endif
303
304 ComponentExtensionInfo info(manifest.get(), root_directory, internal);
305 const Extension* extension = LoadExtension(info, loader_context, handler);
306 if (!extension)
307 ExecuteLoadFailure(handler, ERR_FAILED);
308 }
309
310 // Implementation based on ExtensionService::RemoveComponentExtension.
UnloadExtension(const std::string & extension_id)311 bool CefExtensionSystem::UnloadExtension(const std::string& extension_id) {
312 CEF_REQUIRE_UIT();
313 ExtensionMap::iterator it = extension_map_.find(extension_id);
314 if (it == extension_map_.end()) {
315 // No CEF representation so we've already unloaded it.
316 return false;
317 }
318
319 CefRefPtr<CefExtensionImpl> cef_extension =
320 static_cast<CefExtensionImpl*>(it->second.get());
321
322 // Erase first so that callbacks can't retrieve the unloaded extension.
323 extension_map_.erase(it);
324
325 cef_extension->OnExtensionUnloaded();
326
327 scoped_refptr<const Extension> extension(
328 registry_->GetInstalledExtension(extension_id));
329 UnloadExtension(extension_id, UnloadedExtensionReason::UNINSTALL);
330 if (extension.get()) {
331 registry_->TriggerOnUninstalled(
332 extension.get(), extensions::UNINSTALL_REASON_COMPONENT_REMOVED);
333 }
334
335 return true;
336 }
337
HasExtension(const std::string & extension_id) const338 bool CefExtensionSystem::HasExtension(const std::string& extension_id) const {
339 return !!GetExtension(extension_id);
340 }
341
GetExtension(const std::string & extension_id) const342 CefRefPtr<CefExtension> CefExtensionSystem::GetExtension(
343 const std::string& extension_id) const {
344 CEF_REQUIRE_UIT();
345 ExtensionMap::const_iterator it = extension_map_.find(extension_id);
346 if (it != extension_map_.end())
347 return it->second;
348 return nullptr;
349 }
350
GetExtensions() const351 CefExtensionSystem::ExtensionMap CefExtensionSystem::GetExtensions() const {
352 CEF_REQUIRE_UIT();
353 return extension_map_;
354 }
355
OnRequestContextDeleted(CefRequestContext * context)356 void CefExtensionSystem::OnRequestContextDeleted(CefRequestContext* context) {
357 CEF_REQUIRE_UIT();
358 DCHECK(context);
359
360 // Make a copy of the map because UnloadExtension will modify it.
361 // Don't add any references to |context|.
362 ExtensionMap map = extension_map_;
363 ExtensionMap::const_iterator it = map.begin();
364 for (; it != map.end(); ++it) {
365 CefRefPtr<CefExtensionImpl> cef_extension =
366 static_cast<CefExtensionImpl*>(it->second.get());
367 if (cef_extension->loader_context() == context)
368 UnloadExtension(it->first);
369 }
370 }
371
Shutdown()372 void CefExtensionSystem::Shutdown() {
373 CEF_REQUIRE_UIT();
374 // Only internal extensions should exist at this point.
375 #if DCHECK_IS_ON()
376 ExtensionMap::iterator it = extension_map_.begin();
377 for (; it != extension_map_.end(); ++it) {
378 CefRefPtr<CefExtensionImpl> cef_extension =
379 static_cast<CefExtensionImpl*>(it->second.get());
380 DCHECK(!cef_extension->loader_context());
381 }
382 #endif
383 extension_map_.clear();
384 }
385
InitForRegularProfile(bool extensions_enabled)386 void CefExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
387 DCHECK(!initialized_);
388 service_worker_manager_.reset(new ServiceWorkerManager(browser_context_));
389 runtime_data_.reset(new RuntimeData(registry_));
390 quota_service_.reset(new QuotaService);
391 app_sorting_.reset(new NullAppSorting);
392 }
393
extension_service()394 ExtensionService* CefExtensionSystem::extension_service() {
395 return nullptr;
396 }
397
runtime_data()398 RuntimeData* CefExtensionSystem::runtime_data() {
399 return runtime_data_.get();
400 }
401
management_policy()402 ManagementPolicy* CefExtensionSystem::management_policy() {
403 return nullptr;
404 }
405
service_worker_manager()406 ServiceWorkerManager* CefExtensionSystem::service_worker_manager() {
407 return service_worker_manager_.get();
408 }
409
user_script_manager()410 UserScriptManager* CefExtensionSystem::user_script_manager() {
411 return nullptr;
412 }
413
state_store()414 StateStore* CefExtensionSystem::state_store() {
415 return state_store_.get();
416 }
417
rules_store()418 StateStore* CefExtensionSystem::rules_store() {
419 return rules_store_.get();
420 }
421
store_factory()422 scoped_refptr<ValueStoreFactory> CefExtensionSystem::store_factory() {
423 return store_factory_;
424 }
425
info_map()426 InfoMap* CefExtensionSystem::info_map() {
427 if (!info_map_.get())
428 info_map_ = new InfoMap;
429 return info_map_.get();
430 }
431
quota_service()432 QuotaService* CefExtensionSystem::quota_service() {
433 return quota_service_.get();
434 }
435
app_sorting()436 AppSorting* CefExtensionSystem::app_sorting() {
437 return app_sorting_.get();
438 }
439
440 // Implementation based on
441 // ExtensionSystemImpl::RegisterExtensionWithRequestContexts.
RegisterExtensionWithRequestContexts(const Extension * extension,base::OnceClosure callback)442 void CefExtensionSystem::RegisterExtensionWithRequestContexts(
443 const Extension* extension,
444 base::OnceClosure callback) {
445 // TODO(extensions): The |incognito_enabled| value should be set based on
446 // manifest settings.
447 content::GetIOThreadTaskRunner({})->PostTaskAndReply(
448 FROM_HERE,
449 base::Bind(&InfoMap::AddExtension, info_map(),
450 base::RetainedRef(extension), base::Time::Now(),
451 true, // incognito_enabled
452 false), // notifications_disabled
453 std::move(callback));
454 }
455
456 // Implementation based on
457 // ExtensionSystemImpl::UnregisterExtensionWithRequestContexts.
UnregisterExtensionWithRequestContexts(const std::string & extension_id,const UnloadedExtensionReason reason)458 void CefExtensionSystem::UnregisterExtensionWithRequestContexts(
459 const std::string& extension_id,
460 const UnloadedExtensionReason reason) {
461 content::GetIOThreadTaskRunner({})->PostTask(
462 FROM_HERE,
463 base::Bind(&InfoMap::RemoveExtension, info_map(), extension_id, reason));
464 }
465
ready() const466 const base::OneShotEvent& CefExtensionSystem::ready() const {
467 return ready_;
468 }
469
is_ready() const470 bool CefExtensionSystem::is_ready() const {
471 return ready_.is_signaled();
472 }
473
content_verifier()474 ContentVerifier* CefExtensionSystem::content_verifier() {
475 return nullptr;
476 }
477
GetDependentExtensions(const Extension * extension)478 std::unique_ptr<ExtensionSet> CefExtensionSystem::GetDependentExtensions(
479 const Extension* extension) {
480 return std::make_unique<ExtensionSet>();
481 }
482
InstallUpdate(const std::string & extension_id,const std::string & public_key,const base::FilePath & temp_dir,bool install_immediately,InstallUpdateCallback install_update_callback)483 void CefExtensionSystem::InstallUpdate(
484 const std::string& extension_id,
485 const std::string& public_key,
486 const base::FilePath& temp_dir,
487 bool install_immediately,
488 InstallUpdateCallback install_update_callback) {
489 NOTREACHED();
490 base::DeletePathRecursively(temp_dir);
491 }
492
PerformActionBasedOnOmahaAttributes(const std::string & extension_id,const base::Value & attributes)493 void CefExtensionSystem::PerformActionBasedOnOmahaAttributes(
494 const std::string& extension_id,
495 const base::Value& attributes) {
496 NOTREACHED();
497 }
498
FinishDelayedInstallationIfReady(const std::string & extension_id,bool install_immediately)499 bool CefExtensionSystem::FinishDelayedInstallationIfReady(
500 const std::string& extension_id,
501 bool install_immediately) {
502 NOTREACHED();
503 return false;
504 }
505
ComponentExtensionInfo(const base::DictionaryValue * manifest,const base::FilePath & directory,bool internal)506 CefExtensionSystem::ComponentExtensionInfo::ComponentExtensionInfo(
507 const base::DictionaryValue* manifest,
508 const base::FilePath& directory,
509 bool internal)
510 : manifest(manifest), root_directory(directory), internal(internal) {
511 if (!root_directory.IsAbsolute()) {
512 // This path structure is required by
513 // url_request_util::MaybeCreateURLRequestResourceBundleJob.
514 CHECK(base::PathService::Get(chrome::DIR_RESOURCES, &root_directory));
515 root_directory = root_directory.Append(directory);
516 }
517 }
518
InitPrefs()519 void CefExtensionSystem::InitPrefs() {
520 store_factory_ = new CefValueStoreFactory(browser_context_->GetPath());
521
522 Profile* profile = Profile::FromBrowserContext(browser_context_);
523
524 // Two state stores. The latter, which contains declarative rules, must be
525 // loaded immediately so that the rules are ready before we issue network
526 // requests.
527 state_store_.reset(new StateStore(
528 profile, store_factory_, ValueStoreFrontend::BackendType::STATE, true));
529
530 rules_store_.reset(new StateStore(
531 profile, store_factory_, ValueStoreFrontend::BackendType::RULES, false));
532 }
533
534 // Implementation based on ComponentLoader::CreateExtension.
CreateExtension(const ComponentExtensionInfo & info,std::string * utf8_error)535 scoped_refptr<const Extension> CefExtensionSystem::CreateExtension(
536 const ComponentExtensionInfo& info,
537 std::string* utf8_error) {
538 // TODO(abarth): We should REQUIRE_MODERN_MANIFEST_VERSION once we've updated
539 // our component extensions to the new manifest version.
540 int flags = 0;
541 if (info.internal) {
542 // Internal extensions must have kPublicKey in the manifest.
543 flags |= Extension::REQUIRE_KEY;
544 }
545 return Extension::Create(
546 info.root_directory,
547 // Tests should continue to use the Manifest::COMMAND_LINE value here
548 // Some Chrome APIs will cause undesired effects if this is incorrect
549 // e.g.: alarms API has 1 minute minimum applied to Packed Extensions
550 info.internal ? mojom::ManifestLocation::kComponent
551 : mojom::ManifestLocation::kCommandLine,
552 *info.manifest, flags, utf8_error);
553 }
554
555 // Implementation based on ComponentLoader::Load and
556 // ExtensionService::AddExtension.
LoadExtension(const ComponentExtensionInfo & info,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)557 const Extension* CefExtensionSystem::LoadExtension(
558 const ComponentExtensionInfo& info,
559 CefRefPtr<CefRequestContext> loader_context,
560 CefRefPtr<CefExtensionHandler> handler) {
561 std::string error;
562 scoped_refptr<const Extension> extension(CreateExtension(info, &error));
563 if (!extension.get()) {
564 LOG(ERROR) << error;
565 return nullptr;
566 }
567
568 if (registry_->GetInstalledExtension(extension->id())) {
569 LOG(ERROR) << "Extension with id " << extension->id()
570 << "is already installed";
571 return nullptr;
572 }
573
574 CefRefPtr<CefExtensionImpl> cef_extension =
575 new CefExtensionImpl(extension.get(), loader_context.get(), handler);
576
577 // Insert first so that callbacks can retrieve the loaded extension.
578 extension_map_.insert(std::make_pair(extension->id(), cef_extension));
579
580 // This may trigger additional callbacks.
581 registry_->AddEnabled(extension.get());
582 NotifyExtensionLoaded(extension.get());
583
584 cef_extension->OnExtensionLoaded();
585
586 return extension.get();
587 }
588
589 // Implementation based on ExtensionService::UnloadExtension.
UnloadExtension(const std::string & extension_id,UnloadedExtensionReason reason)590 void CefExtensionSystem::UnloadExtension(const std::string& extension_id,
591 UnloadedExtensionReason reason) {
592 // Make sure the extension gets deleted after we return from this function.
593 int include_mask =
594 ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::TERMINATED;
595 scoped_refptr<const Extension> extension(
596 registry_->GetExtensionById(extension_id, include_mask));
597
598 // This method can be called via PostTask, so the extension may have been
599 // unloaded by the time this runs.
600 if (!extension.get()) {
601 // In case the extension may have crashed/uninstalled. Allow the profile to
602 // clean up its RequestContexts.
603 UnregisterExtensionWithRequestContexts(extension_id, reason);
604 return;
605 }
606
607 if (registry_->disabled_extensions().Contains(extension->id())) {
608 registry_->RemoveDisabled(extension->id());
609 // Make sure the profile cleans up its RequestContexts when an already
610 // disabled extension is unloaded (since they are also tracking the disabled
611 // extensions).
612 UnregisterExtensionWithRequestContexts(extension_id, reason);
613 // Don't send the unloaded notification. It was sent when the extension
614 // was disabled.
615 } else {
616 // Remove the extension from the enabled list.
617 registry_->RemoveEnabled(extension->id());
618 NotifyExtensionUnloaded(extension.get(), reason);
619 }
620
621 content::NotificationService::current()->Notify(
622 extensions::NOTIFICATION_EXTENSION_REMOVED,
623 content::Source<content::BrowserContext>(browser_context_),
624 content::Details<const Extension>(extension.get()));
625 }
626
627 // Implementation based on ExtensionService::NotifyExtensionLoaded.
NotifyExtensionLoaded(const Extension * extension)628 void CefExtensionSystem::NotifyExtensionLoaded(const Extension* extension) {
629 // The URLRequestContexts need to be first to know that the extension
630 // was loaded, otherwise a race can arise where a renderer that is created
631 // for the extension may try to load an extension URL with an extension id
632 // that the request context doesn't yet know about. The profile is responsible
633 // for ensuring its URLRequestContexts appropriately discover the loaded
634 // extension.
635 RegisterExtensionWithRequestContexts(
636 extension,
637 base::Bind(&CefExtensionSystem::OnExtensionRegisteredWithRequestContexts,
638 weak_ptr_factory_.GetWeakPtr(),
639 base::WrapRefCounted(extension)));
640
641 // Tell renderers about the loaded extension.
642 renderer_helper_->OnExtensionLoaded(*extension);
643
644 // Tell subsystems that use the ExtensionRegistryObserver::OnExtensionLoaded
645 // about the new extension.
646 //
647 // NOTE: It is important that this happen after notifying the renderers about
648 // the new extensions so that if we navigate to an extension URL in
649 // ExtensionRegistryObserver::OnExtensionLoaded the renderer is guaranteed to
650 // know about it.
651 registry_->TriggerOnLoaded(extension);
652
653 // Register plugins included with the extension.
654 // Implementation based on PluginManager::OnExtensionLoaded.
655 const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
656 if (handler && !handler->handler_url().empty()) {
657 content::WebPluginInfo info;
658 info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN;
659 info.name = base::UTF8ToUTF16(extension->name());
660 info.path = base::FilePath::FromUTF8Unsafe(extension->url().spec());
661
662 for (std::set<std::string>::const_iterator mime_type =
663 handler->mime_type_set().begin();
664 mime_type != handler->mime_type_set().end(); ++mime_type) {
665 content::WebPluginMimeType mime_type_info;
666 mime_type_info.mime_type = *mime_type;
667 base::FilePath::StringType file_extension;
668 if (net::GetPreferredExtensionForMimeType(*mime_type, &file_extension)) {
669 mime_type_info.file_extensions.push_back(
670 base::FilePath(file_extension).AsUTF8Unsafe());
671 }
672 info.mime_types.push_back(mime_type_info);
673 }
674 content::PluginService* plugin_service =
675 content::PluginService::GetInstance();
676 plugin_service->RefreshPlugins();
677 plugin_service->RegisterInternalPlugin(info, true);
678 }
679 }
680
OnExtensionRegisteredWithRequestContexts(scoped_refptr<const extensions::Extension> extension)681 void CefExtensionSystem::OnExtensionRegisteredWithRequestContexts(
682 scoped_refptr<const extensions::Extension> extension) {
683 registry_->AddReady(extension);
684 if (registry_->enabled_extensions().Contains(extension->id()))
685 registry_->TriggerOnReady(extension.get());
686 }
687
688 // Implementation based on ExtensionService::NotifyExtensionUnloaded.
NotifyExtensionUnloaded(const Extension * extension,UnloadedExtensionReason reason)689 void CefExtensionSystem::NotifyExtensionUnloaded(
690 const Extension* extension,
691 UnloadedExtensionReason reason) {
692 // Unregister plugins included with the extension.
693 // Implementation based on PluginManager::OnExtensionUnloaded.
694 const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
695 if (handler && !handler->handler_url().empty()) {
696 base::FilePath path =
697 base::FilePath::FromUTF8Unsafe(extension->url().spec());
698 content::PluginService* plugin_service =
699 content::PluginService::GetInstance();
700 plugin_service->UnregisterInternalPlugin(path);
701 plugin_service->RefreshPlugins();
702 }
703
704 registry_->TriggerOnUnloaded(extension, reason);
705
706 // Tell renderers about the unloaded extension.
707 renderer_helper_->OnExtensionUnloaded(*extension);
708
709 UnregisterExtensionWithRequestContexts(extension->id(), reason);
710 }
711
712 } // namespace extensions
713