Merge branch 'master' into onovotny/add-strongname

This commit is contained in:
Nikola Metulev 2018-07-16 16:14:51 -07:00 коммит произвёл GitHub
Родитель 5379e100ab ada5e40a57
Коммит 212304df2c
28 изменённых файлов: 1438 добавлений и 82 удалений

Просмотреть файл

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
@ -194,6 +195,14 @@ namespace Microsoft.Toolkit.Win32.UI.Controls
/// <see cref="WebViewControlSettings.IsScriptNotifyAllowed" />
bool IsScriptNotifyAllowed { get; set; }
/// <summary>
/// Gets or sets a partition for this process.
/// </summary>
/// <value>The partition of this process.</value>
/// <remarks>Value can be set prior to the component being initialized.</remarks>
/// <see cref="WebViewControlProcessOptions.Partition"/>
string Partition { get; set; }
/// <summary>
/// Gets the <see cref="WebViewControlProcess"/> that the control is hosted in.
/// </summary>
@ -224,6 +233,12 @@ namespace Microsoft.Toolkit.Win32.UI.Controls
/// <value>The version of EDGEHTML.DLL used by <see cref="IWebView" />.</value>
Version Version { get; }
/// <summary>
/// Adds the script to be loaded before any others on the page.
/// </summary>
/// <param name="script">The script.</param>
void AddPreLoadedScript(string script);
/// <summary>
/// Closes this instance.
/// </summary>
@ -337,6 +352,23 @@ namespace Microsoft.Toolkit.Win32.UI.Controls
/// navigation has completed.
void Navigate(string source);
/// <summary>
/// Navigates the web view with the URI with a HTTP request and HTTP headers.
/// </summary>
/// <param name="requestUri">The Uniform Resource Identifier (URI) to send the request.</param>
/// <param name="httpMethod">The HTTP method of the request.</param>
/// <param name="content">Optional content to send with the request.</param>
/// <param name="headers">Optional headers to send with the request.</param>
/// <remarks>
/// This method only supports <see cref="HttpMethod.Get"/> and <see cref="HttpMethod.Post"/> for the <paramref name="httpMethod"/> parameter.
/// </remarks>
/// <seealso cref="Windows.Web.UI.Interop.WebViewControl.NavigateWithHttpRequestMessage"/>
void Navigate(
Uri requestUri,
HttpMethod httpMethod,
string content = null,
IEnumerable<KeyValuePair<string, string>> headers = null);
/// <summary>
/// Loads the specified HTML content relative to the location of the current executable.
/// </summary>
@ -345,6 +377,13 @@ namespace Microsoft.Toolkit.Win32.UI.Controls
/// navigation has completed.
void NavigateToLocal(string relativePath);
/// <summary>
/// Loads local web content at the specified Uniform Resource Identifier (URI) using an <see cref="IUriToStreamResolver"/>.
/// </summary>
/// <param name="relativePath">A path identifying the local HTML content to load.</param>
/// <param name="streamResolver">A <see cref="IUriToStreamResolver"/> instance that converts a Uniform Resource Identifier (URI) into a stream to load.</param>
void NavigateToLocalStreamUri(Uri relativePath, IUriToStreamResolver streamResolver);
/// <summary>
/// Loads the specified HTML content as a new document.
/// </summary>
@ -370,4 +409,4 @@ namespace Microsoft.Toolkit.Win32.UI.Controls
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Stop", Justification = "Method exposed in WinRT type")]
void Stop();
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Storage.Streams;
namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
{
/// <summary>
/// An adapter converting <see cref="IUriToStreamResolver"/> to <see cref="Windows.Web.IUriToStreamResolver"/>.
/// </summary>
internal sealed class GenericUriToStreamResolver : Windows.Web.IUriToStreamResolver, IUriToStreamResolver
{
private readonly IUriToStreamResolver _streamResolver;
public GenericUriToStreamResolver(IUriToStreamResolver streamResolver)
{
_streamResolver = streamResolver ?? throw new ArgumentNullException(nameof(streamResolver));
}
public Stream UriToStream(Uri uri)
{
return _streamResolver.UriToStream(uri);
}
public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
{
var streamOp = UriToStream(uri);
if (streamOp == null)
{
return null;
}
return Task.FromResult(streamOp.AsInputStream()).AsAsyncOperation();
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
{
/// <summary>
/// Provides a method to translate a Uniform Resource I (URI) to a <see cref="Stream"/> for use by the <see cref="IWebView.NavigateToLocal(string)"/> method.
/// </summary>
/// <seealso cref="Windows.Web.IUriToStreamResolver"/>
public interface IUriToStreamResolver
{
Stream UriToStream(Uri uri);
}
}

Просмотреть файл

@ -1,63 +1,88 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.Toolkit.Win32.UI.Controls.Interop.Win32;
using Windows.Foundation;
using Windows.Storage.Streams;
using Windows.Web;
namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
{
internal sealed class UriToLocalStreamResolver : IUriToStreamResolver
[Obsolete("Use NavigateToLocalStreamUri(Uri, IUriToStreamResolver) instead")]
internal class UriToLocalStreamResolver : IUriToStreamResolver
{
public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
private readonly string _path;
public UriToLocalStreamResolver()
#pragma warning disable SA1129 // Do not use default value type constructor
: this(Path.GetDirectoryName(UnsafeNativeMethods.GetModuleFileName(new HandleRef())))
#pragma warning restore SA1129 // Do not use default value type constructor
{
}
protected UriToLocalStreamResolver(string path)
{
_path = path;
}
public Stream UriToStream(Uri uri)
{
var fullPath = ConvertToPath(uri);
// TODO: Make this proper async all the way through
#pragma warning disable SCS0018 // Path traversal: injection possible in {1} argument passed to '{0}'
return File.Open(fullPath, FileMode.Open, FileAccess.Read);
#pragma warning restore SCS0018 // Path traversal: injection possible in {1} argument passed to '{0}'
}
private string ConvertToPath(Uri uri)
{
if (uri == null)
{
throw new ArgumentNullException(nameof(uri));
}
var path = uri.AbsolutePath;
return GetContentAsync(path).AsAsyncOperation();
}
// The incoming URI will be in the form of
// ms-local-stream://microsoft.win32webviewhost_xxxxxxxx_yyyyyyyyyyyyyy/content.htm
// We are only interested in the items after the application identity (x) and guid (y), e.g. "/content.htm"
// Since we must not read content from web view host
var path = UriHelper.RelativeUriToString(uri);
private bool IsRelative(string path)
{
return path != null && path[0] == '/';
}
private static readonly Lazy<string> BasePath = new Lazy<string>(() =>
#pragma warning disable SA1129 // Do not use default value type constructor
Path.GetDirectoryName(UnsafeNativeMethods.GetModuleFileName(new HandleRef())));
#pragma warning restore SA1129 // Do not use default value type constructor
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
private async Task<IInputStream> GetContentAsync(string path)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
if (IsRelative(path))
if (string.IsNullOrEmpty(path))
{
// Clean up path
while (path[0] == '/')
{
path = path.TrimStart('/');
}
// Translate forward slash into backslash for use in file paths
path = path.Replace(@"/", @"\");
var fullPath = Path.Combine(BasePath.Value, path);
// TODO: Make this proper async all the way through
return File.Open(fullPath, FileMode.Open, FileAccess.Read).AsInputStream();
throw new ArgumentException(DesignerUI.E_WEBVIEW_INVALID_URI, nameof(uri));
}
throw new NotSupportedException();
// Clean up path
path = path.TrimStart(Path.AltDirectorySeparatorChar);
if (string.IsNullOrEmpty(path))
{
// After stripping away the separator chars, we have nothing left
// e.g. uri was "ms-local-stream://microsoft.win32webviewhost_xxxxxxxx_yyyyyyyyyyyyyy/"
throw new ArgumentException(DesignerUI.E_WEBVIEW_INVALID_URI, nameof(uri));
}
// This should never be the case, but guard just in case
if (PathUtilities.IsAbsolute(path))
{
throw new ArgumentException(DesignerUI.E_WEBVIEW_INVALID_URI, nameof(uri));
}
// Translate forward slash into backslash for use in file paths
path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
var fullPath = Path.Combine(_path, path);
// At this point there should be no relative paths (e.g. ../../file.htm)
// This check is the last guard against potential path/directory traversal attack
if (fullPath.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
{
throw new ArgumentOutOfRangeException(nameof(uri));
}
return fullPath;
}
}
}

Просмотреть файл

@ -10,7 +10,7 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
/// <seealso cref="Windows.Web.UI.Interop.WebViewControlAcceleratorKeyPressedEventArgs"/>
/// <summary>This class provides information for the <see cref="IWebView.AcceleratorKeyPressed"/> event.</summary>
/// <seealso cref="System.EventArgs" />
public class WebViewControlAcceleratorKeyPressedEventArgs : EventArgs
public sealed class WebViewControlAcceleratorKeyPressedEventArgs : EventArgs
{
[SecurityCritical]
private readonly Windows.Web.UI.Interop.WebViewControlAcceleratorKeyPressedEventArgs _args;

Просмотреть файл

@ -8,8 +8,9 @@ using System.Diagnostics;
using System.IO;
using System.Security;
using System.Threading.Tasks;
using Windows.Foundation.Metadata;
using Windows.Web;
using Windows.Web.Http;
using Windows.Web.UI;
using Windows.Web.UI.Interop;
@ -42,6 +43,8 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
/// </remarks>
internal sealed class WebViewControlHost : IDisposable
{
private const string LocalContentIdentifier = "LocalContent";
[SecurityCritical]
private WebViewControl _webViewControl;
@ -80,8 +83,12 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
internal event EventHandler<WebViewControlNavigationStartingEventArgs> FrameNavigationStarting = (sender, args) => { };
internal event EventHandler<object> GotFocus = (sender, args) => { };
internal event EventHandler<WebViewControlLongRunningScriptDetectedEventArgs> LongRunningScriptDetected = (sender, args) => { };
internal event EventHandler<object> LostFocus = (sender, args) => { };
internal event EventHandler<WebViewControlMoveFocusRequestedEventArgs> MoveFocusRequested = (sender, args) => { };
internal event EventHandler<WebViewControlNavigationCompletedEventArgs> NavigationCompleted = (sender, args) => { };
@ -282,8 +289,42 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
set;
}
internal void AddPreLoadedScript(string script)
{
if (ApiInformation.IsMethodPresent(
"Windows.Web.UI.Interop.WebViewControl",
"AddPreLoadedScript",
1))
{
_webViewControl?.AddPreLoadedScript(script);
}
}
internal Uri BuildStream(string contentIdentifier, string relativePath)
{
if (string.IsNullOrWhiteSpace(contentIdentifier))
{
throw new ArgumentNullException(nameof(contentIdentifier));
}
if (string.IsNullOrWhiteSpace(relativePath))
{
throw new ArgumentNullException(nameof(relativePath));
}
// If not passing a relative path, the method faults. No exception is thrown, the application just fails fast
// Until that issue resolved, add our own error checking
if (PathUtilities.IsAbsolute(relativePath))
{
throw new ArgumentOutOfRangeException(nameof(relativePath), DesignerUI.E_WEBVIEW_INVALID_URI);
}
// The content identifier is used in conjunction with the application identity to create a guid. The
// guid is appended to the win32webviewhost identity and a ms-local-stream URI is created.
// Given a relative path of "/content.htm" the following is generated:
// ms-local-stream://microsoft.win32webviewhost_xxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyy//content.htm
// If there is relative navigation items (e.g. "..\") they are resolved. URI will ALWAYS be relative to
// the application container, e.g. "..\..\..\..\..\..\file" will resolve to "/file"
return _webViewControl?.BuildLocalStreamUri(contentIdentifier, relativePath);
}
@ -488,16 +529,109 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
Navigate(UriHelper.StringToUri(source));
}
[Obsolete("Use NavigateToLocalStreamUri(Uri, IUriToStreamResolver) instead")]
internal void NavigateToLocal(string relativePath)
{
var uri = BuildStream("LocalContent", relativePath);
var resolver = new UriToLocalStreamResolver();
NavigateToLocalStreamUri(uri, resolver);
var relativeUri = UriHelper.StringToRelativeUri(relativePath);
NavigateToLocalStreamUri(relativeUri, new UriToLocalStreamResolver());
}
internal void NavigateToLocalStreamUri(Uri source, IUriToStreamResolver streamResolver)
internal void NavigateToLocalStreamUri(Uri relativePath, IUriToStreamResolver streamResolver)
{
_webViewControl?.NavigateToLocalStreamUri(source, streamResolver);
if (relativePath == null)
{
throw new ArgumentNullException(nameof(relativePath));
}
if (relativePath.IsAbsoluteUri)
{
throw new ArgumentOutOfRangeException(nameof(relativePath), DesignerUI.E_WEBVIEW_INVALID_URI);
}
if (streamResolver == null)
{
throw new ArgumentNullException(nameof(streamResolver));
}
Windows.Web.IUriToStreamResolver AsWindowsRuntimeUriToStreamResolver(IUriToStreamResolver streamResolverInterop)
{
// Check to see if the stream resolver is actually a wrapper of a WinRT stream resolver
if (streamResolverInterop is Windows.Web.IUriToStreamResolver streamResolverAdapter)
{
return streamResolverAdapter;
}
if (streamResolverInterop is GenericUriToStreamResolver genericAdapter)
{
return genericAdapter;
}
// We have an unwrapped stream resolver
return new GenericUriToStreamResolver(streamResolver);
}
var uri = BuildStream(LocalContentIdentifier, UriHelper.UriToString(relativePath));
_webViewControl?.NavigateToLocalStreamUri(uri, AsWindowsRuntimeUriToStreamResolver(streamResolver));
}
internal void Navigate(
Uri requestUri,
System.Net.Http.HttpMethod method,
string content = null,
IEnumerable<KeyValuePair<string, string>> headers = null)
{
if (requestUri == null)
{
throw new ArgumentNullException(nameof(requestUri));
}
if (method == null)
{
throw new ArgumentNullException(nameof(method));
}
// Convert a System.Net.Http.HttpMethod to Windows.Web.Http.HttpMethod
HttpMethod ToHttpMethod(System.Net.Http.HttpMethod httpMethod)
{
if (System.Net.Http.HttpMethod.Get.Equals(httpMethod))
{
return HttpMethod.Get;
}
if (System.Net.Http.HttpMethod.Post.Equals(httpMethod))
{
return HttpMethod.Post;
}
// For compatabilty with WebView.NavigateWithHttpRequestMessage, this only supports POST and GET
throw new ArgumentOutOfRangeException(nameof(httpMethod));
}
var requestMessage = new HttpRequestMessage
{
RequestUri = requestUri,
Method = ToHttpMethod(method)
};
if (content != null)
{
requestMessage.Content = new HttpStringContent(content);
}
if (headers != null)
{
foreach (var header in headers)
{
requestMessage.Headers.Add(header);
}
}
NavigateWithHttpRequestMessage(requestMessage);
}
internal void NavigateWithHttpRequestMessage(HttpRequestMessage requestMessage)
{
_webViewControl?.NavigateWithHttpRequestMessage(requestMessage);
}
/// <exception cref="ArgumentNullException"><paramref name="text"/> is <see langword="null"/></exception>
@ -678,8 +812,19 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
}
}
private void OnGotFocus(object args)
{
var handler = GotFocus;
if (handler != null)
{
handler(this, args);
}
}
private void OnFrameNavigationStarting(IWebViewControl sender, Windows.Web.UI.WebViewControlNavigationStartingEventArgs args) => OnFrameNavigationStarting(args);
private void OnGotFocus(IWebViewControl sender, object args) => OnGotFocus(args);
private void OnLongRunningScriptDetected(WebViewControlLongRunningScriptDetectedEventArgs args)
{
var handler = LongRunningScriptDetected;
@ -691,6 +836,17 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
private void OnLongRunningScriptDetected(IWebViewControl sender, Windows.Web.UI.WebViewControlLongRunningScriptDetectedEventArgs args) => OnLongRunningScriptDetected(args);
private void OnLostFocus(object args)
{
var handler = LostFocus;
if (handler != null)
{
handler(this, args);
}
}
private void OnLostFocus(IWebViewControl sender, object args) => OnLostFocus(args);
private void OnMoveFocusRequested(WebViewControlMoveFocusRequestedEventArgs args)
{
var handler = MoveFocusRequested;
@ -910,6 +1066,16 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
_webViewControl.UnsafeContentWarningDisplaying += OnUnsafeContentWarningDisplaying;
_webViewControl.UnsupportedUriSchemeIdentified += OnUnsupportedUriSchemeIdentified;
_webViewControl.UnviewableContentIdentified += OnUnviewableContentIdentified;
if (ApiInformation.IsEventPresent("Windows.Web.UI.Interop", "GotFocus"))
{
_webViewControl.GotFocus += OnGotFocus;
}
if (ApiInformation.IsEventPresent("Windows.Web.UI.Interop", "LostFocus"))
{
_webViewControl.LostFocus += OnLostFocus;
}
}
[SecurityCritical]
@ -948,6 +1114,16 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
_webViewControl.UnsafeContentWarningDisplaying -= OnUnsafeContentWarningDisplaying;
_webViewControl.UnsupportedUriSchemeIdentified -= OnUnsupportedUriSchemeIdentified;
_webViewControl.UnviewableContentIdentified -= OnUnviewableContentIdentified;
if (ApiInformation.IsEventPresent("Windows.Web.UI.Interop", "GotFocus"))
{
_webViewControl.GotFocus -= OnGotFocus;
}
if (ApiInformation.IsEventPresent("Windows.Web.UI.Interop", "LostFocus"))
{
_webViewControl.LostFocus -= OnLostFocus;
}
}
private void UnsubscribeProcessExited()

Просмотреть файл

@ -7,6 +7,7 @@ using System.Security;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Metadata;
using Windows.Web.UI.Interop;
namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
@ -14,7 +15,7 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
/// <summary>
/// A proxy for <see cref="Windows.Web.UI.Interop.WebViewControlProcess"/>.
/// </summary>
public class WebViewControlProcess
public sealed class WebViewControlProcess
{
[SecurityCritical]
private readonly Windows.Web.UI.Interop.WebViewControlProcess _process;
@ -60,12 +61,50 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
/// <value><c>true</c> if this instance can access the private network; otherwise, <c>false</c>.</value>
public bool IsPrivateNetworkClientServerCapabilityEnabled => _process.IsPrivateNetworkClientServerCapabilityEnabled;
/// <summary>
/// Gets a value indicating the partition of the web view.
/// </summary>
/// <value>The partition.</value>
public string Partition
{
get
{
if (ApiInformation.IsPropertyPresent(
"Windows.Web.UI.Interop.WebViewControlProcessOptions",
"Partition"))
{
return _process.Partition;
}
return string.Empty;
}
}
/// <summary>
/// Gets the process identifier (PID) of the underlying WWAHost.
/// </summary>
/// <value>The process identifier (PID).</value>
public uint ProcessId => _process.ProcessId;
/// <summary>
/// Gets the user agent of the underlying web view
/// </summary>
/// <value>The user agent.</value>
public string UserAgent
{
get
{
if (ApiInformation.IsPropertyPresent(
"Windows.Web.UI.Interop.WebViewControlProcessOptions",
"UserAgent"))
{
return _process.UserAgent;
}
return string.Empty;
}
}
/// <summary>
/// Performs an implicit conversion from <see cref="Windows.Web.UI.Interop.WebViewControlProcess"/> to <see cref="WebViewControlProcess"/>.
/// </summary>

Просмотреть файл

@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using Windows.Foundation.Metadata;
namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
{
@ -13,7 +14,7 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
/// Copy from <see cref="Windows.Web.UI.Interop.WebViewControlProcessOptions"/> to avoid requirement to link Windows.winmd.
/// </remarks>
/// <seealso cref="Windows.Web.UI.Interop.WebViewControlProcessOptions"/>
public class WebViewControlProcessOptions
public sealed class WebViewControlProcessOptions
{
/// <summary>
/// Gets or sets the enterprise identifier for apps that are WIP-enabled.
@ -21,37 +22,83 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
/// <value>The enterprise identifier.</value>
public string EnterpriseId { get; set; }
/// <summary>
/// Gets or sets the partition for the web view.
/// </summary>
/// <value>The partition.</value>
public string Partition { get; set; }
/// <summary>
/// Gets or sets the private network client server capability.
/// </summary>
/// <value>The private network client server capability.</value>
public WebViewControlProcessCapabilityState PrivateNetworkClientServerCapability { get; set; }
/// <summary>
/// Gets or sets the user agent.
/// </summary>
/// <value>The user agent.</value>
public string UserAgent { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="WebViewControlProcessOptions"/> class.
/// </summary>
public WebViewControlProcessOptions()
{
EnterpriseId = string.Empty;
Partition = string.Empty;
UserAgent = string.Empty;
PrivateNetworkClientServerCapability = WebViewControlProcessCapabilityState.Default;
}
/// <summary>
/// Performs an implicit conversion from <see cref="Windows.Web.UI.Interop.WebViewControlProcessOptions"/> to <see cref="WebViewControlProcessOptions"/>.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator WebViewControlProcessOptions(Windows.Web.UI.Interop.WebViewControlProcessOptions options) => ToWinRtWebViewControlProcessOptions(options);
public static Windows.Web.UI.Interop.WebViewControlProcessOptions ToWinRtWebViewControlProcessOptions(WebViewControlProcessOptions options)
{
var retval = new Windows.Web.UI.Interop.WebViewControlProcessOptions();
if (!string.IsNullOrEmpty(options?.EnterpriseId) && !StringComparer.InvariantCulture.Equals(retval.EnterpriseId, options?.EnterpriseId))
{
retval.EnterpriseId = options.EnterpriseId;
}
if (ApiInformation.IsPropertyPresent(
"Windows.Web.UI.Interop.WebViewControlProcessOptions",
"Partition"))
{
if (!string.IsNullOrEmpty(options?.Partition))
{
retval.Partition = options.Partition;
}
}
retval.PrivateNetworkClientServerCapability = (Windows.Web.UI.Interop.WebViewControlProcessCapabilityState)options?.PrivateNetworkClientServerCapability;
if (ApiInformation.IsPropertyPresent(
"Windows.Web.UI.Interop.WebViewControlProcessOptions",
"UserAgent"))
{
if (!string.IsNullOrEmpty(options?.UserAgent))
{
retval.UserAgent = options.UserAgent;
}
}
return retval;
}
/// <summary>
/// Converts this instance to a <seealso cref="Windows.Web.UI.Interop.WebViewControlProcessOptions"/> instance.
/// </summary>
/// <returns>A <seealso cref="Windows.Web.UI.Interop.WebViewControlProcessOptions"/> instance.</returns>
internal Windows.Web.UI.Interop.WebViewControlProcessOptions ToWinRtWebViewControlProcessOptions()
{
var retval = new Windows.Web.UI.Interop.WebViewControlProcessOptions();
if (!string.IsNullOrEmpty(EnterpriseId) && !StringComparer.InvariantCulture.Equals(retval.EnterpriseId, EnterpriseId))
{
retval.EnterpriseId = EnterpriseId;
}
retval.PrivateNetworkClientServerCapability = (Windows.Web.UI.Interop.WebViewControlProcessCapabilityState)PrivateNetworkClientServerCapability;
return retval;
return ToWinRtWebViewControlProcessOptions(this);
}
}
}

Просмотреть файл

@ -14,6 +14,7 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
public const bool IsJavaScriptEnabled = true;
public const bool IsPrivateNetworkEnabled = false;
public const bool IsScriptNotifyEnabled = false;
public const string Partition = "";
public static readonly Uri AboutBlankUri = new Uri(AboutBlank);
}
}

Просмотреть файл

@ -13,9 +13,18 @@
<!-- Need Metadata contract 6.0 or later -->
<!-- First introduced in 17110: Insider Preview (Fast) -->
<TargetPlatformMinVersion>10.0.17110.0</TargetPlatformMinVersion>
<!-- Most people will have RTM 17134: Insider Preview (Fast), Spring Creators (RS4) -->
<TargetPlatformVersion>10.0.17134.0</TargetPlatformVersion>
<!-- Most people will have RTM 17134: April 2018 Update -->
<TargetPlatformMinVersion>10.0.17134.0</TargetPlatformMinVersion>
<!--
When changing this value, ensure the SDK is installed with the build process.
Need to check several files:
- /.vsts-ci.yml
- /.vsts-pr.yml
This also needs to be installed on your local machine. Can do this with PowerShell:
./build/Install-WindowsSDKISO.ps1 17704
-->
<TargetPlatformVersion>10.0.17704.0</TargetPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -32,6 +41,7 @@
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="WindowsBase" />
<PackageReference Include="System.Net.Http" Version="4.0.0" />
<PackageReference Include="System.Runtime.WindowsRuntime" PrivateAssets="All">
<Version>4.0.0</Version>
</PackageReference>

Просмотреть файл

@ -0,0 +1,42 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.IO;
namespace Microsoft.Toolkit.Win32.UI.Controls
{
// Contains path parsing utilities
internal static class PathUtilities
{
// true if the character is the platform directory separator character or the alternate directory separator
public static bool IsDirectorySeparator(char c) => c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar;
public static bool IsAbsolute(string path)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
// "C:\"
if (IsDriveRootedAbsolutePath(path))
{
// Including invalid paths (e.g. "*:\")
return true;
}
// "\\machine\share"
// Including invalid/incomplete UNC paths (e.g. "\\foo")
return path.Length >= 2 &&
IsDirectorySeparator(path[0]) &&
IsDirectorySeparator(path[1]);
}
// true if given path is absolute and starts with a drive specification (e.g. "C:\"); otherwise, false.
private static bool IsDriveRootedAbsolutePath(string path)
{
return path.Length >= 3 && path[1] == Path.VolumeSeparatorChar && IsDirectorySeparator(path[2]);
}
}
}

Просмотреть файл

@ -36,6 +36,20 @@ namespace Microsoft.Toolkit.Win32.UI.Controls
MAX_URL_LENGTH).ToString();
}
internal static string RelativeUriToString(Uri uri)
{
if (uri == null)
{
throw new ArgumentNullException(nameof(uri));
}
return new StringBuilder(
uri.GetComponents(
UriComponents.PathAndQuery,
UriFormat.SafeUnescaped),
MAX_URL_LENGTH).ToString();
}
internal static Uri StringToUri(string source)
{
if (string.IsNullOrEmpty(source))
@ -51,5 +65,21 @@ namespace Microsoft.Toolkit.Win32.UI.Controls
// Unrecognized URI
throw new ArgumentException(DesignerUI.E_WEBVIEW_INVALID_URI);
}
internal static Uri StringToRelativeUri(string source)
{
if (string.IsNullOrEmpty(source))
{
throw new ArgumentNullException(nameof(source));
}
if (Uri.TryCreate(source, UriKind.Relative, out Uri result))
{
return result;
}
// Unrecognized URI
throw new ArgumentException(DesignerUI.E_WEBVIEW_INVALID_URI);
}
}
}

