using System.Diagnostics; using System.Security.Cryptography; using System.Text.Json; using XFESystem.Browser; using XFESystem.Desktop; var root = Path.GetFullPath(args.Length>0 ? args[0] : "."); bool coreBundle = args.Skip(1).Contains("--core",StringComparer.Ordinal); var output = Path.Combine(root,"build/BrowserProbe",(coreBundle?"ProductionCore-":"Production-")+DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); Directory.CreateDirectory(output); var sourceFiles = Directory.GetFiles(Path.Combine(root,"Source/XFESystem.Browser"),"*.cs") .ToDictionary(path=>path,path=>Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(path)))); var stopwatch=Stopwatch.StartNew(); using var canvas=new WindowsTextCanvas(Path.Combine(root,"assets/fonts/cascadia-mono-2407.24/CascadiaMono-Regular.ttf")); var fontFixture=canvas.RasterizeWebText("M i=42",13,canvas.FontFamilyName,400); if(!fontFixture.Alpha.Any(alpha=>alpha!=0))throw new InvalidOperationException("Real private-TTF rasterization produced no ink"); var browser=coreBundle?new BrowserService(resources:new CoreResources(root)):new BrowserService(); browser.SetCanvas(canvas);browser.ResizeViewport(800,600); var page=browser.Navigate(coreBundle?PackagedBrowserResources.MonacoPageAddress:"about:Monaco"); bool actualCoreBundle=browser.LoadedResources.Any(resource=>resource.Address==PackagedBrowserResources.Origin+"/Browser/Monaco/0.52.2/Core/EditorCore.js"); var expectedAssets = new Dictionary { [PackagedBrowserResources.MonacoCoreRoot+"EditorCore.js"]="assets/Browser/Monaco/0.52.2/Core/EditorCore.js", [PackagedBrowserResources.MonacoCoreRoot+"EditorCore.css"]="assets/Browser/Monaco/0.52.2/Core/EditorCore.css", [PackagedBrowserResources.WebsiteCodeRoot+"Highlight.js"]="assets/Browser/WebsiteCode/Highlight.js", [PackagedBrowserResources.WebsiteCodeRoot+"CSharp.js"]="assets/Browser/WebsiteCode/CSharp.js", [PackagedBrowserResources.WebsiteCodeRoot+"CodePalette.js"]="assets/Browser/WebsiteCode/CodePalette.js", [PackagedBrowserResources.WebsiteCodeRoot+"WebsiteMonaco.js"]="assets/Browser/WebsiteCode/WebsiteMonaco.js" }; bool exactCoreResources=browser.LoadedResources.Count==expectedAssets.Count && browser.LoadedResources.All(resource=>expectedAssets.TryGetValue(resource.Address,out var path) && resource.Characters==File.ReadAllText(Path.Combine(root,path)).Length); var stages=new List(); bool Check(string name,string source) { var timer=Stopwatch.StartNew(); var text=browser.ExecuteJavaScript("(function(){"+source+"})();"); bool passed=text=="PASS"; stages.Add(new(name,passed,text,timer.Elapsed.TotalMilliseconds)); Console.WriteLine(JsonSerializer.Serialize(stages[^1])); return passed; } Check("RealMonacoGlobal","if(typeof monaco!=='object'||typeof monaco.editor.create!=='function')throw new Error('Official Monaco did not initialize');return 'PASS';"); if(actualCoreBundle)Check("WorkerLanguageServicesExcludedFromCore",(exactCoreResources?"":"throw new Error('Production resource graph does not match the pinned core and website code assets');")+"if(monaco.languages.typescript!==undefined)throw new Error('TypeScript worker language-service module unexpectedly exists in the core API');return 'PASS';"); else Check("WorkerLanguageServicesExplicitlyDisabled","const configuration=monaco.languages.typescript.javascriptDefaults.modeConfiguration;if(!configuration||Object.values(configuration).some(value=>value!==false))throw new Error('Worker-backed language services were not explicitly disabled: '+JSON.stringify(configuration));return 'PASS';"); bool created=Check("RealEditorCreated","if(typeof xfeMonacoEditor==='undefined'||!xfeMonacoEditor.getDomNode())throw new Error('Real editor has not been created; status='+document.getElementById('Status').textContent);return 'PASS';"); if(!created)Check("ExplicitRealCreateDiagnostic","try{globalThis.__xfeDiagnosticEditor=monaco.editor.create(document.getElementById('Editor'),{value:'const answer = 42;',language:'javascript',dimension:{width:800,height:600},automaticLayout:true,minimap:{enabled:false},theme:'vs-dark',fontSize:13,lineHeight:20,fontFamily:'Cascadia Mono',accessibilitySupport:'off'});return 'PASS';}catch(error){return String(error)+'\\n'+error.stack;}"); for(int frame=1;frame<=5;frame++)browser.AdvanceTime(frame*17); if(created) { Check("RealInitialViewLines","xfeMonacoEditor.render(true);const lines=document.querySelectorAll('.view-lines .view-line');if(lines.length<2||!Array.from(lines).some(line=>line.textContent.includes('const')))throw new Error('The real Monaco view has no initial source lines');return 'PASS';"); browser.Paint(canvas,new DesktopBounds(0,0,800,600),0);canvas.SavePng(Path.Combine(output,"MonacoInitial.png")); Check("GetValueSetValue","if(!xfeMonacoEditor.getValue().includes('const answer'))throw new Error('Initial model mismatch');xfeMonacoEditor.setValue('const changed = 43;');if(xfeMonacoEditor.getValue()!=='const changed = 43;')throw new Error('Model round trip failed');return 'PASS';"); Check("RealEditingAction","xfeMonacoEditor.focus();xfeMonacoEditor.setPosition({lineNumber:1,column:1});xfeMonacoEditor.trigger('keyboard','type',{text:'// '});if(!xfeMonacoEditor.getValue().startsWith('// '))throw new Error('Real type action failed; actual='+xfeMonacoEditor.getValue());return 'PASS';"); Check("FocusRealTextArea","xfeMonacoEditor.focus();if(!document.activeElement||document.activeElement.tagName!=='TEXTAREA')throw new Error('Editor did not focus its real textarea');globalThis.__xfeKeyEvents=0;globalThis.__xfeInputEvents=0;globalThis.__xfeBeforeInputEvents=0;globalThis.__xfeModelEvents=0;globalThis.__xfeBeforeKeyboardValue=xfeMonacoEditor.getValue();const position=xfeMonacoEditor.getPosition();globalThis.__xfeInsertionOffset=xfeMonacoEditor.getModel().getOffsetAt(position);document.activeElement.addEventListener('keydown',function(){__xfeKeyEvents++;});document.activeElement.addEventListener('beforeinput',function(){__xfeBeforeInputEvents++;});document.activeElement.addEventListener('input',function(){__xfeInputEvents++;});xfeMonacoEditor.onDidChangeModelContent(function(){__xfeModelEvents++;});return 'PASS';"); browser.Key(new BrowserKeyboardInput("keydown","x","KeyX","x")); browser.Key(new BrowserKeyboardInput("keyup","x","KeyX")); Check("KeyboardAndInputEdit","if(__xfeKeyEvents!==1||__xfeBeforeInputEvents!==1||__xfeInputEvents!==1)throw new Error('Actual keyboard/beforeinput/input events not delivered exactly once: '+[__xfeKeyEvents,__xfeBeforeInputEvents,__xfeInputEvents]);const expected=__xfeBeforeKeyboardValue.slice(0,__xfeInsertionOffset)+'x'+__xfeBeforeKeyboardValue.slice(__xfeInsertionOffset);if(xfeMonacoEditor.getValue()!==expected||__xfeModelEvents<1)throw new Error('Actual input did not edit model through Monaco');return 'PASS';"); } for(int frame=6;frame<=10;frame++)browser.AdvanceTime(frame*17); if(created)Check("RealJavaScriptTokenization","const tokens=monaco.editor.tokenize('const answer = 42;','javascript')[0];if(!tokens.some(token=>/keyword|number|identifier/.test(token.type)))throw new Error('Real JavaScript grammar did not tokenize source: '+JSON.stringify(tokens));return 'PASS';"); BrowserDisplayList? layout=null; try {layout=browser.Layout(new BrowserViewport(800,600));} catch(Exception error){stages.Add(new("ProductionCssLayout",false,error.Message,0));} if(created && layout is not null) { var line=layout.Boxes.FirstOrDefault(box=>box.Element?.ClassList.Contains("view-line")==true); if(line is null)stages.Add(new("ActualPointerEditPosition",false,"No laid-out view line exists",0)); else{ var rect=line.BorderBox; Check("InstallPointerObservation","globalThis.__xfeMouseDown=0;globalThis.__xfePositionBeforeClick=xfeMonacoEditor.getPosition().column;xfeMonacoEditor.getDomNode().addEventListener('mousedown',function(){__xfeMouseDown++;},true);return 'PASS';"); browser.Pointer(new BrowserPointerInput("mousedown",rect.X+20,rect.Y+Math.Min(10,Math.Max(1,rect.Height/2)),0,1)); browser.Pointer(new BrowserPointerInput("mouseup",rect.X+20,rect.Y+Math.Min(10,Math.Max(1,rect.Height/2)),0,0)); Check("ActualPointerEditPosition","if(__xfeMouseDown!==1)throw new Error('Actual laid-out pointer hit did not reach editor exactly once');if(xfeMonacoEditor.getPosition().column===__xfePositionBeforeClick)throw new Error('Monaco mouse handling did not move the text position');return 'PASS';"); } } browser.Paint(canvas,new DesktopBounds(0,0,800,600),0);canvas.SavePng(Path.Combine(output,"ProductionCanvas.png")); if(created) { Check("RealOfficialCSharpRegistration","if(!monaco.languages.getLanguages().some(language=>language.id==='csharp'&&language.extensions.includes('.cs')))throw new Error('Official C# configuration is missing');return 'PASS';"); Check("WebsiteCSharpWholeSourceGrammar","const source='using System;\\npublic class widget {\\n public int Run(int value) { if(value>0) return Math.Max(value,42); return 0; }\\n}';const parsed=xfeWebsiteHighlight.tokenizeSource(source);if(parsed.html!==hljs.highlight(source,{language:'csharp',ignoreIllegals:true}).value)throw new Error('Adapter replaced website grammar output');if(!parsed.lines.flat().some(token=>token.scopes==='xfe.csharp_keyword')||!parsed.lines.flat().some(token=>token.scopes==='xfe.type')||!parsed.lines.flat().some(token=>token.scopes==='xfe.number'))throw new Error('Website C# token categories not preserved: '+JSON.stringify(parsed.lines));return 'PASS';"); Check("CSharpEditorUsesWebsitePalette","xfeMonacoSetLanguage('csharp');xfeMonacoEditor.render(true);if(xfeMonacoEditor.getModel().getLanguageId()!=='csharp'||!xfeMonacoEditor.getValue().includes('namespace XFEstudio'))throw new Error('Actual editor model did not switch to C#');if(!xfeWebsiteHighlight.themeRules.some(rule=>rule.token==='xfe.csharp_keyword'&&rule.foreground.toUpperCase()==='C586C0')||!xfeWebsiteHighlight.themeRules.some(rule=>rule.token==='xfe.type'&&rule.foreground.toUpperCase()==='4EC9B0'))throw new Error('Website CSS palette mismatch');return 'PASS';"); for(int frame=11;frame<=15;frame++)browser.AdvanceTime(frame*17); browser.Paint(canvas,new DesktopBounds(0,0,800,600),0);canvas.SavePng(Path.Combine(output,"MonacoCSharp.png")); Check("CSharpRealViewHasColoredTokens","const lines=Array.from(document.querySelectorAll('.view-lines .view-line'));if(!lines.some(line=>line.textContent.replace(/\\u00a0/g,' ').includes('public sealed class Preview')))throw new Error('C# source is absent from the real editor view');const colors=new Set(Array.from(document.querySelectorAll('.view-lines span')).map(span=>getComputedStyle(span).color));if(colors.size<4)throw new Error('Real C# view did not receive website theme colors: '+JSON.stringify(Array.from(colors)));return 'PASS';"); Check("CSharpRenderedColorsMatchWebsiteScopes","const expected=xfeWebsiteHighlight.tokenizeSource(xfeMonacoEditor.getValue());let checked=0;function hex(value){if(value.charAt(0)==='#')return value.slice(1).toLowerCase();const numbers=value.match(/\\d+/g);return numbers?numbers.slice(0,3).map(number=>Number(number).toString(16).padStart(2,'0')).join(''):value.toLowerCase();}for(const line of document.querySelectorAll('.view-lines .view-line')){const index=Math.round(parseFloat(line.style.top)/20);let offset=0;for(const span of line.querySelectorAll('span[class]')){const text=span.textContent.replace(/\\u00a0/g,' ');if(!text.length)continue;const token=expected.lines[index].filter(token=>token.startIndex<=offset).slice(-1)[0];const rule=xfeWebsiteHighlight.themeRules.filter(rule=>rule.foreground&&(token.scopes===rule.token||token.scopes.startsWith(rule.token+'.'))).sort((a,b)=>a.token.length-b.token.length).slice(-1)[0];const color=hex(getComputedStyle(span).color);if(color!==rule.foreground.toLowerCase())throw new Error('Website rendered color mismatch at line '+(index+1)+' column '+offset+' '+JSON.stringify(text)+': '+color+' versus '+rule.foreground+' '+token.scopes);offset+=text.length;checked++;}}if(checked<30)throw new Error('Too few real rendered C# token spans compared');return 'PASS';"); File.WriteAllText(Path.Combine(output,"MonacoCSharpDomReadback.json"),browser.ExecuteJavaScript("JSON.stringify(xfeWebsiteHighlight.getProof(xfeMonacoEditor))")); Check("CSharpEditingKeepsModelAndColoring","xfeMonacoEditor.focus();xfeMonacoEditor.setPosition({lineNumber:1,column:1});xfeMonacoEditor.trigger('keyboard','type',{text:'// C# edit\\n'});if(!xfeMonacoEditor.getValue().startsWith('// C# edit\\n'))throw new Error('C# editor type action failed');xfeMonacoEditor.render(true);return 'PASS';"); for(int frame=16;frame<=25;frame++)browser.AdvanceTime(frame*17); Check("CSharpFollowingLineRetokenizesAfterNewLine","xfeMonacoEditor.render(true);const line=Array.from(document.querySelectorAll('.view-lines .view-line')).find(line=>Math.round(parseFloat(line.style.top)/20)===1);if(!line||!line.textContent.replace(/\\u00a0/g,' ').startsWith('using System;'))throw new Error('Following C# source line is missing');const keyword=Array.from(line.querySelectorAll('span[class]')).find(span=>span.textContent==='using');const color=keyword&&getComputedStyle(keyword).color.toLowerCase().replace(/\\s/g,'');if(!keyword||(color!=='#569cd6'&&color!=='rgb(86,156,214)'))throw new Error('Insertion failed to retokenize the following C# keyword: '+color+' '+(line&&line.innerHTML));return 'PASS';"); browser.Paint(canvas,new DesktopBounds(0,0,800,600),0);canvas.SavePng(Path.Combine(output,"MonacoCSharpEdited.png")); File.WriteAllText(Path.Combine(output,"MonacoCSharpEditedDomReadback.json"),browser.ExecuteJavaScript("JSON.stringify(xfeWebsiteHighlight.getProof(xfeMonacoEditor))")); } var finalPage=browser.RefreshDocument(); File.WriteAllText(Path.Combine(output,"Document.html"),browser.Document?.DocumentElement?.OuterHtml??""); File.WriteAllText(Path.Combine(output,"Page.txt"),finalPage.Text); var result=new{ Kind="ProductionBrowserMonacoProbe",MonacoVersion="0.52.2",ProductionBrowserServiceUsed=true, Distribution=actualCoreBundle?"OfficialESMCoreIife":"OfficialAmd",CoreBundleUsed=actualCoreBundle, ResourceOverrideUsed=coreBundle,ExactCoreResourcesVerified=exactCoreResources, ManualWindowApiShims=false,FakeMonacoObjects=false,HostBrowserUsed=false,NativeGuestTested=false, Created=created,ManualCreateDiagnosticUsed=!created,NavigationTitle=page.Title,PageText=finalPage.Text, WorkerSupported=false,FullTypeScriptLanguageServiceSupported=false, Passed=created&&stages.All(stage=>stage.Passed)&&!finalPage.Text.Contains("Event loop failed:",StringComparison.Ordinal), ElapsedMilliseconds=stopwatch.Elapsed.TotalMilliseconds,ManagedHeapBytes=GC.GetTotalMemory(false),WorkingSetBytes=Process.GetCurrentProcess().WorkingSet64, FontBackend="Windows GDI+ private TTF; not the native renderer",canvas.FontSha256,canvas.FontFamilyName,canvas.MeasureCalls,canvas.RasterCalls,canvas.RasterInkPixels, CssLayoutConnected=layout is not null,LayoutBoxes=layout?.Boxes.Count,DisplayCommands=layout?.Commands.Count,CssDiagnostics=layout?.Diagnostics, SourceUnchangedDuringRun=sourceFiles.All(pair=>pair.Value==Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(pair.Key)))), BrowserSources=sourceFiles.Select(pair=>new{Path=Path.GetRelativePath(root,pair.Key).Replace('\\','/'),Sha256=pair.Value}).ToArray(), BrowserAssemblySha256=Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(typeof(BrowserService).Assembly.Location))), Resources=browser.LoadedResources,Stages=stages }; var resultPath=Path.Combine(output,"Result.json"); File.WriteAllText(resultPath,JsonSerializer.Serialize(result,new JsonSerializerOptions{WriteIndented=true})); Console.WriteLine(resultPath); internal sealed record ProbeCheck(string Name,bool Passed,string Output,double Milliseconds);