From b92c8b359c3a913385236d84c2b47d7ec6018e1d Mon Sep 17 00:00:00 2001 From: Bamidev Date: Fri, 1 Mar 2024 12:32:17 +0100 Subject: [PATCH] Make CEF call creation callback whenever the DOM content is actually loaded. --- c/src/cef/bw_handle_map.cpp | 2 +- c/src/cef/bw_handle_map.hpp | 35 ++++++++++++++++++++--------- c/src/cef/client_handler.hpp | 43 +++++++++++++++++++++++++++--------- docs/GETTING-STARTED.md | 4 ++-- examples/terminal.rs | 8 +------ 5 files changed, 61 insertions(+), 31 deletions(-) diff --git a/c/src/cef/bw_handle_map.cpp b/c/src/cef/bw_handle_map.cpp index 04b0b3e..8e6b08c 100644 --- a/c/src/cef/bw_handle_map.cpp +++ b/c/src/cef/bw_handle_map.cpp @@ -2,4 +2,4 @@ -bw::BwHandleMap bw::bw_handle_map; +bw::HandleMap bw::bw_handle_map; diff --git a/c/src/cef/bw_handle_map.hpp b/c/src/cef/bw_handle_map.hpp index d8d0ab7..c6a19f0 100644 --- a/c/src/cef/bw_handle_map.hpp +++ b/c/src/cef/bw_handle_map.hpp @@ -12,43 +12,58 @@ namespace bw { + struct OnCreateCallback { + bw_BrowserWindowCreationCallbackFn callback; + void* data; + }; + + struct BrowserInfo { + bw_BrowserWindow* handle; + std::optional callback; + }; + // A thread safe class that links CEF browser handles to our browser window handdles. // This makes it possible to get a bw_BrowserWindow* from a CefRefPtr, from any thread. - class BwHandleMap { + class HandleMap { // The CefBrowser's GetIdentifier output is used as the key to identify CEF browser handles - std::map map; + std::map map; std::mutex mutex; public: // The only constructor is the default constructor - BwHandleMap() {} + HandleMap() {} // Remove a link void drop( CefRefPtr cef_handle ) { this->mutex.lock(); - this->map.erase( cef_handle->GetIdentifier() ); + this->map.erase(cef_handle->GetIdentifier()); this->mutex.unlock(); } // Stores a link - void store( CefRefPtr cef_handle, bw_BrowserWindow* our_handle ) { + void store(CefRefPtr cef_handle, bw_BrowserWindow* our_handle, bw_BrowserWindowCreationCallbackFn callback, void* callback_data) { this->mutex.lock(); - this->map[ cef_handle->GetIdentifier() ] = our_handle; + BrowserInfo& bw_info = this->map[cef_handle->GetIdentifier()]; + bw_info.handle = our_handle; + bw_info.callback = std::optional(OnCreateCallback { + callback, + data: callback_data + }); this->mutex.unlock(); } // Fetches a bw_BrowserWindow handle from a cef handle. // Returns an optional bw_BrowserWindow pointer. - std::optional fetch( CefRefPtr cef_handle ) { + std::optional fetch( CefRefPtr cef_handle ) { this->mutex.lock(); auto it = this->map.find( cef_handle->GetIdentifier() ); // If not found return nothing if ( it == this->map.end() ) - return std::optional(); + return std::optional(); // If found: - std::optional result( (*it).second ); + std::optional result( (*it).second ); this->mutex.unlock(); return result; @@ -56,7 +71,7 @@ namespace bw { }; // A global instance - extern BwHandleMap bw_handle_map; + extern HandleMap bw_handle_map; } diff --git a/c/src/cef/client_handler.hpp b/c/src/cef/client_handler.hpp index d4c80e3..9984842 100644 --- a/c/src/cef/client_handler.hpp +++ b/c/src/cef/client_handler.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "bw_handle_map.hpp" #include "../application.h" @@ -19,15 +20,37 @@ struct ExternalInvocationHandlerData { std::vector params; }; -class ClientHandler : public CefClient, public CefLifeSpanHandler { +class ClientHandler : public CefClient, public CefLifeSpanHandler, public CefLoadHandler { bw_Application* app; public: ClientHandler( bw_Application* app ) : app(app) {} - virtual CefRefPtr GetLifeSpanHandler() override { - return this; + virtual CefRefPtr GetLifeSpanHandler() override { return this; } + + virtual CefRefPtr GetLoadHandler() override { return this; } + + virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { + std::optional bw_info_opt = bw::bw_handle_map.fetch(browser); + BW_ASSERT(bw_info_opt.has_value(), "Link between CEF's browser handle and our handle does not exist!\n"); + auto bw_info = bw_info_opt.value(); + auto callback_opt = bw_info.callback; + if (callback_opt.has_value()) { + auto value = callback_opt.value(); + value.callback(bw_info.handle, value.data); + } + } + + virtual void OnLoadError(CefRefPtr browser, CefRefPtr frame, CefLoadHandler::ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl) override { + std::optional bw_info_opt = bw::bw_handle_map.fetch(browser); + BW_ASSERT(bw_info_opt.has_value(), "Link between CEF's browser handle and our handle does not exist!\n"); + auto bw_info = bw_info_opt.value(); + auto callback_opt = bw_info.callback; + if (callback_opt.has_value()) { + auto value = callback_opt.value(); + value.callback(bw_info.handle, value.data); + } } virtual bool OnProcessMessageReceived( @@ -47,7 +70,8 @@ class ClientHandler : public CefClient, public CefLifeSpanHandler { this->onInvokeHandlerReceived( browser, frame, source_process, message ); return true; } - // The message to send data from within javascript to application code + // The OnBrowserCreated event is fired on another process, so we need to catch it here and + // update the bw_handle_map in this process. else if ( message->GetName() == "on-browser-created" ) { this->onBrowserCreated( browser, frame, source_process, message ); return true; @@ -84,14 +108,11 @@ class ClientHandler : public CefClient, public CefLifeSpanHandler { bw_handle->impl.cef_ptr = (void*)cef_ptr; // Store a link with the cef browser handle and our handle in a global map - bw::bw_handle_map.store( *cef_ptr, bw_handle ); + bw::bw_handle_map.store(*cef_ptr, bw_handle, callback, callback_data); // Open dev-tools window if ( dev_tools_enabled ) this->openDevTools( bw_handle, browser->GetHost() ); - - // Invoke the completion callback - callback( bw_handle, callback_data ); } void onEvalJsResultReceived( @@ -151,9 +172,9 @@ class ClientHandler : public CefClient, public CefLifeSpanHandler { (void)(source_process); // Obtain our browser window handle - std::optional _bw_handle = bw::bw_handle_map.fetch( browser ); - BW_ASSERT( _bw_handle.has_value(), "Link between CEF's browser handle and our handle does not exist!\n" ); - bw_BrowserWindow* our_handle = *_bw_handle; + std::optional bw_info = bw::bw_handle_map.fetch(browser); + BW_ASSERT( bw_info.has_value(), "Link between CEF's browser handle and our handle does not exist!\n" ); + bw_BrowserWindow* our_handle = bw_info.value().handle; auto msg_args = msg->GetArgumentList(); diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md index 5b029c4..0351e99 100644 --- a/docs/GETTING-STARTED.md +++ b/docs/GETTING-STARTED.md @@ -18,7 +18,8 @@ Here are the pros and cons of each browser framework. Choose wisely: *Pros:* * Is available on all major platforms: Windows, MacOS, Linux (although MacOS support in _BrowserWindow_ needs some work). If you want the exact same behavior of your app on all platforms, CEF is recommended. -* The cookie API is supported when using CEF in _BrowserWindow_. +* The cookie API of _BrowserWindow_ is supported. +* Is the only framework option which can be decently cross-compiled to Windows. *Cons:* * Can be a pain to set up correctly; requires a lot of files to be present for the executable, and needs the sandbox to have specific permissions. @@ -40,7 +41,6 @@ is even a homebrew package for it on MacOS. *Pro:* * Preinstalled on Windows 11 * Can be statically linked to when using the `*-pc-windows-msvc` toolchain. -* Is easy to cross-compile for. (Needs some testing.) *Cons:* * Currenty not yet working on _BrowserWindow_. diff --git a/examples/terminal.rs b/examples/terminal.rs index d685e02..7d25773 100644 --- a/examples/terminal.rs +++ b/examples/terminal.rs @@ -122,13 +122,7 @@ fn main() { .eval_js(format!("initialize({})", &working_dir_js).as_str()) .await { - Err(_) => bw.exec_js( - format!( - "window.onload = () => {{ initialize({}) }}", - &working_dir_js - ) - .as_str(), - ), + Err(e) => eprintln!("Javascript Error: {:?}", e), Ok(_) => {} }; });