Просмотреть файл

@ -9,6 +9,7 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
@ -90,6 +91,12 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
typeof(WebView),
new PropertyMetadata(WebViewDefaults.AboutBlankUri, PropertyChangedCallback));
private static readonly DependencyProperty PartitionProperty = DependencyProperty.Register(
nameof(Partition),
typeof(string),
typeof(WebView),
new PropertyMetadata(WebViewDefaults.Partition, PropertyChangedCallback));
private WebViewControlProcess _process;
private volatile WebViewControlHost _webViewControl;
@ -424,6 +431,15 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
}
}
/// <inheritdoc />
[StringResourceCategory(Constants.CategoryBehavior)]
[DefaultValue(WebViewDefaults.Partition)]
public string Partition
{
get => (string)GetValue(PartitionProperty);
set => SetValue(PartitionProperty, value);
}
/// <inheritdoc />
[Browsable(false)]
public WebViewControlProcess Process
@ -475,6 +491,14 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
}
}
/// <inheritdoc cref="IWebView.AddPreLoadedScript" />
public void AddPreLoadedScript(string script)
{
VerifyAccess();
Verify.IsNotNull(_webViewControl);
_webViewControl?.AddPreLoadedScript(script);
}
/// <inheritdoc cref="IWebView.Close" />
public override void Close()
{
@ -583,6 +607,26 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
}
/// <inheritdoc />
public void Navigate(
Uri requestUri,
HttpMethod httpMethod,
string content = null,
IEnumerable<KeyValuePair<string, string>> headers = null)
{
VerifyAccess();
do
{
Dispatcher.CurrentDispatcher.DoEvents();
}
while (!_initializationComplete.WaitOne(InitializationBlockingTime));
Verify.IsNotNull(_webViewControl);
_webViewControl.Navigate(requestUri, httpMethod, content, headers);
}
/// <inheritdoc />
[Obsolete("Use NavigateToLocalStreamUri(Uri, IUriToStreamResolver) instead")]
public void NavigateToLocal(string relativePath)
{
VerifyAccess();
@ -597,6 +641,21 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
_webViewControl.NavigateToLocal(relativePath);
}
/// <inheritdoc />
public void NavigateToLocalStreamUri(Uri relativePath, IUriToStreamResolver streamResolver)
{
VerifyAccess();
do
{
Dispatcher.CurrentDispatcher.DoEvents();
}
while (!_initializationComplete.WaitOne(InitializationBlockingTime));
Verify.IsNotNull(_webViewControl);
_webViewControl.NavigateToLocalStreamUri(relativePath, streamResolver);
}
/// <inheritdoc />
public void NavigateToString(string text)
{
@ -645,13 +704,17 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
var enterpriseId = !Dispatcher.CheckAccess()
? Dispatcher.Invoke(() => EnterpriseId)
: EnterpriseId;
var partition = !Dispatcher.CheckAccess()
? Dispatcher.Invoke(() => Partition)
: Partition;
_process = new WebViewControlProcess(new WebViewControlProcessOptions
{
PrivateNetworkClientServerCapability = privateNetworkEnabled
? WebViewControlProcessCapabilityState.Enabled
: WebViewControlProcessCapabilityState.Disabled,
EnterpriseId = enterpriseId
EnterpriseId = enterpriseId,
Partition = partition
});
}
@ -670,7 +733,14 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
Verify.IsNotNull(_webViewControl);
UpdateSize(RenderSize);
if (!Dispatcher.CheckAccess())
{
Dispatcher.Invoke(() => UpdateSize(RenderSize));
}
else
{
UpdateSize(RenderSize);
}
DestroyWindowCore(ChildWindow);
@ -825,6 +895,14 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
throw new InvalidOperationException(DesignerUI.E_CANNOT_CHANGE_AFTER_INIT);
}
}
else if (dependencyPropertyChangedEventArgs.Property.Name == nameof(Partition))
{
Verify.IsFalse(wv.WebViewControlInitialized);
if (wv.WebViewControlInitialized)
{
throw new InvalidOperationException(DesignerUI.E_CANNOT_CHANGE_AFTER_INIT);
}
}
}
}
@ -900,6 +978,11 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
}
}
private void OnGotFocus(object sender, object args)
{
OnGotFocus(new RoutedEventArgs(GotFocusEvent));
}
private void OnLongRunningScriptDetected(object sender, WebViewControlLongRunningScriptDetectedEventArgs args)
{
var handler = LongRunningScriptDetected;
@ -909,6 +992,11 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
}
}
private void OnLostFocus(object sender, object args)
{
OnLostFocus(new RoutedEventArgs(GotFocusEvent));
}
private void OnMoveFocusRequested(object sender, WebViewControlMoveFocusRequestedEventArgs args)
{
var handler = MoveFocusRequested;
@ -1012,7 +1100,9 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
_webViewControl.FrameDOMContentLoaded += OnFrameDOMContentLoaded;
_webViewControl.FrameNavigationCompleted += OnFrameNavigationCompleted;
_webViewControl.FrameNavigationStarting += OnFrameNavigationStarting;
_webViewControl.GotFocus += OnGotFocus;
_webViewControl.LongRunningScriptDetected += OnLongRunningScriptDetected;
_webViewControl.LostFocus += OnLostFocus;
_webViewControl.MoveFocusRequested += OnMoveFocusRequested;
_webViewControl.NavigationCompleted += OnNavigationCompleted;
_webViewControl.NavigationStarting += OnNavigationStarting;
@ -1040,7 +1130,9 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WPF
_webViewControl.FrameDOMContentLoaded -= OnFrameDOMContentLoaded;
_webViewControl.FrameNavigationCompleted -= OnFrameNavigationCompleted;
_webViewControl.FrameNavigationStarting -= OnFrameNavigationStarting;
_webViewControl.GotFocus -= OnGotFocus;
_webViewControl.LongRunningScriptDetected -= OnLongRunningScriptDetected;
_webViewControl.LostFocus -= OnLostFocus;
_webViewControl.MoveFocusRequested -= OnMoveFocusRequested;
_webViewControl.NavigationCompleted -= OnNavigationCompleted;
_webViewControl.NavigationStarting -= OnNavigationStarting;

Просмотреть файл

@ -239,6 +239,11 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
}
}
private void OnGotFocus(object sender, object args)
{
OnGotFocus(EventArgs.Empty);
}
private void OnLongRunningScriptDetected(object sender, WebViewControlLongRunningScriptDetectedEventArgs args)
{
var handler = LongRunningScriptDetected;
@ -248,6 +253,11 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
}
}
private void OnLostFocus(object sender, object args)
{
OnLostFocus(EventArgs.Empty);
}
private void OnMoveFocusRequested(object sender, WebViewControlMoveFocusRequestedEventArgs args)
{
var handler = MoveFocusRequested;

Просмотреть файл

@ -124,7 +124,8 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
PrivateNetworkClientServerCapability = _delayedPrivateNetworkEnabled
? WebViewControlProcessCapabilityState.Enabled
: WebViewControlProcessCapabilityState.Disabled,
EnterpriseId = _delayedEnterpriseId
EnterpriseId = _delayedEnterpriseId,
Partition = _delayedPartition
});
_webViewControl = Process.CreateWebViewControlHost(Handle, ClientRectangle);
SubscribeEvents();
@ -164,7 +165,9 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
_webViewControl.FrameDOMContentLoaded += OnFrameDOMContentLoaded;
_webViewControl.FrameNavigationCompleted += OnFrameNavigationCompleted;
_webViewControl.FrameNavigationStarting += OnFrameNavigationStarting;
_webViewControl.GotFocus += OnGotFocus;
_webViewControl.LongRunningScriptDetected += OnLongRunningScriptDetected;
_webViewControl.LostFocus += OnLostFocus;
_webViewControl.MoveFocusRequested += OnMoveFocusRequested;
_webViewControl.NavigationCompleted += OnNavigationCompleted;
_webViewControl.NavigationStarting += OnNavigationStarting;
@ -191,7 +194,9 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
_webViewControl.FrameDOMContentLoaded -= OnFrameDOMContentLoaded;
_webViewControl.FrameNavigationCompleted -= OnFrameNavigationCompleted;
_webViewControl.FrameNavigationStarting -= OnFrameNavigationStarting;
_webViewControl.GotFocus -= OnGotFocus;
_webViewControl.LongRunningScriptDetected -= OnLongRunningScriptDetected;
_webViewControl.LostFocus -= OnLostFocus;
_webViewControl.MoveFocusRequested -= OnMoveFocusRequested;
_webViewControl.NavigationCompleted -= OnNavigationCompleted;
_webViewControl.NavigationStarting -= OnNavigationStarting;

