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);
        }
예제 #2
0
        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);
        }
예제 #3
0
 /// <summary>
 /// JavascriptObjectRepository
 /// </summary>
 public JavascriptObjectRepository()
 {
     Settings      = new JavascriptBindingSettings();
     nameConverter = new LegacyCamelCaseJavascriptNameConverter();
 }
예제 #4
0
 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);
        }