Skip to content

Commit

Permalink
- Added the possibility to add new js function callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
kkEngine committed Jul 26, 2024
1 parent c58926f commit 12301ec
Show file tree
Hide file tree
Showing 15 changed files with 257 additions and 84 deletions.
139 changes: 83 additions & 56 deletions src/CEFSubprocess/CEF/NirnLabSubprocessCefApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,71 @@ namespace NL::CEF
spdlog::set_default_logger(logger);
}

size_t NirnLabSubprocessCefApp::AddFunctionHandlers(CefRefPtr<CefBrowser> a_browser,
CefRefPtr<CefFrame> a_frame,
CefProcessId a_sourceProcess,
CefRefPtr<CefDictionaryValue> a_funcDict)
{
size_t addedFuncCount = 0;

const auto v8Context = a_frame->GetV8Context();
if (!v8Context->Enter())
{
spdlog::error("{}[{}]: can't enter v8 context", NameOf(NirnLabSubprocessCefApp::AddFunctionHandlers), ::GetCurrentProcessId());
return addedFuncCount;
}

CefDictionaryValue::KeyList keyList;
if (!a_funcDict->GetKeys(keyList))
{
spdlog::error("{}[{}]: can't get keys from function dictionary", NameOf(NirnLabSubprocessCefApp::AddFunctionHandlers), ::GetCurrentProcessId());
}
else
{
for (const auto& objectName : keyList)
{
auto currentObjectValue = v8Context->GetGlobal();
const auto funcList = a_funcDict->GetList(objectName);
for (auto i = 0; i < funcList->GetSize(); ++i)
{
const auto& funcName = funcList->GetString(i);
if (funcName.empty())
{
continue;
}

++addedFuncCount;

if (!objectName.empty() || objectName != IPC_JS_WINDOW_OBJECT_NAME)
{
auto objectValue = currentObjectValue->GetValue(objectName);
if (objectValue == nullptr || objectValue->IsNull() || objectValue->IsUndefined())
{
objectValue = CefV8Value::CreateObject(nullptr, nullptr);
currentObjectValue->SetValue(objectName, objectValue, V8_PROPERTY_ATTRIBUTE_NONE);
}
currentObjectValue = objectValue;
}

CefRefPtr<NL::JS::CEFFunctionHandler> funcHandler = new NL::JS::CEFFunctionHandler(a_browser, objectName);
CefRefPtr<CefV8Value> funcValue = CefV8Value::CreateFunction(funcName, funcHandler);
currentObjectValue->SetValue(funcName, funcValue, V8_PROPERTY_ATTRIBUTE_NONE);
}
}
}

v8Context->Exit();
return addedFuncCount;
}

void NirnLabSubprocessCefApp::OnBeforeCommandLineProcessing(CefString const& process_type,
CefRefPtr<CefCommandLine> command_line)
{
m_processType = process_type;
InitLog(nullptr);

DWORD mainProcessId = std::stoi(command_line->GetSwitchValue(IPC_CL_PROCESS_ID_NAME).ToWString());
if (mainProcessId)
if (mainProcessId && process_type == RENDER_PROCESS_TYPE)
{
new std::thread([=]() {
const auto procHandle = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, mainProcessId);
Expand All @@ -39,6 +97,7 @@ namespace NL::CEF
// // log or email to sportloto?
// }

std::this_thread::sleep_for(1.42s);
::TerminateProcess(::GetCurrentProcess(), EXIT_SUCCESS);
});
}
Expand All @@ -53,81 +112,49 @@ namespace NL::CEF
CefRefPtr<CefDictionaryValue> extra_info)
{
m_logSink->SetBrowser(browser);

if (extra_info != nullptr && extra_info->GetSize() > 0)
{
CefDictionaryValue::KeyList keyList;
if (!extra_info->GetKeys(keyList))
{
spdlog::error("{}: error format in param {}", NameOf(NirnLabSubprocessCefApp::OnBrowserCreated), NameOf(extra_info));
}
else
{
for (const auto& key : keyList)
{
const auto funcList = extra_info->GetList(key);
for (auto i = 0; i < funcList->GetSize(); ++i)
{
const auto funcName = funcList->GetValue(i)->GetString();
if (!funcName.empty())
{
m_funcQueue.AddFunction(key, funcName);
}
else
{
spdlog::error("{}: error format in param {}, key {}", NameOf(NirnLabSubprocessCefApp::OnBrowserCreated), NameOf(extra_info), key.ToString().c_str());
}
}
}
}
}
spdlog::info("{}[{}]: browser created with id {}", NameOf(NirnLabSubprocessCefApp), ::GetCurrentProcessId(), browser->GetIdentifier());
}

