Add NetworkService support for CefURLRequest (see issue #2622).

Requests created using CefURLRequest::Create are not associated with a
browser/frame. When originating from the render process these requests cannot be
intercepted and consequently only http(s) and blob requests are supported. To
work around this limitation a new CefFrame::CreateURLRequest method has been
added that allows the request to be associated with that browser/frame for
interception purposes.

This change also fixes an issue with the NetworkService implementation where
redirected requests could result in two parallel requests being sent to the
target server.

To test: URLRequestTest.* tests pass with NetworkService enabled.
This commit is contained in:
Marshall Greenblatt
2019-05-10 18:14:48 -04:00
parent f9b042c375
commit ba0e1b5719
43 changed files with 1637 additions and 344 deletions

View File

@@ -1868,35 +1868,6 @@ void CefBrowserHostImpl::SendCode(
}
}
void CefBrowserHostImpl::ExecuteJavaScriptWithUserGestureForTests(
int64 frame_id,
const CefString& javascript) {
DCHECK(frame_id >= CefFrameHostImpl::kMainFrameId);
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(
&CefBrowserHostImpl::ExecuteJavaScriptWithUserGestureForTests, this,
frame_id, javascript));
return;
}
if (!web_contents())
return;
content::RenderFrameHost* rfh;
if (frame_id == CefFrameHostImpl::kMainFrameId) {
rfh = web_contents()->GetMainFrame();
} else {
rfh = content::RenderFrameHost::FromID(
web_contents()->GetRenderViewHost()->GetProcess()->GetID(), frame_id);
}
if (rfh)
rfh->ExecuteJavaScriptWithUserGestureForTests(javascript);
}
void CefBrowserHostImpl::ViewText(const std::string& text) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT,

View File

@@ -351,9 +351,6 @@ class CefBrowserHostImpl : public CefBrowserHost,
int script_start_line,
CefRefPtr<CefResponseManager::Handler> responseHandler);
void ExecuteJavaScriptWithUserGestureForTests(int64 frame_id,
const CefString& javascript);
// Open the specified text in the default text editor.
void ViewText(const std::string& text);

View File

