Off-Screen Rendering Results in Invalid OnPaint Buffer
Posted: Sat Sep 12, 2020 1:07 am
Hi! So I've created a mini-example to simulate OSR in my binary. Here are my files:
ClientApp.cpp
ClientApp.h
ClientHandler.cpp
ClientHandler.h
main.cpp
My issue is that when the OnPaint() callback is called my memcpy(...) function has a segmentation fault. This I because my buffer's size seems to be 8 for some reason. Does anyone see any issues with my code that would cause my OSR to result in an invalid buffer?
ClientApp.cpp
- Code: Select all
#include "ClientApp.h"
#include "ClientHandler.h"
#include <include/wrapper/cef_helpers.h>
#include <include/views/cef_browser_view.h>
class MyCefRenderHandler : public CefRenderHandler
{
public:
MyCefRenderHandler(int w, int h) :
width(w),
height(h)
{
}
~MyCefRenderHandler()
{
}
void
GetViewRect(
CefRefPtr<CefBrowser> browser,
CefRect &rect)
{
rect = CefRect(0, 0, width, height);
}
void
OnPaint(
CefRefPtr<CefBrowser> browser,
PaintElementType type,
const RectList &dirtyRects,
const void *buffer,
int w,
int h)
{
memcpy(my_buffer, buffer, w * h * 4);
printf("size: %ld\n", sizeof(buffer));
SaveBuffer();
}
void
SaveBuffer()
{
printf("This is where I'll eventually save my render to a file\n");
// FILE *rendered_file;
// rendered_file = fopen("test_1920x1080.bgra", "wb");
// fwrite(my_buffer, sizeof(uint8_t), sizeof(my_buffer), rendered_file);
//fclose(rendered_file);
}
private:
int width;
int height;
/* Off-sceen rendering parameters */
uint8_t *my_buffer;
int macro_count = 0;
IMPLEMENT_REFCOUNTING(MyCefRenderHandler);
};
ClientApp::ClientApp()
{
}
void
ClientApp::OnContextInitialized()
{
CEF_REQUIRE_UI_THREAD();
CefRefPtr<CefCommandLine> command_line = CefCommandLine::GetGlobalCommandLine();
int width = 1920;
int height = 1080;
/* ClientHandler implements browser-level callbacks */
MyCefRenderHandler *myCefRenderHandler = new MyCefRenderHandler(width, height);
/* MyCefRenderHandlerimplements off-screen rendering callbacks */
CefRefPtr<ClientHandler> handler = new ClientHandler(myCefRenderHandler);
/* Specify CEF browser settings here */
CefBrowserSettings browser_settings;
browser_settings.webgl = STATE_ENABLED;
/* Check if a "--url=" value was provided via the command-line. If so, use that instead of the default URL */
std::string url;
url = command_line->GetSwitchValue("url");
if (url.empty())
{
url = "http://www.google.com";
}
/* Information used when creating the native window */
CefWindowInfo window_info;
window_info.SetAsWindowless(0);
window_info.windowless_rendering_enabled = true;
/* Create the first browser window (default) */
CefBrowserHost::CreateBrowser(window_info, handler, url, browser_settings, NULL, NULL);
}
void
ClientApp::OnBeforeCommandLineProcessing(
const CefString& process_type,
CefRefPtr<CefCommandLine> command_line)
{
printf("ClientApp OnBeforeCommandLineProcessing\n");
/* Add "single-process" to the commandline if it doesn not already exist */
if (!command_line->HasSwitch("disable-gpu"))
{
command_line->AppendSwitch("disable-gpu");
}
if (!command_line->HasSwitch("off-screen-rendering-enabled"))
{
command_line->AppendSwitch("off-screen-rendering-enabled");
}
if (!command_line->HasSwitch("disable-gpu-compositing"))
{
command_line->AppendSwitch("disable-gpu-compositingd");
}
if (!command_line->HasSwitch("enable-begin-frame-scheduling"))
{
command_line->AppendSwitch("enable-begin-frame-scheduling");
}
if (!command_line->HasSwitch("disable-d3d11"))
{
command_line->AppendSwitch("disable-d3d11");
}
}
ClientApp.h
- Code: Select all
#pragma once
#include <include/cef_app.h>
#include <include/cef_client.h>
class ClientApp : public CefApp,
public CefBrowserProcessHandler,
public CefRenderProcessHandler
{
public:
ClientApp();
/* CefApp methods */
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() OVERRIDE { return this; }
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE { return this; }
/* CefBrowserProcessHandler methods */
virtual void OnContextInitialized() OVERRIDE;
/* Programmatically configure command-line arguments */
virtual void OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr<CefCommandLine> command_line) OVERRIDE;
private:
/* Include the default reference counting implementation */
IMPLEMENT_REFCOUNTING(ClientApp);
};
ClientHandler.cpp
- Code: Select all
#include "ClientHandler.h"
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <sstream>
#include <string>
#include <include/base/cef_bind.h>
#include <include/cef_app.h>
#include <include/cef_parser.h>
#include <include/views/cef_browser_view.h>
#include <include/views/cef_window.h>
#include <include/wrapper/cef_closure_task.h>
#include <include/wrapper/cef_helpers.h>
ClientHandler *g_instance = NULL;
void
ClientHandler::PlatformTitleChange(
CefRefPtr<CefBrowser> browser,
const CefString& title)
{
std::string titleStr(title);
#if defined(USE_X11)
/* Retrieve the X11 display shared with Chromium */
::Display* display = cef_get_xdisplay();
DCHECK(display);
/* Retrieve the X11 window handle for the browser */
::Window window = browser->GetHost()->GetWindowHandle();
DCHECK(window != kNullWindowHandle);
/* Retrieve the atoms required by the below XChangeProperty call */
const char* kAtoms[] = {"_NET_WM_NAME", "UTF8_STRING"};
Atom atoms[2];
int result = XInternAtoms(display, const_cast<char**>(kAtoms), 2, false, atoms);
if (!result)
{
NOTREACHED();
}
/* Set the window title */
XChangeProperty(display, window, atoms[0], atoms[1], 8, PropModeReplace,
reinterpret_cast<const unsigned char*>(titleStr.c_str()),
titleStr.size());
XStoreName(display, browser->GetHost()->GetWindowHandle(), titleStr.c_str());
#endif // defined(USE_X11)
}
/* Returns a data: URI with the specified contents */
std::string
GetDataURI(
const std::string& data,
const std::string& mime_type)
{
return "data:" + mime_type + ";base64," + CefURIEncode(CefBase64Encode(data.data(), data.size()), false).ToString();
}
ClientHandler::ClientHandler(CefRefPtr<CefRenderHandler> ptr) : handler(ptr)
{
printf("ClientHandler constructor\n");
is_closing_ = false;
DCHECK(!g_instance);
g_instance = this;
}
ClientHandler::~ClientHandler()
{
printf("ClientHandler destructor\n");
g_instance = NULL;
}
ClientHandler *
ClientHandler::GetInstance()
{
return g_instance;
}
void
ClientHandler::OnTitleChange(
CefRefPtr<CefBrowser> browser,
const CefString& title)
{
printf("ClientHandler OnTitleChange\n");
CEF_REQUIRE_UI_THREAD();
/* Set the title of the window using platform APIs */
PlatformTitleChange(browser, title);
}
void
ClientHandler::OnAfterCreated(
CefRefPtr<CefBrowser> browser)
{
printf("ClientHandler OnAfterCreated\n");
CEF_REQUIRE_UI_THREAD();
/* Add to the list of existing browsers */
browser_list_.push_back(browser);
}
bool
ClientHandler::DoClose(
CefRefPtr<CefBrowser> browser)
{
printf("ClientHandler DoClose\n");
CEF_REQUIRE_UI_THREAD();
/* Closing the main window requires special handling. See the DoClose() documentation in the CEF header for a detailed
* destription of this process */
if (browser_list_.size() == 1)
{
/* Set a flag to indicate that the window close should be allowed */
is_closing_ = true;
}
/*Allow the close. For windowed browsers this will result in the OS close event being sent */
return false;
}
void
ClientHandler::OnBeforeClose(
CefRefPtr<CefBrowser> browser)
{
printf("ClientHandler OnBeforeClose\n");
CEF_REQUIRE_UI_THREAD();
/* Remove from the list of existing browsers */
BrowserList::iterator bit = browser_list_.begin();
for (; bit != browser_list_.end(); ++bit)
{
if ((*bit)->IsSame(browser))
{
browser_list_.erase(bit);
break;
}
}
if (browser_list_.empty())
{
/* All browser windows have closed. Quit the application message loop */
CefQuitMessageLoop();
}
}
void
ClientHandler::OnLoadError(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl)
{
printf("ClientHandler OnLoadError\n");
CEF_REQUIRE_UI_THREAD();
/* Don't display an error for downloaded files */
if (errorCode == ERR_ABORTED)
{
return;
}
/* Display a load error message using a data: URI */
std::stringstream ss;
ss << "<html><body bgcolor=\"white\">" "<h2>Failed to load URL "
<< std::string(failedUrl) << " with error " << std::string(errorText)
<< " (" << errorCode << ").</h2></body></html>";
frame->LoadURL(GetDataURI(ss.str(), "text/html"));
}
void
ClientHandler::CloseAllBrowsers(
bool force_close)
{
printf("ClientHandler CloseAllBrowsers\n");
if (!CefCurrentlyOn(TID_UI))
{
/* Execute on the UI thread */
CefPostTask(TID_UI, base::Bind(&ClientHandler::CloseAllBrowsers, this, force_close));
return;
}
if (browser_list_.empty())
{
return;
}
BrowserList::const_iterator it = browser_list_.begin();
for (; it != browser_list_.end(); ++it)
{
(*it)->GetHost()->CloseBrowser(force_close);
}
}
ClientHandler.h
- Code: Select all
#pragma once
#include <include/cef_render_process_handler.h>
#include <include/cef_client.h>
#include <include/cef_v8.h>
#include <include/cef_browser.h>
#include <list>
class ClientHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler
{
public:
ClientHandler(CefRefPtr<CefRenderHandler> ptr);
~ClientHandler();
/* Provide access to the single global instance of this object */
static ClientHandler* GetInstance();
/* CefClient methods */
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler()
OVERRIDE { return this; }
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler()
OVERRIDE { return this; }
virtual CefRefPtr<CefLoadHandler> GetLoadHandler()
OVERRIDE { return this; }
virtual CefRefPtr<CefRenderHandler> GetRenderHandler()
OVERRIDE { return handler; }
/* CefDisplayHandler methods */
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) OVERRIDE;
/* CefLifeSpanHandler methods */
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
/* CefLoadHandler methods */
virtual void OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode,
const CefString& errorText, const CefString& failedUrl) OVERRIDE;
/* Request that all existing browser windows close */
void CloseAllBrowsers(bool force_close);
bool IsClosing() const { return is_closing_; }
private:
/* Platform-specific implementation */
void PlatformTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title);
/* List of existing browser windows. Only accessed on the CEF UI thread */
typedef std::list<CefRefPtr<CefBrowser>> BrowserList;
BrowserList browser_list_;
bool is_closing_;
/* Parameters used for rendering (both off-screen and default) */
CefRefPtr<CefRenderHandler> handler;
IMPLEMENT_REFCOUNTING(ClientHandler);
};
main.cpp
- Code: Select all
#include <X11/Xlib.h>
#include <thread>
#include <chrono>
#include <include/internal/cef_linux.h>
#include <include/cef_app.h>
#include <include/cef_browser.h>
#include <include/cef_frame.h>
#include "ClientHandler.h"
#include "ClientApp.h"
/* The parent/child processes will ocasionally receive non-fatal errors that will result in a crash. These macros will
* intercept them and handle them appropriately to prevent any crashes. */
namespace
{
int
XErrorHandlerImpl(
Display* display,
XErrorEvent* event)
{
LOG(WARNING) << "X error received: "
<< "type " << event->type << ", "
<< "serial " << event->serial << ", "
<< "error_code " << static_cast<int>(event->error_code) << ", "
<< "request_code " << static_cast<int>(event->request_code)
<< ", "
<< "minor_code " << static_cast<int>(event->minor_code);
return 0;
}
int
XIOErrorHandlerImpl(
Display* display)
{
return 0;
}
}
void CefInitSettings(CefSettings& settings)
{
/* Make browser process message loop run in a separate thread */
settings.multi_threaded_message_loop = false;
/* The resources (cef.pak and/or devtools_resources.pak) directory */
CefString(&settings.resources_dir_path) = CefString();
/* The locales directory */
CefString(&settings.locales_dir_path) = CefString();
/* Ignore errors related to invalid SSL certificates */
settings.ignore_certificate_errors = false;
/* Used when creating a windowless browser (off-screen rendering) */
settings.windowless_rendering_enabled = true;
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
}
/* Program entry-point function. */
int main(int argc, char* argv[])
{
/* Structure for passing command-line arguments. */
CefMainArgs main_args(argc, argv);
int exit_code = CefExecuteProcess(main_args, NULL, NULL);
if (exit_code >= 0)
{
/* The sub-process has completed so return here */
return exit_code;
}
CefRefPtr<ClientApp> app(new ClientApp);
/* Install xlib error handlers so that the application won't be terminated on non-fatal errors */
XSetErrorHandler(XErrorHandlerImpl);
XSetIOErrorHandler(XIOErrorHandlerImpl);
/* Populate this structure to customize CEF behavior */
CefSettings settings;
CefInitSettings(settings);
CefString(&settings.locales_dir_path) = "/home/dangelone/Downloads/temp/chrome-to-libbb/locales";
CefString(&settings.resources_dir_path) = "/home/dangelone/Downloads/temp/chrome-to-libbb/";
/* Initialize CEF in the main process. */
CefInitialize(main_args, settings, app.get(), NULL);
/* Run the CEF message loop. This will block until CefQuitMessageLoop() is called */
CefRunMessageLoop();
/* Shut down CEF */
CefShutdown();
return 0;
}
My issue is that when the OnPaint() callback is called my memcpy(...) function has a segmentation fault. This I because my buffer's size seems to be 8 for some reason. Does anyone see any issues with my code that would cause my OSR to result in an invalid buffer?