diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs index 8eb1058c28..6e4fc59452 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs @@ -16,7 +16,6 @@ using Microsoft.PowerToys.PreviewHandler.Svg.Utilities; using Microsoft.PowerToys.Telemetry; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; -using PreviewHandlerCommon; namespace Microsoft.PowerToys.PreviewHandler.Svg { @@ -38,7 +37,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg /// /// Name of the virtual host /// - public const string VirtualHostName = "PowerToysLocalSvg"; + private const string VirtualHostName = "PowerToysLocalSvg"; /// /// Gets the path of the current assembly. @@ -46,7 +45,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg /// /// Source: https://stackoverflow.com/a/283917/14774889 /// - public static string AssemblyDirectory + private static string AssemblyDirectory { get { @@ -67,12 +66,20 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg /// private bool _infoBarAdded; + /// + /// Represent WebView2 user data folder path. + /// + private string _webView2UserDataFolder = System.Environment.GetEnvironmentVariable("USERPROFILE") + + "\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgPreview-Temp"; + /// /// Start the preview on the Control. /// /// Stream reference to access source file. public override void DoPreview(T dataSource) { + CleanupWebView2UserDataFolder(); + string svgData = null; bool blocked = false; @@ -88,9 +95,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg blocked = SvgPreviewHandlerHelper.CheckBlockedElements(svgData); } -#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) -#pragma warning restore CA1031 // Do not catch general exception types { PreviewError(ex, dataSource); return; @@ -100,9 +105,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg { svgData = SvgPreviewHandlerHelper.AddStyleSVG(svgData); } -#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) -#pragma warning restore CA1031 // Do not catch general exception types { PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = ex.Message }); } @@ -125,9 +128,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg base.DoPreview(dataSource); PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed()); } -#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) -#pragma warning restore CA1031 // Do not catch general exception types { PreviewError(ex, dataSource); } @@ -169,8 +170,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg ConfiguredTaskAwaitable.ConfiguredTaskAwaiter webView2EnvironmentAwaiter = CoreWebView2Environment - .CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") + - "\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgPreview-Temp") + .CreateAsync(userDataFolder: _webView2UserDataFolder) .ConfigureAwait(true).GetAwaiter(); webView2EnvironmentAwaiter.OnCompleted(() => { @@ -183,10 +183,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});"); _browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow); _browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; - _browser.NavigateToString(svgData); + + // WebView2.NavigateToString() limitation + // See https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks + // While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes + if (svgData.Length > 1_500_000) + { + string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html"; + File.WriteAllText(filename, svgData); + _browser.Source = new Uri(filename); + } + else + { + _browser.NavigateToString(svgData); + } + Controls.Add(_browser); } - catch (NullReferenceException) + catch (Exception) { } }); @@ -224,5 +238,25 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg AddTextBoxControl(Properties.Resource.SvgNotPreviewedError); base.DoPreview(dataSource); } + + /// + /// Cleanup the previously created tmp html files from svg files bigger than 2MB. + /// + private void CleanupWebView2UserDataFolder() + { + try + { + // Cleanup temp dir + var dir = new DirectoryInfo(_webView2UserDataFolder); + + foreach (var file in dir.EnumerateFiles("*.html")) + { + file.Delete(); + } + } + catch (Exception) + { + } + } } } diff --git a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs index 620cb8627b..b25e9ed4d8 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs +++ b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs @@ -49,7 +49,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg /// /// Name of the virtual host /// - public const string VirtualHostName = "PowerToysLocalSvgThumbnail"; + private const string VirtualHostName = "PowerToysLocalSvgThumbnail"; /// /// Gets the path of the current assembly. @@ -57,7 +57,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg /// /// Source: https://stackoverflow.com/a/283917/14774889 /// - public static string AssemblyDirectory + private static string AssemblyDirectory { get { @@ -68,6 +68,12 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg } } + /// + /// Represent WebView2 user data folder path. + /// + private string _webView2UserDataFolder = System.Environment.GetEnvironmentVariable("USERPROFILE") + + "\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgThumbnailPreview-Temp"; + /// /// Render SVG using WebView2 control, capture the WebView2 /// preview and create Bitmap out of it. @@ -76,6 +82,8 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg /// The maximum thumbnail size, in pixels. public Bitmap GetThumbnail(string content, uint cx) { + CleanupWebView2UserDataFolder(); + if (cx == 0 || cx > MaxThumbnailSize || string.IsNullOrEmpty(content) || !content.Contains("svg")) { return null; @@ -117,8 +125,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg ConfiguredTaskAwaitable.ConfiguredTaskAwaiter webView2EnvironmentAwaiter = CoreWebView2Environment - .CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") + - "\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgThumbnailPreview-Temp") + .CreateAsync(userDataFolder: _webView2UserDataFolder) .ConfigureAwait(true).GetAwaiter(); webView2EnvironmentAwaiter.OnCompleted(async () => { @@ -129,9 +136,22 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});"); _browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow); _browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; - _browser.NavigateToString(wrappedContent); + + // WebView2.NavigateToString() limitation + // See https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks + // While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes + if (wrappedContent.Length > 1_500_000) + { + string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html"; + File.WriteAllText(filename, wrappedContent); + _browser.Source = new Uri(filename); + } + else + { + _browser.NavigateToString(wrappedContent); + } } - catch (NullReferenceException) + catch (Exception) { } }); @@ -249,5 +269,25 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg { GC.SuppressFinalize(this); } + + /// + /// Cleanup the previously created tmp html files from svg files bigger than 2MB. + /// + private void CleanupWebView2UserDataFolder() + { + try + { + // Cleanup temp dir + var dir = new DirectoryInfo(_webView2UserDataFolder); + + foreach (var file in dir.EnumerateFiles("*.html")) + { + file.Delete(); + } + } + catch (Exception) + { + } + } } }