• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium Embedded Framework Authors.
2 // Portions copyright 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 #include "libcef/browser/chrome/chrome_content_browser_client_cef.h"
7 
8 #include <tuple>
9 
10 #include "libcef/browser/browser_frame.h"
11 #include "libcef/browser/browser_info_manager.h"
12 #include "libcef/browser/browser_manager.h"
13 #include "libcef/browser/chrome/chrome_browser_host_impl.h"
14 #include "libcef/browser/chrome/chrome_browser_main_extra_parts_cef.h"
15 #include "libcef/browser/context.h"
16 #include "libcef/browser/net/chrome_scheme_handler.h"
17 #include "libcef/browser/net/throttle_handler.h"
18 #include "libcef/browser/net_service/cookie_manager_impl.h"
19 #include "libcef/browser/net_service/login_delegate.h"
20 #include "libcef/browser/net_service/proxy_url_loader_factory.h"
21 #include "libcef/browser/net_service/resource_request_handler_wrapper.h"
22 #include "libcef/browser/prefs/browser_prefs.h"
23 #include "libcef/browser/prefs/renderer_prefs.h"
24 #include "libcef/common/app_manager.h"
25 #include "libcef/common/cef_switches.h"
26 #include "libcef/common/command_line_impl.h"
27 
28 #include "base/command_line.h"
29 #include "base/path_service.h"
30 #include "chrome/browser/chrome_browser_main.h"
31 #include "chrome/browser/net/system_network_context_manager.h"
32 #include "chrome/common/chrome_paths.h"
33 #include "chrome/common/chrome_switches.h"
34 #include "content/public/browser/navigation_throttle.h"
35 #include "content/public/browser/render_process_host.h"
36 #include "content/public/browser/render_view_host.h"
37 #include "content/public/browser/render_widget_host.h"
38 #include "content/public/browser/render_widget_host_view.h"
39 #include "content/public/browser/weak_document_ptr.h"
40 #include "content/public/common/content_switches.h"
41 #include "third_party/blink/public/common/web_preferences/web_preferences.h"
42 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
43 
44 namespace {
45 
HandleExternalProtocolHelper(ChromeContentBrowserClientCef * self,content::WebContents::Getter web_contents_getter,int frame_tree_node_id,content::NavigationUIData * navigation_data,network::mojom::WebSandboxFlags sandbox_flags,const network::ResourceRequest & resource_request,const absl::optional<url::Origin> & initiating_origin,content::WeakDocumentPtr initiator_document)46 void HandleExternalProtocolHelper(
47     ChromeContentBrowserClientCef* self,
48     content::WebContents::Getter web_contents_getter,
49     int frame_tree_node_id,
50     content::NavigationUIData* navigation_data,
51     network::mojom::WebSandboxFlags sandbox_flags,
52     const network::ResourceRequest& resource_request,
53     const absl::optional<url::Origin>& initiating_origin,
54     content::WeakDocumentPtr initiator_document) {
55   // May return nullptr if frame has been deleted or a cross-document navigation
56   // has committed in the same RenderFrameHost.
57   auto initiator_rfh = initiator_document.AsRenderFrameHostIfValid();
58   if (!initiator_rfh)
59     return;
60 
61   // Match the logic of the original call in
62   // NavigationURLLoaderImpl::PrepareForNonInterceptedRequest.
63   self->HandleExternalProtocol(
64       resource_request.url, web_contents_getter,
65       content::ChildProcessHost::kInvalidUniqueID, frame_tree_node_id,
66       navigation_data,
67       resource_request.resource_type ==
68           static_cast<int>(blink::mojom::ResourceType::kMainFrame),
69       sandbox_flags,
70       static_cast<ui::PageTransition>(resource_request.transition_type),
71       resource_request.has_user_gesture, initiating_origin, initiator_rfh,
72       nullptr);
73 }
74 
75 }  // namespace
76 
77 ChromeContentBrowserClientCef::ChromeContentBrowserClientCef() = default;
78 ChromeContentBrowserClientCef::~ChromeContentBrowserClientCef() = default;
79 
80 std::unique_ptr<content::BrowserMainParts>
CreateBrowserMainParts(content::MainFunctionParams parameters)81 ChromeContentBrowserClientCef::CreateBrowserMainParts(
82     content::MainFunctionParams parameters) {
83   auto main_parts =
84       ChromeContentBrowserClient::CreateBrowserMainParts(std::move(parameters));
85   browser_main_parts_ = new ChromeBrowserMainExtraPartsCef;
86   static_cast<ChromeBrowserMainParts*>(main_parts.get())
87       ->AddParts(
88           base::WrapUnique<ChromeBrowserMainExtraParts>(browser_main_parts_));
89   return main_parts;
90 }
91 
AppendExtraCommandLineSwitches(base::CommandLine * command_line,int child_process_id)92 void ChromeContentBrowserClientCef::AppendExtraCommandLineSwitches(
93     base::CommandLine* command_line,
94     int child_process_id) {
95   ChromeContentBrowserClient::AppendExtraCommandLineSwitches(command_line,
96                                                              child_process_id);
97 
98   // Necessary to launch sub-processes in the correct mode.
99   command_line->AppendSwitch(switches::kEnableChromeRuntime);
100 
101   // Necessary to populate DIR_USER_DATA in sub-processes.
102   // See resource_util.cc GetUserDataPath.
103   base::FilePath user_data_dir;
104   if (base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
105     command_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir);
106   }
107 
108   const base::CommandLine* browser_cmd = base::CommandLine::ForCurrentProcess();
109 
110   {
111     // Propagate the following switches to all command lines (along with any
112     // associated values) if present in the browser command line.
113     static const char* const kSwitchNames[] = {
114         switches::kUserAgentProductAndVersion,
115     };
116     command_line->CopySwitchesFrom(*browser_cmd, kSwitchNames,
117                                    base::size(kSwitchNames));
118   }
119 
120   const std::string& process_type =
121       command_line->GetSwitchValueASCII(switches::kProcessType);
122   if (process_type == switches::kRendererProcess) {
123     // Propagate the following switches to the renderer command line (along with
124     // any associated values) if present in the browser command line.
125     static const char* const kSwitchNames[] = {
126         switches::kUncaughtExceptionStackSize,
127     };
128     command_line->CopySwitchesFrom(*browser_cmd, kSwitchNames,
129                                    base::size(kSwitchNames));
130   }
131 
132   CefRefPtr<CefApp> app = CefAppManager::Get()->GetApplication();
133   if (app.get()) {
134     CefRefPtr<CefBrowserProcessHandler> handler =
135         app->GetBrowserProcessHandler();
136     if (handler.get()) {
137       CefRefPtr<CefCommandLineImpl> commandLinePtr(
138           new CefCommandLineImpl(command_line, false, false));
139       handler->OnBeforeChildProcessLaunch(commandLinePtr.get());
140       std::ignore = commandLinePtr->Detach(nullptr);
141     }
142   }
143 }
144 
RenderProcessWillLaunch(content::RenderProcessHost * host)145 void ChromeContentBrowserClientCef::RenderProcessWillLaunch(
146     content::RenderProcessHost* host) {
147   ChromeContentBrowserClient::RenderProcessWillLaunch(host);
148 
149   // If the renderer process crashes then the host may already have
150   // CefBrowserInfoManager as an observer. Try to remove it first before adding
151   // to avoid DCHECKs.
152   host->RemoveObserver(CefBrowserInfoManager::GetInstance());
153   host->AddObserver(CefBrowserInfoManager::GetInstance());
154 }
155 
CanCreateWindow(content::RenderFrameHost * opener,const GURL & opener_url,const GURL & opener_top_level_frame_url,const url::Origin & source_origin,content::mojom::WindowContainerType container_type,const GURL & target_url,const content::Referrer & referrer,const std::string & frame_name,WindowOpenDisposition disposition,const blink::mojom::WindowFeatures & features,bool user_gesture,bool opener_suppressed,bool * no_javascript_access)156 bool ChromeContentBrowserClientCef::CanCreateWindow(
157     content::RenderFrameHost* opener,
158     const GURL& opener_url,
159     const GURL& opener_top_level_frame_url,
160     const url::Origin& source_origin,
161     content::mojom::WindowContainerType container_type,
162     const GURL& target_url,
163     const content::Referrer& referrer,
164     const std::string& frame_name,
165     WindowOpenDisposition disposition,
166     const blink::mojom::WindowFeatures& features,
167     bool user_gesture,
168     bool opener_suppressed,
169     bool* no_javascript_access) {
170   // The chrome layer has popup blocker, extensions, etc.
171   if (!ChromeContentBrowserClient::CanCreateWindow(
172           opener, opener_url, opener_top_level_frame_url, source_origin,
173           container_type, target_url, referrer, frame_name, disposition,
174           features, user_gesture, opener_suppressed, no_javascript_access)) {
175     return false;
176   }
177 
178   return CefBrowserInfoManager::GetInstance()->CanCreateWindow(
179       opener, target_url, referrer, frame_name, disposition, features,
180       user_gesture, opener_suppressed, no_javascript_access);
181 }
182 
OverrideWebkitPrefs(content::WebContents * web_contents,blink::web_pref::WebPreferences * prefs)183 void ChromeContentBrowserClientCef::OverrideWebkitPrefs(
184     content::WebContents* web_contents,
185     blink::web_pref::WebPreferences* prefs) {
186   renderer_prefs::SetDefaultPrefs(*prefs);
187 
188   ChromeContentBrowserClient::OverrideWebkitPrefs(web_contents, prefs);
189 
190   SkColor base_background_color;
191   auto browser = ChromeBrowserHostImpl::GetBrowserForContents(web_contents);
192   if (browser) {
193     renderer_prefs::SetCefPrefs(browser->settings(), *prefs);
194 
195     // Set the background color for the WebView.
196     base_background_color = browser->GetBackgroundColor();
197   } else {
198     // We don't know for sure that the browser will be windowless but assume
199     // that the global windowless state is likely to be accurate.
200     base_background_color =
201         CefContext::Get()->GetBackgroundColor(nullptr, STATE_DEFAULT);
202   }
203 
204   web_contents->SetPageBaseBackgroundColor(base_background_color);
205 }
206 
WillCreateURLLoaderFactory(content::BrowserContext * browser_context,content::RenderFrameHost * frame,int render_process_id,URLLoaderFactoryType type,const url::Origin & request_initiator,absl::optional<int64_t> navigation_id,ukm::SourceIdObj ukm_source_id,mojo::PendingReceiver<network::mojom::URLLoaderFactory> * factory_receiver,mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient> * header_client,bool * bypass_redirect_checks,bool * disable_secure_dns,network::mojom::URLLoaderFactoryOverridePtr * factory_override)207 bool ChromeContentBrowserClientCef::WillCreateURLLoaderFactory(
208     content::BrowserContext* browser_context,
209     content::RenderFrameHost* frame,
210     int render_process_id,
211     URLLoaderFactoryType type,
212     const url::Origin& request_initiator,
213     absl::optional<int64_t> navigation_id,
214     ukm::SourceIdObj ukm_source_id,
215     mojo::PendingReceiver<network::mojom::URLLoaderFactory>* factory_receiver,
216     mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
217         header_client,
218     bool* bypass_redirect_checks,
219     bool* disable_secure_dns,
220     network::mojom::URLLoaderFactoryOverridePtr* factory_override) {
221   bool use_proxy = ChromeContentBrowserClient::WillCreateURLLoaderFactory(
222       browser_context, frame, render_process_id, type, request_initiator,
223       navigation_id, ukm_source_id, factory_receiver, header_client,
224       bypass_redirect_checks, disable_secure_dns, factory_override);
225   if (use_proxy) {
226     // The chrome layer will handle the request.
227     return use_proxy;
228   }
229 
230   // Don't intercept requests for Profiles that were not created by CEF.
231   // For example, the User Manager profile created via
232   // profiles::CreateSystemProfileForUserManager.
233   auto profile = Profile::FromBrowserContext(browser_context);
234   if (!CefBrowserContext::FromProfile(profile))
235     return false;
236 
237   auto request_handler = net_service::CreateInterceptedRequestHandler(
238       browser_context, frame, render_process_id,
239       type == URLLoaderFactoryType::kNavigation,
240       type == URLLoaderFactoryType::kDownload, request_initiator);
241 
242   net_service::ProxyURLLoaderFactory::CreateProxy(
243       browser_context, factory_receiver, header_client,
244       std::move(request_handler));
245   return true;
246 }
247 
HandleExternalProtocol(const GURL & url,content::WebContents::Getter web_contents_getter,int child_id,int frame_tree_node_id,content::NavigationUIData * navigation_data,bool is_main_frame,network::mojom::WebSandboxFlags sandbox_flags,ui::PageTransition page_transition,bool has_user_gesture,const absl::optional<url::Origin> & initiating_origin,content::RenderFrameHost * initiator_document,mojo::PendingRemote<network::mojom::URLLoaderFactory> * out_factory)248 bool ChromeContentBrowserClientCef::HandleExternalProtocol(
249     const GURL& url,
250     content::WebContents::Getter web_contents_getter,
251     int child_id,
252     int frame_tree_node_id,
253     content::NavigationUIData* navigation_data,
254     bool is_main_frame,
255     network::mojom::WebSandboxFlags sandbox_flags,
256     ui::PageTransition page_transition,
257     bool has_user_gesture,
258     const absl::optional<url::Origin>& initiating_origin,
259     content::RenderFrameHost* initiator_document,
260     mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) {
261   // |out_factory| will be non-nullptr when this method is initially called
262   // from NavigationURLLoaderImpl::PrepareForNonInterceptedRequest.
263   if (out_factory) {
264     // Let the other HandleExternalProtocol variant handle the request.
265     return false;
266   }
267 
268   // The request was unhandled and we've recieved a callback from
269   // HandleExternalProtocolHelper. Forward to the chrome layer for default
270   // handling.
271   return ChromeContentBrowserClient::HandleExternalProtocol(
272       url, web_contents_getter, child_id, frame_tree_node_id, navigation_data,
273       is_main_frame, sandbox_flags, page_transition, has_user_gesture,
274       initiating_origin, initiator_document, nullptr);
275 }
276 
HandleExternalProtocol(content::WebContents::Getter web_contents_getter,int frame_tree_node_id,content::NavigationUIData * navigation_data,network::mojom::WebSandboxFlags sandbox_flags,const network::ResourceRequest & resource_request,const absl::optional<url::Origin> & initiating_origin,content::RenderFrameHost * initiator_document,mojo::PendingRemote<network::mojom::URLLoaderFactory> * out_factory)277 bool ChromeContentBrowserClientCef::HandleExternalProtocol(
278     content::WebContents::Getter web_contents_getter,
279     int frame_tree_node_id,
280     content::NavigationUIData* navigation_data,
281     network::mojom::WebSandboxFlags sandbox_flags,
282     const network::ResourceRequest& resource_request,
283     const absl::optional<url::Origin>& initiating_origin,
284     content::RenderFrameHost* initiator_document,
285     mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) {
286   mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver =
287       out_factory->InitWithNewPipeAndPassReceiver();
288 
289   auto weak_initiator_document = initiator_document
290                                      ? initiator_document->GetWeakDocumentPtr()
291                                      : content::WeakDocumentPtr();
292 
293   // HandleExternalProtocolHelper may be called if nothing handles the request.
294   auto request_handler = net_service::CreateInterceptedRequestHandler(
295       web_contents_getter, frame_tree_node_id, resource_request,
296       base::BindRepeating(HandleExternalProtocolHelper, base::Unretained(this),
297                           web_contents_getter, frame_tree_node_id,
298                           navigation_data, sandbox_flags, resource_request,
299                           initiating_origin,
300                           std::move(weak_initiator_document)));
301 
302   net_service::ProxyURLLoaderFactory::CreateProxy(
303       web_contents_getter, std::move(receiver), std::move(request_handler));
304   return true;
305 }
306 
307 std::vector<std::unique_ptr<content::NavigationThrottle>>
CreateThrottlesForNavigation(content::NavigationHandle * navigation_handle)308 ChromeContentBrowserClientCef::CreateThrottlesForNavigation(
309     content::NavigationHandle* navigation_handle) {
310   auto throttles = ChromeContentBrowserClient::CreateThrottlesForNavigation(
311       navigation_handle);
312   throttle::CreateThrottlesForNavigation(navigation_handle, throttles);
313   return throttles;
314 }
315 
ConfigureNetworkContextParams(content::BrowserContext * context,bool in_memory,const base::FilePath & relative_partition_path,network::mojom::NetworkContextParams * network_context_params,cert_verifier::mojom::CertVerifierCreationParams * cert_verifier_creation_params)316 bool ChromeContentBrowserClientCef::ConfigureNetworkContextParams(
317     content::BrowserContext* context,
318     bool in_memory,
319     const base::FilePath& relative_partition_path,
320     network::mojom::NetworkContextParams* network_context_params,
321     cert_verifier::mojom::CertVerifierCreationParams*
322         cert_verifier_creation_params) {
323   // This method may be called during shutdown when using multi-threaded
324   // message loop mode. In that case exit early to avoid crashes.
325   if (!SystemNetworkContextManager::GetInstance()) {
326     // Cancel NetworkContext creation in
327     // StoragePartitionImpl::InitNetworkContext.
328     return false;
329   }
330 
331   ChromeContentBrowserClient::ConfigureNetworkContextParams(
332       context, in_memory, relative_partition_path, network_context_params,
333       cert_verifier_creation_params);
334 
335   auto cef_context = CefBrowserContext::FromBrowserContext(context);
336   network_context_params->cookieable_schemes =
337       cef_context ? cef_context->GetCookieableSchemes()
338                   : CefBrowserContext::GetGlobalCookieableSchemes();
339 
340   // Prefer the CEF settings configuration, if specified, instead of the
341   // kAcceptLanguages preference which is controlled by the
342   // chrome://settings/languages configuration.
343   const std::string& accept_language_list =
344       browser_prefs::GetAcceptLanguageList(cef_context, /*browser=*/nullptr,
345                                            /*expand=*/true);
346   if (!accept_language_list.empty() &&
347       accept_language_list != network_context_params->accept_language) {
348     network_context_params->accept_language = accept_language_list;
349   }
350 
351   return true;
352 }
353 
354 std::unique_ptr<content::LoginDelegate>
CreateLoginDelegate(const net::AuthChallengeInfo & auth_info,content::WebContents * web_contents,const content::GlobalRequestID & request_id,bool is_request_for_main_frame,const GURL & url,scoped_refptr<net::HttpResponseHeaders> response_headers,bool first_auth_attempt,LoginAuthRequiredCallback auth_required_callback)355 ChromeContentBrowserClientCef::CreateLoginDelegate(
356     const net::AuthChallengeInfo& auth_info,
357     content::WebContents* web_contents,
358     const content::GlobalRequestID& request_id,
359     bool is_request_for_main_frame,
360     const GURL& url,
361     scoped_refptr<net::HttpResponseHeaders> response_headers,
362     bool first_auth_attempt,
363     LoginAuthRequiredCallback auth_required_callback) {
364   // |web_contents| is nullptr for CefURLRequests without an associated frame.
365   if (!web_contents || base::CommandLine::ForCurrentProcess()->HasSwitch(
366                            switches::kDisableChromeLoginPrompt)) {
367     // Delegate auth callbacks to GetAuthCredentials.
368     return std::make_unique<net_service::LoginDelegate>(
369         auth_info, web_contents, request_id, url,
370         std::move(auth_required_callback));
371   }
372 
373   return ChromeContentBrowserClient::CreateLoginDelegate(
374       auth_info, web_contents, request_id, is_request_for_main_frame, url,
375       response_headers, first_auth_attempt, std::move(auth_required_callback));
376 }
377 
BrowserURLHandlerCreated(content::BrowserURLHandler * handler)378 void ChromeContentBrowserClientCef::BrowserURLHandlerCreated(
379     content::BrowserURLHandler* handler) {
380   // Register the Chrome handlers first for proper URL rewriting.
381   ChromeContentBrowserClient::BrowserURLHandlerCreated(handler);
382   scheme::BrowserURLHandlerCreated(handler);
383 }
384 
IsWebUIAllowedToMakeNetworkRequests(const url::Origin & origin)385 bool ChromeContentBrowserClientCef::IsWebUIAllowedToMakeNetworkRequests(
386     const url::Origin& origin) {
387   return scheme::IsWebUIAllowedToMakeNetworkRequests(origin);
388 }
389 
ExposeInterfacesToRenderer(service_manager::BinderRegistry * registry,blink::AssociatedInterfaceRegistry * associated_registry,content::RenderProcessHost * host)390 void ChromeContentBrowserClientCef::ExposeInterfacesToRenderer(
391     service_manager::BinderRegistry* registry,
392     blink::AssociatedInterfaceRegistry* associated_registry,
393     content::RenderProcessHost* host) {
394   ChromeContentBrowserClient::ExposeInterfacesToRenderer(
395       registry, associated_registry, host);
396 
397   CefBrowserManager::ExposeInterfacesToRenderer(registry, associated_registry,
398                                                 host);
399 }
400 
RegisterBrowserInterfaceBindersForFrame(content::RenderFrameHost * render_frame_host,mojo::BinderMapWithContext<content::RenderFrameHost * > * map)401 void ChromeContentBrowserClientCef::RegisterBrowserInterfaceBindersForFrame(
402     content::RenderFrameHost* render_frame_host,
403     mojo::BinderMapWithContext<content::RenderFrameHost*>* map) {
404   ChromeContentBrowserClient::RegisterBrowserInterfaceBindersForFrame(
405       render_frame_host, map);
406 
407   CefBrowserFrame::RegisterBrowserInterfaceBindersForFrame(render_frame_host,
408                                                            map);
409 }
410 
411 CefRefPtr<CefRequestContextImpl>
request_context() const412 ChromeContentBrowserClientCef::request_context() const {
413   return browser_main_parts_->request_context();
414 }
415 
416 scoped_refptr<base::SingleThreadTaskRunner>
background_task_runner() const417 ChromeContentBrowserClientCef::background_task_runner() const {
418   return browser_main_parts_->background_task_runner();
419 }
420 
421 scoped_refptr<base::SingleThreadTaskRunner>
user_visible_task_runner() const422 ChromeContentBrowserClientCef::user_visible_task_runner() const {
423   return browser_main_parts_->user_visible_task_runner();
424 }
425 
426 scoped_refptr<base::SingleThreadTaskRunner>
user_blocking_task_runner() const427 ChromeContentBrowserClientCef::user_blocking_task_runner() const {
428   return browser_main_parts_->user_blocking_task_runner();
429 }
430