From 7a0b06ad0b9fdb0470fc02c04cbeea69bd31e7ff Mon Sep 17 00:00:00 2001 From: amaitland Date: Thu, 21 Dec 2017 13:00:36 +1000 Subject: [PATCH] JSB Add support for dynamic keyword (using ExpandoObject) As ExpandoObject implements IDictionary we can use it interchangeable with only minor breaking changes. Those expecting Dictionary should change to IDictionary or ExpandoObject for their param types This will also impact anyone who has implemented their own custom IBinder I'd suggest changing the type checking from explicitly checking Dictionary to use Type.IsAssignableFrom or another method of comparison DefaultBinder binder has been updated to accommodate ExpandoObject (now being returned by ObjectsSerialization.cpp) Add new async binding for testing new assignment method Rewrite some of the async bound object tests to use await and let - modernize them --- .../Serialization/ObjectsSerialization.cpp | 10 +- CefSharp.Example/AsyncBoundObject.cs | 18 +++- CefSharp.Example/Resources/BindingTest.html | 96 ++++++++++--------- .../Internals/JavascriptObjectRepository.cs | 8 +- CefSharp/ModelBinding/DefaultBinder.cs | 28 +++--- 5 files changed, 93 insertions(+), 67 deletions(-) diff --git a/CefSharp.Core/Internals/Serialization/ObjectsSerialization.cpp b/CefSharp.Core/Internals/Serialization/ObjectsSerialization.cpp index a9d8c03130..972e31d5ab 100644 --- a/CefSharp.Core/Internals/Serialization/ObjectsSerialization.cpp +++ b/CefSharp.Core/Internals/Serialization/ObjectsSerialization.cpp @@ -7,6 +7,7 @@ #include "Primitives.h" using namespace System::Collections::Generic; +using namespace System::Dynamic; namespace CefSharp { @@ -62,17 +63,20 @@ namespace CefSharp } else if (type == VTYPE_DICTIONARY) { - auto dict = gcnew Dictionary(); + + IDictionary^ expandoObj = gcnew ExpandoObject(); auto subDict = list->GetDictionary(index); std::vector keys; subDict->GetKeys(keys); for (auto i = 0; i < keys.size(); i++) { - dict->Add(StringUtils::ToClr(keys[i]), DeserializeObject(subDict, keys[i], javascriptCallbackFactory)); + auto key = StringUtils::ToClr(keys[i]); + auto value = DeserializeObject(subDict, keys[i], javascriptCallbackFactory); + expandoObj->Add(key, value); } - result = dict; + result = expandoObj; } return result; diff --git a/CefSharp.Example/AsyncBoundObject.cs b/CefSharp.Example/AsyncBoundObject.cs index c95e031e40..07ff2c819d 100644 --- a/CefSharp.Example/AsyncBoundObject.cs +++ b/CefSharp.Example/AsyncBoundObject.cs @@ -3,7 +3,9 @@ // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Text; using System.Threading; namespace CefSharp.Example @@ -34,9 +36,11 @@ public string Hello(string name) return "Hello " + name; } - public void DoSomething() + public string DoSomething() { Thread.Sleep(1000); + + return "Waited for 1000ms before returning"; } public JsObject ReturnObject(string name) @@ -55,5 +59,17 @@ public JsObject[] ObjectArray(string name) new JsObject() { Value = "Item2" } }; } + + public string DynamiObjectList(IList objects) + { + var builder = new StringBuilder(); + + foreach(var browser in objects) + { + builder.Append("Browser(Name:" + browser.Name + ";Engine:" + browser.Engine.Name + ");"); + } + + return builder.ToString(); + } } } diff --git a/CefSharp.Example/Resources/BindingTest.html b/CefSharp.Example/Resources/BindingTest.html index 8dd5514e71..dd7ebc2181 100644 --- a/CefSharp.Example/Resources/BindingTest.html +++ b/CefSharp.Example/Resources/BindingTest.html @@ -15,6 +15,7 @@ var p = document.createElement('p'); var br = document.createElement('br'); var br2 = document.createElement('br'); + var hr = document.createElement('hr'); var title = document.createTextNode('Async Call: '); var callText = document.createTextNode(call); var endText = document.createTextNode(end); @@ -24,13 +25,14 @@ p.appendChild(callText); p.appendChild(br2); p.appendChild(endText); - + p.appendChild(hr); + asResult.appendChild(p); } function asyncError() { - var call = "Async call (Throw Exception): " + Date(); + let call = "Async call (Throw Exception): " + Date(); boundAsync.error().catch(function (e) { var end = "Error: " + e + "(" + Date() + ")"; @@ -38,14 +40,14 @@ }); } - function asyncDivOk() + async function asyncDivOk() { - var call = "Async call (Divide 16 / 2): " + Date(); - boundAsync.div(16, 2).then(function (res) - { - var end = "Result: " + res + "(" + Date() + ")"; - writeAsyncResult(call, end); - }); + let call = "Async call (Divide 16 / 2): " + Date(); + + let res = await boundAsync.div(16, 2); + + let end = "Result: " + res + "(" + Date() + ")"; + writeAsyncResult(call, end); } function asyncDivFail() @@ -63,46 +65,49 @@ }); } - function asyncHello() + async function asyncHello() { - var call = "Async call (Hello): " + Date(); - boundAsync.hello('CefSharp').then(function (res) - { - var end = "Result: " + res + "(" + Date() + ")"; - writeAsyncResult(call, end); - }); + let call = "Async call (Hello): " + Date(); + let res = await boundAsync.hello('CefSharp'); + + var end = "Result: " + res + "(" + Date() + ")"; + writeAsyncResult(call, end); + + return end; } - function asyncDoSomething() + async function asyncDoSomething() { - var call = "Async call (Long Running Task): " + Date(); - boundAsync.doSomething().then(function (res) - { - var end = "Result: " + res + "(" + Date() + ")"; - writeAsyncResult(call, end); - }); + let call = "Async call (Long Running Task): " + Date(); + let res = await boundAsync.doSomething(); + + let end = "Result: " + res + "(" + Date() + ")"; + writeAsyncResult(call, end); } - function asyncObject() + async function asyncObject() { - var call = "Async call (Object): " + Date(); - boundAsync.returnObject('CefSharp').then(function (res) - { - var end = "Result: " + JSON.stringify(res) + " (" + Date() + ")"; - writeAsyncResult(call, end); - }); + let call = "Async call (Object): " + Date(); + var res = await boundAsync.returnObject('CefSharp'); + var end = "Result: " + JSON.stringify(res) + " (" + Date() + ")"; + writeAsyncResult(call, end); } - function asyncObjectArray() + async function asyncObjectArray() { - var call = "Async call (ObjectArray): " + Date(); - boundAsync.objectArray('CefSharp').then(function (res) - { - var end = "Result: [ " + res.map(function (item) { return item.Value }) + " ] (" + Date() + ")"; - writeAsyncResult(call, end); - }); + let call = "Async call (ObjectArray): " + Date(); + let res = await boundAsync.objectArray('CefSharp'); + let end = "Result: [ " + res.map(function (item) { return item.Value }) + " ] (" + Date() + ")"; + writeAsyncResult(call, end); } + async function asyncDictionaryPassedAsParam() + { + let call = [{ Name : "Chrome", Engine : {Name : "WebKit"} }, { Name : "Chromium", Engine : {Name : "WebKit"} }, { Name : "Opera", Engine : {Name : "WebKit"} }]; + let res = await boundAsync.dynamiObjectList(call); + writeAsyncResult(call, res); + } + asyncError(); asyncDivOk(); asyncDivFail(); @@ -110,6 +115,7 @@ asyncHello(); asyncObject(); asyncObjectArray(); + asyncDictionaryPassedAsParam();

