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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using Jint.Native;
using Jint.Native.Object;
using Jint.Runtime.Interop;

namespace XFESystem.Browser;

public sealed partial class DomBridge
{
    private readonly Queue<(int Id, string Address, JsValue Callback, IReadOnlyDictionary<string, string> Headers)> networkRequests = [];
    private int nextNetworkRequestId;
    internal bool HasNetworkRequests => networkRequests.Count != 0;
    internal int NetworkRequestCount => networkRequests.Count;
    private void InstallNetworkCallbacks()
    {
        engine.SetValue("__xfeHttpRequest", new ClrFunction(engine, "request", (_, args) =>
        {
            if (networkRequests.Count >= limits.MaximumPendingTasks) throw new BrowserLimitException("The browser request queue is full.");
            var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            if (Value(args, 2) is JsArray fields)
            {
                if (fields.Length > 32) throw new BrowserLimitException("The script request header count limit was reached.");
                for (uint index = 0; index < fields.Length; index++)
                {
                    if (fields[index] is not JsArray pair || pair.Length != 2) throw new ArgumentException("Invalid request header pair.");
                    string name = LimitedText(pair[0]), value = LimitedText(pair[1]);
                    if (BrowserRequestHeaders.Allowed(name, value)) headers[name] = value;
                }
            }
            int id = checked(++nextNetworkRequestId);
            networkRequests.Enqueue((id, Argument(args, 0), Value(args, 1), BrowserRequestHeaders.Copy(headers)));
            return id;
        }));
        engine.SetValue("__xfeHttpCancel", new ClrFunction(engine, "cancelRequest", (_, args) =>
        {
            int id = NumberIndex(Value(args, 0)), count = networkRequests.Count;
            for (int index = 0; index < count; index++) { var request = networkRequests.Dequeue(); if (request.Id != id) networkRequests.Enqueue(request); }
            return JsValue.Undefined;
        }));
    }
    internal void ProcessNetworkRequest()
    {
        if (!networkRequests.TryDequeue(out var request)) return;
        ObjectInstance? value = null; string? error = null;
        try
        {
            var response = host.GetResourceWithHeaders?.Invoke(request.Address, request.Headers) ??
                (request.Headers.Count == 0 ? host.GetResource?.Invoke(request.Address) : null) ?? throw new NotSupportedException("No page network service for these request headers.");
            value = new BrowserHostObject(engine);
            Data(value, "status", response.StatusCode); Data(value, "url", response.Address.AbsoluteUri);
            Data(value, "text", response.Body); Data(value, "contentType", response.ContentType);
        }
        catch (Exception exception) when (exception is not OutOfMemoryException and not StackOverflowException)
        { error = exception.Message; }
        // Network I/O has completed before entering a fresh bounded callback.
        // No nested Engine.Invoke resets the budget inside an author evaluation.
        callbackDepth++;
        characterBudgetValid = false;
        try { engine.Invoke(request.Callback, engine.Global, [(JsValue?)value ?? JsValue.Null, error is null ? JsValue.Null : error]); engine.Evaluate(BrowserDomBootstrap.FlushJobs); }
        catch { callbackDepth--; throw; }
        callbackDepth--;
    }
}