@@ -3,12 +3,20 @@
// be found in the LICENSE file.
#include "libcef/browser/frame_host_impl.h"
#include "include/cef_request.h"
#include "include/cef_stream.h"
#include "include/cef_v8.h"
#include "include/test/cef_test_helpers.h"
#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/net/browser_urlrequest_old_impl.h"
#include "libcef/browser/net_service/browser_urlrequest_impl.h"
#include "libcef/common/cef_messages.h"
#include "libcef/common/net_service/util.h"
#include "libcef/common/task_runner_impl.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
namespace {
@@ -235,6 +243,42 @@ void CefFrameHostImpl::VisitDOM(CefRefPtr<CefDOMVisitor> visitor) {
NOTREACHED() << "VisitDOM cannot be called from the browser process";
}
CefRefPtr<CefURLRequest> CefFrameHostImpl::CreateURLRequest(
CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client) {
if (!request || !client)
return NULL;
if (!CefTaskRunnerImpl::GetCurrentTaskRunner()) {
NOTREACHED() << "called on invalid thread";
return NULL;
}
CefRefPtr<CefBrowserHostImpl> browser;
{
base::AutoLock lock_scope(state_lock_);
browser = browser_;
}
if (!browser)
return NULL;
auto request_context = browser->request_context();
if (net_service::IsEnabled()) {
CefRefPtr<CefBrowserURLRequest> impl =
new CefBrowserURLRequest(this, request, client, request_context);
if (impl->Start())
return impl.get();
} else {
CefRefPtr<CefBrowserURLRequestOld> impl =
new CefBrowserURLRequestOld(request, client, request_context);
if (impl->Start())
return impl.get();
}
return NULL;
}
void CefFrameHostImpl::SendJavaScript(const std::string& jsCode,
const std::string& scriptUrl,
int startLine) {
@@ -262,6 +306,23 @@ void CefFrameHostImpl::SendJavaScript(const std::string& jsCode,
void CefFrameHostImpl::ExecuteJavaScriptWithUserGestureForTests(
const CefString& javascript) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(
&CefFrameHostImpl::ExecuteJavaScriptWithUserGestureForTests, this,
javascript));
return;
}
content::RenderFrameHost* rfh = GetRenderFrameHost();
if (rfh)
rfh->ExecuteJavaScriptWithUserGestureForTests(javascript);
}
content::RenderFrameHost* CefFrameHostImpl::GetRenderFrameHost() {
CEF_REQUIRE_UIT();
CefRefPtr<CefBrowserHostImpl> browser;
int64 frame_id;
@@ -271,8 +332,18 @@ void CefFrameHostImpl::ExecuteJavaScriptWithUserGestureForTests(
frame_id = (is_main_frame_ ? kMainFrameId : frame_id_);
}
if (browser.get() && frame_id != kInvalidFrameId)
browser->ExecuteJavaScriptWithUserGestureForTests(frame_id, javascript);
if (!browser || frame_id == kInvalidFrameId)
return nullptr;
auto web_contents = browser->web_contents();
if (!web_contents)
return nullptr;
if (frame_id == kMainFrameId)
return web_contents->GetMainFrame();
return content::RenderFrameHost::FromID(
web_contents->GetRenderViewHost()->GetProcess()->GetID(), frame_id);
}
void CefFrameHostImpl::Detach() {
@@ -300,5 +371,6 @@ void CefFrameHostImpl::SendCommand(
void CefExecuteJavaScriptWithUserGestureForTests(CefRefPtr<CefFrame> frame,
const CefString& javascript) {
CefFrameHostImpl* impl = static_cast<CefFrameHostImpl*>(frame.get());
impl->ExecuteJavaScriptWithUserGestureForTests(javascript);
if (impl)
impl->ExecuteJavaScriptWithUserGestureForTests(javascript);
}

View File

@@ -13,6 +13,10 @@
#include "base/synchronization/lock.h"
namespace content {
class RenderFrameHost;
}
class CefBrowserHostImpl;
// Implementation of CefFrame. CefFrameHostImpl objects are owned by the
@@ -55,6 +59,9 @@ class CefFrameHostImpl : public CefFrame {
CefRefPtr<CefBrowser> GetBrowser() override;
CefRefPtr<CefV8Context> GetV8Context() override;
void VisitDOM(CefRefPtr<CefDOMVisitor> visitor) override;
CefRefPtr<CefURLRequest> CreateURLRequest(
CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client) override;
void SetFocused(bool focused);
void SetAttributes(bool is_main_frame,
@@ -69,6 +76,10 @@ class CefFrameHostImpl : public CefFrame {
void ExecuteJavaScriptWithUserGestureForTests(const CefString& javascript);
// Returns the RFH associated with this frame. Must be called on the UI
// thread.
content::RenderFrameHost* GetRenderFrameHost();
// Detach the frame from the browser.
void Detach();

View File

@@ -2,7 +2,7 @@
// reserved. Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
#include "libcef/browser/browser_urlrequest_impl.h"
#include "libcef/browser/net/browser_urlrequest_old_impl.h"
#include <string>
#include <utility>
@@ -36,7 +36,7 @@ namespace {
class CefURLFetcherDelegate : public net::URLFetcherDelegate {
public:
CefURLFetcherDelegate(CefBrowserURLRequest::Context* context,
CefURLFetcherDelegate(CefBrowserURLRequestOld::Context* context,
int request_flags);
~CefURLFetcherDelegate() override;
@@ -52,14 +52,14 @@ class CefURLFetcherDelegate : public net::URLFetcherDelegate {
private:
// The context_ pointer will outlive this object.
CefBrowserURLRequest::Context* context_;
CefBrowserURLRequestOld::Context* context_;
int request_flags_;
};
class CefURLFetcherResponseWriter : public net::URLFetcherResponseWriter {
public:
CefURLFetcherResponseWriter(
CefRefPtr<CefBrowserURLRequest> url_request,
CefRefPtr<CefBrowserURLRequestOld> url_request,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: url_request_(url_request), task_runner_(task_runner) {}
@@ -91,7 +91,7 @@ class CefURLFetcherResponseWriter : public net::URLFetcherResponseWriter {
private:
static void WriteOnClientThread(
CefRefPtr<CefBrowserURLRequest> url_request,
CefRefPtr<CefBrowserURLRequestOld> url_request,
scoped_refptr<net::IOBuffer> buffer,
int num_bytes,
net::CompletionOnceCallback callback,
@@ -111,7 +111,7 @@ class CefURLFetcherResponseWriter : public net::URLFetcherResponseWriter {
std::move(callback).Run(num_bytes);
}
CefRefPtr<CefBrowserURLRequest> url_request_;
CefRefPtr<CefBrowserURLRequestOld> url_request_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(CefURLFetcherResponseWriter);
@@ -124,12 +124,13 @@ std::unique_ptr<base::SupportsUserData::Data> CreateURLRequestUserData(
} // namespace
// CefBrowserURLRequest::Context ----------------------------------------------
// CefBrowserURLRequestOld::Context
// ----------------------------------------------
class CefBrowserURLRequest::Context
: public base::RefCountedThreadSafe<CefBrowserURLRequest::Context> {
class CefBrowserURLRequestOld::Context
: public base::RefCountedThreadSafe<CefBrowserURLRequestOld::Context> {
public:
Context(CefRefPtr<CefBrowserURLRequest> url_request,
Context(CefRefPtr<CefBrowserURLRequestOld> url_request,
CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client,
CefRefPtr<CefRequestContext> request_context)
@@ -177,10 +178,12 @@ class CefBrowserURLRequest::Context
base::PostTaskWithTraitsAndReply(
FROM_HERE, {BrowserThread::UI},
base::Bind(&CefBrowserURLRequest::Context::GetRequestContextOnUIThread,
this),
base::Bind(&CefBrowserURLRequest::Context::ContinueOnOriginatingThread,
this, url, request_type));
base::Bind(
&CefBrowserURLRequestOld::Context::GetRequestContextOnUIThread,
this),
base::Bind(
&CefBrowserURLRequestOld::Context::ContinueOnOriginatingThread,
this, url, request_type));
return true;
}
@@ -330,7 +333,7 @@ class CefBrowserURLRequest::Context
bool response_was_cached() const { return response_was_cached_; }
private:
friend class base::RefCountedThreadSafe<CefBrowserURLRequest::Context>;
friend class base::RefCountedThreadSafe<CefBrowserURLRequestOld::Context>;
~Context() {
if (fetcher_.get()) {
@@ -369,7 +372,7 @@ class CefBrowserURLRequest::Context
}
// Members only accessed on the initialization thread.
CefRefPtr<CefBrowserURLRequest> url_request_;
CefRefPtr<CefBrowserURLRequestOld> url_request_;
CefRefPtr<CefRequest> request_;
CefRefPtr<CefURLRequestClient> client_;
CefRefPtr<CefRequestContext> request_context_;
@@ -391,7 +394,7 @@ class CefBrowserURLRequest::Context
namespace {
CefURLFetcherDelegate::CefURLFetcherDelegate(
CefBrowserURLRequest::Context* context,
CefBrowserURLRequestOld::Context* context,
int request_flags)
: context_(context), request_flags_(request_flags) {}
@@ -402,7 +405,7 @@ void CefURLFetcherDelegate::OnURLFetchComplete(const net::URLFetcher* source) {
// in the call stack.
CefTaskRunnerImpl::GetCurrentTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&CefBrowserURLRequest::Context::OnComplete, context_));
base::Bind(&CefBrowserURLRequestOld::Context::OnComplete, context_));
}
void CefURLFetcherDelegate::OnURLFetchDownloadProgress(
@@ -423,66 +426,67 @@ void CefURLFetcherDelegate::OnURLFetchUploadProgress(
} // namespace
// CefBrowserURLRequest -------------------------------------------------------
// CefBrowserURLRequestOld
// -------------------------------------------------------
CefBrowserURLRequest::CefBrowserURLRequest(
CefBrowserURLRequestOld::CefBrowserURLRequestOld(
CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client,
CefRefPtr<CefRequestContext> request_context) {
context_ = new Context(this, request, client, request_context);
}
CefBrowserURLRequest::~CefBrowserURLRequest() {}
CefBrowserURLRequestOld::~CefBrowserURLRequestOld() {}
bool CefBrowserURLRequest::Start() {
bool CefBrowserURLRequestOld::Start() {
if (!VerifyContext())
return false;
return context_->Start();
}
CefRefPtr<CefRequest> CefBrowserURLRequest::GetRequest() {
CefRefPtr<CefRequest> CefBrowserURLRequestOld::GetRequest() {
if (!VerifyContext())
return NULL;
return context_->request();
}
CefRefPtr<CefURLRequestClient> CefBrowserURLRequest::GetClient() {
CefRefPtr<CefURLRequestClient> CefBrowserURLRequestOld::GetClient() {
if (!VerifyContext())
return NULL;
return context_->client();
}
CefURLRequest::Status CefBrowserURLRequest::GetRequestStatus() {
CefURLRequest::Status CefBrowserURLRequestOld::GetRequestStatus() {
if (!VerifyContext())
return UR_UNKNOWN;
return context_->status();
}
CefURLRequest::ErrorCode CefBrowserURLRequest::GetRequestError() {
CefURLRequest::ErrorCode CefBrowserURLRequestOld::GetRequestError() {
if (!VerifyContext())
return ERR_NONE;
return context_->error_code();
}
CefRefPtr<CefResponse> CefBrowserURLRequest::GetResponse() {
CefRefPtr<CefResponse> CefBrowserURLRequestOld::GetResponse() {
if (!VerifyContext())
return NULL;
return context_->response();
}
bool CefBrowserURLRequest::ResponseWasCached() {
bool CefBrowserURLRequestOld::ResponseWasCached() {
if (!VerifyContext())
return false;
return context_->response_was_cached();
}
void CefBrowserURLRequest::Cancel() {
void CefBrowserURLRequestOld::Cancel() {
if (!VerifyContext())
return;
return context_->Cancel();
}
bool CefBrowserURLRequest::VerifyContext() {
bool CefBrowserURLRequestOld::VerifyContext() {
DCHECK(context_.get());
if (!context_->CalledOnValidThread()) {
NOTREACHED() << "called on invalid thread";

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_NET_BROWSER_URLREQUEST_OLD_IMPL_H_
#define CEF_LIBCEF_BROWSER_NET_BROWSER_URLREQUEST_OLD_IMPL_H_
#include "include/cef_urlrequest.h"
#include "base/memory/ref_counted.h"
class CefBrowserURLRequestOld : public CefURLRequest {
public:
class Context;
CefBrowserURLRequestOld(CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client,
CefRefPtr<CefRequestContext> request_context);
~CefBrowserURLRequestOld() override;
bool Start();
// CefURLRequest methods.
CefRefPtr<CefRequest> GetRequest() override;
CefRefPtr<CefURLRequestClient> GetClient() override;
Status GetRequestStatus() override;
ErrorCode GetRequestError() override;
CefRefPtr<CefResponse> GetResponse() override;
bool ResponseWasCached() override;
void Cancel() override;
private:
bool VerifyContext();
scoped_refptr<Context> context_;
IMPLEMENT_REFCOUNTING(CefBrowserURLRequestOld);
};
#endif // CEF_LIBCEF_BROWSER_NET_BROWSER_URLREQUEST_OLD_IMPL_H_

View File

@@ -0,0 +1,510 @@
// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
// Copyright (c) 2018 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_service/browser_urlrequest_impl.h"
#include <string>
#include <utility>
#include "libcef/browser/browser_context.h"
#include "libcef/browser/frame_host_impl.h"
#include "libcef/browser/net_service/url_loader_factory_getter.h"
#include "libcef/browser/request_context_impl.h"
#include "libcef/browser/thread_util.h"
#include "libcef/common/net_service/net_service_util.h"
#include "libcef/common/request_impl.h"
#include "libcef/common/response_impl.h"
#include "libcef/common/task_runner_impl.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
// CefBrowserURLRequest::Context ----------------------------------------------
class CefBrowserURLRequest::Context
: public network::SimpleURLLoaderStreamConsumer {
public:
Context(CefRefPtr<CefBrowserURLRequest> url_request,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client,
CefRefPtr<CefRequestContext> request_context)
: url_request_(url_request),
frame_(frame),
request_(static_cast<CefRequestImpl*>(request.get())),
client_(client),
request_context_(request_context),
task_runner_(CefTaskRunnerImpl::GetCurrentTaskRunner()),
response_(new CefResponseImpl()),
weak_ptr_factory_(this) {
// Mark the request/response objects as read-only.
request_->SetReadOnly(true);
response_->SetReadOnly(true);
}
~Context() override = default;
bool Start() {
DCHECK(CalledOnValidThread());
const GURL& url = GURL(request_->GetURL().ToString());
if (!url.is_valid())
return false;
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(
&CefBrowserURLRequest::Context::GetURLLoaderFactoryGetterOnUIThread,
frame_, request_context_, weak_ptr_factory_.GetWeakPtr(),
task_runner_));
return true;
}
void Cancel() {
DCHECK(CalledOnValidThread());
// The request may already be complete or canceled.
if (!url_request_)
return;
DCHECK_EQ(status_, UR_IO_PENDING);
status_ = UR_CANCELED;
response_->SetReadOnly(false);
response_->SetError(ERR_ABORTED);
response_->SetReadOnly(true);
cleanup_immediately_ = true;
OnComplete(false);
}
CefRefPtr<CefRequest> request() const { return request_.get(); }
CefRefPtr<CefURLRequestClient> client() const { return client_; }
CefURLRequest::Status status() const { return status_; }
CefRefPtr<CefResponse> response() const { return response_.get(); }
bool response_was_cached() const { return response_was_cached_; }
inline bool CalledOnValidThread() {
return task_runner_->RunsTasksInCurrentSequence();
}
private:
static void GetURLLoaderFactoryGetterOnUIThread(
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequestContext> request_context,
base::WeakPtr<CefBrowserURLRequest::Context> self,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
CEF_REQUIRE_UIT();
// Get or create the request context and browser context.
CefRefPtr<CefRequestContextImpl> request_context_impl =
CefRequestContextImpl::GetOrCreateForRequestContext(request_context);
DCHECK(request_context_impl);
CefBrowserContext* browser_context =
request_context_impl->GetBrowserContext();
DCHECK(browser_context);
content::RenderFrameHost* rfh = nullptr;
if (frame) {
// The request will be associated with this frame/browser.
rfh = static_cast<CefFrameHostImpl*>(frame.get())->GetRenderFrameHost();
}
auto loader_factory_getter =
net_service::URLLoaderFactoryGetter::Create(rfh, browser_context);
task_runner->PostTask(
FROM_HERE,
base::BindOnce(
&CefBrowserURLRequest::Context::ContinueOnOriginatingThread, self,
loader_factory_getter));
}
void ContinueOnOriginatingThread(
scoped_refptr<net_service::URLLoaderFactoryGetter>
loader_factory_getter) {
DCHECK(CalledOnValidThread());
// The request may have been canceled.
if (!url_request_)
return;
DCHECK_EQ(status_, UR_IO_PENDING);
loader_factory_getter_ = loader_factory_getter;
const int request_flags = request_->GetFlags();
// Create the URLLoaderFactory and bind to this thread.
auto loader_factory = loader_factory_getter_->GetURLLoaderFactory();
auto resource_request = std::make_unique<network::ResourceRequest>();
static_cast<CefRequestImpl*>(request_.get())
->Get(resource_request.get(), false);
// SimpleURLLoader is picky about the body contents. Try to populate them
// correctly below.
auto request_body = resource_request->request_body;
resource_request->request_body = nullptr;
std::string content_type;
std::string method = resource_request->method;
if (request_body) {
if (method == "GET" || method == "HEAD") {
// Fix the method value to allow a request body.
method = "POST";
resource_request->method = method;
request_->SetReadOnly(false);
request_->SetMethod(method);
request_->SetReadOnly(true);
}
resource_request->headers.GetHeader(net::HttpRequestHeaders::kContentType,
&content_type);
}
loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
NO_TRAFFIC_ANNOTATION_YET);
if (request_body) {
if (request_body->elements()->size() == 1) {
const auto& element = (*request_body->elements())[0];
if (element.type() == network::mojom::DataElementType::kFile) {
if (content_type.empty()) {
const auto& extension = element.path().Extension();
if (!extension.empty()) {
// Requests should not block on the disk! On POSIX this goes to
// disk. http://code.google.com/p/chromium/issues/detail?id=59849
base::ThreadRestrictions::ScopedAllowIO allow_io;
// Also remove the leading period.
net::GetMimeTypeFromExtension(extension.substr(1), &content_type);
}
}
loader_->AttachFileForUpload(element.path(), content_type);
} else if (element.type() == network::mojom::DataElementType::kBytes) {
if (content_type.empty()) {
content_type = net_service::kContentTypeApplicationFormURLEncoded;
}
loader_->AttachStringForUpload(
std::string(element.bytes() + element.offset(),
element.length() - element.offset()),
content_type);
if (request_flags & UR_FLAG_REPORT_UPLOAD_PROGRESS) {
// Report the expected upload data size.
upload_data_size_ = element.length() - element.offset();
}
} else {
NOTIMPLEMENTED() << "Unsupported element type: " << element.type();
}
} else if (request_body->elements()->size() > 1) {
NOTIMPLEMENTED() << "Multi-part form data is not supported";
}
}
if (request_flags & UR_FLAG_NO_RETRY_ON_5XX) {
// No retries is the default setting, so we don't need to configure that.
// Allow delivery of non-2xx response bodies.
loader_->SetAllowHttpErrorResults(true);
} else {
// Allow 2 retries on 5xx response or network change.
// TODO(network): Consider exposing configuration of max retries and/or
// RETRY_ON_NETWORK_CHANGE as a separate flag.
loader_->SetRetryOptions(
2, network::SimpleURLLoader::RETRY_ON_5XX |
network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
}
if (request_flags & UR_FLAG_STOP_ON_REDIRECT) {
// The request will be canceled in OnRedirect.
loader_->SetOnRedirectCallback(
base::Bind(&CefBrowserURLRequest::Context::OnRedirect,
weak_ptr_factory_.GetWeakPtr()));
}
if (request_flags & UR_FLAG_REPORT_UPLOAD_PROGRESS) {
loader_->SetOnUploadProgressCallback(
base::Bind(&CefBrowserURLRequest::Context::OnUploadProgress,
weak_ptr_factory_.GetWeakPtr()));
}
if ((request_flags & UR_FLAG_NO_DOWNLOAD_DATA) || method == "HEAD") {
loader_->DownloadHeadersOnly(
loader_factory.get(),
base::BindOnce(&CefBrowserURLRequest::Context::OnHeadersOnly,
weak_ptr_factory_.GetWeakPtr()));
} else {
loader_->SetOnResponseStartedCallback(
base::BindOnce(&CefBrowserURLRequest::Context::OnResponseStarted,
weak_ptr_factory_.GetWeakPtr()));
loader_->SetOnDownloadProgressCallback(
base::Bind(&CefBrowserURLRequest::Context::OnDownloadProgress,
weak_ptr_factory_.GetWeakPtr()));
loader_->DownloadAsStream(loader_factory.get(), this);
}
}
void OnHeadersOnly(scoped_refptr<net::HttpResponseHeaders> headers) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(status_, UR_IO_PENDING);
response_->SetReadOnly(false);
response_->SetResponseHeaders(*headers);
response_->SetReadOnly(true);
// Match the previous behavior of sending download progress notifications
// for UR_FLAG_NO_DOWNLOAD_DATA requests but not HEAD requests.
if (request_->GetMethod().ToString() != "HEAD") {
download_data_size_ = headers->GetContentLength();
OnDownloadProgress(0);
}
cleanup_immediately_ = true;
OnComplete(true);
}
void OnRedirect(const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& response_head,
std::vector<std::string>* removed_headers) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(status_, UR_IO_PENDING);
// This method is only called if we intend to stop on redirects.
DCHECK(request_->GetFlags() | UR_FLAG_STOP_ON_REDIRECT);
response_->SetReadOnly(false);
response_->SetResponseHeaders(*response_head.headers);
response_->SetReadOnly(true);
Cancel();
}
void OnResponseStarted(const GURL& final_url,
const network::ResourceResponseHead& response_head) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(status_, UR_IO_PENDING);
response_->SetReadOnly(false);
response_->SetURL(final_url.spec());
response_->SetResponseHeaders(*response_head.headers);
response_->SetReadOnly(true);
download_data_size_ = response_head.content_length;
}
void OnUploadProgress(uint64_t position, uint64_t total) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(status_, UR_IO_PENDING);
upload_data_size_ = total;
if (position == total)
got_upload_progress_complete_ = true;
client_->OnUploadProgress(url_request_.get(), position, total);
}
void OnDownloadProgress(uint64_t current) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(status_, UR_IO_PENDING);
if (response_->GetStatus() == 0) {
// With failed requests this callback may arrive without a proceeding
// OnHeadersOnly or OnResponseStarted.
return;
}
NotifyUploadProgressIfNecessary();
client_->OnDownloadProgress(url_request_.get(), current,
download_data_size_);
}
void NotifyUploadProgressIfNecessary() {
if (!got_upload_progress_complete_ && upload_data_size_ > 0) {
// URLLoader sends upload notifications using a timer and will not send
// a notification if the request completes too quickly. We therefore
// send the notification here if necessary.
client_->OnUploadProgress(url_request_.get(), upload_data_size_,
upload_data_size_);
got_upload_progress_complete_ = true;
}
}
// SimpleURLLoaderStreamConsumer methods:
void OnDataReceived(base::StringPiece string_piece,
base::OnceClosure resume) override {
DCHECK(CalledOnValidThread());
DCHECK_EQ(status_, UR_IO_PENDING);
client_->OnDownloadData(url_request_.get(), string_piece.data(),
string_piece.length());
std::move(resume).Run();
}
void OnComplete(bool success) override {
DCHECK(CalledOnValidThread());
// The request may already be complete or canceled.
if (!url_request_)
return;
// Status will be UR_IO_PENDING if we're called when the request is complete
// (via SimpleURLLoaderStreamConsumer or OnHeadersOnly). We can only call
// these SimpleURLLoader methods if the request is complete.
if (status_ == UR_IO_PENDING) {
status_ = success ? UR_SUCCESS : UR_FAILED;
response_->SetReadOnly(false);
response_->SetURL(loader_->GetFinalURL().spec());
response_->SetError(static_cast<cef_errorcode_t>(loader_->NetError()));
response_->SetReadOnly(true);
response_was_cached_ = loader_->LoadedFromCache();
}
if (success)
NotifyUploadProgressIfNecessary();
client_->OnRequestComplete(url_request_.get());
// When called via SimpleURLLoaderStreamConsumer we need to cleanup
// asynchronously. If the load is still pending this will also cancel it.
Cleanup();
}
void OnRetry(base::OnceClosure start_retry) override {
DCHECK(CalledOnValidThread());
DCHECK_EQ(status_, UR_IO_PENDING);
std::move(start_retry).Run();
}
void Cleanup() {
DCHECK(CalledOnValidThread());
DCHECK(url_request_);
client_ = nullptr;
request_context_ = nullptr;
// We may be canceled before the loader is created.
if (loader_) {
// Must delete the loader before the factory.
if (cleanup_immediately_) {
// Most SimpleURLLoader callbacks let us delete the URLLoader objects
// immediately.
loader_.reset();
loader_factory_getter_ = nullptr;
} else {
// Delete the URLLoader objects asynchronously on the correct thread.
task_runner_->DeleteSoon(FROM_HERE, std::move(loader_));
task_runner_->ReleaseSoon(FROM_HERE, std::move(loader_factory_getter_));
}
}
// We may be holding the last reference to |url_request_|, destruction of
// which will delete |this|. Use a local variable to keep |url_request_|
// alive until this method returns.
auto url_request = url_request_;
url_request_ = nullptr;
}
// Members only accessed on the initialization thread.
CefRefPtr<CefBrowserURLRequest> url_request_;
CefRefPtr<CefFrame> frame_;
CefRefPtr<CefRequestImpl> request_;
CefRefPtr<CefURLRequestClient> client_;
CefRefPtr<CefRequestContext> request_context_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
scoped_refptr<net_service::URLLoaderFactoryGetter> loader_factory_getter_;
std::unique_ptr<network::SimpleURLLoader> loader_;
CefURLRequest::Status status_ = UR_IO_PENDING;
CefRefPtr<CefResponseImpl> response_;
bool response_was_cached_ = false;
int64 upload_data_size_ = 0;
int64 download_data_size_ = -1;
bool got_upload_progress_complete_ = false;
bool cleanup_immediately_ = false;
// Must be the last member.
base::WeakPtrFactory<CefBrowserURLRequest::Context> weak_ptr_factory_;
};
// CefBrowserURLRequest -------------------------------------------------------
CefBrowserURLRequest::CefBrowserURLRequest(
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client,
CefRefPtr<CefRequestContext> request_context) {
context_.reset(new Context(this, frame, request, client, request_context));
}
CefBrowserURLRequest::~CefBrowserURLRequest() {}
bool CefBrowserURLRequest::Start() {
if (!VerifyContext())
return false;
return context_->Start();
}
CefRefPtr<CefRequest> CefBrowserURLRequest::GetRequest() {
if (!VerifyContext())
return NULL;
return context_->request();
}
CefRefPtr<CefURLRequestClient> CefBrowserURLRequest::GetClient() {
if (!VerifyContext())
return NULL;
return context_->client();
}
CefURLRequest::Status CefBrowserURLRequest::GetRequestStatus() {
if (!VerifyContext())
return UR_UNKNOWN;
return context_->status();
}
CefURLRequest::ErrorCode CefBrowserURLRequest::GetRequestError() {
if (!VerifyContext())
return ERR_NONE;
return context_->response()->GetError();
}
CefRefPtr<CefResponse> CefBrowserURLRequest::GetResponse() {
if (!VerifyContext())
return NULL;
return context_->response();
}
bool CefBrowserURLRequest::ResponseWasCached() {
if (!VerifyContext())
return false;
return context_->response_was_cached();
}
void CefBrowserURLRequest::Cancel() {
if (!VerifyContext())
return;
return context_->Cancel();
}
bool CefBrowserURLRequest::VerifyContext() {
if (!context_->CalledOnValidThread()) {
NOTREACHED() << "called on invalid thread";
return false;
}
return true;
}

View File

@@ -2,18 +2,21 @@
// reserved. Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_BROWSER_URLREQUEST_IMPL_H_
#define CEF_LIBCEF_BROWSER_BROWSER_URLREQUEST_IMPL_H_
#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_BROWSER_URLREQUEST_IMPL_H_
#define CEF_LIBCEF_BROWSER_NET_SERVICE_BROWSER_URLREQUEST_IMPL_H_
#include <memory>
#include "include/cef_urlrequest.h"
#include "base/memory/ref_counted.h"
class CefBrowserURLRequest : public CefURLRequest {
public:
class Context;
CefBrowserURLRequest(CefRefPtr<CefRequest> request,
// If |frame| is nullptr requests can still be intercepted but no
// browser/frame will be associated with them.
CefBrowserURLRequest(CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefURLRequestClient> client,
CefRefPtr<CefRequestContext> request_context);
~CefBrowserURLRequest() override;
@@ -32,9 +35,9 @@ class CefBrowserURLRequest : public CefURLRequest {
private:
bool VerifyContext();
scoped_refptr<Context> context_;
std::unique_ptr<Context> context_;
IMPLEMENT_REFCOUNTING(CefBrowserURLRequest);
};
#endif // CEF_LIBCEF_BROWSER_BROWSER_URLREQUEST_IMPL_H_
#endif // CEF_LIBCEF_BROWSER_NET_SERVICE_BROWSER_URLREQUEST_IMPL_H_

View File

@@ -204,6 +204,8 @@ class InterceptedRequest : public network::mojom::URLLoader,
void SendErrorCallback(int error_code, bool safebrowsing_hit);
void OnUploadProgressACK();
ProxyURLLoaderFactory* const factory_;
const RequestId id_;
const uint32_t options_;
@@ -223,6 +225,9 @@ class InterceptedRequest : public network::mojom::URLLoader,
network::URLLoaderCompletionStatus status_;
bool got_loader_error_ = false;
// Used for rate limiting OnUploadProgress callbacks.
bool waiting_for_upload_progress_ack_ = false;
network::ResourceRequest request_;
network::ResourceResponseHead current_response_;
scoped_refptr<net::HttpResponseHeaders> override_headers_;
@@ -486,8 +491,25 @@ void InterceptedRequest::OnReceiveRedirect(
void InterceptedRequest::OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback callback) {
target_client_->OnUploadProgress(current_position, total_size,
std::move(callback));
// Implement our own rate limiting for OnUploadProgress calls.
if (!waiting_for_upload_progress_ack_) {
waiting_for_upload_progress_ack_ = true;
target_client_->OnUploadProgress(
current_position, total_size,
base::BindOnce(&InterceptedRequest::OnUploadProgressACK,
weak_factory_.GetWeakPtr()));
}
// Always execute the callback immediately to avoid a race between
// URLLoaderClient_OnUploadProgress_ProxyToResponder::Run() (which would
// otherwise be blocked on the target client executing the callback) and
// CallOnComplete(). If CallOnComplete() is executed first the interface pipe
// will be closed and the callback destructor will generate an assertion like:
// "URLLoaderClient::OnUploadProgressCallback was destroyed without first
// either being run or its corresponding binding being closed. It is an error
// to drop response callbacks which still correspond to an open interface
// pipe."
std::move(callback).Run();
}
void InterceptedRequest::OnReceiveCachedMetadata(
@@ -524,16 +546,15 @@ void InterceptedRequest::FollowRedirect(
OnProcessRequestHeaders(new_url.value_or(GURL()), &modified_headers,
&removed_headers);
if (target_loader_) {
target_loader_->FollowRedirect(removed_headers, modified_headers, new_url);
}
// If |OnURLLoaderClientError| was called then we're just waiting for the
// connection error handler of |proxied_loader_binding_|. Don't restart the
// job since that'll create another URLLoader.
if (!target_client_)
return;
// Normally we would call FollowRedirect on the target loader and it would
// begin loading the redirected request. However, the client might want to
// intercept that request so restart the job instead.
Restart();
}
@@ -937,6 +958,11 @@ void InterceptedRequest::SendErrorCallback(int error_code,
safebrowsing_hit);
}
void InterceptedRequest::OnUploadProgressACK() {
DCHECK(waiting_for_upload_progress_ack_);
waiting_for_upload_progress_ack_ = false;
}
//==============================
// InterceptedRequestHandler
//==============================

View File

@@ -18,6 +18,9 @@
#include "libcef/common/request_impl.h"
#include "libcef/common/response_impl.h"
#include "chrome/common/chrome_features.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
@@ -77,12 +80,20 @@ std::string GetAcceptLanguageList(content::BrowserContext* browser_context,
return CefString(&browser_settings.accept_language_list);
}
}
const CefRequestContextSettings& context_settings =
static_cast<CefBrowserContext*>(browser_context)->GetSettings();
if (context_settings.accept_language_list.length > 0) {
return CefString(&context_settings.accept_language_list);
}
return "en-US,en";
// This defaults to the value from CefRequestContextSettings via
// browser_prefs::CreatePrefService().
auto prefs = Profile::FromBrowserContext(browser_context)->GetPrefs();
return prefs->GetString(language::prefs::kAcceptLanguages);
}
// Match the logic in chrome/browser/net/profile_network_context_service.cc.
std::string ComputeAcceptLanguageFromPref(const std::string& language_pref) {
std::string accept_languages_str =
base::FeatureList::IsEnabled(features::kUseNewAcceptLanguageHeader)
? net::HttpUtil::ExpandLanguageList(language_pref)
: language_pref;
return net::HttpUtil::GenerateAcceptLanguageHeader(accept_languages_str);
}
class InterceptedRequestHandlerWrapper : public InterceptedRequestHandler {
@@ -167,9 +178,11 @@ class InterceptedRequestHandlerWrapper : public InterceptedRequestHandler {
is_external_ = is_external;
// Default values for standard headers.
accept_language_ = net::HttpUtil::GenerateAcceptLanguageHeader(
accept_language_ = ComputeAcceptLanguageFromPref(
GetAcceptLanguageList(browser_context, browser));
DCHECK(!accept_language_.empty());
user_agent_ = CefContentClient::Get()->browser()->GetUserAgent();
DCHECK(!user_agent_.empty());
CEF_POST_TASK(
CEF_IOT,

View File

@@ -0,0 +1,127 @@
// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
// Copyright (c) 2018 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_service/url_loader_factory_getter.h"
#include "libcef/browser/content_browser_client.h"
#include "libcef/browser/thread_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
namespace net_service {
// Based on CreateDownloadURLLoaderFactoryGetter from
// content/browser/download/download_manager_impl.cc.
// static
scoped_refptr<URLLoaderFactoryGetter> URLLoaderFactoryGetter::Create(
content::RenderFrameHost* render_frame_host,
content::BrowserContext* browser_context) {
CEF_REQUIRE_UIT();
DCHECK(browser_context);
// Call this early because newly created BrowserContexts may need to
// initialize additional state, and that should be done on the UI thread
// instead of potentially racing with the WillCreateURLLoaderFactory
// implementation.
auto loader_factory =
content::BrowserContext::GetDefaultStoragePartition(browser_context)
->GetURLLoaderFactoryForBrowserProcess();
network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info;
network::mojom::URLLoaderFactoryRequest proxy_factory_request;
// Create an intermediate pipe that can be used to proxy the request's
// URLLoaderFactory.
network::mojom::URLLoaderFactoryPtrInfo maybe_proxy_factory_ptr_info;
network::mojom::URLLoaderFactoryRequest maybe_proxy_factory_request =
MakeRequest(&maybe_proxy_factory_ptr_info);
bool should_proxy = false;
int render_process_id = -1;
if (render_frame_host) {
render_process_id = render_frame_host->GetProcess()->GetID();
// Allow DevTools to potentially inject itself into the proxy pipe.
should_proxy =
content::devtools_instrumentation::WillCreateURLLoaderFactory(
static_cast<content::RenderFrameHostImpl*>(render_frame_host),
false /* is_navigation */, false /* is_download */,
&maybe_proxy_factory_request);
}
// Allow the Content embedder to inject itself if it wants to.
should_proxy |= CefContentBrowserClient::Get()->WillCreateURLLoaderFactory(
browser_context, render_frame_host, render_process_id,
false /* is_navigation */, false /* is_download */, url::Origin(),
&maybe_proxy_factory_request, nullptr /* header_client */,
nullptr /* bypass_redirect_checks */);
// If anyone above indicated that they care about proxying, pass the
// intermediate pipe along to the URLLoaderFactoryGetter.
if (should_proxy) {
proxy_factory_ptr_info = std::move(maybe_proxy_factory_ptr_info);
proxy_factory_request = std::move(maybe_proxy_factory_request);
}
return base::WrapRefCounted(new URLLoaderFactoryGetter(
loader_factory->Clone(), std::move(proxy_factory_ptr_info),
std::move(proxy_factory_request)));
}
// Based on NetworkDownloadURLLoaderFactoryGetter from
// content/browser/download/network_download_url_loader_factory_getter.cc.
scoped_refptr<network::SharedURLLoaderFactory>
URLLoaderFactoryGetter::GetURLLoaderFactory() {
// On first call we associate with the current thread.
if (!task_runner_) {
task_runner_ = base::ThreadTaskRunnerHandle::Get();
} else {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
}
if (lazy_factory_)
return lazy_factory_;
// Bind on the current thread.
auto loader_factory =
network::SharedURLLoaderFactory::Create(std::move(loader_factory_info_));
if (proxy_factory_request_.is_pending()) {
loader_factory->Clone(std::move(proxy_factory_request_));
lazy_factory_ =
base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
std::move(proxy_factory_ptr_info_));
} else {
lazy_factory_ = loader_factory;
}
return lazy_factory_;
}
URLLoaderFactoryGetter::URLLoaderFactoryGetter(
std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info,
network::mojom::URLLoaderFactoryRequest proxy_factory_request)
: loader_factory_info_(std::move(loader_factory_info)),
proxy_factory_ptr_info_(std::move(proxy_factory_ptr_info)),
proxy_factory_request_(std::move(proxy_factory_request)) {}
URLLoaderFactoryGetter::~URLLoaderFactoryGetter() = default;
void URLLoaderFactoryGetter::DeleteOnCorrectThread() const {
if (task_runner_ && !task_runner_->RunsTasksInCurrentSequence()) {
task_runner_->DeleteSoon(FROM_HERE, this);
return;
}
delete this;
}
} // namespace net_service

View File

@@ -0,0 +1,76 @@
// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
// Copyright (c) 2018 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.
#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_URL_LOADER_FACTORY_GETTER_H_
#define CEF_LIBCEF_BROWSER_NET_SERVICE_URL_LOADER_FACTORY_GETTER_H_
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/sequenced_task_runner_helpers.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
namespace content {
class BrowserContext;
class RenderFrameHost;
} // namespace content
namespace network {
class SharedURLLoaderFactory;
class SharedURLLoaderFactoryInfo;
} // namespace network
namespace net_service {
struct URLLoaderFactoryGetterDeleter;
// Helper class for retrieving a URLLoaderFactory that can be bound on any
// thread, and that correctly handles proxied requests.
class URLLoaderFactoryGetter
: public base::RefCountedThreadSafe<URLLoaderFactoryGetter,
URLLoaderFactoryGetterDeleter> {
public:
// Create a URLLoaderFactoryGetter on the UI thread.
// |render_frame_host| may be nullptr.
static scoped_refptr<URLLoaderFactoryGetter> Create(
content::RenderFrameHost* render_frame_host,
content::BrowserContext* browser_context);
// Create a SharedURLLoaderFactory on the current thread. All future calls
// to this method must be on the same thread.
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
private:
friend class base::DeleteHelper<URLLoaderFactoryGetter>;
friend class base::RefCountedThreadSafe<URLLoaderFactoryGetter,
URLLoaderFactoryGetterDeleter>;
friend struct URLLoaderFactoryGetterDeleter;
URLLoaderFactoryGetter(
std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info,
network::mojom::URLLoaderFactoryRequest proxy_factory_request);
~URLLoaderFactoryGetter();
void DeleteOnCorrectThread() const;
std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info_;
scoped_refptr<network::SharedURLLoaderFactory> lazy_factory_;
network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info_;
network::mojom::URLLoaderFactoryRequest proxy_factory_request_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(URLLoaderFactoryGetter);
};
struct URLLoaderFactoryGetterDeleter {
static void Destruct(const URLLoaderFactoryGetter* factory_getter) {
factory_getter->DeleteOnCorrectThread();
}
};
} // namespace net_service
#endif // CEF_LIBCEF_BROWSER_NET_SERVICE_URL_LOADER_FACTORY_GETTER_H_

View File

@@ -4,6 +4,7 @@
#include "libcef/browser/prefs/browser_prefs.h"
#include "libcef/browser/browser_context.h"
#include "libcef/browser/media_capture_devices_dispatcher.h"
#include "libcef/browser/net/url_request_context_getter.h"
#include "libcef/browser/prefs/pref_store.h"
@@ -36,6 +37,7 @@
#include "components/google/core/browser/google_url_tracker.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/language/core/browser/language_prefs.h"
#include "components/language/core/browser/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_filter.h"
@@ -58,6 +60,19 @@
namespace browser_prefs {
namespace {
std::string GetAcceptLanguageList(Profile* profile) {
const CefRequestContextSettings& context_settings =
static_cast<CefBrowserContext*>(profile)->GetSettings();
if (context_settings.accept_language_list.length > 0) {
return CefString(&context_settings.accept_language_list);
}
return std::string();
}
} // namespace
const char kUserPrefsFileName[] = "UserPrefs.json";
std::unique_ptr<PrefService> CreatePrefService(Profile* profile,
@@ -230,6 +245,14 @@ std::unique_ptr<PrefService> CreatePrefService(Profile* profile,
// Based on DevToolsWindow::RegisterProfilePrefs.
registry->RegisterDictionaryPref(prefs::kDevToolsPreferences);
registry->RegisterDictionaryPref(prefs::kDevToolsEditedFiles);
// Language preferences. Used by ProfileNetworkContextService and
// InterceptedRequestHandlerWrapper.
const std::string& accept_language_list = GetAcceptLanguageList(profile);
if (!accept_language_list.empty()) {
registry->SetDefaultPrefValue(language::prefs::kAcceptLanguages,
base::Value(accept_language_list));
}
}
// Build the PrefService that manages the PrefRegistry and PrefStores.