cef/libcef/renderer/browser_manager.cc

339 lines
11 KiB
C++
Raw Normal View History

Add chrome runtime support for more callbacks and ceftests (see issue #2969) This change adds support for: - Protocol and request handling. - Loading and navigation events. - Display and focus events. - Mouse/keyboard events. - Popup browsers. - Callbacks in the renderer process. - Misc. functionality required for ceftests. This change also adds a new CefBrowserProcessHandler::GetCookieableSchemes callback for configuring global state that will be applied to all CefCookieManagers by default. This global callback is currently required by the chrome runtime because the primary ProfileImpl is created via ChromeBrowserMainParts::PreMainMessageLoopRun (CreatePrimaryProfile) before OnContextCreated can be called. ProfileImpl will use the "C:\Users\[user]\AppData\Local\CEF\User Data\Default" directory by default (on Windows). Cookies may persist in this directory when running ceftests and may need to be manually deleted if those tests fail. Remaining work includes: - Support for client-created request contexts. - Embedding the browser in a Views hierarchy (cefclient support). - TryCloseBrowser and DoClose support. - Most of the CefSettings configuration. - DevTools protocol and window control (ShowDevTools, ExecuteDevToolsMethod). - CEF-specific WebUI pages (about, license, webui-hosts). - Context menu customization (CefContextMenuHandler). - Auto resize (SetAutoResizeEnabled). - Zoom settings (SetZoomLevel). - File dialog runner (RunFileDialog). - File and JS dialog handlers (CefDialogHandler, CefJSDialogHandler). - Extension loading (LoadExtension, etc). - Plugin loading (OnBeforePluginLoad). - Widevine loading (CefRegisterWidevineCdm). - PDF and print preview does not display. - Crash reporting is untested. - Mac: Web content loads but does not display. The following ceftests are now passing when run with the "--enable-chrome-runtime" command-line flag: CorsTest.* DisplayTest.*:-DisplayTest.AutoResize DOMTest.* DraggableRegionsTest.* ImageTest.* MessageRouterTest.* NavigationTest.* ParserTest.* RequestContextTest.*Global* RequestTest.* ResourceManagerTest.* ResourceRequestHandlerTest.* ResponseTest.* SchemeHandlerTest.* ServerTest.* StreamResourceHandlerTest.* StreamTest.* StringTest.* TaskTest.* TestServerTest.* ThreadTest.* URLRequestTest.*Global* V8Test.*:-V8Test.OnUncaughtExceptionDevTools ValuesTest.* WaitableEventTest.* XmlReaderTest.* ZipReaderTest.*
2020-09-25 03:40:47 +02:00
// 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/browser_manager.h"
#include "base/compiler_specific.h"
// Enable deprecation warnings on Windows. See http://crbug.com/585142.
#if defined(OS_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_messages.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 "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
#include "content/public/renderer/render_view_observer.h"
#include "services/network/public/mojom/cors_origin_pattern.mojom.h"
#include "third_party/blink/public/web/web_security_policy.h"
#include "third_party/blink/public/web/web_view.h"
namespace {
CefBrowserManager* g_manager = nullptr;
} // namespace
// Placeholder object for guest views.
class CefGuestView : public content::RenderViewObserver {
public:
CefGuestView(CefBrowserManager* manager,
content::RenderView* render_view,
bool is_windowless)
: content::RenderViewObserver(render_view),
manager_(manager),
is_windowless_(is_windowless) {}
bool is_windowless() const { return is_windowless_; }
private:
// RenderViewObserver methods.
void OnDestruct() override { manager_->OnGuestViewDestroyed(this); }
CefBrowserManager* const manager_;
const bool is_windowless_;
};
CefBrowserManager::CefBrowserManager() {
DCHECK(!g_manager);
g_manager = this;
}
CefBrowserManager::~CefBrowserManager() {
g_manager = nullptr;
}
// static
CefBrowserManager* CefBrowserManager::Get() {
CEF_REQUIRE_RT_RETURN(nullptr);
return g_manager;
}
void CefBrowserManager::RenderThreadConnected() {
content::RenderThread* thread = content::RenderThread::Get();
// Retrieve the new render thread information synchronously.
CefProcessHostMsg_GetNewRenderThreadInfo_Params params;
thread->Send(new CefProcessHostMsg_GetNewRenderThreadInfo(&params));
// Cross-origin entries need to be added after WebKit is initialized.
cross_origin_whitelist_entries_ = params.cross_origin_whitelist_entries;
WebKitInitialized();
}
void CefBrowserManager::RenderFrameCreated(
content::RenderFrame* render_frame,
CefRenderFrameObserver* render_frame_observer,
bool& browser_created,
base::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 CefBrowserManager::RenderViewCreated(content::RenderView* render_view,
bool& browser_created,
base::Optional<bool>& is_windowless) {
MaybeCreateBrowser(render_view, render_view->GetMainRenderFrame(),
&browser_created, &is_windowless);
}
void CefBrowserManager::DevToolsAgentAttached() {
++devtools_agent_count_;
}
void CefBrowserManager::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_);
}
}
CefRefPtr<CefBrowserImpl> CefBrowserManager::GetBrowserForView(
content::RenderView* view) {
BrowserMap::const_iterator it = browsers_.find(view);
if (it != browsers_.end())
return it->second;
return nullptr;
}
CefRefPtr<CefBrowserImpl> CefBrowserManager::GetBrowserForMainFrame(
blink::WebFrame* frame) {
BrowserMap::const_iterator it = browsers_.begin();
for (; it != browsers_.end(); ++it) {
content::RenderView* render_view = it->second->render_view();
if (render_view && render_view->GetWebView() &&
render_view->GetWebView()->MainFrame() == frame) {
return it->second;
}
}
return nullptr;
}
void CefBrowserManager::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.
Add chrome runtime support for more callbacks and ceftests (see issue #2969) This change adds support for: - Protocol and request handling. - Loading and navigation events. - Display and focus events. - Mouse/keyboard events. - Popup browsers. - Callbacks in the renderer process. - Misc. functionality required for ceftests. This change also adds a new CefBrowserProcessHandler::GetCookieableSchemes callback for configuring global state that will be applied to all CefCookieManagers by default. This global callback is currently required by the chrome runtime because the primary ProfileImpl is created via ChromeBrowserMainParts::PreMainMessageLoopRun (CreatePrimaryProfile) before OnContextCreated can be called. ProfileImpl will use the "C:\Users\[user]\AppData\Local\CEF\User Data\Default" directory by default (on Windows). Cookies may persist in this directory when running ceftests and may need to be manually deleted if those tests fail. Remaining work includes: - Support for client-created request contexts. - Embedding the browser in a Views hierarchy (cefclient support). - TryCloseBrowser and DoClose support. - Most of the CefSettings configuration. - DevTools protocol and window control (ShowDevTools, ExecuteDevToolsMethod). - CEF-specific WebUI pages (about, license, webui-hosts). - Context menu customization (CefContextMenuHandler). - Auto resize (SetAutoResizeEnabled). - Zoom settings (SetZoomLevel). - File dialog runner (RunFileDialog). - File and JS dialog handlers (CefDialogHandler, CefJSDialogHandler). - Extension loading (LoadExtension, etc). - Plugin loading (OnBeforePluginLoad). - Widevine loading (CefRegisterWidevineCdm). - PDF and print preview does not display. - Crash reporting is untested. - Mac: Web content loads but does not display. The following ceftests are now passing when run with the "--enable-chrome-runtime" command-line flag: CorsTest.* DisplayTest.*:-DisplayTest.AutoResize DOMTest.* DraggableRegionsTest.* ImageTest.* MessageRouterTest.* NavigationTest.* ParserTest.* RequestContextTest.*Global* RequestTest.* ResourceManagerTest.* ResourceRequestHandlerTest.* ResponseTest.* SchemeHandlerTest.* ServerTest.* StreamResourceHandlerTest.* StreamTest.* StringTest.* TaskTest.* TestServerTest.* ThreadTest.* URLRequestTest.*Global* V8Test.*:-V8Test.OnUncaughtExceptionDevTools ValuesTest.* WaitableEventTest.* XmlReaderTest.* ZipReaderTest.*
2020-09-25 03:40:47 +02:00
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 (size_t i = 0; i < cross_origin_whitelist_entries_.size(); ++i) {
const Cef_CrossOriginWhiteListEntry_Params& entry =
cross_origin_whitelist_entries_[i];
GURL gurl = GURL(entry.source_origin);
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);
}
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> CefBrowserManager::MaybeCreateBrowser(
content::RenderView* render_view,
content::RenderFrame* render_frame,
bool* browser_created,
base::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 int render_frame_routing_id = render_frame->GetRoutingID();
// Retrieve the browser information synchronously. This will also register
// the routing ids with the browser info object in the browser process.
CefProcessHostMsg_GetNewBrowserInfo_Params params;
content::RenderThread::Get()->Send(new CefProcessHostMsg_GetNewBrowserInfo(
render_frame_routing_id, &params));
if (is_windowless) {
*is_windowless = params.is_windowless;
}
if (params.browser_id == 0) {
// The popup may have been canceled during creation.
return nullptr;
}
if (params.is_guest_view || params.browser_id < 0) {
// Don't create a CefBrowser for guest views, 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(
new CefDictionaryValueImpl(&params.extra_info, false, true));
handler->OnBrowserCreated(browser.get(), dictValuePtr.get());
dictValuePtr->Detach(nullptr);
}
}
if (browser_created)
*browser_created = true;
return browser;
}
void CefBrowserManager::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* CefBrowserManager::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 CefBrowserManager::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 defined(OS_WIN)
#if defined(__clang__)
#pragma GCC diagnostic pop
#else
#pragma warning(pop)
#endif
#endif