Просмотреть файл

@ -3,7 +3,10 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http;
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
{
@ -95,5 +98,34 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
}
}
}
/// <inheritdoc />
public void Navigate(Uri source) => _webViewControl?.Navigate(source);
/// <inheritdoc />
public void Navigate(string source)
{
Verify.IsFalse(IsDisposed);
Verify.IsNotNull(_webViewControl);
_webViewControl?.Navigate(source);
}
/// <inheritdoc />
[Obsolete("Use NavigateToLocalStreamUri(Uri, IUriToStreamResolver) instead")]
public void NavigateToLocal(string relativePath) => _webViewControl?.NavigateToLocal(relativePath);
/// <inheritdoc />
public void NavigateToString(string text) => _webViewControl?.NavigateToString(text);
/// <inheritdoc />
public void NavigateToLocalStreamUri(Uri relativePath, IUriToStreamResolver streamResolver) => _webViewControl?.NavigateToLocalStreamUri(relativePath, streamResolver);
/// <inheritdoc />
public void Navigate(
Uri requestUri,
HttpMethod httpMethod,
string content = null,
IEnumerable<KeyValuePair<string, string>> headers = null) =>
_webViewControl.Navigate(requestUri, httpMethod, content, headers);
}
}

Просмотреть файл

@ -41,6 +41,7 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
private bool _delayedIsIndexDbEnabled = WebViewDefaults.IsIndexedDBEnabled;
private bool _delayedIsJavaScriptEnabled = WebViewDefaults.IsJavaScriptEnabled;
private bool _delayedIsScriptNotifyAllowed = WebViewDefaults.IsScriptNotifyEnabled;
private string _delayedPartition = WebViewDefaults.Partition;
private bool _delayedPrivateNetworkEnabled = WebViewDefaults.IsPrivateNetworkEnabled;
private Uri _delayedSource;
private WebViewControlHost _webViewControl;
@ -55,6 +56,8 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
Layout += OnWebViewLayout;
}
internal WebViewControlHost Host => _webViewControl;
/// <summary>
/// Gets a value indicating whether <see cref="WebView"/> is supported in this environment.
/// </summary>
@ -291,6 +294,37 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
}
}
/// <inheritdoc />
[StringResourceCategory(Constants.CategoryBehavior)]
[DefaultValue(WebViewDefaults.Partition)]
public string Partition
{
get
{
Verify.IsFalse(IsDisposed);
Verify.Implies(Initializing, !Initialized);
Verify.Implies(Initialized, WebViewControlInitialized);
return WebViewControlInitialized
? _webViewControl.Process.Partition
: _delayedPartition;
}
set
{
Verify.IsFalse(IsDisposed);
_delayedPartition = value;
if (!DesignMode)
{
EnsureInitialized();
if (WebViewControlInitialized
&& !string.Equals(_delayedPartition, _webViewControl.Process.Partition, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(DesignerUI.E_CANNOT_CHANGE_AFTER_INIT);
}
}
}
}
/// <summary>
/// Gets a <see cref="WebViewControlProcess" /> object that the control is hosted in.
/// </summary>
@ -369,6 +403,15 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Version Version => _webViewControl?.Version;
/// <inheritdoc />
public void AddPreLoadedScript(string script)
{
Verify.IsFalse(IsDisposed);
Verify.Implies(Initializing, !Initialized);
Verify.Implies(Initialized, WebViewControlInitialized);
_webViewControl?.AddPreLoadedScript(script);
}
/// <summary>
/// Closes this control.
/// </summary>
@ -420,23 +463,6 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.WinForms
/// <inheritdoc />
public void MoveFocus(WebViewControlMoveFocusReason reason) => _webViewControl?.MoveFocus(reason);
/// <inheritdoc />
public void Navigate(Uri source) => _webViewControl?.Navigate(source);
/// <inheritdoc />
public void Navigate(string source)
{
Verify.IsFalse(IsDisposed);
Verify.IsNotNull(_webViewControl);
_webViewControl?.Navigate(source);
}
/// <inheritdoc />
public void NavigateToLocal(string relativePath) => _webViewControl?.NavigateToLocal(relativePath);
/// <inheritdoc />
public void NavigateToString(string text) => _webViewControl?.NavigateToString(text);
/// <summary>
/// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.Control" /> and its child controls and optionally releases the managed resources.
/// </summary>

Просмотреть файл

@ -19,6 +19,9 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Test.WebView.Shared
// Local navigation: when using a null value for Source the uri is about:blank
public static readonly Uri AboutBlank = new Uri("about:blank");
// A simple HTTP Request & Response Service
public static readonly Uri HttpBin = new Uri("http://httpbin.org", UriKind.Absolute);
}
}
}

