Skip to content

Commit

Permalink
[net9.0] Revert "Move HybridWebView platform code to handlers" (#24876)
Browse files Browse the repository at this point in the history
* Revert "Move HybridWebView platform code to handlers (#24479)"

This reverts commit 693e544.

* - disable HybridWebViewTests

---------

Co-authored-by: Shane Neuville <[email protected]>
  • Loading branch information
rmarinho and PureWeen authored Sep 24, 2024
1 parent 693e544 commit c6ca951
Show file tree
Hide file tree
Showing 26 changed files with 217 additions and 374 deletions.
160 changes: 145 additions & 15 deletions src/Controls/src/Core/HybridWebView/HybridWebView.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Maui.Devices;
using System.Collections.Generic;

#if WINDOWS || ANDROID || IOS || MACCATALYST
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Threading.Tasks;
#endif

namespace Microsoft.Maui.Controls
{
Expand Down Expand Up @@ -33,9 +39,37 @@ public string? HybridRoot
set { SetValue(HybridRootProperty, value); }
}

void IHybridWebView.RawMessageReceived(string rawMessage)
void IHybridWebView.MessageReceived(string rawMessage)
{
RawMessageReceived?.Invoke(this, new HybridWebViewRawMessageReceivedEventArgs(rawMessage));
if (string.IsNullOrEmpty(rawMessage))
{
throw new ArgumentException($"The raw message cannot be null or empty.", nameof(rawMessage));
}
var indexOfPipe = rawMessage.IndexOf("|", StringComparison.Ordinal);
if (indexOfPipe == -1)
{
throw new ArgumentException($"The raw message must contain a pipe character ('|').", nameof(rawMessage));
}

var messageType = rawMessage.Substring(0, indexOfPipe);
var messageContent = rawMessage.Substring(indexOfPipe + 1);

switch (messageType)
{
case "InvokeMethodCompleted":
{
var sections = messageContent.Split('|');
var taskId = sections[0];
var result = sections[1];
AsyncTaskCompleted(taskId, result);
}
break;
case "RawMessage":
RawMessageReceived?.Invoke(this, new HybridWebViewRawMessageReceivedEventArgs(messageContent));
break;
default:
throw new ArgumentException($"The message type '{messageType}' is not recognized.", nameof(rawMessage));
}
}

/// <summary>
Expand All @@ -57,12 +91,36 @@ public void SendRawMessage(string rawMessage)
});
}

/// <inheritdoc/>
public async Task<TReturnType?> InvokeJavaScriptAsync<TReturnType>(
string methodName,
JsonTypeInfo<TReturnType> returnTypeJsonTypeInfo,
object?[]? paramValues = null,
JsonTypeInfo?[]? paramJsonTypeInfos = null)
private int _invokeTaskId;
private Dictionary<string, TaskCompletionSource<string>> asyncTaskCallbacks = new Dictionary<string, TaskCompletionSource<string>>();

/// <summary>
/// Handler for when the an Async JavaScript task has completed and needs to notify .NET.
/// </summary>
private void AsyncTaskCompleted(string taskId, string result)
{
//Look for the callback in the list of pending callbacks.
if (!string.IsNullOrEmpty(taskId) && asyncTaskCallbacks.ContainsKey(taskId))
{
//Get the callback and remove it from the list.
var callback = asyncTaskCallbacks[taskId];
callback.SetResult(result);

//Remove the callback.
asyncTaskCallbacks.Remove(taskId);
}
}

