[FileExplorerPreview] Move everything from WebBrowser to WebView2 (#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
This commit is contained in:
Stefan Markovic
2022-04-14 17:27:22 +02:00
committed by GitHub
parent cbd362cef1
commit 88517bfdf7
22 changed files with 454 additions and 559 deletions

View File

@@ -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>
@@ -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>

View File

@@ -5,6 +5,8 @@
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;
@@ -12,7 +14,10 @@ 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
{
@@ -53,13 +58,40 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown
/// <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>
@@ -103,25 +135,52 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown
string parsedMarkdown = Markdig.Markdown.ToHtml(fileText, pipeline);
string markdownHTML = $"{htmlHeader}{parsedMarkdown}{htmlFooter}";
_browser = new WebView2()
{
Dock = DockStyle.Fill,
};
_browser.NavigationStarting += async (object sender, CoreWebView2NavigationStartingEventArgs args) =>
{
if (args.Uri != null && args.IsUserInitiated)
{
args.Cancel = true;
await Launcher.LaunchUriAsync(new Uri(args.Uri));
}
};
InvokeOnControlThread(() =>
{
_browser = new WebBrowserExt
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
webView2EnvironmentAwaiter = CoreWebView2Environment
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\MarkdownPreview-Temp")
.ConfigureAwait(true).GetAwaiter();
webView2EnvironmentAwaiter.OnCompleted(() =>
{
DocumentText = markdownHTML,
Dock = DockStyle.Fill,
IsWebBrowserContextMenuEnabled = false,
ScriptErrorsSuppressed = true,
ScrollBarsEnabled = true,
AllowNavigation = false,
};
Controls.Add(_browser);
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);
}
if (_infoBarDisplayed)
{
_infoBar = GetTextBoxControl(Resources.BlockedImageInfoText);
Resize += FormResized;
Controls.Add(_infoBar);
}
}
catch (NullReferenceException)
{
}
});
});
});
PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewed());

View File

@@ -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>

View File

@@ -5,6 +5,8 @@
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;
@@ -12,6 +14,8 @@ 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
@@ -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.
@@ -73,7 +104,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{
_browser.ScrollBarsEnabled = true;
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = ex.Message });
}
@@ -90,7 +120,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
AddTextBoxControl(Properties.Resource.BlockedElementInfoText);
}
AddBrowserControl(svgData);
AddWebViewControl(svgData);
Resize += FormResized;
base.DoPreview(dataSource);
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed());
@@ -129,19 +159,38 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
}
/// <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>

View File

@@ -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>

View File

@@ -117,7 +117,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg.Utilities
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\");";

View File

