Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FileExplorerPreview] Move everything from WebBrowser to WebView2 #17588

Merged
merged 12 commits into from
Apr 14, 2022
4 changes: 2 additions & 2 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ APPICON
appid
appium
APPLASTZONE
applets
Applets
Applicationcan
applicationframehost
applog
Expand Down Expand Up @@ -303,7 +303,7 @@ constexpr
contentdialog
contentfiles
CONTEXTHELP
CONTEXTMENU
contextmenu
CONTEXTMENUHANDLER
CONTROLL
CONTROLPARENT
Expand Down
1 change: 0 additions & 1 deletion src/codeAnalysis/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
[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")]

// Non general suppressions
[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)")]
[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)")]
[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)")]
[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)")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
[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")]

// Non general suppressions
[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)")]
[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)")]
[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)")]
[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)")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<PropertyGroup>
<ProjectGuid>{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}</ProjectGuid>
<RootNamespace>Microsoft.PowerToys.PreviewHandler.Markdown</RootNamespace>
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net6.0-windows10.0.18362.0</TargetFramework>
<EnableComHosting>true</EnableComHosting>
<IntermediateOutputPath>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(AssemblyName)\</IntermediateOutputPath>
<AssemblyName>PowerToys.MarkdownPreviewHandler</AssemblyName>
Expand Down Expand Up @@ -45,6 +45,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1150.38" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
using System;
using System.Drawing;
using System.IO.Abstractions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Common;
using Markdig;
using Microsoft.PowerToys.PreviewHandler.Markdown.Properties;
using Microsoft.PowerToys.PreviewHandler.Markdown.Telemetry.Events;
using Microsoft.PowerToys.Telemetry;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
using PreviewHandlerCommon;
using Windows.System;

namespace Microsoft.PowerToys.PreviewHandler.Markdown
{
Expand Down Expand Up @@ -53,13 +58,40 @@ public class MarkdownPreviewHandlerControl : FormHandlerControl
/// <summary>
/// Extended Browser Control to display markdown html.
/// </summary>
private WebBrowserExt _browser;
private WebView2 _browser;

/// <summary>
/// WebView2 Environment
/// </summary>
private CoreWebView2Environment _webView2Environment;

/// <summary>
/// Name of the virtual host
/// </summary>
public const string VirtualHostName = "PowerToysLocalMarkdown";

/// <summary>
/// True if external image is blocked, false otherwise.
/// </summary>
private bool _infoBarDisplayed;

/// <summary>
/// Gets the path of the current assembly.
/// </summary>
/// <remarks>
/// Source: https://stackoverflow.com/a/283917/14774889
/// </remarks>
public static string AssemblyDirectory
{
get
{
string codeBase = Assembly.GetExecutingAssembly().Location;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}

/// <summary>
/// Initializes a new instance of the <see cref="MarkdownPreviewHandlerControl"/> class.
/// </summary>
Expand Down Expand Up @@ -103,25 +135,52 @@ public override void DoPreview<T>(T dataSource)
string parsedMarkdown = Markdig.Markdown.ToHtml(fileText, pipeline);
string markdownHTML = $"{htmlHeader}{parsedMarkdown}{htmlFooter}";

InvokeOnControlThread(() =>
_browser = new WebView2()
{
_browser = new WebBrowserExt
{
DocumentText = markdownHTML,
Dock = DockStyle.Fill,
IsWebBrowserContextMenuEnabled = false,
ScriptErrorsSuppressed = true,
ScrollBarsEnabled = true,
AllowNavigation = false,
};
Controls.Add(_browser);

if (_infoBarDisplayed)
Dock = DockStyle.Fill,
};

_browser.NavigationStarting += async (object sender, CoreWebView2NavigationStartingEventArgs args) =>
{
if (args.Uri != null && args.IsUserInitiated)
{
_infoBar = GetTextBoxControl(Resources.BlockedImageInfoText);
Resize += FormResized;
Controls.Add(_infoBar);
args.Cancel = true;
await Launcher.LaunchUriAsync(new Uri(args.Uri));
}
};

InvokeOnControlThread(() =>
{
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
webView2EnvironmentAwaiter = CoreWebView2Environment
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\MarkdownPreview-Temp")
.ConfigureAwait(true).GetAwaiter();
webView2EnvironmentAwaiter.OnCompleted(() =>
{
InvokeOnControlThread(async () =>
{
try
{
_webView2Environment = webView2EnvironmentAwaiter.GetResult();
await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true);
await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});");
_browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
_browser.NavigateToString(markdownHTML);
Controls.Add(_browser);

if (_infoBarDisplayed)
{
_infoBar = GetTextBoxControl(Resources.BlockedImageInfoText);
Resize += FormResized;
Controls.Add(_infoBar);
}
}
catch (NullReferenceException)
{
}
});
});
});

PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewed());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1108.44" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1150.38" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
75 changes: 62 additions & 13 deletions src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using Common;
using Common.Utilities;
using Microsoft.PowerToys.PreviewHandler.Svg.Telemetry.Events;
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
Expand All @@ -22,9 +26,36 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
public class SvgPreviewControl : FormHandlerControl
{
/// <summary>
/// Extended Browser Control to display Svg.
/// WebView2 Control to display Svg.
/// </summary>
private WebBrowserExt _browser;
private WebView2 _browser;

/// <summary>
/// WebView2 Environment
/// </summary>
private CoreWebView2Environment _webView2Environment;

/// <summary>
/// Name of the virtual host
/// </summary>
public const string VirtualHostName = "PowerToysLocalSvg";

/// <summary>
/// Gets the path of the current assembly.
/// </summary>
/// <remarks>
/// Source: https://stackoverflow.com/a/283917/14774889
/// </remarks>
public static string AssemblyDirectory
{
get
{
string codeBase = Assembly.GetExecutingAssembly().Location;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}

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

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

AddBrowserControl(svgData);
AddWebViewControl(svgData);
Resize += FormResized;
base.DoPreview(dataSource);
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed());
Expand Down Expand Up @@ -129,19 +159,38 @@ private void FormResized(object sender, EventArgs e)
}

/// <summary>
/// Adds a Web Browser Control to Control Collection.
/// Adds a WebView2 Control to Control Collection.
/// </summary>
/// <param name="svgData">Svg to display on Browser Control.</param>
private void AddBrowserControl(string svgData)
private void AddWebViewControl(string svgData)
{
_browser = new WebBrowserExt();
_browser.DocumentText = svgData;
_browser = new WebView2();
_browser.Dock = DockStyle.Fill;
_browser.IsWebBrowserContextMenuEnabled = false;
_browser.ScriptErrorsSuppressed = true;
_browser.ScrollBarsEnabled = false;
_browser.AllowNavigation = false;
Controls.Add(_browser);

ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
webView2EnvironmentAwaiter = CoreWebView2Environment
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgPreview-Temp")
.ConfigureAwait(true).GetAwaiter();
webView2EnvironmentAwaiter.OnCompleted(() =>
{
InvokeOnControlThread(async () =>
{
try
{
_webView2Environment = webView2EnvironmentAwaiter.GetResult();
await _browser.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true);
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);
Controls.Add(_browser);
}
catch (NullReferenceException)
{
}
});
});
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1150.38" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public static string AddStyleSVG(string stringSvgData)

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

// 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.
// max-width and max-height not supported. Extra CSS is needed for it to work.
string scaling = $"max-width: {width} ; max-height: {height} ;";
scaling += $" _height:expression(this.scrollHeight > {heightR} ? \" {height}\" : \"auto\"); _width:expression(this.scrollWidth > {widthR} ? \"{width}\" : \"auto\");";

Expand Down