Skip to content

Commit 7a0b06a

Browse files
committed
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<string, object> should change to IDictionary<string, object> 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<string, object> 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
1 parent a6f20e8 commit 7a0b06a

File tree

5 files changed

+93
-67
lines changed

5 files changed

+93
-67
lines changed

CefSharp.Core/Internals/Serialization/ObjectsSerialization.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "Primitives.h"
88

99
using namespace System::Collections::Generic;
10+
using namespace System::Dynamic;
1011

1112
namespace CefSharp
1213
{
@@ -62,17 +63,20 @@ namespace CefSharp
6263
}
6364
else if (type == VTYPE_DICTIONARY)
6465
{
65-
auto dict = gcnew Dictionary<String^, Object^>();
66+
67+
IDictionary<String^, Object^>^ expandoObj = gcnew ExpandoObject();
6668
auto subDict = list->GetDictionary(index);
6769
std::vector<CefString> keys;
6870
subDict->GetKeys(keys);
6971

7072
for (auto i = 0; i < keys.size(); i++)
7173
{
72-
dict->Add(StringUtils::ToClr(keys[i]), DeserializeObject(subDict, keys[i], javascriptCallbackFactory));
74+
auto key = StringUtils::ToClr(keys[i]);
75+
auto value = DeserializeObject(subDict, keys[i], javascriptCallbackFactory);
76+
expandoObj->Add(key, value);
7377
}
7478

75-
result = dict;
79+
result = expandoObj;
7680
}
7781

7882
return result;

CefSharp.Example/AsyncBoundObject.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
44

55
using System;
6+
using System.Collections.Generic;
67
using System.Diagnostics;
8+
using System.Text;
79
using System.Threading;
810

911
namespace CefSharp.Example
@@ -34,9 +36,11 @@ public string Hello(string name)
3436
return "Hello " + name;
3537
}
3638

37-
public void DoSomething()
39+
public string DoSomething()
3840
{
3941
Thread.Sleep(1000);
42+
43+
return "Waited for 1000ms before returning";
4044
}
4145

4246
public JsObject ReturnObject(string name)
@@ -55,5 +59,17 @@ public JsObject[] ObjectArray(string name)
5559
new JsObject() { Value = "Item2" }
5660
};
5761
}
62+
63+
public string DynamiObjectList(IList<dynamic> objects)
64+
{
65+
var builder = new StringBuilder();
66+
67+
foreach(var browser in objects)
68+
{
69+
builder.Append("Browser(Name:" + browser.Name + ";Engine:" + browser.Engine.Name + ");");
70+
}
71+
72+
return builder.ToString();
73+
}
5874
}
5975
}

CefSharp.Example/Resources/BindingTest.html

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
var p = document.createElement('p');
1616
var br = document.createElement('br');
1717
var br2 = document.createElement('br');
18+
var hr = document.createElement('hr');
1819
var title = document.createTextNode('Async Call: ');
1920
var callText = document.createTextNode(call);
2021
var endText = document.createTextNode(end);
@@ -24,28 +25,29 @@
2425
p.appendChild(callText);
2526
p.appendChild(br2);
2627
p.appendChild(endText);
27-
28+
p.appendChild(hr);
29+
2830
asResult.appendChild(p);
2931
}
3032

3133
function asyncError()
3234
{
33-
var call = "Async call (Throw Exception): " + Date();
35+
let call = "Async call (Throw Exception): " + Date();
3436
boundAsync.error().catch(function (e)
3537
{
3638
var end = "Error: " + e + "(" + Date() + ")";
3739
writeAsyncResult(call, end);
3840
});
3941
}
4042

41-
function asyncDivOk()
43+
async function asyncDivOk()
4244
{
43-
var call = "Async call (Divide 16 / 2): " + Date();
44-
boundAsync.div(16, 2).then(function (res)
45-
{
46-
var end = "Result: " + res + "(" + Date() + ")";
47-
writeAsyncResult(call, end);
48-
});
45+
let call = "Async call (Divide 16 / 2): " + Date();
46+
47+
let res = await boundAsync.div(16, 2);
48+
49+
let end = "Result: " + res + "(" + Date() + ")";
50+
writeAsyncResult(call, end);
4951
}
5052