Просмотреть файл

@ -0,0 +1,197 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Should;
using System;
namespace Microsoft.Toolkit.Win32.UI.Controls.Test.WinForms.WebView.FunctionalTests.NavigateToLocalStreamUri
{
public abstract class BuildStreamContextSpecification : HostFormWebViewContextSpecification
{
public Uri Actual { get; set; }
public Uri Expected { get; set; }
public string RelativePath { get; set; }
protected override void When()
{
Actual = WebView.Host.BuildStream("Test", RelativePath);
}
[TestMethod]
public virtual void GeneratedStreamUriIsExpectedValue()
{
Actual.ShouldEqual(Expected);
}
}
[TestClass]
public class Given_a_relative_path_referencing_two_ancestors : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374/foo.htm");
RelativePath = @"..\..\foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_sibling : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374/foo.htm");
RelativePath = @"foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_sibling_backslash : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374//foo.htm");
RelativePath = @"\foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_sibling_dotbackslash : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374/foo.htm");
RelativePath = @".\foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_sibling_frontslash : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374/foo.htm");
RelativePath = @"/foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_sibling_dotfrontslash : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374/foo.htm");
RelativePath = @"./foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_descendant : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374/bar/foo.htm");
RelativePath = @"bar/foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_descendant_backslash : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374//bar/foo.htm");
RelativePath = @"\bar\foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_descendant_dotbackslash : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374/bar/foo.htm");
RelativePath = @".\bar\foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_descendant_frontslash : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374/bar/foo.htm");
RelativePath = @"/bar/foo.htm";
}
}
[TestClass]
public class Given_a_relative_path_referencing_descendant_dotfrontslash : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
Expected = new Uri("ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_54657374/bar/foo.htm");
RelativePath = @"./bar/foo.htm";
}
}
[TestClass]
public class Given_an_absolute_path_referencing_two_ancestors : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
RelativePath = @"C:\foo.htm";
}
protected override void When()
{
// Intentionally blank
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public override void GeneratedStreamUriIsExpectedValue()
{
// The error actually occurs in the BuildStream(String, String) method
base.When();
}
}
[TestClass]
public class Given_an_UNC_path_referencing_two_ancestors : BuildStreamContextSpecification
{
protected override void Given()
{
base.Given();
RelativePath = @"\\machine\share\foo.htm";
}
protected override void When()
{
// Intentionally blank
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public override void GeneratedStreamUriIsExpectedValue()
{
// The error actually occurs in the BuildStream(String, String) method
base.When();
}
}
}

