Skip to content

Commit

Permalink
browser,library: browser custom-js api
Browse files Browse the repository at this point in the history
  • Loading branch information
Adamcake committed Dec 16, 2024
1 parent c8e621d commit 7e7bd18
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 25 deletions.
11 changes: 8 additions & 3 deletions src/browser.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ Browser::Window::Window(CefRefPtr<Browser::Client> client, Browser::Details deta
browser_count(0), client(client.get()), show_devtools(show_devtools), details(details), window(nullptr), browser_view(nullptr), browser(nullptr), pending_child(nullptr), pending_delete(false)
{
fmt::print("[B] Browser::Window constructor, this={}\n", reinterpret_cast<uintptr_t>(this));
this->Init(url);
CefRefPtr<CefDictionaryValue> dict = nullptr;
if (details.has_custom_js) {
dict = CefDictionaryValue::Create();
dict->SetString("customjs", details.custom_js);
}
this->Init(url, dict);
}

Browser::Window::Window(CefRefPtr<Browser::Client> client, Browser::Details details, bool show_devtools):
Expand All @@ -39,10 +44,10 @@ Browser::Window::Window(CefRefPtr<Browser::Client> client, Browser::Details deta
fmt::print("[B] Browser::Window popup constructor, this={}\n", reinterpret_cast<uintptr_t>(this));
}

void Browser::Window::Init(CefString url) {
void Browser::Window::Init(CefString url, CefRefPtr<CefDictionaryValue> extra_info) {
CefBrowserSettings browser_settings;
browser_settings.background_color = CefColorSetARGB(0, 0, 0, 0);
this->browser_view = CefBrowserView::CreateBrowserView(this, url, browser_settings, nullptr, nullptr, this);
this->browser_view = CefBrowserView::CreateBrowserView(this, url, browser_settings, extra_info, nullptr, this);
CefWindow::CreateTopLevelWindow(this);
}

Expand Down
2 changes: 1 addition & 1 deletion src/browser.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Browser {
Window(CefRefPtr<Browser::Client> client, Details, bool);

/// Initialise with a browser_view. Should be called from a constructor, if at all.
void Init(CefString);
void Init(CefString, CefRefPtr<CefDictionaryValue>);

/// Refreshes this browser, ignoring cache. Can be called from any thread in the browser process.
virtual void Refresh() const;
Expand Down
18 changes: 17 additions & 1 deletion src/browser/app.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ static void* shm_file;
static size_t shm_length;
static bool shm_inited = false;

static bool set_launcher_ui = false;
static bool has_custom_js = false;
static CefString custom_js;

class ArrayBufferReleaseCallbackFree: public CefV8ArrayBufferReleaseCallback {
void ReleaseBuffer(void* buffer) override {
::free(buffer);
Expand Down Expand Up @@ -52,12 +56,24 @@ CefRefPtr<CefLoadHandler> Browser::App::GetLoadHandler() {

void Browser::App::OnBrowserCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefDictionaryValue> dict) {
fmt::print("[R] OnBrowserCreated for browser {}\n", browser->GetIdentifier());
if (dict) {
if (dict->HasKey("launcher")) set_launcher_ui = dict->GetBool("launcher");
if (dict->HasKey("customjs")) {
custom_js = dict->GetString("customjs");
has_custom_js = true;
}
}
}

void Browser::App::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) {
const CefRefPtr<CefV8Value> global = context->GetGlobal();
global->SetValue("s", CefV8Value::CreateFunction("s", this), V8_PROPERTY_ATTRIBUTE_READONLY);
global->SetValue("close", CefV8Value::CreateUndefined(), V8_PROPERTY_ATTRIBUTE_READONLY);
if (set_launcher_ui) {
global->SetValue("s", CefV8Value::CreateFunction("s", this), V8_PROPERTY_ATTRIBUTE_READONLY);
}
if (has_custom_js) {
frame->ExecuteJavaScript(custom_js, CefString(), int());
}
}

void Browser::App::OnUncaughtException(
Expand Down
20 changes: 18 additions & 2 deletions src/browser/client.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ constexpr bool SHOW_DEVTOOLS = true;
constexpr bool SHOW_DEVTOOLS = false;
#endif

constexpr Browser::Details LAUNCHER_DETAILS = {
const Browser::Details LAUNCHER_DETAILS = {
.preferred_width = 800,
.preferred_height = 608,
.center_on_open = true,
Expand Down Expand Up @@ -386,16 +386,25 @@ bool Browser::Client::IPCHandleMessage(int fd) {
_bolt_ipc_receive(fd, url, header.url_length);
url[header.url_length] = '\0';

char* custom_js = nullptr;
if (header.has_custom_js) {
custom_js = new char[header.custom_js_length];
_bolt_ipc_receive(fd, custom_js, header.custom_js_length);
}

CefRefPtr<ActivePlugin> plugin = this->GetPluginFromFDAndID(client, header.plugin_id);
Browser::Details details {
.preferred_width = header.w,
.preferred_height = header.h,
.center_on_open = true,
.resizeable = true,
.frame = true,
.has_custom_js = true,
.custom_js = CefString(custom_js),
};
plugin->windows.push_back(new Browser::PluginWindow(this, details, url, plugin, fd, &this->send_lock, header.window_id, header.plugin_id, false));
delete[] url;
delete[] custom_js;
break;
}
case IPC_MSG_CREATEBROWSER_OSR: {
Expand All @@ -405,12 +414,19 @@ bool Browser::Client::IPCHandleMessage(int fd) {
_bolt_ipc_receive(fd, url, header.url_length);
url[header.url_length] = '\0';

char* custom_js = nullptr;
if (header.has_custom_js) {
custom_js = new char[header.custom_js_length];
_bolt_ipc_receive(fd, custom_js, header.custom_js_length);
}

CefRefPtr<ActivePlugin> plugin = this->GetPluginFromFDAndID(client, header.plugin_id);
if (plugin) {
CefRefPtr<Browser::WindowOSR> window = new Browser::WindowOSR(CefString((char*)url), header.w, header.h, fd, this, &this->send_lock, header.pid, header.window_id, header.plugin_id, plugin);
CefRefPtr<Browser::WindowOSR> window = new Browser::WindowOSR(CefString((char*)url), header.w, header.h, fd, this, &this->send_lock, header.pid, header.window_id, header.plugin_id, plugin, custom_js);
plugin->windows_osr.push_back(window);
}
delete[] url;
delete[] custom_js;
break;
}
case IPC_MSG_CLOSEBROWSER_EXTERNAL: {
Expand Down
2 changes: 2 additions & 0 deletions src/browser/common.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace Browser {
bool resizeable;
bool frame;
bool is_devtools = false;
bool has_custom_js = false;
CefString custom_js;
};

struct InternalFile {
Expand Down
5 changes: 4 additions & 1 deletion src/browser/window_launcher.cxx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "client.hxx"
#include "window_launcher.hxx"
#include "include/cef_values.h"
#include "include/internal/cef_types.h"
#include "resource_handler.hxx"
#include "request.hxx"
Expand Down Expand Up @@ -135,7 +136,9 @@ Browser::Launcher::Launcher(
this->plugin_config_path.append("plugins.json");
#endif

this->Init(this->BuildURL());
CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
dict->SetBool("launcher", true);
this->Init(this->BuildURL(), dict);
}

CefRefPtr<CefRequestHandler> Browser::Launcher::GetRequestHandler() {
Expand Down
9 changes: 7 additions & 2 deletions src/browser/window_osr.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static void SendUpdateMsg(BoltSocketType fd, std::mutex* send_lock, uint64_t id,
send_lock->unlock();
}

Browser::WindowOSR::WindowOSR(CefString url, int width, int height, BoltSocketType client_fd, Browser::Client* main_client, std::mutex* send_lock, int pid, uint64_t window_id, uint64_t plugin_id, CefRefPtr<FileManager::Directory> file_manager):
Browser::WindowOSR::WindowOSR(CefString url, int width, int height, BoltSocketType client_fd, Browser::Client* main_client, std::mutex* send_lock, int pid, uint64_t window_id, uint64_t plugin_id, CefRefPtr<FileManager::Directory> file_manager, const char* custom_js):
PluginRequestHandler(IPC_MSG_OSRBROWSERMESSAGE, send_lock),
deleted(false), pending_delete(false), pending_devtools(false), client_fd(client_fd), width(width), height(height), browser(nullptr), window_id(window_id),
plugin_id(plugin_id), main_client(main_client), stored(nullptr), remote_has_remapped(false), remote_is_idle(true), file_manager(file_manager)
Expand Down Expand Up @@ -69,7 +69,12 @@ Browser::WindowOSR::WindowOSR(CefString url, int width, int height, BoltSocketTy
window_info.SetAsWindowless(0);
CefBrowserSettings browser_settings;
browser_settings.background_color = CefColorSetARGB(0, 0, 0, 0);
CefBrowserHost::CreateBrowser(window_info, this, CefString(url), browser_settings, nullptr, nullptr);
CefRefPtr<CefDictionaryValue> dict = nullptr;
if (custom_js) {
dict = CefDictionaryValue::Create();
dict->SetString("customjs", custom_js);
}
CefBrowserHost::CreateBrowser(window_info, this, CefString(url), browser_settings, dict, nullptr);
}

bool Browser::WindowOSR::IsDeleted() {
Expand Down
2 changes: 1 addition & 1 deletion src/browser/window_osr.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Browser {
struct Client;

struct WindowOSR: public CefClient, CefLifeSpanHandler, CefRenderHandler, PluginRequestHandler {
WindowOSR(CefString url, int width, int height, BoltSocketType client_fd, Client* main_client, std::mutex* send_lock, int pid, uint64_t window_id, uint64_t plugin_id, CefRefPtr<FileManager::Directory>);
WindowOSR(CefString url, int width, int height, BoltSocketType client_fd, Client* main_client, std::mutex* send_lock, int pid, uint64_t window_id, uint64_t plugin_id, CefRefPtr<FileManager::Directory>, const char* custom_js);

bool IsDeleted();

Expand Down
16 changes: 12 additions & 4 deletions src/browser/window_plugin.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
#include <fmt/core.h>

struct InitTask: public CefTask {
InitTask(CefRefPtr<Browser::PluginWindow> window, const char* url): window(window), url(CefString(url)) {}
InitTask(CefRefPtr<Browser::PluginWindow> window, const char* url, bool has_custom_js, CefString& custom_js): window(window), url(CefString(url)), has_custom_js(has_custom_js) {
if (has_custom_js) this->custom_js = custom_js;
}
void Execute() override {
this->window->Init(this->url);
CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
if (this->has_custom_js) dict->SetString("customjs", this->custom_js);
this->window->Init(this->url, dict);
}
private:
CefRefPtr<Browser::PluginWindow> window;
CefString url;
CefString custom_js;
bool has_custom_js;
IMPLEMENT_REFCOUNTING(InitTask);
DISALLOW_COPY_AND_ASSIGN(InitTask);
};
Expand All @@ -23,9 +29,11 @@ Browser::PluginWindow::PluginWindow(CefRefPtr<Client> main_client, Details detai
file_manager(file_manager), client_fd(fd), window_id(id), plugin_id(plugin_id), closing(false), deleted(false)
{
if (CefCurrentlyOn(TID_UI)) {
this->Init(CefString(url));
CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
if (details.has_custom_js) dict->SetString("customjs", details.custom_js);
this->Init(CefString(url), dict);
} else {
CefPostTask(TID_UI, new InitTask(this, url));
CefPostTask(TID_UI, new InitTask(this, url, details.has_custom_js, details.custom_js));
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/library/doc/doc.texi
Original file line number Diff line number Diff line change
Expand Up @@ -505,9 +505,19 @@ must use '/' as file separators (if any). Otherwise, it will be treated
as a URL of an internet website. The same rules go for any navigations
or fetch requests made by the browser during its lifetime.

The optional 6th parameter is some javascript code which will be run in
an OnContextCreated callback. This is immediately after the V8 context
has been created, so the JavaScipt @code{window} object is available but
the DOM content is not yet loaded.

The following example shows an invocation without some custom JS
followed by one with some custom JS, to demonstrate that the parameter
is optional.

@example lua
@verbatim
local mybrowser = bolt.createwindow(800, 608, "https://bolt.adamcake.com")
local mybrowser = bolt.createwindow(800, 608, "https://bolt.adamcake.com", "window.myCustomFunction = (a, b) => a + b;")
@end verbatim
@end example

Expand All @@ -522,9 +532,19 @@ will be treated as a URL of an internet website. The same rules go for
any navigations or fetch requests made by the browser during its
lifetime.

The optional 6th parameter is some javascript code which will be run in
an OnContextCreated callback. This is immediately after the V8 context
has been created, so the JavaScipt @code{window} object is available but
the DOM content is not yet loaded.

The following example shows an invocation without some custom JS
followed by one with some custom JS, to demonstrate that the parameter
is optional.

@example lua
@verbatim
local mybrowser = bolt.createembeddedbrowser(50, 60, 800, 608, "https://bolt.adamcake.com")
local mybrowser = bolt.createembeddedbrowser(50, 60, 800, 608, "https://bolt.adamcake.com", "window.myCustomFunction = (a, b) => a + b;")
@end verbatim
@end example

Expand Down
2 changes: 2 additions & 0 deletions src/library/ipc.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ struct BoltIPCCreateBrowserHeader {
int pid;
int w;
int h;
uint8_t has_custom_js;
uint32_t custom_js_length;
};

/// Header for BoltIPCMessageTypeToHost::IPC_MSG_CLOSEBROWSER_*
Expand Down
43 changes: 33 additions & 10 deletions src/library/plugin/plugin_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,15 @@ static int api_createwindow(lua_State* state) {
}

static int api_createbrowser(lua_State* state) {
const int w = luaL_checkinteger(state, 1);
const int h = luaL_checkinteger(state, 2);
size_t url_length;
const char* url = luaL_checklstring(state, 3, &url_length);
const char* custom_js = NULL;
size_t len;
if (lua_gettop(state) >= 4 && !lua_isnil(state, 4)) {
custom_js = luaL_checklstring(state, 4, &len);
}
lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME);
struct Plugin* plugin = lua_touserdata(state, -1);
lua_pop(state, 1);
Expand All @@ -556,29 +565,42 @@ static int api_createbrowser(lua_State* state) {

// send an IPC message to the host to create a browser
const int pid = getpid();
size_t url_length;
const char* url = luaL_checklstring(state, 3, &url_length);
const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_CREATEBROWSER_EXTERNAL;
const struct BoltIPCCreateBrowserHeader header = {
.plugin_id = plugin->id,
.window_id = browser->id,
.url_length = url_length,
.pid = pid,
.w = luaL_checkinteger(state, 1),
.h = luaL_checkinteger(state, 2),
.w = w,
.h = h,
.has_custom_js = custom_js != NULL,
.custom_js_length = len,
};
const uint32_t url_length32 = (uint32_t)url_length;
const BoltSocketType fd = _bolt_plugin_fd();
_bolt_ipc_send(fd, &msg_type, sizeof(msg_type));
_bolt_ipc_send(fd, &header, sizeof(header));
_bolt_ipc_send(fd, url, url_length32);
if (custom_js) _bolt_ipc_send(fd, custom_js, len);

// add to the hashmap
hashmap_set(plugin->external_browsers, &browser);
return 1;
}

static int api_createembeddedbrowser(lua_State* state) {
const int x = luaL_checkinteger(state, 1);
const int y = luaL_checkinteger(state, 2);
const int w = luaL_checkinteger(state, 3);
const int h = luaL_checkinteger(state, 4);
size_t url_length;
const char* url = luaL_checklstring(state, 5, &url_length);
const char* custom_js = NULL;
size_t custom_js_len;
if (lua_gettop(state) >= 6 && !lua_isnil(state, 6)) {
custom_js = luaL_checklstring(state, 6, &custom_js_len);
}

const struct PluginManagedFunctions* managed_functions = _bolt_plugin_managed_functions();
lua_getfield(state, LUA_REGISTRYINDEX, PLUGIN_REGISTRYNAME);
const struct Plugin* plugin = lua_touserdata(state, -1);
Expand All @@ -605,10 +627,10 @@ static int api_createembeddedbrowser(lua_State* state) {
window->plugin = state;
_bolt_rwlock_init(&window->lock);
_bolt_rwlock_init(&window->input_lock);
window->metadata.x = luaL_checkinteger(state, 1);
window->metadata.y = luaL_checkinteger(state, 2);
window->metadata.width = luaL_checkinteger(state, 3);
window->metadata.height = luaL_checkinteger(state, 4);
window->metadata.x = x;
window->metadata.y = y;
window->metadata.width = w;
window->metadata.height = h;
if (window->metadata.width < WINDOW_MIN_SIZE) window->metadata.width = WINDOW_MIN_SIZE;
if (window->metadata.height < WINDOW_MIN_SIZE) window->metadata.height = WINDOW_MIN_SIZE;
memset(&window->input, 0, sizeof(window->input));
Expand All @@ -627,8 +649,6 @@ static int api_createembeddedbrowser(lua_State* state) {

// send an IPC message to the host to create an OSR browser
const int pid = getpid();
size_t url_length;
const char* url = luaL_checklstring(state, 5, &url_length);
const enum BoltIPCMessageTypeToHost msg_type = IPC_MSG_CREATEBROWSER_OSR;
const struct BoltIPCCreateBrowserHeader header = {
.plugin_id = plugin->id,
Expand All @@ -637,12 +657,15 @@ static int api_createembeddedbrowser(lua_State* state) {
.pid = pid,
.w = window->metadata.width,
.h = window->metadata.height,
.has_custom_js = custom_js != NULL,
.custom_js_length = custom_js_len,
};
const uint32_t url_length32 = (uint32_t)url_length;
const BoltSocketType fd = _bolt_plugin_fd();
_bolt_ipc_send(fd, &msg_type, sizeof(msg_type));
_bolt_ipc_send(fd, &header, sizeof(header));
_bolt_ipc_send(fd, url, url_length32);
if (custom_js) _bolt_ipc_send(fd, custom_js, custom_js_len);

// set this window in the hashmap, which is accessible by backends
struct WindowInfo* windows = _bolt_plugin_windowinfo();
Expand Down

0 comments on commit 7e7bd18

Please sign in to comment.