using System.Globalization;
using System.Text;
using AngleSharp.Dom;
namespace XFESystem.Browser;
public sealed partial class BrowserService
{
private string? remoteEvidenceAddress, remoteBodySha256;
private bool remoteCertificateValidated;
private void RecordRemoteResponseEvidence(string? address, string? bodyHash, bool certificateValidated)
{ remoteEvidenceAddress = address; remoteBodySha256 = bodyHash; remoteCertificateValidated = certificateValidated; }
/// <summary>Read actual DOM, loaded resources and existing layout commands on the owning worker.
/// No script, resource load or screenshot enters this readback.</summary>
public string ReadRemotePageProof(long snapshotSequence = 0) => WithGate(() =>
{
if (document is null || displayList is null || currentAddress?.Scheme is not ("http" or "https") ||
remoteEvidenceAddress != currentAddress.AbsoluteUri || string.IsNullOrEmpty(remoteBodySha256)) return "{}";
var visible = new BrowserRect(0, 0, viewport.Width, viewport.Height);
var json = new StringBuilder("{\"Kind\":\"ActualRemotePageDomReadback\",\"Address\":").Append(JavaScriptString(currentAddress.AbsoluteUri))
.Append(",\"Title\":").Append(JavaScriptString(document.Title ?? "")).Append(",\"BodySha256\":").Append(JavaScriptString(remoteBodySha256))
.Append(",\"CertificateValidated\":").Append(remoteCertificateValidated ? "true" : "false")
.Append(",\"SnapshotSequence\":").Append(snapshotSequence)
.Append(",\"DocumentGeneration\":").Append(DocumentGeneration)
.Append(",\"ViewportWidth\":").Append(Number(viewport.Width)).Append(",\"ViewportHeight\":").Append(Number(viewport.Height))
.Append(",\"ScrollY\":").Append(Number(viewport.ScrollY)).Append(",\"TextRuns\":[");
bool comma = false; int count = 0;
foreach (var command in displayList.Commands)
{
if (command.Kind != BrowserDrawKind.Text || command.Opacity <= 0 || string.IsNullOrWhiteSpace(command.Text)) continue;
var bounds = command.ViewportPinned ? command.Bounds : command.Bounds.Translate(-viewport.ScrollX, -viewport.ScrollY);
if (bounds.Intersect(visible) is not { Width: > 0, Height: > 0 }) continue;
if (comma) json.Append(','); comma = true;
json.Append("{\"Text\":").Append(JavaScriptString(command.Text.Length > 256 ? command.Text[..256] : command.Text))
.Append(",\"Bounds\":").Append(Rect(bounds)).Append(",\"Color\":").Append(JavaScriptString("#" + command.Color.ToString("X6"))).Append('}');
if (++count == 64) break;
}
json.Append("],\"SearchInputs\":["); comma = false;
foreach (var element in document.QuerySelectorAll("input[name],textarea[name]").Take(16))
{
if (comma) json.Append(','); comma = true;
json.Append("{\"Name\":").Append(JavaScriptString(element.GetAttribute("name") ?? ""))
.Append(",\"Bounds\":").Append(Rect(displayList.GetBoundingClientRect(element))).Append('}');
}
json.Append("],\"LoadedStyles\":["); comma = false;
foreach (var receipt in LoadedResources.Where(item => item.Kind == "style").Take(128))
{
if (comma) json.Append(','); comma = true;
json.Append("{\"Address\":").Append(JavaScriptString(receipt.Address))
.Append(",\"FinalAddress\":").Append(JavaScriptString(receipt.Address))
.Append(",\"RequestedAddress\":").Append(JavaScriptString(receipt.RequestedAddress ?? receipt.Address))
.Append(",\"Characters\":").Append(receipt.Characters).Append('}');
}
json.Append("],\"DeclaredStyleLinks\":["); comma = false;
foreach (var link in document.QuerySelectorAll("link[rel~=stylesheet][href]").Take(128))
{
if (!Uri.TryCreate(currentAddress, link.GetAttribute("href"), out var uri)) continue;
if (comma) json.Append(','); comma = true; json.Append(JavaScriptString(uri.AbsoluteUri));
}
json.Append("],\"InlineStyleCount\":").Append(document.QuerySelectorAll("style").Length)
.Append(",\"DeclaredCsp\":").Append(cspDeclared ? "true" : "false")
.Append(",\"AuthorStylesBlockedByCsp\":").Append(cspBlocksAuthorStyles ? "true" : "false")
.Append(",\"BaseHrefCount\":").Append(document.QuerySelectorAll("base[href]").Length)
.Append(",\"Diagnostics\":").Append(JavaScriptString(pageDiagnostics?.ToString() ?? ""))
.Append(",\"ReadOnly\":true,\"NoHostScreenshotSubstitution\":true}");
return json.ToString();
});
private static string Number(double value) => double.IsFinite(value) ? value.ToString("0.###", CultureInfo.InvariantCulture) : "0";
private static string Rect(BrowserRect value) => "{\"X\":" + Number(value.X) + ",\"Y\":" + Number(value.Y) +
",\"Width\":" + Number(value.Width) + ",\"Height\":" + Number(value.Height) + "}";
}
using System.Globalization;
using System.Text;
using AngleSharp.Dom;
namespace XFESystem.Browser;
public sealed partial class BrowserService
{
private string? remoteEvidenceAddress, remoteBodySha256;
private bool remoteCertificateValidated;
private void RecordRemoteResponseEvidence(string? address, string? bodyHash, bool certificateValidated)
{ remoteEvidenceAddress = address; remoteBodySha256 = bodyHash; remoteCertificateValidated = certificateValidated; }
/// <summary>Read actual DOM, loaded resources and existing layout commands on the owning worker.
/// No script, resource load or screenshot enters this readback.</summary>
public string ReadRemotePageProof(long snapshotSequence = 0) => WithGate(() =>
{
if (document is null || displayList is null || currentAddress?.Scheme is not ("http" or "https") ||
remoteEvidenceAddress != currentAddress.AbsoluteUri || string.IsNullOrEmpty(remoteBodySha256)) return "{}";
var visible = new BrowserRect(0, 0, viewport.Width, viewport.Height);
var json = new StringBuilder("{\"Kind\":\"ActualRemotePageDomReadback\",\"Address\":").Append(JavaScriptString(currentAddress.AbsoluteUri))
.Append(",\"Title\":").Append(JavaScriptString(document.Title ?? "")).Append(",\"BodySha256\":").Append(JavaScriptString(remoteBodySha256))
.Append(",\"CertificateValidated\":").Append(remoteCertificateValidated ? "true" : "false")
.Append(",\"SnapshotSequence\":").Append(snapshotSequence)
.Append(",\"DocumentGeneration\":").Append(DocumentGeneration)
.Append(",\"ViewportWidth\":").Append(Number(viewport.Width)).Append(",\"ViewportHeight\":").Append(Number(viewport.Height))
.Append(",\"ScrollY\":").Append(Number(viewport.ScrollY)).Append(",\"TextRuns\":[");
bool comma = false; int count = 0;
foreach (var command in displayList.Commands)
{
if (command.Kind != BrowserDrawKind.Text || command.Opacity <= 0 || string.IsNullOrWhiteSpace(command.Text)) continue;
var bounds = command.ViewportPinned ? command.Bounds : command.Bounds.Translate(-viewport.ScrollX, -viewport.ScrollY);
if (bounds.Intersect(visible) is not { Width: > 0, Height: > 0 }) continue;
if (comma) json.Append(','); comma = true;
json.Append("{\"Text\":").Append(JavaScriptString(command.Text.Length > 256 ? command.Text[..256] : command.Text))
.Append(",\"Bounds\":").Append(Rect(bounds)).Append(",\"Color\":").Append(JavaScriptString("#" + command.Color.ToString("X6"))).Append('}');
if (++count == 64) break;
}
json.Append("],\"SearchInputs\":["); comma = false;
foreach (var element in document.QuerySelectorAll("input[name],textarea[name]").Take(16))
{
if (comma) json.Append(','); comma = true;
json.Append("{\"Name\":").Append(JavaScriptString(element.GetAttribute("name") ?? ""))
.Append(",\"Bounds\":").Append(Rect(displayList.GetBoundingClientRect(element))).Append('}');
}
json.Append("],\"LoadedStyles\":["); comma = false;
foreach (var receipt in LoadedResources.Where(item => item.Kind == "style").Take(128))
{
if (comma) json.Append(','); comma = true;
json.Append("{\"Address\":").Append(JavaScriptString(receipt.Address))
.Append(",\"FinalAddress\":").Append(JavaScriptString(receipt.Address))
.Append(",\"RequestedAddress\":").Append(JavaScriptString(receipt.RequestedAddress ?? receipt.Address))
.Append(",\"Characters\":").Append(receipt.Characters).Append('}');
}
json.Append("],\"DeclaredStyleLinks\":["); comma = false;
foreach (var link in document.QuerySelectorAll("link[rel~=stylesheet][href]").Take(128))
{
if (!Uri.TryCreate(currentAddress, link.GetAttribute("href"), out var uri)) continue;
if (comma) json.Append(','); comma = true; json.Append(JavaScriptString(uri.AbsoluteUri));
}
json.Append("],\"InlineStyleCount\":").Append(document.QuerySelectorAll("style").Length)
.Append(",\"DeclaredCsp\":").Append(cspDeclared ? "true" : "false")
.Append(",\"AuthorStylesBlockedByCsp\":").Append(cspBlocksAuthorStyles ? "true" : "false")
.Append(",\"BaseHrefCount\":").Append(document.QuerySelectorAll("base[href]").Length)
.Append(",\"Diagnostics\":").Append(JavaScriptString(pageDiagnostics?.ToString() ?? ""))
.Append(",\"ReadOnly\":true,\"NoHostScreenshotSubstitution\":true}");
return json.ToString();
});
private static string Number(double value) => double.IsFinite(value) ? value.ToString("0.###", CultureInfo.InvariantCulture) : "0";
private static string Rect(BrowserRect value) => "{\"X\":" + Number(value.X) + ",\"Y\":" + Number(value.Y) +
",\"Width\":" + Number(value.Width) + ",\"Height\":" + Number(value.Height) + "}";
}