/// <summary>
/// Invokes a JavaScript method named <paramref name="methodName"/> and optionally passes in the parameter values
/// specified by <paramref name="paramValues"/>.
/// </summary>
/// <param name="methodName">The name of the JavaScript method to invoke.</param>
/// <param name="paramValues">Optional array of objects to be passed to the JavaScript method.</param>
/// <param name="paramJsonTypeInfos">Optional array of metadata about serializing the types of the parameters specified by <paramref name="paramValues"/>.</param>
/// <returns>A string containing the return value of the called method.</returns>
#if WINDOWS || ANDROID || IOS || MACCATALYST
public async Task<string?> InvokeJavaScriptAsync(string methodName, object?[]? paramValues, JsonTypeInfo?[]? paramJsonTypeInfos = null)
{
if (string.IsNullOrEmpty(methodName))
{
Expand All @@ -81,16 +139,57 @@ public void SendRawMessage(string rawMessage)
throw new ArgumentException($"The number of parameter values does not match the number of parameter JSON type infos.", nameof(paramValues));
}

var invokeResult = await Handler?.InvokeAsync(
nameof(IHybridWebView.InvokeJavaScriptAsync),
new HybridWebViewInvokeJavaScriptRequest(methodName, returnTypeJsonTypeInfo, paramValues, paramJsonTypeInfos))!;
// Create a callback for async JavaScript methods to invoke when they are done
var callback = new TaskCompletionSource<string>();
var currentInvokeTaskId = $"{_invokeTaskId++}";
asyncTaskCallbacks.Add(currentInvokeTaskId, callback);

var paramsValuesStringArray =
paramValues == null
? string.Empty
: string.Join(
", ",
paramValues.Select((v, i) => (v == null ? "null" : JsonSerializer.Serialize(v, paramJsonTypeInfos![i]!))));

if (invokeResult is null)
await EvaluateJavaScriptAsync($"window.HybridWebView.InvokeMethod({currentInvokeTaskId}, {methodName}, [{paramsValuesStringArray}])");

return await callback.Task;
}
#else
public Task<string?> InvokeJavaScriptAsync(string methodName, object?[]? paramValues = null, object?[]? paramJsonTypeInfos = null)
{
_invokeTaskId++; // This is to avoid the compiler warning about the field not being used
throw new NotImplementedException();
}
#endif

/// <summary>
/// Invokes a JavaScript method named <paramref name="methodName"/> and optionally passes in the parameter values specified
/// by <paramref name="paramValues"/> by JSON-encoding each one.
/// </summary>
/// <typeparam name="TReturnType">The type of the return value to deserialize from JSON.</typeparam>
/// <param name="methodName">The name of the JavaScript method to invoke.</param>
/// <param name="returnTypeJsonTypeInfo">Metadata about deserializing the type of the return value specified by <typeparamref name="TReturnType"/>.</param>
/// <param name="paramValues">Optional array of objects to be passed to the JavaScript method by JSON-encoding each one.</param>
/// <param name="paramJsonTypeInfos">Optional array of metadata about serializing the types of the parameters specified by <paramref name="paramValues"/>.</param>
/// <returns>An object of type <typeparamref name="TReturnType"/> containing the return value of the called method.</returns>
#if WINDOWS || ANDROID || IOS || MACCATALYST
public async Task<TReturnType?> InvokeJavaScriptAsync<TReturnType>(string methodName, JsonTypeInfo<TReturnType?> returnTypeJsonTypeInfo, object?[]? paramValues = null, JsonTypeInfo?[]? paramJsonTypeInfos = null)
{
var stringResult = await InvokeJavaScriptAsync(methodName, paramValues, paramJsonTypeInfos);

if (stringResult is null)
{
return default;
}
return (TReturnType)invokeResult;
return JsonSerializer.Deserialize<TReturnType?>(stringResult, returnTypeJsonTypeInfo);
}
#else
public Task<TReturnType?> InvokeJavaScriptAsync<TReturnType>(string methodName, object returnTypeJsonTypeInfo, object?[]? paramValues, object?[]? paramJsonTypeInfos)
{
throw new NotImplementedException();
}
#endif

/// <inheritdoc/>
public async Task<string?> EvaluateJavaScriptAsync(string script)
Expand All @@ -100,9 +199,40 @@ public void SendRawMessage(string rawMessage)
return null;
}

