Skip to content

Commit

Permalink
Merge pull request #94 from tomvanenckevort/image-constructor-support
Browse files Browse the repository at this point in the history
Added Image constructor support
  • Loading branch information
FlorianRappl authored Mar 25, 2024
2 parents 4ecbd42 + f019708 commit e8de221
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 0 deletions.
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);
}
}
}
}

0 comments on commit e8de221

Please sign in to comment.