Skip to content

Commit c417970

Browse files
santoshmghsghanti
authored andcommitted
Sync Javascript Binding - Add support for property interception (#3935)
* Core - added support for property interceptor * Incorporated the review comments * Incorporated the review comments 2 * Review comments exclude trygetproperty and trysetproperty for .netcore app * Added the unit test cases for trygetproperty and trysetproperty * Fixed .net core app build failure * Added the separate test case for property interceptor Co-authored-by: sghanti <[email protected]>
1 parent 20aba5c commit c417970

10 files changed

+164
-6
lines changed

CefSharp.Example/CefSharp.Example.netcore.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@
8787
<None Remove="obj/**/*.*" />
8888
<Compile Remove="obj/**/*.*" />
8989
</ItemGroup>
90+
91+
<ItemGroup>
92+
<Compile Remove="ModelBinding\PropertyInterceptorLogger.cs" />
93+
</ItemGroup>
9094

9195
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk.WindowsDesktop" />
9296
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright © 2022 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
using System;
6+
using System.Diagnostics;
7+
using CefSharp.ModelBinding;
8+
9+
namespace CefSharp.Example.ModelBinding
10+
{
11+
public class PropertyInterceptorLogger : IPropertyInterceptor
12+
{
13+
object IPropertyInterceptor.InterceptGet(Func<object> propertyGetter, string propertyName)
14+
{
15+
object result = propertyGetter();
16+
Debug.WriteLine("InterceptGet " + propertyName);
17+
return result;
18+
}
19+
20+
void IPropertyInterceptor.InterceptSet(Action<object> propertySetter, object parameter, string propertName)
21+
{
22+
Debug.WriteLine("InterceptSet " + propertName);
23+
propertySetter(parameter);
24+
}
25+
}
26+
}

CefSharp.Test/JavascriptBinding/JavaScriptObjectRepositoryFacts.cs

+33
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
//
33
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
44

5+
using CefSharp.Example.ModelBinding;
56
using CefSharp.Internals;
7+
using System;
68
using System.Collections.Generic;
79
using Xunit;
810

@@ -46,5 +48,36 @@ public void CanRegisterJavascriptObjectBindWhenNamespaceIsNull()
4648
Assert.True(result.Success);
4749
Assert.Equal("ok", result.ReturnValue.ToString());
4850
}
51+
52+
#if !NETCOREAPP
53+
[Fact]
54+
public void CanRegisterJavascriptObjectPropertyBindWhenNamespaceIsNull()
55+
{
56+
IJavascriptObjectRepositoryInternal javascriptObjectRepository = new JavascriptObjectRepository();
57+
var name = nameof(NoNamespaceClass);
58+
59+
BindingOptions bindingOptions = new BindingOptions()
60+
{
61+
Binder = BindingOptions.DefaultBinder.Binder,
62+
PropertyInterceptor = new PropertyInterceptorLogger()
63+
};
64+
javascriptObjectRepository.Register(name, new NoNamespaceClass(), false, bindingOptions);
65+
Assert.True(javascriptObjectRepository.IsBound(name));
66+
67+
var boundObjects = javascriptObjectRepository.GetObjects(new List<string> { name });
68+
Assert.Single(boundObjects);
69+
70+
object getResult, setResult = 100;
71+
string exception;
72+
NoNamespaceClass noNamespaceClass = new NoNamespaceClass();
73+
bool retValue = javascriptObjectRepository.TrySetProperty(boundObjects[0].Id, "year", setResult, out exception);
74+
Assert.True(retValue);
75+
76+
retValue = javascriptObjectRepository.TryGetProperty(boundObjects[0].Id, "year", out getResult, out exception);
77+
Assert.True(retValue);
78+
Assert.Equal(100, Convert.ToInt32(getResult));
79+
}
80+
#endif
4981
}
82+
5083
}

