mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			708 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			708 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2015 The Chromium Embedded Framework Authors.
 | 
						|
// Portions copyright 2014 The Chromium Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style license that can be
 | 
						|
// found in the LICENSE file.
 | 
						|
 | 
						|
#include "libcef/browser/extensions/extension_system.h"
 | 
						|
 | 
						|
#include <string>
 | 
						|
 | 
						|
#include "libcef/browser/extension_impl.h"
 | 
						|
#include "libcef/browser/extensions/pdf_extension_util.h"
 | 
						|
#include "libcef/browser/extensions/value_store/cef_value_store_factory.h"
 | 
						|
#include "libcef/browser/thread_util.h"
 | 
						|
#include "libcef/common/extensions/extensions_util.h"
 | 
						|
 | 
						|
#include "base/command_line.h"
 | 
						|
#include "base/files/file_path.h"
 | 
						|
#include "base/files/file_util.h"
 | 
						|
#include "base/json/json_string_value_serializer.h"
 | 
						|
#include "base/path_service.h"
 | 
						|
#include "base/strings/string_tokenizer.h"
 | 
						|
#include "base/strings/utf_string_conversions.h"
 | 
						|
#include "base/task/post_task.h"
 | 
						|
#include "base/threading/thread_restrictions.h"
 | 
						|
#include "chrome/browser/profiles/profile.h"
 | 
						|
#include "chrome/common/chrome_paths.h"
 | 
						|
#include "components/crx_file/id_util.h"
 | 
						|
#include "content/public/browser/browser_context.h"
 | 
						|
#include "content/public/browser/browser_task_traits.h"
 | 
						|
#include "content/public/browser/browser_thread.h"
 | 
						|
#include "content/public/browser/notification_details.h"
 | 
						|
#include "content/public/browser/notification_service.h"
 | 
						|
#include "content/public/browser/notification_source.h"
 | 
						|
#include "content/public/browser/plugin_service.h"
 | 
						|
#include "content/public/browser/render_process_host.h"
 | 
						|
#include "extensions/browser/api/app_runtime/app_runtime_api.h"
 | 
						|
#include "extensions/browser/extension_prefs.h"
 | 
						|
#include "extensions/browser/extension_registry.h"
 | 
						|
#include "extensions/browser/info_map.h"
 | 
						|
#include "extensions/browser/notification_types.h"
 | 
						|
#include "extensions/browser/null_app_sorting.h"
 | 
						|
#include "extensions/browser/quota_service.h"
 | 
						|
#include "extensions/browser/renderer_startup_helper.h"
 | 
						|
#include "extensions/browser/runtime_data.h"
 | 
						|
#include "extensions/browser/service_worker_manager.h"
 | 
						|
#include "extensions/browser/state_store.h"
 | 
						|
#include "extensions/browser/unloaded_extension_reason.h"
 | 
						|
#include "extensions/common/constants.h"
 | 
						|
#include "extensions/common/extension_messages.h"
 | 
						|
#include "extensions/common/file_util.h"
 | 
						|
#include "extensions/common/manifest_constants.h"
 | 
						|
#include "extensions/common/manifest_handlers/mime_types_handler.h"
 | 
						|
#include "extensions/common/switches.h"
 | 
						|
#include "net/base/mime_util.h"
 | 
						|
 | 
						|
using content::BrowserContext;
 | 
						|
using content::BrowserThread;
 | 
						|
 | 
						|