@@ -6,13 +6,15 @@ using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using Common.ComInterlop;
using Common.Utilities;
using PreviewHandlerCommon;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
namespace Microsoft.PowerToys.ThumbnailHandler.Svg
{
@@ -22,7 +24,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
[Guid("36B27788-A8BB-4698-A756-DF9F11F64F84")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class SvgThumbnailProvider : IInitializeWithStream, IThumbnailProvider
public class SvgThumbnailProvider : IInitializeWithStream, IThumbnailProvider, IDisposable
{
/// <summary>
/// Gets the stream object to access file.
@@ -35,135 +37,116 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
private const uint MaxThumbnailSize = 10000;
/// <summary>
/// Captures an image representation of browser contents.
/// WebView2 Control to display Svg.
/// </summary>
/// <param name="browser">The WebBrowser instance rendering the SVG.</param>
/// <param name="rectangle">The client rectangle to capture from.</param>
/// <param name="backgroundColor">The default background color to apply.</param>
/// <returns>A Bitmap representing the browser contents.</returns>
public static Bitmap GetBrowserContentImage(WebBrowser browser, Rectangle rectangle, Color backgroundColor)
private WebView2 _browser;
/// <summary>
/// WebView2 Environment
/// </summary>
private CoreWebView2Environment _webView2Environment;
/// <summary>
/// Name of the virtual host
/// </summary>
public const string VirtualHostName = "PowerToysLocalSvgThumbnail";
/// <summary>
/// Gets the path of the current assembly.
/// </summary>
/// <remarks>
/// Source: https://stackoverflow.com/a/283917/14774889
/// </remarks>
public static string AssemblyDirectory
{
Bitmap image = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics graphics = Graphics.FromImage(image))
get
{
IntPtr deviceContextHandle = IntPtr.Zero;
RECT rect = new RECT
{
Left = rectangle.Left,
Top = rectangle.Top,
Right = rectangle.Right,
Bottom = rectangle.Bottom,
};
graphics.Clear(backgroundColor);
try
{
deviceContextHandle = graphics.GetHdc();
IViewObject viewObject = browser?.ActiveXInstance as IViewObject;
viewObject.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, deviceContextHandle, ref rect, IntPtr.Zero, IntPtr.Zero, 0);
}
finally
{
if (deviceContextHandle != IntPtr.Zero)
{
graphics.ReleaseHdc(deviceContextHandle);
}
}
string codeBase = Assembly.GetExecutingAssembly().Location;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
return image;
}
/// <summary>
/// Wrap the SVG markup in HTML with a meta tag to ensure the
/// WebBrowser control is in Edge mode to enable SVG rendering.
/// We also set the padding and margin for the body to zero as
/// there is a default margin of 8.
/// Render SVG using WebView2 control, capture the WebView2
/// preview and create Bitmap out of it.
/// </summary>
/// <param name="content">The content to render.</param>
/// <param name="cx">The maximum thumbnail size, in pixels.</param>
/// <returns>A thumbnail of the rendered content.</returns>
public static Bitmap GetThumbnail(string content, uint cx)
public Bitmap GetThumbnail(string content, uint cx)
{
if (cx > MaxThumbnailSize)
if (cx == 0 || cx > MaxThumbnailSize || string.IsNullOrEmpty(content) || !content.Contains("svg"))
{
return null;
}
Bitmap thumbnail = null;
// Wrap the SVG content in HTML in IE Edge mode so we can ensure
// we render properly.
bool thumbnailDone = false;
string wrappedContent = WrapSVGInHTML(content);
using (WebBrowserExt browser = new WebBrowserExt())
_browser = new WebView2();
_browser.Dock = DockStyle.Fill;
_browser.Visible = true;
_browser.Width = (int)cx;
_browser.Height = (int)cx;
_browser.NavigationCompleted += async (object sender, CoreWebView2NavigationCompletedEventArgs args) =>
{
browser.Dock = DockStyle.Fill;
browser.IsWebBrowserContextMenuEnabled = false;
browser.ScriptErrorsSuppressed = true;
browser.ScrollBarsEnabled = false;
browser.AllowNavigation = false;
browser.Width = (int)cx;
browser.Height = (int)cx;
browser.DocumentText = wrappedContent;
// Wait for the browser to render the content.
while (browser.IsBusy || browser.ReadyState != WebBrowserReadyState.Complete)
var a = await _browser.ExecuteScriptAsync($"document.getElementsByTagName('svg')[0].viewBox;");
if (a != null)
{
Application.DoEvents();
await _browser.ExecuteScriptAsync($"document.getElementsByTagName('svg')[0].style = 'width:100%;height:100%';");
}
// Check size of the rendered SVG.
var svg = browser.Document.GetElementsByTagName("svg").Cast<HtmlElement>().FirstOrDefault();
if (svg != null)
MemoryStream ms = new MemoryStream();
await _browser.CoreWebView2.CapturePreviewAsync(CoreWebView2CapturePreviewImageFormat.Png, ms);
thumbnail = new Bitmap(ms);
if (thumbnail.Width != cx && thumbnail.Height != cx && thumbnail.Width != 0 && thumbnail.Height != 0)
{
var viewBox = svg.GetAttribute("viewbox");
if (viewBox != null)
{
// Update the svg style to override any width or height explicit settings
// Setting to 100% width and height will allow to scale to our intended size
// Otherwise, we would end up with a scaled up blurry image.
svg.Style = "width:100%;height:100%";
// Wait for the browser to render the content.
while (browser.IsBusy || browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
}
// Update the size of the browser control to fit the SVG
// in the visible viewport.
browser.Width = svg.OffsetRectangle.Width;
browser.Height = svg.OffsetRectangle.Height;
// Wait for the browser to render the content.
while (browser.IsBusy || browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
// Capture the image of the SVG from the browser.
thumbnail = GetBrowserContentImage(browser, svg.OffsetRectangle, Color.White);
if (thumbnail.Width != cx && thumbnail.Height != cx)
{
// We are not the appropriate size for caller. Resize now while
// respecting the aspect ratio.
float scale = Math.Min((float)cx / thumbnail.Width, (float)cx / thumbnail.Height);
int scaleWidth = (int)(thumbnail.Width * scale);
int scaleHeight = (int)(thumbnail.Height * scale);
thumbnail = ResizeImage(thumbnail, scaleWidth, scaleHeight);
}
// We are not the appropriate size for caller. Resize now while
// respecting the aspect ratio.
float scale = Math.Min((float)cx / thumbnail.Width, (float)cx / thumbnail.Height);
int scaleWidth = (int)(thumbnail.Width * scale);
int scaleHeight = (int)(thumbnail.Height * scale);
thumbnail = ResizeImage(thumbnail, scaleWidth, scaleHeight);
}
thumbnailDone = true;
};
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
webView2EnvironmentAwaiter = CoreWebView2Environment
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgThumbnailPreview-Temp")
.ConfigureAwait(true).GetAwaiter();
webView2EnvironmentAwaiter.OnCompleted(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(wrappedContent);
}
catch (NullReferenceException)
{
}
});
while (thumbnailDone == false)
{
Application.DoEvents();
}
return thumbnail;
}
/// <summary>
/// Wrap the SVG markup in HTML with a meta tag to ensure the
/// WebBrowser control is in Edge mode to enable SVG rendering.
/// Wrap the SVG markup in HTML with a meta tag to render it
/// using WebView2 control.
/// We also set the padding and margin for the body to zero as
/// there is a default margin of 8.
/// </summary>
@@ -261,5 +244,10 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
}
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
}

