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() 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 diff --git a/examples/opengl-example/src/opengl-example.cpp b/examples/opengl-example/src/opengl-example.cpp index f024f10..cfac92a 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, std::placeholders::_2)); + mDullahan->navigate(mHomeUrl); } @@ -463,6 +482,12 @@ void openglExample::onRequestExitCallback() glfwSetWindowShouldClose(mWindow, GLFW_TRUE); } +std::string openglExample::onJStoCPPMsgCallback(const std::string id, const std::string msg) +{ + std::cout << "Received message with ID: " << id << " from JavaScript: " << msg << std::endl; + return "Message received loud and clear by C++!"; +} + void openglExample::initUI() { IMGUI_CHECKVERSION(); @@ -624,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 cb85f0c..d234f07 100644 --- a/examples/opengl-example/src/opengl-example.h +++ b/examples/opengl-example/src/opengl-example.h @@ -69,6 +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 id, const std::string msg); private: GLFWwindow* mWindow; diff --git a/src/dullahan.cpp b/src/dullahan.cpp index afc0cce..5b9dbe4 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..06911e5 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..f3054e6 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(), args->GetString(1).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..d350fad 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 id, const std::string msg) +{ + if (mOnJStoCPPMsgCallbackFunc) + { + return mOnJStoCPPMsgCallbackFunc(id, msg); + } + + return std::string(); +} diff --git a/src/dullahan_callback_manager.h b/src/dullahan_callback_manager.h index 8141f0e..86a098f 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 id, 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/dullahan_version.h.in b/src/dullahan_version.h.in index e42c6fd..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 26 +#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 diff --git a/src/host/dullahan_host.cpp b/src/host/dullahan_host.cpp index 4d10a64..56bbaf8 100644 --- a/src/host/dullahan_host.cpp +++ b/src/host/dullahan_host.cpp @@ -28,6 +28,94 @@ #include "cef_app.h" +// Shared by Windows and Mac sub-process entry points +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() != 2 || ! arguments[0]->IsString() || ! arguments[1]->IsString()) + { + exception = "JSONtoCPP(json, id) expects two string arguments"; + return true; + } + + // Send the JSON string to the browser process and return an acknowledgment + 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 + // if there was an error in DoJSONtoCPP + return true; + } + + return false; + } + + private: + 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, id); + args->SetString(1, 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, "INFO"); + args->SetString(1, "Hello from the OnContextCreated in the sub-process!"); + browser->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); + } + +private: + IMPLEMENT_REFCOUNTING(MyApp); +}; + #ifdef __linux__ #if defined(NO_STACK_PROTECTOR) NO_STACK_PROTECTOR @@ -49,57 +137,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 +168,7 @@ HANDLE GetParentProcess() } #endif + #if defined(NO_STACK_PROTECTOR) NO_STACK_PROTECTOR #endif @@ -149,9 +187,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 @@ -174,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