private JavascriptObject CreateJavascriptObject(bool rootObject, IJavascriptNameConverter nameConveter) { var id = Interlocked.Increment(ref lastId); var result = new JavascriptObject { Id = id, RootObject = rootObject, NameConverter = nameConveter }; objects[id] = result; return(result); }
private static JavascriptMethod CreateJavaScriptMethod(MethodInfo methodInfo, IJavascriptNameConverter nameConverter) { var jsMethod = new JavascriptMethod(); jsMethod.ManagedName = methodInfo.Name; jsMethod.JavascriptName = nameConverter == null ? methodInfo.Name : nameConverter.ConvertToJavascript(methodInfo); jsMethod.Function = methodInfo.Invoke; jsMethod.ReturnType = methodInfo.ReturnType; jsMethod.ParameterCount = methodInfo.GetParameters().Length; jsMethod.Parameters = methodInfo.GetParameters() .Select(t => new MethodParameter() { IsParamArray = t.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0, Type = t.ParameterType }).ToList(); //Pre compute HasParamArray for a very minor performance gain jsMethod.HasParamArray = jsMethod.Parameters.LastOrDefault(t => t.IsParamArray) != null; return(jsMethod); }
/// <summary> /// JavascriptObjectRepository /// </summary> public JavascriptObjectRepository() { Settings = new JavascriptBindingSettings(); nameConverter = new LegacyCamelCaseJavascriptNameConverter(); }
public DefaultBinder(IJavascriptNameConverter javascriptNameConverter = null) { this.javascriptNameConverter = javascriptNameConverter; }
private static JavascriptProperty CreateJavaScriptProperty(PropertyInfo propertyInfo, IJavascriptNameConverter nameConverter) { var jsProperty = new JavascriptProperty(); jsProperty.ManagedName = propertyInfo.Name; jsProperty.JavascriptName = nameConverter == null ? propertyInfo.Name : nameConverter.ConvertToJavascript(propertyInfo.Name); jsProperty.SetValue = (o, v) => propertyInfo.SetValue(o, v, null); jsProperty.GetValue = (o) => propertyInfo.GetValue(o, null); jsProperty.IsComplexType = IsComplexType(propertyInfo.PropertyType); jsProperty.IsReadOnly = !propertyInfo.CanWrite; return(jsProperty); }
/// <summary> /// Analyse the object and generate metadata which will /// be used by the browser subprocess to interact with Cef. /// Method is called recursively /// </summary> /// <param name="obj">Javascript object</param> /// <param name="analyseMethods">Analyse methods for inclusion in metadata model</param> /// <param name="analyseProperties">Analyse properties for inclusion in metadata model</param> /// <param name="readPropertyValue">When analysis is done on a property, if true then get it's value for transmission over WCF</param> /// <param name="nameConverter">convert names of properties/methods</param> private void AnalyseObjectForBinding(JavascriptObject obj, bool analyseMethods, bool analyseProperties, bool readPropertyValue, IJavascriptNameConverter nameConverter) { if (obj.Value == null) { return; } var type = obj.Value.GetType(); if (type.IsPrimitive || type == typeof(string)) { return; } if (analyseMethods) { foreach (var methodInfo in type.GetMethods(BindingFlags.Instance | BindingFlags.Public).Where(p => !p.IsSpecialName)) { // Type objects can not be serialized. if (methodInfo.ReturnType == typeof(Type) || Attribute.IsDefined(methodInfo, typeof(JavascriptIgnoreAttribute))) { continue; } var jsMethod = CreateJavaScriptMethod(methodInfo, nameConverter); obj.Methods.Add(jsMethod); } } if (analyseProperties) { foreach (var propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => !p.IsSpecialName)) { //https://msdn.microsoft.com/en-us/library/system.reflection.propertyinfo.getindexparameters(v=vs.110).aspx //An array of type ParameterInfo containing the parameters for the indexes. If the property is not indexed, the array has 0 (zero) elements. //According to MSDN array has zero elements when it's not an indexer, so in theory no null check is required var isIndexer = propertyInfo.GetIndexParameters().Length > 0; var hasIgnoreAttribute = Attribute.IsDefined(propertyInfo, typeof(JavascriptIgnoreAttribute)); if (propertyInfo.PropertyType == typeof(Type) || isIndexer || hasIgnoreAttribute) { continue; } var jsProperty = CreateJavaScriptProperty(propertyInfo, nameConverter); if (jsProperty.IsComplexType) { var jsObject = CreateJavascriptObject(rootObject: false, nameConveter: nameConverter); jsObject.Name = propertyInfo.Name; jsObject.JavascriptName = nameConverter == null ? propertyInfo.Name : nameConverter.ConvertToJavascript(propertyInfo.Name); jsObject.Value = jsProperty.GetValue(obj.Value); jsProperty.JsObject = jsObject; AnalyseObjectForBinding(jsProperty.JsObject, analyseMethods, analyseProperties: true, readPropertyValue: readPropertyValue, nameConverter: nameConverter); } else if (readPropertyValue) { jsProperty.PropertyValue = jsProperty.GetValue(obj.Value); } obj.Properties.Add(jsProperty); } } }
public void Register(string name, object value, bool isAsync, BindingOptions options) { if (name == null) { throw new ArgumentNullException("name"); } if (value == null) { throw new ArgumentNullException("value"); } //Enable WCF if not already enabled - can only be done before the browser has been initliazed //if done after the subprocess won't be WCF enabled it we'll have to throw an exception if (!IsBrowserInitialized && !isAsync) { CefSharpSettings.WcfEnabled = true; } if (!CefSharpSettings.WcfEnabled && !isAsync) { throw new InvalidOperationException(@"To enable synchronous JS bindings set WcfEnabled true in CefSharpSettings before you create your ChromiumWebBrowser instances."); } //Validation name is unique if (objects.Values.Count(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)) > 0) { throw new ArgumentException("Object already bound with name:" + name, name); } //Binding of System types is problematic, so we don't support it var type = value.GetType(); if (type.IsPrimitive || type.BaseType.Namespace.StartsWith("System.")) { throw new ArgumentException("Registering of .Net framework built in types is not supported, " + "create your own Object and proxy the calls if you need to access a Window/Form/Control.", "value"); } IJavascriptNameConverter nameConverter = null; //If a custom name converter was provided we'll use that //as a preference if (options != null && options.NameConverter != null) { nameConverter = options.NameConverter; } else if (options == null || options.CamelCaseJavascriptNames) { nameConverter = new CamelCaseJavascriptNameConverter(); } var jsObject = CreateJavascriptObject(rootObject: true, nameConveter: nameConverter); jsObject.Value = value; jsObject.Name = name; jsObject.JavascriptName = name; jsObject.IsAsync = isAsync; jsObject.Binder = options?.Binder; jsObject.MethodInterceptor = options?.MethodInterceptor; AnalyseObjectForBinding(jsObject, analyseMethods: true, analyseProperties: !isAsync, readPropertyValue: false, nameConverter: nameConverter); }