View File

@@ -25,6 +25,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>

View File

@@ -2,18 +2,23 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using Microsoft.PowerToys.PreviewHandler.Markdown;
using Microsoft.PowerToys.STATestExtension;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PreviewHandlerCommon;
using Microsoft.Web.WebView2.WinForms;
namespace MarkdownPreviewHandlerUnitTests
{
[STATestClass]
public class MarkdownPreviewHandlerTest
{
private static readonly int TenSecondsInMilliseconds = 10000;
private static readonly int SleepTimeInMilliseconds = 200;
[TestMethod]
public void MarkdownPreviewHandlerControlAddsBrowserToFormWhenDoPreviewIsCalled()
{
@@ -23,9 +28,17 @@ namespace MarkdownPreviewHandlerUnitTests
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithExternalImage.txt");
int beforeTick = Environment.TickCount;
while (markdownPreviewHandlerControl.Controls.Count == 0 && Environment.TickCount < beforeTick + TenSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.AreEqual(2, markdownPreviewHandlerControl.Controls.Count);
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebBrowserExt));
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebView2));
}
}
@@ -38,6 +51,14 @@ namespace MarkdownPreviewHandlerUnitTests
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithExternalImage.txt");
int beforeTick = Environment.TickCount;
while (markdownPreviewHandlerControl.Controls.Count == 0 && Environment.TickCount < beforeTick + TenSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.AreEqual(2, markdownPreviewHandlerControl.Controls.Count);
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox));
@@ -53,6 +74,14 @@ namespace MarkdownPreviewHandlerUnitTests
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithHTMLImageTag.txt");
int beforeTick = Environment.TickCount;
while (markdownPreviewHandlerControl.Controls.Count < 2 && Environment.TickCount < beforeTick + TenSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.AreEqual(2, markdownPreviewHandlerControl.Controls.Count);
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox));
@@ -68,9 +97,17 @@ namespace MarkdownPreviewHandlerUnitTests
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithScript.txt");
int beforeTick = Environment.TickCount;
while (markdownPreviewHandlerControl.Controls.Count == 0 && Environment.TickCount < beforeTick + TenSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.AreEqual(1, markdownPreviewHandlerControl.Controls.Count);
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebBrowserExt));
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebView2));
}
}
@@ -83,14 +120,17 @@ namespace MarkdownPreviewHandlerUnitTests
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithExternalImage.txt");
int beforeTick = Environment.TickCount;
while (markdownPreviewHandlerControl.Controls.Count < 2 && Environment.TickCount < beforeTick + TenSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebBrowserExt));
Assert.IsNotNull(((WebBrowser)markdownPreviewHandlerControl.Controls[0]).DocumentText);
Assert.AreEqual(DockStyle.Fill, ((WebBrowser)markdownPreviewHandlerControl.Controls[0]).Dock);
Assert.AreEqual(false, ((WebBrowser)markdownPreviewHandlerControl.Controls[0]).IsWebBrowserContextMenuEnabled);
Assert.AreEqual(true, ((WebBrowser)markdownPreviewHandlerControl.Controls[0]).ScriptErrorsSuppressed);
Assert.AreEqual(true, ((WebBrowser)markdownPreviewHandlerControl.Controls[0]).ScrollBarsEnabled);
Assert.AreEqual(false, ((WebBrowser)markdownPreviewHandlerControl.Controls[0]).AllowNavigation);
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[0], typeof(WebView2));
Assert.AreEqual(DockStyle.Fill, ((WebView2)markdownPreviewHandlerControl.Controls[0]).Dock);
}
}
@@ -103,6 +143,14 @@ namespace MarkdownPreviewHandlerUnitTests
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithExternalImage.txt");
int beforeTick = Environment.TickCount;
while (markdownPreviewHandlerControl.Controls.Count == 0 && Environment.TickCount < beforeTick + TenSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox));
Assert.IsNotNull(((RichTextBox)markdownPreviewHandlerControl.Controls[1]).Text);

