Skip to content

Commit e8de221

Browse files
authored
Merge pull request #94 from tomvanenckevort/image-constructor-support
Added Image constructor support
2 parents 4ecbd42 + f019708 commit e8de221

8 files changed

+154
-0
lines changed

src/AngleSharp.Js.Tests/ScriptEvalTests.cs

+7
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ public async Task CreateXmlHttpRequestShouldWork()
7575
Assert.AreEqual("1", result);
7676
}
7777

78+
[Test]
79+
public async Task CreateImageShouldWork()
80+
{
81+
var result = await EvaluateComplexScriptAsync("var img = new Image(400, 200); img.src = '/image.jpg';", SetResult("img.width"));
82+
Assert.AreEqual("400", result);
83+
}
84+
7885
[Test]
7986
public async Task PerformXmlHttpRequestSynchronousToDataUrlShouldWork()
8087
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace AngleSharp.Js.Attributes
2+
{
3+
using System;
4+
5+
/// <summary>
6+
/// This attribute is used to mark a method to be uses as a
7+
/// constructor function from scripts.
8+
/// </summary>
9+
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
10+
public sealed class DomConstructorFunctionAttribute : Attribute
11+
{
12+
/// <summary>
13+
/// Creates a new DomConstructorFunctionAttribute.
14+
/// </summary>
15+
/// <param name="officialName">
16+
/// The official name of the decorated method.
17+
/// </param>
18+
public DomConstructorFunctionAttribute(String officialName)
19+
{
20+
OfficialName = officialName;
21+
}
22+
23+
/// <summary>
24+
/// Gets the official name of the given class.
25+
/// </summary>
26+
public String OfficialName { get; }
27+
}
28+
}

src/AngleSharp.Js/Cache/CreatorCache.cs

+35
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using AngleSharp.Attributes;
2+
using AngleSharp.Js.Attributes;
3+
using AngleSharp.Js.Proxies;
24
using Jint.Native.Object;
35
using Jint.Runtime.Descriptors;
46
using System;
@@ -40,6 +42,39 @@ public static Action<EngineInstance, ObjectInstance> GetConstructorAction(this T
4042
return action;
4143
}
4244

45+
private static readonly Dictionary<Type, Action<EngineInstance, ObjectInstance>> _constructorFunctionActions = new Dictionary<Type, Action<EngineInstance, ObjectInstance>>();
46+
47+
public static Action<EngineInstance, ObjectInstance> GetConstructorFunctionAction(this Type type)
48+
{
49+
if (!_constructorFunctionActions.TryGetValue(type, out var action))
50+
{
51+
var constructorFunctions = type.GetTypeInfo().GetMethods().Where(m => m.GetCustomAttributes<DomConstructorFunctionAttribute>().Any());
52+
53+
if (constructorFunctions.Any())
54+
{
55+
action = (engine, obj) =>
56+
{
57+
foreach (var constructorFunction in constructorFunctions)
58+
{
59+
var attribute = constructorFunction.GetCustomAttribute<DomConstructorFunctionAttribute>();
60+
61+
var constructorFunctionInstance = new DomConstructorFunctionInstance(engine, constructorFunction, attribute.OfficialName);
62+
63+
obj.FastSetProperty(attribute.OfficialName, new PropertyDescriptor(constructorFunctionInstance, false, true, false));
64+
}
65+
};
66+
}
67+
else
68+
{
69+
action = (e, o) => { };
70+
}
71+
72+
_constructorFunctionActions.Add(type, action);
73+
}
74+
75+
return action;
76+
}
77+
4378
private static readonly Dictionary<Type, Action<EngineInstance, ObjectInstance>> _instanceActions = new Dictionary<Type, Action<EngineInstance, ObjectInstance>>();
4479

4580
public static Action<EngineInstance, ObjectInstance> GetInstanceAction(this Type type)

src/AngleSharp.Js/Dom/WindowExtensions.cs

+27
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ namespace AngleSharp.Js.Dom
44
using AngleSharp.Browser;
55
using AngleSharp.Dom;
66
using AngleSharp.Dom.Events;
7+
using AngleSharp.Html.Dom;
8+
using AngleSharp.Js.Attributes;
79
using System;
810

