From 1b5dec7a2eef6f006027b9afc4fbd6c52998f509 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 28 May 2026 06:59:21 -0700 Subject: [PATCH 1/9] Update the version of CEF used (media enabled) to 148.0.9 for Windows and macOS by pulling in the updated CEF 148 autobuild package. (Linux to follow) --- autobuild.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 6ca9dae..ed3aee1 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -19,11 +19,11 @@ archive hash - f6803c42c9468419d281cbc5afb99ed21942fd30 + 3218ee0b5acc5e3fa4d5e9e9e24929b9913688ad hash_algorithm sha1 url - https://automated-builds-secondlife-com.s3.us-east-1.amazonaws.com/gh/secondlife/cef/cef_bin-139.0.40_g465474a_chromium-139.0.7258.139-darwin64-252542250.tar.zst + https://automated-builds-secondlife-com.s3.us-east-1.amazonaws.com/gh/secondlife/cef/cef_bin-148.0.9_g0d9d52a_chromium-148.0.7778.180-darwin64-261471908.tar.zst name darwin64 @@ -33,18 +33,18 @@ archive hash - 1b09d806f4fe041f800c4bf2e1e0e0884f76973b + f677be20cd859b2a7cf6925cf60bd6ab4829266a hash_algorithm sha1 url - https://automated-builds-secondlife-com.s3.us-east-1.amazonaws.com/gh/secondlife/cef/cef_bin-139.0.40_g465474a_chromium-139.0.7258.139-windows64-252550005.tar.zst + https://automated-builds-secondlife-com.s3.us-east-1.amazonaws.com/gh/secondlife/cef/cef_bin-148.0.9_g0d9d52a_chromium-148.0.7778.180-windows64-261470050.tar.zst name windows64 version - 139.0.17_g6c347eb_chromium-139.0.7258.31 + cef_bin-148.0.9_g0d9d52a_chromium-148.0.7778.180 package_description From 1c5d330472a4bce8db15b35d364bdc0bb3e8026e Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 28 May 2026 09:48:30 -0700 Subject: [PATCH 2/9] Bump Dullahan version to disambiguate it after inclusion of CEF 148 --- src/dullahan_version.h.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dullahan_version.h.in b/src/dullahan_version.h.in index e42c6fd..6bbf55e 100644 --- a/src/dullahan_version.h.in +++ b/src/dullahan_version.h.in @@ -37,7 +37,7 @@ // version of this package #define DULLAHAN_VERSION_MAJOR 1 -#define DULLAHAN_VERSION_MINOR 26 +#define DULLAHAN_VERSION_MINOR 31 #define DULLAHAN_VERSION_POINT 0 // The build version number as of v1.2 is now the date/time the build was made From 57d34f4a880ccbc05882c586e3a9658f3cad1724 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 10 Jun 2026 10:42:02 -0700 Subject: [PATCH 3/9] CEF now requires C++20 to build --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d40424d..f61c165 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ if(CMAKE_CONFIGURATION_TYPES) set(CMAKE_CONFIGURATION_TYPES "Release") endif() -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY ON) @@ -473,4 +473,4 @@ if( USE_SPOTIFY_CEF ) install( FILES ${CEF_RESOURCE_DIR}/${resFile} DESTINATION resources) endif() endforeach() -endif() \ No newline at end of file +endif() From 33e527e77c07f12557ed6f0f0f6d468332ce846c Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 11 Jun 2026 14:44:37 -0700 Subject: [PATCH 4/9] Add the V8 based message feature from JS to C++ and provide some simple examples in the OpenGL example app --- .../opengl-example/src/opengl-example.cpp | 27 +++- examples/opengl-example/src/opengl-example.h | 4 +- src/dullahan.cpp | 5 + src/dullahan.h | 3 + src/dullahan_browser_client.cpp | 25 +++ src/dullahan_browser_client.h | 5 + src/dullahan_callback_manager.cpp | 15 ++ src/dullahan_callback_manager.h | 4 + src/host/dullahan_host.cpp | 143 +++++++++++------- 9 files changed, 176 insertions(+), 55 deletions(-) diff --git a/examples/opengl-example/src/opengl-example.cpp b/examples/opengl-example/src/opengl-example.cpp index f024f10..9ca3319 100644 --- a/examples/opengl-example/src/opengl-example.cpp +++ b/examples/opengl-example/src/opengl-example.cpp @@ -79,6 +79,24 @@ void openglExample::handleKeyEvent(int key, int scancode, int action, int mods) { mDullahan->requestExit(); } + else if (mods & GLFW_MOD_CONTROL) + { + if (key >= '1' && key <= '9') + { + std::ostringstream cmd("fromCPP", std::ios::ate); + cmd << "("; + cmd << "{ "; + cmd << "name: 'Key-" + std::to_string(key - '0') << "'"; + cmd << ", "; + cmd << "id: '" + std::to_string(key - '0') << "'"; + cmd << ", "; + cmd << "content : 'Payload for key " + std::to_string(key - '0') << "'"; + cmd << " }"; + cmd << ");"; + std::cout << "--> cmd.str(): " << cmd.str() << std::endl; + mDullahan->executeJavaScript(cmd.str()); + } + } } } @@ -311,7 +329,8 @@ bool openglExample::init() mDullahan->setOnPageChangedCallback(std::bind(&openglExample::onPageChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); mDullahan->setOnRequestExitCallback(std::bind(&openglExample::onRequestExitCallback, this)); - + mDullahan->setOnJStoCPPMsgCallback(std::bind(&openglExample::onJStoCPPMsgCallback, this, std::placeholders::_1)); + mDullahan->navigate(mHomeUrl); } @@ -463,6 +482,12 @@ void openglExample::onRequestExitCallback() glfwSetWindowShouldClose(mWindow, GLFW_TRUE); } +std::string openglExample::onJStoCPPMsgCallback(const std::string msg) +{ + std::cout << "Received message from JavaScript: " << msg << std::endl; + return "Message received loud and clear by C++!"; +} + void openglExample::initUI() { IMGUI_CHECKVERSION(); diff --git a/examples/opengl-example/src/opengl-example.h b/examples/opengl-example/src/opengl-example.h index cb85f0c..60c6a53 100644 --- a/examples/opengl-example/src/opengl-example.h +++ b/examples/opengl-example/src/opengl-example.h @@ -69,12 +69,14 @@ class openglExample // callbacks void onPageChanged(const unsigned char* pixels, int x, int y, const int width, const int height); void onRequestExitCallback(); + std::string onJStoCPPMsgCallback(const std::string msg); private: GLFWwindow* mWindow; const std::string mWindowTitle = "Dullahan OpenGL Example"; const std::string mAppVersionStr = "0.0.1"; - const std::string mHomeUrl = "https://sl-viewer-media-system.s3.amazonaws.com/bookmarks/index.html"; + //const std::string mHomeUrl = "https://sl-viewer-media-system.s3.amazonaws.com/bookmarks/index.html"; + const std::string mHomeUrl = "localhost"; const int mWindowWidth = 1280; const int mWindowHeight = 1280; bool mShowAbout; diff --git a/src/dullahan.cpp b/src/dullahan.cpp index afc0cce..c189f10 100644 --- a/src/dullahan.cpp +++ b/src/dullahan.cpp @@ -446,3 +446,8 @@ void dullahan::setOnJSBeforeUnloadCallback(std::function callback) { mImpl->getCallbackManager()->setOnJSBeforeUnloadCallback(callback); } + +void dullahan::setOnJStoCPPMsgCallback(std::function callback) +{ + mImpl->getCallbackManager()->setOnJStoCPPMsgCallback(callback); +} diff --git a/src/dullahan.h b/src/dullahan.h index 04eee29..254f1f8 100644 --- a/src/dullahan.h +++ b/src/dullahan.h @@ -397,6 +397,9 @@ class dullahan // JS before unload callback (alert) void setOnJSBeforeUnloadCallback(std::function callback); + // Message from JS to CPP + void setOnJStoCPPMsgCallback(std::function callback); + private: std::unique_ptr mImpl; }; diff --git a/src/dullahan_browser_client.cpp b/src/dullahan_browser_client.cpp index f043eee..03fd1cf 100644 --- a/src/dullahan_browser_client.cpp +++ b/src/dullahan_browser_client.cpp @@ -37,6 +37,7 @@ #include #include +#include #include dullahan_browser_client::dullahan_browser_client(dullahan_impl* parent, @@ -58,6 +59,30 @@ CefRefPtr dullahan_browser_client::GetRenderHandler() return mRenderHandler; } +// CefClient override +bool dullahan_browser_client::OnProcessMessageReceived(CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) +{ + CEF_REQUIRE_UI_THREAD(); + + if (message->GetName() == "JSONtoCPP_MSG") + { + CefRefPtr args = message->GetArgumentList(); + if (args) + { + //std::cout << ">>> Received JSONtoCPP_MSG from render process: " << args->GetString(0).ToString() << std::endl; + mParent->getCallbackManager()->onJStoCPPMsgCallback(args->GetString(0).ToString()); + } + + // Indicate we processed this message and it should not be sent to other handlers + return true; + } + + return false; +} + // CefLifeSpanHandler override bool dullahan_browser_client::OnBeforePopup(CefRefPtr browser, CefRefPtr frame, diff --git a/src/dullahan_browser_client.h b/src/dullahan_browser_client.h index a6807b2..64e97a2 100644 --- a/src/dullahan_browser_client.h +++ b/src/dullahan_browser_client.h @@ -58,6 +58,11 @@ class dullahan_browser_client : return this; } + bool OnProcessMessageReceived(CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) override; + bool OnBeforePopup(CefRefPtr browser, CefRefPtr frame, int popup_id, diff --git a/src/dullahan_callback_manager.cpp b/src/dullahan_callback_manager.cpp index 715c3c9..0519382 100644 --- a/src/dullahan_callback_manager.cpp +++ b/src/dullahan_callback_manager.cpp @@ -292,3 +292,18 @@ bool dullahan_callback_manager::onJSBeforeUnloadCallback() return false; } +void dullahan_callback_manager::setOnJStoCPPMsgCallback( + std::function callback) +{ + mOnJStoCPPMsgCallbackFunc = callback; +} + +std::string dullahan_callback_manager::onJStoCPPMsgCallback(const std::string msg) +{ + if (mOnJStoCPPMsgCallbackFunc) + { + return mOnJStoCPPMsgCallbackFunc(msg); + } + + return std::string(); +} diff --git a/src/dullahan_callback_manager.h b/src/dullahan_callback_manager.h index 8141f0e..44efb8f 100644 --- a/src/dullahan_callback_manager.h +++ b/src/dullahan_callback_manager.h @@ -91,6 +91,9 @@ class dullahan_callback_manager void setOnJSBeforeUnloadCallback(std::function callback); bool onJSBeforeUnloadCallback(); + void setOnJStoCPPMsgCallback(std::function callback); + std::string onJStoCPPMsgCallback(const std::string msg); + private: std::function mOnAddressChangeCallbackFunc; std::function mOnConsoleMessageCallbackFunc; @@ -111,6 +114,7 @@ class dullahan_callback_manager std::function(dullahan::EFileDialogType, const std::string, const std::string, const std::string, bool&)> mOnFileDialogCallbackFunc; std::function mOnJSDialogCallbackFunc; std::function mOnJSBeforeUnloadCallbackFunc; + std::function mOnJStoCPPMsgCallbackFunc; }; #endif //_DULLAHAN_CALLBACK_MANAGER diff --git a/src/host/dullahan_host.cpp b/src/host/dullahan_host.cpp index 4d10a64..f4b1ef1 100644 --- a/src/host/dullahan_host.cpp +++ b/src/host/dullahan_host.cpp @@ -49,57 +49,6 @@ int main(int argc, char* argv[]) #include #include -/* - Nasty hack to stop flash from displaying a popup with "NO SANDBOX" - Flashplayer will try to spawn a cmd.exe and echo this message into it, we - use a process group to limit the number of processes allowed to 1, thus preventing - popup. - - Limitation: NeedsWindows 8 or higher, the viewer already does put SLPlugin (and with that - all sub processes) into a job, so all plugin instances get killed when the viewer does exit. - Anything before Windows 8 will not allow a process being part of more than one job. - - Using the sandbox would fix this problem, but for using the sandbox the same executable - must be used for browser and all sub processes (see cef_sandbox_win.h); but the viewer - uses slplugin.exe and llceflib_host.exe. -*/ -void enablePPAPIFlashHack(LPSTR lpCmdLine) -{ - if (!lpCmdLine) - { - return; - } - - std::string strCmdLine = lpCmdLine; - - std::string strType = "--type=ppapi"; - std::string::size_type i = strCmdLine.find(strType); - - if (i == std::string::npos) - { - return; - } - - HANDLE hJob = CreateJobObject(nullptr, nullptr); - HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId()); - - if (!AssignProcessToJobObject(hJob, hProc)) - { - ::CloseHandle(hProc); - ::CloseHandle(hJob); - return; - } - - JOBOBJECT_BASIC_LIMIT_INFORMATION baseLimits = {}; - baseLimits.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS; - baseLimits.ActiveProcessLimit = 1; - - SetInformationJobObject(hJob, JobObjectBasicLimitInformation, &baseLimits, sizeof(baseLimits)); - - ::CloseHandle(hProc); - ::CloseHandle(hJob); -} - // taken from http://magpcss.org/ceforum/viewtopic.php?f=6&t=15817&start=10#p37820 // works around a CEF issue (yet to be filed) where the host process is not destroyed // after CEF exits in some case on Windows 7 @@ -131,6 +80,92 @@ HANDLE GetParentProcess() } #endif + +class JSONtoCPPHandler : public CefV8Handler +{ + public: + JSONtoCPPHandler(CefRefPtr browser) : + // Save the browser reference from OnContextCreated() + // for later use in IPC communication + mBrowser(browser) + { + } + + bool Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) override + { + if (name == "JSONtoCPP") + { + // Check args + if (arguments.size() != 1 || ! arguments[0]->IsString()) + { + exception = "JSONtoCPP(json) expects one string argument"; + return true; + } + + // Send the JSON string to the browser process and return an acknowledgment + std::string json = arguments[0]->GetStringValue(); + std::string result = DoJSONtoCPP(json); + retval = CefV8Value::CreateString(result); + + // Indicate we handled the function call, even + // if there was an error in DoJSONtoCPP + return true; + } + + return false; + } + + private: + std::string DoJSONtoCPP(const std::string& json) + { + // Send the JSON string to the browser process via IPC + CefRefPtr msg = CefProcessMessage::Create("JSONtoCPP_MSG"); + CefRefPtr args = msg->GetArgumentList(); + args->SetString(0, json); + mBrowser->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); + + // Acknowledge receipt of the JSON string + return "{\"ACK\": true}"; + } + + CefRefPtr mBrowser; + IMPLEMENT_REFCOUNTING(JSONtoCPPHandler); +}; + +class MyApp : public CefApp, + public CefRenderProcessHandler +{ +public: + CefRefPtr GetRenderProcessHandler() override + { + return this; + } + + void OnContextCreated(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) override + { + CefRefPtr global = context->GetGlobal(); + CefRefPtr handler = new JSONtoCPPHandler(browser); + CefRefPtr func = CefV8Value::CreateFunction("JSONtoCPP", handler); + global->SetValue("JSONtoCPP", func, V8_PROPERTY_ATTRIBUTE_NONE); + + // Friendly greeting to the browser process to confirm the context was created + CefRefPtr msg = CefProcessMessage::Create("JSONtoCPP_MSG"); + CefRefPtr args = msg->GetArgumentList(); + args->SetString(0, "Hello from the OnContextCreated in the sub-process!"); + browser->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); + } + +private: + IMPLEMENT_REFCOUNTING(MyApp); +}; + + #if defined(NO_STACK_PROTECTOR) NO_STACK_PROTECTOR #endif @@ -149,9 +184,11 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, CefMainArgs args(GetModuleHandle(nullptr)); - enablePPAPIFlashHack(lpCmdLine); + // Important: Create the CefApp instance in the main function to ensure it is available + // to CefExecuteProcess() when the sub-process is launched. Creating the CefApp instance + const CefRefPtr app = new MyApp(); - return CefExecuteProcess(args, nullptr, nullptr); + return CefExecuteProcess(args, app, nullptr); } #endif From d2ff42c9bf98e7f5978f0e83c63b7deb6b6e327b Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 11 Jun 2026 16:02:40 -0700 Subject: [PATCH 5/9] Remove the 'welcome' message until we have a way to disambiguate it from one with a JSON blob --- src/host/dullahan_host.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/host/dullahan_host.cpp b/src/host/dullahan_host.cpp index f4b1ef1..846fdf9 100644 --- a/src/host/dullahan_host.cpp +++ b/src/host/dullahan_host.cpp @@ -155,10 +155,15 @@ class MyApp : public CefApp, global->SetValue("JSONtoCPP", func, V8_PROPERTY_ATTRIBUTE_NONE); // Friendly greeting to the browser process to confirm the context was created - CefRefPtr msg = CefProcessMessage::Create("JSONtoCPP_MSG"); - CefRefPtr args = msg->GetArgumentList(); - args->SetString(0, "Hello from the OnContextCreated in the sub-process!"); - browser->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); + // + // TODO: + // I think we need to craft each message with an ID that the browser process + // can use to route the message to the correct callback + // + //CefRefPtr msg = CefProcessMessage::Create("JSONtoCPP_MSG"); + //CefRefPtr args = msg->GetArgumentList(); + //args->SetString(0, "Hello from the OnContextCreated in the sub-process!"); + //browser->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); } private: From 8f7f98247f1a52a24904547877cf8a5ac9823916 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 11 Jun 2026 16:47:06 -0700 Subject: [PATCH 6/9] Introduce a second parameter in the args of the V8 message - useful for passing an ID - e.g. 'json', 'base64' etc. and then the payload itself as the second paramater e.g. json string --- .../opengl-example/src/opengl-example.cpp | 6 ++-- examples/opengl-example/src/opengl-example.h | 2 +- src/dullahan.cpp | 2 +- src/dullahan.h | 2 +- src/dullahan_browser_client.cpp | 2 +- src/dullahan_callback_manager.cpp | 6 ++-- src/dullahan_callback_manager.h | 6 ++-- src/host/dullahan_host.cpp | 28 +++++++++---------- 8 files changed, 26 insertions(+), 28 deletions(-) diff --git a/examples/opengl-example/src/opengl-example.cpp b/examples/opengl-example/src/opengl-example.cpp index 9ca3319..e9f5cf3 100644 --- a/examples/opengl-example/src/opengl-example.cpp +++ b/examples/opengl-example/src/opengl-example.cpp @@ -329,7 +329,7 @@ bool openglExample::init() mDullahan->setOnPageChangedCallback(std::bind(&openglExample::onPageChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); mDullahan->setOnRequestExitCallback(std::bind(&openglExample::onRequestExitCallback, this)); - mDullahan->setOnJStoCPPMsgCallback(std::bind(&openglExample::onJStoCPPMsgCallback, this, std::placeholders::_1)); + mDullahan->setOnJStoCPPMsgCallback(std::bind(&openglExample::onJStoCPPMsgCallback, this, std::placeholders::_1, std::placeholders::_2)); mDullahan->navigate(mHomeUrl); } @@ -482,9 +482,9 @@ void openglExample::onRequestExitCallback() glfwSetWindowShouldClose(mWindow, GLFW_TRUE); } -std::string openglExample::onJStoCPPMsgCallback(const std::string msg) +std::string openglExample::onJStoCPPMsgCallback(const std::string id, const std::string msg) { - std::cout << "Received message from JavaScript: " << msg << std::endl; + std::cout << "Received message with ID: " << id << " from JavaScript: " << msg << std::endl; return "Message received loud and clear by C++!"; } diff --git a/examples/opengl-example/src/opengl-example.h b/examples/opengl-example/src/opengl-example.h index 60c6a53..2f36ff9 100644 --- a/examples/opengl-example/src/opengl-example.h +++ b/examples/opengl-example/src/opengl-example.h @@ -69,7 +69,7 @@ class openglExample // callbacks void onPageChanged(const unsigned char* pixels, int x, int y, const int width, const int height); void onRequestExitCallback(); - std::string onJStoCPPMsgCallback(const std::string msg); + std::string onJStoCPPMsgCallback(const std::string id, const std::string msg); private: GLFWwindow* mWindow; diff --git a/src/dullahan.cpp b/src/dullahan.cpp index c189f10..5b9dbe4 100644 --- a/src/dullahan.cpp +++ b/src/dullahan.cpp @@ -447,7 +447,7 @@ void dullahan::setOnJSBeforeUnloadCallback(std::function callback) mImpl->getCallbackManager()->setOnJSBeforeUnloadCallback(callback); } -void dullahan::setOnJStoCPPMsgCallback(std::function callback) +void dullahan::setOnJStoCPPMsgCallback(std::function callback) { mImpl->getCallbackManager()->setOnJStoCPPMsgCallback(callback); } diff --git a/src/dullahan.h b/src/dullahan.h index 254f1f8..06911e5 100644 --- a/src/dullahan.h +++ b/src/dullahan.h @@ -398,7 +398,7 @@ class dullahan void setOnJSBeforeUnloadCallback(std::function callback); // Message from JS to CPP - void setOnJStoCPPMsgCallback(std::function callback); + void setOnJStoCPPMsgCallback(std::function callback); private: std::unique_ptr mImpl; diff --git a/src/dullahan_browser_client.cpp b/src/dullahan_browser_client.cpp index 03fd1cf..f3054e6 100644 --- a/src/dullahan_browser_client.cpp +++ b/src/dullahan_browser_client.cpp @@ -73,7 +73,7 @@ bool dullahan_browser_client::OnProcessMessageReceived(CefRefPtr bro if (args) { //std::cout << ">>> Received JSONtoCPP_MSG from render process: " << args->GetString(0).ToString() << std::endl; - mParent->getCallbackManager()->onJStoCPPMsgCallback(args->GetString(0).ToString()); + mParent->getCallbackManager()->onJStoCPPMsgCallback(args->GetString(0).ToString(), args->GetString(1).ToString()); } // Indicate we processed this message and it should not be sent to other handlers diff --git a/src/dullahan_callback_manager.cpp b/src/dullahan_callback_manager.cpp index 0519382..d350fad 100644 --- a/src/dullahan_callback_manager.cpp +++ b/src/dullahan_callback_manager.cpp @@ -293,16 +293,16 @@ bool dullahan_callback_manager::onJSBeforeUnloadCallback() } void dullahan_callback_manager::setOnJStoCPPMsgCallback( - std::function callback) + std::function callback) { mOnJStoCPPMsgCallbackFunc = callback; } -std::string dullahan_callback_manager::onJStoCPPMsgCallback(const std::string msg) +std::string dullahan_callback_manager::onJStoCPPMsgCallback(const std::string id, const std::string msg) { if (mOnJStoCPPMsgCallbackFunc) { - return mOnJStoCPPMsgCallbackFunc(msg); + return mOnJStoCPPMsgCallbackFunc(id, msg); } return std::string(); diff --git a/src/dullahan_callback_manager.h b/src/dullahan_callback_manager.h index 44efb8f..86a098f 100644 --- a/src/dullahan_callback_manager.h +++ b/src/dullahan_callback_manager.h @@ -91,8 +91,8 @@ class dullahan_callback_manager void setOnJSBeforeUnloadCallback(std::function callback); bool onJSBeforeUnloadCallback(); - void setOnJStoCPPMsgCallback(std::function callback); - std::string onJStoCPPMsgCallback(const std::string msg); + void setOnJStoCPPMsgCallback(std::function callback); + std::string onJStoCPPMsgCallback(const std::string id, const std::string msg); private: std::function mOnAddressChangeCallbackFunc; @@ -114,7 +114,7 @@ class dullahan_callback_manager std::function(dullahan::EFileDialogType, const std::string, const std::string, const std::string, bool&)> mOnFileDialogCallbackFunc; std::function mOnJSDialogCallbackFunc; std::function mOnJSBeforeUnloadCallbackFunc; - std::function mOnJStoCPPMsgCallbackFunc; + std::function mOnJStoCPPMsgCallbackFunc; }; #endif //_DULLAHAN_CALLBACK_MANAGER diff --git a/src/host/dullahan_host.cpp b/src/host/dullahan_host.cpp index 846fdf9..481fafa 100644 --- a/src/host/dullahan_host.cpp +++ b/src/host/dullahan_host.cpp @@ -100,15 +100,16 @@ class JSONtoCPPHandler : public CefV8Handler if (name == "JSONtoCPP") { // Check args - if (arguments.size() != 1 || ! arguments[0]->IsString()) + if (arguments.size() != 2 || ! arguments[0]->IsString() || ! arguments[1]->IsString()) { - exception = "JSONtoCPP(json) expects one string argument"; + exception = "JSONtoCPP(json, id) expects two string arguments"; return true; } // Send the JSON string to the browser process and return an acknowledgment - std::string json = arguments[0]->GetStringValue(); - std::string result = DoJSONtoCPP(json); + std::string id = arguments[0]->GetStringValue(); + std::string json = arguments[1]->GetStringValue(); + std::string result = DoJSONtoCPP(id, json); retval = CefV8Value::CreateString(result); // Indicate we handled the function call, even @@ -120,12 +121,13 @@ class JSONtoCPPHandler : public CefV8Handler } private: - std::string DoJSONtoCPP(const std::string& json) + std::string DoJSONtoCPP(const std::string& id, const std::string& json) { // Send the JSON string to the browser process via IPC CefRefPtr msg = CefProcessMessage::Create("JSONtoCPP_MSG"); CefRefPtr args = msg->GetArgumentList(); - args->SetString(0, json); + args->SetString(0, id); + args->SetString(1, json); mBrowser->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); // Acknowledge receipt of the JSON string @@ -155,15 +157,11 @@ class MyApp : public CefApp, global->SetValue("JSONtoCPP", func, V8_PROPERTY_ATTRIBUTE_NONE); // Friendly greeting to the browser process to confirm the context was created - // - // TODO: - // I think we need to craft each message with an ID that the browser process - // can use to route the message to the correct callback - // - //CefRefPtr msg = CefProcessMessage::Create("JSONtoCPP_MSG"); - //CefRefPtr args = msg->GetArgumentList(); - //args->SetString(0, "Hello from the OnContextCreated in the sub-process!"); - //browser->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); + CefRefPtr msg = CefProcessMessage::Create("JSONtoCPP_MSG"); + CefRefPtr args = msg->GetArgumentList(); + args->SetString(0, "INFO"); + args->SetString(1, "Hello from the OnContextCreated in the sub-process!"); + browser->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); } private: From 32044caf4117dbb6b3accedc341bf3476af56f5e Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 11 Jun 2026 16:48:26 -0700 Subject: [PATCH 7/9] Bump Dullahan version to 1.35 to disambiguate the new V8 message with id/payload --- src/dullahan_version.h.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dullahan_version.h.in b/src/dullahan_version.h.in index 6bbf55e..7f5bf62 100644 --- a/src/dullahan_version.h.in +++ b/src/dullahan_version.h.in @@ -37,7 +37,7 @@ // version of this package #define DULLAHAN_VERSION_MAJOR 1 -#define DULLAHAN_VERSION_MINOR 31 +#define DULLAHAN_VERSION_MINOR 35 #define DULLAHAN_VERSION_POINT 0 // The build version number as of v1.2 is now the date/time the build was made From a6b29dce153d20d8e1d27b254e6c8c20f100991c Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 11 Jun 2026 18:19:21 -0700 Subject: [PATCH 8/9] Remove the hard-coded 'localhost' url i was using for tasting.. add a link to the test web page in the opengl-example test app bookmark links --- examples/opengl-example/src/opengl-example.cpp | 3 ++- examples/opengl-example/src/opengl-example.h | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/opengl-example/src/opengl-example.cpp b/examples/opengl-example/src/opengl-example.cpp index e9f5cf3..cfac92a 100644 --- a/examples/opengl-example/src/opengl-example.cpp +++ b/examples/opengl-example/src/opengl-example.cpp @@ -649,7 +649,8 @@ void openglExample::updateUI() "chrome://version", "https://sl-viewer-media-system.s3.amazonaws.com/bookmarks/index.html", "https://viewer-login.agni.lindenlab.com/", - "https://secondlife.com" + "https://secondlife.com", + "https://sl-viewer-media-system.s3.amazonaws.com/apps/v8-msg/index.html", }; static const char* current_item = "Select a bookmark"; ImGui::SetNextItemWidth(main_viewport->Size.x); diff --git a/examples/opengl-example/src/opengl-example.h b/examples/opengl-example/src/opengl-example.h index 2f36ff9..d234f07 100644 --- a/examples/opengl-example/src/opengl-example.h +++ b/examples/opengl-example/src/opengl-example.h @@ -75,8 +75,7 @@ class openglExample GLFWwindow* mWindow; const std::string mWindowTitle = "Dullahan OpenGL Example"; const std::string mAppVersionStr = "0.0.1"; - //const std::string mHomeUrl = "https://sl-viewer-media-system.s3.amazonaws.com/bookmarks/index.html"; - const std::string mHomeUrl = "localhost"; + const std::string mHomeUrl = "https://sl-viewer-media-system.s3.amazonaws.com/bookmarks/index.html"; const int mWindowWidth = 1280; const int mWindowHeight = 1280; bool mShowAbout; From dd986f780f1096e531210683d3d559694ac8b2e3 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Mon, 6 Jul 2026 20:47:27 +0300 Subject: [PATCH 9/9] Bind window.JSONtoCPP on OSX by passing CefApp to sub-process --- src/host/dullahan_host.cpp | 115 +++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/src/host/dullahan_host.cpp b/src/host/dullahan_host.cpp index 481fafa..56bbaf8 100644 --- a/src/host/dullahan_host.cpp +++ b/src/host/dullahan_host.cpp @@ -28,66 +28,14 @@ #include "cef_app.h" -#ifdef __linux__ -#if defined(NO_STACK_PROTECTOR) -NO_STACK_PROTECTOR -#endif -int main(int argc, char* argv[]) -{ - CefMainArgs main_args(argc, argv); - return CefExecuteProcess(main_args, nullptr, nullptr); -} -#endif -#ifdef WIN32 -#include - -#define HOST_PROCESS_REAPER - -#ifdef HOST_PROCESS_REAPER -// Ignore c:\program files (x86)\microsoft visual studio 12.0\vc\include\thr\xthread(196): warning C4702: unreachable code -#pragma warning( disable : 4702) -#include -#include - -// taken from http://magpcss.org/ceforum/viewtopic.php?f=6&t=15817&start=10#p37820 -// works around a CEF issue (yet to be filed) where the host process is not destroyed -// after CEF exits in some case on Windows 7 -// Making it switchable for now while I investigate it a bit -HANDLE GetParentProcess() -{ - HANDLE Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - - PROCESSENTRY32 ProcessEntry = {}; - ProcessEntry.dwSize = sizeof(PROCESSENTRY32); - - if (Process32First(Snapshot, &ProcessEntry)) - { - DWORD CurrentProcessId = GetCurrentProcessId(); - - do - { - if (ProcessEntry.th32ProcessID == CurrentProcessId) - { - break; - } - } - while (Process32Next(Snapshot, &ProcessEntry)); - } - - CloseHandle(Snapshot); - - return OpenProcess(SYNCHRONIZE, FALSE, ProcessEntry.th32ParentProcessID); -} -#endif - - +// Shared by Windows and Mac sub-process entry points class JSONtoCPPHandler : public CefV8Handler { public: - JSONtoCPPHandler(CefRefPtr browser) : + JSONtoCPPHandler(CefRefPtr browser) : // Save the browser reference from OnContextCreated() // for later use in IPC communication - mBrowser(browser) + mBrowser(browser) { } @@ -168,6 +116,58 @@ class MyApp : public CefApp, IMPLEMENT_REFCOUNTING(MyApp); }; +#ifdef __linux__ +#if defined(NO_STACK_PROTECTOR) +NO_STACK_PROTECTOR +#endif +int main(int argc, char* argv[]) +{ + CefMainArgs main_args(argc, argv); + return CefExecuteProcess(main_args, nullptr, nullptr); +} +#endif +#ifdef WIN32 +#include + +#define HOST_PROCESS_REAPER + +#ifdef HOST_PROCESS_REAPER +// Ignore c:\program files (x86)\microsoft visual studio 12.0\vc\include\thr\xthread(196): warning C4702: unreachable code +#pragma warning( disable : 4702) +#include +#include + +// taken from http://magpcss.org/ceforum/viewtopic.php?f=6&t=15817&start=10#p37820 +// works around a CEF issue (yet to be filed) where the host process is not destroyed +// after CEF exits in some case on Windows 7 +// Making it switchable for now while I investigate it a bit +HANDLE GetParentProcess() +{ + HANDLE Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + PROCESSENTRY32 ProcessEntry = {}; + ProcessEntry.dwSize = sizeof(PROCESSENTRY32); + + if (Process32First(Snapshot, &ProcessEntry)) + { + DWORD CurrentProcessId = GetCurrentProcessId(); + + do + { + if (ProcessEntry.th32ProcessID == CurrentProcessId) + { + break; + } + } + while (Process32Next(Snapshot, &ProcessEntry)); + } + + CloseHandle(Snapshot); + + return OpenProcess(SYNCHRONIZE, FALSE, ProcessEntry.th32ParentProcessID); +} +#endif + #if defined(NO_STACK_PROTECTOR) NO_STACK_PROTECTOR @@ -214,7 +214,10 @@ int main(int argc, char* argv[]) // Provide CEF with command-line arguments. CefMainArgs args(argc, argv); + // Important: same as Windows, CefApp instance should be created in the main function + const CefRefPtr app = new MyApp(); + // Execute the sub-process. - return CefExecuteProcess(args, nullptr, nullptr); + return CefExecuteProcess(args, app, nullptr); } #endif