View File

@@ -11,7 +11,7 @@
<ProjectGuid>{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}</ProjectGuid>
<RootNamespace>PreviewPaneUnitTests</RootNamespace>
<AssemblyName>PreviewPaneUnitTests</AssemblyName>
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net6.0-windows10.0.18362.0</TargetFramework>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>

View File

@@ -25,9 +25,6 @@
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
</ItemGroup>
<ItemGroup>
<Compile Update="WebBrowserExtUnitTests.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\codeAnalysis\GlobalSuppressions.cs" Link="GlobalSuppressions.cs" />
<Compile Include="..\STATestClassAttribute.cs" Link="STATestClassAttribute.cs" />

View File

@@ -1,60 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Reflection;
using Common;
using Microsoft.PowerToys.STATestExtension;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PreviewHandlerCommon;
namespace PreviewHandlerCommonUnitTests
{
[STATestClass]
public class WebBrowserExtUnitTests : WebBrowserExt
{
private const string DISPIDAMBIENTDLCONTROL = "[DISPID=-5512]";
[TestMethod]
public void InvokeMemberShouldSetValidFlagsWhenCalledWithValidDispId()
{
// Arrange
var extendedSite = CreateWebBrowserSiteBase() as WebBrowserSiteExt;
// Act
var actualFlags = (int)extendedSite.InvokeMember(DISPIDAMBIENTDLCONTROL, BindingFlags.InvokeMethod, null, null, null, null, null, null);
// Assert
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.PRAGMA_NO_CACHE) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.FORCEOFFLINE) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_CLIENTPULL) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_SCRIPTS) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_JAVA) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_FRAMEDOWNLOAD) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NOFRAMES) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_DLACTIVEXCTLS) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_RUNACTIVEXCTLS) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_BEHAVIORS) >= 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.SILENT) >= 0);
}
[TestMethod]
public void InvokeMemberShouldOnlySetValidFlagsWhenCalledWithValidDispId()
{
// Arrange
var extendedSite = CreateWebBrowserSiteBase() as WebBrowserSiteExt;
// Act
var actualFlags = (int)extendedSite.InvokeMember(DISPIDAMBIENTDLCONTROL, BindingFlags.InvokeMethod, null, null, null, null, null, null);
// Assert
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.VIDEOS) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.BGSOUNDS) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.DOWNLOADONLY) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.RESYNCHRONIZE) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.NO_METACHARSET) == 0);
Assert.IsTrue((actualFlags & (int)WebBrowserDownloadControlFlags.URL_ENCODING_DISABLE_UTF8) == 0);
Assert.IsTrue((actualFlags & (uint)WebBrowserDownloadControlFlags.URL_ENCODING_ENABLE_UTF8) == 0);
}
}
}

View File