Просмотреть файл

@ -0,0 +1,108 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.Toolkit.Win32.UI.Controls.Test.WebView.Shared;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Should;
namespace Microsoft.Toolkit.Win32.UI.Controls.Test.WinForms.WebView.FunctionalTests.NavigateToLocalStreamUri
{
[TestClass]
[TestCategory(TestConstants.Categories.Nav)]
[DeploymentItem("FunctionalTests\\NavigateToLocalStreamUri\\async.htm")]
public class Given_a_local_htm_file : HostFormWebViewContextSpecification
{
private bool _success;
protected override void Given()
{
base.Given();
WebView.NavigationCompleted += (o, e) =>
{
_success = e.IsSuccess;
Form.Close();
};
}
protected override void When()
{
NavigateToLocalAndWaitForFormClose(new Uri("async.htm", UriKind.Relative), new TestStreamResolver());
}
[TestMethod]
public void LocalNavigationCompleted()
{
_success.ShouldBeTrue();
}
}
[TestClass]
[TestCategory(TestConstants.Categories.Nav)]
[DeploymentItem("FunctionalTests\\NavigateToLocalStreamUri\\async.htm")]
public class Given_a_local_htm_file_with_async_XHR_for_local_content : HostFormWebViewContextSpecification
{
private string _scriptNotifyResult;
protected override void Given()
{
base.Given();
WebView.IsJavaScriptEnabled = true;
WebView.IsScriptNotifyAllowed = true;
WebView.ScriptNotify += (o, e) =>
{
_scriptNotifyResult = e.Value;
Form.Close();
};
}
protected override void When()
{
NavigateToLocalAndWaitForFormClose(new Uri("async.htm", UriKind.Relative), new TestStreamResolver());
}
[TestMethod]
public void LocalNavigationCompleted()
{
_scriptNotifyResult.ShouldEqual("Success");
}
}
[TestClass]
[TestCategory(TestConstants.Categories.Nav)]
[DeploymentItem("FunctionalTests\\NavigateToLocalStreamUri\\sync.htm")]
public class Given_a_local_htm_file_with_sync_XHR_for_local_content : HostFormWebViewContextSpecification
{
private string _scriptNotifyResult;
protected override void Given()
{
base.Given();
WebView.IsJavaScriptEnabled = true;
WebView.IsScriptNotifyAllowed = true;
WebView.ScriptNotify += (o, e) =>
{
_scriptNotifyResult = e.Value;
Form.Close();
};
}
protected override void When()
{
NavigateToLocalAndWaitForFormClose(new Uri("sync.htm", UriKind.Relative), new TestStreamResolver());
}
[TestMethod]
public void LocalNavigationCompleted()
{
_scriptNotifyResult.ShouldEqual("Success");
}
}
}

