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

XFESystem

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

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

namespace XFESystem.Browser;

public sealed partial class BrowserService
{
    private readonly Dictionary<string, BrowserCanvasSurface?> images = new(StringComparer.Ordinal);
    private readonly Dictionary<string, string> imageFormats = new(StringComparer.Ordinal);
    private long imagePixels;
    private bool executingPageScript;

    private string ImageKey(string source)
    {
        if (source.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) return source;
        return ResourceAddress(source).AbsoluteUri;
    }
    private BrowserCanvasSurface? FindImage(string source)
    {
        if (source.Length == 0) return null;
        try { return images.GetValueOrDefault(ImageKey(source)); }
        catch (Exception e) when (e is UriFormatException or NotSupportedException) { return null; }
    }
    private bool LoadImage(string source)
    {
        if (source.Length == 0) return false;
        string key;
        try { key = ImageKey(source); }
        catch (Exception error) when (error is UriFormatException or NotSupportedException)
        { Diagnostic("Image failed: " + error.Message); return false; }
        if (images.ContainsKey(key)) return false;
        if (images.Count >= activeLimits.MaximumExternalResources) return false;
        images.Add(key, null); // Failed images are not repeatedly fetched on each frame.
        try
        {
            if (scriptPolicy?.BlocksAllScripts == true) throw new NotSupportedException("The declared image policy cannot be enforced.");
            CheckOperationCancellation();
            byte[] bytes;
            if (key.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
            {
                int comma = key.IndexOf(',');
                if (comma < 0 || comma > 256 || !key.AsSpan(5, comma - 5).StartsWith("image/", StringComparison.OrdinalIgnoreCase))
                    throw new NotSupportedException("The image data URL is invalid.");
                if (key.Length - comma - 1 > activeLimits.MaximumImageBytes * 3L) throw new BrowserLimitException("The image data URL exceeds its byte limit.");
                bytes = key[..comma].EndsWith(";base64", StringComparison.OrdinalIgnoreCase)
                    ? Convert.FromBase64String(key[(comma + 1)..]) : System.Text.Encoding.UTF8.GetBytes(Uri.UnescapeDataString(key[(comma + 1)..]));
            }
            else
            {
                var response = resourceLoader!.Get(new Uri(key), "image");
                bytes = response.BinaryBody ?? throw new NotSupportedException("The transport did not supply binary image bytes.");
            }
            if (bytes.Length > activeLimits.MaximumImageBytes) throw new BrowserLimitException("The image exceeds its byte limit.");
            int remaining = (int)Math.Min(activeLimits.MaximumImagePixels, activeLimits.MaximumTotalImagePixels - imagePixels);
            var surface = BrowserImageDecoder.Decode(bytes, remaining);
            images[key] = surface; imagePixels += (long)surface.Width * surface.Height;
            imageFormats[key] = BrowserPngDecoder.HasSignature(bytes) ? "PNG" : bytes.Length >= 12 && System.Text.Encoding.ASCII.GetString(bytes, 8, 4) == "WEBP" ? "WebP" :
                bytes.Length >= 3 && bytes[0] == 255 && bytes[1] == 216 ? "JPEG" : bytes.Length >= 3 && System.Text.Encoding.ASCII.GetString(bytes, 0, 3) == "GIF" ? "GIF" : "SVG";
            Trace?.Invoke("XFE_BROWSER_IMAGE|Address|" + (key.StartsWith("data:") ? "DataUrl" : key) + "|Width|" + surface.Width + "|Height|" + surface.Height + "|Bytes|" + bytes.Length + "\n");
            InvalidateDocument(); return true;
        }
        catch (OperationCanceledException) when (OperationCancelled) { throw; }
        catch (Exception error) when (error is not OutOfMemoryException and not StackOverflowException)
        { Diagnostic("Image failed: " + error.Message); return false; }
    }
    private void LoadImage(IElement element)
    {
        string source = element.GetAttribute("src") ?? "";
        LoadImage(source);
        if (source.Length != 0) dom?.DispatchEvent(element, FindImage(source) is not null ? "load" : "error", new() { Bubbles = false, Cancelable = false });
    }
    private bool LoadDisplayImages(BrowserDisplayList list)
    {
        if (executingPageScript || runtime?.IsExecutingScript == true || dom?.IsExecutingCallback == true) return false;
        bool changed = false;
        foreach (var command in list.Commands.Where(c => c.Kind == BrowserDrawKind.Image && c.Opacity > 0 &&
            (c.Bounds.Width > 0 && c.Bounds.Height > 0 || c.Node is IElement { LocalName: "img" })).ToArray())
            changed |= LoadImage(command.ImageSource);
        return changed;
    }
}