@@ -7,12 +7,13 @@ using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Microsoft.PowerToys.PreviewHandler.Svg;
using Microsoft.PowerToys.STATestExtension;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Web.WebView2.WinForms;
using Moq;
using PreviewHandlerCommon;
namespace SvgPreviewHandlerUnitTests
{
@@ -20,6 +21,9 @@ namespace SvgPreviewHandlerUnitTests
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "new Exception() is fine in test projects.")]
public class SvgPreviewControlTests
{
private static readonly int ThreeSecondsInMilliseconds = 3000;
private static readonly int SleepTimeInMilliseconds = 200;
[TestMethod]
public void SvgPreviewControlShouldAddExtendedBrowserControlWhenDoPreviewCalled()
{
@@ -29,14 +33,22 @@ namespace SvgPreviewHandlerUnitTests
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
int beforeTick = Environment.TickCount;
while (svgPreviewControl.Controls.Count == 0 && Environment.TickCount < beforeTick + ThreeSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.AreEqual(1, svgPreviewControl.Controls.Count);
Assert.IsInstanceOfType(svgPreviewControl.Controls[0], typeof(WebBrowserExt));
Assert.IsInstanceOfType(svgPreviewControl.Controls[0], typeof(WebView2));
}
}
[TestMethod]
public void SvgPreviewControlShouldSetDocumentStreamWhenDoPreviewCalled()
public void SvgPreviewControlShouldFillDockForWebView2WhenDoPreviewCalled()
{
// Arrange
using (var svgPreviewControl = new SvgPreviewControl())
@@ -44,80 +56,16 @@ namespace SvgPreviewHandlerUnitTests
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.IsNotNull(((WebBrowser)svgPreviewControl.Controls[0]).DocumentStream);
}
}
int beforeTick = Environment.TickCount;
[TestMethod]
public void SvgPreviewControlShouldDisableWebBrowserContextMenuWhenDoPreviewCalled()
{
// Arrange
using (var svgPreviewControl = new SvgPreviewControl())
{
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
while (svgPreviewControl.Controls.Count == 0 && Environment.TickCount < beforeTick + ThreeSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.AreEqual(false, ((WebBrowser)svgPreviewControl.Controls[0]).IsWebBrowserContextMenuEnabled);
}
}
[TestMethod]
public void SvgPreviewControlShouldFillDockForWebBrowserWhenDoPreviewCalled()
{
// Arrange
using (var svgPreviewControl = new SvgPreviewControl())
{
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(DockStyle.Fill, ((WebBrowser)svgPreviewControl.Controls[0]).Dock);
}
}
[TestMethod]
public void SvgPreviewControlShouldSetScriptErrorsSuppressedPropertyWhenDoPreviewCalled()
{
// Arrange
using (var svgPreviewControl = new SvgPreviewControl())
{
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(true, ((WebBrowser)svgPreviewControl.Controls[0]).ScriptErrorsSuppressed);
}
}
// ToDo: fix unit test
[Ignore]
[TestMethod]
public void SvgPreviewControlShouldSetScrollBarsEnabledPropertyWhenDoPreviewCalled()
{
// Arrange
using (var svgPreviewControl = new SvgPreviewControl())
{
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(true, ((WebBrowser)svgPreviewControl.Controls[0]).ScrollBarsEnabled);
}
}
[TestMethod]
public void SvgPreviewControlShouldDisableAllowNavigationWhenDoPreviewCalled()
{
// Arrange
using (var svgPreviewControl = new SvgPreviewControl())
{
// Act
svgPreviewControl.DoPreview(GetMockStream("<svg></svg>"));
// Assert
Assert.AreEqual(false, ((WebBrowser)svgPreviewControl.Controls[0]).AllowNavigation);
Assert.AreEqual(DockStyle.Fill, ((WebView2)svgPreviewControl.Controls[0]).Dock);
}
}
@@ -134,6 +82,15 @@ namespace SvgPreviewHandlerUnitTests
// Act
svgPreviewControl.DoPreview(mockStream.Object);
int beforeTick = Environment.TickCount;
while (svgPreviewControl.Controls.Count == 0 && Environment.TickCount < beforeTick + ThreeSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
var textBox = svgPreviewControl.Controls[0] as RichTextBox;
// Assert
@@ -159,6 +116,15 @@ namespace SvgPreviewHandlerUnitTests
.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Throws(new Exception());
svgPreviewControl.DoPreview(mockStream.Object);
int beforeTick = Environment.TickCount;
while (svgPreviewControl.Controls.Count == 0 && Environment.TickCount < beforeTick + ThreeSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
var textBox = svgPreviewControl.Controls[0] as RichTextBox;
var incrementParentControlWidth = 5;
var initialParentWidth = svgPreviewControl.Width;
@@ -188,9 +154,17 @@ namespace SvgPreviewHandlerUnitTests
// Act
svgPreviewControl.DoPreview(GetMockStream(svgBuilder.ToString()));
int beforeTick = Environment.TickCount;
while (svgPreviewControl.Controls.Count < 2 && Environment.TickCount < beforeTick + ThreeSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.IsInstanceOfType(svgPreviewControl.Controls[0], typeof(RichTextBox));
Assert.IsInstanceOfType(svgPreviewControl.Controls[1], typeof(WebBrowserExt));
Assert.IsInstanceOfType(svgPreviewControl.Controls[1], typeof(WebView2));
Assert.AreEqual(2, svgPreviewControl.Controls.Count);
}
}
@@ -210,8 +184,16 @@ namespace SvgPreviewHandlerUnitTests
// Act
svgPreviewControl.DoPreview(GetMockStream(svgBuilder.ToString()));
int beforeTick = Environment.TickCount;
while (svgPreviewControl.Controls.Count == 0 && Environment.TickCount < beforeTick + ThreeSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
// Assert
Assert.IsInstanceOfType(svgPreviewControl.Controls[0], typeof(WebBrowserExt));
Assert.IsInstanceOfType(svgPreviewControl.Controls[0], typeof(WebView2));
Assert.AreEqual(1, svgPreviewControl.Controls.Count);
}
}
@@ -227,6 +209,15 @@ namespace SvgPreviewHandlerUnitTests
svgBuilder.AppendLine("\t<script>alert(\"hello\")</script>");
svgBuilder.AppendLine("</svg>");
svgPreviewControl.DoPreview(GetMockStream(svgBuilder.ToString()));
int beforeTick = Environment.TickCount;
while (svgPreviewControl.Controls.Count == 0 && Environment.TickCount < beforeTick + ThreeSecondsInMilliseconds)
{
Application.DoEvents();
Thread.Sleep(SleepTimeInMilliseconds);
}
var textBox = svgPreviewControl.Controls[0] as RichTextBox;
var incrementParentControlWidth = 5;
var initialParentWidth = svgPreviewControl.Width;

View File

@@ -7,6 +7,7 @@ using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Windows.Forms;
using Common.ComInterlop;
using Microsoft.PowerToys.STATestExtension;
using Microsoft.PowerToys.ThumbnailHandler.Svg;
@@ -27,8 +28,12 @@ namespace SvgThumbnailProviderUnitTests
svgBuilder.AppendLine("\t</circle>");
svgBuilder.AppendLine("</svg>");
Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
Assert.IsTrue(thumbnail != null);
SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider();
Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
Assert.IsNotNull(thumbnail);
Assert.IsTrue(thumbnail.Width > 0);
Assert.IsTrue(thumbnail.Height > 0);
}
[TestMethod]
@@ -41,7 +46,8 @@ namespace SvgThumbnailProviderUnitTests
svgBuilder.AppendLine("\t</circle>");
svgBuilder.AppendLine("</svg>");
Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider();
Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
Assert.IsTrue(thumbnail != null);
}
@@ -51,21 +57,24 @@ namespace SvgThumbnailProviderUnitTests
var svgBuilder = new StringBuilder();
svgBuilder.AppendLine("<p>foo</p>");
Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider();
Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
Assert.IsTrue(thumbnail == null);
}
[TestMethod]
public void CheckNoSvgEmptyStringShouldReturnNullBitmap()
{
Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(string.Empty, 256);
SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider();
Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(string.Empty, 256);
Assert.IsTrue(thumbnail == null);
}
[TestMethod]
public void CheckNoSvgNullStringShouldReturnNullBitmap()
{
Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(null, 256);
SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider();
Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(null, 256);
Assert.IsTrue(thumbnail == null);
}
@@ -73,7 +82,8 @@ namespace SvgThumbnailProviderUnitTests
public void CheckZeroSizedThumbnailShouldReturnNullBitmap()
{
string content = "<svg></svg>";
Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(content, 0);
SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider();
Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(content, 0);
Assert.IsTrue(thumbnail == null);
}
@@ -94,7 +104,8 @@ namespace SvgThumbnailProviderUnitTests
svgBuilder.AppendLine("</body>");
svgBuilder.AppendLine("</html>");
Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider();
Bitmap thumbnail = svgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
Assert.IsTrue(thumbnail != null);
}