namespace extensions {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
// Implementation based on ComponentLoader::ParseManifest.
 | 
						|
std::unique_ptr<base::DictionaryValue> ParseManifest(
 | 
						|
    const std::string& manifest_contents) {
 | 
						|
  JSONStringValueDeserializer deserializer(manifest_contents);
 | 
						|
  std::unique_ptr<base::Value> manifest(
 | 
						|
      deserializer.Deserialize(nullptr, nullptr));
 | 
						|
 | 
						|
  if (!manifest.get() || !manifest->is_dict()) {
 | 
						|
    LOG(ERROR) << "Failed to parse extension manifest.";
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  // Transfer ownership to the caller.
 | 
						|
  return base::WrapUnique(
 | 
						|
      static_cast<base::DictionaryValue*>(manifest.release()));
 | 
						|
}
 | 
						|
 | 
						|
void ExecuteLoadFailure(CefRefPtr<CefExtensionHandler> handler,
 | 
						|
                        cef_errorcode_t result) {
 | 
						|
  if (!handler)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (!CEF_CURRENTLY_ON_UIT()) {
 | 
						|
    CEF_POST_TASK(CEF_UIT, base::BindOnce(ExecuteLoadFailure, handler, result));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  handler->OnExtensionLoadFailed(result);
 | 
						|
}
 | 
						|
 | 
						|
void 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) {
 | 
						|
  if (!CEF_CURRENTLY_ON_UIT()) {
 | 
						|
    CEF_POST_TASK(CEF_UIT, base::BindOnce(LoadExtensionOnUIThread, context,
 | 
						|
                                          std::move(manifest), root_directory,
 | 
						|
                                          internal, loader_context, handler));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (context) {
 | 
						|
    context->LoadExtension(std::move(manifest), root_directory, internal,
 | 
						|
                           loader_context, handler);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void LoadExtensionWithManifest(base::WeakPtr<CefExtensionSystem> context,
 | 
						|
                               const std::string& manifest_contents,
 | 
						|
                               const base::FilePath& root_directory,
 | 
						|
                               bool internal,
 | 
						|
                               CefRefPtr<CefRequestContext> loader_context,
 | 
						|
                               CefRefPtr<CefExtensionHandler> handler) {
 | 
						|
  CEF_REQUIRE_BLOCKING();
 | 
						|
 | 
						|
  std::unique_ptr<base::DictionaryValue> manifest =
 | 
						|
      ParseManifest(manifest_contents);
 | 
						|
  if (!manifest) {
 | 
						|
    LOG(WARNING) << "Failed to parse extension manifest";
 | 
						|
    ExecuteLoadFailure(handler, ERR_INVALID_ARGUMENT);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  LoadExtensionOnUIThread(context, std::move(manifest), root_directory,
 | 
						|
                          internal, loader_context, handler);
 | 
						|
}
 | 
						|
 | 
						|
void LoadExtensionFromDisk(base::WeakPtr<CefExtensionSystem> context,
 | 
						|
                           const base::FilePath& root_directory,
 | 
						|
                           bool internal,
 | 
						|
                           CefRefPtr<CefRequestContext> loader_context,
 | 
						|
                           CefRefPtr<CefExtensionHandler> handler) {
 | 
						|
  CEF_REQUIRE_BLOCKING();
 | 
						|
 | 
						|
  base::FilePath manifest_path = root_directory.AppendASCII("manifest.json");
 | 
						|
  std::string manifest_contents;
 | 
						|
  if (!base::ReadFileToString(manifest_path, &manifest_contents)) {
 | 
						|
    LOG(WARNING) << "Failed to read extension manifest from "
 | 
						|
                 << manifest_path.MaybeAsASCII();
 | 
						|
    ExecuteLoadFailure(handler, ERR_FILE_NOT_FOUND);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  LoadExtensionWithManifest(context, manifest_contents, root_directory,
 | 
						|
                            internal, loader_context, handler);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
CefExtensionSystem::CefExtensionSystem(BrowserContext* browser_context)
 | 
						|
    : browser_context_(browser_context),
 | 
						|
      initialized_(false),
 | 
						|
      registry_(ExtensionRegistry::Get(browser_context)),
 | 
						|
      renderer_helper_(
 | 
						|
          extensions::RendererStartupHelperFactory::GetForBrowserContext(
 | 
						|
              browser_context)),
 | 
						|
      weak_ptr_factory_(this) {
 | 
						|
  InitPrefs();
 | 
						|
}
 | 
						|
 | 
						|
CefExtensionSystem::~CefExtensionSystem() {}
 | 
						|
 | 
						|
void CefExtensionSystem::Init() {
 | 
						|
  DCHECK(!initialized_);
 | 
						|
 | 
						|
  // There's complexity here related to the ordering of message delivery. For
 | 
						|
  // an extension to load correctly both the ExtensionMsg_Loaded and
 | 
						|
  // ExtensionMsg_ActivateExtension messages must be sent. These messages are
 | 
						|
  // currently sent by RendererStartupHelper, ExtensionWebContentsObserver, and
 | 
						|
  // this class. ExtensionMsg_Loaded is handled by Dispatcher::OnLoaded and adds
 | 
						|
  // the extension to |extensions_|. ExtensionMsg_ActivateExtension is handled
 | 
						|
  // by Dispatcher::OnActivateExtension and adds the extension to
 | 
						|
  // |active_extension_ids_|. If these messages are not sent correctly then
 | 
						|
  // ScriptContextSet::Register called from Dispatcher::DidCreateScriptContext
 | 
						|
  // will classify the extension incorrectly and API bindings will not be added.
 | 
						|
 | 
						|
  // Inform the rest of the extensions system to start.
 | 
						|
  ready_.Signal();
 | 
						|
  content::NotificationService::current()->Notify(
 | 
						|
      NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
 | 
						|
      content::Source<BrowserContext>(browser_context_),
 | 
						|
      content::NotificationService::NoDetails());
 | 
						|
 | 
						|
  // Add the internal PDF extension. PDF loading works as follows:
 | 
						|
  // 1. The PDF PPAPI plugin is registered in libcef/common/content_client.cc
 | 
						|
  //    ComputeBuiltInPlugins to handle the kPDFPluginOutOfProcessMimeType.
 | 
						|
  // 2. The PDF extension is registered by the below call to AddExtension and
 | 
						|
  //    associated with the "application/pdf" mime type.
 | 
						|
  // 3. Web content running in the owner CefBrowser requests to load a PDF file
 | 
						|
  //    resource with the "application/pdf" mime type. This can be via a frame
 | 
						|
  //    (main frame/iframe) or object/embed tag.
 | 
						|
  // 4. PluginResponseInterceptorURLLoaderThrottle intercepts the PDF resource
 | 
						|
  //    load in the browser process and registers the PDF resource as a stream
 | 
						|
  //    via MimeHandlerStreamManager::AddStream.
 | 
						|
  // 5. PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse triggers
 | 
						|
  //    creation of a MimeHandlerViewEmbedder in the browser process via
 | 
						|
  //    MimeHandlerViewAttachHelper::OverrideBodyForInterceptedResponse.
 | 
						|
  // 6. MimeHandlerViewEmbedder::ReadyToCommitNavigation is called and sends a
 | 
						|
  //    Mojo message to MimeHandlerViewContainerManager::SetInternalId in the
 | 
						|
  //    owner renderer process.
 | 
						|
  // 7. The MimeHandlerViewContainerManager is created in the owner renderer
 | 
						|
  //    process via MimeHandlerViewContainerManager::BindReceiver and the
 | 
						|
  //    SetInternalId call arrives.
 | 
						|
  // 8. HTMLPlugInElement::RequestObject is called in the owner renderer process
 | 
						|
  //    to handle the PDF file frame/object/embed tag. This results in calls to
 | 
						|
  //    ContentBrowserClient::GetPluginMimeTypesWithExternalHandlers (browser
 | 
						|
  //    process) and ContentRendererClient::IsPluginHandledExternally (owner
 | 
						|
  //    renderer process), and determines that the plugin should be handled
 | 
						|
  //    externally (handled_externally=true).
 | 
						|
  // 9. MimeHandlerViewContainerManager::IsManagedByContainerManager sends a
 | 
						|
  //    Mojo message to MimeHandlerViewEmbedder::ReadyToCreateMimeHandlerView
 | 
						|
  //    in the browser process.
 | 
						|
  // 10.MimeHandlerViewEmbedder::RenderFrameCreated triggers creation of a
 | 
						|
  //    MimeHandlerViewGuest and CefMimeHandlerViewGuestDelegate in the browser
 | 
						|
  //    process.
 | 
						|
  // 11.MimeHandlerViewGuest::CreateWebContents creates a new guest WebContents
 | 
						|
  //    (is_guest_view=true) to host the PDF extension and the PDF resource
 | 
						|
  //    stream is retrieved via MimeHandlerStreamManager::ReleaseStream.
 | 
						|
  // 12.MimeHandlerViewGuest::DidAttachToEmbedder calls
 | 
						|
  //    CefMimeHandlerViewGuestDelegate::OnGuestAttached to associate the guest
 | 
						|
  //    WebContents routing IDs with the owner CefBrowser. MimeHandlerViewGuest
 | 
						|
  //    then loads the extension URL (index.html) in the guest WebContents.
 | 
						|
  // 13.Creation of the RenderFrame in the guest renderer process triggers a
 | 
						|
  //    sync IPC call from CefContentRendererClient::MaybeCreateBrowser to
 | 
						|
  //    CefBrowserInfoManager::GetBrowserInfo in the browser process to retrieve
 | 
						|
  //    the CefBrowser information, which will be immediately available due to
 | 
						|
  //    step 12.
 | 
						|
  // 14.The PDF extension begins to load. Extension resource requests are
 | 
						|
  //    handled via ExtensionURLLoaderFactory::CreateLoaderAndStart in the
 | 
						|
  //    browser process. Access to PDF extension resources is checked by
 | 
						|
  //    CefExtensionsBrowserClient::AllowCrossRendererResourceLoad and
 | 
						|
  //    PDF extension resources are provided from bundle via
 | 
						|
  //    CefExtensionsBrowserClient::LoadResourceFromResourceBundle
 | 
						|
  //    and CefComponentExtensionResourceManager. Access to chrome://resources
 | 
						|
  //    is granted via CefExtensionWebContentsObserver::RenderViewCreated.
 | 
						|
  // 15.The PDF extension calls chrome.mimeHandlerPrivate.getStreamInfo
 | 
						|
  //    (chrome/browser/resources/pdf/browser_api.js) to retrieve the PDF
 | 
						|
  //    resource stream. This API is implemented using Mojo as described in
 | 
						|
  //    libcef/common/extensions/api/README.txt.
 | 
						|
  // 16.The PDF extension requests the PDF PPAPI plugin to handle
 | 
						|
  //    kPDFPluginOutOfProcessMimeType. Approval arrives in the guest renderer
 | 
						|
  //    process via ExtensionFrameHelper::OnExtensionResponse which calls
 | 
						|
  //    NativeExtensionBindingsSystem::HandleResponse. This triggers creation of
 | 
						|
  //    an HTMLPlugInElement via native V8 bindings to host the PDF plugin.
 | 
						|
  // 17.HTMLPlugInElement::RequestObject is called in the guest renderer process
 | 
						|
  //    and determines that the PDF PPAPI plugin should be handled internally
 | 
						|
  //    (handled_externally=false). A PluginDocument is created and
 | 
						|
  //    CefContentRendererClient::OverrideCreatePlugin is called to create a
 | 
						|
  //    WebPlugin.
 | 
						|
  // 18.The PDF extension and PDF plugin are now loaded. Print commands, if
 | 
						|
  //    any, are handled in the guest renderer process by ChromePDFPrintClient
 | 
						|
  //    and CefPrintRenderFrameHelperDelegate.
 | 
						|
  // 19.When navigating away from the PDF file or closing the owner CefBrowser
 | 
						|
  //    the guest WebContents will be destroyed. This triggers a call to
 | 
						|
  //    CefMimeHandlerViewGuestDelegate::OnGuestDetached which removes the
 | 
						|
  //    routing ID association with the owner CefBrowser.
 | 
						|
  if (PdfExtensionEnabled()) {
 | 
						|
    LoadExtension(ParseManifest(pdf_extension_util::GetManifest()),
 | 
						|
                  base::FilePath(FILE_PATH_LITERAL("pdf")), true /* internal */,
 | 
						|
                  nullptr, nullptr);
 | 
						|
  }
 | 
						|
 | 
						|
  initialized_ = true;
 | 
						|
}
 | 
						|
 | 
						|
void CefExtensionSystem::LoadExtension(
 | 
						|
    const base::FilePath& root_directory,
 | 
						|
    bool internal,
 | 
						|
    CefRefPtr<CefRequestContext> loader_context,
 | 
						|
    CefRefPtr<CefExtensionHandler> handler) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
  CEF_POST_USER_VISIBLE_TASK(
 | 
						|
      base::BindOnce(LoadExtensionFromDisk, weak_ptr_factory_.GetWeakPtr(),
 | 
						|
                     root_directory, internal, loader_context, handler));
 | 
						|
}
 | 
						|
 | 
						|
void CefExtensionSystem::LoadExtension(
 | 
						|
    const std::string& manifest_contents,
 | 
						|
    const base::FilePath& root_directory,
 | 
						|
    bool internal,
 | 
						|
    CefRefPtr<CefRequestContext> loader_context,
 | 
						|
    CefRefPtr<CefExtensionHandler> handler) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
  CEF_POST_USER_VISIBLE_TASK(base::BindOnce(
 | 
						|
      LoadExtensionWithManifest, weak_ptr_factory_.GetWeakPtr(),
 | 
						|
      manifest_contents, root_directory, internal, loader_context, handler));
 | 
						|
}
 | 
						|
 | 
						|
// Implementation based on ComponentLoader::Add.
 | 
						|
void CefExtensionSystem::LoadExtension(
 | 
						|
    std::unique_ptr<base::DictionaryValue> manifest,
 | 
						|
    const base::FilePath& root_directory,
 | 
						|
    bool internal,
 | 
						|
    CefRefPtr<CefRequestContext> loader_context,
 | 
						|
    CefRefPtr<CefExtensionHandler> handler) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
 | 
						|
// Internal extensions don't have a loader context. External extensions should.
 | 
						|
#if DCHECK_IS_ON()
 | 
						|
  if (internal) {
 | 
						|
    DCHECK(!loader_context);
 | 
						|
  } else {
 | 
						|
    DCHECK(loader_context);
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  ComponentExtensionInfo info(manifest.get(), root_directory, internal);
 | 
						|
  const Extension* extension = LoadExtension(info, loader_context, handler);
 | 
						|
  if (!extension)
 | 
						|
    ExecuteLoadFailure(handler, ERR_FAILED);
 | 
						|
}
 | 
						|
 | 
						|
// Implementation based on ExtensionService::RemoveComponentExtension.
 | 
						|
bool CefExtensionSystem::UnloadExtension(const std::string& extension_id) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
  ExtensionMap::iterator it = extension_map_.find(extension_id);
 | 
						|
  if (it == extension_map_.end()) {
 | 
						|
    // No CEF representation so we've already unloaded it.
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  CefRefPtr<CefExtensionImpl> cef_extension =
 | 
						|
      static_cast<CefExtensionImpl*>(it->second.get());
 | 
						|
 | 
						|
  // Erase first so that callbacks can't retrieve the unloaded extension.
 | 
						|
  extension_map_.erase(it);
 | 
						|
 | 
						|
  cef_extension->OnExtensionUnloaded();
 | 
						|
 | 
						|
  scoped_refptr<const Extension> extension(
 | 
						|
      registry_->GetInstalledExtension(extension_id));
 | 
						|
  UnloadExtension(extension_id, UnloadedExtensionReason::UNINSTALL);
 | 
						|
  if (extension.get()) {
 | 
						|
    registry_->TriggerOnUninstalled(
 | 
						|
        extension.get(), extensions::UNINSTALL_REASON_COMPONENT_REMOVED);
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool CefExtensionSystem::HasExtension(const std::string& extension_id) const {
 | 
						|
  return !!GetExtension(extension_id);
 | 
						|
}
 | 
						|
 | 
						|
CefRefPtr<CefExtension> CefExtensionSystem::GetExtension(
 | 
						|
    const std::string& extension_id) const {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
  ExtensionMap::const_iterator it = extension_map_.find(extension_id);
 | 
						|
  if (it != extension_map_.end())
 | 
						|
    return it->second;
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
CefExtensionSystem::ExtensionMap CefExtensionSystem::GetExtensions() const {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
  return extension_map_;
 | 
						|
}
 | 
						|
 | 
						|
void CefExtensionSystem::OnRequestContextDeleted(CefRequestContext* context) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
  DCHECK(context);
 | 
						|
 | 
						|
  // Make a copy of the map because UnloadExtension will modify it.
 | 
						|
  // Don't add any references to |context|.
 | 
						|
  ExtensionMap map = extension_map_;
 | 
						|
  ExtensionMap::const_iterator it = map.begin();
 | 
						|
  for (; it != map.end(); ++it) {
 | 
						|
    CefRefPtr<CefExtensionImpl> cef_extension =
 | 
						|
        static_cast<CefExtensionImpl*>(it->second.get());
 | 
						|
    if (cef_extension->loader_context() == context)
 | 
						|
      UnloadExtension(it->first);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void CefExtensionSystem::Shutdown() {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
// Only internal extensions should exist at this point.
 | 
						|
#if DCHECK_IS_ON()
 | 
						|
  ExtensionMap::iterator it = extension_map_.begin();
 | 
						|
  for (; it != extension_map_.end(); ++it) {
 | 
						|
    CefRefPtr<CefExtensionImpl> cef_extension =
 | 
						|
        static_cast<CefExtensionImpl*>(it->second.get());
 | 
						|
    DCHECK(!cef_extension->loader_context());
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  extension_map_.clear();
 | 
						|
}
 | 
						|
 | 
						|
void CefExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
 | 
						|
  DCHECK(!initialized_);
 | 
						|
  service_worker_manager_.reset(new ServiceWorkerManager(browser_context_));
 | 
						|
  runtime_data_.reset(new RuntimeData(registry_));
 | 
						|
  quota_service_.reset(new QuotaService);
 | 
						|
  app_sorting_.reset(new NullAppSorting);
 | 
						|
}
 | 
						|
 | 
						|
ExtensionService* CefExtensionSystem::extension_service() {
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
RuntimeData* CefExtensionSystem::runtime_data() {
 | 
						|
  return runtime_data_.get();
 | 
						|
}
 | 
						|
 | 
						|
ManagementPolicy* CefExtensionSystem::management_policy() {
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
ServiceWorkerManager* CefExtensionSystem::service_worker_manager() {
 | 
						|
  return service_worker_manager_.get();
 | 
						|
}
 | 
						|
 | 
						|
SharedUserScriptMaster* CefExtensionSystem::shared_user_script_master() {
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
StateStore* CefExtensionSystem::state_store() {
 | 
						|
  return state_store_.get();
 | 
						|
}
 | 
						|
 | 
						|
StateStore* CefExtensionSystem::rules_store() {
 | 
						|
  return rules_store_.get();
 | 
						|
}
 | 
						|
 | 
						|
scoped_refptr<ValueStoreFactory> CefExtensionSystem::store_factory() {
 | 
						|
  return store_factory_;
 | 
						|
}
 | 
						|
 | 
						|
InfoMap* CefExtensionSystem::info_map() {
 | 
						|
  if (!info_map_.get())
 | 
						|
    info_map_ = new InfoMap;
 | 
						|
  return info_map_.get();
 | 
						|
}
 | 
						|
 | 
						|
QuotaService* CefExtensionSystem::quota_service() {
 | 
						|
  return quota_service_.get();
 | 
						|
}
 | 
						|
 | 
						|
AppSorting* CefExtensionSystem::app_sorting() {
 | 
						|
  return app_sorting_.get();
 | 
						|
}
 | 
						|
 | 
						|
// Implementation based on
 | 
						|
// ExtensionSystemImpl::RegisterExtensionWithRequestContexts.
 | 
						|
void CefExtensionSystem::RegisterExtensionWithRequestContexts(
 | 
						|
    const Extension* extension,
 | 
						|
    base::OnceClosure callback) {
 | 
						|
  // TODO(extensions): The |incognito_enabled| value should be set based on
 | 
						|
  // manifest settings.
 | 
						|
  base::PostTaskAndReply(
 | 
						|
      FROM_HERE, {BrowserThread::IO},
 | 
						|
      base::Bind(&InfoMap::AddExtension, info_map(),
 | 
						|
                 base::RetainedRef(extension), base::Time::Now(),
 | 
						|
                 true,    // incognito_enabled
 | 
						|
                 false),  // notifications_disabled
 | 
						|
      std::move(callback));
 | 
						|
}
 | 
						|
 | 
						|
// Implementation based on
 | 
						|
// ExtensionSystemImpl::UnregisterExtensionWithRequestContexts.
 | 
						|
void CefExtensionSystem::UnregisterExtensionWithRequestContexts(
 | 
						|
    const std::string& extension_id,
 | 
						|
    const UnloadedExtensionReason reason) {
 | 
						|
  base::PostTask(
 | 
						|
      FROM_HERE, {BrowserThread::IO},
 | 
						|
      base::Bind(&InfoMap::RemoveExtension, info_map(), extension_id, reason));
 | 
						|
}
 | 
						|
 | 
						|
const base::OneShotEvent& CefExtensionSystem::ready() const {
 | 
						|
  return ready_;
 | 
						|
}
 | 
						|
 | 
						|
ContentVerifier* CefExtensionSystem::content_verifier() {
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
std::unique_ptr<ExtensionSet> CefExtensionSystem::GetDependentExtensions(
 | 
						|
    const Extension* extension) {
 | 
						|
  return std::make_unique<ExtensionSet>();
 | 
						|
}
 | 
						|
 | 
						|
void CefExtensionSystem::InstallUpdate(
 | 
						|
    const std::string& extension_id,
 | 
						|
    const std::string& public_key,
 | 
						|
    const base::FilePath& temp_dir,
 | 
						|
    bool install_immediately,
 | 
						|
    InstallUpdateCallback install_update_callback) {
 | 
						|
  NOTREACHED();
 | 
						|
  base::DeleteFile(temp_dir, true /* recursive */);
 | 
						|
}
 | 
						|
 | 
						|
bool CefExtensionSystem::FinishDelayedInstallationIfReady(
 | 
						|
    const std::string& extension_id,
 | 
						|
    bool install_immediately) {
 | 
						|
  NOTREACHED();
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
CefExtensionSystem::ComponentExtensionInfo::ComponentExtensionInfo(
 | 
						|
    const base::DictionaryValue* manifest,
 | 
						|
    const base::FilePath& directory,
 | 
						|
    bool internal)
 | 
						|
    : manifest(manifest), root_directory(directory), internal(internal) {
 | 
						|
  if (!root_directory.IsAbsolute()) {
 | 
						|
    // This path structure is required by
 | 
						|
    // url_request_util::MaybeCreateURLRequestResourceBundleJob.
 | 
						|
    CHECK(base::PathService::Get(chrome::DIR_RESOURCES, &root_directory));
 | 
						|
    root_directory = root_directory.Append(directory);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void CefExtensionSystem::InitPrefs() {
 | 
						|
  store_factory_ = new CefValueStoreFactory(browser_context_->GetPath());
 | 
						|
 | 
						|
  Profile* profile = Profile::FromBrowserContext(browser_context_);
 | 
						|
 | 
						|
  // Two state stores. The latter, which contains declarative rules, must be
 | 
						|
  // loaded immediately so that the rules are ready before we issue network
 | 
						|
  // requests.
 | 
						|
  state_store_.reset(new StateStore(
 | 
						|
      profile, store_factory_, ValueStoreFrontend::BackendType::STATE, true));
 | 
						|
 | 
						|
  rules_store_.reset(new StateStore(
 | 
						|
      profile, store_factory_, ValueStoreFrontend::BackendType::RULES, false));
 | 
						|
}
 | 
						|
 | 
						|
// Implementation based on ComponentLoader::CreateExtension.
 | 
						|
scoped_refptr<const Extension> CefExtensionSystem::CreateExtension(
 | 
						|
    const ComponentExtensionInfo& info,
 | 
						|
    std::string* utf8_error) {
 | 
						|
  // TODO(abarth): We should REQUIRE_MODERN_MANIFEST_VERSION once we've updated
 | 
						|
  //               our component extensions to the new manifest version.
 | 
						|
  int flags = 0;
 | 
						|
  if (info.internal) {
 | 
						|
    // Internal extensions must have kPublicKey in the manifest.
 | 
						|
    flags |= Extension::REQUIRE_KEY;
 | 
						|
  }
 | 
						|
  return Extension::Create(
 | 
						|
      info.root_directory,
 | 
						|
      // Tests should continue to use the Manifest::COMMAND_LINE value here
 | 
						|
      // Some Chrome APIs will cause undesired effects if this is incorrect
 | 
						|
      // e.g.: alarms API has 1 minute minimum applied to Packed Extensions
 | 
						|
      info.internal ? Manifest::COMPONENT : Manifest::COMMAND_LINE,
 | 
						|
      *info.manifest, flags, utf8_error);
 | 
						|
}
 | 
						|
 | 
						|
// Implementation based on ComponentLoader::Load and
 | 
						|
// ExtensionService::AddExtension.
 | 
						|
const Extension* CefExtensionSystem::LoadExtension(
 | 
						|
    const ComponentExtensionInfo& info,
 | 
						|
    CefRefPtr<CefRequestContext> loader_context,
 | 
						|
    CefRefPtr<CefExtensionHandler> handler) {
 | 
						|
  std::string error;
 | 
						|
  scoped_refptr<const Extension> extension(CreateExtension(info, &error));
 | 
						|
  if (!extension.get()) {
 | 
						|
    LOG(ERROR) << error;
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (registry_->GetInstalledExtension(extension->id())) {
 | 
						|
    LOG(ERROR) << "Extension with id " << extension->id()
 | 
						|
               << "is already installed";
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  CefRefPtr<CefExtensionImpl> cef_extension =
 | 
						|
      new CefExtensionImpl(extension.get(), loader_context.get(), handler);
 | 
						|
 | 
						|
  // Insert first so that callbacks can retrieve the loaded extension.
 | 
						|
  extension_map_.insert(std::make_pair(extension->id(), cef_extension));
 | 
						|
 | 
						|
  cef_extension->OnExtensionLoaded();
 | 
						|
 | 
						|
  // This may trigger additional callbacks.
 | 
						|
  registry_->AddEnabled(extension.get());
 | 
						|
  NotifyExtensionLoaded(extension.get());
 | 
						|
 | 
						|
  return extension.get();
 | 
						|
}
 | 
						|
 | 
						|
// Implementation based on ExtensionService::UnloadExtension.
 | 
						|
void CefExtensionSystem::UnloadExtension(const std::string& extension_id,
 | 
						|
                                         UnloadedExtensionReason reason) {
 | 
						|
  // Make sure the extension gets deleted after we return from this function.
 | 
						|
  int include_mask =
 | 
						|
      ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::TERMINATED;
 | 
						|
  scoped_refptr<const Extension> extension(
 | 
						|
      registry_->GetExtensionById(extension_id, include_mask));
 | 
						|
 | 
						|
  // This method can be called via PostTask, so the extension may have been
 | 
						|
  // unloaded by the time this runs.
 | 
						|
  if (!extension.get()) {
 | 
						|
    // In case the extension may have crashed/uninstalled. Allow the profile to
 | 
						|
    // clean up its RequestContexts.
 | 
						|
    UnregisterExtensionWithRequestContexts(extension_id, reason);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (registry_->disabled_extensions().Contains(extension->id())) {
 | 
						|
    registry_->RemoveDisabled(extension->id());
 | 
						|
    // Make sure the profile cleans up its RequestContexts when an already
 | 
						|
    // disabled extension is unloaded (since they are also tracking the disabled
 | 
						|
    // extensions).
 | 
						|
    UnregisterExtensionWithRequestContexts(extension_id, reason);
 | 
						|
    // Don't send the unloaded notification. It was sent when the extension
 | 
						|
    // was disabled.
 | 
						|
  } else {
 | 
						|
    // Remove the extension from the enabled list.
 | 
						|
    registry_->RemoveEnabled(extension->id());
 | 
						|
    NotifyExtensionUnloaded(extension.get(), reason);
 | 
						|
  }
 | 
						|
 | 
						|
  content::NotificationService::current()->Notify(
 | 
						|
      extensions::NOTIFICATION_EXTENSION_REMOVED,
 | 
						|
      content::Source<content::BrowserContext>(browser_context_),
 | 
						|
      content::Details<const Extension>(extension.get()));
 | 
						|
}
 | 
						|
 | 
						|
// Implementation based on ExtensionService::NotifyExtensionLoaded.
 | 
						|
void CefExtensionSystem::NotifyExtensionLoaded(const Extension* extension) {
 | 
						|
  // The URLRequestContexts need to be first to know that the extension
 | 
						|
  // was loaded, otherwise a race can arise where a renderer that is created
 | 
						|
  // for the extension may try to load an extension URL with an extension id
 | 
						|
  // that the request context doesn't yet know about. The profile is responsible
 | 
						|
  // for ensuring its URLRequestContexts appropriately discover the loaded
 | 
						|
  // extension.
 | 
						|
  RegisterExtensionWithRequestContexts(
 | 
						|
      extension,
 | 
						|
      base::Bind(&CefExtensionSystem::OnExtensionRegisteredWithRequestContexts,
 | 
						|
                 weak_ptr_factory_.GetWeakPtr(),
 | 
						|
                 base::WrapRefCounted(extension)));
 | 
						|
 | 
						|
  // Tell renderers about the loaded extension.
 | 
						|
  renderer_helper_->OnExtensionLoaded(*extension);
 | 
						|
 | 
						|
  // Tell subsystems that use the ExtensionRegistryObserver::OnExtensionLoaded
 | 
						|
  // about the new extension.
 | 
						|
  //
 | 
						|
  // NOTE: It is important that this happen after notifying the renderers about
 | 
						|
  // the new extensions so that if we navigate to an extension URL in
 | 
						|
  // ExtensionRegistryObserver::OnExtensionLoaded the renderer is guaranteed to
 | 
						|
  // know about it.
 | 
						|
  registry_->TriggerOnLoaded(extension);
 | 
						|
 | 
						|
  // Register plugins included with the extension.
 | 
						|
  // Implementation based on PluginManager::OnExtensionLoaded.
 | 
						|
  const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
 | 
						|
  if (handler && !handler->handler_url().empty()) {
 | 
						|
    content::WebPluginInfo info;
 | 
						|
    info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN;
 | 
						|
    info.name = base::UTF8ToUTF16(extension->name());
 | 
						|
    info.path = base::FilePath::FromUTF8Unsafe(extension->url().spec());
 | 
						|
 | 
						|
    for (std::set<std::string>::const_iterator mime_type =
 | 
						|
             handler->mime_type_set().begin();
 | 
						|
         mime_type != handler->mime_type_set().end(); ++mime_type) {
 | 
						|
      content::WebPluginMimeType mime_type_info;
 | 
						|
      mime_type_info.mime_type = *mime_type;
 | 
						|
      base::FilePath::StringType file_extension;
 | 
						|
      if (net::GetPreferredExtensionForMimeType(*mime_type, &file_extension)) {
 | 
						|
        mime_type_info.file_extensions.push_back(
 | 
						|
            base::FilePath(file_extension).AsUTF8Unsafe());
 | 
						|
      }
 | 
						|
      info.mime_types.push_back(mime_type_info);
 | 
						|
    }
 | 
						|
    content::PluginService* plugin_service =
 | 
						|
        content::PluginService::GetInstance();
 | 
						|
    plugin_service->RefreshPlugins();
 | 
						|
    plugin_service->RegisterInternalPlugin(info, true);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void CefExtensionSystem::OnExtensionRegisteredWithRequestContexts(
 | 
						|
    scoped_refptr<const extensions::Extension> extension) {
 | 
						|
  registry_->AddReady(extension);
 | 
						|
  if (registry_->enabled_extensions().Contains(extension->id()))
 | 
						|
    registry_->TriggerOnReady(extension.get());
 | 
						|
}
 | 
						|
 | 
						|
// Implementation based on ExtensionService::NotifyExtensionUnloaded.
 | 
						|
void CefExtensionSystem::NotifyExtensionUnloaded(
 | 
						|
    const Extension* extension,
 | 
						|
    UnloadedExtensionReason reason) {
 | 
						|
  // Unregister plugins included with the extension.
 | 
						|
  // Implementation based on PluginManager::OnExtensionUnloaded.
 | 
						|
  const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
 | 
						|
  if (handler && !handler->handler_url().empty()) {
 | 
						|
    base::FilePath path =
 | 
						|
        base::FilePath::FromUTF8Unsafe(extension->url().spec());
 | 
						|
    content::PluginService* plugin_service =
 | 
						|
        content::PluginService::GetInstance();
 | 
						|
    plugin_service->UnregisterInternalPlugin(path);
 | 
						|
    plugin_service->RefreshPlugins();
 | 
						|
  }
 | 
						|
 | 
						|
  registry_->TriggerOnUnloaded(extension, reason);
 | 
						|
 | 
						|
  // Tell renderers about the unloaded extension.
 | 
						|
  renderer_helper_->OnExtensionUnloaded(*extension);
 | 
						|
 | 
						|
  UnregisterExtensionWithRequestContexts(extension->id(), reason);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace extensions
 |