@@ -16,7 +16,9 @@ public partial class MainWindow : Window
1616 private readonly NativeWebView _webView ;
1717 private readonly AvaloniaWebViewHost _host ;
1818 private DispatcherTimer ? _smokeTimer ;
19+ private DispatcherTimer ? _smokePoll ;
1920 private bool _smokeProbed ;
21+ private bool _smokeBusy ;
2022
2123 public MainWindow ( )
2224 {
@@ -47,51 +49,62 @@ private void OnNavigationCompleted()
4749 // Headless smoke (--smoke): drive a page->host bridge round-trip on the real native
4850 // webview. If the host receives the message the JS posted, the JS<->WebKitGTK<->C#
4951 // bridge works; exit 0. Otherwise time out and exit 1.
50- private async void StartSmokeProbe ( )
52+ // On WebKitGTK, NavigationCompleted fires while the document is still "loading" (body not
53+ // yet parsed), so poll until the rendered DOM has pointable [data-line] elements, then
54+ // dispatch a real click. The DOM is shared across JS worlds, so the click fires the page's
55+ // own main-world pointing handler -> BridgeShim -> native host (the genuine path).
56+ private void StartSmokeProbe ( )
5157 {
52- _smokeTimer = new DispatcherTimer { Interval = TimeSpan . FromSeconds ( 20 ) } ;
58+ _smokeTimer = new DispatcherTimer { Interval = TimeSpan . FromSeconds ( 30 ) } ;
5359 _smokeTimer . Tick += ( _ , _ ) =>
5460 {
5561 Console . Error . WriteLine ( "SMOKE FAIL: no bridge message within timeout" ) ;
5662 Exit ( 1 ) ;
5763 } ;
5864 _smokeTimer . Start ( ) ;
5965
60- // Diagnostic: report what InvokeScript's JS context actually sees, then (if pointable
61- // elements exist) dispatch a real click — the DOM is shared across JS worlds, so this
62- // fires the page's own main-world pointing handler -> BridgeShim -> native host.
63- const string probe = @"(function(){
64- var diag = JSON.stringify({
65- rs: document.readyState,
66- url: String(location.href).slice(0,80),
67- bodyKids: document.body ? document.body.childElementCount : -1,
68- allEls: document.querySelectorAll('*').length,
69- dataLine: document.querySelectorAll('[data-line]').length,
70- htmlLen: document.documentElement ? document.documentElement.outerHTML.length : -1,
71- chrome: typeof window.chrome,
72- invoke: typeof window.invokeCSharpAction
73- });
74- var el = document.querySelector('[data-line]');
75- if (el) ['mouseover','mousedown','click'].forEach(function(t){
76- el.dispatchEvent(new MouseEvent(t, {bubbles:true, cancelable:true, view:window}));
77- });
78- return diag;
79- })()" ;
66+ _smokePoll = new DispatcherTimer { Interval = TimeSpan . FromMilliseconds ( 300 ) } ;
67+ _smokePoll . Tick += OnSmokePoll ;
68+ _smokePoll . Start ( ) ;
69+ }
70+
71+ private const string SmokeProbeJs = @"(function(){
72+ if (document.readyState === 'loading') return 'waiting';
73+ if (document.querySelectorAll('[data-line]').length === 0) return 'waiting';
74+ var el = document.querySelector('[data-line]');
75+ ['mouseover','mousedown','click'].forEach(function(t){
76+ el.dispatchEvent(new MouseEvent(t, {bubbles:true, cancelable:true, view:window}));
77+ });
78+ return 'dispatched';
79+ })()" ;
80+
81+ private async void OnSmokePoll ( object ? sender , EventArgs e )
82+ {
83+ if ( _smokeBusy ) return ;
84+ _smokeBusy = true ;
8085 try
8186 {
82- var result = await _host . ExecuteScriptAsync ( probe ) ;
83- Console . Error . WriteLine ( "SMOKE DIAG: " + result ) ;
87+ var result = await _host . ExecuteScriptAsync ( SmokeProbeJs ) ;
88+ if ( result != null && result . Contains ( "dispatched" ) )
89+ {
90+ _smokePoll ? . Stop ( ) ;
91+ Console . Error . WriteLine ( "SMOKE: dispatched click on a pointable element" ) ;
92+ }
8493 }
8594 catch ( Exception ex )
8695 {
87- Console . Error . WriteLine ( "SMOKE FAIL: InvokeScript threw: " + ex . Message ) ;
88- Exit ( 1 ) ;
96+ Console . Error . WriteLine ( "SMOKE probe error (retrying): " + ex . Message ) ;
97+ }
98+ finally
99+ {
100+ _smokeBusy = false ;
89101 }
90102 }
91103
92104 private void Exit ( int code )
93105 {
94106 _smokeTimer ? . Stop ( ) ;
107+ _smokePoll ? . Stop ( ) ;
95108 if ( Application . Current ? . ApplicationLifetime is IClassicDesktopStyleApplicationLifetime life )
96109 life . Shutdown ( code ) ;
97110 else
0 commit comments