diff --git a/CefSharp.Example/CefSharp.Example.netcore.csproj b/CefSharp.Example/CefSharp.Example.netcore.csproj index ef7f06eef5..fa2ddc79fd 100644 --- a/CefSharp.Example/CefSharp.Example.netcore.csproj +++ b/CefSharp.Example/CefSharp.Example.netcore.csproj @@ -87,6 +87,10 @@ + + + + \ No newline at end of file diff --git a/CefSharp.Example/ModelBinding/PropertyInterceptorLogger.cs b/CefSharp.Example/ModelBinding/PropertyInterceptorLogger.cs new file mode 100644 index 0000000000..73ff5f2dda --- /dev/null +++ b/CefSharp.Example/ModelBinding/PropertyInterceptorLogger.cs @@ -0,0 +1,26 @@ +// Copyright © 2022 The CefSharp Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +using System; +using System.Diagnostics; +using CefSharp.ModelBinding; + +namespace CefSharp.Example.ModelBinding +{ + public class PropertyInterceptorLogger : IPropertyInterceptor + { + object IPropertyInterceptor.InterceptGet(Func propertyGetter, string propertyName) + { + object result = propertyGetter(); + Debug.WriteLine("InterceptGet " + propertyName); + return result; + } + + void IPropertyInterceptor.InterceptSet(Action propertySetter, object parameter, string propertName) + { + Debug.WriteLine("InterceptSet " + propertName); + propertySetter(parameter); + } + } +} diff --git a/CefSharp.Test/JavascriptBinding/JavaScriptObjectRepositoryFacts.cs b/CefSharp.Test/JavascriptBinding/JavaScriptObjectRepositoryFacts.cs index c366aac27c..6695ebb36c 100644 --- a/CefSharp.Test/JavascriptBinding/JavaScriptObjectRepositoryFacts.cs +++ b/CefSharp.Test/JavascriptBinding/JavaScriptObjectRepositoryFacts.cs @@ -2,7 +2,9 @@ // // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. +using CefSharp.Example.ModelBinding; using CefSharp.Internals; +using System; using System.Collections.Generic; using Xunit; @@ -46,5 +48,36 @@ public void CanRegisterJavascriptObjectBindWhenNamespaceIsNull() Assert.True(result.Success); Assert.Equal("ok", result.ReturnValue.ToString()); } + +#if !NETCOREAPP + [Fact] + public void CanRegisterJavascriptObjectPropertyBindWhenNamespaceIsNull() + { + IJavascriptObjectRepositoryInternal javascriptObjectRepository = new JavascriptObjectRepository(); + var name = nameof(NoNamespaceClass); + + BindingOptions bindingOptions = new BindingOptions() + { + Binder = BindingOptions.DefaultBinder.Binder, + PropertyInterceptor = new PropertyInterceptorLogger() + }; + javascriptObjectRepository.Register(name, new NoNamespaceClass(), false, bindingOptions); + Assert.True(javascriptObjectRepository.IsBound(name)); + + var boundObjects = javascriptObjectRepository.GetObjects(new List { name }); + Assert.Single(boundObjects); + + object getResult, setResult = 100; + string exception; + NoNamespaceClass noNamespaceClass = new NoNamespaceClass(); + bool retValue = javascriptObjectRepository.TrySetProperty(boundObjects[0].Id, "year", setResult, out exception); + Assert.True(retValue); + + retValue = javascriptObjectRepository.TryGetProperty(boundObjects[0].Id, "year", out getResult, out exception); + Assert.True(retValue); + Assert.Equal(100, Convert.ToInt32(getResult)); + } +#endif } + } diff --git a/CefSharp.Wpf.Example/Views/BrowserTabView.xaml.cs b/CefSharp.Wpf.Example/Views/BrowserTabView.xaml.cs index d213923246..341d321502 100644 --- a/CefSharp.Wpf.Example/Views/BrowserTabView.xaml.cs +++ b/CefSharp.Wpf.Example/Views/BrowserTabView.xaml.cs @@ -46,7 +46,10 @@ public BrowserTabView() var bindingOptions = new BindingOptions() { Binder = BindingOptions.DefaultBinder.Binder, - MethodInterceptor = new MethodInterceptorLogger() // intercept .net methods calls from js and log it + MethodInterceptor = new MethodInterceptorLogger(), // intercept .net methods calls from js and log it +#if !NETCOREAPP + PropertyInterceptor = new PropertyInterceptorLogger() +#endif }; //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() { if (e.ObjectName == "bound") { - repo.Register("bound", new BoundObject(), isAsync: false, options: BindingOptions.DefaultBinder); + repo.Register("bound", new BoundObject(), isAsync: false, options: bindingOptions); } else if (e.ObjectName == "boundAsync") { diff --git a/CefSharp/BindingOptions.cs b/CefSharp/BindingOptions.cs index 5ea892b1cc..65c2cf7bc9 100644 --- a/CefSharp/BindingOptions.cs +++ b/CefSharp/BindingOptions.cs @@ -29,5 +29,14 @@ public static BindingOptions DefaultBinder /// for logging calls (from js) to .net methods. /// public IMethodInterceptor MethodInterceptor { get; set; } + +#if !NETCOREAPP + /// + /// Interceptor used for intercepting get/set calls to the target object property. For instance, can be used + /// for logging calls to .net property (from js) + /// + public IPropertyInterceptor PropertyInterceptor { get; set; } +#endif + } } diff --git a/CefSharp/CefSharp.netcore.csproj b/CefSharp/CefSharp.netcore.csproj index d118d6140f..87e6884813 100644 --- a/CefSharp/CefSharp.netcore.csproj +++ b/CefSharp/CefSharp.netcore.csproj @@ -45,6 +45,7 @@ + diff --git a/CefSharp/Internals/IJavascriptObjectRepositoryInternal.cs b/CefSharp/Internals/IJavascriptObjectRepositoryInternal.cs index da9091a966..1069ebf52e 100644 --- a/CefSharp/Internals/IJavascriptObjectRepositoryInternal.cs +++ b/CefSharp/Internals/IJavascriptObjectRepositoryInternal.cs @@ -14,8 +14,10 @@ public interface IJavascriptObjectRepositoryInternal : IJavascriptObjectReposito { TryCallMethodResult TryCallMethod(long objectId, string name, object[] parameters); Task TryCallMethodAsync(long objectId, string name, object[] parameters); +#if !NETCOREAPP bool TryGetProperty(long objectId, string name, out object result, out string exception); bool TrySetProperty(long objectId, string name, object value, out string exception); +#endif bool IsBrowserInitialized { get; set; } List GetObjects(List names = null); List GetLegacyBoundObjects(); diff --git a/CefSharp/Internals/JavascriptObject.cs b/CefSharp/Internals/JavascriptObject.cs index 1876ae5f2a..61fb410078 100644 --- a/CefSharp/Internals/JavascriptObject.cs +++ b/CefSharp/Internals/JavascriptObject.cs @@ -62,6 +62,10 @@ public class JavascriptObject //: DynamicObject maybe later public IMethodInterceptor MethodInterceptor { get; set; } +#if !NETCOREAPP + public IPropertyInterceptor PropertyInterceptor { get; set; } +#endif + public JavascriptObject() { Methods = new List(); diff --git a/CefSharp/Internals/JavascriptObjectRepository.cs b/CefSharp/Internals/JavascriptObjectRepository.cs index 345ae8771b..b9aa11ec9b 100644 --- a/CefSharp/Internals/JavascriptObjectRepository.cs +++ b/CefSharp/Internals/JavascriptObjectRepository.cs @@ -250,6 +250,9 @@ public void Register(string name, object value, bool isAsync, BindingOptions opt jsObject.IsAsync = isAsync; jsObject.Binder = options?.Binder; jsObject.MethodInterceptor = options?.MethodInterceptor; +#if !NETCOREAPP + jsObject.PropertyInterceptor = options?.PropertyInterceptor; +#endif AnalyseObjectForBinding(jsObject, analyseMethods: true, analyseProperties: !isAsync, readPropertyValue: false); } @@ -562,6 +565,7 @@ protected virtual async Task TryCallMethodAsync(long object return new TryCallMethodResult(false, result, exception); } +#if !NETCOREAPP bool IJavascriptObjectRepositoryInternal.TryGetProperty(long objectId, string name, out object result, out string exception) { return TryGetProperty(objectId, name, out result, out exception); @@ -585,8 +589,14 @@ protected virtual bool TryGetProperty(long objectId, string name, out object res try { - result = property.GetValue(obj.Value); - + if (obj.PropertyInterceptor == null) + { + result = property.GetValue(obj.Value); + } + else + { + result = obj.PropertyInterceptor.InterceptGet(() => property.GetValue(obj.Value), property.ManagedName); + } return true; } catch (Exception ex) @@ -596,7 +606,9 @@ protected virtual bool TryGetProperty(long objectId, string name, out object res return false; } +#endif +#if !NETCOREAPP bool IJavascriptObjectRepositoryInternal.TrySetProperty(long objectId, string name, object value, out string exception) { return TrySetProperty(objectId, name, value, out exception); @@ -618,8 +630,14 @@ protected virtual bool TrySetProperty(long objectId, string name, object value, } try { - property.SetValue(obj.Value, value); - + if (obj.PropertyInterceptor == null) + { + property.SetValue(obj.Value, value); + } + else + { + obj.PropertyInterceptor.InterceptSet((p) => property.SetValue(obj.Value, p), value, property.ManagedName); + } return true; } catch (Exception ex) @@ -629,6 +647,8 @@ protected virtual bool TrySetProperty(long objectId, string name, object value, return false; } +#endif + /// /// Analyse the object and generate metadata which will diff --git a/CefSharp/ModelBinding/IPropertyInterceptor.cs b/CefSharp/ModelBinding/IPropertyInterceptor.cs new file mode 100644 index 0000000000..4e13461b75 --- /dev/null +++ b/CefSharp/ModelBinding/IPropertyInterceptor.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CefSharp.ModelBinding +{ + /// + /// Provides the capability intercepting get/set property calls made from javascript as part of the + /// JavascriptBinding (JSB) implementation. + /// + public interface IPropertyInterceptor + { + /// + /// Called before the get property is invokved. You are now responsible for evaluating + /// the property and returning the result. + /// + /// A Func that represents the property to be called + /// Name of the property to be called + /// The property result + /// + /// + /// propertyGetter, string propertyName) + /// { + /// object result = propertyGetter(); + /// Debug.WriteLine("InterceptGet " + propertyName); + /// return result; + /// } + /// ]]> + /// + /// + object InterceptGet(Func propertyGetter, string propertName); + + /// + /// Called before the set property is invokved. You are now responsible for evaluating + /// the property. + /// + /// A Func that represents the property to be called + /// paramater to be set to property + /// Name of the property to be called + /// + /// + /// propertySetter, object parameter, string propertName) + /// { + /// Debug.WriteLine("InterceptSet " + propertName); + /// propertySetter(parameter); + /// } + /// ]]> + /// + /// + void InterceptSet(Action propertySetter, object parameter, string propertName); + } +}