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

Request intercept and proxy improvements #73

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions ExampleWrappedHybridWebViewLibrary/EmbeddedWebPageResource.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="_hwv/HybridWebView.js"></script>
<link href="styles/my-styles.css" rel="stylesheet" />
</head>
<body>
<h1>HybridWebView demo: Embedded Resource page</h1>
<div>
This web page is an embedded resource. It can access resources from in the
same way as normal via the HybridAssetRoot (not an embedded resrouce) and other embedded resources.
Native/JS communication works as expected.
This is useful when using the HybridWebView in a .NET Maui library when wrapping a web app that is
to be conusmed by other .NET Maui app.

<p>
<button type="button" onclick="invokeDotNetFibonacciMethod()">Invoke .NET Fibonacci method</button>
<br /><br />
<textarea id="logArea" style="width:100%" rows="10"></textarea>
</p>

<p>
The following image is loaded from an embedded resource via the proxy:
<br />
<br />
<img src="proxy?operation=embeddedResource&resourceName=EmbeddedImage.png" />
</p>
</div>

<script>
async function invokeDotNetFibonacciMethod() {
var log = document.getElementById('logArea');

var fibNumber = Math.round(Math.random() * 30) + 1;
log.value += `Calling .NET Fibonacci method with value: ${fibNumber}\n`;

var r = await HybridWebView.SendInvokeMessageToDotNetAsync('Fibonacci', [fibNumber]);
log.value += `C# Fibonacci result for ${fibNumber}: ${r}\n`;
}
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>

<ItemGroup>
<None Remove="EmbeddedImage.png" />
<None Remove="EmbeddedWebPageResource.html" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="EmbeddedImage.png" />
<EmbeddedResource Include="EmbeddedWebPageResource.html" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\HybridWebView\HybridWebView.csproj" />
</ItemGroup>

</Project>
122 changes: 122 additions & 0 deletions ExampleWrappedHybridWebViewLibrary/MyCustomControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using HybridWebView;

namespace ExampleWrappedHybridWebViewLibrary
{
/// <summary>
/// A custom control that wraps a hybrid web view and loads embedded resources in addition to supporting the
/// Hybrid asset root and all other capabilites of the HybridWebView.
/// </summary>
public class MyCustomControl: Grid
{
private readonly HybridWebView.HybridWebView _webView;

#region Constructor

public MyCustomControl() : base()
{
bool enableWebDevTools = false;

#if DEBUG
//Enable web dev tools when in debug mode.
enableWebDevTools = true;
#endif

//Create a web view control.
_webView = new HybridWebView.HybridWebView
{
HybridAssetRoot = HybridAssetRoot ?? "hybrid_root",
MainFile = "proxy?operation=embeddedResource&resourceName=EmbeddedWebPageResource.html",
EnableWebDevTools = enableWebDevTools
};

//Set the target for JavaScript interop.
_webView.JSInvokeTarget = new MyJSInvokeTarget();

//Monitor proxy requests.
_webView.ProxyRequestReceived += WebView_ProxyRequestReceived;

#if WINDOWS
//In Windows, disable manual user zooming of web pages.
_webView.HybridWebViewInitialized += (s, e) =>
{
//Disable the user manually zooming. Don't want the user accidentally zooming the HTML page.
e.WebView.CoreWebView2.Settings.IsZoomControlEnabled = false;
};
#endif

//Add the web view to the control.
this.Children.Insert(0, _webView);
}

#endregion

#region Public Properties

public string? HybridAssetRoot { get; set; } = null;

#endregion

#region Private Methods

private async Task WebView_ProxyRequestReceived(HybridWebView.HybridWebViewProxyEventArgs args)
{
// Check to see if our custom parameter is present.
if (args.QueryParams.ContainsKey("operation"))
{
switch (args.QueryParams["operation"])
{
case "embeddedResource":
if (args.QueryParams.TryGetValue("resourceName", out string? resourceName) && !string.IsNullOrWhiteSpace(resourceName))
{
var thisAssembly = typeof(MyCustomControl).Assembly;
var assemblyName = thisAssembly.GetName().Name;
using (var fs = thisAssembly.GetManifestResourceStream($"{assemblyName}.{resourceName.Replace("/", ".")}"))
{
if (fs != null)
{
var ms = new MemoryStream();
await fs.CopyToAsync(ms);
ms.Position = 0;

args.ResponseStream = ms;
args.ResponseContentType = PathUtils.GetMimeType(resourceName);
}
}
}
break;
default:
break;
}
}
}

private sealed class MyJSInvokeTarget
{
public MyJSInvokeTarget()
{
}

/// <summary>
/// An example of a round trip method that takes an input parameter and returns a simple value type (number).
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
public double Fibonacci(int n)
{
if (n == 0) return 0;

int prev = 0;
int next = 1;
for (int i = 1; i < n; i++)
{
int sum = prev + next;
prev = next;
next = sum;
}
return next;
}
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ExampleWrappedHybridWebViewLibrary
{
// All the code in this file is only included on Android.
public class PlatformClass1
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ExampleWrappedHybridWebViewLibrary
{
// All the code in this file is only included on Mac Catalyst.
public class PlatformClass1
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace ExampleWrappedHybridWebViewLibrary
{
// All the code in this file is only included on Tizen.
public class PlatformClass1
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ExampleWrappedHybridWebViewLibrary
{
// All the code in this file is only included on Windows.
public class PlatformClass1
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ExampleWrappedHybridWebViewLibrary
{
// All the code in this file is only included on iOS.
public class PlatformClass1
{
}
}
18 changes: 18 additions & 0 deletions HybridWebView/HybridWebViewProxyEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ public HybridWebViewProxyEventArgs(string fullUrl)
QueryParams = QueryStringHelper.GetKeyValuePairs(fullUrl);
}

/// <summary>
/// Creates a new instance of <see cref="HybridWebViewProxyEventArgs"/>.
/// </summary>
/// <param name="fullUrl">The full request URL.</param>
/// <param name="contentType">The estimated response content type based on uri parsing.</param>
public HybridWebViewProxyEventArgs(string fullUrl, string contentType)
{
Url = fullUrl;
ResponseContentType = contentType;
QueryParams = QueryStringHelper.GetKeyValuePairs(fullUrl);
}

/// <summary>
/// The full request URL.
/// </summary>
Expand All @@ -35,5 +47,11 @@ public HybridWebViewProxyEventArgs(string fullUrl)
/// The response stream to be used to respond to the request.
/// </summary>
public Stream? ResponseStream { get; set; } = null;

/// <summary>
/// Additional headers to be added to the response.
/// Useful for things like adding a cache control header.
/// </summary>
public IDictionary<string, string>? CustomResponseHeaders { get; set; }
}
}
Loading
Loading