using AngleSharp.Html.Parser; using XFESystem.Browser; namespace XFESystem.Kernel; /// Same code is run on the guest: reports actual JavaScript/host identity and dispatch results. public static class BrowserNativeEventRegression { public static bool Run(BrowserService browser, Action log) { var passed = true; void Stage(string name, string actual, string expected) { var ok = actual == expected; passed &= ok; log("XFE_BROWSER_EVENT_REGRESSION|" + name + "|" + (ok ? "PASS" : "FAIL") + "|" + actual.Replace('\r', ' ').Replace('\n', ' ') + "\n"); } string Page(string source) => browser.ExecuteJavaScript(source); try { Stage("WeakMapPlainSameEval", Page("var __xfeRegKey={},__xfeRegWeak=new WeakMap();__xfeRegWeak.set(__xfeRegKey,42);__xfeRegWeak.has(__xfeRegKey)+':'+__xfeRegWeak.get(__xfeRegKey)"), "true:42"); Stage("WeakMapPlainCrossEval", Page("__xfeRegWeak.has(__xfeRegKey)+':'+__xfeRegWeak.get(__xfeRegKey)"), "true:42"); Stage("WeakMapHostSameEval", Page("var __xfeRegBody=document.body,__xfeRegHostWeak=new WeakMap();__xfeRegHostWeak.set(__xfeRegBody,43);__xfeRegHostWeak.has(document.body)+':'+__xfeRegHostWeak.get(document.body)+':'+(__xfeRegBody===document.body)"), "true:43:true"); Stage("WeakMapHostCrossEval", Page("__xfeRegHostWeak.has(document.body)+':'+__xfeRegHostWeak.get(document.body)+':'+(__xfeRegBody===document.body)"), "true:43:true"); Stage("PlainEventTargetSameEval", Page("var __xfeRegCount=0,__xfeRegTarget=new EventTarget();__xfeRegTarget.addEventListener('xfe-test',()=>++__xfeRegCount);__xfeRegTarget.dispatchEvent(new Event('xfe-test'));__xfeRegCount"), "1"); Stage("PlainEventTargetCrossEval", Page("__xfeRegTarget.dispatchEvent(new Event('xfe-test'));__xfeRegCount"), "2"); Stage("HostEventSameEval", Page("var __xfeRegButton=document.createElement('button');__xfeRegButton.id='XfeNativeEventRegression';document.body.appendChild(__xfeRegButton);var __xfeRegButtonCount=0;__xfeRegButton.addEventListener('click',()=>{++__xfeRegButtonCount;__xfeRegButton.textContent='clicked '+__xfeRegButtonCount;});__xfeRegButton.dispatchEvent(new Event('click'));__xfeRegButtonCount+':'+__xfeRegButton.textContent"), "1:clicked 1"); Stage("HostEventCrossEval", Page("document.getElementById('XfeNativeEventRegression').dispatchEvent(new Event('click'));__xfeRegButtonCount+':'+__xfeRegButton.textContent"), "2:clicked 2"); Stage("HostClickMethodCrossEval", Page("document.getElementById('XfeNativeEventRegression').click();__xfeRegButtonCount+':'+__xfeRegButton.textContent"), "3:clicked 3"); Stage("HostListenerReceiver", Page("var __xfeRegSameReceiver=false;__xfeRegButton.addEventListener('receiver',function(e){__xfeRegSameReceiver=this===__xfeRegButton&&e.target===this;});__xfeRegButton.dispatchEvent(new Event('receiver'));__xfeRegSameReceiver"), "true"); Page("__xfeRegButton.remove()"); } catch (Exception error) { passed = false; log("XFE_BROWSER_EVENT_REGRESSION|PageException|FAIL|" + error.Message + "\n"); } JavaScriptRuntime? bridgeRuntime = null; DomBridge? bridgeDom = null; try { var limits = new BrowserLimits(); var runtime = bridgeRuntime = new JavaScriptRuntime(limits); var document = new HtmlParser().ParseDocument(""); var mutations = 0; var dom = bridgeDom = new DomBridge(runtime, document, new Uri("http://xfe.test/Regression"), limits, new() { Mutated = () => ++mutations }); Stage("BridgeSetup", runtime.Evaluate("var count=0;var button=document.getElementById('Button');button.addEventListener('click',()=>{++count;button.textContent='clicked '+count;});'ready'").Text, "ready"); Stage("BridgeCrossCSharpAllowed", dom.DispatchEvent(document.GetElementById("Button"), "click").ToString(), "True"); Stage("BridgeCrossCSharpMutation", runtime.Evaluate("count+':'+document.getElementById('Button').textContent").Text, "1:clicked 1"); Stage("BridgeMutationCallback", mutations.ToString(), "1"); Stage("BridgeWeakMapIdentity", runtime.Evaluate("var weak=new WeakMap();weak.set(button,44);weak.get(document.getElementById('Button'))+':'+weak.has(button)").Text, "44:true"); Stage("BridgeSecondCSharpAllowed", dom.DispatchEvent(document.GetElementById("Button"), "click").ToString(), "True"); Stage("BridgeSecondCSharpMutation", runtime.Evaluate("count+':'+button.textContent").Text, "2:clicked 2"); Stage("BridgeEventErrorVisible", runtime.Evaluate("var afterError=0;button.addEventListener('error-probe',()=>{throw Error('NativeEventErrorVisible');});button.addEventListener('error-probe',()=>++afterError);'ready'").Text, "ready"); dom.DispatchEvent(document.GetElementById("Button"), "error-probe"); Stage("BridgeErrorConsole", runtime.DrainConsoleOutput().Contains("NativeEventErrorVisible", StringComparison.Ordinal).ToString(), "True"); Stage("BridgeAfterErrorListener", runtime.Evaluate("afterError").Text, "1"); Stage("BridgeAfterErrorLexicalScope", runtime.Evaluate("function outer(){const value=21;try{(()=>{throw Error('scope')})()}catch(error){return (()=>value*2)()}}outer()").Text, "42"); } catch (Exception error) { passed = false; log("XFE_BROWSER_EVENT_REGRESSION|BridgeException|FAIL|" + error.Message + "\n"); } bridgeDom?.ReleaseRetainedState(); bridgeRuntime?.Dispose(); log("XFE_BROWSER_EVENT_REGRESSION|Complete|" + (passed ? "PASS" : "FAIL") + "\n"); return passed; } }