5153
function asyncDivFail()
@@ -63,53 +65,57 @@
6365
});
6466
}
6567

66-
function asyncHello()
68+
async function asyncHello()
6769
{
68-
var call = "Async call (Hello): " + Date();
69-
boundAsync.hello('CefSharp').then(function (res)
70-
{
71-
var end = "Result: " + res + "(" + Date() + ")";
72-
writeAsyncResult(call, end);
73-
});
70+
let call = "Async call (Hello): " + Date();
71+
let res = await boundAsync.hello('CefSharp');
72+
73+
var end = "Result: " + res + "(" + Date() + ")";
74+
writeAsyncResult(call, end);
75+
76+
return end;
7477
}
7578

76-
function asyncDoSomething()
79+
async function asyncDoSomething()
7780
{
78-
var call = "Async call (Long Running Task): " + Date();
79-
boundAsync.doSomething().then(function (res)
80-
{
81-
var end = "Result: " + res + "(" + Date() + ")";
82-
writeAsyncResult(call, end);
83-
});
81+
let call = "Async call (Long Running Task): " + Date();
82+
let res = await boundAsync.doSomething();
83+
84+
let end = "Result: " + res + "(" + Date() + ")";
85+
writeAsyncResult(call, end);
8486
}
8587

86-
function asyncObject()
88+
async function asyncObject()
8789
{
88-
var call = "Async call (Object): " + Date();
89-
boundAsync.returnObject('CefSharp').then(function (res)
90-
{
91-
var end = "Result: " + JSON.stringify(res) + " (" + Date() + ")";
92-
writeAsyncResult(call, end);
93-
});
90+
let call = "Async call (Object): " + Date();
91+
var res = await boundAsync.returnObject('CefSharp');
92+
var end = "Result: " + JSON.stringify(res) + " (" + Date() + ")";
93+
writeAsyncResult(call, end);
9494
}
9595

96-
function asyncObjectArray()
96+
async function asyncObjectArray()
9797
{
98-
var call = "Async call (ObjectArray): " + Date();
99-
boundAsync.objectArray('CefSharp').then(function (res)
100-
{
101-
var end = "Result: [ " + res.map(function (item) { return item.Value }) + " ] (" + Date() + ")";
102-
writeAsyncResult(call, end);
103-
});
98+
let call = "Async call (ObjectArray): " + Date();
99+
let res = await boundAsync.objectArray('CefSharp');
100+
let end = "Result: [ " + res.map(function (item) { return item.Value }) + " ] (" + Date() + ")";
101+
writeAsyncResult(call, end);
104102
}
105103

104+
async function asyncDictionaryPassedAsParam()
105+
{
106+
let call = [{ Name : "Chrome", Engine : {Name : "WebKit"} }, { Name : "Chromium", Engine : {Name : "WebKit"} }, { Name : "Opera", Engine : {Name : "WebKit"} }];
107+
let res = await boundAsync.dynamiObjectList(call);
108+
writeAsyncResult(call, res);
109+
}
110+
106111
asyncError();
107112
asyncDivOk();
108113
asyncDivFail();
109114
asyncDoSomething();
110115
asyncHello();
111116
asyncObject();
112117
asyncObjectArray();
118+
asyncDictionaryPassedAsParam();
113119
</script>
114120
</p>
115121
<p>
@@ -293,13 +299,8 @@
293299
<script type="text/javascript">
294300
function str2ab(str)
295301
{
296-
var buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
297-
var bufView = new Uint16Array(buf);
298-
for (var i = 0, strLen = str.length; i < strLen; i++)
299-
{
300-
bufView[i] = str.charCodeAt(i);
301-
}
302-
return buf;
302+
var enc = new TextEncoder("utf-8");
303+
return enc.encode(str).buffer;
303304
}
304305

