#include "libcef/browser/net_service/resource_request_handler_wrapper.h"
#include "libcef/browser/browser_host_base.h"
#include "libcef/browser/context.h"
#include "libcef/browser/iothread_state.h"
#include "libcef/browser/net_service/cookie_helper.h"
#include "libcef/browser/net_service/proxy_url_loader_factory.h"
#include "libcef/browser/net_service/resource_handler_wrapper.h"
#include "libcef/browser/net_service/response_filter_wrapper.h"
#include "libcef/browser/prefs/browser_prefs.h"
#include "libcef/browser/thread_util.h"
#include "libcef/common/app_manager.h"
#include "libcef/common/net/scheme_registration.h"
#include "libcef/common/net_service/net_service_util.h"
#include "libcef/common/request_impl.h"
#include "libcef/common/response_impl.h"
#include "chrome/browser/profiles/profile.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
#include "ui/base/page_transition_types.h"
#include "url/origin.h"
namespace net_service {
namespace {
const int kLoadNoCookiesFlags =
net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
class RequestCallbackWrapper : public CefCallback {
public:
using Callback = base::OnceCallback<void(bool )>;
explicit RequestCallbackWrapper(Callback callback)
: callback_(std::move(callback)),
work_thread_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
RequestCallbackWrapper(const RequestCallbackWrapper&) = delete;
RequestCallbackWrapper& operator=(const RequestCallbackWrapper&) = delete;
~RequestCallbackWrapper() override {
if (!callback_.is_null()) {
work_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(callback_), true));
}
}
void Continue() override { ContinueNow(true); }
void Cancel() override { ContinueNow(false); }
private:
void ContinueNow(bool allow) {
if (!work_thread_task_runner_->RunsTasksInCurrentSequence()) {
work_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&RequestCallbackWrapper::ContinueNow, this, allow));
return;
}
if (!callback_.is_null()) {
std::move(callback_).Run(allow);
}
}
Callback callback_;
scoped_refptr<base::SequencedTaskRunner> work_thread_task_runner_;
IMPLEMENT_REFCOUNTING(RequestCallbackWrapper);
};
class InterceptedRequestHandlerWrapper : public InterceptedRequestHandler {
public:
struct RequestState {
RequestState() {}
void Reset(CefRefPtr<CefResourceRequestHandler> handler,
CefRefPtr<CefSchemeHandlerFactory> scheme_factory,
CefRefPtr<CefRequestImpl> request,
bool request_was_redirected,
CancelRequestCallback cancel_callback) {
handler_ = handler;
scheme_factory_ = scheme_factory;
cookie_filter_ = nullptr;
pending_request_ = request;
pending_response_ = nullptr;
request_was_redirected_ = request_was_redirected;
was_custom_handled_ = false;
cancel_callback_ = std::move(cancel_callback);
}
CefRefPtr<CefResourceRequestHandler> handler_;
CefRefPtr<CefSchemeHandlerFactory> scheme_factory_;
CefRefPtr<CefCookieAccessFilter> cookie_filter_;
CefRefPtr<CefRequestImpl> pending_request_;
CefRefPtr<CefResponseImpl> pending_response_;
bool request_was_redirected_ = false;
bool was_custom_handled_ = false;
CancelRequestCallback cancel_callback_;
};
struct PendingRequest {
PendingRequest(int32_t request_id,
network::ResourceRequest* request,
bool request_was_redirected,
OnBeforeRequestResultCallback callback,
CancelRequestCallback cancel_callback)
: id_(request_id),
request_(request),
request_was_redirected_(request_was_redirected),
callback_(std::move(callback)),
cancel_callback_(std::move(cancel_callback)) {}
~PendingRequest() {
if (cancel_callback_) {
std::move(cancel_callback_).Run(net::ERR_ABORTED);
}
}
void Run(InterceptedRequestHandlerWrapper* self) {
self->OnBeforeRequest(id_, request_, request_was_redirected_,
std::move(callback_), std::move(cancel_callback_));
}
const int32_t id_;
network::ResourceRequest* const request_;
const bool request_was_redirected_;
OnBeforeRequestResultCallback callback_;
CancelRequestCallback cancel_callback_;
};
class DestructionObserver : public CefBrowserHostBase::Observer,
public CefContext::Observer {
public:
explicit DestructionObserver(CefBrowserHostBase* browser) {
if (browser) {
browser_info_ = browser->browser_info();
browser->AddObserver(this);
} else {
CefContext::Get()->AddObserver(this);
}
}
DestructionObserver(const DestructionObserver&) = delete;
DestructionObserver& operator=(const DestructionObserver&) = delete;
virtual ~DestructionObserver() {
CEF_REQUIRE_UIT();
if (!registered_)
return;
if (browser_info_) {
auto browser = browser_info_->browser();
if (browser)
browser->RemoveObserver(this);
} else if (CefContext::Get()) {
CefContext::Get()->RemoveObserver(this);
}
}
void SetWrapper(base::WeakPtr<InterceptedRequestHandlerWrapper> wrapper) {
CEF_REQUIRE_IOT();
wrapper_ = wrapper;
}
void OnBrowserDestroyed(CefBrowserHostBase* browser) override {
CEF_REQUIRE_UIT();
browser->RemoveObserver(this);
registered_ = false;
browser_info_ = nullptr;
NotifyOnDestroyed();
}
void OnContextDestroyed() override {
CEF_REQUIRE_UIT();
CefContext::Get()->RemoveObserver(this);
registered_ = false;
NotifyOnDestroyed();
}
private:
void NotifyOnDestroyed() {
if (wrapper_.MaybeValid()) {
CEF_POST_TASK(
CEF_IOT,
base::BindOnce(&InterceptedRequestHandlerWrapper::OnDestroyed,
wrapper_));
}
}
scoped_refptr<CefBrowserInfo> browser_info_;
bool registered_ = true;
base::WeakPtr<InterceptedRequestHandlerWrapper> wrapper_;
};
struct InitState {
InitState() {}
~InitState() {
if (destruction_observer_) {
if (initialized_) {
destruction_observer_->SetWrapper(nullptr);
}
DeleteDestructionObserver();
}
}
void Initialize(content::BrowserContext* browser_context,
CefRefPtr<CefBrowserHostBase> browser,
CefRefPtr<CefFrame> frame,
const content::GlobalRenderFrameHostId& global_id,
bool is_navigation,
bool is_download,
const url::Origin& request_initiator,
const base::RepeatingClosure& unhandled_request_callback) {
CEF_REQUIRE_UIT();
auto profile = Profile::FromBrowserContext(browser_context);
auto cef_browser_context = CefBrowserContext::FromProfile(profile);
browser_context_getter_ = cef_browser_context->getter();
iothread_state_ = cef_browser_context->iothread_state();
CHECK(iothread_state_);
cookieable_schemes_ = cef_browser_context->GetCookieableSchemes();
destruction_observer_.reset(new DestructionObserver(browser.get()));
if (browser) {
browser_ = browser;
frame_ = frame;
}
global_id_ = global_id;
is_navigation_ = is_navigation;
is_download_ = is_download;
request_initiator_ = request_initiator.Serialize();
unhandled_request_callback_ = unhandled_request_callback;
accept_language_ = browser_prefs::GetAcceptLanguageList(
cef_browser_context, browser.get(), true);
DCHECK(!accept_language_.empty());
user_agent_ =
CefAppManager::Get()->GetContentClient()->browser()->GetUserAgent();
DCHECK(!user_agent_.empty());
}
void DeleteDestructionObserver() {
DCHECK(destruction_observer_);
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(&InitState::DeleteDestructionObserverOnUIThread,
std::move(destruction_observer_)));
}
static void DeleteDestructionObserverOnUIThread(
std::unique_ptr<DestructionObserver> observer) {}
CefBrowserContext::Getter browser_context_getter_;
bool initialized_ = false;
CefRefPtr<CefBrowserHostBase> browser_;
CefRefPtr<CefFrame> frame_;
scoped_refptr<CefIOThreadState> iothread_state_;
CefBrowserContext::CookieableSchemes cookieable_schemes_;
content::GlobalRenderFrameHostId global_id_;
bool is_navigation_ = true;
bool is_download_ = false;
CefString request_initiator_;
base::RepeatingClosure unhandled_request_callback_;
std::string accept_language_;
std::string user_agent_;
mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
url_loader_network_observer_;
bool did_try_create_url_loader_network_observer_ = false;
std::unique_ptr<DestructionObserver> destruction_observer_;
};
struct InitHelper : base::RefCountedThreadSafe<InitHelper> {
public:
explicit InitHelper(InterceptedRequestHandlerWrapper* wrapper)
: wrapper_(wrapper) {}
InitHelper(const InitHelper&) = delete;
InitHelper& operator=(const InitHelper&) = delete;
void MaybeSetInitialized(std::unique_ptr<InitState> init_state) {
CEF_POST_TASK(CEF_IOT, base::BindOnce(&InitHelper::SetInitialized, this,
std::move(init_state)));
}
void Disconnect() {
base::AutoLock lock_scope(lock_);
wrapper_ = nullptr;
}
private:
void SetInitialized(std::unique_ptr<InitState> init_state) {
base::AutoLock lock_scope(lock_);
if (!wrapper_)
return;
wrapper_->SetInitialized(std::move(init_state));
wrapper_ = nullptr;
}
base::Lock lock_;
InterceptedRequestHandlerWrapper* wrapper_;
};
InterceptedRequestHandlerWrapper()
: init_helper_(base::MakeRefCounted<InitHelper>(this)),
weak_ptr_factory_(this) {}
InterceptedRequestHandlerWrapper(const InterceptedRequestHandlerWrapper&) =
delete;
InterceptedRequestHandlerWrapper& operator=(
const InterceptedRequestHandlerWrapper&) = delete;
~InterceptedRequestHandlerWrapper() override {
CEF_REQUIRE_IOT();
DCHECK(request_map_.empty());
init_helper_->Disconnect();
}
scoped_refptr<InitHelper> init_helper() const { return init_helper_; }
void SetInitialized(std::unique_ptr<InitState> init_state) {
CEF_REQUIRE_IOT();
DCHECK(!init_state_);
init_state_ = std::move(init_state);
if (init_state_->browser_) {
if (!init_state_->browser_->browser_info()->browser()) {
OnDestroyed();
return;
}
} else if (!CONTEXT_STATE_VALID()) {
OnDestroyed();
return;
}
init_state_->initialized_ = true;
init_state_->destruction_observer_->SetWrapper(
weak_ptr_factory_.GetWeakPtr());
if (!pending_requests_.empty()) {
for (const auto& request : pending_requests_)
request->Run(this);
pending_requests_.clear();
}
}
static void TryCreateURLLoaderNetworkObserver(
std::unique_ptr<PendingRequest> pending_request,
CefRefPtr<CefFrame> frame,
const CefBrowserContext::Getter& browser_context_getter,
base::WeakPtr<InterceptedRequestHandlerWrapper> self) {
CEF_REQUIRE_UIT();
mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
url_loader_network_observer;
if (frame) {
content::RenderFrameHost* rfh =
static_cast<CefFrameHostImpl*>(frame.get())->GetRenderFrameHost();
if (rfh) {
url_loader_network_observer =
static_cast<content::RenderFrameHostImpl*>(rfh)
->CreateURLLoaderNetworkObserver();
}
} else {
auto cef_browser_context = browser_context_getter.Run();
auto browser_context = cef_browser_context
? cef_browser_context->AsBrowserContext()
: nullptr;
if (browser_context) {
url_loader_network_observer =
static_cast<content::StoragePartitionImpl*>(
browser_context->GetDefaultStoragePartition())
->CreateAuthCertObserverForServiceWorker();
}
}
CEF_POST_TASK(CEF_IOT,
base::BindOnce(&InterceptedRequestHandlerWrapper::
ContinueCreateURLLoaderNetworkObserver,
self, std::move(pending_request),
std::move(url_loader_network_observer)));
}
void ContinueCreateURLLoaderNetworkObserver(
std::unique_ptr<PendingRequest> pending_request,
mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
url_loader_network_observer) {
CEF_REQUIRE_IOT();
DCHECK(!init_state_->did_try_create_url_loader_network_observer_);
init_state_->did_try_create_url_loader_network_observer_ = true;
init_state_->url_loader_network_observer_ =
std::move(url_loader_network_observer);
pending_request->Run(this);
}
void OnBeforeRequest(int32_t request_id,
network::ResourceRequest* request,
bool request_was_redirected,
OnBeforeRequestResultCallback callback,
CancelRequestCallback cancel_callback) override {
CEF_REQUIRE_IOT();
if (shutting_down_) {
std::move(cancel_callback).Run(net::ERR_ABORTED);
return;
}
if (!init_state_) {
pending_requests_.push_back(std::make_unique<PendingRequest>(
request_id, request, request_was_redirected, std::move(callback),
std::move(cancel_callback)));
return;
}
if (request->trusted_params &&
!request->trusted_params->url_loader_network_observer &&
!init_state_->did_try_create_url_loader_network_observer_) {
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(&InterceptedRequestHandlerWrapper::
TryCreateURLLoaderNetworkObserver,
std::make_unique<PendingRequest>(
request_id, request, request_was_redirected,
std::move(callback), std::move(cancel_callback)),
init_state_->frame_,
init_state_->browser_context_getter_,
weak_ptr_factory_.GetWeakPtr()));
return;
}
RequestState* state = GetOrCreateState(request_id);
if (init_state_->did_try_create_url_loader_network_observer_) {
if (init_state_->url_loader_network_observer_) {
request->trusted_params->url_loader_network_observer =
std::move(init_state_->url_loader_network_observer_);
}
init_state_->did_try_create_url_loader_network_observer_ = false;
}
request->headers.SetHeaderIfMissing(
net::HttpRequestHeaders::kAcceptLanguage,
init_state_->accept_language_);
request->headers.SetHeaderIfMissing(net::HttpRequestHeaders::kUserAgent,
init_state_->user_agent_);
const bool is_external = IsExternalRequest(request);
bool intercept_only = is_external;
CefRefPtr<CefRequestImpl> requestPtr;
CefRefPtr<CefResourceRequestHandler> handler =
GetHandler(request_id, request, &intercept_only, requestPtr);
CefRefPtr<CefSchemeHandlerFactory> scheme_factory =
init_state_->iothread_state_->GetSchemeHandlerFactory(request->url);
if (scheme_factory && !requestPtr) {
requestPtr = MakeRequest(request, request_id, true);
}
const bool maybe_intercept_request = handler || scheme_factory;
if (!maybe_intercept_request && requestPtr)
requestPtr = nullptr;
state->Reset(handler, scheme_factory, requestPtr, request_was_redirected,
std::move(cancel_callback));
if (handler) {
state->cookie_filter_ = handler->GetCookieAccessFilter(
init_state_->browser_, init_state_->frame_, requestPtr.get());
}
auto exec_callback =
base::BindOnce(std::move(callback), maybe_intercept_request,
is_external ? true : intercept_only);
if (!maybe_intercept_request) {
std::move(exec_callback).Run();
return;
}
MaybeLoadCookies(request_id, state, request, std::move(exec_callback));
}
void MaybeLoadCookies(int32_t request_id,
RequestState* state,
network::ResourceRequest* request,
base::OnceClosure callback) {
CEF_REQUIRE_IOT();
if (!cookie_helper::IsCookieableScheme(request->url,
init_state_->cookieable_schemes_)) {
std::move(callback).Run();
return;
}
auto allow_cookie_callback =
state->cookie_filter_
? base::BindRepeating(
&InterceptedRequestHandlerWrapper::AllowCookieLoad,
weak_ptr_factory_.GetWeakPtr(), request_id)
: base::BindRepeating(
&InterceptedRequestHandlerWrapper::AllowCookieAlways);
auto done_cookie_callback = base::BindOnce(
&InterceptedRequestHandlerWrapper::ContinueWithLoadedCookies,
weak_ptr_factory_.GetWeakPtr(), request_id, request,
std::move(callback));
cookie_helper::LoadCookies(init_state_->browser_context_getter_, *request,
allow_cookie_callback,
std::move(done_cookie_callback));
}
static void AllowCookieAlways(const net::CanonicalCookie& cookie,
bool* allow) {
*allow = true;
}
void AllowCookieLoad(int32_t request_id,
const net::CanonicalCookie& cookie,
bool* allow) {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
return;
}
DCHECK(state->cookie_filter_);
CefCookie cef_cookie;
if (net_service::MakeCefCookie(cookie, cef_cookie)) {
*allow = state->cookie_filter_->CanSendCookie(
init_state_->browser_, init_state_->frame_,
state->pending_request_.get(), cef_cookie);
}
}
void ContinueWithLoadedCookies(int32_t request_id,
network::ResourceRequest* request,
base::OnceClosure callback,
int total_count,
net::CookieList allowed_cookies) {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
return;
}
if (state->cookie_filter_) {
request->load_flags |= kLoadNoCookiesFlags;
}
if (!allowed_cookies.empty()) {
const std::string& cookie_line =
net::CanonicalCookie::BuildCookieLine(allowed_cookies);
request->headers.SetHeader(net::HttpRequestHeaders::kCookie, cookie_line);
state->pending_request_->SetReadOnly(false);
state->pending_request_->SetHeaderByName(net::HttpRequestHeaders::kCookie,
cookie_line, true);
state->pending_request_->SetReadOnly(true);
}
std::move(callback).Run();
}
void ShouldInterceptRequest(
int32_t request_id,
network::ResourceRequest* request,
ShouldInterceptRequestResultCallback callback) override {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
return;
}
DCHECK(state->handler_ || state->scheme_factory_);
DCHECK(state->pending_request_);
if (state->handler_) {
state->pending_request_->SetReadOnly(false);
state->pending_request_->SetTrackChanges(true,
true );
CefRefPtr<RequestCallbackWrapper> callbackPtr =
new RequestCallbackWrapper(base::BindOnce(
&InterceptedRequestHandlerWrapper::ContinueShouldInterceptRequest,
weak_ptr_factory_.GetWeakPtr(), request_id,
base::Unretained(request), std::move(callback)));
cef_return_value_t retval = state->handler_->OnBeforeResourceLoad(
init_state_->browser_, init_state_->frame_,
state->pending_request_.get(), callbackPtr.get());
if (retval != RV_CONTINUE_ASYNC) {
if (retval == RV_CONTINUE) {
callbackPtr->Continue();
} else {
callbackPtr->Cancel();
}
}
} else {
ContinueShouldInterceptRequest(request_id, request, std::move(callback),
true);
}
}
void ContinueShouldInterceptRequest(
int32_t request_id,
network::ResourceRequest* request,
ShouldInterceptRequestResultCallback callback,
bool allow) {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
return;
}
DCHECK(state->handler_ || state->scheme_factory_);
DCHECK(state->pending_request_);
if (state->handler_) {
if (allow) {
state->pending_request_->Get(request, true );
}
const bool redirect =
(state->pending_request_->GetChanges() & CefRequestImpl::kChangedUrl);
if (redirect) {
state->pending_request_->RevertChanges();
}
state->pending_request_->SetReadOnly(true);
state->pending_request_->SetTrackChanges(false);
if (!allow) {
if (state->cancel_callback_) {
std::move(state->cancel_callback_).Run(net::ERR_ABORTED);
}
return;
}
if (redirect) {
std::move(callback).Run(nullptr);
return;
}
}
CefRefPtr<CefResourceHandler> resource_handler;
if (state->handler_) {
resource_handler = state->handler_->GetResourceHandler(
init_state_->browser_, init_state_->frame_,
state->pending_request_.get());
}
if (!resource_handler && state->scheme_factory_) {
resource_handler = state->scheme_factory_->Create(
init_state_->browser_, init_state_->frame_, request->url.scheme(),
state->pending_request_.get());
}
std::unique_ptr<ResourceResponse> resource_response;
if (resource_handler) {
resource_response = CreateResourceResponse(request_id, resource_handler);
DCHECK(resource_response);
state->was_custom_handled_ = true;
} else {
request->headers.RemoveHeader(net::HttpRequestHeaders::kAcceptLanguage);
}
std::move(callback).Run(std::move(resource_response));
}
void ProcessResponseHeaders(int32_t request_id,
const network::ResourceRequest& request,
const GURL& redirect_url,
net::HttpResponseHeaders* headers) override {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
return;
}
if (!state->handler_)
return;
if (!state->pending_response_)
state->pending_response_ = new CefResponseImpl();
else
state->pending_response_->SetReadOnly(false);
if (headers)
state->pending_response_->SetResponseHeaders(*headers);
state->pending_response_->SetReadOnly(true);
}
void OnRequestResponse(int32_t request_id,
network::ResourceRequest* request,
net::HttpResponseHeaders* headers,
absl::optional<net::RedirectInfo> redirect_info,
OnRequestResponseResultCallback callback) override {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
return;
}
if (state->cookie_filter_) {
request->load_flags &= ~kLoadNoCookiesFlags;
}
if (!state->handler_) {
MaybeSaveCookies(
request_id, state, request, headers,
base::BindOnce(
std::move(callback), ResponseMode::CONTINUE, nullptr,
redirect_info.has_value() ? redirect_info->new_url : GURL()));
return;
}
DCHECK(state->pending_request_);
DCHECK(state->pending_response_);
if (redirect_info.has_value()) {
HandleRedirect(request_id, state, request, headers, *redirect_info,
std::move(callback));
} else {
HandleResponse(request_id, state, request, headers, std::move(callback));
}
}
void HandleRedirect(int32_t request_id,
RequestState* state,
network::ResourceRequest* request,
net::HttpResponseHeaders* headers,
const net::RedirectInfo& redirect_info,
OnRequestResponseResultCallback callback) {
GURL new_url = redirect_info.new_url;
CefString newUrl = redirect_info.new_url.spec();
CefString oldUrl = newUrl;
bool url_changed = false;
state->handler_->OnResourceRedirect(
init_state_->browser_, init_state_->frame_,
state->pending_request_.get(), state->pending_response_.get(), newUrl);
if (newUrl != oldUrl) {
const GURL& url = redirect_info.new_url.Resolve(newUrl.ToString());
if (url.is_valid()) {
url_changed = true;
new_url = url;
}
}
state->pending_request_->SetReadOnly(false);
state->pending_request_->Set(redirect_info);
if (url_changed) {
state->pending_request_->SetURL(new_url.spec());
}
state->pending_request_->SetReadOnly(true);
auto exec_callback = base::BindOnce(
std::move(callback), ResponseMode::CONTINUE, nullptr, new_url);
MaybeSaveCookies(request_id, state, request, headers,
std::move(exec_callback));
}
void HandleResponse(int32_t request_id,
RequestState* state,
network::ResourceRequest* request,
net::HttpResponseHeaders* headers,
OnRequestResponseResultCallback callback) {
state->pending_request_->SetReadOnly(false);
state->pending_request_->SetTrackChanges(true, true );
auto response_mode = ResponseMode::CONTINUE;
GURL new_url;
if (state->handler_->OnResourceResponse(
init_state_->browser_, init_state_->frame_,
state->pending_request_.get(), state->pending_response_.get())) {
const auto changes = state->pending_request_->GetChanges();
if (changes) {
state->pending_request_->Get(request, true );
if (changes & CefRequestImpl::kChangedUrl) {
new_url = GURL(state->pending_request_->GetURL().ToString());
} else {
response_mode = ResponseMode::RESTART;
}
}
}
state->pending_request_->RevertChanges();
state->pending_request_->SetReadOnly(true);
state->pending_request_->SetTrackChanges(false);
auto exec_callback =
base::BindOnce(std::move(callback), response_mode, nullptr, new_url);
if (response_mode == ResponseMode::RESTART) {
std::move(exec_callback).Run();
return;
}
MaybeSaveCookies(request_id, state, request, headers,
std::move(exec_callback));
}
void MaybeSaveCookies(int32_t request_id,
RequestState* state,
network::ResourceRequest* request,
net::HttpResponseHeaders* headers,
base::OnceClosure callback) {
CEF_REQUIRE_IOT();
if (!state->cookie_filter_ && !state->was_custom_handled_) {
std::move(callback).Run();
return;
}
if (!cookie_helper::IsCookieableScheme(request->url,
init_state_->cookieable_schemes_)) {
std::move(callback).Run();
return;
}
auto allow_cookie_callback =
state->cookie_filter_
? base::BindRepeating(
&InterceptedRequestHandlerWrapper::AllowCookieSave,
weak_ptr_factory_.GetWeakPtr(), request_id)
: base::BindRepeating(
&InterceptedRequestHandlerWrapper::AllowCookieAlways);
auto done_cookie_callback = base::BindOnce(
&InterceptedRequestHandlerWrapper::ContinueWithSavedCookies,
weak_ptr_factory_.GetWeakPtr(), request_id, std::move(callback));
cookie_helper::SaveCookies(init_state_->browser_context_getter_, *request,
headers, allow_cookie_callback,
std::move(done_cookie_callback));
}
void AllowCookieSave(int32_t request_id,
const net::CanonicalCookie& cookie,
bool* allow) {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
return;
}
DCHECK(state->cookie_filter_);
CefCookie cef_cookie;
if (net_service::MakeCefCookie(cookie, cef_cookie)) {
*allow = state->cookie_filter_->CanSaveCookie(
init_state_->browser_, init_state_->frame_,
state->pending_request_.get(), state->pending_response_.get(),
cef_cookie);
}
}
void ContinueWithSavedCookies(int32_t request_id,
base::OnceClosure callback,
int total_count,
net::CookieList allowed_cookies) {
CEF_REQUIRE_IOT();
std::move(callback).Run();
}
mojo::ScopedDataPipeConsumerHandle OnFilterResponseBody(
int32_t request_id,
const network::ResourceRequest& request,
mojo::ScopedDataPipeConsumerHandle body) override {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
return body;
}
if (state->handler_) {
auto filter = state->handler_->GetResourceResponseFilter(
init_state_->browser_, init_state_->frame_,
state->pending_request_.get(), state->pending_response_.get());
if (filter) {
return CreateResponseFilterHandler(
filter, std::move(body),
base::BindOnce(&InterceptedRequestHandlerWrapper::OnFilterError,
weak_ptr_factory_.GetWeakPtr(), request_id));
}
}
return body;
}
void OnFilterError(int32_t request_id) {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
return;
}
if (state->cancel_callback_) {
std::move(state->cancel_callback_).Run(net::ERR_CONTENT_DECODING_FAILED);
}
}
void OnRequestComplete(
int32_t request_id,
const network::ResourceRequest& request,
const network::URLLoaderCompletionStatus& status) override {
CEF_REQUIRE_IOT();
RequestState* state = GetState(request_id);
if (!state) {
if (!pending_requests_.empty()) {
PendingRequests::iterator it = pending_requests_.begin();
for (; it != pending_requests_.end(); ++it) {
if ((*it)->id_ == request_id) {
pending_requests_.erase(it);
break;
}
}
}
return;
}
const bool is_external = IsExternalRequest(&request);
const bool ignore_result = is_external && request.url.IsStandard() &&
status.error_code == net::ERR_ABORTED &&
state->pending_response_.get() &&
net::HttpResponseHeaders::IsRedirectResponseCode(
state->pending_response_->GetStatus());
if (state->handler_ && !ignore_result) {
DCHECK(state->pending_request_);
CallHandlerOnComplete(state, status);
if (status.error_code != 0 && status.error_code != ERR_ABORTED &&
is_external) {
bool allow_os_execution = false;
state->handler_->OnProtocolExecution(
init_state_->browser_, init_state_->frame_,
state->pending_request_.get(), allow_os_execution);
if (allow_os_execution && init_state_->unhandled_request_callback_) {
init_state_->unhandled_request_callback_.Run();
}
}
}
RemoveState(request_id);
}
private:
void CallHandlerOnComplete(RequestState* state,
const network::URLLoaderCompletionStatus& status) {
if (!state->handler_ || !state->pending_request_)
return;
if (!state->pending_request_->IsReadOnly()) {
state->pending_request_->SetReadOnly(true);
}
if (!state->pending_response_) {
state->pending_response_ = new CefResponseImpl();
} else {
state->pending_response_->SetReadOnly(false);
}
state->pending_response_->SetError(
static_cast<cef_errorcode_t>(status.error_code));
state->pending_response_->SetReadOnly(true);
state->handler_->OnResourceLoadComplete(
init_state_->browser_, init_state_->frame_,
state->pending_request_.get(), state->pending_response_.get(),
status.error_code == 0 ? UR_SUCCESS : UR_FAILED,
status.encoded_body_length);
}
CefRefPtr<CefResourceRequestHandler> GetHandler(
int32_t request_id,
network::ResourceRequest* request,
bool* intercept_only,
CefRefPtr<CefRequestImpl>& requestPtr) const {
CefRefPtr<CefResourceRequestHandler> handler;
if (init_state_->browser_) {
CefRefPtr<CefClient> client =
init_state_->browser_->GetHost()->GetClient();
if (client) {
CefRefPtr<CefRequestHandler> request_handler =
client->GetRequestHandler();
if (request_handler) {
requestPtr = MakeRequest(request, request_id, true);
handler = request_handler->GetResourceRequestHandler(
init_state_->browser_, init_state_->frame_, requestPtr.get(),
init_state_->is_navigation_, init_state_->is_download_,
init_state_->request_initiator_, *intercept_only);
}
}
}
if (!handler) {
CefRefPtr<CefRequestContextHandler> context_handler =
init_state_->iothread_state_->GetHandler(
init_state_->global_id_, false);
if (context_handler) {
if (!requestPtr)
requestPtr = MakeRequest(request, request_id, true);
handler = context_handler->GetResourceRequestHandler(
init_state_->browser_, init_state_->frame_, requestPtr.get(),
init_state_->is_navigation_, init_state_->is_download_,
init_state_->request_initiator_, *intercept_only);
}
}
return handler;
}
RequestState* GetOrCreateState(int32_t request_id) {
RequestState* state = GetState(request_id);
if (!state) {
state = new RequestState();
request_map_.insert(std::make_pair(request_id, base::WrapUnique(state)));
}
return state;
}
RequestState* GetState(int32_t request_id) const {
RequestMap::const_iterator it = request_map_.find(request_id);
if (it != request_map_.end())
return it->second.get();
return nullptr;
}
void RemoveState(int32_t request_id) {
RequestMap::iterator it = request_map_.find(request_id);
DCHECK(it != request_map_.end());
if (it != request_map_.end())
request_map_.erase(it);
}
void OnDestroyed() {
CEF_REQUIRE_IOT();
DCHECK(init_state_);
init_state_->DeleteDestructionObserver();
shutting_down_ = true;
weak_ptr_factory_.InvalidateWeakPtrs();
PendingRequests pending_requests;
pending_requests.swap(pending_requests_);
RequestMap request_map;
request_map.swap(request_map_);
for (const auto& pair : request_map) {
CallHandlerOnComplete(
pair.second.get(),
network::URLLoaderCompletionStatus(net::ERR_ABORTED));
}
if (init_state_->browser_) {
init_state_->browser_ = nullptr;
init_state_->frame_ = nullptr;
}
pending_requests.clear();
for (auto& pair : request_map) {
auto state = std::move(pair.second);
if (state->cancel_callback_) {
std::move(state->cancel_callback_).Run(net::ERR_ABORTED);
}
}
}
static CefRefPtr<CefRequestImpl> MakeRequest(
const network::ResourceRequest* request,
int64 request_id,
bool read_only) {
CefRefPtr<CefRequestImpl> requestPtr = new CefRequestImpl();
requestPtr->Set(request, request_id);
if (read_only)
requestPtr->SetReadOnly(true);
else
requestPtr->SetTrackChanges(true);
return requestPtr;
}
static bool IsExternalRequest(const network::ResourceRequest* request) {
return !scheme::IsInternalHandledScheme(request->url.scheme());
}
scoped_refptr<InitHelper> init_helper_;
std::unique_ptr<InitState> init_state_;
bool shutting_down_ = false;
using RequestMap = std::map<int32_t, std::unique_ptr<RequestState>>;
RequestMap request_map_;
using PendingRequests = std::vector<std::unique_ptr<PendingRequest>>;
PendingRequests pending_requests_;
base::WeakPtrFactory<InterceptedRequestHandlerWrapper> weak_ptr_factory_;
};
}
std::unique_ptr<InterceptedRequestHandler> CreateInterceptedRequestHandler(
content::BrowserContext* browser_context,
content::RenderFrameHost* frame,
int render_process_id,
bool is_navigation,
bool is_download,
const url::Origin& request_initiator) {
CEF_REQUIRE_UIT();
CHECK(browser_context);
CefRefPtr<CefBrowserHostBase> browserPtr;
CefRefPtr<CefFrame> framePtr;
content::GlobalRenderFrameHostId global_id(render_process_id,
MSG_ROUTING_NONE);
if (frame) {
browserPtr = CefBrowserHostBase::GetBrowserForHost(frame);
if (browserPtr) {
framePtr = browserPtr->GetFrameForHost(frame);
CHECK(framePtr);
global_id = frame->GetGlobalId();
}
}
auto init_state =
std::make_unique<InterceptedRequestHandlerWrapper::InitState>();
init_state->Initialize(browser_context, browserPtr, framePtr, global_id,
is_navigation, is_download, request_initiator,
base::RepeatingClosure());
auto wrapper = std::make_unique<InterceptedRequestHandlerWrapper>();
wrapper->init_helper()->MaybeSetInitialized(std::move(init_state));
return wrapper;
}
std::unique_ptr<InterceptedRequestHandler> CreateInterceptedRequestHandler(
content::WebContents::Getter web_contents_getter,
int frame_tree_node_id,
const network::ResourceRequest& request,
const base::RepeatingClosure& unhandled_request_callback) {
CEF_REQUIRE_UIT();
content::WebContents* web_contents = web_contents_getter.Run();
CHECK(web_contents);
content::BrowserContext* browser_context = web_contents->GetBrowserContext();
CHECK(browser_context);
content::RenderFrameHost* frame = nullptr;
if (request.is_main_frame ||
static_cast<blink::mojom::ResourceType>(request.resource_type) ==
blink::mojom::ResourceType::kMainFrame) {
frame = web_contents->GetMainFrame();
CHECK(frame);
} else {
auto node = content::FrameTreeNode::GloballyFindByID(frame_tree_node_id);
if (node) {
frame = node->current_frame_host();
if (content::WebContents::FromRenderFrameHost(frame) != web_contents) {
frame = nullptr;
}
}
if (!frame) {
frame = web_contents->GetMainFrame();
CHECK(frame);
}
}
CefRefPtr<CefBrowserHostBase> browserPtr;
CefRefPtr<CefFrame> framePtr;
content::GlobalRenderFrameHostId global_id(frame->GetProcess()->GetID(),
MSG_ROUTING_NONE);
browserPtr = CefBrowserHostBase::GetBrowserForHost(frame);
if (browserPtr) {
framePtr = browserPtr->GetFrameForHost(frame);
DCHECK(framePtr);
global_id = frame->GetGlobalId();
}
const bool is_navigation = ui::PageTransitionIsNewNavigation(
static_cast<ui::PageTransition>(request.transition_type));
const bool is_download = false;
url::Origin request_initiator;
if (request.request_initiator.has_value())
request_initiator = *request.request_initiator;
auto init_state =
std::make_unique<InterceptedRequestHandlerWrapper::InitState>();
init_state->Initialize(browser_context, browserPtr, framePtr, global_id,
is_navigation, is_download, request_initiator,
unhandled_request_callback);
auto wrapper = std::make_unique<InterceptedRequestHandlerWrapper>();
wrapper->init_helper()->MaybeSetInitialized(std::move(init_state));
return wrapper;
}
}