Skip to content

Commit

Permalink
[main] Merge release 9.0.1xx to main (dotnet#25527)
Browse files Browse the repository at this point in the history
### Description of Change

Bring latest changes to main from release branch and the new template.
  • Loading branch information
rmarinho authored Oct 25, 2024
2 parents 8845bdc + ec64736 commit f269ef3
Show file tree
Hide file tree
Showing 137 changed files with 12,749 additions and 299 deletions.
5 changes: 5 additions & 0 deletions docs/design/FeatureSwitches.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The following switches are toggled for applications running on Mono for `TrimMod
| MauiImplicitCastOperatorsUsageViaReflectionSupport | Microsoft.Maui.RuntimeFeature.IsImplicitCastOperatorsUsageViaReflectionSupported | When disabled, MAUI won't look for implicit cast operators when converting values from one type to another. This feature is not trim-compatible. |
| _MauiBindingInterceptorsSupport | Microsoft.Maui.RuntimeFeature.AreBindingInterceptorsSupported | When disabled, MAUI won't intercept any calls to `SetBinding` methods and try to compile them. Enabled by default. |
| MauiEnableXamlCBindingWithSourceCompilation | Microsoft.Maui.RuntimeFeature.XamlCBindingWithSourceCompilationEnabled | When enabled, MAUI will compile all bindings, including those where the `Source` property is used. |
| MauiHybridWebViewSupported | Microsoft.Maui.RuntimeFeature.IsHybridWebViewSupported | Enables HybridWebView, which makes use of dynamic System.Text.Json serialization features |

## MauiEnableIVisualAssemblyScanning

Expand Down Expand Up @@ -43,6 +44,10 @@ This feature is a counterpart of [XAML Compiled bindings](https://learn.microsof

It is necessary to use this feature instead of the string-based bindings in NativeAOT apps and in apps with full trimming enabled.

## MauiHybridWebViewSupported

When this feature is disabled, `HybridWebView` will not be available. This is the default for projects using `TrimMode=full` or `PublishAot=true`.

### Example use-case

String-based binding in code:
Expand Down
2 changes: 2 additions & 0 deletions eng/automation/SignList.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
<ThirdPartyScript Include="OpenSans-Regular.ttf" />
<ThirdPartyScript Include="OpenSans-Semibold.ttf" />
<ThirdPartyScript Include="open-iconic.ttf" />
<ThirdPartyScript Include="FluentSystemIcons-Regular.ttf" />
<ThirdPartyScript Include="SegoeUI-Semibold.ttf " />
</ItemGroup>

<ItemGroup>
Expand Down
25 changes: 14 additions & 11 deletions src/BlazorWebView/src/Maui/Android/BlazorWebViewHandler.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@ protected override AWebView CreatePlatformView()

private const string AndroidFireAndForgetAsyncSwitch = "BlazorWebView.AndroidFireAndForgetAsync";

private static bool IsAndroidFireAndForgetAsyncEnabled =>
AppContext.TryGetSwitch(AndroidFireAndForgetAsyncSwitch, out var enabled) && enabled;

protected override void DisconnectHandler(AWebView platformView)
{
platformView.StopLoading();
Expand All @@ -77,19 +74,25 @@ protected override void DisconnectHandler(AWebView platformView)
.DisposeAsync()
.AsTask()!;

if (IsAndroidFireAndForgetAsyncEnabled)
{
// If the app is configured to fire-and-forget via an AppContext Switch, we'll do that.
disposalTask.FireAndForget();
}
else
// When determining whether to block on disposal, we respect the more specific AndroidFireAndForgetAsync switch
// if specified. If not, we fall back to the general UseBlockingDisposal switch, defaulting to false.
var shouldBlockOnDispose = AppContext.TryGetSwitch(AndroidFireAndForgetAsyncSwitch, out var enableFireAndForget)
? !enableFireAndForget
: IsBlockingDisposalEnabled;

if (shouldBlockOnDispose)
{
// Otherwise by default, we'll synchronously wait for the disposal to complete. This can cause
// a deadlock, but is the original behavior.
// If the app is configured to block on dispose via an AppContext switch,
// we'll synchronously wait for the disposal to complete. This can cause a deadlock.
disposalTask
.GetAwaiter()
.GetResult();
}
else
{
// Otherwise, by default, we'll fire-and-forget the disposal task.
disposalTask.FireAndForget(_logger);
}

_webviewManager = null;
}
Expand Down
5 changes: 5 additions & 0 deletions src/BlazorWebView/src/Maui/BlazorWebViewHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ namespace Microsoft.AspNetCore.Components.WebView.Maui
#endif
public partial class BlazorWebViewHandler
{
private const string UseBlockingDisposalSwitch = "BlazorWebView.UseBlockingDisposal";

private static bool IsBlockingDisposalEnabled =>
AppContext.TryGetSwitch(UseBlockingDisposalSwitch, out var enabled) && enabled;

/// <summary>
/// This field is part of MAUI infrastructure and is not intended for use by application code.
/// </summary>
Expand Down
24 changes: 18 additions & 6 deletions src/BlazorWebView/src/Maui/Windows/BlazorWebViewHandler.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Maui;
using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Handlers;
using WebView2Control = Microsoft.UI.Xaml.Controls.WebView2;
Expand All @@ -30,13 +31,24 @@ protected override void DisconnectHandler(WebView2Control platformView)
{
if (_webviewManager != null)
{
// Dispose this component's contents and block on completion so that user-written disposal logic and
// Blazor disposal logic will complete.
_webviewManager?
// Start the disposal...
var disposalTask = _webviewManager?
.DisposeAsync()
.AsTask()
.GetAwaiter()
.GetResult();
.AsTask()!;

if (IsBlockingDisposalEnabled)
{
// If the app is configured to block on dispose via an AppContext switch,
// we'll synchronously wait for the disposal to complete. This can cause a deadlock.
disposalTask
.GetAwaiter()
.GetResult();
}
else
{
// Otherwise, by default, we'll fire-and-forget the disposal task.
disposalTask.FireAndForget();
}

_webviewManager = null;
}
Expand Down
24 changes: 18 additions & 6 deletions src/BlazorWebView/src/Maui/iOS/BlazorWebViewHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Maui;
using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Handlers;
using UIKit;
Expand Down Expand Up @@ -127,13 +128,24 @@ protected override void DisconnectHandler(WKWebView platformView)

if (_webviewManager != null)
{
// Dispose this component's contents and block on completion so that user-written disposal logic and
// Blazor disposal logic will complete.
_webviewManager?
// Start the disposal...
var disposalTask = _webviewManager?
.DisposeAsync()
.AsTask()
.GetAwaiter()
.GetResult();
.AsTask()!;

if (IsBlockingDisposalEnabled)
{
// If the app is configured to block on dispose via an AppContext switch,
// we'll synchronously wait for the disposal to complete. This can cause a deadlock.
disposalTask
.GetAwaiter()
.GetResult();
}
else
{
// Otherwise, by default, we'll fire-and-forget the disposal task.
disposalTask.FireAndForget(_logger);
}

_webviewManager = null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Microsoft.Maui.Controls;

namespace Maui.Controls.Sample.Pages
Expand All @@ -11,6 +13,8 @@ public partial class HybridWebViewPage
public HybridWebViewPage()
{
InitializeComponent();

hwv.SetInvokeJavaScriptTarget<DotNetMethods>(new DotNetMethods(this));
}

int count;
Expand All @@ -28,8 +32,8 @@ private async void InvokeJSMethodButton_Clicked(object sender, EventArgs e)
var result = await hwv.InvokeJavaScriptAsync<ComputationResult>(
"AddNumbers",
SampleInvokeJsContext.Default.ComputationResult,
new object?[] { x, null, y, null },
new[] { SampleInvokeJsContext.Default.Double, null, SampleInvokeJsContext.Default.Double, null });
[x, null, y, null],
[SampleInvokeJsContext.Default.Double, null, SampleInvokeJsContext.Default.Double, null]);

if (result is null)
{
Expand All @@ -47,11 +51,11 @@ private async void InvokeAsyncJSMethodButton_Clicked(object sender, EventArgs e)
{
var statusResult = "";

var asyncFuncResult = await hwv.InvokeJavaScriptAsync<Dictionary<string,string>>(
var asyncFuncResult = await hwv.InvokeJavaScriptAsync<Dictionary<string, string>>(
"EvaluateMeWithParamsAndAsyncReturn",
SampleInvokeJsContext.Default.DictionaryStringString,
new object?[] { "new_key", "new_value" },
new[] { SampleInvokeJsContext.Default.String, SampleInvokeJsContext.Default.String });
["new_key", "new_value"],
[SampleInvokeJsContext.Default.String, SampleInvokeJsContext.Default.String]);

if (asyncFuncResult == null)
{
Expand Down Expand Up @@ -84,5 +88,47 @@ public class ComputationResult
internal partial class SampleInvokeJsContext : JsonSerializerContext
{
}

private class DotNetMethods
{
private HybridWebViewPage _hybridWebViewPage;

public DotNetMethods(HybridWebViewPage hybridWebViewPage)
{
_hybridWebViewPage = hybridWebViewPage;
}

public void DoSyncWork()
{
Debug.WriteLine("DoSyncWork");
}

public void DoSyncWorkParams(int i, string s)
{
Debug.WriteLine($"DoSyncWorkParams: {i}, {s}");
}

public string DoSyncWorkReturn()
{
Debug.WriteLine("DoSyncWorkReturn");
return "Hello from C#!";
}

public SyncReturn DoSyncWorkParamsReturn(int i, string s)
{
Debug.WriteLine($"DoSyncWorkParamsReturn: {i}, {s}");
return new SyncReturn
{
Message = "Hello from C#! " + s,
Value = i,
};
}
}

public class SyncReturn
{
public string? Message { get; set; }
public int Value { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@
<link rel="stylesheet" href="styles/app.css">
<script src="scripts/HybridWebView.js"></script>
<script>
function LogMessage(msg) {
var messageLog = document.getElementById("messageLog");
messageLog.value += '\r\n' + msg;
}

window.addEventListener(
"HybridWebViewMessageReceived",
function (e) {
var messageFromCSharp = document.getElementById("messageFromCSharp");
messageFromCSharp.value += '\r\n' + e.detail.message;
LogMessage("Raw message: " + e.detail.message);
});

function AddNumbers(a, x, b, y) {
Expand All @@ -28,7 +32,7 @@
async function EvaluateMeWithParamsAndAsyncReturn(s1, s2) {
const response = await fetch("/asyncdata.txt");
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
throw new Error(`HTTP error: ${response.status}`);
}
var jsonData = await response.json();

Expand All @@ -39,6 +43,32 @@

return jsonData;
}


async function InvokeDoSyncWork() {
LogMessage("Invoking DoSyncWork");
await window.HybridWebView.InvokeDotNet('DoSyncWork');
LogMessage("Invoked DoSyncWork");
}

async function InvokeDoSyncWorkParams() {
LogMessage("Invoking DoSyncWorkParams");
await window.HybridWebView.InvokeDotNet('DoSyncWorkParams', [123, 'hello']);
LogMessage("Invoked DoSyncWorkParams");
}

async function InvokeDoSyncWorkReturn() {
LogMessage("Invoking DoSyncWorkReturn");
const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkReturn');
LogMessage("Invoked DoSyncWorkReturn, return value: " + retValue);
}

async function InvokeDoSyncWorkParamsReturn() {
LogMessage("Invoking DoSyncWorkParamsReturn");
const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkParamsReturn', [123, 'hello']);
LogMessage("Invoked DoSyncWorkParamsReturn, return value: message=" + retValue.Message + ", value=" + retValue.Value);
}

</script>
</head>
<body>
Expand All @@ -49,7 +79,13 @@
<button onclick="window.HybridWebView.SendRawMessage('Message from JS! ' + (count++))">Send message to C#</button>
</div>
<div>
Message from C#: <textarea readonly id="messageFromCSharp" style="width: 80%; height: 10em;"></textarea>
<button onclick="InvokeDoSyncWork()">Call C# sync method (no params)</button>
<button onclick="InvokeDoSyncWorkParams()">Call C# sync method (params)</button>
<button onclick="InvokeDoSyncWorkReturn()">Call C# method (no params) and get simple return value</button>
<button onclick="InvokeDoSyncWorkParamsReturn()">Call C# method (params) and get complex return value</button>
</div>
<div>
Log: <textarea readonly id="messageLog" style="width: 80%; height: 10em;"></textarea>
</div>
<div>
Consider checking out this PDF: <a href="docs/sample.pdf">sample.pdf</a>
Expand Down
Loading

0 comments on commit f269ef3

Please sign in to comment.