Просмотреть файл

@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
using System.IO;
namespace Microsoft.Toolkit.Win32.UI.Controls.Test.WinForms.WebView.FunctionalTests.NavigateToLocalStreamUri
{
internal class TestStreamResolver : UriToLocalStreamResolver
{
public TestStreamResolver()
:base(Path.GetDirectoryName(typeof(TestStreamResolver).Assembly.Location))
{
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<title>Async XHR Test</title>
</head>
<body>
<span id="progress">Loading...</span>
<script type="text/javascript">
// Set timeout to show the page before causing the deadlock
setTimeout(function () {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/async.htm', /* async */ true);
xhr.onload = function () {
/* this will never be called... */
document.getElementById('progress').textContent = 'Success';
window.external.notify('Success');
};
xhr.onerror = function () {
/* this will never be called... */
document.getElementById('progress').textContent = 'Error';
window.external.notify('Error');
};
xhr.send();
}, 1);
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<title>Sync XHR Test</title>
</head>
<body>
<span id="progress">Loading...</span>
<script type="text/javascript">
// Set timeout to show the page before causing the deadlock
setTimeout(function () {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/sync.htm', /* async */ false);
xhr.onload = function () {
/* this will never be called... */
document.getElementById('progress').textContent = 'Success';
window.external.notify('Success');
};
xhr.onerror = function () {
/* this will never be called... */
document.getElementById('progress').textContent = 'Error';
window.external.notify('Error');
};
xhr.send();
}, 1);
</script>
</body>
</html>

Просмотреть файл

@ -3,7 +3,10 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using Microsoft.Toolkit.Win32.UI.Controls.Test.WebView.Shared;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Should;
@ -83,4 +86,185 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Test.WinForms.WebView.FunctionalTe
});
}
}
[TestClass]
public class Navigate2Tests : HostFormWebViewContextSpecification
{
private bool _navigationCompleted;
protected override void Given()
{
base.Given();
WebView.NavigationCompleted += (o, e) =>
{
_navigationCompleted = e.IsSuccess;
Form.Close();
};
}
protected override void When()
{
PerformActionAndWaitForFormClose(() =>
{
WebView.Navigate(TestConstants.Uris.HttpBin, HttpMethod.Get);
});
}
[TestMethod]
public void Explict_HTTP_GET_succeeds()
{
_navigationCompleted.ShouldBeTrue();
}
}
[TestClass]
public class NavigateGetWithHeaders : HostFormWebViewContextSpecification
{
private bool _navigationCompleted;
protected override void Given()
{
base.Given();
WebView.NavigationCompleted += (o, e) =>
{
_navigationCompleted = e.IsSuccess;
Form.Close();
};
}
protected override void When()
{
PerformActionAndWaitForFormClose(() =>
{
WebView.Navigate(
TestConstants.Uris.HttpBin,
HttpMethod.Get,
null,
new[] { new KeyValuePair<string, string>("pragma", "no-cache") });
});
}
[TestMethod]
public void Explict_HTTP_GET_with_HEADERS_succeeds()
{
_navigationCompleted.ShouldBeTrue();
}
}
[TestClass]
public class NavigateGetWithBasicAuth : HostFormWebViewContextSpecification
{
private bool _navigationCompleted;
protected override void Given()
{
base.Given();
WebView.NavigationCompleted += (o, e) =>
{
_navigationCompleted = e.IsSuccess;
Form.Close();
};
}
protected override void When()
{
PerformActionAndWaitForFormClose(() =>
{
const string user = "usr";
const string password = "pwd";
const string header = "Authorization";
var authInfo = Convert.ToBase64String(Encoding.Default.GetBytes($"{user}:{password}"));
WebView.Navigate(
new Uri(TestConstants.Uris.HttpBin, new Uri($"/basic-auth/{user}/{password}", UriKind.Relative)),
HttpMethod.Get,
null,
new[] { new KeyValuePair<string, string>(header, $"Basic {authInfo}") });
});
}
[TestMethod]
public void Explict_HTTP_GET_with_AUTH_BASIC_succeeds()
{
_navigationCompleted.ShouldBeTrue();
}
}
[TestClass]
public class NavigateOption : HostFormWebViewContextSpecification
{
private bool _navigationCompleted;
protected override void Given()
{
base.Given();
WebView.NavigationCompleted += (o, e) =>
{
_navigationCompleted = e.IsSuccess;
Form.Close();
};
}
protected override void When()
{
PerformActionAndWaitForFormClose(() =>
{
WebView.Navigate(
TestConstants.Uris.ExampleCom,
HttpMethod.Options
);
});
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
[Ignore("Pops UI that stalls test")]
public void Explict_HTTP_OPTION_fails()
{
_navigationCompleted.ShouldBeFalse();
}
}
[TestClass]
public class NavigatePostWithContent : HostFormWebViewContextSpecification
{
private bool _navigationCompleted;
protected override void Given()
{
base.Given();
WebView.NavigationCompleted += (o, e) =>
{
_navigationCompleted = e.IsSuccess;
Form.Close();
};
}
protected override void When()
{
PerformActionAndWaitForFormClose(() =>
{
string Foo()
{
var c = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("Foo", "Bar"), });
return c.ReadAsStringAsync().Result;
}
WebView.Navigate(
new Uri(TestConstants.Uris.HttpBin, "/post"),
HttpMethod.Post,
Foo()
);
});
}
[TestMethod]
public void Explict_HTTP_POST_with_data_succeeds()
{
_navigationCompleted.ShouldBeTrue();
}
}
}