void NirnLabSubprocessCefApp::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser)
{
spdlog::info("{}[{}]: browser destroyed with id {}", NameOf(NirnLabSubprocessCefApp), ::GetCurrentProcessId(), browser->GetIdentifier());
m_logSink->SetBrowser(nullptr);
}

void NirnLabSubprocessCefApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
if (!frame->IsMain())
if (frame->IsMain())
{
return;
auto message = CefProcessMessage::Create(IPC_JS_CONTEXT_CREATED);
frame->SendProcessMessage(PID_BROWSER, message);
}
}

std::uint32_t functionsCount = 0;
auto currentObjectValue = context->GetGlobal();
void NirnLabSubprocessCefApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
}

auto addFuncInfo = m_funcQueue.PopNext();
while (addFuncInfo != nullptr)
bool NirnLabSubprocessCefApp::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message)
{
if (message->GetName() == IPC_JS_FUNCION_ADD_EVENT)
{
++functionsCount;
if (!addFuncInfo->objectName.empty() || addFuncInfo->objectName != IPC_JS_WINDOW_OBJECT_NAME)
const auto funcDict = message->GetArgumentList()->GetDictionary(0);
if (funcDict == nullptr)
{
auto objectValue = currentObjectValue->GetValue(addFuncInfo->objectName);
if (objectValue == nullptr || objectValue->IsNull() || objectValue->IsUndefined())
{
objectValue = CefV8Value::CreateObject(nullptr, nullptr);
currentObjectValue->SetValue(addFuncInfo->objectName, objectValue, V8_PROPERTY_ATTRIBUTE_NONE);
}
currentObjectValue = objectValue;
return true;
}

CefRefPtr<NL::JS::CEFFunctionHandler> funcHandler = new NL::JS::CEFFunctionHandler(browser, addFuncInfo->objectName);
CefRefPtr<CefV8Value> funcValue = CefV8Value::CreateFunction(addFuncInfo->functionName, funcHandler);
currentObjectValue->SetValue(addFuncInfo->functionName, funcValue, V8_PROPERTY_ATTRIBUTE_NONE);

addFuncInfo = m_funcQueue.PopNext();
const auto addedFuncCount = AddFunctionHandlers(browser, frame, source_process, funcDict);
spdlog::info("{}[{}]: registered {} functions for the browser with id {}", NameOf(NirnLabSubprocessCefApp::OnProcessMessageReceived), ::GetCurrentProcessId(), addedFuncCount, browser->GetIdentifier());
}

spdlog::info("{}: registered {} functions for the browser with id {}", NameOf(OnContextCreated), functionsCount, browser->GetIdentifier());
}

void NirnLabSubprocessCefApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
return false;
}
}
14 changes: 12 additions & 2 deletions src/CEFSubprocess/CEF/NirnLabSubprocessCefApp.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#define RENDER_PROCESS_TYPE "renderer"

#include "PCH.h"
#include "Log/IPCLogSink.hpp"
#include "JS/CEFFunctionQueue.h"
Expand All @@ -13,14 +15,18 @@ namespace NL::CEF
IMPLEMENT_REFCOUNTING(NirnLabSubprocessCefApp);

private:
NL::JS::CEFFunctionQueue m_funcQueue;

std::shared_ptr<NL::Log::IPCLogSink_mt> m_logSink = nullptr;
CefString m_processType;
void InitLog(CefRefPtr<CefBrowser> a_browser);

public:
NirnLabSubprocessCefApp() = default;

size_t AddFunctionHandlers(CefRefPtr<CefBrowser> a_browser,
CefRefPtr<CefFrame> a_frame,
CefProcessId a_sourceProcess,
CefRefPtr<CefDictionaryValue> a_funcDict);

// CefApp
void OnBeforeCommandLineProcessing(CefString const& process_type, CefRefPtr<CefCommandLine> command_line) override;
CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override;
Expand All @@ -35,5 +41,9 @@ namespace NL::CEF
void OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) override;
bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override;
};
}
1 change: 1 addition & 0 deletions src/CEFSubprocess/IPC.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
#define IPC_LOG_EVENT "log-event"

#define IPC_JS_WINDOW_OBJECT_NAME "window"
#define IPC_JS_CONTEXT_CREATED "js-context-created"
#define IPC_JS_FUNCTION_CALL_EVENT "js-function-call"
#define IPC_JS_FUNCION_ADD_EVENT "js-function-add"
41 changes: 28 additions & 13 deletions src/UIPlatform/CEF/DefaultBrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,35 @@ namespace NL::CEF
// load url
if (m_isUrlCached)
{
LoadBrowserURL(m_urlCache.c_str(), m_urlClearJSCache);
LoadBrowserURL(m_urlCache.c_str(), m_clearJSFunctions);
}
});

