XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using AngleSharp.Css.Dom;
using AngleSharp.Css.Parser;
using Jint;
using Jint.Native;
using Jint.Native.Object;

namespace XFESystem.Browser;

public sealed partial class DomBridge
{
    private readonly Dictionary<int, (ICssStyleSheet Sheet, int Characters)> constructedSheets = [];
    private readonly CssParser constructedCssParser = new(new CssParserOptions { IsIncludingUnknownDeclarations = true, IsIncludingUnknownRules = true });
    private int nextSheetId, constructedCssCharacters;
    private void InstallCssomCallbacks()
    {
        var callbacks = new BrowserHostObject(engine);
        Function(callbacks, "create", _ =>
        {
            if (constructedSheets.Count >= limits.MaximumStyleSheets) throw new BrowserLimitException("The constructed stylesheet count limit was reached.");
            var id = checked(++nextSheetId); constructedSheets.Add(id, (constructedCssParser.ParseStyleSheet(string.Empty), 0)); return id;
        });
        Function(callbacks, "rules", args =>
        {
            var sheet = Sheet(Value(args, 0));
            return ListValues(sheet.Sheet.Rules.Select(rule => (JsValue)rule.CssText));
        });
        Function(callbacks, "replace", args =>
        {
            var id = SheetId(Value(args, 0)); var previous = constructedSheets[id]; var text = LimitedText(Value(args, 1));
            CheckCssCharacters(text.Length - previous.Characters);
            var replacement = constructedCssParser.ParseStyleSheet(text); CheckCssRules(replacement);
            constructedSheets[id] = (replacement, text.Length); constructedCssCharacters += text.Length - previous.Characters; return JsValue.Undefined;
        });
        Function(callbacks, "insert", args =>
        {
            var id = SheetId(Value(args, 0)); var previous = constructedSheets[id]; var text = LimitedText(Value(args, 1));
            var index = CssIndex(Value(args, 2), previous.Sheet.Rules.Length, allowEnd: true);
            var parsed = constructedCssParser.ParseStyleSheet(text);
            if (parsed.Rules.Length != 1) throw AdjacentError("SyntaxError", "insertRule requires exactly one CSS rule.", 12);
            CheckCssCharacters(text.Length);
            if (previous.Sheet.Rules.Length >= 4096) throw new BrowserLimitException("The CSSOM rule limit was reached.");
            try { previous.Sheet.Insert(text, index); }
            catch (Exception error) when (error is not OutOfMemoryException and not StackOverflowException)
            { throw AdjacentError("SyntaxError", error.Message, 12); }
            constructedSheets[id] = (previous.Sheet, previous.Characters + text.Length); constructedCssCharacters += text.Length; return index;
        });
        Function(callbacks, "delete", args =>
        {
            var id = SheetId(Value(args, 0)); var previous = constructedSheets[id];
            var index = CssIndex(Value(args, 1), previous.Sheet.Rules.Length, allowEnd: false);
            previous.Sheet.RemoveAt(index);
            // Serialize after deletion so the retained budget reflects actual
            // remaining content rather than a potentially different input form.
            var characters = previous.Sheet.Rules.Sum(rule => rule.CssText.Length);
            constructedSheets[id] = (previous.Sheet, characters); constructedCssCharacters += characters - previous.Characters;
            return JsValue.Undefined;
        });
        engine.SetValue("__xfeCssom", callbacks);
    }
    private int SheetId(JsValue value)
    {
        if (!value.IsNumber() || value.AsNumber() != Math.Truncate(value.AsNumber()) || value.AsNumber() > int.MaxValue ||
            value.AsNumber() <= 0 || !constructedSheets.ContainsKey((int)value.AsNumber())) throw new ArgumentException("Expected this document's constructed stylesheet.");
        return (int)value.AsNumber();
    }
    private (ICssStyleSheet Sheet, int Characters) Sheet(JsValue value) => constructedSheets[SheetId(value)];
    private void CheckCssCharacters(int difference)
    { if (difference > limits.MaximumStyleCharacters - constructedCssCharacters) throw new BrowserLimitException("The constructed CSS character limit was reached."); }
    private static void CheckCssRules(ICssStyleSheet sheet)
    { if (sheet.Rules.Length > 4096) throw new BrowserLimitException("The CSSOM rule limit was reached."); }
    private Jint.Runtime.JavaScriptException CssIndexError() => AdjacentError("IndexSizeError", "The CSS rule index is outside the stylesheet.", 1);
    private int CssIndex(JsValue value, int count, bool allowEnd)
    {
        var number = value.IsUndefined() ? 0 : Number(value);
        if (!double.IsFinite(number) || number < 0 || number > uint.MaxValue) throw CssIndexError();
        var index = (long)Math.Truncate(number);
        if (index > count || !allowEnd && index == count) throw CssIndexError();
        return (int)index;
    }
}