@@ -293,13 +299,8 @@

diff --git a/CefSharp/Internals/JavascriptObjectRepository.cs b/CefSharp/Internals/JavascriptObjectRepository.cs index 0f66a365c4..a247cca23c 100644 --- a/CefSharp/Internals/JavascriptObjectRepository.cs +++ b/CefSharp/Internals/JavascriptObjectRepository.cs @@ -174,14 +174,14 @@ public bool TryCallMethod(long objectId, string name, object[] parameters, out o { var paramType = method.Parameters[i].Type; - if(parameters[i].GetType() == typeof(Dictionary)) + if(typeof(IDictionary).IsAssignableFrom(parameters[i].GetType())) { - var dictionary = (Dictionary)parameters[i]; + var dictionary = (IDictionary)parameters[i]; parameters[i] = obj.Binder.Bind(dictionary, paramType); } - else if (parameters[i].GetType() == typeof(List)) + else if (typeof(IList).IsAssignableFrom(parameters[i].GetType())) { - var list = (List)parameters[i]; + var list = (IList)parameters[i]; parameters[i] = obj.Binder.Bind(list, paramType); } } diff --git a/CefSharp/ModelBinding/DefaultBinder.cs b/CefSharp/ModelBinding/DefaultBinder.cs index 6109cf6b25..bc59ce9c03 100644 --- a/CefSharp/ModelBinding/DefaultBinder.cs +++ b/CefSharp/ModelBinding/DefaultBinder.cs @@ -100,7 +100,7 @@ public virtual object Bind(object obj, Type modelType) if (val != null) { - if (val.GetType() == typeof(Dictionary)) + if (typeof(IDictionary).IsAssignableFrom(val.GetType())) { var subModel = Bind(val, genericType); model.Add(subModel); @@ -114,13 +114,18 @@ public virtual object Bind(object obj, Type modelType) } else { - foreach (var modelProperty in bindingContext.ValidModelBindingMembers) - { - var val = GetValue(modelProperty.Name, bindingContext); - - if (val != null) + //If the object type is a dictionary (we're using ExpandoObject instead of Dictionary now) + //Then attempt to bind all the members + if (typeof(IDictionary).IsAssignableFrom(bindingContext.Object.GetType())) + { + foreach (var modelProperty in bindingContext.ValidModelBindingMembers) { - BindValue(modelProperty, val, bindingContext); + var val = GetValue(modelProperty.Name, bindingContext); + + if (val != null) + { + BindValue(modelProperty, val, bindingContext); + } } } } @@ -187,13 +192,10 @@ protected virtual object CreateModel(Type modelType, Type genericType) protected virtual object GetValue(string propertyName, BindingContext context) { - if (context.Object.GetType() == typeof(Dictionary)) + var dictionary = (IDictionary)context.Object; + if (dictionary.ContainsKey(propertyName)) { - var dictionary = (Dictionary)context.Object; - if (dictionary.ContainsKey(propertyName)) - { - return dictionary[propertyName]; - } + return dictionary[propertyName]; } return null;