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/browser_manager.h"
6
7 #include "base/compiler_specific.h"
8
9 // Enable deprecation warnings on Windows. See http://crbug.com/585142.
10 #if defined(OS_WIN)
11 #if defined(__clang__)
12 #pragma GCC diagnostic push
13 #pragma GCC diagnostic error "-Wdeprecated-declarations"
14 #else
15 #pragma warning(push)
16 #pragma warning(default : 4996)
17 #endif
18 #endif
19
20 #include "libcef/common/app_manager.h"
21 #include "libcef/common/cef_messages.h"
22 #include "libcef/common/cef_switches.h"
23 #include "libcef/common/net/scheme_info.h"
24 #include "libcef/common/values_impl.h"
25 #include "libcef/renderer/blink_glue.h"
26 #include "libcef/renderer/browser_impl.h"
27 #include "libcef/renderer/render_frame_observer.h"
28 #include "libcef/renderer/thread_util.h"
29 #include "libcef/renderer/v8_impl.h"
30
31 #include "base/command_line.h"
32 #include "base/strings/string_number_conversions.h"
33 #include "content/public/renderer/render_frame.h"
34 #include "content/public/renderer/render_thread.h"
35 #include "content/public/renderer/render_view.h"
36 #include "services/network/public/mojom/cors_origin_pattern.mojom.h"
37 #include "third_party/blink/public/web/web_security_policy.h"
38 #include "third_party/blink/public/web/web_view.h"
39 #include "third_party/blink/public/web/web_view_observer.h"
40
41 namespace {
42
43 CefBrowserManager* g_manager = nullptr;
44
45 } // namespace
46
47 // Placeholder object for guest views.
48 class CefGuestView : public blink::WebViewObserver {
49 public:
CefGuestView(CefBrowserManager * manager,content::RenderView * render_view,bool is_windowless)50 CefGuestView(CefBrowserManager* manager,
51 content::RenderView* render_view,
52 bool is_windowless)
53 : blink::WebViewObserver(render_view->GetWebView()),
54 manager_(manager),
55 is_windowless_(is_windowless) {}
56
is_windowless() const57 bool is_windowless() const { return is_windowless_; }
58
59 private:
60 // RenderViewObserver methods.
OnDestruct()61 void OnDestruct() override { manager_->OnGuestViewDestroyed(this); }
62
63 CefBrowserManager* const manager_;
64 const bool is_windowless_;
65 };
66
CefBrowserManager()67 CefBrowserManager::CefBrowserManager() {
68 DCHECK(!g_manager);
69 g_manager = this;
70 }
71
~CefBrowserManager()72 CefBrowserManager::~CefBrowserManager() {
73 g_manager = nullptr;
74 }
75
76 // static
Get()77 CefBrowserManager* CefBrowserManager::Get() {
78 CEF_REQUIRE_RT_RETURN(nullptr);
79 return g_manager;
80 }
81
RenderThreadConnected()82 void CefBrowserManager::RenderThreadConnected() {
83 content::RenderThread* thread = content::RenderThread::Get();
84
85 // Retrieve the new render thread information synchronously.
86 CefProcessHostMsg_GetNewRenderThreadInfo_Params params;
87 thread->Send(new CefProcessHostMsg_GetNewRenderThreadInfo(¶ms));
88
89 // Cross-origin entries need to be added after WebKit is initialized.
90 cross_origin_whitelist_entries_ = params.cross_origin_whitelist_entries;
91
92 WebKitInitialized();
93 }
94
RenderFrameCreated(content::RenderFrame * render_frame,CefRenderFrameObserver * render_frame_observer,bool & browser_created,base::Optional<bool> & is_windowless)95 void CefBrowserManager::RenderFrameCreated(
96 content::RenderFrame* render_frame,
97 CefRenderFrameObserver* render_frame_observer,
98 bool& browser_created,
99 base::Optional<bool>& is_windowless) {
100 auto browser = MaybeCreateBrowser(render_frame->GetRenderView(), render_frame,
101 &browser_created, &is_windowless);
102 if (browser) {
103 // Attach the frame to the observer for message routing purposes.
104 render_frame_observer->AttachFrame(
105 browser->GetWebFrameImpl(render_frame->GetWebFrame()).get());
106 }
107 }
108
RenderViewCreated(content::RenderView * render_view,bool & browser_created,base::Optional<bool> & is_windowless)109 void CefBrowserManager::RenderViewCreated(content::RenderView* render_view,
110 bool& browser_created,
111 base::Optional<bool>& is_windowless) {
112 MaybeCreateBrowser(render_view, render_view->GetMainRenderFrame(),
113 &browser_created, &is_windowless);
114 }
115
DevToolsAgentAttached()116 void CefBrowserManager::DevToolsAgentAttached() {
117 ++devtools_agent_count_;
118 }
119
DevToolsAgentDetached()120 void CefBrowserManager::DevToolsAgentDetached() {
121 --devtools_agent_count_;
122 if (devtools_agent_count_ == 0 && uncaught_exception_stack_size_ > 0) {
123 // When the last DevToolsAgent is detached the stack size is set to 0.
124 // Restore the user-specified stack size here.
125 CefV8SetUncaughtExceptionStackSize(uncaught_exception_stack_size_);
126 }
127 }
128
GetBrowserForView(content::RenderView * view)129 CefRefPtr<CefBrowserImpl> CefBrowserManager::GetBrowserForView(
130 content::RenderView* view) {
131 BrowserMap::const_iterator it = browsers_.find(view);
132 if (it != browsers_.end())
133 return it->second;
134 return nullptr;
135 }
136
GetBrowserForMainFrame(blink::WebFrame * frame)137 CefRefPtr<CefBrowserImpl> CefBrowserManager::GetBrowserForMainFrame(
138 blink::WebFrame* frame) {
139 BrowserMap::const_iterator it = browsers_.begin();
140 for (; it != browsers_.end(); ++it) {
141 auto web_view = it->second->GetWebView();
142 if (web_view && web_view->MainFrame() == frame) {
143 return it->second;
144 }
145 }
146
147 return nullptr;
148 }
149
WebKitInitialized()150 void CefBrowserManager::WebKitInitialized() {
151 const base::CommandLine* command_line =
152 base::CommandLine::ForCurrentProcess();
153
154 // Create global objects associated with the default Isolate.
155 CefV8IsolateCreated();
156
157 const CefAppManager::SchemeInfoList* schemes =
158 CefAppManager::Get()->GetCustomSchemes();
159 if (!schemes->empty()) {
160 // Register the custom schemes. Some attributes are excluded here because
161 // they use url/url_util.h APIs instead.
162 CefAppManager::SchemeInfoList::const_iterator it = schemes->begin();
163 for (; it != schemes->end(); ++it) {
164 const CefSchemeInfo& info = *it;
165 const blink::WebString& scheme =
166 blink::WebString::FromUTF8(info.scheme_name);
167 if (info.is_display_isolated)
168 blink::WebSecurityPolicy::RegisterURLSchemeAsDisplayIsolated(scheme);
169 if (info.is_fetch_enabled)
170 blink_glue::RegisterURLSchemeAsSupportingFetchAPI(scheme);
171 }
172 }
173
174 if (!cross_origin_whitelist_entries_.empty()) {
175 // Add the cross-origin white list entries.
176 for (size_t i = 0; i < cross_origin_whitelist_entries_.size(); ++i) {
177 const Cef_CrossOriginWhiteListEntry_Params& entry =
178 cross_origin_whitelist_entries_[i];
179 GURL gurl = GURL(entry.source_origin);
180 blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
181 gurl, blink::WebString::FromUTF8(entry.target_protocol),
182 blink::WebString::FromUTF8(entry.target_domain),
183 /*destination_port=*/0,
184 entry.allow_target_subdomains
185 ? network::mojom::CorsDomainMatchMode::kAllowSubdomains
186 : network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
187 network::mojom::CorsPortMatchMode::kAllowAnyPort,
188 network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
189 }
190 cross_origin_whitelist_entries_.clear();
191 }
192
193 // The number of stack trace frames to capture for uncaught exceptions.
194 if (command_line->HasSwitch(switches::kUncaughtExceptionStackSize)) {
195 int uncaught_exception_stack_size = 0;
196 base::StringToInt(command_line->GetSwitchValueASCII(
197 switches::kUncaughtExceptionStackSize),
198 &uncaught_exception_stack_size);
199
200 if (uncaught_exception_stack_size > 0) {
201 uncaught_exception_stack_size_ = uncaught_exception_stack_size;
202 CefV8SetUncaughtExceptionStackSize(uncaught_exception_stack_size_);
203 }
204 }
205
206 // Notify the render process handler.
207 CefRefPtr<CefApp> application = CefAppManager::Get()->GetApplication();
208 if (application.get()) {
209 CefRefPtr<CefRenderProcessHandler> handler =
210 application->GetRenderProcessHandler();
211 if (handler.get())
212 handler->OnWebKitInitialized();
213 }
214 }
215
MaybeCreateBrowser(content::RenderView * render_view,content::RenderFrame * render_frame,bool * browser_created,base::Optional<bool> * is_windowless)216 CefRefPtr<CefBrowserImpl> CefBrowserManager::MaybeCreateBrowser(
217 content::RenderView* render_view,
218 content::RenderFrame* render_frame,
219 bool* browser_created,
220 base::Optional<bool>* is_windowless) {
221 if (browser_created)
222 *browser_created = false;
223
224 if (!render_view || !render_frame)
225 return nullptr;
226
227 // Don't create another browser or guest view object if one already exists for
228 // the view.
229 auto browser = GetBrowserForView(render_view);
230 if (browser) {
231 if (is_windowless) {
232 *is_windowless = browser->is_windowless();
233 }
234 return browser;
235 }
236
237 auto guest_view = GetGuestViewForView(render_view);
238 if (guest_view) {
239 if (is_windowless) {
240 *is_windowless = guest_view->is_windowless();
241 }
242 return nullptr;
243 }
244
245 const int render_frame_routing_id = render_frame->GetRoutingID();
246
247 // Retrieve the browser information synchronously. This will also register
248 // the routing ids with the browser info object in the browser process.
249 CefProcessHostMsg_GetNewBrowserInfo_Params params;
250 content::RenderThread::Get()->Send(new CefProcessHostMsg_GetNewBrowserInfo(
251 render_frame_routing_id, ¶ms));
252
253 if (is_windowless) {
254 *is_windowless = params.is_windowless;
255 }
256
257 if (params.browser_id == 0) {
258 // The popup may have been canceled during creation.
259 return nullptr;
260 }
261
262 if (params.is_guest_view || params.browser_id < 0) {
263 // Don't create a CefBrowser for guest views, or if the new browser info
264 // response has timed out.
265 guest_views_.insert(std::make_pair(
266 render_view, std::make_unique<CefGuestView>(this, render_view,
267 params.is_windowless)));
268 return nullptr;
269 }
270
271 browser = new CefBrowserImpl(render_view, params.browser_id, params.is_popup,
272 params.is_windowless);
273 browsers_.insert(std::make_pair(render_view, browser));
274
275 // Notify the render process handler.
276 CefRefPtr<CefApp> application = CefAppManager::Get()->GetApplication();
277 if (application.get()) {
278 CefRefPtr<CefRenderProcessHandler> handler =
279 application->GetRenderProcessHandler();
280 if (handler.get()) {
281 CefRefPtr<CefDictionaryValueImpl> dictValuePtr(
282 new CefDictionaryValueImpl(¶ms.extra_info, false, true));
283 handler->OnBrowserCreated(browser.get(), dictValuePtr.get());
284 dictValuePtr->Detach(nullptr);
285 }
286 }
287
288 if (browser_created)
289 *browser_created = true;
290
291 return browser;
292 }
293
OnBrowserDestroyed(CefBrowserImpl * browser)294 void CefBrowserManager::OnBrowserDestroyed(CefBrowserImpl* browser) {
295 BrowserMap::iterator it = browsers_.begin();
296 for (; it != browsers_.end(); ++it) {
297 if (it->second.get() == browser) {
298 browsers_.erase(it);
299 return;
300 }
301 }
302
303 // No browser was found in the map.
304 NOTREACHED();
305 }
306
GetGuestViewForView(content::RenderView * view)307 CefGuestView* CefBrowserManager::GetGuestViewForView(
308 content::RenderView* view) {
309 CEF_REQUIRE_RT_RETURN(nullptr);
310
311 GuestViewMap::const_iterator it = guest_views_.find(view);
312 if (it != guest_views_.end())
313 return it->second.get();
314 return nullptr;
315 }
316
OnGuestViewDestroyed(CefGuestView * guest_view)317 void CefBrowserManager::OnGuestViewDestroyed(CefGuestView* guest_view) {
318 GuestViewMap::iterator it = guest_views_.begin();
319 for (; it != guest_views_.end(); ++it) {
320 if (it->second.get() == guest_view) {
321 guest_views_.erase(it);
322 return;
323 }
324 }
325
326 // No guest view was found in the map.
327 NOTREACHED();
328 }
329
330 // Enable deprecation warnings on Windows. See http://crbug.com/585142.
331 #if defined(OS_WIN)
332 #if defined(__clang__)
333 #pragma GCC diagnostic pop
334 #else
335 #pragma warning(pop)
336 #endif
337 #endif
338