411 lines
13 KiB
C++
411 lines
13 KiB
C++
// Copyright 2015 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/renderer/render_manager.h"
|
|
|
|
#include <tuple>
|
|
|
|
#include "build/build_config.h"
|
|
|
|
// Enable deprecation warnings on Windows. See http://crbug.com/585142.
|
|
#if BUILDFLAG(IS_WIN)
|
|
#if defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic error "-Wdeprecated-declarations"
|
|
#else
|
|
#pragma warning(push)
|
|
#pragma warning(default : 4996)
|
|
#endif
|
|
#endif
|
|
|
|
#include "libcef/common/app_manager.h"
|
|
#include "libcef/common/cef_switches.h"
|
|
#include "libcef/common/net/scheme_info.h"
|
|
#include "libcef/common/values_impl.h"
|
|
#include "libcef/renderer/blink_glue.h"
|
|
#include "libcef/renderer/browser_impl.h"
|
|
#include "libcef/renderer/render_frame_observer.h"
|
|
#include "libcef/renderer/thread_util.h"
|
|
#include "libcef/renderer/v8_impl.h"
|
|
|
|
#include "base/command_line.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
#include "cef/libcef/common/mojom/cef.mojom.h"
|
|
#include "content/public/common/content_switches.h"
|
|
#include "content/public/renderer/render_frame.h"
|
|
#include "content/public/renderer/render_thread.h"
|
|
#include "content/public/renderer/render_view.h"
|
|
#include "extensions/common/switches.h"
|
|
#include "mojo/public/cpp/bindings/binder_map.h"
|
|
#include "services/network/public/mojom/cors_origin_pattern.mojom.h"
|
|
#include "third_party/blink/public/platform/web_string.h"
|
|
#include "third_party/blink/public/platform/web_url.h"
|
|
#include "third_party/blink/public/web/web_frame.h"
|
|
#include "third_party/blink/public/web/web_security_policy.h"
|
|
#include "third_party/blink/public/web/web_view.h"
|
|
#include "third_party/blink/public/web/web_view_observer.h"
|
|
|
|
namespace {
|
|
|
|
CefRenderManager* g_manager = nullptr;
|
|
|
|
} // namespace
|
|
|
|
// Placeholder object for guest views.
|
|
class CefGuestView : public blink::WebViewObserver {
|
|
public:
|
|
CefGuestView(CefRenderManager* manager,
|
|
content::RenderView* render_view,
|
|
bool is_windowless)
|
|
: blink::WebViewObserver(render_view->GetWebView()),
|
|
manager_(manager),
|
|
is_windowless_(is_windowless) {}
|
|
|
|
bool is_windowless() const { return is_windowless_; }
|
|
|
|
private:
|
|
// RenderViewObserver methods.
|
|
void OnDestruct() override { manager_->OnGuestViewDestroyed(this); }
|
|
|
|
CefRenderManager* const manager_;
|
|
const bool is_windowless_;
|
|
};
|
|
|
|
CefRenderManager::CefRenderManager() {
|
|
DCHECK(!g_manager);
|
|
g_manager = this;
|
|
}
|
|
|
|
CefRenderManager::~CefRenderManager() {
|
|
g_manager = nullptr;
|
|
}
|
|
|
|
// static
|
|
CefRenderManager* CefRenderManager::Get() {
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
|
return g_manager;
|
|
}
|
|
|
|
void CefRenderManager::RenderThreadConnected() {
|
|
// Retrieve the new render thread information synchronously.
|
|
auto params = cef::mojom::NewRenderThreadInfo::New();
|
|
GetBrowserManager()->GetNewRenderThreadInfo(¶ms);
|
|
|
|
// Cross-origin entries need to be added after WebKit is initialized.
|
|
if (params->cross_origin_whitelist_entries) {
|
|
cross_origin_whitelist_entries_.swap(
|
|
*params->cross_origin_whitelist_entries);
|
|
}
|
|
|
|
WebKitInitialized();
|
|
}
|
|
|
|
void CefRenderManager::RenderFrameCreated(
|
|
content::RenderFrame* render_frame,
|
|
CefRenderFrameObserver* render_frame_observer,
|
|
bool& browser_created,
|
|
absl::optional<bool>& is_windowless) {
|
|
auto browser = MaybeCreateBrowser(render_frame->GetRenderView(), render_frame,
|
|
&browser_created, &is_windowless);
|
|
if (browser) {
|
|
// Attach the frame to the observer for message routing purposes.
|
|
render_frame_observer->AttachFrame(
|
|
browser->GetWebFrameImpl(render_frame->GetWebFrame()).get());
|
|
}
|
|
}
|
|
|
|
void CefRenderManager::WebViewCreated(blink::WebView* web_view,
|
|
bool& browser_created,
|
|
absl::optional<bool>& is_windowless) {
|
|
auto render_view = content::RenderView::FromWebView(web_view);
|
|
CHECK(render_view);
|
|
content::RenderFrame* render_frame = nullptr;
|
|
if (web_view->MainFrame()->IsWebLocalFrame()) {
|
|
render_frame = content::RenderFrame::FromWebFrame(
|
|
web_view->MainFrame()->ToWebLocalFrame());
|
|
}
|
|
|
|
MaybeCreateBrowser(render_view, render_frame, &browser_created,
|
|
&is_windowless);
|
|
}
|
|
|
|
void CefRenderManager::DevToolsAgentAttached() {
|
|
++devtools_agent_count_;
|
|
}
|
|
|
|
void CefRenderManager::DevToolsAgentDetached() {
|
|
--devtools_agent_count_;
|
|
if (devtools_agent_count_ == 0 && uncaught_exception_stack_size_ > 0) {
|
|
// When the last DevToolsAgent is detached the stack size is set to 0.
|
|
// Restore the user-specified stack size here.
|
|
CefV8SetUncaughtExceptionStackSize(uncaught_exception_stack_size_);
|
|
}
|
|
}
|
|
|
|
void CefRenderManager::ExposeInterfacesToBrowser(mojo::BinderMap* binders) {
|
|
auto task_runner = base::SequencedTaskRunnerHandle::Get();
|
|
|
|
binders->Add(
|
|
base::BindRepeating(
|
|
[](CefRenderManager* render_manager,
|
|
mojo::PendingReceiver<cef::mojom::RenderManager> receiver) {
|
|
render_manager->BindReceiver(std::move(receiver));
|
|
},
|
|
base::Unretained(this)),
|
|
task_runner);
|
|
}
|
|
|
|
CefRefPtr<CefBrowserImpl> CefRenderManager::GetBrowserForView(
|
|
content::RenderView* view) {
|
|
BrowserMap::const_iterator it = browsers_.find(view);
|
|
if (it != browsers_.end())
|
|
return it->second;
|
|
return nullptr;
|
|
}
|
|
|
|
CefRefPtr<CefBrowserImpl> CefRenderManager::GetBrowserForMainFrame(
|
|
blink::WebFrame* frame) {
|
|
BrowserMap::const_iterator it = browsers_.begin();
|
|
for (; it != browsers_.end(); ++it) {
|
|
auto web_view = it->second->GetWebView();
|
|
if (web_view && web_view->MainFrame() == frame) {
|
|
return it->second;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
mojo::Remote<cef::mojom::BrowserManager>&
|
|
CefRenderManager::GetBrowserManager() {
|
|
if (!browser_manager_) {
|
|
content::RenderThread::Get()->BindHostReceiver(
|
|
browser_manager_.BindNewPipeAndPassReceiver());
|
|
}
|
|
return browser_manager_;
|
|
}
|
|
|
|
// static
|
|
bool CefRenderManager::IsExtensionProcess() {
|
|
return base::CommandLine::ForCurrentProcess()->HasSwitch(
|
|
extensions::switches::kExtensionProcess);
|
|
}
|
|
|
|
// static
|
|
bool CefRenderManager::IsPdfProcess() {
|
|
return base::CommandLine::ForCurrentProcess()->HasSwitch(
|
|
switches::kPdfRenderer);
|
|
}
|
|
|
|
void CefRenderManager::BindReceiver(
|
|
mojo::PendingReceiver<cef::mojom::RenderManager> receiver) {
|
|
receivers_.Add(this, std::move(receiver));
|
|
}
|
|
|
|
void CefRenderManager::ModifyCrossOriginWhitelistEntry(
|
|
bool add,
|
|
cef::mojom::CrossOriginWhiteListEntryPtr entry) {
|
|
GURL gurl = GURL(entry->source_origin);
|
|
if (add) {
|
|
blink::WebSecurityPolicy::AddOriginAccessAllowListEntry(
|
|
gurl, blink::WebString::FromUTF8(entry->target_protocol),
|
|
blink::WebString::FromUTF8(entry->target_domain),
|
|
/*destination_port=*/0,
|
|
entry->allow_target_subdomains
|
|
? network::mojom::CorsDomainMatchMode::kAllowSubdomains
|
|
: network::mojom::CorsDomainMatchMode::kDisallowSubdomains,
|
|
network::mojom::CorsPortMatchMode::kAllowAnyPort,
|
|
network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
|
|
} else {
|
|
blink::WebSecurityPolicy::ClearOriginAccessListForOrigin(gurl);
|
|
}
|
|
}
|
|
|
|
void CefRenderManager::ClearCrossOriginWhitelist() {
|
|
blink::WebSecurityPolicy::ClearOriginAccessList();
|
|
}
|
|
|
|
void CefRenderManager::WebKitInitialized() {
|
|
const base::CommandLine* command_line =
|
|
base::CommandLine::ForCurrentProcess();
|
|
|
|
// Create global objects associated with the default Isolate.
|
|
CefV8IsolateCreated();
|
|
|
|
const CefAppManager::SchemeInfoList* schemes =
|
|
CefAppManager::Get()->GetCustomSchemes();
|
|
if (!schemes->empty()) {
|
|
// Register the custom schemes. Some attributes are excluded here because
|
|
// they use url/url_util.h APIs instead.
|
|
CefAppManager::SchemeInfoList::const_iterator it = schemes->begin();
|
|
for (; it != schemes->end(); ++it) {
|
|
const CefSchemeInfo& info = *it;
|
|
const blink::WebString& scheme =
|
|
blink::WebString::FromUTF8(info.scheme_name);
|
|
if (info.is_display_isolated)
|
|
blink::WebSecurityPolicy::RegisterURLSchemeAsDisplayIsolated(scheme);
|
|
if (info.is_fetch_enabled)
|
|
blink_glue::RegisterURLSchemeAsSupportingFetchAPI(scheme);
|
|
}
|
|
}
|
|
|
|
if (!cross_origin_whitelist_entries_.empty()) {
|
|
// Add the cross-origin white list entries.
|
|
for (auto& entry : cross_origin_whitelist_entries_) {
|
|
ModifyCrossOriginWhitelistEntry(/*add=*/true, std::move(entry));
|
|
}
|
|
cross_origin_whitelist_entries_.clear();
|
|
}
|
|
|
|
// The number of stack trace frames to capture for uncaught exceptions.
|
|
if (command_line->HasSwitch(switches::kUncaughtExceptionStackSize)) {
|
|
int uncaught_exception_stack_size = 0;
|
|
base::StringToInt(command_line->GetSwitchValueASCII(
|
|
switches::kUncaughtExceptionStackSize),
|
|
&uncaught_exception_stack_size);
|
|
|
|
if (uncaught_exception_stack_size > 0) {
|
|
uncaught_exception_stack_size_ = uncaught_exception_stack_size;
|
|
CefV8SetUncaughtExceptionStackSize(uncaught_exception_stack_size_);
|
|
}
|
|
}
|
|
|
|
// Notify the render process handler.
|
|
CefRefPtr<CefApp> application = CefAppManager::Get()->GetApplication();
|
|
if (application.get()) {
|
|
CefRefPtr<CefRenderProcessHandler> handler =
|
|
application->GetRenderProcessHandler();
|
|
if (handler.get())
|
|
handler->OnWebKitInitialized();
|
|
}
|
|
}
|
|
|
|
CefRefPtr<CefBrowserImpl> CefRenderManager::MaybeCreateBrowser(
|
|
content::RenderView* render_view,
|
|
content::RenderFrame* render_frame,
|
|
bool* browser_created,
|
|
absl::optional<bool>* is_windowless) {
|
|
if (browser_created)
|
|
*browser_created = false;
|
|
|
|
if (!render_view || !render_frame)
|
|
return nullptr;
|
|
|
|
// Don't create another browser or guest view object if one already exists for
|
|
// the view.
|
|
auto browser = GetBrowserForView(render_view);
|
|
if (browser) {
|
|
if (is_windowless) {
|
|
*is_windowless = browser->is_windowless();
|
|
}
|
|
return browser;
|
|
}
|
|
|
|
auto guest_view = GetGuestViewForView(render_view);
|
|
if (guest_view) {
|
|
if (is_windowless) {
|
|
*is_windowless = guest_view->is_windowless();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const bool is_pdf = IsPdfProcess();
|
|
|
|
auto params = cef::mojom::NewBrowserInfo::New();
|
|
if (!is_pdf) {
|
|
// Retrieve browser information synchronously.
|
|
GetBrowserManager()->GetNewBrowserInfo(render_frame->GetRoutingID(),
|
|
¶ms);
|
|
if (params->browser_id == 0) {
|
|
// The popup may have been canceled during creation.
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (is_windowless) {
|
|
*is_windowless = params->is_windowless;
|
|
}
|
|
|
|
if (is_pdf || params->is_guest_view || params->browser_id < 0) {
|
|
// Don't create a CefBrowser for a PDF renderer, guest view, or if the new
|
|
// browser info response has timed out.
|
|
guest_views_.insert(std::make_pair(
|
|
render_view, std::make_unique<CefGuestView>(this, render_view,
|
|
params->is_windowless)));
|
|
return nullptr;
|
|
}
|
|
|
|
browser = new CefBrowserImpl(render_view, params->browser_id,
|
|
params->is_popup, params->is_windowless);
|
|
browsers_.insert(std::make_pair(render_view, browser));
|
|
|
|
// Notify the render process handler.
|
|
CefRefPtr<CefApp> application = CefAppManager::Get()->GetApplication();
|
|
if (application.get()) {
|
|
CefRefPtr<CefRenderProcessHandler> handler =
|
|
application->GetRenderProcessHandler();
|
|
if (handler.get()) {
|
|
CefRefPtr<CefDictionaryValueImpl> dictValuePtr;
|
|
if (params->extra_info) {
|
|
auto& dict_value = base::Value::AsDictionaryValue(*params->extra_info);
|
|
dictValuePtr = new CefDictionaryValueImpl(
|
|
const_cast<base::DictionaryValue*>(&dict_value),
|
|
/*will_delete=*/false, /*read_only=*/true);
|
|
}
|
|
handler->OnBrowserCreated(browser.get(), dictValuePtr.get());
|
|
if (dictValuePtr)
|
|
std::ignore = dictValuePtr->Detach(nullptr);
|
|
}
|
|
}
|
|
|
|
if (browser_created)
|
|
*browser_created = true;
|
|
|
|
return browser;
|
|
}
|
|
|
|
void CefRenderManager::OnBrowserDestroyed(CefBrowserImpl* browser) {
|
|
BrowserMap::iterator it = browsers_.begin();
|
|
for (; it != browsers_.end(); ++it) {
|
|
if (it->second.get() == browser) {
|
|
browsers_.erase(it);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No browser was found in the map.
|
|
NOTREACHED();
|
|
}
|
|
|
|
CefGuestView* CefRenderManager::GetGuestViewForView(content::RenderView* view) {
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
|
|
|
GuestViewMap::const_iterator it = guest_views_.find(view);
|
|
if (it != guest_views_.end())
|
|
return it->second.get();
|
|
return nullptr;
|
|
}
|
|
|
|
void CefRenderManager::OnGuestViewDestroyed(CefGuestView* guest_view) {
|
|
GuestViewMap::iterator it = guest_views_.begin();
|
|
for (; it != guest_views_.end(); ++it) {
|
|
if (it->second.get() == guest_view) {
|
|
guest_views_.erase(it);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No guest view was found in the map.
|
|
NOTREACHED();
|
|
}
|
|
|
|
// Enable deprecation warnings on Windows. See http://crbug.com/585142.
|
|
#if BUILDFLAG(IS_WIN)
|
|
#if defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#else
|
|
#pragma warning(pop)
|
|
#endif
|
|
#endif
|