Skip to content

Commit 88517bf

Browse files
authored
[FileExplorerPreview] Move everything from WebBrowser to WebView2 (microsoft#17588)
* Move MarkdownPreviewHandler from WebBrowser to WebView2 * Disable context menu Open links in default browser * Update expect.txt * Move SvgPreviewHandler from WebBrowser to WebView2 * Migrate SvgThumbnailProvider from WebBrowser to WebView2 * Migrate CustomControlTest to WebView2 Remove WebBrowser related stuff * Update tests * Revert GetThumbnail return value Disable javascript dialogs in WebView2 for Svg thumbnail and preview * expect.txt * Increase timeout for Markdown tests * Add sleeps * Add zero check
1 parent cbd362c commit 88517bf

File tree

22 files changed

+451
-556
lines changed

22 files changed

+451
-556
lines changed

.github/actions/spell-check/expect.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ APPICON
6565
appid
6666
appium
6767
APPLASTZONE
68-
applets
68+
Applets
6969
Applicationcan
7070
applicationframehost
7171
applog
@@ -303,7 +303,7 @@ constexpr
303303
contentdialog
304304
contentfiles
305305
CONTEXTHELP
306-
CONTEXTMENU
306+
contextmenu
307307
CONTEXTMENUHANDLER
308308
CONTROLL
309309
CONTROLPARENT

src/codeAnalysis/GlobalSuppressions.cs

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
[assembly: SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly", Scope = "member", Target = "Microsoft.Templates.Core.Locations.TemplatesSynchronization.#SyncStatusChanged", Justification = "Using an Action<object, SyncStatusEventArgs> does not allow the required notation")]
2727

2828
// Non general suppressions
29-
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "The WebBrowser is loading source code to be shown to the user. No localization required.", MessageId = "System.Windows.Controls.WebBrowser.NavigateToString(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Controls.CodeViewer.#UpdateCodeView(System.Func`2<System.String,System.String>,System.String,System.String,System.Boolean)")]
3029
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "This is part of the markdown processing", MessageId = "System.Windows.Documents.Run.#ctor(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Controls.Markdown.#ImageInlineEvaluator(System.Text.RegularExpressions.Match)")]
3130
[assembly: SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We need to have the names of these keys in lowercase to be able to compare with the keys becoming form the template json. ContainsKey does not allow StringComparer specification to IgnoreCase", Scope = "member", Target = "Microsoft.Templates.Core.ITemplateInfoExtensions.#GetQueryableProperties(Microsoft.TemplateEngine.Abstractions.ITemplateInfo)")]
3231
[assembly: SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We need to have the names of these keys in lowercase to be able to compare with the keys becoming form the template json. ContainsKey does not allow StringComparer specification to IgnoreCase", Scope = "member", Target = "Microsoft.Templates.Core.Composition.CompositionQuery.#Match(System.Collections.Generic.IEnumerable`1<Microsoft.Templates.Core.Composition.QueryNode>,Microsoft.Templates.Core.Composition.QueryablePropertyDictionary)")]

src/modules/imageresizer/codeAnalysis/GlobalSuppressions.cs

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
[assembly: SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly", Scope = "member", Target = "Microsoft.Templates.Core.Locations.TemplatesSynchronization.#SyncStatusChanged", Justification = "Using an Action<object, SyncStatusEventArgs> does not allow the required notation")]
2727

2828
// Non general suppressions
29-
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "The WebBrowser is loading source code to be shown to the user. No localization required.", MessageId = "System.Windows.Controls.WebBrowser.NavigateToString(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Controls.CodeViewer.#UpdateCodeView(System.Func`2<System.String,System.String>,System.String,System.String,System.Boolean)")]
3029
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "This is part of the markdown processing", MessageId = "System.Windows.Documents.Run.#ctor(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Controls.Markdown.#ImageInlineEvaluator(System.Text.RegularExpressions.Match)")]
3130
[assembly: SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We need to have the names of these keys in lowercase to be able to compare with the keys becoming form the template json. ContainsKey does not allow StringComparer specification to IgnoreCase", Scope = "member", Target = "Microsoft.Templates.Core.ITemplateInfoExtensions.#GetQueryableProperties(Microsoft.TemplateEngine.Abstractions.ITemplateInfo)")]
3231
[assembly: SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We need to have the names of these keys in lowercase to be able to compare with the keys becoming form the template json. ContainsKey does not allow StringComparer specification to IgnoreCase", Scope = "member", Target = "Microsoft.Templates.Core.Composition.CompositionQuery.#Match(System.Collections.Generic.IEnumerable`1<Microsoft.Templates.Core.Composition.QueryNode>,Microsoft.Templates.Core.Composition.QueryablePropertyDictionary)")]

src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<PropertyGroup>
1616
<ProjectGuid>{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}</ProjectGuid>
1717
<RootNamespace>Microsoft.PowerToys.PreviewHandler.Markdown</RootNamespace>
18-
<TargetFramework>net6.0-windows</TargetFramework>
18+
<TargetFramework>net6.0-windows10.0.18362.0</TargetFramework>
1919
<EnableComHosting>true</EnableComHosting>
2020
<IntermediateOutputPath>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(AssemblyName)\</IntermediateOutputPath>
2121
<AssemblyName>PowerToys.MarkdownPreviewHandler</AssemblyName>
@@ -45,6 +45,7 @@
4545
<PrivateAssets>all</PrivateAssets>
4646
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
4747
</PackageReference>
48+
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1150.38" />
4849
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
4950
<PrivateAssets>all</PrivateAssets>
5051
</PackageReference>

src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs

+76-17
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,19 @@
55
using System;
66
using System.Drawing;
77
using System.IO.Abstractions;
8+
using System.Reflection;
9+
using System.Runtime.CompilerServices;
810
using System.Text.RegularExpressions;
911
using System.Windows.Forms;
1012
using Common;
1113
using Markdig;
1214
using Microsoft.PowerToys.PreviewHandler.Markdown.Properties;
1315
using Microsoft.PowerToys.PreviewHandler.Markdown.Telemetry.Events;
1416
using Microsoft.PowerToys.Telemetry;
17+
using Microsoft.Web.WebView2.Core;
18+
using Microsoft.Web.WebView2.WinForms;
1519
using PreviewHandlerCommon;
20+
using Windows.System;
1621

1722
namespace Microsoft.PowerToys.PreviewHandler.Markdown
1823
{
@@ -53,13 +58,40 @@ public class MarkdownPreviewHandlerControl : FormHandlerControl
5358
/// <summary>
5459
/// Extended Browser Control to display markdown html.
5560
/// </summary>
56-
private WebBrowserExt _browser;
61+
private WebView2 _browser;
62+
63+
/// <summary>
64+
/// WebView2 Environment
65+
/// </summary>
66+
private CoreWebView2Environment _webView2Environment;
67+
68+
/// <summary>
69+
/// Name of the virtual host
70+
/// </summary>
71+
public const string VirtualHostName = "PowerToysLocalMarkdown";
5772

5873
/// <summary>
5974
/// True if external image is blocked, false otherwise.
6075
/// </summary>
6176
private bool _infoBarDisplayed;
6277

78+
/// <summary>
79+
/// Gets the path of the current assembly.
80+
/// </summary>
81+
/// <remarks>
82+
/// Source: https://stackoverflow.com/a/283917/14774889
83+
/// </remarks>
84+
public static string AssemblyDirectory
85+
{
86+
get
87+
{
88+
string codeBase = Assembly.GetExecutingAssembly().Location;
89+
UriBuilder uri = new UriBuilder(codeBase);
90+
string path = Uri.UnescapeDataString(uri.Path);
91+
return Path.GetDirectoryName(path);
92+
}
93+
}
94+
6395
/// <summary>
6496
/// Initializes a new instance of the <see cref="MarkdownPreviewHandlerControl"/> class.
6597
/// </summary>
@@ -103,25 +135,52 @@ public override void DoPreview<T>(T dataSource)
103135
string parsedMarkdown = Markdig.Markdown.ToHtml(fileText, pipeline);
104136
string markdownHTML = $"{htmlHeader}{parsedMarkdown}{htmlFooter}";
105137

106-
InvokeOnControlThread(() =>
138+
_browser = new WebView2()
107139
{
108-
_browser = new WebBrowserExt
109-
{
110-
DocumentText = markdownHTML,
111-
Dock = DockStyle.Fill,
112-
IsWebBrowserContextMenuEnabled = false,
113-
ScriptErrorsSuppressed = true,
114-
ScrollBarsEnabled = true,
115-
AllowNavigation = false,
116-
};
117-
Controls.Add(_browser);
118-
119-
if (_infoBarDisplayed)
140+
Dock = DockStyle.Fill,
141+
};
142+
143+
_browser.NavigationStarting += async (object sender, CoreWebView2NavigationStartingEventArgs args) =>
144+
{
145+
if (args.Uri != null && args.IsUserInitiated)
120146
{
121-
_infoBar = GetTextBoxControl(Resources.BlockedImageInfoText);
122-
Resize += FormResized;
123-
Controls.Add(_infoBar);
147+
args.Cancel = true;
148+
await Launcher.LaunchUriAsync(new Uri(args.Uri));
124149
}
150+
};
151+
152+
InvokeOnControlThread(() =>
153+
{
154+
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
155+
webView2EnvironmentAwaiter = CoreWebView2Environment
156+
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
157+
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\MarkdownPreview-Temp")
158+
.ConfigureAwait(true).GetAwaiter();
159+
webView2EnvironmentAwaiter.OnCompleted(() =>
160+
{
161+
InvokeOnControlThread(async () =>
162+
{
163+
try
164+
{
165+
_webView2Environment = webView2EnvironmentAwaiter.GetResult();
166+
await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true);
167+
await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});");
168+
_browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
169+
_browser.NavigateToString(markdownHTML);
170+
Controls.Add(_browser);
171+
172+
if (_infoBarDisplayed)
173+
{
174+
_infoBar = GetTextBoxControl(Resources.BlockedImageInfoText);
175+
Resize += FormResized;
176+
Controls.Add(_infoBar);
177+
}
178+
}
179+
catch (NullReferenceException)
180+
{
181+
}
182+
});
183+
});
125184
});
126185

127186
PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewed());

