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(),