diff --git a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/ChmWebpagePanel.cs b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/ChmWebpagePanel.cs index 9bff638c1..37b37812b 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/ChmWebpagePanel.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/ChmWebpagePanel.cs @@ -15,7 +15,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -using QuickLook.Common.Helpers; using QuickLook.Plugin.HtmlViewer; using System; using System.Collections.Generic; @@ -42,27 +41,6 @@ static ChmWebpagePanel() public ChmWebpagePanel() { _homePage = ReadResource("/chm2html.html") ?? []; - - if (OSThemeHelper.AppsUseDarkTheme()) - { - // Invoke using reflection: WebView2.CreationProperties.AdditionalBrowserArguments - // This approach allows the library to avoid direct dependency on WebView2 - if (typeof(WebpagePanel).GetField("_webView", BindingFlags.NonPublic | BindingFlags.Instance) is FieldInfo fieldInfo) - { - object webView2 = fieldInfo.GetValue(this); - - if (webView2?.GetType().GetProperty("CreationProperties", BindingFlags.Public | BindingFlags.Instance) is PropertyInfo creationPropertiesProperty) - { - object creationProperties = creationPropertiesProperty.GetValue(webView2); - - if (creationProperties?.GetType().GetProperty("AdditionalBrowserArguments", BindingFlags.Public | BindingFlags.Instance) is PropertyInfo additionalBrowserArgumentsProperty) - { - string additionalBrowserArguments = (additionalBrowserArgumentsProperty.GetValue(creationProperties) as string) ?? string.Empty; - additionalBrowserArgumentsProperty.SetValue(creationProperties, additionalBrowserArguments + " --enable-features=WebContentsForceDark"); - } - } - } - } } private static void InitializeResources() diff --git a/QuickLook.Plugin/QuickLook.Plugin.FontViewer/WebfontPanel.cs b/QuickLook.Plugin/QuickLook.Plugin.FontViewer/WebfontPanel.cs index c0a63bf0c..5ff3b9297 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.FontViewer/WebfontPanel.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.FontViewer/WebfontPanel.cs @@ -43,14 +43,6 @@ static WebfontPanel() InitializeResources(); } - public WebfontPanel() - { - if (OSThemeHelper.AppsUseDarkTheme()) - { - _webView.CreationProperties.AdditionalBrowserArguments = "--enable-features=WebContentsForceDark"; - } - } - protected static void InitializeResources() { if (_resources.Any()) return; diff --git a/QuickLook.Plugin/QuickLook.Plugin.HtmlViewer/WebpagePanel.cs b/QuickLook.Plugin/QuickLook.Plugin.HtmlViewer/WebpagePanel.cs index b0004f107..ac8009049 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.HtmlViewer/WebpagePanel.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.HtmlViewer/WebpagePanel.cs @@ -54,10 +54,7 @@ protected virtual void InitializeComponent() { _webView = new WebView2 { - CreationProperties = new CoreWebView2CreationProperties - { - UserDataFolder = Path.Combine(SettingHelper.LocalDataPath, @"WebView2_Data\"), - }, + CreationProperties = CreateCreationProperties(), // Prevent white flash in dark mode DefaultBackgroundColor = OSThemeHelper.AppsUseDarkTheme() ? Color.FromArgb(255, 32, 32, 32) : Color.White, @@ -68,6 +65,14 @@ protected virtual void InitializeComponent() Content = _webView; } + protected static CoreWebView2CreationProperties CreateCreationProperties() + { + return new CoreWebView2CreationProperties + { + UserDataFolder = Path.Combine(SettingHelper.LocalDataPath, @"WebView2_Data\"), + }; + } + public void NavigateToFile(string path) { try @@ -168,16 +173,45 @@ protected virtual void Webview_NavigationStarting(object sender, CoreWebView2Nav protected virtual void WebView_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e) { - _webView.DefaultBackgroundColor = Color.White; // Reset to white after page load to match expected default behavior + if (_webView != null) + _webView.DefaultBackgroundColor = Color.White; // Reset to white after page load to match expected default behavior } protected virtual void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e) { - if (e.IsSuccess) + if (!e.IsSuccess) { - _webView.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All); - _webView.CoreWebView2.WebResourceRequested += WebView_WebResourceRequested; + ProcessHelper.WriteLog($"WebView2 initialization failed: {e.InitializationException}"); + + // Remove the half-initialized control from the visual tree before + // its window procedure can receive messages with a null COM + // interface, then dispose it and show the fallback action. + Content = null; + _webView.CoreWebView2InitializationCompleted -= WebView_CoreWebView2InitializationCompleted; + try + { + _webView.Dispose(); + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to dispose WebView2: {ex.Message}"); + } + _webView = null; + Content = CreateDownloadButton(); + return; } + + _webView.CoreWebView2.Profile.PreferredColorScheme = OSThemeHelper.AppsUseDarkTheme() + ? CoreWebView2PreferredColorScheme.Dark + : CoreWebView2PreferredColorScheme.Light; + _webView.CoreWebView2.ProcessFailed += WebView_ProcessFailed; + _webView.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All); + _webView.CoreWebView2.WebResourceRequested += WebView_WebResourceRequested; + } + + protected virtual void WebView_ProcessFailed(object sender, CoreWebView2ProcessFailedEventArgs e) + { + ProcessHelper.WriteLog($"WebView2 process failed: {e.ProcessFailedKind}; reason: {e.Reason}; exit code: {e.ExitCode}"); } protected virtual void WebView_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs args) @@ -239,7 +273,7 @@ protected virtual void WebView_WebResourceRequested(object sender, CoreWebView2W } } - public void Dispose() + public virtual void Dispose() { _webView?.Dispose(); _webView = null; diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/PlantUml/PuImagePanel.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/PlantUml/PuImagePanel.cs index 975f0caa8..6b5f93813 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/PlantUml/PuImagePanel.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/PlantUml/PuImagePanel.cs @@ -17,7 +17,6 @@ using Microsoft.Web.WebView2.Core; using QuickLook.Common.Helpers; -using QuickLook.Plugin.HtmlViewer; using QuickLook.Plugin.ImageViewer.Webview.Svg; using System; using System.IO; @@ -32,30 +31,6 @@ public class PuImagePanel : SvgImagePanel { private string _puContent; - public PuImagePanel() - { - if (OSThemeHelper.AppsUseDarkTheme()) - { - // Invoke using reflection: WebView2.CreationProperties.AdditionalBrowserArguments - // This approach allows the library to avoid direct dependency on WebView2 - if (typeof(WebpagePanel).GetField("_webView", BindingFlags.NonPublic | BindingFlags.Instance) is FieldInfo fieldInfo) - { - object webView2 = fieldInfo.GetValue(this); - - if (webView2?.GetType().GetProperty("CreationProperties", BindingFlags.Public | BindingFlags.Instance) is PropertyInfo creationPropertiesProperty) - { - object creationProperties = creationPropertiesProperty.GetValue(webView2); - - if (creationProperties?.GetType().GetProperty("AdditionalBrowserArguments", BindingFlags.Public | BindingFlags.Instance) is PropertyInfo additionalBrowserArgumentsProperty) - { - string additionalBrowserArguments = (additionalBrowserArgumentsProperty.GetValue(creationProperties) as string) ?? string.Empty; - additionalBrowserArgumentsProperty.SetValue(creationProperties, additionalBrowserArguments + "--enable-features=WebContentsForceDark"); - } - } - } - } - } - public override void Preview(string path) { bool consented = SettingHelper.Get("PlantUMLOnlineConsented", false, "QuickLook.Plugin.ImageViewer"); @@ -77,7 +52,7 @@ public override void Preview(string path) string deniedMsg = TranslationHelper.Get("PU_OnlineDeniedMessage", translationFile, failsafe: "Network access was denied. PlantUML preview requires an internet connection."); NavigateToHtml( - $"" + + $"" + $"

{HttpUtility.HtmlEncode(deniedMsg)}

"); return; } diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/Svg/SvgImagePanel.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/Svg/SvgImagePanel.cs index e829611e1..239ed53f4 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/Svg/SvgImagePanel.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/Svg/SvgImagePanel.cs @@ -1,4 +1,4 @@ -// Copyright © 2017-2026 QL-Win Contributors +// Copyright © 2017-2026 QL-Win Contributors // // This file is part of QuickLook program. // @@ -18,7 +18,9 @@ using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.Wpf; using QuickLook.Common.Helpers; +using QuickLook.Common.Plugin; using QuickLook.Plugin.HtmlViewer; +using QuickLook.Plugin.ImageViewer.Helpers; using System; using System.Collections.Generic; using System.Diagnostics; @@ -37,6 +39,10 @@ public class SvgImagePanel : WebpagePanel, IWebImagePanel protected const string _resourcePrefix = "QuickLook.Plugin.ImageViewer.Resources."; protected internal static readonly Dictionary _resources = []; protected byte[] _homePage; + private string _path; + private ImagePanel _fallbackPanel; + + public ContextObject ContextObject { get; set; } public object ObjectForScripting { @@ -62,10 +68,7 @@ protected override void InitializeComponent() { _webView = new WebView2() { - CreationProperties = new CoreWebView2CreationProperties - { - UserDataFolder = Path.Combine(SettingHelper.LocalDataPath, @"WebView2_Data\"), - }, + CreationProperties = CreateCreationProperties(), DefaultBackgroundColor = Color.Transparent, }; _webView.CoreWebView2InitializationCompleted += WebView_CoreWebView2InitializationCompleted; @@ -95,6 +98,7 @@ protected static void InitializeResources() public virtual void Preview(string path) { + _path = path; FallbackPath = Path.GetDirectoryName(path); ObjectForScripting ??= new ScriptHandler(path); @@ -103,6 +107,44 @@ public virtual void Preview(string path) NavigateToUri(new Uri("file://quicklook/")); } + protected override void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e) + { + base.WebView_CoreWebView2InitializationCompleted(sender, e); + + if (!e.IsSuccess) + ShowImageMagickFallback(); + } + + private void ShowImageMagickFallback() + { + if (_fallbackPanel != null || string.IsNullOrEmpty(_path) || ContextObject == null) + return; + + try + { + var panel = new ImagePanel(ContextObject, new MetaProvider(_path)) + { + ImageUriSource = ImageHelper.FilePathToFileUrl(_path) + }; + + _fallbackPanel = panel; + Content = panel; + ContextObject.IsBusy = false; + ProcessHelper.WriteLog($"Fell back to ImageMagick for the SVG preview: {_path}"); + } + catch (Exception ex) + { + ProcessHelper.WriteLog($"Failed to render the SVG with ImageMagick: {ex}"); + } + } + + public override void Dispose() + { + _fallbackPanel?.Dispose(); + _fallbackPanel = null; + base.Dispose(); + } + protected override void WebView_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs args) { Debug.WriteLine($"[{args.Request.Method}] {args.Request.Uri}"); diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/WebHandler.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/WebHandler.cs index 39edb5bbc..b07438504 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/WebHandler.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Webview/WebHandler.cs @@ -122,7 +122,7 @@ public static bool TryView(string path, ContextObject context, IWebMetaProvider ipWeb = ext switch { - ".svg" => new SvgImagePanel(), + ".svg" => new SvgImagePanel { ContextObject = context }, ".svga" => new SvgaImagePanel(metaWeb), ".lottie" or ".json" => new LottieImagePanel(), ".tgs" => new TgsImagePanel(),