// Copyright (c) 2019 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. #include "libcef/browser/net_service/resource_request_handler_wrapper.h" #include "libcef/browser/browser_host_impl.h" #include "libcef/browser/browser_platform_delegate.h" #include "libcef/browser/content_browser_client.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/resource_context.h" #include "libcef/browser/thread_util.h" #include "libcef/common/content_client.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 "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/http/http_status_code.h" #include "ui/base/page_transition_types.h" #include "url/origin.h" namespace net_service { namespace { class RequestCallbackWrapper : public CefRequestCallback { public: using Callback = base::OnceCallback; explicit RequestCallbackWrapper(Callback callback) : callback_(std::move(callback)), work_thread_task_runner_(base::SequencedTaskRunnerHandle::Get()) {} ~RequestCallbackWrapper() override { if (!callback_.is_null()) { // Make sure it executes on the correct thread. work_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(std::move(callback_), true)); } } void Continue(bool allow) override { if (!work_thread_task_runner_->RunsTasksInCurrentSequence()) { work_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&RequestCallbackWrapper::Continue, this, allow)); return; } if (!callback_.is_null()) { std::move(callback_).Run(allow); } } void Cancel() override { Continue(false); } private: Callback callback_; scoped_refptr work_thread_task_runner_; IMPLEMENT_REFCOUNTING(RequestCallbackWrapper); DISALLOW_COPY_AND_ASSIGN(RequestCallbackWrapper); }; std::string GetAcceptLanguageList(content::BrowserContext* browser_context, CefRefPtr browser) { if (browser) { const CefBrowserSettings& browser_settings = browser->settings(); if (browser_settings.accept_language_list.length > 0) { return CefString(&browser_settings.accept_language_list); } } const CefRequestContextSettings& context_settings = static_cast(browser_context)->GetSettings(); if (context_settings.accept_language_list.length > 0) { return CefString(&context_settings.accept_language_list); } return "en-US,en"; } class InterceptedRequestHandlerWrapper : public InterceptedRequestHandler { public: struct RequestState { RequestState() {} void Reset(CefRefPtr handler, CefRefPtr scheme_factory, CefRefPtr request, bool request_was_redirected) { 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; } CefRefPtr handler_; CefRefPtr scheme_factory_; CefRefPtr cookie_filter_; CefRefPtr pending_request_; CefRefPtr pending_response_; bool request_was_redirected_ = false; bool was_custom_handled_ = false; }; struct PendingRequest { PendingRequest(const RequestId& id, network::ResourceRequest* request, bool request_was_redirected, OnBeforeRequestResultCallback callback) : id_(id), request_(request), request_was_redirected_(request_was_redirected), callback_(std::move(callback)) {} void Run(InterceptedRequestHandlerWrapper* self) { self->OnBeforeRequest(id_, request_, request_was_redirected_, std::move(callback_)); } const RequestId id_; network::ResourceRequest* const request_; const bool request_was_redirected_; OnBeforeRequestResultCallback callback_; }; InterceptedRequestHandlerWrapper() : weak_ptr_factory_(this) {} ~InterceptedRequestHandlerWrapper() override {} void Initialize(content::BrowserContext* browser_context, CefRefPtr browser, CefRefPtr frame, int render_process_id, int render_frame_id, int frame_tree_node_id, bool is_navigation, bool is_download, const url::Origin& request_initiator, bool is_external) { CEF_REQUIRE_UIT(); browser_context_ = browser_context; resource_context_ = static_cast(browser_context->GetResourceContext()); DCHECK(resource_context_); // Don't hold a RefPtr to the CefBrowserHostImpl. if (browser) browser_info_ = browser->browser_info(); frame_ = frame; render_process_id_ = render_process_id; render_frame_id_ = render_frame_id; frame_tree_node_id_ = frame_tree_node_id; is_navigation_ = is_navigation; is_download_ = is_download; request_initiator_ = request_initiator.Serialize(); is_external_ = is_external; // Default values for standard headers. accept_language_ = net::HttpUtil::GenerateAcceptLanguageHeader( GetAcceptLanguageList(browser_context, browser)); user_agent_ = CefContentClient::Get()->browser()->GetUserAgent(); CEF_POST_TASK( CEF_IOT, base::BindOnce(&InterceptedRequestHandlerWrapper::SetInitialized, weak_ptr_factory_.GetWeakPtr())); } void SetInitialized() { CEF_REQUIRE_IOT(); initialized_ = true; // Continue any pending requests. if (!pending_requests_.empty()) { for (const auto& request : pending_requests_) request->Run(this); pending_requests_.clear(); } } // InterceptedRequestHandler methods: void OnBeforeRequest(const RequestId& id, network::ResourceRequest* request, bool request_was_redirected, OnBeforeRequestResultCallback callback) override { CEF_REQUIRE_IOT(); if (!initialized_) { // Queue requests until we're initialized. pending_requests_.push_back(std::make_unique( id, request, request_was_redirected, std::move(callback))); return; } // State may already exist for restarted requests. RequestState* state = GetOrCreateState(id); // Add standard headers, if currently unspecified. request->headers.SetHeaderIfMissing( net::HttpRequestHeaders::kAcceptLanguage, accept_language_); request->headers.SetHeaderIfMissing(net::HttpRequestHeaders::kUserAgent, user_agent_); // External requests will not have a default handler. bool intercept_only = is_external_; CefRefPtr requestPtr; CefRefPtr handler = GetHandler(id, request, &intercept_only, requestPtr); CefRefPtr scheme_factory = resource_context_->GetSchemeHandlerFactory(request->url); if (scheme_factory && !requestPtr) { requestPtr = MakeRequest(request, id.hash(), true); } // True if there's a possibility that the client might handle the request. const bool maybe_intercept_request = handler || scheme_factory; if (!maybe_intercept_request && requestPtr) requestPtr = nullptr; // May have a handler and/or scheme factory. state->Reset(handler, scheme_factory, requestPtr, request_was_redirected); if (handler) { state->cookie_filter_ = handler->GetCookieAccessFilter( GetBrowser(), frame_, requestPtr.get()); } auto exec_callback = base::BindOnce(std::move(callback), maybe_intercept_request, is_external_ ? true : intercept_only); if (!maybe_intercept_request) { // Cookies will be handled by the NetworkService. std::move(exec_callback).Run(); return; } MaybeLoadCookies(state, request, std::move(exec_callback)); } void MaybeLoadCookies(RequestState* state, network::ResourceRequest* request, base::OnceClosure callback) { CEF_REQUIRE_IOT(); // We need to load/save cookies ourselves for custom-handled requests, or // if we're using a cookie filter. auto allow_cookie_callback = base::BindRepeating(&InterceptedRequestHandlerWrapper::AllowCookieLoad, weak_ptr_factory_.GetWeakPtr(), state); auto done_cookie_callback = base::BindOnce( &InterceptedRequestHandlerWrapper::ContinueWithLoadedCookies, weak_ptr_factory_.GetWeakPtr(), state, request, std::move(callback)); net_service::LoadCookies(browser_context_, *request, allow_cookie_callback, std::move(done_cookie_callback)); } void AllowCookieLoad(RequestState* state, const net::CanonicalCookie& cookie, bool* allow) { CEF_REQUIRE_IOT(); if (!state->cookie_filter_) { *allow = true; return; } CefCookie cef_cookie; if (net_service::MakeCefCookie(cookie, cef_cookie)) { *allow = state->cookie_filter_->CanSendCookie( GetBrowser(), frame_, state->pending_request_.get(), cef_cookie); } } void ContinueWithLoadedCookies(RequestState* state, network::ResourceRequest* request, base::OnceClosure callback, int total_count, net::CookieList allowed_cookies) { CEF_REQUIRE_IOT(); if (state->cookie_filter_) { // Also add/save cookies ourselves for default-handled network requests // so that we can filter them. This will be a no-op for custom-handled // requests. request->load_flags |= net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; } 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( const RequestId& id, network::ResourceRequest* request, ShouldInterceptRequestResultCallback callback) override { CEF_REQUIRE_IOT(); RequestState* state = GetState(id); DCHECK(state); // Must have a handler and/or scheme factory. DCHECK(state->handler_ || state->scheme_factory_); DCHECK(state->pending_request_); if (state->handler_) { // The client may modify |pending_request_| before executing the callback. state->pending_request_->SetReadOnly(false); state->pending_request_->SetTrackChanges(true, true /* backup_on_change */); CefRefPtr callbackPtr = new RequestCallbackWrapper(base::BindOnce( &InterceptedRequestHandlerWrapper::ContinueShouldInterceptRequest, weak_ptr_factory_.GetWeakPtr(), id, base::Unretained(request), base::Passed(std::move(callback)))); cef_return_value_t retval = state->handler_->OnBeforeResourceLoad( GetBrowser(), frame_, state->pending_request_.get(), callbackPtr.get()); if (retval != RV_CONTINUE_ASYNC) { // Continue or cancel the request immediately. callbackPtr->Continue(retval == RV_CONTINUE); } } else { // The scheme factory may choose to handle it. ContinueShouldInterceptRequest(id, request, std::move(callback), true); } } void ContinueShouldInterceptRequest( const RequestId& id, network::ResourceRequest* request, ShouldInterceptRequestResultCallback callback, bool allow) { CEF_REQUIRE_IOT(); RequestState* state = GetState(id); DCHECK(state); // Must have a handler and/or scheme factory. DCHECK(state->handler_ || state->scheme_factory_); DCHECK(state->pending_request_); if (state->handler_) { if (allow) { // Apply any |requestPtr| changes to |request|. state->pending_request_->Get(request, true /* changed_only */); } const bool redirect = (state->pending_request_->GetChanges() & CefRequestImpl::kChangedUrl); if (redirect) { // Revert any changes for now. We'll get them back after the redirect. state->pending_request_->RevertChanges(); } state->pending_request_->SetReadOnly(true); state->pending_request_->SetTrackChanges(false); if (!allow) { // Cancel the request. std::move(callback).Run(nullptr, true /* cancel_request */); return; } if (redirect) { // Performing a redirect. std::move(callback).Run(nullptr, false /* cancel_request */); return; } } CefRefPtr resource_handler; CefRefPtr browser = GetBrowser(); if (state->handler_) { // Does the client want to handle the request? resource_handler = state->handler_->GetResourceHandler( browser, frame_, state->pending_request_.get()); } if (!resource_handler && state->scheme_factory_) { // Does the scheme factory want to handle the request? resource_handler = state->scheme_factory_->Create(browser, frame_, request->url.scheme(), state->pending_request_.get()); } std::unique_ptr resource_response; if (resource_handler) { resource_response = CreateResourceResponse(id, resource_handler); DCHECK(resource_response); state->was_custom_handled_ = true; } // Continue the request. std::move(callback).Run(std::move(resource_response), false /* cancel_request */); } void ProcessResponseHeaders( const RequestId& id, const network::ResourceRequest& request, const GURL& redirect_url, const network::ResourceResponseHead& head) override { CEF_REQUIRE_IOT(); RequestState* state = GetState(id); DCHECK(state); if (!state->handler_) return; if (!state->pending_response_) state->pending_response_ = new CefResponseImpl(); else state->pending_response_->SetReadOnly(false); if (head.headers) state->pending_response_->SetResponseHeaders(*head.headers); state->pending_response_->SetReadOnly(true); } void OnRequestResponse(const RequestId& id, network::ResourceRequest* request, const network::ResourceResponseHead& head, base::Optional redirect_info, OnRequestResponseResultCallback callback) override { CEF_REQUIRE_IOT(); RequestState* state = GetState(id); DCHECK(state); if (!state->handler_) { InterceptedRequestHandler::OnRequestResponse( id, request, head, redirect_info, std::move(callback)); return; } DCHECK(state->pending_request_); DCHECK(state->pending_response_); if (redirect_info.has_value()) { HandleRedirect(state, request, head, *redirect_info, std::move(callback)); } else { HandleResponse(state, request, head, std::move(callback)); } } void HandleRedirect(RequestState* state, network::ResourceRequest* request, const network::ResourceResponseHead& head, 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(GetBrowser(), frame_, state->pending_request_.get(), state->pending_response_.get(), newUrl); if (newUrl != oldUrl) { // Also support relative URLs. const GURL& url = redirect_info.new_url.Resolve(newUrl.ToString()); if (url.is_valid()) { url_changed = true; new_url = url; } } // Update the |pending_request_| object with the new info. 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(state, request, head, std::move(exec_callback)); } void HandleResponse(RequestState* state, network::ResourceRequest* request, const network::ResourceResponseHead& head, OnRequestResponseResultCallback callback) { // The client may modify |pending_request_| in OnResourceResponse. state->pending_request_->SetReadOnly(false); state->pending_request_->SetTrackChanges(true, true /* backup_on_change */); auto response_mode = ResponseMode::CONTINUE; GURL new_url; if (state->handler_->OnResourceResponse(GetBrowser(), frame_, state->pending_request_.get(), state->pending_response_.get())) { // The request may have been modified. const auto changes = state->pending_request_->GetChanges(); if (changes) { state->pending_request_->Get(request, true /* changed_only */); if (changes & CefRequestImpl::kChangedUrl) { // Redirect to the new URL. new_url = GURL(state->pending_request_->GetURL().ToString()); } else { // Restart the request. response_mode = ResponseMode::RESTART; } } } // Revert any changes for now. We'll get them back after the redirect or // 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) { // Get any cookies after the restart. std::move(exec_callback).Run(); return; } MaybeSaveCookies(state, request, head, std::move(exec_callback)); } void MaybeSaveCookies(RequestState* state, network::ResourceRequest* request, const network::ResourceResponseHead& head, base::OnceClosure callback) { CEF_REQUIRE_IOT(); if (!state->cookie_filter_ && !state->was_custom_handled_) { // The NetworkService saves the cookies for default-handled requests. std::move(callback).Run(); return; } // We need to load/save cookies ourselves for custom-handled requests, or // if we're using a cookie filter. auto allow_cookie_callback = base::BindRepeating(&InterceptedRequestHandlerWrapper::AllowCookieSave, weak_ptr_factory_.GetWeakPtr(), state); auto done_cookie_callback = base::BindOnce( &InterceptedRequestHandlerWrapper::ContinueWithSavedCookies, weak_ptr_factory_.GetWeakPtr(), state, std::move(callback)); net_service::SaveCookies(browser_context_, *request, head, allow_cookie_callback, std::move(done_cookie_callback)); } void AllowCookieSave(RequestState* state, const net::CanonicalCookie& cookie, bool* allow) { CEF_REQUIRE_IOT(); if (!state->cookie_filter_) { *allow = true; return; } CefCookie cef_cookie; if (net_service::MakeCefCookie(cookie, cef_cookie)) { *allow = state->cookie_filter_->CanSaveCookie( GetBrowser(), frame_, state->pending_request_.get(), state->pending_response_.get(), cef_cookie); } } void ContinueWithSavedCookies(RequestState* state, base::OnceClosure callback, int total_count, net::CookieList allowed_cookies) { CEF_REQUIRE_IOT(); std::move(callback).Run(); } void OnRequestComplete( const RequestId& id, const network::ResourceRequest& request, const network::URLLoaderCompletionStatus& status) override { CEF_REQUIRE_IOT(); RequestState* state = GetState(id); DCHECK(state); // Redirection of standard custom schemes is handled with a restart, so we // get completion notifications for both the original (redirected) request // and the final request. Don't report completion of the redirected 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_); if (!state->pending_response_) { // If the request failed there may not be a response object yet. state->pending_response_ = new CefResponseImpl(); } else { state->pending_response_->SetReadOnly(false); } state->pending_response_->SetError( static_cast(status.error_code)); state->pending_response_->SetReadOnly(true); CefRefPtr browser = GetBrowser(); state->handler_->OnResourceLoadComplete( browser, frame_, state->pending_request_.get(), state->pending_response_.get(), status.error_code == 0 ? UR_SUCCESS : UR_FAILED, status.encoded_body_length); if (status.error_code != 0 && is_external_) { bool allow_os_execution = false; state->handler_->OnProtocolExecution( browser, frame_, state->pending_request_.get(), allow_os_execution); if (allow_os_execution) { CefBrowserPlatformDelegate::HandleExternalProtocol(request.url); } } } RemoveState(id); } private: // Returns the handler, if any, that should be used for this request. CefRefPtr GetHandler( const RequestId& id, network::ResourceRequest* request, bool* intercept_only, CefRefPtr& requestPtr) const { CefRefPtr handler; const int64 request_id = id.hash(); CefRefPtr browser = GetBrowser(); if (browser) { // Maybe the browser's client wants to handle it? CefRefPtr client = browser->GetHost()->GetClient(); if (client) { CefRefPtr request_handler = client->GetRequestHandler(); if (request_handler) { requestPtr = MakeRequest(request, request_id, true); handler = request_handler->GetResourceRequestHandler( browser, frame_, requestPtr.get(), is_navigation_, is_download_, request_initiator_, *intercept_only); } } } if (!handler) { // Maybe the request context wants to handle it? CefRefPtr context_handler = resource_context_->GetHandler(render_process_id_, request->render_frame_id, frame_tree_node_id_, false); if (context_handler) { if (!requestPtr) requestPtr = MakeRequest(request, request_id, true); handler = context_handler->GetResourceRequestHandler( browser, frame_, requestPtr.get(), is_navigation_, is_download_, request_initiator_, *intercept_only); } } return handler; } RequestState* GetOrCreateState(const RequestId& id) { RequestState* state = GetState(id); if (!state) { state = new RequestState(); request_map_.insert(std::make_pair(id, base::WrapUnique(state))); } return state; } RequestState* GetState(const RequestId& id) const { RequestMap::const_iterator it = request_map_.find(id); if (it != request_map_.end()) return it->second.get(); return nullptr; } void RemoveState(const RequestId& id) { RequestMap::iterator it = request_map_.find(id); DCHECK(it != request_map_.end()); if (it != request_map_.end()) request_map_.erase(it); } CefRefPtr GetBrowser() const { if (browser_info_) return browser_info_->browser().get(); return nullptr; } static CefRefPtr MakeRequest( const network::ResourceRequest* request, int64 request_id, bool read_only) { CefRefPtr requestPtr = new CefRequestImpl(); requestPtr->Set(request, request_id); if (read_only) requestPtr->SetReadOnly(true); else requestPtr->SetTrackChanges(true); return requestPtr; } bool initialized_ = false; // Only accessed on the UI thread. content::BrowserContext* browser_context_ = nullptr; scoped_refptr browser_info_; CefRefPtr frame_; CefResourceContext* resource_context_ = nullptr; int render_process_id_ = 0; int render_frame_id_ = -1; int frame_tree_node_id_ = -1; bool is_navigation_ = true; bool is_download_ = false; CefString request_initiator_; bool is_external_ = false; // Default values for standard headers. std::string accept_language_; std::string user_agent_; using RequestMap = std::map>; RequestMap request_map_; using PendingRequests = std::vector>; PendingRequests pending_requests_; base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(InterceptedRequestHandlerWrapper); }; void InitOnUIThread( InterceptedRequestHandlerWrapper* wrapper, content::ResourceRequestInfo::WebContentsGetter web_contents_getter, int frame_tree_node_id, const network::ResourceRequest& request) { CEF_REQUIRE_UIT(); content::WebContents* web_contents = web_contents_getter.Run(); DCHECK(web_contents); content::BrowserContext* browser_context = web_contents->GetBrowserContext(); DCHECK(browser_context); const int render_process_id = web_contents->GetRenderViewHost()->GetProcess()->GetID(); content::RenderFrameHost* frame = nullptr; bool get_frame_by_route = false; if (request.render_frame_id >= 0) { // TODO(network): Are these main frame checks equivalent? if (request.is_main_frame || request.resource_type == content::RESOURCE_TYPE_MAIN_FRAME) { frame = web_contents->GetMainFrame(); DCHECK(frame); } else { // May return null for newly created iframes. frame = content::RenderFrameHost::FromID(render_process_id, request.render_frame_id); if (!frame && frame_tree_node_id >= 0) { // May return null for frames in inner WebContents. frame = web_contents->FindFrameByFrameTreeNodeId(frame_tree_node_id, render_process_id); } if (!frame) { // Use the main frame for the CefBrowserHost, but choose a more // appropriate CefFrame for the route. frame = web_contents->GetMainFrame(); DCHECK(frame); get_frame_by_route = true; } } } CefRefPtr browserPtr; CefRefPtr framePtr; // |frame| may be null for service worker requests. if (frame) { #if DCHECK_IS_ON() if (frame_tree_node_id >= 0 && !get_frame_by_route) { // Sanity check that we ended up with the expected frame. DCHECK_EQ(frame_tree_node_id, frame->GetFrameTreeNodeId()); } #endif browserPtr = CefBrowserHostImpl::GetBrowserForHost(frame); if (browserPtr) { if (get_frame_by_route) { framePtr = browserPtr->GetFrameForHostRoutingId(request.render_frame_id); frame_tree_node_id = -1; } else { framePtr = browserPtr->GetFrameForHost(frame); if (frame_tree_node_id < 0) frame_tree_node_id = frame->GetFrameTreeNodeId(); } DCHECK(framePtr); } } const bool is_navigation = ui::PageTransitionIsNewNavigation( static_cast(request.transition_type)); // TODO(navigation): Can we determine the |is_download| value? const bool is_download = false; url::Origin request_initiator; if (request.request_initiator.has_value()) request_initiator = *request.request_initiator; wrapper->Initialize(browser_context, browserPtr, framePtr, render_process_id, request.render_frame_id, frame_tree_node_id, is_navigation, is_download, request_initiator, true /* is_external */); } } // namespace std::unique_ptr 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(); auto wrapper = std::make_unique(); CefRefPtr browserPtr; CefRefPtr framePtr; int render_frame_id = -1; int frame_tree_node_id = -1; // |frame| may be null for service worker requests. if (frame) { render_frame_id = frame->GetRoutingID(); frame_tree_node_id = frame->GetFrameTreeNodeId(); browserPtr = CefBrowserHostImpl::GetBrowserForHost(frame); if (browserPtr) { framePtr = browserPtr->GetFrameForHost(frame); DCHECK(framePtr); } } // Flag subresource loads of custom schemes. 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, is_download, request_initiator, is_external); return wrapper; } std::unique_ptr CreateInterceptedRequestHandler( content::ResourceRequestInfo::WebContentsGetter web_contents_getter, int frame_tree_node_id, const network::ResourceRequest& request) { CEF_REQUIRE_IOT(); auto wrapper = std::make_unique(); CEF_POST_TASK(CEF_UIT, base::BindOnce( InitOnUIThread, base::Unretained(wrapper.get()), web_contents_getter, frame_tree_node_id, request)); return wrapper; } } // namespace net_service