using System.Globalization;
using System.Text;
using Jint;
using Jint.Native;
using Jint.Runtime.Interop;
namespace XFESystem.Browser;
public sealed partial class DomBridge
{
private sealed class DecoderState(Decoder decoder, bool ignoreBom, string name) { public Decoder Decoder = decoder; public bool IgnoreBom = ignoreBom; public string Name = name; public bool First = true; }
private readonly Dictionary<int, DecoderState> decoders = [];
private int nextDecoderId;
private void InstallEncodingCallbacks()
{
var encoding = new BrowserHostObject(engine);
Function(encoding, "encode", args =>
{
var text = Value(args, 0).IsUndefined() ? string.Empty : Argument(args, 0);
var bytes = Encoding.UTF8.GetBytes(text);
if (bytes.LongLength > limits.MaximumScriptMemoryBytes / 2) throw new BrowserLimitException("UTF-8 output exceeds its byte budget.");
var array = CallInternal(bootstrap.Get("byteArray"), [bytes.Length]).AsObject();
for (var index = 0; index < bytes.Length; ++index) array.Set(index.ToString(CultureInfo.InvariantCulture), (int)bytes[index], array);
return array;
});
Function(encoding, "decoder", args =>
{
if (decoders.Count >= limits.MaximumPendingTasks) throw new BrowserLimitException("The UTF-8 decoder limit was reached.");
var name = Argument(args, 2); bool fatal = Bool(Value(args, 0));
Encoding codec = name switch
{
"utf-8" => new UTF8Encoding(false, fatal),
"utf-16le" => new UnicodeEncoding(false, false, fatal),
"utf-16be" => new UnicodeEncoding(true, false, fatal),
_ => throw new ArgumentException("Unsupported text decoding label.")
};
var id = ++nextDecoderId; decoders.Add(id, new(codec.GetDecoder(), Bool(Value(args, 1)), name)); return id;
});
Function(encoding, "decode", args =>
{
if (!decoders.TryGetValue(NumberIndex(Value(args, 0)), out var state)) throw new ArgumentException("Unknown text decoder.");
var view = CallInternal(bootstrap.Get("bytesOf"), [Value(args, 1)]).AsObject(); var length = NumberIndex(view.Get("length"));
if (length > limits.MaximumHtmlCharacters * 4L || length > limits.MaximumScriptMemoryBytes / 2) throw new BrowserLimitException("UTF-8 input exceeds its byte budget.");
var bytes = new byte[length]; for (var index = 0; index < length; ++index) bytes[index] = (byte)Math.Clamp(Number(view.Get(index.ToString(CultureInfo.InvariantCulture))), 0, 255);
bool flush = !Bool(Value(args, 2)); var characters = new char[length + 2];
int count;
try { count = state.Decoder.GetChars(bytes, 0, bytes.Length, characters, 0, flush); }
catch (DecoderFallbackException) { state.Decoder.Reset(); state.First = true; throw new ArgumentException("The encoded data is not valid " + state.Name + "."); }
var result = new string(characters, 0, count);
if (state.First && result.Length > 0) { state.First = false; if (!state.IgnoreBom && result[0] == '\uFEFF') result = result[1..]; }
if (flush) { state.Decoder.Reset(); state.First = true; }
if (result.Length > limits.MaximumHtmlCharacters) throw new BrowserLimitException("Decoded text exceeds its character budget.");
return result;
});
engine.SetValue("__xfeEncoding", encoding);
}
}
using System.Globalization;
using System.Text;
using Jint;
using Jint.Native;
using Jint.Runtime.Interop;
namespace XFESystem.Browser;
public sealed partial class DomBridge
{
private sealed class DecoderState(Decoder decoder, bool ignoreBom, string name) { public Decoder Decoder = decoder; public bool IgnoreBom = ignoreBom; public string Name = name; public bool First = true; }
private readonly Dictionary<int, DecoderState> decoders = [];
private int nextDecoderId;
private void InstallEncodingCallbacks()
{
var encoding = new BrowserHostObject(engine);
Function(encoding, "encode", args =>
{
var text = Value(args, 0).IsUndefined() ? string.Empty : Argument(args, 0);
var bytes = Encoding.UTF8.GetBytes(text);
if (bytes.LongLength > limits.MaximumScriptMemoryBytes / 2) throw new BrowserLimitException("UTF-8 output exceeds its byte budget.");
var array = CallInternal(bootstrap.Get("byteArray"), [bytes.Length]).AsObject();
for (var index = 0; index < bytes.Length; ++index) array.Set(index.ToString(CultureInfo.InvariantCulture), (int)bytes[index], array);
return array;
});
Function(encoding, "decoder", args =>
{
if (decoders.Count >= limits.MaximumPendingTasks) throw new BrowserLimitException("The UTF-8 decoder limit was reached.");
var name = Argument(args, 2); bool fatal = Bool(Value(args, 0));
Encoding codec = name switch
{
"utf-8" => new UTF8Encoding(false, fatal),
"utf-16le" => new UnicodeEncoding(false, false, fatal),
"utf-16be" => new UnicodeEncoding(true, false, fatal),
_ => throw new ArgumentException("Unsupported text decoding label.")
};
var id = ++nextDecoderId; decoders.Add(id, new(codec.GetDecoder(), Bool(Value(args, 1)), name)); return id;
});
Function(encoding, "decode", args =>
{
if (!decoders.TryGetValue(NumberIndex(Value(args, 0)), out var state)) throw new ArgumentException("Unknown text decoder.");
var view = CallInternal(bootstrap.Get("bytesOf"), [Value(args, 1)]).AsObject(); var length = NumberIndex(view.Get("length"));
if (length > limits.MaximumHtmlCharacters * 4L || length > limits.MaximumScriptMemoryBytes / 2) throw new BrowserLimitException("UTF-8 input exceeds its byte budget.");
var bytes = new byte[length]; for (var index = 0; index < length; ++index) bytes[index] = (byte)Math.Clamp(Number(view.Get(index.ToString(CultureInfo.InvariantCulture))), 0, 255);
bool flush = !Bool(Value(args, 2)); var characters = new char[length + 2];
int count;
try { count = state.Decoder.GetChars(bytes, 0, bytes.Length, characters, 0, flush); }
catch (DecoderFallbackException) { state.Decoder.Reset(); state.First = true; throw new ArgumentException("The encoded data is not valid " + state.Name + "."); }
var result = new string(characters, 0, count);
if (state.First && result.Length > 0) { state.First = false; if (!state.IgnoreBom && result[0] == '\uFEFF') result = result[1..]; }
if (flush) { state.Decoder.Reset(); state.First = true; }
if (result.Length > limits.MaximumHtmlCharacters) throw new BrowserLimitException("Decoded text exceeds its character budget.");
return result;
});
engine.SetValue("__xfeEncoding", encoding);
}
}