forked from dotnet/maui
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add BlazorWebView skeleton code (#62)
- Loading branch information
1 parent
7c29524
commit 898c684
Showing
4 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
144 changes: 144 additions & 0 deletions
144
src/BlazorWebView/src/Maui/Tizen/BlazorWebViewHandler.Tizen.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
using System; | ||
using System.IO; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.FileProviders; | ||
using Microsoft.Maui.Handlers; | ||
using TChromium = Tizen.WebView.Chromium; | ||
using TWebView = Tizen.WebView.WebView; | ||
|
||
namespace Microsoft.AspNetCore.Components.WebView.Maui | ||
{ | ||
public partial class BlazorWebViewHandler : ViewHandler<IBlazorWebView, WebViewContainer> | ||
{ | ||
private const string AppOrigin = "app://0.0.0.0/"; | ||
private const string BlazorInitScript = @" | ||
window.__receiveMessageCallbacks = []; | ||
window.__dispatchMessageCallback = function(message) { | ||
window.__receiveMessageCallbacks.forEach(function(callback) { callback(message); }); | ||
}; | ||
window.external = { | ||
sendMessage: function(message) { | ||
window.webkit.messageHandlers.webwindowinterop.postMessage(message); | ||
}, | ||
receiveMessage: function(callback) { | ||
window.__receiveMessageCallbacks.push(callback); | ||
} | ||
}; | ||
Blazor.start(); | ||
(function () { | ||
window.onpageshow = function(event) { | ||
if (event.persisted) { | ||
window.location.reload(); | ||
} | ||
}; | ||
})(); | ||
"; | ||
|
||
private TizenWebViewManager? _webviewManager; | ||
private WebViewExtension.InterceptRequestCallback? _interceptRequestCallback; | ||
|
||
private TWebView NativeWebView => NativeView.WebView; | ||
|
||
private bool RequiredStartupPropertiesSet => | ||
//_webview != null && | ||
HostPage != null && | ||
Services != null; | ||
|
||
protected override WebViewContainer CreateNativeView() | ||
{ | ||
TChromium.Initialize(); | ||
Context!.CurrentApplication!.Terminated += (s, e) => TChromium.Shutdown(); | ||
return new WebViewContainer(Context!.NativeParent); | ||
} | ||
|
||
protected override void ConnectHandler(WebViewContainer nativeView) | ||
{ | ||
_interceptRequestCallback = OnRequestInterceptCallback; | ||
NativeWebView.LoadStarted += OnLoadStarted; | ||
NativeWebView.LoadFinished += OnLoadFinished; | ||
//NativeWebView.AddJavaScriptMessageHandler("BlazorHandler", PostMessageFromJS); | ||
NativeWebView.SetInterceptRequestCallback(_interceptRequestCallback); | ||
NativeWebView.GetSettings().JavaScriptEnabled = true; | ||
|
||
} | ||
|
||
protected override void DisconnectHandler(WebViewContainer nativeView) | ||
{ | ||
base.DisconnectHandler(nativeView); | ||
} | ||
|
||
private void StartWebViewCoreIfPossible() | ||
{ | ||
if (!RequiredStartupPropertiesSet || | ||
_webviewManager != null) | ||
{ | ||
return; | ||
} | ||
if (NativeView == null) | ||
{ | ||
throw new InvalidOperationException($"Can't start {nameof(BlazorWebView)} without native web view instance."); | ||
} | ||
|
||
var assetConfig = Services!.GetRequiredService<BlazorAssetsAssemblyConfiguration>()!; | ||
|
||
// We assume the host page is always in the root of the content directory, because it's | ||
// unclear there's any other use case. We can add more options later if so. | ||
var contentRootDir = Path.GetDirectoryName(HostPage!) ?? string.Empty; | ||
var hostPageRelativePath = Path.GetRelativePath(contentRootDir, HostPage!); | ||
|
||
var fileProvider = new ManifestEmbeddedFileProvider(assetConfig.AssetsAssembly, root: contentRootDir); | ||
|
||
_webviewManager = new TizenWebViewManager(this, NativeWebView, Services!, MauiDispatcher.Instance, fileProvider, hostPageRelativePath); | ||
if (RootComponents != null) | ||
{ | ||
foreach (var rootComponent in RootComponents) | ||
{ | ||
// Since the page isn't loaded yet, this will always complete synchronously | ||
_ = rootComponent.AddToWebViewManagerAsync(_webviewManager); | ||
} | ||
} | ||
_webviewManager.Navigate("/"); | ||
} | ||
|
||
private void OnRequestInterceptCallback(IntPtr context, IntPtr request, IntPtr userdata) | ||
{ | ||
if (request == IntPtr.Zero) | ||
{ | ||
throw new ArgumentNullException(nameof(request)); | ||
} | ||
|
||
var url = NativeWebView.GetInterceptRequestUrl(request); | ||
var urlScheme = url.Substring(0, url.IndexOf(':')); | ||
|
||
if (urlScheme == "app") | ||
{ | ||
var allowFallbackOnHostPage = url.EndsWith("/"); | ||
if (_webviewManager!.TryGetResponseContentInternal(url, allowFallbackOnHostPage, out var statusCode, out var statusMessage, out var content, out var headers)) | ||
{ | ||
var contentType = headers["Content-Type"]; | ||
var header = $"HTTP/1.0 200 OK\r\nContent-Type:{contentType}; charset=utf-8\r\nCache-Control:no-cache, max-age=0, must-revalidate, no-store\r\n\r\n"; | ||
var body = new StreamReader(content).ReadToEnd(); | ||
NativeWebView.SetInterceptRequestResponse(request, header, body, (uint)body.Length); | ||
return; | ||
} | ||
} | ||
|
||
NativeWebView.IgnoreInterceptRequest(request); | ||
} | ||
|
||
private void OnLoadStarted(object? sender, EventArgs e) | ||
{ | ||
} | ||
|
||
private void OnLoadFinished(object? sender, EventArgs e) | ||
{ | ||
NativeWebView.SetFocus(true); | ||
|
||
var url = NativeWebView.Url; | ||
if (url == AppOrigin) | ||
NativeWebView.Eval(BlazorInitScript); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using Microsoft.Extensions.FileProviders; | ||
using TWebView = Tizen.WebView.WebView; | ||
|
||
namespace Microsoft.AspNetCore.Components.WebView.Maui | ||
{ | ||
public class TizenWebViewManager : WebViewManager | ||
{ | ||
private const string AppOrigin = "app://0.0.0.0/"; | ||
|
||
private readonly BlazorWebViewHandler _blazorMauiWebViewHandler; | ||
private readonly TWebView _webview; | ||
|
||
public TizenWebViewManager(BlazorWebViewHandler blazorMauiWebViewHandler, TWebView webview, IServiceProvider services, Dispatcher dispatcher, IFileProvider fileProvider, string hostPageRelativePath) | ||
: base(services, dispatcher, new Uri(AppOrigin), fileProvider, hostPageRelativePath) | ||
{ | ||
_blazorMauiWebViewHandler = blazorMauiWebViewHandler ?? throw new ArgumentNullException(nameof(blazorMauiWebViewHandler)); | ||
_webview = webview ?? throw new ArgumentNullException(nameof(webview)); | ||
|
||
} | ||
|
||
internal bool TryGetResponseContentInternal(string uri, bool allowFallbackOnHostPage, out int statusCode, out string statusMessage, out Stream content, out IDictionary<string, string> headers) => | ||
TryGetResponseContent(uri, allowFallbackOnHostPage, out statusCode, out statusMessage, out content, out headers); | ||
|
||
/// <inheritdoc /> | ||
protected override void NavigateCore(Uri absoluteUri) | ||
{ | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override void SendMessage(string message) | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using System; | ||
using Tizen.UIExtensions.ElmSharp; | ||
using ElmSharp; | ||
using TWebView = Tizen.WebView.WebView; | ||
|
||
namespace Microsoft.AspNetCore.Components.WebView.Maui | ||
{ | ||
public class WebViewContainer : WidgetLayout | ||
{ | ||
public TWebView WebView { get; } | ||
|
||
public WebViewContainer(EvasObject parent) : base(parent) | ||
{ | ||
WebView = new TWebView(parent); | ||
SetContent(WebView); | ||
AllowFocus(true); | ||
Focused += OnFocused; | ||
Unfocused += OnUnfocused; | ||
} | ||
|
||
void OnFocused(object? sender, EventArgs e) | ||
{ | ||
WebView.SetFocus(true); | ||
} | ||
|
||
void OnUnfocused(object? sender, EventArgs e) | ||
{ | ||
WebView.SetFocus(false); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
using TWebView = Tizen.WebView.WebView; | ||
|
||
namespace Microsoft.AspNetCore.Components.WebView.Maui | ||
{ | ||
public static class WebViewExtension | ||
{ | ||
public const string ChromiumEwk = "libchromium-ewk.so"; | ||
|
||
public static void SetInterceptRequestCallback(this TWebView webView, InterceptRequestCallback callback) | ||
{ | ||
var context = webView.GetContext(); | ||
var handleField = context.GetType().GetField("_handle", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); | ||
var contextHandle = (IntPtr?)handleField?.GetValue(context); | ||
if (contextHandle != null) | ||
ewk_context_intercept_request_callback_set(contextHandle.Value, callback, IntPtr.Zero); | ||
} | ||
|
||
#pragma warning disable IDE0060 // Remove unused parameter | ||
public static bool SetInterceptRequestResponse(this TWebView webView, IntPtr request, string header, string body, uint length) | ||
#pragma warning restore IDE0060 // Remove unused parameter | ||
{ | ||
return ewk_intercept_request_response_set(request, header, body, length); | ||
} | ||
|
||
#pragma warning disable IDE0060 // Remove unused parameter | ||
public static bool IgnoreInterceptRequest(this TWebView webView, IntPtr request) | ||
#pragma warning restore IDE0060 // Remove unused parameter | ||
{ | ||
return ewk_intercept_request_ignore(request); | ||
} | ||
|
||
#pragma warning disable IDE0060 // Remove unused parameter | ||
public static string GetInterceptRequestUrl(this TWebView webView, IntPtr request) | ||
#pragma warning restore IDE0060 // Remove unused parameter | ||
{ | ||
return Marshal.PtrToStringAnsi(_ewk_intercept_request_url_get(request)) ?? string.Empty; | ||
} | ||
|
||
[DllImport(ChromiumEwk)] | ||
internal static extern IntPtr ewk_view_context_get(IntPtr obj); | ||
|
||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] | ||
public delegate void InterceptRequestCallback(IntPtr context, IntPtr request, IntPtr userData); | ||
|
||
[DllImport(ChromiumEwk)] | ||
internal static extern void ewk_context_intercept_request_callback_set(IntPtr context, InterceptRequestCallback callback, IntPtr userData); | ||
|
||
[DllImport(ChromiumEwk, EntryPoint = "ewk_intercept_request_url_get")] | ||
internal static extern IntPtr _ewk_intercept_request_url_get(IntPtr request); | ||
|
||
[DllImport(ChromiumEwk, EntryPoint = "ewk_intercept_request_http_method_get")] | ||
internal static extern IntPtr _ewk_intercept_request_http_method_get(IntPtr request); | ||
|
||
internal static string ewk_intercept_request_http_method_get(IntPtr request) | ||
{ | ||
return Marshal.PtrToStringAnsi(_ewk_intercept_request_http_method_get(request)) ?? string.Empty; | ||
} | ||
|
||
[DllImport(ChromiumEwk)] | ||
internal static extern bool ewk_intercept_request_ignore(IntPtr request); | ||
|
||
#pragma warning disable CA2101 // Specify marshaling for P/Invoke string arguments | ||
[DllImport(ChromiumEwk)] | ||
#pragma warning restore CA2101 // Specify marshaling for P/Invoke string arguments | ||
internal static extern bool ewk_intercept_request_response_set(IntPtr request, string header, string body, uint length); | ||
} | ||
} |