CefSharp.Wpf.Example/Views/BrowserTabView.xaml.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ public BrowserTabView()
4646
var bindingOptions = new BindingOptions()
4747
{
4848
Binder = BindingOptions.DefaultBinder.Binder,
49-
MethodInterceptor = new MethodInterceptorLogger() // intercept .net methods calls from js and log it
49+
MethodInterceptor = new MethodInterceptorLogger(), // intercept .net methods calls from js and log it
50+
#if !NETCOREAPP
51+
PropertyInterceptor = new PropertyInterceptorLogger()
52+
#endif
5053
};
5154

5255
//To use the ResolveObject below and bind an object with isAsync:false we must set CefSharpSettings.WcfEnabled = true before
@@ -90,7 +93,7 @@ public BrowserTabView()
9093
{
9194
if (e.ObjectName == "bound")
9295
{
93-
repo.Register("bound", new BoundObject(), isAsync: false, options: BindingOptions.DefaultBinder);
96+
repo.Register("bound", new BoundObject(), isAsync: false, options: bindingOptions);
9497
}
9598
else if (e.ObjectName == "boundAsync")
9699
{

CefSharp/BindingOptions.cs

+9
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,14 @@ public static BindingOptions DefaultBinder
2929
/// for logging calls (from js) to .net methods.
3030
/// </summary>
3131
public IMethodInterceptor MethodInterceptor { get; set; }
32+
33+
#if !NETCOREAPP
34+
/// <summary>
35+
/// Interceptor used for intercepting get/set calls to the target object property. For instance, can be used
36+
/// for logging calls to .net property (from js)
37+
/// </summary>
38+
public IPropertyInterceptor PropertyInterceptor { get; set; }
39+
#endif
40+
3241
}
3342
}

CefSharp/CefSharp.netcore.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<Compile Remove="CefRuntime.cs" />
4646
<Compile Remove="DevTools\DevToolsClient.Generated.cs" />
4747
<Compile Remove="Internals\Partial\ChromiumWebBrowser.Partial.cs" />
48+
<Compile Remove="ModelBinding\IPropertyInterceptor.cs" />
4849
</ItemGroup>
4950

5051
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />

CefSharp/Internals/IJavascriptObjectRepositoryInternal.cs