src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
<PrivateAssets>all</PrivateAssets>
3232
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3333
</PackageReference>
34-
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1108.44" />
34+
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1150.38" />
3535
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
3636
<PrivateAssets>all</PrivateAssets>
3737
</PackageReference>

src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs

+62-13
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@
55
using System;
66
using System.Drawing;
77
using System.IO;
8+
using System.Reflection;
9+
using System.Runtime.CompilerServices;
810
using System.Runtime.InteropServices.ComTypes;
911
using System.Windows.Forms;
1012
using Common;
1113
using Common.Utilities;
1214
using Microsoft.PowerToys.PreviewHandler.Svg.Telemetry.Events;
1315
using Microsoft.PowerToys.PreviewHandler.Svg.Utilities;
1416
using Microsoft.PowerToys.Telemetry;
17+
using Microsoft.Web.WebView2.Core;
18+
using Microsoft.Web.WebView2.WinForms;
1519
using PreviewHandlerCommon;
1620

1721
namespace Microsoft.PowerToys.PreviewHandler.Svg
@@ -22,9 +26,36 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
2226
public class SvgPreviewControl : FormHandlerControl
2327
{
2428
/// <summary>
25-
/// Extended Browser Control to display Svg.
29+
/// WebView2 Control to display Svg.
2630
/// </summary>
27-
private WebBrowserExt _browser;
31+
private WebView2 _browser;
32+
33+
/// <summary>
34+
/// WebView2 Environment
35+
/// </summary>
36+
private CoreWebView2Environment _webView2Environment;
37+
38+
/// <summary>
39+
/// Name of the virtual host
40+
/// </summary>
41+
public const string VirtualHostName = "PowerToysLocalSvg";
42+
43+
/// <summary>
44+
/// Gets the path of the current assembly.
45+
/// </summary>
46+
/// <remarks>
47+
/// Source: https://stackoverflow.com/a/283917/14774889
48+
/// </remarks>
49+
public static string AssemblyDirectory
50+
{
51+
get
52+
{
53+
string codeBase = Assembly.GetExecutingAssembly().Location;
54+
UriBuilder uri = new UriBuilder(codeBase);
55+
string path = Uri.UnescapeDataString(uri.Path);
56+
return Path.GetDirectoryName(path);
57+
}
58+
}
2859

2960
/// <summary>
3061
/// Text box to display the information about blocked elements from Svg.
@@ -73,7 +104,6 @@ public override void DoPreview<T>(T dataSource)
73104
catch (Exception ex)
74105
#pragma warning restore CA1031 // Do not catch general exception types
75106
{
76-
_browser.ScrollBarsEnabled = true;
77107
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = ex.Message });
78108
}
79109