911
/// <summary>
@@ -42,5 +44,30 @@ public static Console Console(this IWindow window)
4244
{
4345
return new Console(window);
4446
}
47+
48+
/// <summary>
49+
/// Creates a new IHtmlImageElement instance.
50+
/// </summary>
51+
/// <param name="window"></param>
52+
/// <param name="width"></param>
53+
/// <param name="height"></param>
54+
/// <returns></returns>
55+
[DomConstructorFunction("Image")]
56+
public static IHtmlImageElement Image(this IWindow window, int? width = null, int? height = null)
57+
{
58+
var imageElement = window.Document.CreateElement(TagNames.Img) as IHtmlImageElement;
59+
60+
if (width.HasValue)
61+
{
62+
imageElement.DisplayWidth = width.Value;
63+
}
64+
65+
if (height.HasValue)
66+
{
67+
imageElement.DisplayHeight = height.Value;
68+
}
69+
70+
return imageElement;
71+
}
4572
}
4673
}

src/AngleSharp.Js/EngineInstance.cs

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public EngineInstance(IWindow window, IDictionary<String, Object> assignments, I
4747
foreach (var lib in libs)
4848
{
4949
this.AddConstructors(_window, lib);
50+
this.AddConstructorFunctions(_window, lib);
5051
this.AddInstances(_window, lib);
5152
}
5253

src/AngleSharp.Js/Extensions/EngineExtensions.cs

+14
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ public static void AddConstructors(this EngineInstance engine, ObjectInstance ct
178178
}
179179
}
180180

181+
public static void AddConstructorFunctions(this EngineInstance engine, ObjectInstance ctx, Assembly assembly)
182+
{
183+
foreach (var exportedType in assembly.ExportedTypes)
184+
{
185+
engine.AddConstructorFunction(ctx, exportedType);
186+
}
187+
}
188+
181189
public static void AddInstances(this EngineInstance engine, ObjectInstance obj, Assembly assembly)
182190
{
183191
foreach (var exportedType in assembly.ExportedTypes)
@@ -192,6 +200,12 @@ public static void AddConstructor(this EngineInstance engine, ObjectInstance obj
192200
apply.Invoke(engine, obj);
193201
}
194202

203+
public static void AddConstructorFunction(this EngineInstance engine, ObjectInstance obj, Type type)
204+
{
205+
var apply = type.GetConstructorFunctionAction();
206+
apply.Invoke(engine, obj);
207+
}
208+
195209
public static void AddInstance(this EngineInstance engine, ObjectInstance obj, Type type)
196210
{
197211
var apply = type.GetInstanceAction();

src/AngleSharp.Js/Extensions/JsValueExtensions.cs

+11
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ public static Object As(this JsValue value, Type targetType, EngineInstance engi
7373
{
7474
return TypeConverter.ToInt32(value);
7575
}
76+
else if (targetType == typeof(Nullable<Int32>))
77+
{
78+
if (value.IsUndefined())
79+
{
80+
return null;
81+
}
82+
else
83+
{
84+
return TypeConverter.ToInt32(value);
85+
}
86+
}
7687
else if (targetType == typeof(Double))
7788
{
7889
return TypeConverter.ToNumber(value);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace AngleSharp.Js.Proxies
2+
{
3+
using Jint.Native;
4+
using Jint.Native.Object;
5+
using Jint.Runtime;
6+
using System.Reflection;
7+
8+
sealed class DomConstructorFunctionInstance : Constructor
9+
{
10+
private readonly EngineInstance _instance;
11+
private readonly MethodInfo _constructorFunction;
12+
13+
public DomConstructorFunctionInstance(EngineInstance instance, MethodInfo constructorFunction, string name) : base(instance.Jint, name)
14+
{
15+
_instance = instance;
16+
_constructorFunction = constructorFunction;
17+
}
18+
19+
public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
20+
{
21+
try
22+
{
23+
return _instance.Call(_constructorFunction, _instance.Window, arguments) as ObjectInstance;
24+
}
25+
catch
26+
{
27+
throw new JavaScriptException(_instance.Jint.Intrinsics.Error);
28+
}
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)