using XFESystem.Collections;
namespace XFESystem.Browser;
public sealed partial class BrowserService
{
public BrowserDomLifetime? ReadLifetimeDiagnostics()=>WithGate(()=>dom?.Lifetime);
public WeakCollectionLifetime? ReadWeakCollectionLifetime()=>WithGate(()=>runtime?.WeakCollectionLifetime);
/// <summary>Only the current owner calls this. Background proxies schedule
/// it on their worker after any indivisible script callback returns.</summary>
public void Release()=>WithGate(()=>
{
ReleasePageOwners("Release");
dom=null;runtime=null;document=null;displayList=null;metricCanvas=null;textMetrics=new BrowserFallbackTextMetrics();
layoutEngine.ReleaseDocument();
linkedStyles.Clear();pendingResources.Clear();scheduledResources.Clear();executedScripts.Clear();
hoveredNode=pointerDownNode=null;resourceLoader=null;pageDiagnostics=null;ResetDocumentLoading();
return 0;
});
// All callers already execute on the page's exclusive owner. Keep this
// common path explicit: native cleanup cannot depend on finally/finalizers.
private void ReleasePageOwners(string reason)
{
var previousDom=dom;var previousRuntime=runtime;
var domBefore=previousDom?.Lifetime;
var weakBefore=previousRuntime?.WeakCollectionLifetime;
previousDom?.ReleaseRetainedState();previousRuntime?.Release();
var domAfter=previousDom?.Lifetime;
var weakAfter=previousRuntime?.WeakCollectionLifetime;
// Drop each released owner before diagnostics. A throwing observer
// must not leave a dead engine published or interrupt navigation.
dom=null;runtime=null;
string records="";
if(domBefore is not null&&domAfter is not null)
records="XFE_BROWSER_RELEASE|HandlesBefore|"+domBefore.OutstandingHandles+
"|HandlesAfter|"+domAfter.OutstandingHandles+"\n";
if(weakBefore is not null&&weakAfter is not null)
records+="XFE_BROWSER_WEAK_RELEASE|TablesBefore|"+weakBefore.Tables+
"|EntriesBefore|"+weakBefore.Entries+"|TablesAfter|"+weakAfter.Tables+
"|EntriesAfter|"+weakAfter.Entries+"|Created|"+weakAfter.Created+
"|Disposed|"+weakAfter.Disposed+"|OwnerHandlesCreated|"+weakAfter.TableHandlesCreated+
"|OwnerHandlesDisposed|"+weakAfter.TableHandlesDisposed+"|Page|"+paintIdentity+"|Reason|"+reason+"\n";
if(records.Length!=0)TraceReleasedOwner(records);
}
private void TraceReleasedOwner(string text)
{
try { Trace?.Invoke(text); }
catch(Exception error)when(error is not OutOfMemoryException and not StackOverflowException)
{ /* Diagnostic observers do not own page cleanup. */ }
}
}
using XFESystem.Collections;
namespace XFESystem.Browser;
public sealed partial class BrowserService
{
public BrowserDomLifetime? ReadLifetimeDiagnostics()=>WithGate(()=>dom?.Lifetime);
public WeakCollectionLifetime? ReadWeakCollectionLifetime()=>WithGate(()=>runtime?.WeakCollectionLifetime);
/// <summary>Only the current owner calls this. Background proxies schedule
/// it on their worker after any indivisible script callback returns.</summary>
public void Release()=>WithGate(()=>
{
ReleasePageOwners("Release");
dom=null;runtime=null;document=null;displayList=null;metricCanvas=null;textMetrics=new BrowserFallbackTextMetrics();
layoutEngine.ReleaseDocument();
linkedStyles.Clear();pendingResources.Clear();scheduledResources.Clear();executedScripts.Clear();
hoveredNode=pointerDownNode=null;resourceLoader=null;pageDiagnostics=null;ResetDocumentLoading();
return 0;
});
// All callers already execute on the page's exclusive owner. Keep this
// common path explicit: native cleanup cannot depend on finally/finalizers.
private void ReleasePageOwners(string reason)
{
var previousDom=dom;var previousRuntime=runtime;
var domBefore=previousDom?.Lifetime;
var weakBefore=previousRuntime?.WeakCollectionLifetime;
previousDom?.ReleaseRetainedState();previousRuntime?.Release();
var domAfter=previousDom?.Lifetime;
var weakAfter=previousRuntime?.WeakCollectionLifetime;
// Drop each released owner before diagnostics. A throwing observer
// must not leave a dead engine published or interrupt navigation.
dom=null;runtime=null;
string records="";
if(domBefore is not null&&domAfter is not null)
records="XFE_BROWSER_RELEASE|HandlesBefore|"+domBefore.OutstandingHandles+
"|HandlesAfter|"+domAfter.OutstandingHandles+"\n";
if(weakBefore is not null&&weakAfter is not null)
records+="XFE_BROWSER_WEAK_RELEASE|TablesBefore|"+weakBefore.Tables+
"|EntriesBefore|"+weakBefore.Entries+"|TablesAfter|"+weakAfter.Tables+
"|EntriesAfter|"+weakAfter.Entries+"|Created|"+weakAfter.Created+
"|Disposed|"+weakAfter.Disposed+"|OwnerHandlesCreated|"+weakAfter.TableHandlesCreated+
"|OwnerHandlesDisposed|"+weakAfter.TableHandlesDisposed+"|Page|"+paintIdentity+"|Reason|"+reason+"\n";
if(records.Length!=0)TraceReleasedOwner(records);
}
private void TraceReleasedOwner(string text)
{
try { Trace?.Invoke(text); }
catch(Exception error)when(error is not OutOfMemoryException and not StackOverflowException)
{ /* Diagnostic observers do not own page cleanup. */ }
}
}