Просмотреть файл

@ -0,0 +1,125 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.Toolkit.Win32.UI.Controls.Test.WebView.Shared;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Should;
using System.IO;
namespace Microsoft.Toolkit.Win32.UI.Controls.Test.WinForms.WebView.FunctionalTests.PreLoad
{
[TestClass]
public class NullPreLoadScript : HostFormWebViewContextSpecification
{
protected override void Given()
{
base.Given();
WebView.IsJavaScriptEnabled = true;
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void CannotPassNullForPreLoadScript()
{
WebView.AddPreLoadedScript(null);
}
}
[TestClass]
public class EmptyPreLoadScript : HostFormWebViewContextSpecification
{
private bool _navSuccess;
protected override void Given()
{
base.Given();
WebView.IsJavaScriptEnabled = true;
WebView.AddPreLoadedScript(string.Empty);
WebView.NavigationCompleted += (o, e) =>
{
_navSuccess = e.IsSuccess;
Form.Close();
};
}
protected override void When()
{
NavigateAndWaitForFormClose(TestConstants.Uris.ExampleCom);
}
[TestMethod]
public void NavigationCompletesForEmptyPreLoadScript()
{
_navSuccess.ShouldBeTrue();
}
}
[TestClass]
public class NonExistentPreLoadScript : HostFormWebViewContextSpecification
{
private bool _navSuccess;
protected override void Given()
{
base.Given();
WebView.IsJavaScriptEnabled = true;
WebView.AddPreLoadedScript($"./non-exist.js");
WebView.NavigationCompleted += (o, e) =>
{
_navSuccess = e.IsSuccess;
Form.Close();
};
}
protected override void When()
{
NavigateAndWaitForFormClose(TestConstants.Uris.ExampleCom);
}
[TestMethod]
public void NavigateCompletesWithoutError()
{
_navSuccess.ShouldBeTrue();
}
}
[TestClass]
[DeploymentItem("FunctionalTests/PreLoad/preload.js")]
public class RelativePreLoadScript : HostFormWebViewContextSpecification
{
private bool _scriptNotifyCalled;
protected override void Given()
{
base.Given();
WebView.IsScriptNotifyAllowed = true;
WebView.IsJavaScriptEnabled = true;
// Not sure how to get this to execute
WebView.AddPreLoadedScript(Path.Combine(TestContext.TestDeploymentDir, "preload.js"));
// Set up the event handler
WebView.ScriptNotify += (o, e) =>
{
_scriptNotifyCalled = true;
Form.Close();
};
}
protected override void When()
{
NavigateAndWaitForFormClose(TestConstants.Uris.ExampleCom);
}
[TestMethod]
[Timeout(TestConstants.Timeouts.Longest)]
public void ScriptNotifyRaised()
{
_scriptNotifyCalled.ShouldBeTrue();
}
}
}

Просмотреть файл

@ -0,0 +1 @@
window.external.notify('preload');

Просмотреть файл

@ -5,6 +5,7 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
using Should;
namespace Microsoft.Toolkit.Win32.UI.Controls.Test.WinForms.WebView.FunctionalTests
@ -110,6 +111,22 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Test.WinForms.WebView.FunctionalTe
});
}
protected virtual void NavigateToLocalAndWaitForFormClose(string relativePath)
{
PerformActionAndWaitForFormClose(() =>
{
WriteLine("Navigating WebView:");
WebView.NavigateToLocal(relativePath);
});
}
protected virtual void NavigateToLocalAndWaitForFormClose(Uri relativePath, IUriToStreamResolver streamResolver)
{
PerformActionAndWaitForFormClose(() =>
{
WriteLine("Navigating WebView");
WebView.NavigateToLocalStreamUri(relativePath, streamResolver);
});
}
}
}

