diff --git a/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs b/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs index f4c1b208..654ad279 100644 --- a/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs +++ b/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs @@ -100,12 +100,44 @@ public class AvaloniaLoginChoiceEager : ILoginChoiceEager dialog.Source = new Uri(shoiceIn.LoginUrl); }; - if (!Configuration.IsLinux && App.MainWindow is TopLevel topLevel) - dialog.Show(topLevel); - else - dialog.Show(); + // NativeWebDialog can fault on a posted dispatcher job (e.g. missing libwebkit2gtk on Linux), which bypasses a try/catch around Show(). + void onUnhandledException(object? sender, DispatcherUnhandledExceptionEventArgs e) + { + if (!WebView2LoginErrorMessage.IsWebView2SignInInfrastructureFailure(e.Exception)) + return; + e.Handled = true; + try + { + dialog.Close(); + } + catch + { + /* ignore */ + } + tcs.TrySetException(e.Exception); + } - return await tcs.Task; + Dispatcher.UIThread.UnhandledException += onUnhandledException; + try + { + try + { + if (!Configuration.IsLinux && App.MainWindow is TopLevel topLevel) + dialog.Show(topLevel); + else + dialog.Show(); + } + catch (Exception ex) when (WebView2LoginErrorMessage.IsWebView2SignInInfrastructureFailure(ex)) + { + tcs.TrySetException(ex); + } + + return await tcs.Task; + } + finally + { + Dispatcher.UIThread.UnhandledException -= onUnhandledException; + } void Dialog_EnvironmentRequested(object? sender, WebViewEnvironmentRequestedEventArgs e) { diff --git a/Source/LibationUiBase/WebView2LoginErrorMessage.cs b/Source/LibationUiBase/WebView2LoginErrorMessage.cs index ce56ee5a..c1b60927 100644 --- a/Source/LibationUiBase/WebView2LoginErrorMessage.cs +++ b/Source/LibationUiBase/WebView2LoginErrorMessage.cs @@ -11,21 +11,56 @@ public static class WebView2LoginErrorMessage { public const string Caption = "Sign-in browser could not start"; - public static string ExplainerBody => - "Libation could not start the in-app sign-in browser. On Windows this uses Microsoft WebView2. " - + "This is a local sign-in or system setup issue — not a failure of the library scan itself.\r\n\r\n" - + "Things to try:\r\n" - + "• On Windows: install or repair the Microsoft Edge WebView2 Runtime from https://developer.microsoft.com/microsoft-edge/webview2/\r\n" - + "• In Libation Settings, try turning off the option to use the embedded browser and use external browser sign-in instead.\r\n" - + "• Check that security software is not blocking Libation or the embedded browser.\r\n" - + "• Ensure your account can write to local app data folders (permissions).\r\n" - + "• If you run as Administrator, try running Libation as a normal user (or the reverse).\r\n\r\n" - + "After sign-in works, use Import again to scan your library."; + public static string ExplainerBody + { + get + { + const string localIssue = + "This is a local sign-in or system setup issue — not a failure of the library scan itself.\r\n\r\n"; + const string thingsToTry = "Things to try:\r\n"; + const string settingsBullet = + "• In Libation Settings, try turning off the option to use the embedded browser and use external browser sign-in instead.\r\n"; + const string securityBullet = + "• Check that security software is not blocking Libation or the embedded browser.\r\n"; + const string permissionsBullet = + "• Ensure your account can write to local app data folders (permissions).\r\n"; + const string adminBullet = + "• If you run as Administrator, try running Libation as a normal user (or the reverse).\r\n\r\n"; + const string footer = "After sign-in works, use Import again to scan your library."; + + if (OperatingSystem.IsLinux()) + { + return "Libation could not start the in-app sign-in browser. On Linux this uses WebKit2GTK (native WebKit). " + + localIssue + + thingsToTry + + "• Install WebKit2GTK for your distro (e.g. on Arch: webkit2gtk or webkit2gtk-4.1).\r\n" + + settingsBullet + + securityBullet + + permissionsBullet + + adminBullet + + footer; + } + + return "Libation could not start the in-app sign-in browser. On Windows this uses Microsoft WebView2. " + + localIssue + + thingsToTry + + "• On Windows: install or repair the Microsoft Edge WebView2 Runtime from https://developer.microsoft.com/microsoft-edge/webview2/\r\n" + + settingsBullet + + securityBullet + + permissionsBullet + + adminBullet + + footer; + } + } public static bool IsWebView2SignInInfrastructureFailure(Exception ex) { for (var e = ex; e is not null; e = e.InnerException) { + // Gtk / WebView2: missing native WebKit or WebView2 runtime often surfaces as DllNotFoundException (sometimes without a useful stack). + if (e is DllNotFoundException dll && IsEmbeddedWebViewNativeDllFailure(dll)) + return true; + if (!StackMentionsEmbeddedSignInBrowser(e)) continue; @@ -40,6 +75,18 @@ public static class WebView2LoginErrorMessage return false; } + /// + /// Gtk WebView (e.g. Avalonia NativeWebDialog on Linux) requires WebKit2GTK; minimal installs omit libwebkit2gtk. + /// + private static bool IsEmbeddedWebViewNativeDllFailure(DllNotFoundException e) + { + if (e.Message.Contains("libwebkit2gtk", StringComparison.OrdinalIgnoreCase) + || e.Message.Contains("webkit2gtk", StringComparison.OrdinalIgnoreCase)) + return true; + + return StackMentionsEmbeddedSignInBrowser(e); + } + public static bool TryFindInTree(Exception ex, out Exception? match) { Exception? found = null; @@ -70,6 +117,8 @@ public static class WebView2LoginErrorMessage return stack is not null && (stack.Contains("WebView2", StringComparison.Ordinal) || stack.Contains("CoreWebView2", StringComparison.Ordinal) - || stack.Contains("NativeWebDialog", StringComparison.Ordinal)); + || stack.Contains("NativeWebDialog", StringComparison.Ordinal) + || stack.Contains("GtkWebView", StringComparison.Ordinal) + || stack.Contains("WebViewControlAvalonia", StringComparison.Ordinal)); } }