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/Async/JavascriptAsyncObjectWrapper.cpp b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.cpp index 401b85a643..a3ee367771 100644 --- a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.cpp @@ -15,12 +15,11 @@ namespace CefSharp { namespace Async { - void JavascriptAsyncObjectWrapper::Bind(JavascriptObject^ object, const CefRefPtr &value) + CefRefPtr JavascriptAsyncObjectWrapper::ConvertToV8Value(JavascriptObject^ object) { //V8Value that represents this javascript object - only one per complex type, no accessor auto javascriptObject = CefV8Value::CreateObject(nullptr, nullptr); auto objectName = StringUtils::ToNative(object->JavascriptName); - value->SetValue(objectName, javascriptObject, V8_PROPERTY_ATTRIBUTE_NONE); for each (JavascriptMethod^ method in Enumerable::OfType(object->Methods)) { @@ -29,6 +28,8 @@ namespace CefSharp _wrappedMethods->Add(wrappedMethod); } + + return javascriptObject; } } } diff --git a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.h b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.h index f42ac60e42..7229dc72dc 100644 --- a/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/Async/JavascriptAsyncObjectWrapper.h @@ -38,7 +38,7 @@ namespace CefSharp } } - void Bind(JavascriptObject^ object, const CefRefPtr &value); + CefRefPtr ConvertToV8Value(JavascriptObject^ object); }; } } diff --git a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h index 9ece880b2b..5ca5764c61 100644 --- a/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h +++ b/CefSharp.BrowserSubprocess.Core/BindObjectAsyncHandler.h @@ -129,6 +129,7 @@ namespace CefSharp if (frame.get() && frame->IsValid()) { + const auto resultPromise = CefV8Value::CreatePromise(); if (boundObjectRequired || ignoreCache) { //If the number of cached objects matches the number of args @@ -162,57 +163,17 @@ namespace CefSharp } //Cached objects only contains a list of objects not already bound - rootObject->Bind(cachedObjects, context->GetGlobal()); + auto bindResult = 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; - } + const auto resultForPromise = CreateBindResult(cachedObjects->Count, "OK", true, bindResult); + resultPromise->ResolvePromise(resultForPromise); 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); - + auto callback = gcnew JavascriptAsyncMethodCallback(context, resultPromise); auto request = CefProcessMessage::Create(kJavascriptRootObjectRequest); auto argList = request->GetArgumentList(); @@ -228,25 +189,16 @@ 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; + const auto resultForPromise = CreateBindResult(0, "Object(s) already bound", false, CefV8Value::CreateArray(0)); + resultPromise->ResolvePromise(resultForPromise); - if (notifyIfAlreadyBound) - { - NotifyObjectBound(frame, objectNamesWithBoundStatus); - } - } - else + if (notifyIfAlreadyBound) { - exception = promiseException->GetMessage(); + NotifyObjectBound(frame, objectNamesWithBoundStatus); } } + + retval = resultPromise; } else { @@ -267,6 +219,27 @@ namespace CefSharp return true; } + static CefRefPtr CreateBindResult(int count, String^ message, bool isSuccess, const CefRefPtr bindResult) + { + auto response = CefV8Value::CreateObject(nullptr, nullptr); + + 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("BindResult", bindResult, 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); + response->SetValue("bindResult", bindResult, 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..fe6dda73cf 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"); @@ -658,27 +648,19 @@ namespace CefSharp try { - rootObject->Bind(javascriptObjects, context->GetGlobal()); + auto bindResult = rootObject->Bind(javascriptObjects, context->GetGlobal()); 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::CreateBindResult(javascriptObjects->Count, "OK", true, bindResult)); } 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::CreateBindResult(javascriptObjects->Count, "Zero objects bounds", false, bindResult)); } //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) { diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.cpp b/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.cpp index afb2926dd8..d3e0349fa6 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.cpp @@ -12,7 +12,7 @@ namespace CefSharp { namespace BrowserSubprocess { - void JavascriptObjectWrapper::Bind(JavascriptObject^ object, const CefRefPtr& v8Value, JavascriptCallbackRegistry^ callbackRegistry) + CefRefPtr JavascriptObjectWrapper::ConvertToV8Value(JavascriptObject^ object, JavascriptCallbackRegistry^ callbackRegistry) { _objectId = object->Id; @@ -24,8 +24,6 @@ namespace CefSharp //V8Value that represents this javascript object - only one per complex type auto javascriptObject = CefV8Value::CreateObject(_jsPropertyHandler.get(), nullptr); - auto objectName = StringUtils::ToNative(object->JavascriptName); - v8Value->SetValue(objectName, javascriptObject, V8_PROPERTY_ATTRIBUTE_NONE); for each (JavascriptMethod ^ method in Enumerable::OfType(object->Methods)) { @@ -42,6 +40,8 @@ namespace CefSharp _wrappedProperties->Add(wrappedproperty); } + + return javascriptObject; } BrowserProcessResponse^ JavascriptObjectWrapper::GetProperty(String^ memberName) diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.h b/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.h index 28eebd3b55..59508acee9 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptObjectWrapper.h @@ -56,7 +56,7 @@ namespace CefSharp } } - void Bind(JavascriptObject^ object, const CefRefPtr& v8Value, JavascriptCallbackRegistry^ callbackRegistry); + CefRefPtr ConvertToV8Value(JavascriptObject^ object, JavascriptCallbackRegistry^ callbackRegistry); BrowserProcessResponse^ GetProperty(String^ memberName); BrowserProcessResponse^ SetProperty(String^ memberName, Object^ value); }; diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.cpp b/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.cpp index 63ec03618d..07a4ed1e48 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/JavascriptPropertyWrapper.cpp @@ -23,7 +23,10 @@ namespace CefSharp if (javascriptProperty->IsComplexType) { auto javascriptObjectWrapper = gcnew JavascriptObjectWrapper(_browserProcess); - javascriptObjectWrapper->Bind(javascriptProperty->JsObject, v8Value, callbackRegistry); + const auto v8Obj = javascriptObjectWrapper->ConvertToV8Value(javascriptProperty->JsObject, callbackRegistry); + + auto objectName = StringUtils::ToNative(javascriptProperty->JsObject->JavascriptName); + v8Value->SetValue(objectName, v8Obj, V8_PROPERTY_ATTRIBUTE_NONE); _javascriptObjectWrapper = javascriptObjectWrapper; } diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.cpp b/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.cpp index 6e49c59df4..7a01f09098 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.cpp +++ b/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.cpp @@ -14,19 +14,21 @@ namespace CefSharp { namespace BrowserSubprocess { - void JavascriptRootObjectWrapper::Bind(ICollection^ objects, const CefRefPtr& v8Value) + CefRefPtr JavascriptRootObjectWrapper::Bind(ICollection^ objects, const CefRefPtr& global) { + auto result = CefV8Value::CreateArray(objects->Count); if (objects->Count > 0) { auto saveMethod = gcnew Func(this, &JavascriptRootObjectWrapper::SaveMethodCallback); for each (JavascriptObject ^ obj in Enumerable::OfType(objects)) { + const auto name = StringUtils::ToNative(obj->JavascriptName); + CefRefPtr v8Obj; if (obj->IsAsync) { auto wrapperObject = gcnew JavascriptAsyncObjectWrapper(_callbackRegistry, saveMethod); - wrapperObject->Bind(obj, v8Value); - + v8Obj = wrapperObject->ConvertToV8Value(obj); _wrappedAsyncObjects->Add(wrapperObject); } #ifndef NETCOREAPP @@ -40,13 +42,17 @@ namespace CefSharp } auto wrapperObject = gcnew JavascriptObjectWrapper(_browserProcess); - wrapperObject->Bind(obj, v8Value, _callbackRegistry); - + v8Obj = wrapperObject->ConvertToV8Value(obj, _callbackRegistry); _wrappedObjects->Add(wrapperObject); } #endif + + result->SetValue(name, v8Obj, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_READONLY); + global->SetValue(name, v8Obj, CefV8Value::PropertyAttribute::V8_PROPERTY_ATTRIBUTE_NONE); } } + + return result; } JavascriptCallbackRegistry^ JavascriptRootObjectWrapper::CallbackRegistry::get() diff --git a/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h b/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h index 45b94b8ed4..33c8ac88ee 100644 --- a/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h +++ b/CefSharp.BrowserSubprocess.Core/JavascriptRootObjectWrapper.h @@ -103,7 +103,7 @@ namespace CefSharp bool TryGetAndRemoveMethodCallback(int64_t id, JavascriptAsyncMethodCallback^% callback); - void Bind(ICollection^ objects, const CefRefPtr& v8Value); + CefRefPtr Bind(ICollection^ objects, const CefRefPtr& global); }; } } diff --git a/CefSharp.Example/Resources/BindingTestAsync.js b/CefSharp.Example/Resources/BindingTestAsync.js index 7defa87416..47a99415bc 100644 --- a/CefSharp.Example/Resources/BindingTestAsync.js +++ b/CefSharp.Example/Resources/BindingTestAsync.js @@ -2,197 +2,197 @@ QUnit.module('BindingTestAsync', (hooks) => { hooks.before(async () => { - await CefSharp.BindObjectAsync("boundAsync"); + await CefSharp.BindObjectAsync('boundAsync'); }); - QUnit.test("BindObjectAsync Second call with boundAsync param", async (assert) => + QUnit.test('BindObjectAsync Second call with boundAsync param', async (assert) => { - const res = await CefSharp.BindObjectAsync("boundAsync"); - assert.equal(res.Success, false, "Second call to BindObjectAsync with already bound objects as params returned false."); + const res = await CefSharp.BindObjectAsync('boundAsync'); + assert.equal(res.Success, false, 'Second call to BindObjectAsync with already bound objects as params returned false.'); }); - QUnit.test("Async call (Throw .Net Exception)", async (assert) => + QUnit.test('Async call (Throw .Net Exception)', async (assert) => { assert.rejects(boundAsync.error()); }); - QUnit.test("Async call (Divide 16 / 2):", async (assert) => + QUnit.test('Async call (Divide 16 / 2):', async (assert) => { const actualResult = await boundAsync.div(16, 2); const expectedResult = 8; - assert.equal(expectedResult, actualResult, "Divide 16 / 2"); + assert.equal(expectedResult, actualResult, 'Divide 16 / 2'); }); - QUnit.test("Async call (Div with Blocking Task 16 / 2):", async (assert) => + QUnit.test('Async call (Div with Blocking Task 16 / 2):', async (assert) => { const actualResult = await boundAsync.divWithBlockingTaskCall(16, 2); const expectedResult = 8; - assert.equal(expectedResult, actualResult, "Divide 16 / 2"); + assert.equal(expectedResult, actualResult, 'Divide 16 / 2'); }); - QUnit.test("Async call (Divide 16 /0)", async (assert) => + QUnit.test('Async call (Divide 16 /0)', async (assert) => { assert.rejects(boundAsync.div(16, 0)); }); - QUnit.test("Async call (UIntAddModel 3 + 2):", async (assert) => + QUnit.test('Async call (UIntAddModel 3 + 2):', async (assert) => { const actualResult = await boundAsync.uIntAddModel({ ParamA: 3, ParamB: 2 }); const expectedResult = 5; - assert.equal(expectedResult, actualResult, "Add 3 + 2 resulted in " + expectedResult); + assert.equal(expectedResult, actualResult, 'Add 3 + 2 resulted in ' + expectedResult); }); - QUnit.test("Async call (UIntAdd 3 + 2):", async (assert) => + QUnit.test('Async call (UIntAdd 3 + 2):', async (assert) => { const actualResult = await boundAsync.uIntAdd(3, 2); const expectedResult = 5; - assert.equal(expectedResult, actualResult, "Add 3 + 2 resulted in " + expectedResult); + assert.equal(expectedResult, actualResult, 'Add 3 + 2 resulted in ' + expectedResult); }); - QUnit.test("Async call (Hello):", async (assert) => + QUnit.test('Async call (Hello):', async (assert) => { const res = await boundAsync.hello('CefSharp'); - assert.equal(res, "Hello CefSharp") + assert.equal(res, 'Hello CefSharp') }); - QUnit.test("Async call (Long Running Task):", async (assert) => + QUnit.test('Async call (Long Running Task):', async (assert) => { const res = await boundAsync.doSomething(); - assert.ok(true, "Slept for 1000ms") + assert.ok(true, 'Slept for 1000ms') }); - QUnit.test("Async call (return Struct):", async (assert) => + QUnit.test('Async call (return Struct):', async (assert) => { var res = await boundAsync.returnObject('CefSharp Struct Test'); - assert.equal(res.Value, "CefSharp Struct Test", "Struct with a single field"); + assert.equal(res.Value, 'CefSharp Struct Test', 'Struct with a single field'); }); - QUnit.test("Async call (return Class):", async (assert) => + QUnit.test('Async call (return Class):', async (assert) => { //Returns a class const res = await boundAsync.returnClass('CefSharp Class Test'); const expectedResult = 'CefSharp Class Test'; - assert.equal(expectedResult, res.Value, "Class with a single property"); + assert.equal(expectedResult, res.Value, 'Class with a single property'); }); - QUnit.test("Async call (return Class as JsonString):", async (assert) => + QUnit.test('Async call (return Class as JsonString):', async (assert) => { const expectedResult = 'CefSharp Class Test'; //Returns a class const res = await boundAsync.returnClassAsJsonString(expectedResult); - assert.equal(expectedResult, res.Value, "Class with a single property"); + assert.equal(expectedResult, res.Value, 'Class with a single property'); }); - QUnit.test("Async call (returnStructArray):", async (assert) => + QUnit.test('Async call (returnStructArray):', async (assert) => { const res = await boundAsync.returnStructArray('CefSharp'); - assert.equal(res[0].Value, "CefSharpItem1", "Expected Result of CefSharpItem1"); - assert.equal(res[1].Value, "CefSharpItem2", "Expected Result of CefSharpItem2"); + assert.equal(res[0].Value, 'CefSharpItem1', 'Expected Result of CefSharpItem1'); + assert.equal(res[1].Value, 'CefSharpItem2', 'Expected Result of CefSharpItem2'); }); - QUnit.test("Async call (returnClassesArray):", async (assert) => + QUnit.test('Async call (returnClassesArray):', async (assert) => { var asyncCallback = assert.async(); boundAsync.returnClassesArray('CefSharp').then(function (res) { - assert.equal(res[0].Value, "CefSharpItem1", "Expected Result of CefSharpItem1"); - assert.equal(res[1].Value, "CefSharpItem2", "Expected Result of CefSharpItem2"); + assert.equal(res[0].Value, 'CefSharpItem1', 'Expected Result of CefSharpItem1'); + assert.equal(res[1].Value, 'CefSharpItem2', 'Expected Result of CefSharpItem2'); asyncCallback(); }); }); - QUnit.test("Async call (echoDateTime): now", async (assert) => + QUnit.test('Async call (echoDateTime): now', async (assert) => { const now = new Date(); const res = await boundAsync.echoDateTime(now); - assert.deepEqual(res, now, "Expected echo datetime"); + assert.deepEqual(res, now, 'Expected echo datetime'); }); - QUnit.test("Async call (echoDateTime): unixTimeZero", async (assert) => + QUnit.test('Async call (echoDateTime): unixTimeZero', async (assert) => { const unixTimeZero = new Date(Date.parse('01 Jan 1970 00:00:00 GMT')); const res = await boundAsync.echoDateTime(unixTimeZero); - assert.deepEqual(res, unixTimeZero, "Expected echo unixTimeZero"); + assert.deepEqual(res, unixTimeZero, 'Expected echo unixTimeZero'); }); - QUnit.test("Async call (echoNullableDateTime):", async (assert) => + QUnit.test('Async call (echoNullableDateTime):', async (assert) => { const now = new Date(); const res = await boundAsync.echoNullableDateTime(now); - assert.deepEqual(res, now, "Expected echo datetime"); + assert.deepEqual(res, now, 'Expected echo datetime'); }); - QUnit.test("Async call (echoNullableDateTime) null param:", async (assert) => + QUnit.test('Async call (echoNullableDateTime) null param:', async (assert) => { const res = await boundAsync.echoNullableDateTime(null); - assert.equal(res, null, "Expected null"); + assert.equal(res, null, 'Expected null'); }); - QUnit.test("Async call (echoArray):", async (assert) => + QUnit.test('Async call (echoArray):', async (assert) => { - const res = await boundAsync.echoArray(["one", null, "three"]); + const res = await boundAsync.echoArray(['one', null, 'three']); - assert.equal(res.length, 3, "Expected result to be length 3"); - assert.equal(res[0], "one", "Expected Result of 1st item"); - assert.equal(res[1], null, "Expected Result of 2nd item"); - assert.equal(res[2], "three", "Expected Result of 3rd item"); + assert.equal(res.length, 3, 'Expected result to be length 3'); + assert.equal(res[0], 'one', 'Expected Result of 1st item'); + assert.equal(res[1], null, 'Expected Result of 2nd item'); + assert.equal(res[2], 'three', 'Expected Result of 3rd item'); }); - QUnit.test("Async call (Test Empty Array):", async (assert) => + QUnit.test('Async call (Test Empty Array):', async (assert) => { const res = await boundAsync.echoArray([]); - assert.equal(Array.isArray(res), true, "Expected result is an array"); - assert.equal(res.length, 0, "Expected result to be length 0 (empty array)"); + assert.equal(Array.isArray(res), true, 'Expected result is an array'); + assert.equal(res.length, 0, 'Expected result to be length 0 (empty array)'); }); - QUnit.test("Async call (echoValueTypeArray):", async (assert) => + QUnit.test('Async call (echoValueTypeArray):', async (assert) => { const res = await boundAsync.echoValueTypeArray([1, null, 3]); - assert.equal(res.length, 3, "Expected result to be length 3"); - assert.equal(res[0], 1, "Expected Result of 1st item"); - assert.equal(res[1], 0, "Expected Result of 2nd item"); - assert.equal(res[2], 3, "Expected Result of 3rd item"); + assert.equal(res.length, 3, 'Expected result to be length 3'); + assert.equal(res[0], 1, 'Expected Result of 1st item'); + assert.equal(res[1], 0, 'Expected Result of 2nd item'); + assert.equal(res[2], 3, 'Expected Result of 3rd item'); }); - QUnit.test("Async call (echoMultidimensionalArray):", async (assert) => + QUnit.test('Async call (echoMultidimensionalArray):', async (assert) => { const res = await boundAsync.echoMultidimensionalArray([[1, 2], null, [3, 4]]); - assert.equal(res.length, 3, "Expected result to be length 3"); - assert.equal(res[0][0], 1, "Expected Result of 1st item"); - assert.equal(res[0][1], 2, "Expected Result of 1st item"); - assert.equal(res[1], null, "Expected Result of 2nd item"); - assert.equal(res[2][0], 3, "Expected Result of 3rd item"); - assert.equal(res[2][1], 4, "Expected Result of 3rd item"); + assert.equal(res.length, 3, 'Expected result to be length 3'); + assert.equal(res[0][0], 1, 'Expected Result of 1st item'); + assert.equal(res[0][1], 2, 'Expected Result of 1st item'); + assert.equal(res[1], null, 'Expected Result of 2nd item'); + assert.equal(res[2][0], 3, 'Expected Result of 3rd item'); + assert.equal(res[2][1], 4, 'Expected Result of 3rd item'); }); - QUnit.test("Async call (methodReturnList):", async (assert) => + QUnit.test('Async call (methodReturnList):', async (assert) => { const list1 = [ - "Element 0 - First", - "Element 1", - "Element 2 - Last" + 'Element 0 - First', + 'Element 1', + 'Element 2 - Last' ]; const list2 = [ - ["Element 0, 0", "Element 0, 1"], - ["Element 1, 0", "Element 1, 1"], - ["Element 2, 0", "Element 2, 1"] + ['Element 0, 0', 'Element 0, 1'], + ['Element 1, 0', 'Element 1, 1'], + ['Element 2, 0', 'Element 2, 1'] ]; const results = await Promise.all([ @@ -200,11 +200,11 @@ QUnit.module('BindingTestAsync', (hooks) => boundAsync.methodReturnsListOfLists(), ]); - assert.deepEqual(results[0], list1, "Call to boundAsync.MethodReturnsList() resulted in : " + JSON.stringify(results[0])); - assert.deepEqual(results[1], list2, "Call to boundAsync.MethodReturnsListOfLists() resulted in : " + JSON.stringify(results[1])); + assert.deepEqual(results[0], list1, 'Call to boundAsync.MethodReturnsList() resulted in : ' + JSON.stringify(results[0])); + assert.deepEqual(results[1], list2, 'Call to boundAsync.MethodReturnsListOfLists() resulted in : ' + JSON.stringify(results[1])); }); - QUnit.test("Async call (methodReturnsDictionary):", async (assert) => + QUnit.test('Async call (methodReturnsDictionary):', async (assert) => { const dict1 = { @@ -215,7 +215,7 @@ QUnit.module('BindingTestAsync', (hooks) => { "onepointfive": 1.5, "five": 5, - "ten": "ten", + "ten": 'ten', "twotwo": [2, 2] }; const dict3 = @@ -224,7 +224,7 @@ QUnit.module('BindingTestAsync', (hooks) => { "onepointfive": 1.5, "five": 5, - "ten": "ten", + "ten": 'ten', "twotwo": [2, 2] } }; @@ -235,55 +235,77 @@ QUnit.module('BindingTestAsync', (hooks) => boundAsync.methodReturnsDictionary3() ]); - assert.deepEqual(results[0], dict1, "Call to boundAsync.MethodReturnsDictionary1() resulted in : " + JSON.stringify(results[0])); - assert.deepEqual(results[1], dict2, "Call to boundAsync.MethodReturnsDictionary2() resulted in : " + JSON.stringify(results[1])); - assert.deepEqual(results[2], dict3, "Call to boundAsync.MethodReturnsDictionary3() resulted in : " + JSON.stringify(results[2])); + assert.deepEqual(results[0], dict1, 'Call to boundAsync.MethodReturnsDictionary1() resulted in : ' + JSON.stringify(results[0])); + assert.deepEqual(results[1], dict2, 'Call to boundAsync.MethodReturnsDictionary2() resulted in : ' + JSON.stringify(results[1])); + assert.deepEqual(results[2], dict3, 'Call to boundAsync.MethodReturnsDictionary3() resulted in : ' + JSON.stringify(results[2])); }); - QUnit.test("Async call (PassSimpleClassAsArgument):", async (assert) => + QUnit.test('Async call (PassSimpleClassAsArgument):', async (assert) => { const res = await boundAsync.passSimpleClassAsArgument({ - TestString: "Hello", + TestString: 'Hello', SubClasses: [ - { PropertyOne: "Test Property One", Numbers: [1, 2, 3] }, - { PropertyOne: "Test Property Two", Numbers: [4, 5, 6] } + { PropertyOne: 'Test Property One', Numbers: [1, 2, 3] }, + { PropertyOne: 'Test Property Two', Numbers: [4, 5, 6] } ] }); assert.equal(res.Item1, true) - assert.equal(res.Item2, "TestString:Hello;SubClasses[0].PropertyOne:Test Property One"); + assert.equal(res.Item2, 'TestString:Hello;SubClasses[0].PropertyOne:Test Property One'); }); - QUnit.test("Bind boundAsync2 and call (Hello):", async (assert) => + QUnit.test('Bind boundAsync2 and call (Hello):', async (assert) => { //Can use both cefSharp.bindObjectAsync and CefSharp.BindObjectAsync, both do the same - const result = await cefSharp.bindObjectAsync({ NotifyIfAlreadyBound: true }, "boundAsync2"); + const result = await cefSharp.bindObjectAsync({ NotifyIfAlreadyBound: true }, 'boundAsync2'); const res = await boundAsync2.hello('CefSharp'); - assert.equal(res, "Hello CefSharp") - assert.equal(true, CefSharp.DeleteBoundObject("boundAsync2"), "Object was unbound"); - assert.ok(window.boundAsync2 === undefined, "boundAsync2 is now undefined"); + assert.equal(res, 'Hello CefSharp') + assert.equal(true, CefSharp.DeleteBoundObject('boundAsync2'), 'Object was unbound'); + assert.ok(window.boundAsync2 === undefined, 'boundAsync2 is now undefined'); + assert.equal(true, !!result.BindResult.boundAsync2); }); //Repeat of the previous test to make sure binding a deleted object is working - QUnit.test("Bind boundAsync2 and call (Hello) Second Attempt:", async (assert) => + QUnit.test('Bind boundAsync2 and call (Hello) Second Attempt:', async (assert) => { - const result = await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true }, "boundAsync2"); + const result = await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true }, 'boundAsync2'); const res = await boundAsync2.hello('CefSharp'); - assert.equal(res, "Hello CefSharp") + assert.equal(res, 'Hello CefSharp') + assert.equal(true, !!result.BindResult.boundAsync2); }); //Repeat of the previous test to make sure binding a deleted object is working - QUnit.test("Bind boundAsync2 and call (Hello) Third Attempt:", async (assert) => + QUnit.test('Bind boundAsync2 and call (Hello) Third Attempt:', async (assert) => { - const result = await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true }, "boundAsync2"); + const result = await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true }, 'boundAsync2'); const res = await boundAsync2.hello('CefSharp'); - assert.equal(res, "Hello CefSharp") - assert.equal(true, CefSharp.DeleteBoundObject("boundAsync2"), "Object was unbound"); - assert.ok(window.boundAsync2 === undefined, "boundAsync2 is now undefined"); + assert.equal(res, 'Hello CefSharp') + assert.equal(true, CefSharp.DeleteBoundObject('boundAsync2'), 'Object was unbound'); + assert.ok(window.boundAsync2 === undefined, 'boundAsync2 is now undefined'); + assert.equal(true, !!result.BindResult.boundAsync2); + }); + + QUnit.test('Validates BindObjectAsync result fields:', async (assert) => { + const bindResult = 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, !!keys['bindResult'], 'bindResult'); + assert.equal(true, !!keys['BindResult'], 'BindResult'); + + assert.equal(true, CefSharp.DeleteBoundObject('boundAsync2'), 'Object was unbound'); }); });