Skip to content

Commit

Permalink
- Added the possibility to remove js function callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
kkEngine committed Jul 27, 2024
1 parent 12301ec commit d1846c7
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 14 deletions.
70 changes: 67 additions & 3 deletions src/CEFSubprocess/CEF/NirnLabSubprocessCefApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ namespace NL::CEF
continue;
}

++addedFuncCount;

if (!objectName.empty() || objectName != IPC_JS_WINDOW_OBJECT_NAME)
if (!objectName.empty())
{
auto objectValue = currentObjectValue->GetValue(objectName);
if (objectValue == nullptr || objectValue->IsNull() || objectValue->IsUndefined())
Expand All @@ -69,6 +67,7 @@ namespace NL::CEF
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);
++addedFuncCount;
}
}
}
Expand All @@ -77,6 +76,60 @@ namespace NL::CEF
return addedFuncCount;
}

size_t NirnLabSubprocessCefApp::RemoveFunctionHandlers(CefRefPtr<CefBrowser> a_browser,
CefRefPtr<CefFrame> a_frame,
CefProcessId a_sourceProcess,
CefRefPtr<CefDictionaryValue> a_funcDict)
{
size_t removedFuncCount = 0;
const auto v8Context = a_frame->GetV8Context();
if (!v8Context->Enter())
{
spdlog::error("{}[{}]: can't enter v8 context", NameOf(NirnLabSubprocessCefApp::RemoveFunctionHandlers), ::GetCurrentProcessId());
return removedFuncCount;
}

CefDictionaryValue::KeyList keyList;
if (!a_funcDict->GetKeys(keyList))
{
spdlog::error("{}[{}]: can't get keys from function dictionary", NameOf(NirnLabSubprocessCefApp::RemoveFunctionHandlers), ::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;
}

if (!objectName.empty())
{
auto objectValue = currentObjectValue->GetValue(objectName);
if (objectValue != nullptr && objectValue->IsObject())
{
currentObjectValue = objectValue;
}
}

// Note: the window object does not allow deleting a custom function for some reason. Use different object.
if (currentObjectValue->DeleteValue(funcName))
{
++removedFuncCount;
}
}
}
}

v8Context->Exit();
return removedFuncCount;
}

