using System.Diagnostics;
using System.Security.Cryptography;
using System.Text.Json;
using AngleSharp.Dom;
using AngleSharp.Html.Parser;
using Jint.Runtime.Interop;
using XFESystem.Browser;
var root = Path.GetFullPath(args.Length > 0 ? args[0] : ".");
var snapshot = Path.GetFullPath(args.Length > 1 ? args[1] : Path.Combine(root,"build/BrowserProbe/CurrentDom"));
var assets = Path.GetFullPath(Path.Combine(root, "assets/Browser/Monaco/0.52.2"));
var output = Path.Combine(root, "build/BrowserProbe", "CurrentDom-" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
Directory.CreateDirectory(output);
var document = new HtmlParser().ParseDocument("<!doctype html><html><head></head><body><div id='container' style='width:800px;height:600px'></div></body></html>");
var limits = new BrowserLimits {
MaximumScriptCharacters = 8_388_608, MaximumScriptMemoryBytes = 536_870_912,
MaximumStatements = 100_000_000, MaximumRecursionDepth = 256, ScriptTimeout = TimeSpan.FromSeconds(60),
MaximumOutputCharacters = 1_048_576, MaximumDomNodes = 100_000
};
var runtime = new JavaScriptRuntime(limits);
runtime.Engine.SetValue("__xfeTimerType", new ClrFunction(runtime.Engine,"timerType",(_,values)=>values[0].GetType().FullName ?? "unknown"));
var resources = new Queue<IElement>();
var resourceLog = new List<object>();
var stages = new List<object>();
var sheets = new List<BrowserStyleSheet>();
int mutations = 0;
var bridge = new DomBridge(runtime, document, new Uri("http://xfe.test/Monaco/Index.html"), limits, new() {
ResourceRequested = node => resources.Enqueue(node), Mutated = () => mutations++,
Viewport = () => new(800,600)
});
bool Step(string name, string source)
{
var stopwatch = Stopwatch.StartNew();
var result = runtime.Evaluate(source);
stages.Add(new { Name=name, result.Success, result.Error, result.Value, result.ConsoleOutput, Milliseconds=stopwatch.Elapsed.TotalMilliseconds });
Console.WriteLine(JsonSerializer.Serialize(new { Name=name, result.Success, result.Error, result.Value, result.ConsoleOutput }));
return result.Success;
}
void PumpResources()
{
for (int count=0; resources.Count>0 && count<128; count++)
{
var element=resources.Dequeue();
var relative=element.GetAttribute(element.LocalName=="link"?"href":"src") ?? "";
var address=new Uri(new Uri("http://xfe.test/Monaco/Index.html"),relative);
if(address.GetLeftPart(UriPartial.Authority)!="http://xfe.test" || !address.AbsolutePath.StartsWith("/Monaco/min/",StringComparison.Ordinal))
throw new InvalidOperationException("Only pinned offline Monaco resource URLs are allowed: "+address);
var file=Path.GetFullPath(Path.Combine(assets,address.AbsolutePath["/Monaco/".Length..].Replace('/',Path.DirectorySeparatorChar)));
if(!file.StartsWith(assets+Path.DirectorySeparatorChar,StringComparison.OrdinalIgnoreCase))throw new InvalidOperationException("Asset traversal rejected");
var source=File.ReadAllText(file);
bool success=true;string error="";
if(element.LocalName=="script")
{
try { bridge.CurrentScript=element;if(source.Length>limits.MaximumScriptCharacters)throw new InvalidOperationException("Resource script budget exceeded");runtime.Engine.Execute(source); }
catch(Exception exception) {success=false;error=exception.ToString();Console.WriteLine("TIMER_CALLS "+runtime.Engine.Evaluate("JSON.stringify(globalThis.__xfeTimerCalls)").ToString());}
finally {bridge.CurrentScript=null;}
}
else sheets.Add(new(source,element,address.AbsoluteUri));
resourceLog.Add(new { Address=address.AbsoluteUri, Kind=element.LocalName, Characters=source.Length,
Sha256=Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(file))), Success=success, Error=error });
Console.WriteLine(JsonSerializer.Serialize(resourceLog[^1]));
bridge.DispatchEvent(element,success?"load":"error",new(){Bubbles=false,Cancelable=false});
if(!success)break;
}
}
try
{
Step("RealLoader",File.ReadAllText(Path.Combine(assets,"min/vs/loader.js")));
Step("DiagnosticTimerTrace", "globalThis.__xfeTimerCalls=[];const __xfeNativeTimeout=setTimeout;globalThis.setTimeout=function(callback,...args){__xfeTimerCalls.push({type:typeof callback,hostType:__xfeTimerType(callback),text:String(callback).slice(0,100)});return __xfeNativeTimeout(callback,...args);};");
Step("RequireUnmodifiedEditorWithRealCss", "require.config({paths:{vs:'http://xfe.test/Monaco/min/vs'}});require(['vs/editor/editor.main'],function(){globalThis.__xfeLoaded=true;},function(error){globalThis.__xfeLoadError=String(error);});");
PumpResources();
Step("EntryResolved", "if(!globalThis.__xfeLoaded)throw new Error(globalThis.__xfeLoadError||'AMD entry not resolved');typeof monaco.editor.create;");
if(Step("CreateRealEditor", "globalThis.__xfeEditor=monaco.editor.create(document.getElementById('container'),{value:'const answer = 42;',language:'plaintext',automaticLayout:false,minimap:{enabled:false},dimension:{width:800,height:600},accessibilitySupport:'off'});"))
{
Step("ValueRoundTrip", "if(__xfeEditor.getValue()!=='const answer = 42;')throw new Error('Initial model mismatch');__xfeEditor.setValue('const changed = 43;');if(__xfeEditor.getValue()!=='const changed = 43;')throw new Error('setValue/getValue mismatch');__xfeEditor.getValue();");
Step("EditingAction", "__xfeEditor.setPosition({lineNumber:1,column:1});__xfeEditor.trigger('keyboard','type',{text:'// '});if(!__xfeEditor.getValue().startsWith('// '))throw new Error('Editor typing action did not change model');__xfeEditor.getValue();");
PumpResources();
}
}
catch(Exception error)
{
stages.Add(new {Name="ProbeHostFailure",Success=false,Error=error.ToString()});
Console.WriteLine(error);
}
File.WriteAllText(Path.Combine(output,"Document.html"),document.DocumentElement?.OuterHtml ?? "");
var receipt=new {
Kind="RealMonacoCurrentDomProbe",MonacoVersion="0.52.2",JintVersion="4.11.0",ProbeOnly=true,
NativeGuestTested=false,HostBrowserUsed=false,FakeMonacoObjects=false,CssPluginDisabled=false,
CssResourcesRead=sheets.Count,NativeCssLayoutConnected=false,DomMutations=mutations,DomElements=document.QuerySelectorAll("*").Length,
SnapshotPath=snapshot,Snapshot=Directory.GetFiles(snapshot,"*.cs").Select(p=>new{Name=Path.GetFileName(p),Sha256=Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(p)))}).ToArray(),
Resources=resourceLog,Stages=stages
};
var receiptPath=Path.Combine(output,"Result.json");
File.WriteAllText(receiptPath,JsonSerializer.Serialize(receipt,new JsonSerializerOptions{WriteIndented=true}));
Console.WriteLine(receiptPath);
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text.Json;
using AngleSharp.Dom;
using AngleSharp.Html.Parser;
using Jint.Runtime.Interop;
using XFESystem.Browser;
var root = Path.GetFullPath(args.Length > 0 ? args[0] : ".");
var snapshot = Path.GetFullPath(args.Length > 1 ? args[1] : Path.Combine(root,"build/BrowserProbe/CurrentDom"));
var assets = Path.GetFullPath(Path.Combine(root, "assets/Browser/Monaco/0.52.2"));
var output = Path.Combine(root, "build/BrowserProbe", "CurrentDom-" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
Directory.CreateDirectory(output);
var document = new HtmlParser().ParseDocument("<!doctype html><html><head></head><body><div id='container' style='width:800px;height:600px'></div></body></html>");
var limits = new BrowserLimits {
MaximumScriptCharacters = 8_388_608, MaximumScriptMemoryBytes = 536_870_912,
MaximumStatements = 100_000_000, MaximumRecursionDepth = 256, ScriptTimeout = TimeSpan.FromSeconds(60),
MaximumOutputCharacters = 1_048_576, MaximumDomNodes = 100_000
};
var runtime = new JavaScriptRuntime(limits);
runtime.Engine.SetValue("__xfeTimerType", new ClrFunction(runtime.Engine,"timerType",(_,values)=>values[0].GetType().FullName ?? "unknown"));
var resources = new Queue<IElement>();
var resourceLog = new List<object>();
var stages = new List<object>();
var sheets = new List<BrowserStyleSheet>();
int mutations = 0;
var bridge = new DomBridge(runtime, document, new Uri("http://xfe.test/Monaco/Index.html"), limits, new() {
ResourceRequested = node => resources.Enqueue(node), Mutated = () => mutations++,
Viewport = () => new(800,600)
});
bool Step(string name, string source)
{
var stopwatch = Stopwatch.StartNew();
var result = runtime.Evaluate(source);
stages.Add(new { Name=name, result.Success, result.Error, result.Value, result.ConsoleOutput, Milliseconds=stopwatch.Elapsed.TotalMilliseconds });
Console.WriteLine(JsonSerializer.Serialize(new { Name=name, result.Success, result.Error, result.Value, result.ConsoleOutput }));
return result.Success;
}
void PumpResources()
{
for (int count=0; resources.Count>0 && count<128; count++)
{
var element=resources.Dequeue();
var relative=element.GetAttribute(element.LocalName=="link"?"href":"src") ?? "";
var address=new Uri(new Uri("http://xfe.test/Monaco/Index.html"),relative);
if(address.GetLeftPart(UriPartial.Authority)!="http://xfe.test" || !address.AbsolutePath.StartsWith("/Monaco/min/",StringComparison.Ordinal))
throw new InvalidOperationException("Only pinned offline Monaco resource URLs are allowed: "+address);
var file=Path.GetFullPath(Path.Combine(assets,address.AbsolutePath["/Monaco/".Length..].Replace('/',Path.DirectorySeparatorChar)));
if(!file.StartsWith(assets+Path.DirectorySeparatorChar,StringComparison.OrdinalIgnoreCase))throw new InvalidOperationException("Asset traversal rejected");
var source=File.ReadAllText(file);
bool success=true;string error="";
if(element.LocalName=="script")
{
try { bridge.CurrentScript=element;if(source.Length>limits.MaximumScriptCharacters)throw new InvalidOperationException("Resource script budget exceeded");runtime.Engine.Execute(source); }
catch(Exception exception) {success=false;error=exception.ToString();Console.WriteLine("TIMER_CALLS "+runtime.Engine.Evaluate("JSON.stringify(globalThis.__xfeTimerCalls)").ToString());}
finally {bridge.CurrentScript=null;}
}
else sheets.Add(new(source,element,address.AbsoluteUri));
resourceLog.Add(new { Address=address.AbsoluteUri, Kind=element.LocalName, Characters=source.Length,
Sha256=Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(file))), Success=success, Error=error });
Console.WriteLine(JsonSerializer.Serialize(resourceLog[^1]));
bridge.DispatchEvent(element,success?"load":"error",new(){Bubbles=false,Cancelable=false});
if(!success)break;
}
}
try
{
Step("RealLoader",File.ReadAllText(Path.Combine(assets,"min/vs/loader.js")));
Step("DiagnosticTimerTrace", "globalThis.__xfeTimerCalls=[];const __xfeNativeTimeout=setTimeout;globalThis.setTimeout=function(callback,...args){__xfeTimerCalls.push({type:typeof callback,hostType:__xfeTimerType(callback),text:String(callback).slice(0,100)});return __xfeNativeTimeout(callback,...args);};");
Step("RequireUnmodifiedEditorWithRealCss", "require.config({paths:{vs:'http://xfe.test/Monaco/min/vs'}});require(['vs/editor/editor.main'],function(){globalThis.__xfeLoaded=true;},function(error){globalThis.__xfeLoadError=String(error);});");
PumpResources();
Step("EntryResolved", "if(!globalThis.__xfeLoaded)throw new Error(globalThis.__xfeLoadError||'AMD entry not resolved');typeof monaco.editor.create;");
if(Step("CreateRealEditor", "globalThis.__xfeEditor=monaco.editor.create(document.getElementById('container'),{value:'const answer = 42;',language:'plaintext',automaticLayout:false,minimap:{enabled:false},dimension:{width:800,height:600},accessibilitySupport:'off'});"))
{
Step("ValueRoundTrip", "if(__xfeEditor.getValue()!=='const answer = 42;')throw new Error('Initial model mismatch');__xfeEditor.setValue('const changed = 43;');if(__xfeEditor.getValue()!=='const changed = 43;')throw new Error('setValue/getValue mismatch');__xfeEditor.getValue();");
Step("EditingAction", "__xfeEditor.setPosition({lineNumber:1,column:1});__xfeEditor.trigger('keyboard','type',{text:'// '});if(!__xfeEditor.getValue().startsWith('// '))throw new Error('Editor typing action did not change model');__xfeEditor.getValue();");
PumpResources();
}
}
catch(Exception error)
{
stages.Add(new {Name="ProbeHostFailure",Success=false,Error=error.ToString()});
Console.WriteLine(error);
}
File.WriteAllText(Path.Combine(output,"Document.html"),document.DocumentElement?.OuterHtml ?? "");
var receipt=new {
Kind="RealMonacoCurrentDomProbe",MonacoVersion="0.52.2",JintVersion="4.11.0",ProbeOnly=true,
NativeGuestTested=false,HostBrowserUsed=false,FakeMonacoObjects=false,CssPluginDisabled=false,
CssResourcesRead=sheets.Count,NativeCssLayoutConnected=false,DomMutations=mutations,DomElements=document.QuerySelectorAll("*").Length,
SnapshotPath=snapshot,Snapshot=Directory.GetFiles(snapshot,"*.cs").Select(p=>new{Name=Path.GetFileName(p),Sha256=Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(p)))}).ToArray(),
Resources=resourceLog,Stages=stages
};
var receiptPath=Path.Combine(output,"Result.json");
File.WriteAllText(receiptPath,JsonSerializer.Serialize(receipt,new JsonSerializerOptions{WriteIndented=true}));
Console.WriteLine(receiptPath);