From c4bfd440a17f1bad9ea4a91976a5056a8f6a192a Mon Sep 17 00:00:00 2001 From: AgapovAlexsey Date: Thu, 24 Apr 2025 19:29:49 +0200 Subject: [PATCH 1/5] Replaced kPromiseCreatorScript with native promise --- .../Async/JavascriptAsyncMethodCallback.cpp | 12 +-- .../Async/JavascriptAsyncMethodCallback.h | 10 +-- .../Async/JavascriptAsyncMethodHandler.cpp | 28 +------ .../BindObjectAsyncHandler.h | 77 ++++--------------- .../CefAppUnmanagedWrapper.cpp | 22 +----- .../CefAppUnmanagedWrapper.h | 1 - 6 files changed, 29 insertions(+), 121 deletions(-) diff --git a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodCallback.cpp b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodCallback.cpp index b7c6b55967..2de283714c 100644 --- a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodCallback.cpp +++ b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodCallback.cpp @@ -13,13 +13,11 @@ namespace CefSharp { void JavascriptAsyncMethodCallback::Success(const CefRefPtr& result) { - if (_resolve.get() && _context.get() && _context->Enter()) + if (_promise.get() && _context.get() && _context->Enter()) { try { - CefV8ValueList args; - args.push_back(result); - _resolve->ExecuteFunction(nullptr, args); + _promise->ResolvePromise(result); } finally { @@ -30,13 +28,11 @@ namespace CefSharp void JavascriptAsyncMethodCallback::Fail(const CefString& exception) { - if (_reject.get() && _context.get() && _context->Enter()) + if (_promise.get() && _context.get() && _context->Enter()) { try { - CefV8ValueList args; - args.push_back(CefV8Value::CreateString(exception)); - _reject->ExecuteFunction(nullptr, args); + _promise->RejectPromise(exception); } finally { diff --git a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodCallback.h b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodCallback.h index 27411d722b..808563a541 100644 --- a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodCallback.h +++ b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodCallback.h @@ -16,21 +16,19 @@ namespace CefSharp { private: MCefRefPtr _context; - MCefRefPtr _resolve; - MCefRefPtr _reject; + MCefRefPtr _promise; public: - JavascriptAsyncMethodCallback(CefRefPtr context, CefRefPtr resolve, CefRefPtr reject) - :_context(context), _resolve(resolve.get()), _reject(reject.get()) + JavascriptAsyncMethodCallback(CefRefPtr context, CefRefPtr promise) + :_context(context), _promise(promise.get()) { } !JavascriptAsyncMethodCallback() { - _resolve = nullptr; - _reject = nullptr; _context = nullptr; + _promise = nullptr; } ~JavascriptAsyncMethodCallback() diff --git a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodHandler.cpp b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodHandler.cpp index 71628d738f..cece76c0be 100644 --- a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodHandler.cpp +++ b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncMethodHandler.cpp @@ -24,32 +24,10 @@ namespace CefSharp auto context = CefV8Context::GetCurrentContext(); auto frame = context->GetFrame(); - CefRefPtr promiseData; - CefRefPtr promiseException; - //this will create a promise and give us the reject/resolve functions {p: Promise, res: resolve(), rej: reject()} - if (!context->Eval(CefAppUnmanagedWrapper::kPromiseCreatorScript, CefString(), 0, promiseData, promiseException)) - { - LOG(WARNING) << "JavascriptAsyncMethodHandler::Execute promiseData returned exception: " + promiseException->GetMessage().ToString(); - - exception = promiseException->GetMessage(); - - return true; - } - - //when refreshing the browser this is sometimes null, in this case return true and log message - //https://github.com/cefsharp/CefSharp/pull/2446 - if (promiseData == nullptr) - { - LOG(WARNING) << "JavascriptAsyncMethodHandler::Execute promiseData returned nullptr"; - - return true; - } - - retval = promiseData->GetValue("p"); + CefRefPtr promise = CefV8Value::CreatePromise(); + retval = promise; - auto resolve = promiseData->GetValue("res"); - auto reject = promiseData->GetValue("rej"); - auto callback = gcnew JavascriptAsyncMethodCallback(context, resolve, reject); + auto callback = gcnew JavascriptAsyncMethodCallback(context, promise); auto callbackId = _methodCallbackSave->Invoke(callback); auto request = CefProcessMessage::Create(kJavascriptAsyncMethodCallRequest); diff --git a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h index 9ece880b2b..240c73f6d0 100644 --- a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h +++ b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h @@ -165,54 +165,15 @@ namespace CefSharp rootObject->Bind(cachedObjects, context->GetGlobal()); //Objects already bound or ignore cache - CefRefPtr promiseResolve; - CefRefPtr promiseException; - - auto promiseResolveScript = StringUtils::ToNative("Promise.resolve({Success:true, Count:" + cachedObjects->Count + ", Message:'OK'});"); - - if (context->Eval(promiseResolveScript, CefString(), 0, promiseResolve, promiseException)) - { - retval = promiseResolve; - } - else - { - exception = promiseException->GetMessage(); - - return true; - } + retval = CreateResultObject(cachedObjects->Count, "OK", true); NotifyObjectBound(frame, objectNamesWithBoundStatus); } else { - CefRefPtr promiseData; - CefRefPtr promiseException; - //this will create a promise and give us the reject/resolve functions {p: Promise, res: resolve(), rej: reject()} - if (!context->Eval(CefAppUnmanagedWrapper::kPromiseCreatorScript, CefString(), 0, promiseData, promiseException)) - { - exception = promiseException->GetMessage(); - - return true; - } - - //when refreshing the browser this is sometimes null, in this case return true and log message - //https://github.com/cefsharp/CefSharp/pull/2446 - if (promiseData == nullptr) - { - LOG(WARNING) << "BindObjectAsyncHandler::Execute promiseData returned nullptr"; - - return true; - } - - //return the promose - retval = promiseData->GetValue("p"); - - //References to the promise resolve and reject methods - auto resolve = promiseData->GetValue("res"); - auto reject = promiseData->GetValue("rej"); - - auto callback = gcnew JavascriptAsyncMethodCallback(context, resolve, reject); - + CefRefPtr promise = CefV8Value::CreatePromise(); + retval = promise; + auto callback = gcnew JavascriptAsyncMethodCallback(context, promise); auto request = CefProcessMessage::Create(kJavascriptRootObjectRequest); auto argList = request->GetArgumentList(); @@ -228,24 +189,7 @@ namespace CefSharp else { //Objects already bound or ignore cache - CefRefPtr promiseResolve; - CefRefPtr promiseException; - - auto promiseResolveScript = CefString("Promise.resolve({Success:false, Count:0, Message:'Object(s) already bound'});"); - - if (context->Eval(promiseResolveScript, CefString(), 0, promiseResolve, promiseException)) - { - retval = promiseResolve; - - if (notifyIfAlreadyBound) - { - NotifyObjectBound(frame, objectNamesWithBoundStatus); - } - } - else - { - exception = promiseException->GetMessage(); - } + retval = CreateResultObject(0, "Object(s) already bound", false); } } else @@ -267,6 +211,17 @@ namespace CefSharp return true; } + static CefRefPtr CreateResultObject(int count, String^ message, bool isSuccess) + { + auto response = CefV8Value::CreateObject(nullptr, nullptr); + + response->SetValue("Count", CefV8Value::CreateInt(count), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + response->SetValue("Message", CefV8Value::CreateString(StringUtils::ToNative(message)), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + response->SetValue("Success", CefV8Value::CreateBool(isSuccess), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + + return response; + } + private: void NotifyObjectBound(const CefRefPtr frame, List^>^ objectNamesWithBoundStatus) { diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp index a7b2ceb1f3..02abd5e807 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.cpp @@ -35,16 +35,6 @@ namespace CefSharp { namespace BrowserSubprocess { - const CefString CefAppUnmanagedWrapper::kPromiseCreatorScript = "" - "(function()" - "{" - " var result = {};" - " var promise = new Promise(function(resolve, reject) {" - " result.res = resolve; result.rej = reject;" - " });" - " result.p = promise;" - " return result;" - "})();"; const CefString kRenderProcessId = CefString("RenderProcessId"); const CefString kRenderProcessIdCamelCase = CefString("renderProcessId"); @@ -662,23 +652,15 @@ namespace CefSharp if (_registerBoundObjectRegistry->TryGetAndRemoveMethodCallback(callbackId, callback)) { - //Response object has no Accessor or Interceptor - auto response = CefV8Value::CreateObject(nullptr, nullptr); - - response->SetValue("Count", CefV8Value::CreateInt(javascriptObjects->Count), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); if (javascriptObjects->Count > 0) { //TODO: JSB Should we include a list of successfully bound object names? - response->SetValue("Success", CefV8Value::CreateBool(true), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); - response->SetValue("Message", CefV8Value::CreateString("OK"), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); - callback->Success(response); + callback->Success(BindObjectAsyncHandler::CreateResultObject(javascriptObjects->Count, "OK", true)); } else { - response->SetValue("Success", CefV8Value::CreateBool(false), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); - response->SetValue("Message", CefV8Value::CreateString("Zero objects bounds"), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); - callback->Success(response); + callback->Success(BindObjectAsyncHandler::CreateResultObject(javascriptObjects->Count, "Zero objects bounds", false)); } //Send message notifying Browser Process of which objects were bound diff --git a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h index 38f8da2881..c22b2ec4c2 100644 --- a/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/CefAppUnmanagedWrapper.h @@ -40,7 +40,6 @@ namespace CefSharp gcroot _registerBoundObjectRegistry; public: - static const CefString kPromiseCreatorScript; CefAppUnmanagedWrapper(IRenderProcessHandler^ handler, List^ schemes, bool enableFocusedNodeChanged, Action^ onBrowserCreated, Action^ onBrowserDestroyed) : SubProcessApp(schemes) { From fc6021eb5cbf8d849f5bb51ac8051ed0064db579 Mon Sep 17 00:00:00 2001 From: AgapovAlexsey Date: Sat, 26 Apr 2025 19:11:17 +0200 Subject: [PATCH 2/5] Applied camelCase to field names --- .../BindObjectAsyncHandler.h | 14 +++++++++++--- CefSharp.Example/Resources/BindingTestAsync.js | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h index 240c73f6d0..67607f48cc 100644 --- a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h +++ b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h @@ -215,9 +215,17 @@ namespace CefSharp { auto response = CefV8Value::CreateObject(nullptr, nullptr); - response->SetValue("Count", CefV8Value::CreateInt(count), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); - response->SetValue("Message", CefV8Value::CreateString(StringUtils::ToNative(message)), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); - response->SetValue("Success", CefV8Value::CreateBool(isSuccess), CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + const auto countResult = CefV8Value::CreateInt(count); + const auto messageResult = CefV8Value::CreateString(StringUtils::ToNative(message)); + const auto successResult = CefV8Value::CreateBool(isSuccess); + + response->SetValue("Count", countResult, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + response->SetValue("Message", messageResult, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + response->SetValue("Success", successResult, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + + response->SetValue("count", countResult, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + response->SetValue("message", messageResult, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + response->SetValue("success", successResult, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); return response; } diff --git a/CefSharp.Example/Resources/BindingTestAsync.js b/CefSharp.Example/Resources/BindingTestAsync.js index 7defa87416..c87fe508f7 100644 --- a/CefSharp.Example/Resources/BindingTestAsync.js +++ b/CefSharp.Example/Resources/BindingTestAsync.js @@ -286,4 +286,20 @@ QUnit.module('BindingTestAsync', (hooks) => assert.ok(window.boundAsync2 === undefined, "boundAsync2 is now undefined"); }); + QUnit.test('Validate BindObjectAsync result fields:', async (assert) => { + const response = await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true }, 'boundAsync2'); + const keys = Object.getOwnPropertyDescriptors(bindResult); + + assert.equal(true, !!keys['count'], 'count'); + assert.equal(true, !!keys['Count'], 'Count'); + + assert.equal(true, !!keys['message'], 'message'); + assert.equal(true, !!keys['Message'], 'Message'); + + assert.equal(true, !!keys['success'], 'success'); + assert.equal(true, !!keys['Success'], 'Success'); + + assert.equal(true, CefSharp.DeleteBoundObject('boundAsync2'), 'Object was unbound'); + }); + }); From f7540d2bdd0b93f2a9f48973696b8466934338e8 Mon Sep 17 00:00:00 2001 From: amaitland <307872+amaitland@users.noreply.github.com> Date: Wed, 30 Apr 2025 18:56:26 +1000 Subject: [PATCH 3/5] BindObjectAsyncHandler should always return a promise --- CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h index 67607f48cc..3dfd64467c 100644 --- a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h +++ b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h @@ -165,7 +165,9 @@ namespace CefSharp rootObject->Bind(cachedObjects, context->GetGlobal()); //Objects already bound or ignore cache - retval = CreateResultObject(cachedObjects->Count, "OK", true); + auto promiseResolve = CefV8Value::CreatePromise(); + promiseResolve->ResolvePromise(CreateResultObject(cachedObjects->Count, "OK", true)); + retval = promiseResolve; NotifyObjectBound(frame, objectNamesWithBoundStatus); } @@ -189,7 +191,9 @@ namespace CefSharp else { //Objects already bound or ignore cache - retval = CreateResultObject(0, "Object(s) already bound", false); + auto promiseResolve = CefV8Value::CreatePromise(); + promiseResolve->ResolvePromise(CreateResultObject(0, "Object(s) already bound", false)); + retval = promiseResolve; } } else From 82d37214dee792e3830ab296caf56b6f68e51444 Mon Sep 17 00:00:00 2001 From: amaitland <307872+amaitland@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:02:09 +1000 Subject: [PATCH 4/5] Fix xunit test --- .../Resources/BindingTestAsync.js | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/CefSharp.Example/Resources/BindingTestAsync.js b/CefSharp.Example/Resources/BindingTestAsync.js index c87fe508f7..daa47cbaa4 100644 --- a/CefSharp.Example/Resources/BindingTestAsync.js +++ b/CefSharp.Example/Resources/BindingTestAsync.js @@ -286,20 +286,22 @@ QUnit.module('BindingTestAsync', (hooks) => assert.ok(window.boundAsync2 === undefined, "boundAsync2 is now undefined"); }); - QUnit.test('Validate BindObjectAsync result fields:', async (assert) => { - const response = await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true }, 'boundAsync2'); - const keys = Object.getOwnPropertyDescriptors(bindResult); + QUnit.test("Validate BindObjectAsync result object fields:", async (assert) => + { + const response = await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true }, "boundAsync2"); + const keys = Object.getOwnPropertyDescriptors(response); - assert.equal(true, !!keys['count'], 'count'); - assert.equal(true, !!keys['Count'], 'Count'); + assert.equal(!!keys["count"], true, "count"); + assert.equal(!!keys["Count"], true, "Count"); - assert.equal(true, !!keys['message'], 'message'); - assert.equal(true, !!keys['Message'], 'Message'); + assert.equal(!!keys["message"], true, "message"); + assert.equal(!!keys["Message"], true, "Message"); - assert.equal(true, !!keys['success'], 'success'); - assert.equal(true, !!keys['Success'], 'Success'); + assert.equal(!!keys["success"], true, "success"); + assert.equal(!!keys["Success"], true, "Success"); - assert.equal(true, CefSharp.DeleteBoundObject('boundAsync2'), 'Object was unbound'); + assert.equal(true, CefSharp.DeleteBoundObject("boundAsync2"), "Object was unbound"); + assert.ok(window.boundAsync2 === undefined, "boundAsync2 is now undefined"); }); }); From 5e5577455cb50ae23405221d148cd0acf704fecd Mon Sep 17 00:00:00 2001 From: amaitland <307872+amaitland@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:05:54 +1000 Subject: [PATCH 5/5] Readd missing code --- CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h index 3dfd64467c..36311426b2 100644 --- a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h +++ b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h @@ -194,6 +194,11 @@ namespace CefSharp auto promiseResolve = CefV8Value::CreatePromise(); promiseResolve->ResolvePromise(CreateResultObject(0, "Object(s) already bound", false)); retval = promiseResolve; + + if (notifyIfAlreadyBound) + { + NotifyObjectBound(frame, objectNamesWithBoundStatus); + } } } else