cef/libcef/browser/net/url_request_manager.cc
Marshall Greenblatt 241941a44a Move message routing from CefBrowser to CefFrame (see issue #2498).
This change moves the SendProcessMessage method from CefBrowser to CefFrame and
adds CefBrowser parameters to OnProcessMessageReceived and
OnDraggableRegionsChanged.

The internal implementation has changed as follows:
- Frame IDs are now a 64-bit combination of the 32-bit render_process_id and
  render_routing_id values that uniquely identify a RenderFrameHost (RFH).
- CefFrameHostImpl objects are now managed by CefBrowserInfo with life span tied
  to RFH expectations. Specifically, a CefFrameHostImpl object representing a
  sub-frame will be created when a RenderFrame is created in the renderer
  process and detached when the associated RenderFrame is deleted or the
  renderer process in which it runs has died.
- The CefFrameHostImpl object representing the main frame will always be valid
  but the underlying RFH (and associated frame ID) may change over time as a
  result of cross-origin navigations. Despite these changes calling LoadURL on
  the main frame object in the browser process will always navigate as expected.
- Speculative RFHs, which may be created as a result of a cross-origin
  navigation and discarded if that navigation is not committed, are now handled
  correctly (e.g. ignored in most cases until they're committed).
- It is less likely, but still possible, to receive a CefFrame object with an
  invalid frame ID (ID < 0). This can happen in cases where a RFH has not yet
  been created for a sub-frame. For example, when OnBeforeBrowse is called
  before initiating navigation in a previously nonexisting sub-frame.

To test: All tests pass with NetworkService enabled and disabled.
2019-05-29 17:44:56 +03:00

280 lines
8.5 KiB
C++

// Copyright (c) 2015 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2006-2009 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/net/url_request_manager.h"
#include "include/cef_browser.h"
#include "include/cef_scheme.h"
#include "libcef/browser/browser_context.h"
#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/net/net_util.h"
#include "libcef/browser/net/resource_request_job.h"
#include "libcef/browser/net/scheme_handler.h"
#include "libcef/browser/thread_util.h"
#include "libcef/common/net/scheme_registration.h"
#include "libcef/common/request_impl.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_filter.h"
#include "net/url_request/url_request_http_job.h"
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_job_factory_impl.h"
#include "url/third_party/mozilla/url_parse.h"
#include "url/url_util.h"
using net::URLRequestStatus;
namespace {
// Copied from net/url_request/url_request_job_manager.cc.
struct SchemeToFactory {
const char* scheme;
net::URLRequest::ProtocolFactory* factory;
};
static const SchemeToFactory kBuiltinFactories[] = {
{"http", net::URLRequestHttpJob::Factory},
{"https", net::URLRequestHttpJob::Factory},
};
bool IsBuiltinScheme(const std::string& scheme) {
for (size_t i = 0; i < base::size(kBuiltinFactories); ++i)
if (base::LowerCaseEqualsASCII(scheme, kBuiltinFactories[i].scheme))
return true;
return false;
}
net::URLRequestJob* GetBuiltinSchemeRequestJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const std::string& scheme) {
// See if the request should be handled by a built-in protocol factory.
for (size_t i = 0; i < base::size(kBuiltinFactories); ++i) {
if (scheme == kBuiltinFactories[i].scheme) {
net::URLRequestJob* job =
(kBuiltinFactories[i].factory)(request, network_delegate, scheme);
DCHECK(job); // The built-in factories are not expected to fail!
return job;
}
}
return NULL;
}
std::string ToLower(const std::string& str) {
std::string str_lower = str;
std::transform(str_lower.begin(), str_lower.end(), str_lower.begin(),
towlower);
return str;
}
} // namespace
// Class used for creating URLRequestJob instances. The lifespan of this object
// is managed by URLRequestJobFactory.
class CefProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
public:
CefProtocolHandler(CefURLRequestManager* request_manager,
const std::string& scheme)
: request_manager_(request_manager), scheme_(scheme) {}
// From net::URLRequestJobFactory::ProtocolHandler
net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override {
CEF_REQUIRE_IOT();
return request_manager_->GetRequestJob(request, network_delegate, scheme_);
}
private:
CefURLRequestManager* request_manager_;
std::string scheme_;
};
CefURLRequestManager::CefURLRequestManager(
net::URLRequestJobFactoryImpl* job_factory)
: job_factory_(job_factory) {
CEF_REQUIRE_IOT();
DCHECK(job_factory_);
}
CefURLRequestManager::~CefURLRequestManager() {
CEF_REQUIRE_IOT();
}
bool CefURLRequestManager::AddFactory(
const std::string& scheme,
const std::string& domain,
CefRefPtr<CefSchemeHandlerFactory> factory) {
if (!factory.get()) {
RemoveFactory(scheme, domain);
return true;
}
CEF_REQUIRE_IOT();
std::string scheme_lower = ToLower(scheme);
std::string domain_lower = ToLower(domain);
// Hostname is only supported for standard schemes.
if (!scheme::IsStandardScheme(scheme_lower))
domain_lower.clear();
SetProtocolHandlerIfNecessary(scheme_lower, true);
handler_map_[make_pair(scheme_lower, domain_lower)] = factory;
return true;
}
void CefURLRequestManager::RemoveFactory(const std::string& scheme,
const std::string& domain) {
CEF_REQUIRE_IOT();
std::string scheme_lower = ToLower(scheme);
std::string domain_lower = ToLower(domain);
// Hostname is only supported for standard schemes.
if (!scheme::IsStandardScheme(scheme_lower))
domain_lower.clear();
HandlerMap::iterator iter =
handler_map_.find(make_pair(scheme_lower, domain_lower));
if (iter != handler_map_.end()) {
handler_map_.erase(iter);
SetProtocolHandlerIfNecessary(scheme_lower, false);
}
}
// Clear all the existing URL handlers and unregister the ProtocolFactory.
void CefURLRequestManager::ClearFactories() {
CEF_REQUIRE_IOT();
// Create a unique set of scheme names.
std::set<std::string> schemes;
for (HandlerMap::const_iterator i = handler_map_.begin();
i != handler_map_.end(); ++i) {
schemes.insert(i->first.first);
}
for (std::set<std::string>::const_iterator scheme = schemes.begin();
scheme != schemes.end(); ++scheme) {
const std::string& scheme_name = *scheme;
if (!scheme::IsInternalProtectedScheme(scheme_name)) {
bool set_protocol = job_factory_->SetProtocolHandler(scheme_name, NULL);
DCHECK(set_protocol);
}
}
handler_map_.clear();
// Re-register internal scheme handlers that can be overridden.
scheme::RegisterInternalHandlers(this);
}
// Helper for chaining ProtocolHandler implementations.
net::URLRequestJob* CefURLRequestManager::GetRequestJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) {
CEF_REQUIRE_IOT();
return GetRequestJob(request, network_delegate, request->url().scheme());
}
void CefURLRequestManager::SetProtocolHandlerIfNecessary(
const std::string& scheme,
bool add) {
// Don't modify a protocol handler for internal protected schemes or if the
// protocol handler is still needed by other registered factories.
if (scheme::IsInternalProtectedScheme(scheme) || HasFactory(scheme))
return;
bool set_protocol = job_factory_->SetProtocolHandler(
scheme,
base::WrapUnique(add ? new CefProtocolHandler(this, scheme) : NULL));
DCHECK(set_protocol);
}
bool CefURLRequestManager::HasFactory(const std::string& scheme) {
if (handler_map_.empty())
return false;
for (HandlerMap::const_iterator i = handler_map_.begin();
i != handler_map_.end(); ++i) {
if (scheme == i->first.first)
return true;
}
return false;
}
CefRefPtr<CefSchemeHandlerFactory> CefURLRequestManager::GetHandlerFactory(
net::URLRequest* request,
const std::string& scheme) {
CefRefPtr<CefSchemeHandlerFactory> factory;
if (request->url().is_valid() && scheme::IsStandardScheme(scheme)) {
// Check for a match with a domain first.
const std::string& domain = request->url().host();
HandlerMap::iterator i = handler_map_.find(make_pair(scheme, domain));
if (i != handler_map_.end())
factory = i->second;
}
if (!factory.get()) {
// Check for a match with no specified domain.
HandlerMap::iterator i =
handler_map_.find(make_pair(scheme, std::string()));
if (i != handler_map_.end())
factory = i->second;
}
return factory;
}
net::URLRequestJob* CefURLRequestManager::GetRequestJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const std::string& scheme) {
net::URLRequestJob* job = NULL;
CefRefPtr<CefSchemeHandlerFactory> factory =
GetHandlerFactory(request, scheme);
if (factory.get()) {
CefRefPtr<CefBrowserHostImpl> browser =
net_util::GetBrowserForRequest(request);
CefRefPtr<CefFrame> frame;
if (browser.get()) {
frame = net_util::GetFrameForRequest(browser->browser_info(), request);
}
// Populate the request data.
CefRefPtr<CefRequestImpl> requestPtr(new CefRequestImpl());
requestPtr->Set(request);
// Call the handler factory to create the handler for the request.
CefRefPtr<CefResourceHandler> handler =
factory->Create(browser.get(), frame, scheme, requestPtr.get());
if (handler.get())
job = new CefResourceRequestJob(request, network_delegate, handler);
}
if (!job && IsBuiltinScheme(scheme)) {
// Give the built-in scheme handler a chance to handle the request.
job = GetBuiltinSchemeRequestJob(request, network_delegate, scheme);
}
#if DCHECK_IS_ON()
if (job)
DLOG(INFO) << "CefURLRequestManager hit for " << request->url().spec();
#endif
return job;
}