+2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ public interface IJavascriptObjectRepositoryInternal : IJavascriptObjectReposito
1414
{
1515
TryCallMethodResult TryCallMethod(long objectId, string name, object[] parameters);
1616
Task<TryCallMethodResult> TryCallMethodAsync(long objectId, string name, object[] parameters);
17+
#if !NETCOREAPP
1718
bool TryGetProperty(long objectId, string name, out object result, out string exception);
1819
bool TrySetProperty(long objectId, string name, object value, out string exception);
20+
#endif
1921
bool IsBrowserInitialized { get; set; }
2022
List<JavascriptObject> GetObjects(List<string> names = null);
2123
List<JavascriptObject> GetLegacyBoundObjects();

CefSharp/Internals/JavascriptObject.cs

+4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ public class JavascriptObject //: DynamicObject maybe later
6262

6363
public IMethodInterceptor MethodInterceptor { get; set; }
6464

65+
#if !NETCOREAPP
66+
public IPropertyInterceptor PropertyInterceptor { get; set; }
67+
#endif
68+
6569
public JavascriptObject()
6670
{
6771
Methods = new List<JavascriptMethod>();

CefSharp/Internals/JavascriptObjectRepository.cs

+24-4
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ public void Register(string name, object value, bool isAsync, BindingOptions opt
250250
jsObject.IsAsync = isAsync;
251251
jsObject.Binder = options?.Binder;
252252
jsObject.MethodInterceptor = options?.MethodInterceptor;
253+
#if !NETCOREAPP
254+
jsObject.PropertyInterceptor = options?.PropertyInterceptor;
255+
#endif
253256

254257
AnalyseObjectForBinding(jsObject, analyseMethods: true, analyseProperties: !isAsync, readPropertyValue: false);
255258
}
@@ -562,6 +565,7 @@ protected virtual async Task<TryCallMethodResult> TryCallMethodAsync(long object
562565
return new TryCallMethodResult(false, result, exception);
563566
}
564567

568+
#if !NETCOREAPP
565569
bool IJavascriptObjectRepositoryInternal.TryGetProperty(long objectId, string name, out object result, out string exception)
566570
{
567571
return TryGetProperty(objectId, name, out result, out exception);
@@ -585,8 +589,14 @@ protected virtual bool TryGetProperty(long objectId, string name, out object res
585589

586590
try
587591
{
588-
result = property.GetValue(obj.Value);
589-
592+
if (obj.PropertyInterceptor == null)
593+
{
594+
result = property.GetValue(obj.Value);
595+
}
596+
else
597+
{
598+
result = obj.PropertyInterceptor.InterceptGet(() => property.GetValue(obj.Value), property.ManagedName);
599+
}
590600
return true;
591601
}
592602
catch (Exception ex)
@@ -596,7 +606,9 @@ protected virtual bool TryGetProperty(long objectId, string name, out object res
596606

597607
return false;
598608
}
609+
#endif
599610

611+
#if !NETCOREAPP
600612
bool IJavascriptObjectRepositoryInternal.TrySetProperty(long objectId, string name, object value, out string exception)
601613
{
602614
return TrySetProperty(objectId, name, value, out exception);
@@ -618,8 +630,14 @@ protected virtual bool TrySetProperty(long objectId, string name, object value,
618630
}
619631
try
620632
{
621-
property.SetValue(obj.Value, value);
622-
633+
if (obj.PropertyInterceptor == null)
634+
{
635+
property.SetValue(obj.Value, value);
636+
}
637+
else
638+
{
639+
obj.PropertyInterceptor.InterceptSet((p) => property.SetValue(obj.Value, p), value, property.ManagedName);
640+
}
623641
return true;
624642
}
625643
catch (Exception ex)
@@ -629,6 +647,8 @@ protected virtual bool TrySetProperty(long objectId, string name, object value,
629647

630648
return false;
631649
}
650+
#endif
651+
632652

633653
/// <summary>
634654
/// Analyse the object and generate metadata which will
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace CefSharp.ModelBinding
8+
{
9+
/// <summary>
10+
/// Provides the capability intercepting get/set property calls made from javascript as part of the
11+
/// JavascriptBinding (JSB) implementation.
12+
/// </summary>
13+
public interface IPropertyInterceptor
14+
{
15+
/// <summary>
16+
/// Called before the get property is invokved. You are now responsible for evaluating
17+
/// the property and returning the result.
18+
/// </summary>
19+
/// <param name="propertyGetter">A Func that represents the property to be called</param>
20+
/// <param name="propertName">Name of the property to be called</param>
21+
/// <returns>The property result</returns>
22+
/// <example>
23+
/// <code>
24+
/// <![CDATA[
25+
/// public object IPropertyInterceptor.InterceptGet(Func<object> propertyGetter, string propertyName)
26+
/// {
27+
/// object result = propertyGetter();
28+
/// Debug.WriteLine("InterceptGet " + propertyName);
29+
/// return result;
30+
/// }
31+
/// ]]>
32+
/// </code>
33+
/// </example>
34+
object InterceptGet(Func<object> propertyGetter, string propertName);
35+
36+
/// <summary>
37+
/// Called before the set property is invokved. You are now responsible for evaluating
38+
/// the property.
39+
/// </summary>
40+
/// <param name="propertySetter">A Func that represents the property to be called</param>
41+
/// <param name="parameter">paramater to be set to property</param>
42+
/// <param name="propertName">Name of the property to be called</param>
43+
/// <example>
44+
/// <code>
45+
/// <![CDATA[
46+
/// public object IPropertyInterceptor.InterceptSet(Action<object> propertySetter, object parameter, string propertName)
47+
/// {
48+
/// Debug.WriteLine("InterceptSet " + propertName);
49+
/// propertySetter(parameter);
50+
/// }
51+
/// ]]>
52+
/// </code>
53+
/// </example>
54+
void InterceptSet(Action<Object> propertySetter, object parameter, string propertName);
55+
}
56+
}

0 commit comments

Comments
 (0)