• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/value_store/cef_value_store_factory.h"
12 #include "libcef/browser/thread_util.h"
13 #include "libcef/common/extensions/extensions_util.h"
14 
15 #include "base/bind.h"
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/pdf/pdf_extension_util.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "components/crx_file/id_util.h"
28 #include "content/public/browser/browser_context.h"
29 #include "content/public/browser/browser_task_traits.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/notification_details.h"
32 #include "content/public/browser/notification_service.h"
33 #include "content/public/browser/notification_source.h"
34 #include "content/public/browser/plugin_service.h"
35 #include "content/public/browser/render_process_host.h"
36 #include "extensions/browser/api/app_runtime/app_runtime_api.h"
37 #include "extensions/browser/extension_prefs.h"
38 #include "extensions/browser/extension_registry.h"
39 #include "extensions/browser/info_map.h"
40 #include "extensions/browser/notification_types.h"
41 #include "extensions/browser/null_app_sorting.h"
42 #include "extensions/browser/quota_service.h"
43 #include "extensions/browser/renderer_startup_helper.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 plugin is registered in libcef/common/content_client.cc
183   //    ComputeBuiltInPlugins to handle the pdf::kInternalPluginMimeType.
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 requests the PDF plugin to handle
234   //    pdf::kInternalPluginMimeType. Approval arrives in the guest renderer
235   //    process via ExtensionFrameHelper::OnExtensionResponse which calls
236   //    NativeExtensionBindingsSystem::HandleResponse. This triggers creation of
237   //    an HTMLPlugInElement via native V8 bindings to host the PDF plugin.
238   // 16.- With the old PPAPI plugin:
239   //      The PDF extension calls chrome.mimeHandlerPrivate.getStreamInfo
240   //      (chrome/browser/resources/pdf/browser_api.js) to retrieve the PDF
241   //      resource stream. This API is implemented using Mojo as described in
242   //      libcef/common/extensions/api/README.txt.
243   //    - With the new PdfUnseasoned plugin:
244   //      The PDF resource navigation is redirected by PdfNavigationThrottle and
245   //      the stream contents are replaced by PdfURLLoaderRequestInterceptor.
246   // 17.HTMLPlugInElement::RequestObject is called in the guest renderer process
247   //    and determines that the PDF plugin should be handled internally
248   //    (handled_externally=false). A PluginDocument is created and
249   //    AlloyContentRendererClient::OverrideCreatePlugin is called to create a
250   //    WebPlugin.
251   // 18.- With the old PPAPI plugin:
252   //      The PDF plugin is loaded by ChromeContentRendererClient::CreatePlugin
253   //      calling RenderFrameImpl::CreatePlugin.
254   //    - With the new PdfUnseasoned plugin:
255   //      The PDF plugin is loaded by ChromeContentRendererClient::CreatePlugin
256   //      calling pdf::CreateInternalPlugin.
257   // 19.The PDF extension and PDF plugin are now loaded. Print commands, if
258   //    any, are handled in the guest renderer process by ChromePDFPrintClient
259   //    and CefPrintRenderFrameHelperDelegate.
260   // 20.When navigating away from the PDF file or closing the owner CefBrowser
261   //    the guest WebContents will be destroyed. This triggers a call to
262   //    CefMimeHandlerViewGuestDelegate::OnGuestDetached which removes the
263   //    routing ID association with the owner CefBrowser.
264   if (PdfExtensionEnabled()) {
265     LoadExtension(ParseManifest(pdf_extension_util::GetManifest()),
266                   base::FilePath(FILE_PATH_LITERAL("pdf")), true /* internal */,
267                   nullptr, nullptr);
268   }
269 
270   initialized_ = true;
271 }
272 
LoadExtension(const base::FilePath & root_directory,bool internal,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)273 void CefExtensionSystem::LoadExtension(
274     const base::FilePath& root_directory,
275     bool internal,
276     CefRefPtr<CefRequestContext> loader_context,
277     CefRefPtr<CefExtensionHandler> handler) {
278   CEF_REQUIRE_UIT();
279   CEF_POST_USER_VISIBLE_TASK(
280       base::BindOnce(LoadExtensionFromDisk, weak_ptr_factory_.GetWeakPtr(),
281                      root_directory, internal, loader_context, handler));
282 }
283 
LoadExtension(const std::string & manifest_contents,const base::FilePath & root_directory,bool internal,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)284 void CefExtensionSystem::LoadExtension(
285     const std::string& manifest_contents,
286     const base::FilePath& root_directory,
287     bool internal,
288     CefRefPtr<CefRequestContext> loader_context,
289     CefRefPtr<CefExtensionHandler> handler) {
290   CEF_REQUIRE_UIT();
291   CEF_POST_USER_VISIBLE_TASK(base::BindOnce(
292       LoadExtensionWithManifest, weak_ptr_factory_.GetWeakPtr(),
293       manifest_contents, root_directory, internal, loader_context, handler));
294 }
295 
296 // 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)297 void CefExtensionSystem::LoadExtension(
298     std::unique_ptr<base::DictionaryValue> manifest,
299     const base::FilePath& root_directory,
300     bool internal,
301     CefRefPtr<CefRequestContext> loader_context,
302     CefRefPtr<CefExtensionHandler> handler) {
303   CEF_REQUIRE_UIT();
304 
305 // Internal extensions don't have a loader context. External extensions should.
306 #if DCHECK_IS_ON()
307   if (internal) {
308     DCHECK(!loader_context);
309   } else {
310     DCHECK(loader_context);
311   }
312 #endif
313 
314   ComponentExtensionInfo info(manifest.get(), root_directory, internal);
315   const Extension* extension = LoadExtension(info, loader_context, handler);
316   if (!extension)
317     ExecuteLoadFailure(handler, ERR_FAILED);
318 }
319 
320 // Implementation based on ExtensionService::RemoveComponentExtension.
UnloadExtension(const std::string & extension_id)321 bool CefExtensionSystem::UnloadExtension(const std::string& extension_id) {
322   CEF_REQUIRE_UIT();
323   ExtensionMap::iterator it = extension_map_.find(extension_id);
324   if (it == extension_map_.end()) {
325     // No CEF representation so we've already unloaded it.
326     return false;
327   }
328 
329   CefRefPtr<CefExtensionImpl> cef_extension =
330       static_cast<CefExtensionImpl*>(it->second.get());
331 
332   // Erase first so that callbacks can't retrieve the unloaded extension.
333   extension_map_.erase(it);
334 
335   cef_extension->OnExtensionUnloaded();
336 
337   scoped_refptr<const Extension> extension(
338       registry_->GetInstalledExtension(extension_id));
339   UnloadExtension(extension_id, UnloadedExtensionReason::UNINSTALL);
340   if (extension.get()) {
341     registry_->TriggerOnUninstalled(
342         extension.get(), extensions::UNINSTALL_REASON_COMPONENT_REMOVED);
343   }
344 
345   return true;
346 }
347 
HasExtension(const std::string & extension_id) const348 bool CefExtensionSystem::HasExtension(const std::string& extension_id) const {
349   return !!GetExtension(extension_id);
350 }
351 
GetExtension(const std::string & extension_id) const352 CefRefPtr<CefExtension> CefExtensionSystem::GetExtension(
353     const std::string& extension_id) const {
354   CEF_REQUIRE_UIT();
355   ExtensionMap::const_iterator it = extension_map_.find(extension_id);
356   if (it != extension_map_.end())
357     return it->second;
358   return nullptr;
359 }
360 
GetExtensions() const361 CefExtensionSystem::ExtensionMap CefExtensionSystem::GetExtensions() const {
362   CEF_REQUIRE_UIT();
363   return extension_map_;
364 }
365 
OnRequestContextDeleted(CefRequestContext * context)366 void CefExtensionSystem::OnRequestContextDeleted(CefRequestContext* context) {
367   CEF_REQUIRE_UIT();
368   DCHECK(context);
369 
370   // Make a copy of the map because UnloadExtension will modify it.
371   // Don't add any references to |context|.
372   ExtensionMap map = extension_map_;
373   ExtensionMap::const_iterator it = map.begin();
374   for (; it != map.end(); ++it) {
375     CefRefPtr<CefExtensionImpl> cef_extension =
376         static_cast<CefExtensionImpl*>(it->second.get());
377     if (cef_extension->loader_context() == context)
378       UnloadExtension(it->first);
379   }
380 }
381 
Shutdown()382 void CefExtensionSystem::Shutdown() {
383   CEF_REQUIRE_UIT();
384 // Only internal extensions should exist at this point.
385 #if DCHECK_IS_ON()
386   ExtensionMap::iterator it = extension_map_.begin();
387   for (; it != extension_map_.end(); ++it) {
388     CefRefPtr<CefExtensionImpl> cef_extension =
389         static_cast<CefExtensionImpl*>(it->second.get());
390     DCHECK(!cef_extension->loader_context());
391   }
392 #endif
393   extension_map_.clear();
394 }
395 
InitForRegularProfile(bool extensions_enabled)396 void CefExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
397   DCHECK(!initialized_);
398   service_worker_manager_.reset(new ServiceWorkerManager(browser_context_));
399   quota_service_.reset(new QuotaService);
400   app_sorting_.reset(new NullAppSorting);
401 }
402 
extension_service()403 ExtensionService* CefExtensionSystem::extension_service() {
404   return nullptr;
405 }
406 
management_policy()407 ManagementPolicy* CefExtensionSystem::management_policy() {
408   return nullptr;
409 }
410 
service_worker_manager()411 ServiceWorkerManager* CefExtensionSystem::service_worker_manager() {
412   return service_worker_manager_.get();
413 }
414 
user_script_manager()415 UserScriptManager* CefExtensionSystem::user_script_manager() {
416   return nullptr;
417 }
418 
state_store()419 StateStore* CefExtensionSystem::state_store() {
420   return state_store_.get();
421 }
422 
rules_store()423 StateStore* CefExtensionSystem::rules_store() {
424   return rules_store_.get();
425 }
426 
dynamic_user_scripts_store()427 StateStore* CefExtensionSystem::dynamic_user_scripts_store() {
428   return nullptr;
429 }
430 
431 scoped_refptr<value_store::ValueStoreFactory>
store_factory()432 CefExtensionSystem::store_factory() {
433   return store_factory_;
434 }
435 
info_map()436 InfoMap* CefExtensionSystem::info_map() {
437   if (!info_map_.get())
438     info_map_ = new InfoMap;
439   return info_map_.get();
440 }
441 
quota_service()442 QuotaService* CefExtensionSystem::quota_service() {
443   return quota_service_.get();
444 }
445 
app_sorting()446 AppSorting* CefExtensionSystem::app_sorting() {
447   return app_sorting_.get();
448 }
449 
450 // Implementation based on
451 // ExtensionSystemImpl::RegisterExtensionWithRequestContexts.
RegisterExtensionWithRequestContexts(const Extension * extension,base::OnceClosure callback)452 void CefExtensionSystem::RegisterExtensionWithRequestContexts(
453     const Extension* extension,
454     base::OnceClosure callback) {
455   // TODO(extensions): The |incognito_enabled| value should be set based on
456   // manifest settings.
457   content::GetIOThreadTaskRunner({})->PostTaskAndReply(
458       FROM_HERE,
459       base::BindOnce(&InfoMap::AddExtension, info_map(),
460                      base::RetainedRef(extension), base::Time::Now(),
461                      true,    // incognito_enabled
462                      false),  // notifications_disabled
463       std::move(callback));
464 }
465 
466 // Implementation based on
467 // ExtensionSystemImpl::UnregisterExtensionWithRequestContexts.
UnregisterExtensionWithRequestContexts(const std::string & extension_id,const UnloadedExtensionReason reason)468 void CefExtensionSystem::UnregisterExtensionWithRequestContexts(
469     const std::string& extension_id,
470     const UnloadedExtensionReason reason) {
471   content::GetIOThreadTaskRunner({})->PostTask(
472       FROM_HERE, base::BindOnce(&InfoMap::RemoveExtension, info_map(),
473                                 extension_id, reason));
474 }
475 
ready() const476 const base::OneShotEvent& CefExtensionSystem::ready() const {
477   return ready_;
478 }
479 
is_ready() const480 bool CefExtensionSystem::is_ready() const {
481   return ready_.is_signaled();
482 }
483 
content_verifier()484 ContentVerifier* CefExtensionSystem::content_verifier() {
485   return nullptr;
486 }
487 
GetDependentExtensions(const Extension * extension)488 std::unique_ptr<ExtensionSet> CefExtensionSystem::GetDependentExtensions(
489     const Extension* extension) {
490   return std::make_unique<ExtensionSet>();
491 }
492 
InstallUpdate(const std::string & extension_id,const std::string & public_key,const base::FilePath & temp_dir,bool install_immediately,InstallUpdateCallback install_update_callback)493 void CefExtensionSystem::InstallUpdate(
494     const std::string& extension_id,
495     const std::string& public_key,
496     const base::FilePath& temp_dir,
497     bool install_immediately,
498     InstallUpdateCallback install_update_callback) {
499   NOTREACHED();
500   base::DeletePathRecursively(temp_dir);
501 }
502 
PerformActionBasedOnOmahaAttributes(const std::string & extension_id,const base::Value & attributes)503 void CefExtensionSystem::PerformActionBasedOnOmahaAttributes(
504     const std::string& extension_id,
505     const base::Value& attributes) {
506   NOTREACHED();
507 }
508 
FinishDelayedInstallationIfReady(const std::string & extension_id,bool install_immediately)509 bool CefExtensionSystem::FinishDelayedInstallationIfReady(
510     const std::string& extension_id,
511     bool install_immediately) {
512   NOTREACHED();
513   return false;
514 }
515 
ComponentExtensionInfo(const base::DictionaryValue * manifest,const base::FilePath & directory,bool internal)516 CefExtensionSystem::ComponentExtensionInfo::ComponentExtensionInfo(
517     const base::DictionaryValue* manifest,
518     const base::FilePath& directory,
519     bool internal)
520     : manifest(manifest), root_directory(directory), internal(internal) {
521   if (!root_directory.IsAbsolute()) {
522     // This path structure is required by
523     // url_request_util::MaybeCreateURLRequestResourceBundleJob.
524     CHECK(base::PathService::Get(chrome::DIR_RESOURCES, &root_directory));
525     root_directory = root_directory.Append(directory);
526   }
527 }
528 
InitPrefs()529 void CefExtensionSystem::InitPrefs() {
530   store_factory_ =
531       new value_store::CefValueStoreFactory(browser_context_->GetPath());
532 
533   Profile* profile = Profile::FromBrowserContext(browser_context_);
534 
535   // Two state stores. The latter, which contains declarative rules, must be
536   // loaded immediately so that the rules are ready before we issue network
537   // requests.
538   state_store_ = std::make_unique<StateStore>(
539       profile, store_factory_, StateStore::BackendType::STATE, true);
540 
541   rules_store_ = std::make_unique<StateStore>(
542       profile, store_factory_, StateStore::BackendType::RULES, false);
543 }
544 
545 // Implementation based on ComponentLoader::CreateExtension.
CreateExtension(const ComponentExtensionInfo & info,std::string * utf8_error)546 scoped_refptr<const Extension> CefExtensionSystem::CreateExtension(
547     const ComponentExtensionInfo& info,
548     std::string* utf8_error) {
549   // TODO(abarth): We should REQUIRE_MODERN_MANIFEST_VERSION once we've updated
550   //               our component extensions to the new manifest version.
551   int flags = 0;
552   if (info.internal) {
553     // Internal extensions must have kPublicKey in the manifest.
554     flags |= Extension::REQUIRE_KEY;
555   }
556   return Extension::Create(
557       info.root_directory,
558       // Tests should continue to use the Manifest::COMMAND_LINE value here
559       // Some Chrome APIs will cause undesired effects if this is incorrect
560       // e.g.: alarms API has 1 minute minimum applied to Packed Extensions
561       info.internal ? mojom::ManifestLocation::kComponent
562                     : mojom::ManifestLocation::kCommandLine,
563       *info.manifest, flags, utf8_error);
564 }
565 
566 // Implementation based on ComponentLoader::Load and
567 // ExtensionService::AddExtension.
LoadExtension(const ComponentExtensionInfo & info,CefRefPtr<CefRequestContext> loader_context,CefRefPtr<CefExtensionHandler> handler)568 const Extension* CefExtensionSystem::LoadExtension(
569     const ComponentExtensionInfo& info,
570     CefRefPtr<CefRequestContext> loader_context,
571     CefRefPtr<CefExtensionHandler> handler) {
572   std::string error;
573   scoped_refptr<const Extension> extension(CreateExtension(info, &error));
574   if (!extension.get()) {
575     LOG(ERROR) << error;
576     return nullptr;
577   }
578 
579   if (registry_->GetInstalledExtension(extension->id())) {
580     LOG(ERROR) << "Extension with id " << extension->id()
581                << "is already installed";
582     return nullptr;
583   }
584 
585   CefRefPtr<CefExtensionImpl> cef_extension =
586       new CefExtensionImpl(extension.get(), loader_context.get(), handler);
587 
588   // Insert first so that callbacks can retrieve the loaded extension.
589   extension_map_.insert(std::make_pair(extension->id(), cef_extension));
590 
591   // This may trigger additional callbacks.
592   registry_->AddEnabled(extension.get());
593   NotifyExtensionLoaded(extension.get());
594 
595   cef_extension->OnExtensionLoaded();
596 
597   return extension.get();
598 }
599 
600 // Implementation based on ExtensionService::UnloadExtension.
UnloadExtension(const std::string & extension_id,UnloadedExtensionReason reason)601 void CefExtensionSystem::UnloadExtension(const std::string& extension_id,
602                                          UnloadedExtensionReason reason) {
603   // Make sure the extension gets deleted after we return from this function.
604   int include_mask =
605       ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::TERMINATED;
606   scoped_refptr<const Extension> extension(
607       registry_->GetExtensionById(extension_id, include_mask));
608 
609   // This method can be called via PostTask, so the extension may have been
610   // unloaded by the time this runs.
611   if (!extension.get()) {
612     // In case the extension may have crashed/uninstalled. Allow the profile to
613     // clean up its RequestContexts.
614     UnregisterExtensionWithRequestContexts(extension_id, reason);
615     return;
616   }
617 
618   if (registry_->disabled_extensions().Contains(extension->id())) {
619     registry_->RemoveDisabled(extension->id());
620     // Make sure the profile cleans up its RequestContexts when an already
621     // disabled extension is unloaded (since they are also tracking the disabled
622     // extensions).
623     UnregisterExtensionWithRequestContexts(extension_id, reason);
624     // Don't send the unloaded notification. It was sent when the extension
625     // was disabled.
626   } else {
627     // Remove the extension from the enabled list.
628     registry_->RemoveEnabled(extension->id());
629     NotifyExtensionUnloaded(extension.get(), reason);
630   }
631 }
632 
633 // Implementation based on ExtensionService::NotifyExtensionLoaded.
NotifyExtensionLoaded(const Extension * extension)634 void CefExtensionSystem::NotifyExtensionLoaded(const Extension* extension) {
635   // The URLRequestContexts need to be first to know that the extension
636   // was loaded, otherwise a race can arise where a renderer that is created
637   // for the extension may try to load an extension URL with an extension id
638   // that the request context doesn't yet know about. The profile is responsible
639   // for ensuring its URLRequestContexts appropriately discover the loaded
640   // extension.
641   RegisterExtensionWithRequestContexts(
642       extension,
643       base::BindOnce(
644           &CefExtensionSystem::OnExtensionRegisteredWithRequestContexts,
645           weak_ptr_factory_.GetWeakPtr(), base::WrapRefCounted(extension)));
646 
647   // Tell renderers about the loaded extension.
648   renderer_helper_->OnExtensionLoaded(*extension);
649 
650   // Tell subsystems that use the ExtensionRegistryObserver::OnExtensionLoaded
651   // about the new extension.
652   //
653   // NOTE: It is important that this happen after notifying the renderers about
654   // the new extensions so that if we navigate to an extension URL in
655   // ExtensionRegistryObserver::OnExtensionLoaded the renderer is guaranteed to
656   // know about it.
657   registry_->TriggerOnLoaded(extension);
658 
659   // Register plugins included with the extension.
660   // Implementation based on PluginManager::OnExtensionLoaded.
661   const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
662   if (handler && !handler->handler_url().empty()) {
663     content::WebPluginInfo info;
664     info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN;
665     info.name = base::UTF8ToUTF16(extension->name());
666     info.path = base::FilePath::FromUTF8Unsafe(extension->url().spec());
667 
668     for (std::set<std::string>::const_iterator mime_type =
669              handler->mime_type_set().begin();
670          mime_type != handler->mime_type_set().end(); ++mime_type) {
671       content::WebPluginMimeType mime_type_info;
672       mime_type_info.mime_type = *mime_type;
673       base::FilePath::StringType file_extension;
674       if (net::GetPreferredExtensionForMimeType(*mime_type, &file_extension)) {
675         mime_type_info.file_extensions.push_back(
676             base::FilePath(file_extension).AsUTF8Unsafe());
677       }
678       info.mime_types.push_back(mime_type_info);
679     }
680     content::PluginService* plugin_service =
681         content::PluginService::GetInstance();
682     plugin_service->RefreshPlugins();
683     plugin_service->RegisterInternalPlugin(info, true);
684   }
685 }
686 
OnExtensionRegisteredWithRequestContexts(scoped_refptr<const extensions::Extension> extension)687 void CefExtensionSystem::OnExtensionRegisteredWithRequestContexts(
688     scoped_refptr<const extensions::Extension> extension) {
689   registry_->AddReady(extension);
690   if (registry_->enabled_extensions().Contains(extension->id()))
691     registry_->TriggerOnReady(extension.get());
692 }
693 
694 // Implementation based on ExtensionService::NotifyExtensionUnloaded.
NotifyExtensionUnloaded(const Extension * extension,UnloadedExtensionReason reason)695 void CefExtensionSystem::NotifyExtensionUnloaded(
696     const Extension* extension,
697     UnloadedExtensionReason reason) {
698   // Unregister plugins included with the extension.
699   // Implementation based on PluginManager::OnExtensionUnloaded.
700   const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
701   if (handler && !handler->handler_url().empty()) {
702     base::FilePath path =
703         base::FilePath::FromUTF8Unsafe(extension->url().spec());
704     content::PluginService* plugin_service =
705         content::PluginService::GetInstance();
706     plugin_service->UnregisterInternalPlugin(path);
707     plugin_service->RefreshPlugins();
708   }
709 
710   registry_->TriggerOnUnloaded(extension, reason);
711 
712   // Tell renderers about the unloaded extension.
713   renderer_helper_->OnExtensionUnloaded(*extension);
714 
715   UnregisterExtensionWithRequestContexts(extension->id(), reason);
716 }
717 
718 }  // namespace extensions
719