View File

@@ -23,7 +23,6 @@
<ItemGroup>
<Compile Include="..\..\..\codeAnalysis\GlobalSuppressions.cs" Link="GlobalSuppressions.cs" />
<Compile Update="controls\WebBrowserExt.cs" />
<Compile Update="examplehandler\CustomControlTest.cs" />
<Compile Update="controls\FormHandlerControl.cs" />
</ItemGroup>
@@ -39,6 +38,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>

View File

@@ -120,7 +120,7 @@ namespace Common
this.Controls.Clear();
});
// Call garbage collection at the time of unloading of Preview. This is to mitigate issue with WebBrowser Control not able to dispose properly.
// Call garbage collection at the time of unloading of Preview.
// Which is preventing prevhost.exe to exit at the time of closing File explorer.
// Preview Handlers run in a separate process from PowerToys. This will not affect the performance of other modules.
// Mitigate the following Github issue: https://github.com/microsoft/PowerToys/issues/1468

View File

@@ -1,112 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Common
{
/// <summary>
/// Flags to control download and execution in Web Browser Control.
/// Values of flags are defined in mshtmdid.h in distributed Windows Sdk.
/// </summary>
[Flags]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Interop, keeping stuff in sync")]
public enum WebBrowserDownloadControlFlags : int
{
/// <summary>
/// Images will be downloaded from the server if this flag is set.
/// </summary>
DLIMAGES = 0x00000010,
/// <summary>
/// Videos will be downloaded from the server if this flag is set.
/// </summary>
VIDEOS = 0x00000020,
/// <summary>
/// Background sounds will be downloaded from the server if this flag is set.
/// </summary>
BGSOUNDS = 0x00000040,
/// <summary>
/// Scripts will not be executed.
/// </summary>
NO_SCRIPTS = 0x00000080,
/// <summary>
/// Java applets will not be executed.
/// </summary>
NO_JAVA = 0x00000100,
/// <summary>
/// ActiveX controls will not be executed.
/// </summary>
NO_RUNACTIVEXCTLS = 0x00000200,
/// <summary>
/// ActiveX controls will not be downloaded.
/// </summary>
NO_DLACTIVEXCTLS = 0x00000400,
/// <summary>
/// The page will only be downloaded, not displayed.
/// </summary>
DOWNLOADONLY = 0x00000800,
/// <summary>
/// WebBrowser Control will download and parse a frameSet, but not the individual frame objects within the frameSet.
/// </summary>
NO_FRAMEDOWNLOAD = 0x00001000,
/// <summary>
/// The server will be asked for update status. Cached files will be used if the server indicates that the cached information is up-to-date.
/// </summary>
RESYNCHRONIZE = 0x00002000,
/// <summary>
/// Files will be re-downloaded from the server regardless of the update status of the files.
/// </summary>
PRAGMA_NO_CACHE = 0x00004000,
/// <summary>
/// Behaviors are not downloaded and are disabled in the document.
/// </summary>
NO_BEHAVIORS = 0x00008000,
/// <summary>
/// Character sets specified in meta elements are suppressed.
/// </summary>
NO_METACHARSET = 0x00010000,
/// <summary>
/// The browsing component will disable UTF-8 encoding.
/// </summary>
URL_ENCODING_DISABLE_UTF8 = 0x00020000,
/// <summary>
/// The browsing component will enable UTF-8 encoding.
/// </summary>
URL_ENCODING_ENABLE_UTF8 = 0x00040000,
/// <summary>
/// No Documentation Available.
/// </summary>
NOFRAMES = 0x00080000,
/// <summary>
/// WebBrowser Control always operates in offline mode.
/// </summary>
FORCEOFFLINE = 0x10000000,
/// <summary>
/// No client pull operations will be performed.
/// </summary>
NO_CLIENTPULL = 0x20000000,
/// <summary>
/// No user interface will be displayed during downloads.
/// </summary>
SILENT = 0x40000000,
}
}