var result = await Handler!.InvokeAsync(nameof(IHybridWebView.EvaluateJavaScriptAsync),
// Make all the platforms mimic Android's implementation, which is by far the most complete.
if (DeviceInfo.Platform != DevicePlatform.Android)
{
script = WebView.EscapeJsString(script);

if (DeviceInfo.Platform != DevicePlatform.WinUI)
{
// Use JSON.stringify() method to converts a JavaScript value to a JSON string
script = "try{JSON.stringify(eval('" + script + "'))}catch(e){'null'};";
}
else
{
script = "try{eval('" + script + "')}catch(e){'null'};";
}
}

string? result;

// Use the handler command to evaluate the JS
result = await Handler!.InvokeAsync(nameof(IHybridWebView.EvaluateJavaScriptAsync),
new EvaluateJavaScriptAsyncRequest(script));

//if the js function errored or returned null/undefined treat it as null
if (result == "null")
{
result = null;
}
//JSON.stringify wraps the result in literal quotes, we just want the actual returned result
//note that if the js function returns the string "null" we will get here and not above
else if (result != null)
{
result = result.Trim('"');
}

return result;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ Microsoft.Maui.Controls.HybridWebView.EvaluateJavaScriptAsync(string! script) ->
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync(string! methodName, object?[]? paramValues, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType?>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ Microsoft.Maui.Controls.HybridWebView.EvaluateJavaScriptAsync(string! script) ->
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync(string! methodName, object?[]? paramValues, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType?>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ Microsoft.Maui.Controls.HybridWebView.EvaluateJavaScriptAsync(string! script) ->
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync(string! methodName, object?[]? paramValues, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType?>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ Microsoft.Maui.Controls.HybridWebView.EvaluateJavaScriptAsync(string! script) ->
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync(string! methodName, object?[]? paramValues = null, object?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, object! returnTypeJsonTypeInfo, object?[]? paramValues, object?[]? paramJsonTypeInfos) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ Microsoft.Maui.Controls.HybridWebView.EvaluateJavaScriptAsync(string! script) ->
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync(string! methodName, object?[]? paramValues, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType?>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Expand Down
3 changes: 2 additions & 1 deletion src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ Microsoft.Maui.Controls.HybridWebView.EvaluateJavaScriptAsync(string! script) ->
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync(string! methodName, object?[]? paramValues = null, object?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, object! returnTypeJsonTypeInfo, object?[]? paramValues, object?[]? paramJsonTypeInfos) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ Microsoft.Maui.Controls.HybridWebView.EvaluateJavaScriptAsync(string! script) ->
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TReturnType>! returnTypeJsonTypeInfo, object?[]? paramValues = null, System.Text.Json.Serialization.Metadata.JsonTypeInfo?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync(string! methodName, object?[]? paramValues = null, object?[]? paramJsonTypeInfos = null) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Controls.HybridWebView.InvokeJavaScriptAsync<TReturnType>(string! methodName, object! returnTypeJsonTypeInfo, object?[]? paramValues, object?[]? paramJsonTypeInfos) -> System.Threading.Tasks.Task<TReturnType?>!
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Core/WebView/WebView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ public IPlatformElementConfiguration<T, WebView> On<T>() where T : IConfigPlatfo
return _platformConfigurationRegistry.Value.On<T>();
}

private static string EscapeJsString(string js)
internal static string EscapeJsString(string js)
{
if (js == null)
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
#if !ANDROID
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
Expand Down Expand Up @@ -355,3 +356,4 @@ private async Task WaitForHybridWebViewLoaded(HybridWebView hybridWebView)
}
}
}
#endif
1 change: 0 additions & 1 deletion src/Core/src/Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<ProjectReference Include="..\..\Graphics\src\Graphics\Graphics.csproj" />
<PackageReference Include="System.Numerics.Vectors" Condition="$(TargetFramework.StartsWith('netstandard'))" />
<PackageReference Include="System.Text.Json" Condition="$(TargetFramework.StartsWith('netstandard'))" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.Contains('-windows'))">
<PackageReference Include="Microsoft.WindowsAppSDK" />
Expand Down
Loading

0 comments on commit c6ca951

Please sign in to comment.