using XFESystem.Desktop;
namespace XFESystem.Browser.Tests;
public sealed class DomParserTests
{
[Fact]
public void ParsedHtmlHasItsOwnDocumentAndRemainsInertAfterInsertion()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("Original,true,true,true,complete,text/html", page.ExecuteJavaScript("""
const parsed=new DOMParser().parseFromString('<title>Original</title><div id=isolated>value</div><script>globalThis.parsedCanary=42</scr'+'ipt>','text/html');
const node=parsed.getElementById('isolated');
[parsed.title,node.ownerDocument===parsed,parsed!==document,parsed.defaultView===null,parsed.readyState,parsed.contentType].join(',')
"""));
Assert.Equal("undefined", page.ExecuteJavaScript("const script=parsed.querySelector('script');document.body.appendChild(script.cloneNode(true));document.body.appendChild(script);typeof parsedCanary"));
Assert.Equal("true", page.ExecuteJavaScript("document.getElementById('isolated')===null"));
Assert.Equal("true", page.ExecuteJavaScript("parsed.ownerDocument===null"));
}
finally { page.Release(); }
}
[Fact]
public void SvgParsingPreservesNamespaceCaseAndActualGeometryAfterAdoption()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("svg,http://www.w3.org/2000/svg,0 0 20 10,M0 0L20 10,true,image/svg+xml", page.ExecuteJavaScript("""
const parsed=new DOMParser().parseFromString('<svg xmlns="http://www.w3.org/2000/svg" width="20" height="10" viewBox="0 0 20 10"><path d="M0 0L20 10"/></svg>','image/svg+xml');
const svg=parsed.documentElement;
[svg.localName,svg.namespaceURI,svg.getAttribute('viewBox'),svg.firstChild.getAttribute('d'),svg.ownerDocument===parsed,parsed.contentType].join(',')
"""));
page.ExecuteJavaScript("document.body.appendChild(svg)");
var svg = page.Document!.QuerySelector("svg")!;
var rect = page.Layout(new(800, 600)).GetBoundingClientRect(svg);
Assert.Equal(20, rect.Width, 5); Assert.Equal(10, rect.Height, 5);
Assert.Equal("true", page.ExecuteJavaScript("svg.ownerDocument===document"));
}
finally { page.Release(); }
}
[Fact]
public void XmlCommentAndCdataRemainLiteralWhileAdoptedScriptsStayInert()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("svg", page.ExecuteJavaScript("const xml=new DOMParser().parseFromString('<svg xmlns=\"http://www.w3.org/2000/svg\"><!-- <!DOCTYPE literal> --><text><![CDATA[<!DOCTYPE literal>]]></text><script>globalThis.xmlCanary=42</scr'+'ipt></svg>','image/svg+xml');xml.documentElement.localName"));
Assert.Equal("undefined", page.ExecuteJavaScript("document.body.appendChild(xml.documentElement.cloneNode(true));typeof xmlCanary"));
}
finally { page.Release(); }
}
[Theory]
[InlineData("<svg><path></svg>")]
[InlineData("<!DOCTYPE x SYSTEM 'https://must-not-fetch.test/x'><x/>")]
[InlineData("<!DOCTYPE x [<!ENTITY y SYSTEM 'file:///must-not-read'>]><x>&y;</x>")]
public void MalformedXmlAndDtdProduceParserErrorsWithoutExternalResolution(string source)
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
string encoded = System.Text.Json.JsonSerializer.Serialize(source);
Assert.Equal("parsererror", page.ExecuteJavaScript("new DOMParser().parseFromString(" + encoded + ",'image/svg+xml').documentElement.localName"));
}
finally { page.Release(); }
}
[Fact]
public void ParserRejectsUnknownMimeAndChargesRepeatedDetachedDocuments()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("TypeError", page.ExecuteJavaScript("let type='';try{new DOMParser().parseFromString('x','application/json')}catch(e){type=e.name}type"));
string result = page.ExecuteJavaScript("for(let i=0;i<33;i++)new DOMParser().parseFromString('<p>bounded</p>','text/html')");
Assert.Contains("detached-document budget", result, StringComparison.Ordinal);
}
finally { page.Release(); }
}
}
using XFESystem.Desktop;
namespace XFESystem.Browser.Tests;
public sealed class DomParserTests
{
[Fact]
public void ParsedHtmlHasItsOwnDocumentAndRemainsInertAfterInsertion()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("Original,true,true,true,complete,text/html", page.ExecuteJavaScript("""
const parsed=new DOMParser().parseFromString('<title>Original</title><div id=isolated>value</div><script>globalThis.parsedCanary=42</scr'+'ipt>','text/html');
const node=parsed.getElementById('isolated');
[parsed.title,node.ownerDocument===parsed,parsed!==document,parsed.defaultView===null,parsed.readyState,parsed.contentType].join(',')
"""));
Assert.Equal("undefined", page.ExecuteJavaScript("const script=parsed.querySelector('script');document.body.appendChild(script.cloneNode(true));document.body.appendChild(script);typeof parsedCanary"));
Assert.Equal("true", page.ExecuteJavaScript("document.getElementById('isolated')===null"));
Assert.Equal("true", page.ExecuteJavaScript("parsed.ownerDocument===null"));
}
finally { page.Release(); }
}
[Fact]
public void SvgParsingPreservesNamespaceCaseAndActualGeometryAfterAdoption()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("svg,http://www.w3.org/2000/svg,0 0 20 10,M0 0L20 10,true,image/svg+xml", page.ExecuteJavaScript("""
const parsed=new DOMParser().parseFromString('<svg xmlns="http://www.w3.org/2000/svg" width="20" height="10" viewBox="0 0 20 10"><path d="M0 0L20 10"/></svg>','image/svg+xml');
const svg=parsed.documentElement;
[svg.localName,svg.namespaceURI,svg.getAttribute('viewBox'),svg.firstChild.getAttribute('d'),svg.ownerDocument===parsed,parsed.contentType].join(',')
"""));
page.ExecuteJavaScript("document.body.appendChild(svg)");
var svg = page.Document!.QuerySelector("svg")!;
var rect = page.Layout(new(800, 600)).GetBoundingClientRect(svg);
Assert.Equal(20, rect.Width, 5); Assert.Equal(10, rect.Height, 5);
Assert.Equal("true", page.ExecuteJavaScript("svg.ownerDocument===document"));
}
finally { page.Release(); }
}
[Fact]
public void XmlCommentAndCdataRemainLiteralWhileAdoptedScriptsStayInert()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("svg", page.ExecuteJavaScript("const xml=new DOMParser().parseFromString('<svg xmlns=\"http://www.w3.org/2000/svg\"><!-- <!DOCTYPE literal> --><text><![CDATA[<!DOCTYPE literal>]]></text><script>globalThis.xmlCanary=42</scr'+'ipt></svg>','image/svg+xml');xml.documentElement.localName"));
Assert.Equal("undefined", page.ExecuteJavaScript("document.body.appendChild(xml.documentElement.cloneNode(true));typeof xmlCanary"));
}
finally { page.Release(); }
}
[Theory]
[InlineData("<svg><path></svg>")]
[InlineData("<!DOCTYPE x SYSTEM 'https://must-not-fetch.test/x'><x/>")]
[InlineData("<!DOCTYPE x [<!ENTITY y SYSTEM 'file:///must-not-read'>]><x>&y;</x>")]
public void MalformedXmlAndDtdProduceParserErrorsWithoutExternalResolution(string source)
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
string encoded = System.Text.Json.JsonSerializer.Serialize(source);
Assert.Equal("parsererror", page.ExecuteJavaScript("new DOMParser().parseFromString(" + encoded + ",'image/svg+xml').documentElement.localName"));
}
finally { page.Release(); }
}
[Fact]
public void ParserRejectsUnknownMimeAndChargesRepeatedDetachedDocuments()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("TypeError", page.ExecuteJavaScript("let type='';try{new DOMParser().parseFromString('x','application/json')}catch(e){type=e.name}type"));
string result = page.ExecuteJavaScript("for(let i=0;i<33;i++)new DOMParser().parseFromString('<p>bounded</p>','text/html')");
Assert.Contains("detached-document budget", result, StringComparison.Ordinal);
}
finally { page.Release(); }
}
}