View File

@@ -1,142 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Reflection;
using System.Windows.Forms;
using Common;
namespace PreviewHandlerCommon
{
/// <summary>
/// Customized the WebBrowser to get control over what it downloads, displays and executes.
/// </summary>
public class WebBrowserExt : WebBrowser
{
/// <inheritdoc/>
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
// Returns instance of WebBrowserSiteExt.
return new WebBrowserSiteExt(this);
}
/// <summary>
/// Extend the WebBrowserSite with IDispatch implementation to handle the DISPID_AMBIENT_DLCONTROL.
/// More details: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa770041(v=vs.85)?redirectedfrom=MSDN#controlling-download-and-execution.
/// </summary>
protected class WebBrowserSiteExt : WebBrowserSite, IReflect
{
// Dispid of DISPID_AMBIENT_DLCONTROL is defined in MsHtmdid.h header file in distributed Windows Sdk component.
private const string DISPIDAMBIENTDLCONTROL = "[DISPID=-5512]";
private WebBrowserExt browserExtControl;
/// <summary>
/// Initializes a new instance of the <see cref="WebBrowserSiteExt"/> class.
/// </summary>
/// <param name="browserControl">Browser Control Instance pass to the site.</param>
public WebBrowserSiteExt(WebBrowserExt browserControl)
: base(browserControl)
{
this.browserExtControl = browserControl;
}
/// <inheritdoc/>
public Type UnderlyingSystemType
{
get { return this.GetType(); }
}
/// <inheritdoc/>
public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
{
object result;
if (name != null && name.Equals(DISPIDAMBIENTDLCONTROL, StringComparison.Ordinal))
{
// Using InvariantCulture since this is used for web browser configurations
result = Convert.ToInt32(
WebBrowserDownloadControlFlags.DLIMAGES |
WebBrowserDownloadControlFlags.PRAGMA_NO_CACHE |
WebBrowserDownloadControlFlags.FORCEOFFLINE |
WebBrowserDownloadControlFlags.NO_CLIENTPULL |
WebBrowserDownloadControlFlags.NO_SCRIPTS |
WebBrowserDownloadControlFlags.NO_JAVA |
WebBrowserDownloadControlFlags.NO_FRAMEDOWNLOAD |
WebBrowserDownloadControlFlags.NOFRAMES |
WebBrowserDownloadControlFlags.NO_DLACTIVEXCTLS |
WebBrowserDownloadControlFlags.NO_RUNACTIVEXCTLS |
WebBrowserDownloadControlFlags.NO_BEHAVIORS |
WebBrowserDownloadControlFlags.SILENT, CultureInfo.InvariantCulture);
}
else
{
result = GetType().InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
}
return result;
}
/// <inheritdoc/>
public FieldInfo[] GetFields(BindingFlags bindingAttr)
{
return this.GetType().GetFields(bindingAttr);
}
/// <inheritdoc/>
public MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
return this.GetType().GetMethods(bindingAttr);
}
/// <inheritdoc/>
public PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
return this.GetType().GetProperties(bindingAttr);
}
/// <inheritdoc/>
public FieldInfo GetField(string name, BindingFlags bindingAttr)
{
return this.GetType().GetField(name, bindingAttr);
}
/// <inheritdoc/>
public MemberInfo[] GetMember(string name, BindingFlags bindingAttr)
{
return this.GetType().GetMember(name, bindingAttr);
}
/// <inheritdoc/>
public MemberInfo[] GetMembers(BindingFlags bindingAttr)
{
return this.GetType().GetMembers(bindingAttr);
}
/// <inheritdoc/>
public MethodInfo GetMethod(string name, BindingFlags bindingAttr)
{
return this.GetType().GetMethod(name, bindingAttr);
}
/// <inheritdoc/>
public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
{
return this.GetType().GetMethod(name, bindingAttr, binder, types, modifiers);
}
/// <inheritdoc/>
public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
{
return this.GetType().GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
}
/// <inheritdoc/>
public PropertyInfo GetProperty(string name, BindingFlags bindingAttr)
{
return this.GetType().GetProperty(name, bindingAttr);
}
}
}
}

