1 // Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can
3 // be found in the LICENSE file.
4
5 #include "libcef/renderer/render_manager.h"
6
7 #include <tuple>
8
9 #include "base/compiler_specific.h"
10
11 // Enable deprecation warnings on Windows. See http://crbug.com/585142.
12 #if BUILDFLAG(IS_WIN)
13 #if defined(__clang__)
14 #pragma GCC diagnostic push
15 #pragma GCC diagnostic error "-Wdeprecated-declarations"
16 #else
17 #pragma warning(push)
18 #pragma warning(default : 4996)
19 #endif
20 #endif
21
22 #include "libcef/common/app_manager.h"
23 #include "libcef/common/cef_switches.h"
24 #include "libcef/common/net/scheme_info.h"
25 #include "libcef/common/values_impl.h"
26 #include "libcef/renderer/blink_glue.h"
27 #include "libcef/renderer/browser_impl.h"
28 #include "libcef/renderer/render_frame_observer.h"
29 #include "libcef/renderer/thread_util.h"
30 #include "libcef/renderer/v8_impl.h"
31
32 #include "base/command_line.h"
33 #include "base/strings/string_number_conversions.h"
34 #include "cef/libcef/common/mojom/cef.mojom.h"
35 #include "content/public/common/content_switches.h"
36 #include "content/public/renderer/render_frame.h"
37 #include "content/public/renderer/render_thread.h"
38 #include "content/public/renderer/render_view.h"
39 #include "extensions/common/switches.h"
40 #include "mojo/public/cpp/bindings/binder_map.h"
41 #include "services/network/public/mojom/cors_origin_pattern.mojom.h"
42 #include "third_party/blink/public/platform/web_string.h"
43 #include "third_party/blink/public/platform/web_url.h"
44 #include "third_party/blink/public/web/web_security_policy.h"
45 #include "third_party/blink/public/web/web_view.h"
46 #include "third_party/blink/public/web/web_view_observer.h"
47
48 namespace {
49
50 CefRenderManager* g_manager = nullptr;
51
52 } // namespace
53
54 // Placeholder object for guest views.
55 class CefGuestView : public blink::WebViewObserver {
56 public:
CefGuestView(CefRenderManager * manager,content::RenderView * render_view,bool is_windowless)57 CefGuestView(CefRenderManager* manager,
58 content::RenderView* render_view,
59 bool is_windowless)
60 : blink::WebViewObserver(render_view->GetWebView()),
61 manager_(manager),
62 is_windowless_(is_windowless) {}
63
is_windowless() const64 bool is_windowless() const { return is_windowless_; }
65
66 private:
67 // RenderViewObserver methods.
OnDestruct()68 void OnDestruct() override { manager_->OnGuestViewDestroyed(this); }
69
70 CefRenderManager* const manager_;
71 const bool is_windowless_;
72 };
73
CefRenderManager()74 CefRenderManager::CefRenderManager() {
75 DCHECK(!g_manager);
76 g_manager = this;
77 }
78
~CefRenderManager()79 CefRenderManager::~CefRenderManager() {
80 g_manager = nullptr;
81 }
82
83 // static
Get()84 CefRenderManager* CefRenderManager::Get() {
85 CEF_REQUIRE_RT_RETURN(nullptr);
86 return g_manager;
87 }
88
RenderThreadConnected()89 void CefRenderManager::RenderThreadConnected() {
90 // Retrieve the new render thread information synchronously.
91 auto params = cef::mojom::NewRenderThreadInfo::New();
92 GetBrowserManager()->GetNewRenderThreadInfo(¶ms);
93
94 // Cross-origin entries need to be added after WebKit is initialized.
95 if (params->cross_origin_whitelist_entries) {
96 cross_origin_whitelist_entries_.swap(
97 *params->cross_origin_whitelist_entries);
98 }
99
100 WebKitInitialized();
101 }
102
RenderFrameCreated(content::RenderFrame * render_frame,CefRenderFrameObserver * render_frame_observer,bool & browser_created,absl::optional<bool> & is_windowless)103 void CefRenderManager::RenderFrameCreated(
104 content::RenderFrame* render_frame,
105 CefRenderFrameObserver* render_frame_observer,
106 bool& browser_created,
107 absl::optional<bool>& is_windowless) {
108 auto browser = MaybeCreateBrowser(render_frame->GetRenderView(), render_frame,
109 &browser_created, &is_windowless);
110 if (browser) {
111 // Attach the frame to the observer for message routing purposes.
112 render_frame_observer->AttachFrame(
113 browser->GetWebFrameImpl(render_frame->GetWebFrame()).get());
114 }
115 }
116
WebViewCreated(blink::WebView * web_view,bool & browser_created,absl::optional<bool> & is_windowless)117 void CefRenderManager::WebViewCreated(blink::WebView* web_view,
118 bool& browser_created,
119 absl::optional<bool>& is_windowless) {
120 auto render_view = content::RenderView::FromWebView(web_view);
121 CHECK(render_view);
122 content::RenderFrame* render_frame = nullptr;
123 if (web_view->MainFrame()->IsWebLocalFrame()) {
124 render_frame = content::RenderFrame::FromWebFrame(
125 web_view->MainFrame()->ToWebLocalFrame());
126 }
127
128 MaybeCreateBrowser(render_view, render_frame, &browser_created,
129 &is_windowless);
130 }
131
DevToolsAgentAttached()132 void CefRenderManager::DevToolsAgentAttached() {
133 ++devtools_agent_count_;
134 }
135
DevToolsAgentDetached()136 void CefRenderManager::DevToolsAgentDetached() {
137 --devtools_agent_count_;
138 if (devtools_agent_count_ == 0 && uncaught_exception_stack_size_ > 0) {
139 // When the last DevToolsAgent is detached the stack size is set to 0.
140 // Restore the user-specified stack size here.
141 CefV8SetUncaughtExceptionStackSize(uncaught_exception_stack_size_);
142 }
143 }
144
ExposeInterfacesToBrowser(mojo::BinderMap * binders)145 void CefRenderManager::ExposeInterfacesToBrowser(mojo::BinderMap* binders) {
146 auto task_runner = base::SequencedTaskRunnerHandle::Get();
147
148 binders->Add(
149 base::BindRepeating(
150 [](CefRenderManager* render_manager,
151 mojo::PendingReceiver<cef::mojom::RenderManager> receiver) {
152 render_manager->BindReceiver(std::move(receiver));
153 },
154 base::Unretained(this)),
155 task_runner);
156 }
157
GetBrowserForView(content::RenderView * view)158 CefRefPtr<CefBrowserImpl> CefRenderManager::GetBrowserForView(
159 content::RenderView* view) {
160 BrowserMap::const_iterator it = browsers_.find(view);
161 if (it != browsers_.end())
162 return it->second;
163 return nullptr;
164 }
165
GetBrowserForMainFrame(blink::WebFrame * frame)166 CefRefPtr<CefBrowserImpl> CefRenderManager::GetBrowserForMainFrame(
167 blink::WebFrame* frame) {
168 BrowserMap::const_iterator it = browsers_.begin();
169 for (; it != browsers_.end(); ++it) {
170 auto web_view = it->second->GetWebView();
171 if (web_view && web_view->MainFrame() == frame) {
172 return it->second;
173 }
174 }
175
176 return nullptr;
177 }
178
179 mojo::Remote<cef::mojom::BrowserManager>&
GetBrowserManager()180 CefRenderManager::GetBrowserManager() {
181 if (!browser_manager_) {
182 content::RenderThread::Get()->BindHostReceiver(
183 browser_manager_.BindNewPipeAndPassReceiver());
184 }
185 return browser_manager_;
186 }
187
188 // static
IsExtensionProcess()189 bool CefRenderManager::IsExtensionProcess() {
190 return base::CommandLine::ForCurrentProcess()->HasSwitch(
191 extensions::switches::kExtensionProcess);
192 }
193
194 // static
IsPdfProcess()195 bool CefRenderManager::IsPdfProcess() {
196 return base::CommandLine::ForCurrentProcess()->HasSwitch(
197 switches::kPdfRenderer);
198 }
199
BindReceiver(mojo::PendingReceiver<cef::mojom::RenderManager> receiver)200 void CefRenderManager::BindReceiver(
201 mojo::PendingReceiver<cef::mojom::RenderManager> receiver) {
202 receivers_.Add(this, std::move(receiver));
203 }
204
ModifyCrossOriginWhitelistEntry(bool add,cef::mojom::CrossOriginWhiteListEntryPtr entry)205 void CefRenderManager::ModifyCrossOriginWhitelistEntry(
206 bool add,
207 cef::mojom::CrossOriginWhiteListEntryPtr entry) {
208 GURL gurl = GURL(entry->source_origin);
209 if (add) {
210 blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
211 gurl, blink::WebString::FromUTF8(entry->target_protocol),
212 blink::WebString::FromUTF8(entry->target_domain),
213 /*destination_port=*/0,
214 entry->allow_target_subdomains
215 ? network::mojom::CorsDomainMatchMode::kAllowSubdomains
216 : network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
217 network::mojom::CorsPortMatchMode::kAllowAnyPort,
218 network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
219 } else {
220 blink::WebSecurityPolicy::ClearOriginAccessListForOrigin(gurl);
221 }
222 }
223
ClearCrossOriginWhitelist()224 void CefRenderManager::ClearCrossOriginWhitelist() {
225 blink::WebSecurityPolicy::ClearOriginAccessList();
226 }
227
WebKitInitialized()228 void CefRenderManager::WebKitInitialized() {
229 const base::CommandLine* command_line =
230 base::CommandLine::ForCurrentProcess();
231
232 // Create global objects associated with the default Isolate.
233 CefV8IsolateCreated();
234
235 const CefAppManager::SchemeInfoList* schemes =
236 CefAppManager::Get()->GetCustomSchemes();
237 if (!schemes->empty()) {
238 // Register the custom schemes. Some attributes are excluded here because
239 // they use url/url_util.h APIs instead.
240 CefAppManager::SchemeInfoList::const_iterator it = schemes->begin();
241 for (; it != schemes->end(); ++it) {
242 const CefSchemeInfo& info = *it;
243 const blink::WebString& scheme =
244 blink::WebString::FromUTF8(info.scheme_name);
245 if (info.is_display_isolated)
246 blink::WebSecurityPolicy::RegisterURLSchemeAsDisplayIsolated(scheme);
247 if (info.is_fetch_enabled)
248 blink_glue::RegisterURLSchemeAsSupportingFetchAPI(scheme);
249 }
250 }
251
252 if (!cross_origin_whitelist_entries_.empty()) {
253 // Add the cross-origin white list entries.
254 for (auto& entry : cross_origin_whitelist_entries_) {
255 ModifyCrossOriginWhitelistEntry(/*add=*/true, std::move(entry));
256 }
257 cross_origin_whitelist_entries_.clear();
258 }
259
260 // The number of stack trace frames to capture for uncaught exceptions.
261 if (command_line->HasSwitch(switches::kUncaughtExceptionStackSize)) {
262 int uncaught_exception_stack_size = 0;
263 base::StringToInt(command_line->GetSwitchValueASCII(
264 switches::kUncaughtExceptionStackSize),
265 &uncaught_exception_stack_size);
266
267 if (uncaught_exception_stack_size > 0) {
268 uncaught_exception_stack_size_ = uncaught_exception_stack_size;
269 CefV8SetUncaughtExceptionStackSize(uncaught_exception_stack_size_);
270 }
271 }
272
273 // Notify the render process handler.
274 CefRefPtr<CefApp> application = CefAppManager::Get()->GetApplication();
275 if (application.get()) {
276 CefRefPtr<CefRenderProcessHandler> handler =
277 application->GetRenderProcessHandler();
278 if (handler.get())
279 handler->OnWebKitInitialized();
280 }
281 }
282
MaybeCreateBrowser(content::RenderView * render_view,content::RenderFrame * render_frame,bool * browser_created,absl::optional<bool> * is_windowless)283 CefRefPtr<CefBrowserImpl> CefRenderManager::MaybeCreateBrowser(
284 content::RenderView* render_view,
285 content::RenderFrame* render_frame,
286 bool* browser_created,
287 absl::optional<bool>* is_windowless) {
288 if (browser_created)
289 *browser_created = false;
290
291 if (!render_view || !render_frame)
292 return nullptr;
293
294 // Don't create another browser or guest view object if one already exists for
295 // the view.
296 auto browser = GetBrowserForView(render_view);
297 if (browser) {
298 if (is_windowless) {
299 *is_windowless = browser->is_windowless();
300 }
301 return browser;
302 }
303
304 auto guest_view = GetGuestViewForView(render_view);
305 if (guest_view) {
306 if (is_windowless) {
307 *is_windowless = guest_view->is_windowless();
308 }
309 return nullptr;
310 }
311
312 const bool is_pdf = IsPdfProcess();
313
314 auto params = cef::mojom::NewBrowserInfo::New();
315 if (!is_pdf) {
316 // Retrieve browser information synchronously.
317 GetBrowserManager()->GetNewBrowserInfo(render_frame->GetRoutingID(),
318 ¶ms);
319 if (params->browser_id == 0) {
320 // The popup may have been canceled during creation.
321 return nullptr;
322 }
323 }
324
325 if (is_windowless) {
326 *is_windowless = params->is_windowless;
327 }
328
329 if (is_pdf || params->is_guest_view || params->browser_id < 0) {
330 // Don't create a CefBrowser for a PDF renderer, guest view, or if the new
331 // browser info response has timed out.
332 guest_views_.insert(std::make_pair(
333 render_view, std::make_unique<CefGuestView>(this, render_view,
334 params->is_windowless)));
335 return nullptr;
336 }
337
338 browser = new CefBrowserImpl(render_view, params->browser_id,
339 params->is_popup, params->is_windowless);
340 browsers_.insert(std::make_pair(render_view, browser));
341
342 // Notify the render process handler.
343 CefRefPtr<CefApp> application = CefAppManager::Get()->GetApplication();
344 if (application.get()) {
345 CefRefPtr<CefRenderProcessHandler> handler =
346 application->GetRenderProcessHandler();
347 if (handler.get()) {
348 CefRefPtr<CefDictionaryValueImpl> dictValuePtr;
349 if (params->extra_info) {
350 auto& dict_value = base::Value::AsDictionaryValue(*params->extra_info);
351 dictValuePtr = new CefDictionaryValueImpl(
352 const_cast<base::DictionaryValue*>(&dict_value),
353 /*will_delete=*/false, /*read_only=*/true);
354 }
355 handler->OnBrowserCreated(browser.get(), dictValuePtr.get());
356 if (dictValuePtr)
357 std::ignore = dictValuePtr->Detach(nullptr);
358 }
359 }
360
361 if (browser_created)
362 *browser_created = true;
363
364 return browser;
365 }
366
OnBrowserDestroyed(CefBrowserImpl * browser)367 void CefRenderManager::OnBrowserDestroyed(CefBrowserImpl* browser) {
368 BrowserMap::iterator it = browsers_.begin();
369 for (; it != browsers_.end(); ++it) {
370 if (it->second.get() == browser) {
371 browsers_.erase(it);
372 return;
373 }
374 }
375
376 // No browser was found in the map.
377 NOTREACHED();
378 }
379
GetGuestViewForView(content::RenderView * view)380 CefGuestView* CefRenderManager::GetGuestViewForView(content::RenderView* view) {
381 CEF_REQUIRE_RT_RETURN(nullptr);
382
383 GuestViewMap::const_iterator it = guest_views_.find(view);
384 if (it != guest_views_.end())
385 return it->second.get();
386 return nullptr;
387 }
388
OnGuestViewDestroyed(CefGuestView * guest_view)389 void CefRenderManager::OnGuestViewDestroyed(CefGuestView* guest_view) {
390 GuestViewMap::iterator it = guest_views_.begin();
391 for (; it != guest_views_.end(); ++it) {
392 if (it->second.get() == guest_view) {
393 guest_views_.erase(it);
394 return;
395 }
396 }
397
398 // No guest view was found in the map.
399 NOTREACHED();
400 }
401
402 // Enable deprecation warnings on Windows. See http://crbug.com/585142.
403 #if BUILDFLAG(IS_WIN)
404 #if defined(__clang__)
405 #pragma GCC diagnostic pop
406 #else
407 #pragma warning(pop)
408 #endif
409 #endif
410