@@ -90,7 +120,7 @@ public override void DoPreview<T>(T dataSource)
90120
AddTextBoxControl(Properties.Resource.BlockedElementInfoText);
91121
}
92122

93-
AddBrowserControl(svgData);
123+
AddWebViewControl(svgData);
94124
Resize += FormResized;
95125
base.DoPreview(dataSource);
96126
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed());
@@ -129,19 +159,38 @@ private void FormResized(object sender, EventArgs e)
129159
}
130160

131161
/// <summary>
132-
/// Adds a Web Browser Control to Control Collection.
162+
/// Adds a WebView2 Control to Control Collection.
133163
/// </summary>
134164
/// <param name="svgData">Svg to display on Browser Control.</param>
135-
private void AddBrowserControl(string svgData)
165+
private void AddWebViewControl(string svgData)
136166
{
137-
_browser = new WebBrowserExt();
138-
_browser.DocumentText = svgData;
167+
_browser = new WebView2();
139168
_browser.Dock = DockStyle.Fill;
140-
_browser.IsWebBrowserContextMenuEnabled = false;
141-
_browser.ScriptErrorsSuppressed = true;
142-
_browser.ScrollBarsEnabled = false;
143-
_browser.AllowNavigation = false;
144-
Controls.Add(_browser);
169+
170+
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
171+
webView2EnvironmentAwaiter = CoreWebView2Environment
172+
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
173+
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgPreview-Temp")
174+
.ConfigureAwait(true).GetAwaiter();
175+
webView2EnvironmentAwaiter.OnCompleted(() =>
176+
{
177+
InvokeOnControlThread(async () =>
178+
{
179+
try
180+
{
181+
_webView2Environment = webView2EnvironmentAwaiter.GetResult();
182+
await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true);
183+
await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});");
184+
_browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
185+
_browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
186+
_browser.NavigateToString(svgData);
187+
Controls.Add(_browser);
188+
}
189+
catch (NullReferenceException)
190+
{
191+
}
192+
});
193+
});
145194
}
146195

147196
/// <summary>

src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
<PrivateAssets>all</PrivateAssets>
5050
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
5151
</PackageReference>
52+
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1150.38" />
5253
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
5354
<PrivateAssets>all</PrivateAssets>
5455
</PackageReference>

src/modules/previewpane/SvgPreviewHandler/Utilities/SvgPreviewHandlerHelper.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public static string AddStyleSVG(string stringSvgData)
117117

118118
string centering = "position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);";
119119

120-
// Because WebBrowser class is based on IE version that do not support max-width and max-height extra CSS is needed for it to work.
120+
// max-width and max-height not supported. Extra CSS is needed for it to work.
121121
string scaling = $"max-width: {width} ; max-height: {height} ;";
122122
scaling += $" _height:expression(this.scrollHeight > {heightR} ? \" {height}\" : \"auto\"); _width:expression(this.scrollWidth > {widthR} ? \"{width}\" : \"auto\");";
123123

0 commit comments

Comments
 (0)