var url = "http://anglesharp.azurewebsites.net/PostUrlencodeNormal"; var config = new Configuration { AllowRequests = true }; var html = DocumentBuilder.Html(new Uri(url), config); var form = html.Forms[0] as IHtmlFormElement; var name = form.Elements["Name"] as IHtmlInputElement; var number = form.Elements["Number"] as IHtmlInputElement; var active = form.Elements["IsActive"] as IHtmlInputElement; name.Value = "Test"; number.Value = "1"; active.IsChecked = true; form.Submit();
public interface IConfiguration { Boolean IsScripting { get; set; } Boolean IsStyling { get; set; } Boolean IsEmbedded { get; set; } CultureInfo Culture { get; set; } IEnumerable<IScriptEngine> ScriptEngines { get; } IEnumerable<IStyleEngine> StyleEngines { get; } IEnumerable<IService> Services { get; } IEnumerable<IRequester> Requesters { get; } void ReportError(ParseErrorEventArgs e); }
var config = new Configuration().WithDefaultRequester();
var config = new Configuration() .WithDefaultRequester() .WithCss() .WithScripting();
public interface IScriptEngine { String Type { get; } void Evaluate(String source, ScriptOptions options); void Evaluate(IResponse response, ScriptOptions options); }
public interface IStyleEngine { String Type { get; } IStyleSheet Parse(String source, StyleOptions options); IStyleSheet Parse(IResponse response, StyleOptions options); }
public interface ICookieService : IService { String this[String origin] { get; set; } }
public class JavaScriptEngine : IScriptEngine { readonly Engine _engine; readonly LexicalEnvironment _variable; public JavaScriptEngine() { _engine = new Engine(); _engine.SetValue("console", new ConsoleInstance(_engine)); _variable = LexicalEnvironment.NewObjectEnvironment(_engine, _engine.Global, null, false); } public String Type { get { return "text/javascript"; } } public void Evaluate(String source, ScriptOptions options) { var context = new DomNodeInstance(_engine, options.Context ?? new AnalysisWindow(options.Document)); var env = LexicalEnvironment.NewObjectEnvironment(_engine, context, _engine.ExecutionContext.LexicalEnvironment, true); _engine.EnterExecutionContext(env, _variable, context); _engine.Execute(source); _engine.LeaveExecutionContext(); } public void Evaluate(IResponse response, ScriptOptions options) { var reader = new StreamReader(response.Content, options.Encoding ?? Encoding.UTF8, true); var content = reader.ReadToEnd(); reader.Close(); Evaluate(content, options); } }
static class DomDelegates { public static Delegate ToDelegate(this Type type, FunctionInstance function) { if (type == typeof(EventListener)) return ToListener(function); var method = typeof(DomDelegates).GetMethod("ToCallback").MakeGenericMethod(type); return method.Invoke(null, new Object[] { function }) as Delegate; } public static DomEventHandler ToListener(this FunctionInstance function) { return (obj, ev) => { var engine = function.Engine; function.Call(obj.ToJsValue(engine), new[] { ev.ToJsValue(engine) }); }; } public static T ToCallback<T>(this FunctionInstance function) { var type = typeof(T); var methodInfo = type.GetMethod("Invoke"); var convert = typeof(Extensions).GetMethod("ToJsValue"); var mps = methodInfo.GetParameters(); var parameters = new ParameterExpression[mps.Length]; for (var i = 0; i < mps.Length; i++) parameters[i] = Expression.Parameter(mps[i].ParameterType, mps[i].Name); var obj = Expression.Constant(function); var engine = Expression.Property(obj, "Engine"); var call = Expression.Call(obj, "Call", new Type[0], new Expression[] { Expression.Call(convert, parameters[0], engine), Expression.NewArrayInit(typeof(JsValue), parameters.Skip(1).Select(m => Expression.Call(convert, m, engine)).ToArray()) }); return Expression.Lambda<T>(call, parameters).Compile(); } }
sealed class DomNodeInstance : ObjectInstance { readonly Object _value; public DomNodeInstance(Engine engine, Object value) : base(engine) { _value = value; SetMembers(value.GetType()); } void SetMembers(Type type) { if (type.GetCustomAttribute<DomNameAttribute>() == null) { foreach (var contract in type.GetInterfaces()) SetMembers(contract); } else { SetProperties(type.GetProperties()); SetMethods(type.GetMethods()); } } void SetProperties(PropertyInfo[] properties) { foreach (var property in properties) { var names = property.GetCustomAttributes<DomNameAttribute>(); foreach (var name in names.Select(m => m.OfficialName)) { FastSetProperty(name, new PropertyDescriptor( new DomFunctionInstance(this, property.GetMethod), new DomFunctionInstance(this, property.SetMethod), false, false)); } } } void SetMethods(MethodInfo[] methods) { foreach (var method in methods) { var names = method.GetCustomAttributes<DomNameAttribute>(); foreach (var name in names.Select(m => m.OfficialName)) FastAddProperty(name, new DomFunctionInstance(this, method), false, false, false); } } public Object Value { get { return _value; } } }
sealed class DomFunctionInstance : FunctionInstance { readonly MethodInfo _method; readonly DomNodeInstance _host; public DomFunctionInstance(DomNodeInstance host, MethodInfo method) : base(host.Engine, GetParameters(method), null, false) { _host = host; _method = method; } public override JsValue Call(JsValue thisObject, JsValue[] arguments) { if (_method != null && thisObject.Type == Types.Object) { var node = thisObject.AsObject() as DomNodeInstance; if (node != null) return _method.Invoke(node.Value, BuildArgs(arguments)).ToJsValue(Engine); } return JsValue.Undefined; } Object[] BuildArgs(JsValue[] arguments) { var parameters = _method.GetParameters(); var max = parameters.Length; var args = new Object[max]; if (max > 0 && parameters[max - 1].GetCustomAttribute<ParamArrayAttribute>() != null) max--; var n = Math.Min(arguments.Length, max); for (int i = 0; i < n; i++) args[i] = arguments[i].FromJsValue().As(parameters[i].ParameterType); for (int i = n; i < max; i++) args[i] = parameters[i].IsOptional ? parameters[i].DefaultValue : parameters[i].ParameterType.GetDefaultValue(); if (max != parameters.Length) { var array = Array.CreateInstance(parameters[max].ParameterType.GetElementType(), Math.Max(0, arguments.Length - max)); for (int i = max; i < arguments.Length; i++) array.SetValue(arguments[i].FromJsValue(), i - max); args[max] = array; } return args; } static String[] GetParameters(MethodInfo method) { if (method == null) return new String[0]; return method.GetParameters().Select(m => m.Name).ToArray(); } }
<!doctype html> <html> <head><title>Event sample</title></head> <body> <script> console.log('Before setting the handler!'); document.addEventListener('load', function() { console.log('Document loaded!'); }); document.addEventListener('hello', function() { console.log('hello world from JavaScript!'); }); console.log('After setting the handler!'); </script> </body>
public static void EventScriptingExample() { //We require a custom configuration var config = new Configuration(); //Including a script engine config.Register(new JavaScriptEngine()); //And enabling scripting + styling (should be enabled anyway) config.IsScripting = true; config.IsStyling = true; //This is our sample source, we will trigger the load event var source = @"/* Code from above */"; var document = DocumentBuilder.Html(source, config); //Register Hello event listener from C# (we also have one in JS) document.AddEventListener("hello", (s, ev) => { Console.WriteLine("hello world from C#!"); }); var e = document.CreateEvent("event"); e.Init("hello", false, false); document.Dispatch(e); }