Просмотреть файл

@ -55,10 +55,14 @@
<Compile Include="FunctionalTests\Ctor\WebViewControlProcessCreateWebViewAsyncTests.cs" />
<Compile Include="FunctionalTests\DocumentTitle\DocumentTitleTests.cs" />
<Compile Include="FunctionalTests\FullScreen\FullScreenTests.cs" />
<Compile Include="FunctionalTests\NavigateToLocalStreamUri\BuildStreamTests.cs" />
<Compile Include="FunctionalTests\NavigateToLocalStreamUri\NavigateToLocalStreamUriTests.cs" />
<Compile Include="FunctionalTests\NavigateToLocalStreamUri\TestStreamResolver.cs" />
<Compile Include="FunctionalTests\Navigation\NavigateTests.cs" />
<Compile Include="FunctionalTests\Navigation\StopTests.cs" />
<Compile Include="FunctionalTests\NewWindow\NewWindowTests.cs" />
<Compile Include="FunctionalTests\PartialTrust\PartialTrustTests.cs" />
<Compile Include="FunctionalTests\PreLoad\PreLoadTests.cs" />
<Compile Include="FunctionalTests\Termination\TerminationTests.cs" />
<Compile Include="FunctionalTests\TestHostForm.cs">
<SubType>Form</SubType>
@ -114,6 +118,17 @@
<Version>1.1.20</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Content Include="FunctionalTests\NavigateToLocalStreamUri\sync.htm">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="FunctionalTests\NavigateToLocalStreamUri\async.htm">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="FunctionalTests\PreLoad\preload.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="Pack">