• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&params);
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                                            &params);
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