Add NetworkService support for extensions and downloads (see issue #2622).

To test:
- All tests pass with NetworkService disabled. DownloadTest.*, ExtensionTest.*
  and PluginTest.* tests pass with NetworkService enabled.
- The PDF extension displays a file, and the download and print buttons work.
This commit is contained in:
Marshall Greenblatt 2019-05-01 15:09:59 -04:00
parent 0193a3fe54
commit 5ce52bd775
10 changed files with 181 additions and 37 deletions

View File

@ -18,6 +18,7 @@
#include "libcef/browser/context.h"
#include "libcef/browser/devtools/devtools_manager_delegate.h"
#include "libcef/browser/extensions/extension_system.h"
#include "libcef/browser/extensions/extension_web_contents_observer.h"
#include "libcef/browser/media_capture_devices_dispatcher.h"
#include "libcef/browser/net/chrome_scheme_handler.h"
#include "libcef/browser/net/net_util.h"
@ -58,11 +59,13 @@
#include "chrome/browser/chrome_service.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/plugins/plugin_info_host_impl.h"
#include "chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/constants.mojom.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/services/printing/public/mojom/constants.mojom.h"
@ -88,6 +91,7 @@
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_ui_url_loader_factory.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/service_names.mojom.h"
#include "content/public/common/storage_quota_params.h"
@ -1116,6 +1120,26 @@ CefContentBrowserClient::CreateThrottlesForNavigation(
return throttles;
}
std::vector<std::unique_ptr<content::URLLoaderThrottle>>
CefContentBrowserClient::CreateURLLoaderThrottles(
const network::ResourceRequest& request,
content::ResourceContext* resource_context,
const base::RepeatingCallback<content::WebContents*()>& wc_getter,
content::NavigationUIData* navigation_ui_data,
int frame_tree_node_id) {
CEF_REQUIRE_IOT();
std::vector<std::unique_ptr<content::URLLoaderThrottle>> result;
if (net_service::IsEnabled()) {
// Used to substitute View ID for PDF contents when using the PDF plugin.
result.push_back(
std::make_unique<PluginResponseInterceptorURLLoaderThrottle>(
resource_context, request.resource_type, frame_tree_node_id));
}
return result;
}
#if defined(OS_LINUX)
void CefContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
const base::CommandLine& command_line,
@ -1195,6 +1219,43 @@ void CefContentBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
render_frame_id);
if (factory)
factories->emplace(extensions::kExtensionScheme, std::move(factory));
content::RenderFrameHost* frame_host =
content::RenderFrameHost::FromID(render_process_id, render_frame_id);
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(frame_host);
if (!web_contents)
return;
extensions::CefExtensionWebContentsObserver* web_observer =
extensions::CefExtensionWebContentsObserver::FromWebContents(
web_contents);
// There is nothing to do if no CefExtensionWebContentsObserver is attached
// to the |web_contents|.
if (!web_observer)
return;
const extensions::Extension* extension =
web_observer->GetExtensionFromFrame(frame_host, false);
if (!extension)
return;
std::vector<std::string> allowed_webui_hosts;
// Support for chrome:// scheme if appropriate.
if ((extension->is_extension() || extension->is_platform_app()) &&
extensions::Manifest::IsComponentLocation(extension->location())) {
// Components of chrome that are implemented as extensions or platform apps
// are allowed to use chrome://resources/ and chrome://theme/ URLs.
allowed_webui_hosts.emplace_back(content::kChromeUIResourcesHost);
allowed_webui_hosts.emplace_back(chrome::kChromeUIThemeHost);
}
if (!allowed_webui_hosts.empty()) {
factories->emplace(
content::kChromeUIScheme,
content::CreateWebUIURLLoader(frame_host, content::kChromeUIScheme,
std::move(allowed_webui_hosts)));
}
}
bool CefContentBrowserClient::WillCreateURLLoaderFactory(

View File

@ -124,6 +124,13 @@ class CefContentBrowserClient : public content::ContentBrowserClient {
std::vector<std::unique_ptr<content::NavigationThrottle>>
CreateThrottlesForNavigation(
content::NavigationHandle* navigation_handle) override;
std::vector<std::unique_ptr<content::URLLoaderThrottle>>
CreateURLLoaderThrottles(
const network::ResourceRequest& request,
content::ResourceContext* resource_context,
const base::RepeatingCallback<content::WebContents*()>& wc_getter,
content::NavigationUIData* navigation_ui_data,
int frame_tree_node_id) override;
#if defined(OS_LINUX)
void GetAdditionalMappedFilesForChildProcess(

View File

@ -317,13 +317,9 @@ void CefDownloadManagerDelegate::OnDownloadDestroyed(DownloadItem* item) {
void CefDownloadManagerDelegate::OnDownloadCreated(DownloadManager* manager,
DownloadItem* item) {
CefBrowserHostImpl* browser = nullptr;
content::WebContents* contents =
content::DownloadItemUtils::GetWebContents(item);
if (contents) {
browser = CefBrowserHostImpl::GetBrowserForContents(contents).get();
DCHECK(browser);
}
// When NetworkService is enabled this callback may arrive after
// DetermineDownloadTarget, so we allow association from either method.
CefRefPtr<CefBrowserHostImpl> browser = GetOrAssociateBrowser(item);
if (!browser) {
// If the download is rejected (e.g. ALT+click on an invalid protocol link)
// then an "interrupted" download will be started via DownloadManagerImpl::
@ -336,17 +332,7 @@ void CefDownloadManagerDelegate::OnDownloadCreated(DownloadManager* manager,
LOG(INFO) << "Rejected download of " << url_chain.back().spec();
}
item->Cancel(true);
return;
}
item->AddObserver(this);
item_browser_map_.insert(std::make_pair(item, browser));
// Register as an observer so that we can cancel associated DownloadItems when
// the browser is destroyed.
if (!browser->HasObserver(this))
browser->AddObserver(this);
}
void CefDownloadManagerDelegate::ManagerGoingDown(DownloadManager* manager) {
@ -368,7 +354,9 @@ bool CefDownloadManagerDelegate::DetermineDownloadTarget(
return true;
}
CefRefPtr<CefBrowserHostImpl> browser = GetBrowser(item);
// When NetworkService is enabled this callback may arrive before
// OnDownloadCreated, so we allow association from either method.
CefRefPtr<CefBrowserHostImpl> browser = GetOrAssociateBrowser(item);
CefRefPtr<CefDownloadHandler> handler;
if (browser.get())
handler = GetDownloadHandler(browser);
@ -412,6 +400,34 @@ void CefDownloadManagerDelegate::OnBrowserDestroyed(
}
}
CefBrowserHostImpl* CefDownloadManagerDelegate::GetOrAssociateBrowser(
download::DownloadItem* item) {
ItemBrowserMap::const_iterator it = item_browser_map_.find(item);
if (it != item_browser_map_.end())
return it->second;
CefBrowserHostImpl* browser = nullptr;
content::WebContents* contents =
content::DownloadItemUtils::GetWebContents(item);
if (contents) {
browser = CefBrowserHostImpl::GetBrowserForContents(contents).get();
DCHECK(browser);
}
if (!browser)
return nullptr;
item->AddObserver(this);
item_browser_map_.insert(std::make_pair(item, browser));
// Register as an observer so that we can cancel associated DownloadItems when
// the browser is destroyed.
if (!browser->HasObserver(this))
browser->AddObserver(this);
return browser;
}
CefBrowserHostImpl* CefDownloadManagerDelegate::GetBrowser(DownloadItem* item) {
ItemBrowserMap::const_iterator it = item_browser_map_.find(item);
if (it != item_browser_map_.end())

View File

@ -43,6 +43,7 @@ class CefDownloadManagerDelegate : public download::DownloadItem::Observer,
// CefBrowserHostImpl::Observer methods.
void OnBrowserDestroyed(CefBrowserHostImpl* browser) override;
CefBrowserHostImpl* GetOrAssociateBrowser(download::DownloadItem* item);
CefBrowserHostImpl* GetBrowser(download::DownloadItem* item);
content::DownloadManager* manager_;

View File

@ -191,6 +191,7 @@ void CefExtensionSystem::Init() {
// 3. A page requests a resource with the "application/pdf" mime type. For
// example, by loading a PDF file.
// 4. CefResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream
// or PluginResponseInterceptorURLLoaderThrottle (NetworkService)
// intercepts the PDF resource load in the browser process, generates a
// unique View ID that is associated with the resource request for later
// retrieval via MimeHandlerStreamManager and the
@ -212,8 +213,10 @@ void CefExtensionSystem::Init() {
// 8. Access to PDF extension resources is checked by
// CefExtensionsBrowserClient::AllowCrossRendererResourceLoad.
// 9. PDF extension resources are provided from bundle via
// CefExtensionsBrowserClient::MaybeCreateResourceBundleRequestJob and
// CefComponentExtensionResourceManager.
// CefExtensionsBrowserClient::MaybeCreateResourceBundleRequestJob or
// CefExtensionsBrowserClient::LoadResourceFromResourceBundle,
// CefContentBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories
// (NetworkService) and CefComponentExtensionResourceManager.
// 10.The PDF extension (chrome/browser/resources/pdf/browser_api.js) calls
// chrome.mimeHandlerPrivate.getStreamInfo to retrieve the PDF resource
// stream. This API is implemented using Mojo as described in

View File

@ -4,6 +4,7 @@
#include "libcef/browser/extensions/extension_web_contents_observer.h"
#include "chrome/common/webui_url_constants.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
@ -40,11 +41,13 @@ void CefExtensionWebContentsObserver::RenderFrameCreated(
auto policy = content::ChildProcessSecurityPolicy::GetInstance();
// Components of chrome that are implemented as extensions or platform apps
// are allowed to use chrome://resources/ URLs.
// are allowed to use chrome://resources/ and chrome://theme/ URLs.
if ((extension->is_extension() || extension->is_platform_app()) &&
Manifest::IsComponentLocation(extension->location())) {
policy->GrantRequestOrigin(
process_id, url::Origin::Create(GURL(content::kChromeUIResourcesURL)));
policy->GrantRequestOrigin(
process_id, url::Origin::Create(GURL(chrome::kChromeUIThemeURL)));
}
}

View File

@ -122,20 +122,22 @@ CefExtensionsBrowserClient::MaybeCreateResourceBundleRequestJob(
base::FilePath CefExtensionsBrowserClient::GetBundleResourcePath(
const network::ResourceRequest& request,
const base::FilePath& extension_resources_path,
ComponentExtensionResourceInfo* resource_id) const {
*resource_id = {0};
return base::FilePath();
ComponentExtensionResourceInfo* resource_info) const {
return chrome_url_request_util::GetBundleResourcePath(
request, extension_resources_path, resource_info);
}
void CefExtensionsBrowserClient::LoadResourceFromResourceBundle(
const network::ResourceRequest& request,
network::mojom::URLLoaderRequest loader,
const base::FilePath& resource_relative_path,
const ComponentExtensionResourceInfo& resource_id,
const ComponentExtensionResourceInfo& resource_info,
const std::string& content_security_policy,
network::mojom::URLLoaderClientPtr client,
bool send_cors_header) {
NOTREACHED() << "Load resources from bundles not supported.";
chrome_url_request_util::LoadResourceFromResourceBundle(
request, std::move(loader), resource_relative_path, resource_info,
content_security_policy, std::move(client), send_cors_header);
}
bool CefExtensionsBrowserClient::AllowCrossRendererResourceLoad(
@ -147,13 +149,6 @@ bool CefExtensionsBrowserClient::AllowCrossRendererResourceLoad(
const Extension* extension,
const ExtensionSet& extensions,
const ProcessMap& process_map) {
// TODO(cef): This bypasses additional checks added to
// AllowCrossRendererResourceLoad() in https://crrev.com/5cf9d45c. Figure out
// why permission is not being granted based on "web_accessible_resources"
// specified in the PDF extension manifest.json file.
if (extension && extension->id() == extension_misc::kPdfExtensionId)
return true;
bool allowed = false;
if (url_request_util::AllowCrossRendererResourceLoad(
url, resource_type, page_transition, child_id, is_incognito,

View File

@ -52,12 +52,12 @@ class CefExtensionsBrowserClient : public ExtensionsBrowserClient {
base::FilePath GetBundleResourcePath(
const network::ResourceRequest& request,
const base::FilePath& extension_resources_path,
ComponentExtensionResourceInfo* resource_id) const override;
ComponentExtensionResourceInfo* resource_info) const override;
void LoadResourceFromResourceBundle(
const network::ResourceRequest& request,
network::mojom::URLLoaderRequest loader,
const base::FilePath& resource_relative_path,
const ComponentExtensionResourceInfo& resource_id,
const ComponentExtensionResourceInfo& resource_info,
const std::string& content_security_policy,
network::mojom::URLLoaderClientPtr client,
bool send_cors_header) override;

View File

@ -888,8 +888,9 @@ std::unique_ptr<InterceptedRequestHandler> CreateInterceptedRequestHandler(
}
// Flag subresource loads of custom schemes.
const bool is_external = !is_navigation && !scheme::IsInternalHandledScheme(
request_initiator.scheme());
const bool is_external =
!is_navigation && !is_download && !request_initiator.scheme().empty() &&
!scheme::IsInternalHandledScheme(request_initiator.scheme());
wrapper->Initialize(browser_context, browserPtr, framePtr, render_process_id,
render_frame_id, frame_tree_node_id, is_navigation,

View File

@ -124,6 +124,63 @@ index e699f1feb070..f5fcc878c246 100644
// If we broke out of the loop, we have found an enabled plugin.
bool enabled = i < matching_plugins.size();
diff --git chrome/browser/plugins/plugin_utils.cc chrome/browser/plugins/plugin_utils.cc
index 68e7057b7cf6..102caf10e68c 100644
--- chrome/browser/plugins/plugin_utils.cc
+++ chrome/browser/plugins/plugin_utils.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/plugins/plugin_utils.h"
#include "base/values.h"
+#include "cef/libcef/features/features.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/common/plugin_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -14,6 +15,11 @@
#include "url/gurl.h"
#include "url/origin.h"
+#if BUILDFLAG(ENABLE_CEF)
+#include "cef/libcef/browser/resource_context.h"
+#include "cef/libcef/common/extensions/extensions_util.h"
+#endif
+
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/browser/info_map.h"
#include "extensions/common/constants.h"
@@ -158,10 +164,23 @@ PluginUtils::GetMimeTypeToExtensionIdMap(
content::ResourceContext* resource_context) {
base::flat_map<std::string, std::string> mime_type_to_extension_id_map;
#if BUILDFLAG(ENABLE_EXTENSIONS)
+
+#if BUILDFLAG(ENABLE_CEF)
+ CefResourceContext* context =
+ static_cast<CefResourceContext*>(resource_context);
+ bool profile_is_off_the_record = context->IsOffTheRecord();
+ const scoped_refptr<const extensions::InfoMap> extension_info_map(
+ context->GetExtensionInfoMap());
+ bool always_open_pdf_externally = !extensions::PdfExtensionEnabled();
+#else
ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
bool profile_is_off_the_record = io_data->IsOffTheRecord();
const scoped_refptr<const extensions::InfoMap> extension_info_map(
io_data->GetExtensionInfoMap());
+ bool always_open_pdf_externally =
+ io_data->always_open_pdf_externally()->GetValue();
+#endif
+
std::vector<std::string> whitelist = MimeTypesHandler::GetMIMETypeWhitelist();
// Go through the white-listed extensions and try to use them to intercept
// the URL request.
@@ -176,7 +195,7 @@ PluginUtils::GetMimeTypeToExtensionIdMap(
}
if (extension_id == extension_misc::kPdfExtensionId &&
- io_data->always_open_pdf_externally()->GetValue()) {
+ always_open_pdf_externally) {
continue;
}
diff --git chrome/renderer/chrome_content_renderer_client.cc chrome/renderer/chrome_content_renderer_client.cc
index a838fabbdfcb..93ccc1cbdb35 100644
--- chrome/renderer/chrome_content_renderer_client.cc