View File

@@ -3,7 +3,12 @@
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
namespace Common
{
@@ -12,6 +17,38 @@ namespace Common
/// </summary>
public class CustomControlTest : FormHandlerControl
{
/// <summary>
/// WebView2 Control to display Svg.
/// </summary>
private WebView2 _browser;
/// <summary>
/// WebView2 Environment
/// </summary>
private CoreWebView2Environment _webView2Environment;
/// <summary>
/// Name of the virtual host
/// </summary>
public const string VirtualHostName = "PowerToysLocalCustomControlTest";
/// <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>
/// Start the preview on the Control.
/// </summary>
@@ -21,13 +58,41 @@ namespace Common
this.InvokeOnControlThread(() =>
{
var filePath = dataSource as string;
WebBrowser browser = new WebBrowser();
browser.DocumentText = "Test";
browser.Navigate(new Uri(filePath));
browser.Dock = DockStyle.Fill;
browser.IsWebBrowserContextMenuEnabled = false;
this.Controls.Add(browser);
_browser = new WebView2();
_browser.Dock = DockStyle.Fill;
_browser.Visible = true;
_browser.NavigationCompleted += (object sender, CoreWebView2NavigationCompletedEventArgs args) =>
{
// Put here logic needed after WebView2 control is done navigating to url/page
};
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
webView2EnvironmentAwaiter = CoreWebView2Environment
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\CustomControlTest-Temp")
.ConfigureAwait(true).GetAwaiter();
webView2EnvironmentAwaiter.OnCompleted(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);
// Navigate to page represented as a string
_browser.NavigateToString("Test");
// Or navigate to Uri
_browser.Source = new Uri(filePath);
}
catch (NullReferenceException)
{
}
});
this.Controls.Add(_browser);
base.DoPreview(dataSource);
});
}