Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Image constructor support #94

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/AngleSharp.Js.Tests/ScriptEvalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ public async Task CreateXmlHttpRequestShouldWork()
Assert.AreEqual("1", result);
}

[Test]
public async Task CreateImageShouldWork()
{
var result = await EvaluateComplexScriptAsync("var img = new Image(400, 200); img.src = '/image.jpg';", SetResult("img.width"));
Assert.AreEqual("400", result);
}

[Test]
public async Task PerformXmlHttpRequestSynchronousToDataUrlShouldWork()
{
Expand Down
28 changes: 28 additions & 0 deletions src/AngleSharp.Js/Attributes/DomConstructorFunctionAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace AngleSharp.Js.Attributes
{
using System;

/// <summary>
/// This attribute is used to mark a method to be uses as a
/// constructor function from scripts.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public sealed class DomConstructorFunctionAttribute : Attribute
{
/// <summary>
/// Creates a new DomConstructorFunctionAttribute.
/// </summary>
/// <param name="officialName">
/// The official name of the decorated method.
/// </param>
public DomConstructorFunctionAttribute(String officialName)
{
OfficialName = officialName;
}

/// <summary>
/// Gets the official name of the given class.
/// </summary>
public String OfficialName { get; }
}
}
35 changes: 35 additions & 0 deletions src/AngleSharp.Js/Cache/CreatorCache.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using AngleSharp.Attributes;
using AngleSharp.Js.Attributes;
using AngleSharp.Js.Proxies;
using Jint.Native.Object;
using Jint.Runtime.Descriptors;
using System;
Expand Down Expand Up @@ -40,6 +42,39 @@ public static Action<EngineInstance, ObjectInstance> GetConstructorAction(this T
return action;
}

private static readonly Dictionary<Type, Action<EngineInstance, ObjectInstance>> _constructorFunctionActions = new Dictionary<Type, Action<EngineInstance, ObjectInstance>>();

public static Action<EngineInstance, ObjectInstance> GetConstructorFunctionAction(this Type type)
{
if (!_constructorFunctionActions.TryGetValue(type, out var action))
{
var constructorFunctions = type.GetTypeInfo().GetMethods().Where(m => m.GetCustomAttributes<DomConstructorFunctionAttribute>().Any());

if (constructorFunctions.Any())
{
action = (engine, obj) =>
{
foreach (var constructorFunction in constructorFunctions)
{
var attribute = constructorFunction.GetCustomAttribute<DomConstructorFunctionAttribute>();

var constructorFunctionInstance = new DomConstructorFunctionInstance(engine, constructorFunction, attribute.OfficialName);

obj.FastSetProperty(attribute.OfficialName, new PropertyDescriptor(constructorFunctionInstance, false, true, false));
}
};
}
else
{
action = (e, o) => { };
}

_constructorFunctionActions.Add(type, action);
}

return action;
}

private static readonly Dictionary<Type, Action<EngineInstance, ObjectInstance>> _instanceActions = new Dictionary<Type, Action<EngineInstance, ObjectInstance>>();

public static Action<EngineInstance, ObjectInstance> GetInstanceAction(this Type type)
Expand Down
27 changes: 27 additions & 0 deletions src/AngleSharp.Js/Dom/WindowExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ namespace AngleSharp.Js.Dom
using AngleSharp.Browser;
using AngleSharp.Dom;
using AngleSharp.Dom.Events;
using AngleSharp.Html.Dom;
using AngleSharp.Js.Attributes;
using System;

/// <summary>
Expand Down Expand Up @@ -42,5 +44,30 @@ public static Console Console(this IWindow window)
{
return new Console(window);
}

/// <summary>
/// Creates a new IHtmlImageElement instance.
/// </summary>
/// <param name="window"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
[DomConstructorFunction("Image")]
public static IHtmlImageElement Image(this IWindow window, int? width = null, int? height = null)
{
var imageElement = window.Document.CreateElement(TagNames.Img) as IHtmlImageElement;

if (width.HasValue)
{
imageElement.DisplayWidth = width.Value;
}

if (height.HasValue)
{
imageElement.DisplayHeight = height.Value;
}

return imageElement;
}
}
}
1 change: 1 addition & 0 deletions src/AngleSharp.Js/EngineInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public EngineInstance(IWindow window, IDictionary<String, Object> assignments, I
foreach (var lib in libs)
{
this.AddConstructors(_window, lib);
this.AddConstructorFunctions(_window, lib);
this.AddInstances(_window, lib);
}

Expand Down
14 changes: 14 additions & 0 deletions src/AngleSharp.Js/Extensions/EngineExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ public static void AddConstructors(this EngineInstance engine, ObjectInstance ct
}
}

public static void AddConstructorFunctions(this EngineInstance engine, ObjectInstance ctx, Assembly assembly)
{
foreach (var exportedType in assembly.ExportedTypes)
{
engine.AddConstructorFunction(ctx, exportedType);
}
}

public static void AddInstances(this EngineInstance engine, ObjectInstance obj, Assembly assembly)
{
foreach (var exportedType in assembly.ExportedTypes)
Expand All @@ -192,6 +200,12 @@ public static void AddConstructor(this EngineInstance engine, ObjectInstance obj
apply.Invoke(engine, obj);
}

public static void AddConstructorFunction(this EngineInstance engine, ObjectInstance obj, Type type)
{
var apply = type.GetConstructorFunctionAction();
apply.Invoke(engine, obj);
}

public static void AddInstance(this EngineInstance engine, ObjectInstance obj, Type type)
{
var apply = type.GetInstanceAction();
Expand Down
11 changes: 11 additions & 0 deletions src/AngleSharp.Js/Extensions/JsValueExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ public static Object As(this JsValue value, Type targetType, EngineInstance engi
{
return TypeConverter.ToInt32(value);
}
else if (targetType == typeof(Nullable<Int32>))
{
if (value.IsUndefined())
{
return null;
}
else
{
return TypeConverter.ToInt32(value);
}
}
else if (targetType == typeof(Double))
{
return TypeConverter.ToNumber(value);
Expand Down
31 changes: 31 additions & 0 deletions src/AngleSharp.Js/Proxies/DomConstructorFunctionInstance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace AngleSharp.Js.Proxies
{
using Jint.Native;
using Jint.Native.Object;
using Jint.Runtime;
using System.Reflection;

sealed class DomConstructorFunctionInstance : Constructor
{
private readonly EngineInstance _instance;
private readonly MethodInfo _constructorFunction;

public DomConstructorFunctionInstance(EngineInstance instance, MethodInfo constructorFunction, string name) : base(instance.Jint, name)
{
_instance = instance;
_constructorFunction = constructorFunction;
}

public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
{
try
{
return _instance.Call(_constructorFunction, _instance.Window, arguments) as ObjectInstance;
}
catch
{
throw new JavaScriptException(_instance.Jint.Intrinsics.Error);
}
}
}
}
Loading