void NirnLabSubprocessCefApp::OnBeforeCommandLineProcessing(CefString const& process_type,
CefRefPtr<CefCommandLine> command_line)
{
Expand Down Expand Up @@ -154,6 +207,17 @@ namespace NL::CEF
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());
}
else if (message->GetName() == IPC_JS_FUNCTION_REMOVE_EVENT)
{
const auto funcDict = message->GetArgumentList()->GetDictionary(0);
if (funcDict == nullptr)
{
return true;
}

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

return false;
}
Expand Down
4 changes: 4 additions & 0 deletions src/CEFSubprocess/CEF/NirnLabSubprocessCefApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ namespace NL::CEF
CefRefPtr<CefFrame> a_frame,
CefProcessId a_sourceProcess,
CefRefPtr<CefDictionaryValue> a_funcDict);
size_t RemoveFunctionHandlers(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;
Expand Down
1 change: 1 addition & 0 deletions src/CEFSubprocess/IPC.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
#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"
#define IPC_JS_FUNCTION_REMOVE_EVENT "js-function-remove"
68 changes: 64 additions & 4 deletions src/UIPlatform/CEF/DefaultBrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ namespace NL::CEF
m_onMainFrameLoadStart_Connection = m_cefClient->onMainFrameLoadStart.connect([&]() {
std::lock_guard locker(m_urlMutex);
m_isPageLoaded = true;
// JS funcs callback

// Add js func callbacks
if (m_clearJSFunctions)
{
m_jsFuncStorage->ClearFunctionCallback();
Expand All @@ -80,6 +80,13 @@ namespace NL::CEF
}
m_jsFuncCallbackInfoCache.clear();

// Remove js func callbacks
for (auto& funcInfo : m_jsFuncRemoveCache)
{
RemoveFunctionCallbackAndSendMessage(std::get<0>(funcInfo).c_str(), std::get<1>(funcInfo).c_str());
}
m_jsFuncRemoveCache.clear();

// JS exec scripts
if (!m_jsExecCache.empty())
{
Expand Down Expand Up @@ -233,8 +240,25 @@ namespace NL::CEF
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);
listValue->SetString(0, a_callbackInfo.funcName);
dictValue->SetList(a_callbackInfo.objectName, listValue);
cefMessage->GetArgumentList()->SetDictionary(0, dictValue);
browser->GetMainFrame()->SendProcessMessage(CefProcessId::PID_RENDERER, cefMessage);
}
}

void DefaultBrowser::RemoveFunctionCallbackAndSendMessage(const char* a_objectName, const char* a_funcName)
{
m_jsFuncStorage->RemoveFunctionCallback(a_objectName, a_funcName);
const auto browser = m_cefClient->GetBrowser();
if (browser != nullptr)
{
auto cefMessage = CefProcessMessage::Create(IPC_JS_FUNCTION_REMOVE_EVENT);
auto dictValue = CefDictionaryValue::Create();
auto listValue = CefListValue::Create();
listValue->SetSize(1);
listValue->SetString(0, a_objectName);
dictValue->SetList(a_funcName, listValue);
cefMessage->GetArgumentList()->SetDictionary(0, dictValue);
browser->GetMainFrame()->SendProcessMessage(CefProcessId::PID_RENDERER, cefMessage);
}
Expand Down Expand Up @@ -369,13 +393,49 @@ namespace NL::CEF
std::lock_guard locker(m_urlMutex);
if (!IsPageLoaded())
{
for (auto it = m_jsFuncRemoveCache.begin(); it != m_jsFuncRemoveCache.end(); ++it)
{
if (std::get<0>(*it) == a_callbackInfo.objectName && std::get<1>(*it) == a_callbackInfo.funcName)
{
m_jsFuncRemoveCache.erase(it);
}
}

m_jsFuncCallbackInfoCache.push_back(a_callbackInfo);
return;
}

AddFunctionCallbackAndSendMessage(a_callbackInfo);
}

void __cdecl DefaultBrowser::RemoveFunctionCallback(const char* a_objectName, const char* a_funcName)
{
std::lock_guard locker(m_urlMutex);
if (!IsPageLoaded())
{
for (auto it = m_jsFuncCallbackInfoCache.begin(); it != m_jsFuncCallbackInfoCache.end(); ++it)
{
if (it->objectNameString == a_objectName && it->funcNameString == a_funcName)
{
m_jsFuncCallbackInfoCache.erase(it);
}
}

if (!m_jsFuncStorage->RemoveFunctionCallback(a_objectName, a_funcName))
{
m_jsFuncRemoveCache.push_back({a_objectName, a_funcName});
}
return;
}

RemoveFunctionCallbackAndSendMessage(a_objectName, a_funcName);
}

void __cdecl DefaultBrowser::RemoveFunctionCallback(const NL::JS::JSFuncInfo& a_callbackInfo)
{
RemoveFunctionCallback(a_callbackInfo.objectName, a_callbackInfo.funcName);
}

#pragma endregion

#pragma region RE::MenuEventHandler
Expand Down
6 changes: 5 additions & 1 deletion src/UIPlatform/CEF/DefaultBrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ namespace NL::CEF
std::vector<std::tuple<std::string, std::string>> m_jsExecCache;

// JS function callback
std::vector<NL::JS::JSFuncInfoString> m_jsFuncCallbackInfoCache;
std::list<NL::JS::JSFuncInfoString> m_jsFuncCallbackInfoCache;
std::list<std::tuple<std::string, std::string>> m_jsFuncRemoveCache;

RE::CursorMenu* m_cursorMenu = nullptr;
float& m_currentMousePosX = RE::MenuCursor::GetSingleton()->cursorPosX;
Expand Down Expand Up @@ -77,6 +78,7 @@ namespace NL::CEF
bool IsReadyAndLog();

void AddFunctionCallbackAndSendMessage(const NL::JS::JSFuncInfo& a_callbackInfo);
void RemoveFunctionCallbackAndSendMessage(const char* a_objectName, const char* a_funcName);

// IBrowser
bool __cdecl IsBrowserReady() override;
Expand All @@ -93,6 +95,8 @@ namespace NL::CEF
void __cdecl LoadBrowserURL(const char* a_url, bool a_clearJSFunctions = true) override;
void __cdecl ExecuteJavaScript(const char* a_script, const char* a_scriptUrl = JS_EXECUTE_SCRIPT_URL) override;
void __cdecl AddFunctionCallback(const NL::JS::JSFuncInfo& a_callbackInfo) override;
void __cdecl RemoveFunctionCallback(const char* a_objectName, const char* a_funcName) override;
void __cdecl RemoveFunctionCallback(const NL::JS::JSFuncInfo& a_callbackInfo) override;

// RE::MenuEventHandler
bool CanProcess(RE::InputEvent* a_event) override;
Expand Down
2 changes: 2 additions & 0 deletions src/UIPlatform/NirnLabUIPlatformAPI/IBrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,7 @@ namespace NL::CEF
virtual void __cdecl LoadBrowserURL(const char* a_url, bool a_clearJSFunctions = true) = 0;
virtual void __cdecl ExecuteJavaScript(const char* a_script, const char* a_scriptUrl = JS_EXECUTE_SCRIPT_URL) = 0;
virtual void __cdecl AddFunctionCallback(const NL::JS::JSFuncInfo& a_callbackInfo) = 0;
virtual void __cdecl RemoveFunctionCallback(const char* a_objectName, const char* a_funcName) = 0;
virtual void __cdecl RemoveFunctionCallback(const NL::JS::JSFuncInfo& a_callbackInfo) = 0;
};
}
2 changes: 2 additions & 0 deletions src/UIPlatformTest/NirnLabUIPlatformAPI/IBrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,7 @@ namespace NL::CEF
virtual void __cdecl LoadBrowserURL(const char* a_url, bool a_clearJSFunctions = true) = 0;
virtual void __cdecl ExecuteJavaScript(const char* a_script, const char* a_scriptUrl = JS_EXECUTE_SCRIPT_URL) = 0;
virtual void __cdecl AddFunctionCallback(const NL::JS::JSFuncInfo& a_callbackInfo) = 0;
virtual void __cdecl RemoveFunctionCallback(const char* a_objectName, const char* a_funcName) = 0;
virtual void __cdecl RemoveFunctionCallback(const NL::JS::JSFuncInfo& a_callbackInfo) = 0;
};
}
13 changes: 7 additions & 6 deletions src/UIPlatformTest/TestCases/LocalTestPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace NL::UI::TestCase
{
// func1
auto func1 = new JS::JSFuncInfo();
func1->objectName = "window";
func1->objectName = "NL";
func1->funcName = "func1";
func1->callbackData.executeInGameThread = false;
func1->callbackData.callback = [](const char** a_args, int a_argsCount) {
Expand All @@ -21,7 +21,7 @@ namespace NL::UI::TestCase

// func2
auto func2 = new JS::JSFuncInfo();
func2->objectName = "window";
func2->objectName = "NL";
func2->funcName = "func2";
func2->callbackData.executeInGameThread = true;
func2->callbackData.callback = [](const char** a_args, int a_argsCount) {
Expand Down Expand Up @@ -58,10 +58,10 @@ namespace NL::UI::TestCase
m_pingThread = std::make_shared<std::thread>([=]() {
std::this_thread::sleep_for(12s);

m_browser->LoadBrowserURL("file:///_testLocalPage.html", false);
m_browser->LoadBrowserURL("file:///_testLocalPage.html", true);

// func1
JS::JSFuncInfoString strFunInfo("window", "func1");
JS::JSFuncInfoString strFunInfo("NL", "func1");
strFunInfo.callbackData.executeInGameThread = false;
strFunInfo.callbackData.callback = [](const char** a_args, int a_argsCount) {
std::string argsStr = "";
Expand All @@ -73,17 +73,18 @@ namespace NL::UI::TestCase
spdlog::info("func1__ callback. params: {}", argsStr);
};
m_browser->AddFunctionCallback(strFunInfo);

while (!m_browser->IsPageLoaded())
{
std::this_thread::sleep_for(100ms);
}
//m_browser->RemoveFunctionCallback(strFunInfo.objectName, strFunInfo.funcName);

int i = 0;
while (i < 10)
{
std::this_thread::sleep_for(1s);
m_browser->ExecuteJavaScript(fmt::format("window.func1({})", std::to_string(++i).c_str()).c_str());
m_browser->ExecuteJavaScript(fmt::format("NL.func1({})", std::to_string(++i).c_str()).c_str());
}

a_api->ReleaseBrowserHandle(m_browserHandle);
Expand Down

0 comments on commit d1846c7

Please sign in to comment.