Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Correct issues with ApiInformation implementation #2305

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// 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 Windows.Foundation.Metadata;

namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
{
// .NET applications use a concept of Union WinMD, which is the union of all known types that exist in the Windows SDK
// that correspond to the MaxVersionTested setting of the app. If you're running the app on a down-level platform,
// ApiInformation will tell you the API doesn't exist, but .NET can still JIT methods based on the Union WinMD and
// perform some reflection tasks. If you actually try to call the API you will get a MissingMethodException at runtime
// because the API doesn't really exist.
// A different problem can occur if you include a higher-versioned .NET library inside a lower-versioned app and try to run
// it on the higher-versioned build of the OS. In this case, ApiInformation will succeed because the type exists in the
// system metadata, but .NET will throw a MissingMethodException at runtime because the type didn't exist in the Union WinMD
// used to build the app.
public static class ApiInformationExtensions
{
public static void ExecuteIfPropertyPresent(string typeName, string propertyName, Action action)
{
if (ApiInformation.IsPropertyPresent(typeName, propertyName))
{
try
{
action();
}
catch (MissingMethodException)
{
}
}
}

public static T ExecuteIfPropertyPresent<T>(string typeName, string propertyName, Func<T> action)
{
if (ApiInformation.IsPropertyPresent(typeName, propertyName))
{
try
{
return action();
}
catch (MissingMethodException)
{
}
}

return default(T);
}

public static void ExecuteIfMethodPresent(string typeName, string methodName, Action action)
{
if (ApiInformation.IsMethodPresent(typeName, methodName))
{
try
{
action();
}
catch (MissingMethodException)
{
}
}
}

public static void ExecuteIfMethodPresent(string typeName, string methodName, uint inputParameterCount, Action action)
{
if (ApiInformation.IsMethodPresent(typeName, methodName, inputParameterCount))
{
try
{
action();
}
catch (MissingMethodException)
{
}
}
}

public static void ExecuteIfEventPresent(string typeName, string eventName, Action action)
{
if (ApiInformation.IsEventPresent(typeName, eventName))
{
try
{
action();
}
catch (MissingMethodException)
{
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
internal sealed class WebViewControlHost : IDisposable
{
private const string LocalContentIdentifier = "LocalContent";
private const string WinRtType = "Windows.Web.UI.Interop.WebViewControl";

[SecurityCritical]
private WebViewControl _webViewControl;
Expand Down Expand Up @@ -291,13 +292,11 @@ private bool NavigatingToAboutBlank

internal void AddPreLoadedScript(string script)
{
if (ApiInformation.IsMethodPresent(
"Windows.Web.UI.Interop.WebViewControl",
ApiInformationExtensions.ExecuteIfMethodPresent(
WinRtType,
"AddPreLoadedScript",
1))
{
_webViewControl?.AddPreLoadedScript(script);
}
1,
() => { _webViewControl?.AddPreLoadedScript(script); });
}

internal Uri BuildStream(string contentIdentifier, string relativePath)
Expand Down Expand Up @@ -1067,15 +1066,15 @@ private void SubscribeEvents()
_webViewControl.UnsupportedUriSchemeIdentified += OnUnsupportedUriSchemeIdentified;
_webViewControl.UnviewableContentIdentified += OnUnviewableContentIdentified;

if (ApiInformation.IsEventPresent("Windows.Web.UI.Interop", "GotFocus"))
{
_webViewControl.GotFocus += OnGotFocus;
}
ApiInformationExtensions.ExecuteIfEventPresent(
WinRtType,
"GotFocus",
() => { _webViewControl.GotFocus += OnGotFocus; });

if (ApiInformation.IsEventPresent("Windows.Web.UI.Interop", "LostFocus"))
{
_webViewControl.LostFocus += OnLostFocus;
}
ApiInformationExtensions.ExecuteIfEventPresent(
WinRtType,
"LostFocus",
() => { _webViewControl.GotFocus += OnLostFocus; });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be LostFocus

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4bc5d4c

}

[SecurityCritical]
Expand Down Expand Up @@ -1115,15 +1114,15 @@ private void UnsubscribeEvents()
_webViewControl.UnsupportedUriSchemeIdentified -= OnUnsupportedUriSchemeIdentified;
_webViewControl.UnviewableContentIdentified -= OnUnviewableContentIdentified;

if (ApiInformation.IsEventPresent("Windows.Web.UI.Interop", "GotFocus"))
{
_webViewControl.GotFocus -= OnGotFocus;
}
ApiInformationExtensions.ExecuteIfEventPresent(
WinRtType,
"GotFocus",
() => { _webViewControl.GotFocus -= OnGotFocus; });

if (ApiInformation.IsEventPresent("Windows.Web.UI.Interop", "LostFocus"))
{
_webViewControl.LostFocus -= OnLostFocus;
}
ApiInformationExtensions.ExecuteIfEventPresent(
WinRtType,
"LostFocus",
() => { _webViewControl.GotFocus -= OnLostFocus; });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be LostFocus?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4bc5d4c

}

private void UnsubscribeProcessExited()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT
/// </summary>
public sealed class WebViewControlProcess
{
private const string WinRtType = "Windows.Web.UI.Interop.WebViewControlProcessOptions";

[SecurityCritical]
private readonly Windows.Web.UI.Interop.WebViewControlProcess _process;

Expand Down Expand Up @@ -69,14 +71,10 @@ public string Partition
{
get
{
if (ApiInformation.IsPropertyPresent(
"Windows.Web.UI.Interop.WebViewControlProcessOptions",
"Partition"))
{
return _process.Partition;
}

return string.Empty;
return ApiInformationExtensions.ExecuteIfPropertyPresent(
WinRtType,
"Partition",
() => _process.Partition) ?? string.Empty;
}
}

Expand All @@ -94,14 +92,10 @@ public string UserAgent
{
get
{
if (ApiInformation.IsPropertyPresent(
"Windows.Web.UI.Interop.WebViewControlProcessOptions",
"UserAgent"))
{
return _process.UserAgent;
}

return string.Empty;
return ApiInformationExtensions.ExecuteIfPropertyPresent(
WinRtType,
"Partition",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UserAgent?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be replaced to, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was in 193077d

() => _process.Partition) ?? string.Empty;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,34 +60,38 @@ public WebViewControlProcessOptions()

public static Windows.Web.UI.Interop.WebViewControlProcessOptions ToWinRtWebViewControlProcessOptions(WebViewControlProcessOptions options)
{
const string winRtType = "Windows.Web.UI.Interop.WebViewControlProcessOptions";

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))
ApiInformationExtensions.ExecuteIfPropertyPresent(
winRtType,
"Partition",
() =>
{
retval.UserAgent = options.UserAgent;
}
}
if (!string.IsNullOrEmpty(options?.Partition))
{
retval.Partition = options.Partition;
}
});

ApiInformationExtensions.ExecuteIfPropertyPresent(
winRtType,
"UserAgent",
() =>
{
if (!string.IsNullOrEmpty(options?.UserAgent))
{
retval.UserAgent = options.UserAgent;
}
});

return retval;
}
Expand All @@ -98,7 +102,7 @@ public static Windows.Web.UI.Interop.WebViewControlProcessOptions ToWinRtWebView
/// <returns>A <seealso cref="Windows.Web.UI.Interop.WebViewControlProcessOptions"/> instance.</returns>
internal Windows.Web.UI.Interop.WebViewControlProcessOptions ToWinRtWebViewControlProcessOptions()
{
return ToWinRtWebViewControlProcessOptions(this);
return ToWinRtWebViewControlProcessOptions(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.ComponentModel;
using Microsoft.Toolkit.Win32.UI.Controls.Interop;
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
using WebViewControlProcess = Windows.Web.UI.Interop.WebViewControlProcess;
using WebViewControlProcessCapabilityState = Windows.Web.UI.Interop.WebViewControlProcessCapabilityState;
using WebViewControlProcessOptions = Windows.Web.UI.Interop.WebViewControlProcessOptions;

Expand Down Expand Up @@ -119,14 +118,16 @@ private void Initialize()
Verify.IsNull(Process);

// Was not injected via ctor, create using defaults
Process = new WebViewControlProcess(new WebViewControlProcessOptions
var options = new Interop.WinRT.WebViewControlProcessOptions()
{
PrivateNetworkClientServerCapability = _delayedPrivateNetworkEnabled
? WebViewControlProcessCapabilityState.Enabled
: WebViewControlProcessCapabilityState.Disabled,
PrivateNetworkClientServerCapability = (Interop.WinRT.WebViewControlProcessCapabilityState)(_delayedPrivateNetworkEnabled
? WebViewControlProcessCapabilityState.Enabled
: WebViewControlProcessCapabilityState.Disabled),
EnterpriseId = _delayedEnterpriseId,
Partition = _delayedPartition
});
};

Process = new WebViewControlProcess(options);
_webViewControl = Process.CreateWebViewControlHost(Handle, ClientRectangle);
SubscribeEvents();

Expand Down
Original file line number Diff line number Diff line change
@@ -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 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.UserAgent
{
[TestClass]
public class Given_a_WebView : HostFormWebViewContextSpecification
{
private string _userAgent;

protected override void Given()
{
base.Given();

WebView.NavigationCompleted += (o, e) =>
{
var wv = o as Controls.WinForms.WebView;
_userAgent = wv.Process.UserAgent;

Form.Close();
};
}

protected override void When()
{
NavigateAndWaitForFormClose(TestConstants.Uris.ExampleCom);
}


[TestMethod]
public void UserAgent_accessed_without_exception()
{
_userAgent.ShouldEqual(string.Empty);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
<Compile Include="FunctionalTests\ScriptNotify\ScriptNotifyOnNavigate.cs" />
<Compile Include="FunctionalTests\ScriptNotify\ScriptNotifyOnNavigateToString.cs" />
<Compile Include="FunctionalTests\UnsupportedUriScheme\UnsupportedUriSchemeTests.cs" />
<Compile Include="FunctionalTests\UserAgent\UserAgentTests.cs" />
<Compile Include="FunctionalTests\WebResourceRequested\WebResourceRequestedTests.cs" />
<Compile Include="FunctionalTests\WebViewFormContextSpecification.cs" />
<Compile Include="FunctionalTests\Ctor\ISupportInitializeDefaultsTests.cs" />
Expand Down