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

XFESystem

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

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

namespace XFESystem.Browser;

public sealed partial class DomBridge
{
    private JsValue CreateTreeWalker(IDocument owner, JsValue[] arguments)
    {
        var root = RequireNode(Value(arguments, 0));
        uint mask = Value(arguments, 1).IsUndefined() ? uint.MaxValue : Jint.Runtime.TypeConverter.ToUint32(Value(arguments, 1));
        var filter = Value(arguments, 2); bool filtering = false; int visits = 0;
        FilterResult Accept(INode node)
        {
            if (++visits > limits.MaximumDomNodes) throw new BrowserLimitException("TreeWalker exceeds its traversal budget.");
            if (filter.IsNull() || filter.IsUndefined()) return FilterResult.Accept;
            if (filtering) throw AdjacentError("InvalidStateError", "The TreeWalker filter is already active.", 11);
            filtering = true; JsValue result;
            try { result = CallInternal(bootstrap.Get("filterNode"), [filter, Wrap(node)]); }
            catch { filtering = false; throw; }
            filtering = false; return (FilterResult)(ushort)result.AsNumber();
        }
        var walker = owner.CreateTreeWalker(root, unchecked((FilterSettings)mask), Accept);
        INode? Previous()
        {
            // AngleSharp 1.7.1's backward loop does not advance a rejected
            // sibling or assign its parent. Use bounded DOM links here.
            INode? candidate = walker.Current; int steps = 0;
            FilterResult Test(INode node) => (mask & (1u << ((int)node.NodeType - 1))) == 0 ? FilterResult.Skip : Accept(node);
            while (candidate is not null && candidate != root)
            {
                if (++steps > limits.MaximumDomNodes) throw new BrowserLimitException("TreeWalker exceeds its traversal budget.");
                if (candidate.PreviousSibling is { } sibling)
                {
                    candidate = sibling; var decision = Test(candidate);
                    while (decision != FilterResult.Reject && candidate.LastChild is { } child)
                    {
                        if (++steps > limits.MaximumDomNodes) throw new BrowserLimitException("TreeWalker exceeds its traversal budget.");
                        candidate = child; decision = Test(candidate);
                    }
                    if (decision == FilterResult.Accept) { walker.Current = candidate; return candidate; }
                }
                else
                {
                    candidate = candidate.Parent;
                    if (candidate is not null && Test(candidate) == FilterResult.Accept) { walker.Current = candidate; return candidate; }
                }
            }
            return null;
        }
        var wrapper = new BrowserHostObject(engine);
        CallInternal(bootstrap.Get("initTreeWalker"), [wrapper]);
        Property(wrapper, "root", () => Wrap(root)); Property(wrapper, "whatToShow", () => (double)mask);
        Property(wrapper, "filter", () => filter.IsUndefined() ? JsValue.Null : filter);
        Property(wrapper, "currentNode", () => Wrap(walker.Current), value => walker.Current = RequireNode(value));
        void Method(string name, Func<INode?> action) => Function(wrapper, name, _ =>
        {
            if (filtering) throw AdjacentError("InvalidStateError", "The TreeWalker filter is already active.", 11);
            visits = 0; return Wrap(action());
        });
        Method("nextNode", walker.ToNext); Method("previousNode", Previous); Method("parentNode", walker.ToParent);
        Method("firstChild", walker.ToFirst); Method("lastChild", walker.ToLast);
        Method("nextSibling", walker.ToNextSibling); Method("previousSibling", walker.ToPreviousSibling);
        return wrapper;
    }
    private JsValue SplitTextNode(IText text, JsValue value)
    {
        uint offset = Jint.Runtime.TypeConverter.ToUint32(value); string old = text.TextContent;
        if (offset > old.Length) throw AdjacentError("IndexSizeError", "The text offset is outside its UTF-16 data.", 1);
        // Splitting conserves all characters and adds exactly one node. Admit
        // that node before mutation so rejected budgets leave ranges untouched.
        ValidateBudget([text.Owner!.CreateTextNode("")]);
        var result = text.Split((int)offset);
        Changed(text, "characterData", oldValue: old);
        if (text.Parent is { } parent) Changed(parent, "childList", added: [result]);
        return Wrap(result);
    }
}