305306
document.write(bound.methodWithParams('With 1 Params', 'hello-world') + "<br/>");
@@ -313,8 +314,11 @@
313314
document.write(bound.methodWithThreeParamsOneOptionalOneArray("Test", null) + "<br/>");
314315
document.write(bound.methodWithThreeParamsOneOptionalOneArray(null, null, "Arg1", "Arg2") + "<br/>");
315316

316-
var buffer = str2ab("Testing array buffer");
317-
document.write(bound.complexParamObject(buffer));
317+
//CEF Does not currently support ArrayBuffer directly
318+
//https://bitbucket.org/chromiumembedded/cef/issues/244
319+
//https://bitbucket.org/chromiumembedded/cef/pull-requests/12/v8-renderer-add-basic-arraybuffer-support/diff
320+
//var buffer = str2ab("Testing array buffer");
321+
//document.write(bound.complexParamObject(buffer));
318322
</script>
319323
</p>
320324
</body>

CefSharp/Internals/JavascriptObjectRepository.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,14 @@ public bool TryCallMethod(long objectId, string name, object[] parameters, out o
174174
{
175175
var paramType = method.Parameters[i].Type;
176176

177-
if(parameters[i].GetType() == typeof(Dictionary<string, object>))
177+
if(typeof(IDictionary<string, object>).IsAssignableFrom(parameters[i].GetType()))
178178
{
179-
var dictionary = (Dictionary<string, object>)parameters[i];
179+
var dictionary = (IDictionary<string, object>)parameters[i];
180180
parameters[i] = obj.Binder.Bind(dictionary, paramType);
181181
}
182-
else if (parameters[i].GetType() == typeof(List<object>))
182+
else if (typeof(IList<object>).IsAssignableFrom(parameters[i].GetType()))
183183
{
184-
var list = (List<object>)parameters[i];
184+
var list = (IList<object>)parameters[i];
185185
parameters[i] = obj.Binder.Bind(list, paramType);
186186
}
187187
}

CefSharp/ModelBinding/DefaultBinder.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public virtual object Bind(object obj, Type modelType)
100100

101101
if (val != null)
102102
{
103-
if (val.GetType() == typeof(Dictionary<string, object>))
103+
if (typeof(IDictionary<string, object>).IsAssignableFrom(val.GetType()))
104104
{
105105
var subModel = Bind(val, genericType);
106106
model.Add(subModel);
@@ -114,13 +114,18 @@ public virtual object Bind(object obj, Type modelType)
114114
}
115115
else
116116
{
117-
foreach (var modelProperty in bindingContext.ValidModelBindingMembers)
118-
{
119-
var val = GetValue(modelProperty.Name, bindingContext);
120-
121-
if (val != null)
117+
//If the object type is a dictionary (we're using ExpandoObject instead of Dictionary now)
118+
//Then attempt to bind all the members
119+
if (typeof(IDictionary<string, object>).IsAssignableFrom(bindingContext.Object.GetType()))
120+
{
121+
foreach (var modelProperty in bindingContext.ValidModelBindingMembers)
122122
{
123-
BindValue(modelProperty, val, bindingContext);
123+
var val = GetValue(modelProperty.Name, bindingContext);
124+
125+
if (val != null)
126+
{
127+
BindValue(modelProperty, val, bindingContext);
128+
}
124129
}
125130
}
126131
}
@@ -187,13 +192,10 @@ protected virtual object CreateModel(Type modelType, Type genericType)
187192

188193
protected virtual object GetValue(string propertyName, BindingContext context)
189194
{
190-
if (context.Object.GetType() == typeof(Dictionary<string, object>))
195+
var dictionary = (IDictionary<string, object>)context.Object;
196+
if (dictionary.ContainsKey(propertyName))
191197
{
192-
var dictionary = (Dictionary<string, object>)context.Object;
193-
if (dictionary.ContainsKey(propertyName))
194-
{
195-
return dictionary[propertyName];
196-
}
198+
return dictionary[propertyName];
197199
}
198200

199201
return null;

0 commit comments

Comments
 (0)