m_onMainFrameLoadStart_Connection = m_cefClient->onMainFrameLoadStart.connect([&]() {
std::lock_guard locker(m_urlMutex);
m_isPageLoaded = false;
});

m_onMainFrameLoadEnd_Connection = m_cefClient->onMainFrameLoadEnd.connect([&]() {
std::lock_guard locker(m_urlMutex);
m_isPageLoaded = true;

// JS funcs callback
if (m_clearJSFunctions)
{
m_jsFuncStorage->ClearFunctionCallback();
}
else
{
const auto browser = m_cefClient->GetBrowser();
if (browser != nullptr && m_jsFuncStorage->GetSize() > 0)
{
auto cefMessage = CefProcessMessage::Create(IPC_JS_FUNCION_ADD_EVENT);
cefMessage->GetArgumentList()->SetDictionary(0, m_jsFuncStorage->ConvertToCefDictionary());
browser->GetMainFrame()->SendProcessMessage(CefProcessId::PID_RENDERER, cefMessage);
}
}

// JS funcs
for (auto& funcInfo : m_jsFuncCallbackInfoCache)
{
AddFunctionCallbackAndSendMessage(funcInfo);
}
m_jsFuncCallbackInfoCache.clear();

// JS exec scripts
if (!m_jsExecCache.empty())
Expand Down Expand Up @@ -219,8 +230,12 @@ namespace NL::CEF
if (browser != nullptr)
{
auto cefMessage = CefProcessMessage::Create(IPC_JS_FUNCION_ADD_EVENT);
cefMessage->GetArgumentList()->SetDictionary(0, m_jsFuncStorage->ConvertToCefDictionary());

auto dictValue = CefDictionaryValue::Create();
auto listValue = CefListValue::Create();
listValue->SetSize(1);
listValue->SetString(0, CefString(a_callbackInfo.funcName));
dictValue->SetList(CefString(a_callbackInfo.objectName), listValue);
cefMessage->GetArgumentList()->SetDictionary(0, dictValue);
browser->GetMainFrame()->SendProcessMessage(CefProcessId::PID_RENDERER, cefMessage);
}
}
Expand Down Expand Up @@ -303,18 +318,18 @@ namespace NL::CEF
void __cdecl DefaultBrowser::LoadBrowserURL(const char* a_url, bool a_clearJSFunctions)
{
std::lock_guard locker(m_urlMutex);
if (!IsBrowserReady())
m_clearJSFunctions = a_clearJSFunctions;
if (!IsPageLoaded())
{
m_isUrlCached = true;
m_urlCache = a_url;
m_urlClearJSCache = a_clearJSFunctions;
return;
}

const auto frame = m_cefClient->GetBrowser()->GetMainFrame();
if (frame)
{

m_isPageLoaded = false;
frame->LoadURL(CefString(a_url));
}
else
Expand Down Expand Up @@ -354,7 +369,7 @@ namespace NL::CEF
std::lock_guard locker(m_urlMutex);
if (!IsPageLoaded())
{
m_jsFuncCallbackInfoCache.push_back(NL::JS::JSFuncInfoString(a_callbackInfo));
m_jsFuncCallbackInfoCache.push_back(a_callbackInfo);
return;
}

Expand Down
2 changes: 1 addition & 1 deletion src/UIPlatform/CEF/DefaultBrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace NL::CEF
std::recursive_mutex m_urlMutex;
bool m_isUrlCached = false;
bool m_isPageLoaded = false;
bool m_urlClearJSCache = true;
bool m_clearJSFunctions = false;
std::string m_urlCache = "";

// Focus
Expand Down
6 changes: 6 additions & 0 deletions src/UIPlatform/CEF/NirnLabCefApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ namespace NL::CEF
// tell Chromium to autoplay <video> elements without
// requiring the muted attribute or user interaction
command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required");

// https://chromium.googlesource.com/chromium/src/+/main/docs/process_model_and_site_isolation.md
command_line->AppendSwitch("disable-site-isolation-for-policy");
command_line->AppendSwitch("disable-site-isolation-trials");
command_line->AppendSwitchWithValue("process-per-site", "false");
//command_line->AppendSwitch("single-process");
}

CefRefPtr<CefBrowserProcessHandler> CEF::NirnLabCefApp::GetBrowserProcessHandler()
Expand Down
3 changes: 2 additions & 1 deletion src/UIPlatform/CEF/NirnLabCefClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ namespace NL::CEF
CefRefPtr<CefFrame> frame,
int httpStatusCode)
{
if (browser->IsSame(m_cefBrowser) && frame->IsMain())
// YES, httpStatusCode can be negative o_O
if (browser->IsSame(m_cefBrowser) && frame->IsMain() && httpStatusCode >= 0)
{
onMainFrameLoadEnd();
}
Expand Down
Loading

0 comments on commit 12301ec

Please sign in to comment.