From c7bdd9671999b436d9005075bb91d8846e1c3d64 Mon Sep 17 00:00:00 2001 From: Eilon Lipton Date: Fri, 4 Jun 2021 11:01:36 -0700 Subject: [PATCH] Use new Blazor WebView2 abstractions --- .../Windows/BlazorWebViewHandler.Windows.cs | 3 +- .../src/core/Windows/IWebView2Wrapper.cs | 41 -------- .../CoreWebView2WebResourceContextWrapper.cs | 83 +++++++++++++++++ ...w2AcceleratorKeyPressedEventArgsWrapper.cs | 32 +++++++ .../ICoreWebView2SettingsWrapper.cs | 93 +++++++++++++++++++ .../ICoreWebView2WebResourceRequestWrapper.cs | 38 ++++++++ ...ew2WebResourceRequestedEventArgsWrapper.cs | 38 ++++++++ .../SharedSource/ICoreWebView2Wrapper.cs | 52 +++++++++++ .../Windows/SharedSource/IWebView2Wrapper.cs | 48 ++++++++++ .../WebMessageReceivedEventArgs.cs | 37 ++++++++ .../WebView2WebViewManager.cs | 78 +++++++--------- .../WinUICoreWebView2SettingsWrapper.cs | 61 ++++++++++++ ...UICoreWebView2WebResourceRequestWrapper.cs | 26 ++++++ ...ew2WebResourceRequestedEventArgsWrapper.cs | 39 ++++++++ .../core/Windows/WinUICoreWebView2Wrapper.cs | 82 ++++++++++++++++ .../src/core/Windows/WinUIWebView2Wrapper.cs | 42 +++++---- .../src/core/Windows/WinUIWebViewManager.cs | 32 +++++++ 17 files changed, 722 insertions(+), 103 deletions(-) delete mode 100644 src/BlazorWebView/src/core/Windows/IWebView2Wrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/SharedSource/CoreWebView2WebResourceContextWrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2AcceleratorKeyPressedEventArgsWrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2SettingsWrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2WebResourceRequestWrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2WebResourceRequestedEventArgsWrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2Wrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/SharedSource/IWebView2Wrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/SharedSource/WebMessageReceivedEventArgs.cs rename src/BlazorWebView/src/core/Windows/{ => SharedSource}/WebView2WebViewManager.cs (69%) create mode 100644 src/BlazorWebView/src/core/Windows/WinUICoreWebView2SettingsWrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/WinUICoreWebView2WebResourceRequestWrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/WinUICoreWebView2WebResourceRequestedEventArgsWrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/WinUICoreWebView2Wrapper.cs create mode 100644 src/BlazorWebView/src/core/Windows/WinUIWebViewManager.cs diff --git a/src/BlazorWebView/src/core/Windows/BlazorWebViewHandler.Windows.cs b/src/BlazorWebView/src/core/Windows/BlazorWebViewHandler.Windows.cs index 2a1c5f5e9725..105f0434e2e5 100644 --- a/src/BlazorWebView/src/core/Windows/BlazorWebViewHandler.Windows.cs +++ b/src/BlazorWebView/src/core/Windows/BlazorWebViewHandler.Windows.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Microsoft.AspNetCore.Components.WebView.WebView2; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Maui.Handlers; @@ -50,7 +51,7 @@ private void StartWebViewCoreIfPossible() var fileProvider = new ManifestEmbeddedFileProvider(assetConfig.AssetsAssembly, root: contentRootDir); - _webviewManager = new WebView2WebViewManager(new WinUIWebView2Wrapper(NativeView), Services!, MauiDispatcher.Instance, fileProvider, hostPageRelativePath); + _webviewManager = new WinUIWebViewManager(NativeView, new WinUIWebView2Wrapper(NativeView), Services!, MauiDispatcher.Instance, fileProvider, hostPageRelativePath); if (RootComponents != null) { foreach (var rootComponent in RootComponents) diff --git a/src/BlazorWebView/src/core/Windows/IWebView2Wrapper.cs b/src/BlazorWebView/src/core/Windows/IWebView2Wrapper.cs deleted file mode 100644 index 7730915d01c6..000000000000 --- a/src/BlazorWebView/src/core/Windows/IWebView2Wrapper.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading.Tasks; -using Microsoft.Web.WebView2.Core; - -namespace Microsoft.AspNetCore.Components.WebView.WebView2 -{ - /// - /// Provides an abstraction for different UI frameworks to provide access to APIs from - /// and related controls. - /// - public interface IWebView2Wrapper - { - /// - /// Gets the instance on the control. This is only available - /// once the returned by - /// has completed. - /// - CoreWebView2 CoreWebView2 { get; } - - /// - /// Gets or sets the source URI of the control. Setting the source URI causes page navigation. - /// - Uri Source { get; set; } - - /// - /// Initializes the instance on the control. This should only be called once - /// per control. - /// - /// A that can be used to customize the control's behavior. - /// A that will complete once the is initialized and attached to the control. - Task EnsureCoreWebView2Async(CoreWebView2Environment? environment = null); - - /// - /// Event that occurs when an accelerator key is pressed. - /// - event EventHandler AcceleratorKeyPressed; - } -} diff --git a/src/BlazorWebView/src/core/Windows/SharedSource/CoreWebView2WebResourceContextWrapper.cs b/src/BlazorWebView/src/core/Windows/SharedSource/CoreWebView2WebResourceContextWrapper.cs new file mode 100644 index 000000000000..9ebde6a2a2df --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/SharedSource/CoreWebView2WebResourceContextWrapper.cs @@ -0,0 +1,83 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Components.WebView.WebView2 +{ + /// + /// Types in the Microsoft.AspNetCore.Components.WebView.WebView2 are not recommended for use outside + /// of the Blazor framework. These types will change in a future release. + /// + /// Specifies the web resource request contexts. + /// + public enum CoreWebView2WebResourceContextWrapper + { + /// + /// Specifies all resources. + /// + All, + /// + /// Specifies a document resources. + /// + Document, + /// + /// Specifies a CSS resources. + /// + Stylesheet, + /// + /// Specifies an image resources. + /// + Image, + /// + /// Specifies another media resource such as a video. + /// + Media, + /// + /// Specifies a font resource. + /// + Font, + /// + /// Specifies a script resource. + /// + Script, + /// + /// Specifies an XML HTTP request. + /// + XmlHttpRequest, + /// + /// Specifies a Fetch API communication. + /// + Fetch, + /// + /// Specifies a TextTrack resource. + /// + TextTrack, + /// + /// Specifies an EventSource API communication. + /// + EventSource, + /// + /// Specifies a WebSocket API communication. + /// + Websocket, + /// + /// Specifies a Web App Manifest. + /// + Manifest, + /// + /// Specifies a Signed HTTP Exchange. + /// + SignedExchange, + /// + /// Specifies a Ping request. + /// + Ping, + /// + /// Specifies a CSP Violation Report. + /// + CspViolationReport, + /// + /// Specifies an other resource. + /// + Other + } +} diff --git a/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2AcceleratorKeyPressedEventArgsWrapper.cs b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2AcceleratorKeyPressedEventArgsWrapper.cs new file mode 100644 index 000000000000..9674d539fa83 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2AcceleratorKeyPressedEventArgsWrapper.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Components.WebView.WebView2 +{ + /// + /// Types in the Microsoft.AspNetCore.Components.WebView.WebView2 are not recommended for use outside + /// of the Blazor framework. These types will change in a future release. + /// + /// Event args for the AcceleratorKeyPressed event. + /// + public interface ICoreWebView2AcceleratorKeyPressedEventArgsWrapper + { + /// + /// Gets the Win32 virtual key code of the key that was pressed or released. + /// + uint VirtualKey { get; } + + /// + /// Gets the LPARAM value that accompanied the window message. + /// + int KeyEventLParam { get; } + + /// + /// Indicates whether the AcceleratorKeyPressed event is handled by host. + /// + /// + /// If set to true then this prevents the WebView from performing the default action for this accelerator key. Otherwise the WebView will perform the default action for the accelerator key. + /// + bool Handled { get; set; } + } +} diff --git a/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2SettingsWrapper.cs b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2SettingsWrapper.cs new file mode 100644 index 000000000000..2211fbd044d8 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2SettingsWrapper.cs @@ -0,0 +1,93 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Components.WebView.WebView2 +{ + /// + /// Types in the Microsoft.AspNetCore.Components.WebView.WebView2 are not recommended for use outside + /// of the Blazor framework. These types will change in a future release. + /// + /// Defines properties that enable, disable, or modify WebView features. + /// + /// + /// Setting changes made after event do not apply until the next top-level navigation. + /// + public interface ICoreWebView2SettingsWrapper + { + /// + /// Determines whether running JavaScript is enabled in all future navigations in the WebView. + /// + /// + /// This only affects scripts in the document. Scripts injected with runs even if script is disabled. The default value is true. + /// + public bool IsScriptEnabled { get; set; } + + /// + /// Determines whether communication from the host to the top-level HTML document of the WebView is allowed. + /// + /// + /// This is used when loading a new HTML document. If set to true, communication from the host to the top-level HTML document of the WebView is allowed using , , and message event of window.chrome.webview. Communication from the top-level HTML document of the WebView to the host is allowed using window.chrome.webview.postMessage function and the event. If set to false, then communication is disallowed. and fail and window.chrome.webview.postMessage fails by throwing an instance of an Error object. The default value is true. + /// + /// + /// + /// + public bool IsWebMessageEnabled { get; set; } + + /// + /// Determines whether WebView renders the default Javascript dialog box. + /// + /// + /// This is used when loading a new HTML document. If set to false, WebView does not render the default JavaScript dialog box (specifically those displayed by the JavaScript alert, confirm, prompt functions and beforeunload event). Instead, WebView raises event that contains all of the information for the dialog and allow the host app to show a custom UI. The default value is true. + /// + /// + public bool AreDefaultScriptDialogsEnabled { get; set; } + + /// + /// Determines whether the status bar is displayed. + /// + /// + /// The status bar is usually displayed in the lower left of the WebView and shows things such as the URI of a link when the user hovers over it and other information. The default value is true. + /// + public bool IsStatusBarEnabled { get; set; } + + /// + /// Determines whether the user is able to use the context menu or keyboard shortcuts to open the DevTools window. + /// + /// + /// The default value is true. + /// + public bool AreDevToolsEnabled { get; set; } + + /// + /// Determines whether the default context menus are shown to the user in WebView. + /// + /// + /// The default value is true. + /// + public bool AreDefaultContextMenusEnabled { get; set; } + + /// + /// Determines whether host objects are accessible from the page in WebView. + /// + /// + /// The default value is true. + /// + public bool AreHostObjectsAllowed { get; set; } + + /// + /// Determines whether the user is able to impact the zoom of the WebView. + /// + /// + /// When disabled, the user is not able to zoom using Ctrl++, Ctr+-, or Ctrl+mouse wheel, but the zoom is set using property. The default value is true. + /// + public bool IsZoomControlEnabled { get; set; } + + /// + /// Determines whether to disable built in error page for navigation failure and render process failure. + /// + /// + /// When disabled, blank page is displayed when related error happens. The default value is true. + /// + public bool IsBuiltInErrorPageEnabled { get; set; } + } +} diff --git a/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2WebResourceRequestWrapper.cs b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2WebResourceRequestWrapper.cs new file mode 100644 index 000000000000..e8cf3e483389 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2WebResourceRequestWrapper.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Components.WebView.WebView2 +{ + /// + /// Types in the Microsoft.AspNetCore.Components.WebView.WebView2 are not recommended for use outside + /// of the Blazor framework. These types will change in a future release. + /// + /// An HTTP request used with the event. + /// + public interface ICoreWebView2WebResourceRequestWrapper + { + /// + /// Gets or sets the request URI. + /// + string Uri { get; set; } + + /// + /// Gets or sets the HTTP request method. + /// + string Method { get; set; } + + ///// + ///// Gets or sets the HTTP request message body as stream. + ///// + ///// + ///// POST data should be here. If a stream is set, which overrides the message body, the stream must have all the content data available by the time the event deferral of this request is completed. Stream should be agile or be created from a background STA to prevent performance impact to the UI thread. null means no content data. + ///// + ///// + //Stream Content { get; set; } + + ///// + ///// Gets the mutable HTTP request headers. + ///// + //ICoreWebView2HttpRequestHeadersWrapper Headers { get; } + } +} diff --git a/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2WebResourceRequestedEventArgsWrapper.cs b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2WebResourceRequestedEventArgsWrapper.cs new file mode 100644 index 000000000000..9260ee10475b --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2WebResourceRequestedEventArgsWrapper.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; + +namespace Microsoft.AspNetCore.Components.WebView.WebView2 +{ + /// + /// Types in the Microsoft.AspNetCore.Components.WebView.WebView2 are not recommended for use outside + /// of the Blazor framework. These types will change in a future release. + /// + /// Event args for the WebResourceRequested event. + /// + public interface ICoreWebView2WebResourceRequestedEventArgsWrapper + { + /// + /// Gets the web resource request. + /// + /// + /// The request object may be missing some headers that are added by network stack at a later time. + /// + ICoreWebView2WebResourceRequestWrapper Request { get; } + + /// + /// Gets the web resource request context. + /// + CoreWebView2WebResourceContextWrapper ResourceContext { get; } + + /// + /// Set the response content for this web resource. + /// + /// + /// + /// + /// + void SetResponse(Stream content, int statusCode, string statusMessage, string headerString); + } +} diff --git a/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2Wrapper.cs b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2Wrapper.cs new file mode 100644 index 000000000000..284f160db5b3 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/SharedSource/ICoreWebView2Wrapper.cs @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Components.WebView.WebView2 +{ + /// + /// Types in the Microsoft.AspNetCore.Components.WebView.WebView2 are not recommended for use outside + /// of the Blazor framework. These types will change in a future release. + /// + /// Defines an abstraction for different WebView2 implementations on different platforms. + /// + public interface ICoreWebView2Wrapper + { + /// + /// Gets the object contains various modifiable settings for the running WebView. + /// + public ICoreWebView2SettingsWrapper Settings { get; } + + /// + /// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created, but before the HTML document has been parsed and before any other script included by the HTML document is run. + /// + /// The JavaScript code to be run. + Task AddScriptToExecuteOnDocumentCreatedAsync(string javaScript); + + + /// + /// WebMessageReceived is raised when the IsWebMessageEnabled setting is set and the top-level document of the WebView runs window.chrome.webview.postMessage. + /// + Action AddWebMessageReceivedHandler(Action messageReceivedHandler); + + /// + /// Adds a URI and resource context filter to the WebResourceRequested event. + /// + /// An URI to be added to the WebResourceRequested event. + /// A resource context filter to be added to the WebResourceRequested event. + void AddWebResourceRequestedFilter(string uri, CoreWebView2WebResourceContextWrapper resourceContext); + + /// + /// WebResourceRequested is raised when the WebView is performing a URL request to a matching URL and resource context filter that was added with AddWebResourceRequestedFilter />. + /// + Action AddWebResourceRequestedHandler(EventHandler webResourceRequestedHandler); + + /// + /// Posts a message that is a simple rather than a JSON string representation of a JavaScript object. + /// + /// The web message to be posted to the top level document in this WebView. + void PostWebMessageAsString(string webMessageAsString); + } +} diff --git a/src/BlazorWebView/src/core/Windows/SharedSource/IWebView2Wrapper.cs b/src/BlazorWebView/src/core/Windows/SharedSource/IWebView2Wrapper.cs new file mode 100644 index 000000000000..f5dcd61d3f70 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/SharedSource/IWebView2Wrapper.cs @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Components.WebView.WebView2 +{ + /// + /// Types in the Microsoft.AspNetCore.Components.WebView.WebView2 are not recommended for use outside + /// of the Blazor framework. These types will change in a future release. + /// + /// Provides an abstraction for different UI frameworks to provide access to APIs from + /// Microsoft.Web.WebView2.Core.CoreWebView2 and related controls. + /// + public interface IWebView2Wrapper + { + /// + /// Creates a WebView2 Environment using the installed or a custom WebView2 Runtime version. + /// The implementation should store the CoreWebView2Environment created in this method so that it can + /// be used in . + /// + Task CreateEnvironmentAsync(); + + /// + /// The underlying CoreWebView2. Use this property to perform more operations on the WebView2 content than is exposed + /// on the WebView2. This value is null until it is initialized. You can force the underlying CoreWebView2 to + /// initialize via the InitializeAsync method. + /// + ICoreWebView2Wrapper CoreWebView2 { get; } + + /// + /// Gets or sets the source URI of the control. Setting the source URI causes page navigation. + /// + Uri Source { get; set; } + + /// + /// Explicitly trigger initialization of the control's CoreWebView2. The implementation should use the CoreWebView2Environment that was + /// created in . + /// + Task EnsureCoreWebView2Async(); + + /// + /// Event that occurs when an accelerator key is pressed. + /// + Action AddAcceleratorKeyPressedHandler(EventHandler eventHandler); + } +} diff --git a/src/BlazorWebView/src/core/Windows/SharedSource/WebMessageReceivedEventArgs.cs b/src/BlazorWebView/src/core/Windows/SharedSource/WebMessageReceivedEventArgs.cs new file mode 100644 index 000000000000..a5817424aab0 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/SharedSource/WebMessageReceivedEventArgs.cs @@ -0,0 +1,37 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Components.WebView.WebView2 +{ + /// + /// Types in the Microsoft.AspNetCore.Components.WebView.WebView2 are not recommended for use outside + /// of the Blazor framework. These types will change in a future release. + /// + /// Event arguments for the WebMessageReceived event. + /// + public class WebMessageReceivedEventArgs : EventArgs + { + /// + /// Constructs a new instance of with the provider source and message. + /// + /// The URI of the document that sent this web message. + /// the message posted from the WebView content to the host as a . + public WebMessageReceivedEventArgs(string source, string webMessageAsString) + { + Source = source; + WebMessageAsString = webMessageAsString; + } + + /// + /// Gets the URI of the document that sent this web message. + /// + public string Source { get; } + + /// + /// Gets the message posted from the WebView content to the host as a . + /// + public string WebMessageAsString { get; } + } +} diff --git a/src/BlazorWebView/src/core/Windows/WebView2WebViewManager.cs b/src/BlazorWebView/src/core/Windows/SharedSource/WebView2WebViewManager.cs similarity index 69% rename from src/BlazorWebView/src/core/Windows/WebView2WebViewManager.cs rename to src/BlazorWebView/src/core/Windows/SharedSource/WebView2WebViewManager.cs index 4359ff5ac9c9..e199e2b97d7e 100644 --- a/src/BlazorWebView/src/core/Windows/WebView2WebViewManager.cs +++ b/src/BlazorWebView/src/core/Windows/SharedSource/WebView2WebViewManager.cs @@ -1,15 +1,13 @@ -using System; +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components.WebView.WebView2; using Microsoft.Extensions.FileProviders; -using Microsoft.Web.WebView2.Core; -using Windows.Storage.Streams; -namespace Microsoft.AspNetCore.Components.WebView.Maui +namespace Microsoft.AspNetCore.Components.WebView.WebView2 { /// /// An implementation of that uses the Edge WebView2 browser control @@ -60,36 +58,30 @@ protected override void SendMessage(string message) private async Task InitializeWebView2() { - var environment = await CoreWebView2Environment.CreateAsync(); - await _webview.EnsureCoreWebView2Async(environment); + await _webview.CreateEnvironmentAsync().ConfigureAwait(true); + await _webview.EnsureCoreWebView2Async(); ApplyDefaultWebViewSettings(); - _webview.CoreWebView2.AddWebResourceRequestedFilter($"{AppOrigin}*", CoreWebView2WebResourceContext.All); - _webview.CoreWebView2.WebResourceRequested += (sender, eventArgs) => + _webview.CoreWebView2.AddWebResourceRequestedFilter($"{AppOrigin}*", CoreWebView2WebResourceContextWrapper.All); + var removeResourceCallback = _webview.CoreWebView2.AddWebResourceRequestedHandler((s, eventArgs) => { // Unlike server-side code, we get told exactly why the browser is making the request, // so we can be smarter about fallback. We can ensure that 'fetch' requests never result // in fallback, for example. var allowFallbackOnHostPage = - eventArgs.ResourceContext == CoreWebView2WebResourceContext.Document || - eventArgs.ResourceContext == CoreWebView2WebResourceContext.Other; // e.g., dev tools requesting page source + eventArgs.ResourceContext == CoreWebView2WebResourceContextWrapper.Document || + eventArgs.ResourceContext == CoreWebView2WebResourceContextWrapper.Other; // e.g., dev tools requesting page source if (TryGetResponseContent(eventArgs.Request.Uri, allowFallbackOnHostPage, out var statusCode, out var statusMessage, out var content, out var headers)) { - // NOTE: This is stream copying is to work around a hanging bug in WinRT with managed streams - var memStream = new MemoryStream(); - content.CopyTo(memStream); - var ms = new InMemoryRandomAccessStream(); - ms.WriteAsync(memStream.GetWindowsRuntimeBuffer()).AsTask().Wait(); - var headerString = GetHeaderString(headers); - eventArgs.Response = environment.CreateWebResourceResponse(ms, statusCode, statusMessage, headerString); + eventArgs.SetResponse(content, statusCode, statusMessage, headerString); } - }; + }); // The code inside blazor.webview.js is meant to be agnostic to specific webview technologies, // so the following is an adaptor from blazor.webview.js conventions to WebView2 APIs - await _webview.CoreWebView2!.AddScriptToExecuteOnDocumentCreatedAsync(@" + await _webview.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(@" window.external = { sendMessage: message => { window.chrome.webview.postMessage(message); @@ -98,17 +90,19 @@ private async Task InitializeWebView2() window.chrome.webview.addEventListener('message', e => callback(e.data)); } }; - "); + ").ConfigureAwait(true); - _webview.CoreWebView2.WebMessageReceived += (sender, eventArgs) - => MessageReceived(new Uri(eventArgs.Source), eventArgs.TryGetWebMessageAsString()); + QueueBlazorStart(); - _webview.CoreWebView2.DOMContentLoaded += async (_, __) => - { - await _webview.CoreWebView2!.ExecuteScriptAsync(@" - Blazor.start(); - "); - }; + var removeMessageCallback = _webview.CoreWebView2.AddWebMessageReceivedHandler(e + => MessageReceived(new Uri(e.Source), e.WebMessageAsString)); + } + + /// + /// Override this method to queue a call to Blazor.start(). Not all platforms require this. + /// + protected virtual void QueueBlazorStart() + { } private static string GetHeaderString(IDictionary headers) => @@ -122,21 +116,19 @@ private void ApplyDefaultWebViewSettings() // Desktop applications almost never want to show a URL preview when hovering over a link _webview.CoreWebView2.Settings.IsStatusBarEnabled = false; - // TODO: This event doesn't exist in WinUI - // Desktop applications don't normally want to enable things like "alt-left to go back" // or "ctrl+f to find". Developers should explicitly opt into allowing these. // Issues #30511 and #30624 track making an option to control this. - //_webview.AcceleratorKeyPressed += (sender, eventArgs) => - //{ - // if (eventArgs.VirtualKey != 0x49) // Allow ctrl+shift+i to open dev tools, at least for now - // { - // // Note: due to what seems like a bug (https://github.com/MicrosoftEdge/WebView2Feedback/issues/549), - // // setting eventArgs.Handled doesn't actually have any effect in WPF, even though it works fine in - // // WinForms. Leaving the code here because it's supposedly fixed in a newer version. - // eventArgs.Handled = true; - // } - //}; + var removeKeyPressCallback = _webview.AddAcceleratorKeyPressedHandler((sender, eventArgs) => + { + if (eventArgs.VirtualKey != 0x49) // Allow ctrl+shift+i to open dev tools, at least for now + { + // Note: due to what seems like a bug (https://github.com/MicrosoftEdge/WebView2Feedback/issues/549), + // setting eventArgs.Handled doesn't actually have any effect in WPF, even though it works fine in + // WinForms. Leaving the code here because it's supposedly fixed in a newer version. + eventArgs.Handled = true; + } + }); } } } diff --git a/src/BlazorWebView/src/core/Windows/WinUICoreWebView2SettingsWrapper.cs b/src/BlazorWebView/src/core/Windows/WinUICoreWebView2SettingsWrapper.cs new file mode 100644 index 000000000000..90596256c030 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/WinUICoreWebView2SettingsWrapper.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Components.WebView.WebView2; +using Microsoft.Web.WebView2.Core; + +namespace Microsoft.AspNetCore.Components.WebView.Maui +{ + internal class WinUICoreWebView2SettingsWrapper : ICoreWebView2SettingsWrapper + { + private readonly CoreWebView2Settings _settings; + + public WinUICoreWebView2SettingsWrapper(CoreWebView2Settings settings) + { + _settings = settings; + } + + public bool IsScriptEnabled + { + get => _settings.IsScriptEnabled; + set => _settings.IsScriptEnabled = value; + } + public bool IsWebMessageEnabled + { + get => _settings.IsWebMessageEnabled; + set => _settings.IsWebMessageEnabled = value; + } + public bool AreDefaultScriptDialogsEnabled + { + get => _settings.AreDefaultScriptDialogsEnabled; + set => _settings.AreDefaultScriptDialogsEnabled = value; + } + public bool IsStatusBarEnabled + { + get => _settings.IsStatusBarEnabled; + set => _settings.IsStatusBarEnabled = value; + } + public bool AreDevToolsEnabled + { + get => _settings.AreDevToolsEnabled; + set => _settings.AreDevToolsEnabled = value; + } + public bool AreDefaultContextMenusEnabled + { + get => _settings.AreDefaultContextMenusEnabled; + set => _settings.AreDefaultContextMenusEnabled = value; + } + public bool AreHostObjectsAllowed + { + get => _settings.AreHostObjectsAllowed; + set => _settings.AreHostObjectsAllowed = value; + } + public bool IsZoomControlEnabled + { + get => _settings.IsZoomControlEnabled; + set => _settings.IsZoomControlEnabled = value; + } + public bool IsBuiltInErrorPageEnabled + { + get => _settings.IsBuiltInErrorPageEnabled; + set => _settings.IsBuiltInErrorPageEnabled = value; + } + } +} diff --git a/src/BlazorWebView/src/core/Windows/WinUICoreWebView2WebResourceRequestWrapper.cs b/src/BlazorWebView/src/core/Windows/WinUICoreWebView2WebResourceRequestWrapper.cs new file mode 100644 index 000000000000..ae439eb85d49 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/WinUICoreWebView2WebResourceRequestWrapper.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Components.WebView.WebView2; +using Microsoft.Web.WebView2.Core; + +namespace Microsoft.AspNetCore.Components.WebView.Maui +{ + internal class WinUICoreWebView2WebResourceRequestWrapper : ICoreWebView2WebResourceRequestWrapper + { + private readonly CoreWebView2WebResourceRequestedEventArgs _webResourceRequestedEventArgs; + + public WinUICoreWebView2WebResourceRequestWrapper(CoreWebView2WebResourceRequestedEventArgs webResourceRequestedEventArgs) + { + _webResourceRequestedEventArgs = webResourceRequestedEventArgs; + } + + public string Uri + { + get => _webResourceRequestedEventArgs.Request.Uri; + set => _webResourceRequestedEventArgs.Request.Uri = value; + } + public string Method + { + get => _webResourceRequestedEventArgs.Request.Method; + set => _webResourceRequestedEventArgs.Request.Method = value; + } + } +} diff --git a/src/BlazorWebView/src/core/Windows/WinUICoreWebView2WebResourceRequestedEventArgsWrapper.cs b/src/BlazorWebView/src/core/Windows/WinUICoreWebView2WebResourceRequestedEventArgsWrapper.cs new file mode 100644 index 000000000000..a6de095b2fb2 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/WinUICoreWebView2WebResourceRequestedEventArgsWrapper.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.AspNetCore.Components.WebView.WebView2; +using Microsoft.Web.WebView2.Core; +using Windows.Storage.Streams; + +namespace Microsoft.AspNetCore.Components.WebView.Maui +{ + internal class WinUICoreWebView2WebResourceRequestedEventArgsWrapper : ICoreWebView2WebResourceRequestedEventArgsWrapper + { + private readonly CoreWebView2Environment _environment; + private readonly CoreWebView2WebResourceRequestedEventArgs _webResourceRequestedEventArgs; + + public WinUICoreWebView2WebResourceRequestedEventArgsWrapper(CoreWebView2Environment environment, CoreWebView2WebResourceRequestedEventArgs webResourceRequestedEventArgs) + { + _environment = environment; + _webResourceRequestedEventArgs = webResourceRequestedEventArgs; + + Request = new WinUICoreWebView2WebResourceRequestWrapper(webResourceRequestedEventArgs); + ResourceContext = (CoreWebView2WebResourceContextWrapper)webResourceRequestedEventArgs.ResourceContext; + } + + public ICoreWebView2WebResourceRequestWrapper Request { get; } + + public CoreWebView2WebResourceContextWrapper ResourceContext { get; } + + public void SetResponse(Stream content, int statusCode, string statusMessage, string headerString) + { + // NOTE: This is stream copying is to work around a hanging bug in WinRT with managed streams + var memStream = new MemoryStream(); + content.CopyTo(memStream); + var ms = new InMemoryRandomAccessStream(); + ms.WriteAsync(memStream.GetWindowsRuntimeBuffer()).AsTask().Wait(); + + _webResourceRequestedEventArgs.Response = _environment.CreateWebResourceResponse(ms, statusCode, statusMessage, headerString); + } + } +} diff --git a/src/BlazorWebView/src/core/Windows/WinUICoreWebView2Wrapper.cs b/src/BlazorWebView/src/core/Windows/WinUICoreWebView2Wrapper.cs new file mode 100644 index 000000000000..6dcb0a54d6db --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/WinUICoreWebView2Wrapper.cs @@ -0,0 +1,82 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.WebView.WebView2; +using Microsoft.Web.WebView2.Core; + +namespace Microsoft.AspNetCore.Components.WebView.Maui +{ + internal class WinUICoreWebView2Wrapper : ICoreWebView2Wrapper + { + private readonly WinUIWebView2Wrapper _webView2; + private WinUICoreWebView2SettingsWrapper? _settings; + + public WinUICoreWebView2Wrapper(WinUIWebView2Wrapper webView2) + { + if (webView2 is null) + { + throw new ArgumentNullException(nameof(webView2)); + } + + _webView2 = webView2; + } + + public ICoreWebView2SettingsWrapper Settings + { + get + { + if (_settings == null) + { + _settings = new WinUICoreWebView2SettingsWrapper(_webView2.WebView2.CoreWebView2.Settings); + } + return _settings; + } + } + + public Task AddScriptToExecuteOnDocumentCreatedAsync(string javaScript) + { + return _webView2.WebView2.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(javaScript).AsTask(); + } + + public Action AddWebMessageReceivedHandler(Action messageReceivedHandler) + { + void nativeEventHandler(object sender, CoreWebView2WebMessageReceivedEventArgs e) + { + messageReceivedHandler(new WebMessageReceivedEventArgs(e.Source, e.TryGetWebMessageAsString())); + } + + _webView2.WebView2.CoreWebView2.WebMessageReceived += nativeEventHandler; + + // Return removal callback + return () => + { + _webView2.WebView2.CoreWebView2.WebMessageReceived -= nativeEventHandler; + }; + } + + public void AddWebResourceRequestedFilter(string uri, CoreWebView2WebResourceContextWrapper resourceContext) + { + _webView2.WebView2.CoreWebView2.AddWebResourceRequestedFilter(uri, (CoreWebView2WebResourceContext)resourceContext); + } + + public Action AddWebResourceRequestedHandler(EventHandler webResourceRequestedHandler) + { + void nativeEventHandler(object sender, CoreWebView2WebResourceRequestedEventArgs e) + { + webResourceRequestedHandler(_webView2.WebView2, new WinUICoreWebView2WebResourceRequestedEventArgsWrapper(_webView2.Environment!, e)); + } + + _webView2.WebView2.CoreWebView2.WebResourceRequested += nativeEventHandler; + + // Return removal callback + return () => + { + _webView2.WebView2.CoreWebView2.WebResourceRequested -= nativeEventHandler; + }; + } + + public void PostWebMessageAsString(string webMessageAsString) + { + _webView2.WebView2.CoreWebView2.PostWebMessageAsString(webMessageAsString); + } + } +} diff --git a/src/BlazorWebView/src/core/Windows/WinUIWebView2Wrapper.cs b/src/BlazorWebView/src/core/Windows/WinUIWebView2Wrapper.cs index 1ddfc9c7a1b1..cfdc5d706441 100644 --- a/src/BlazorWebView/src/core/Windows/WinUIWebView2Wrapper.cs +++ b/src/BlazorWebView/src/core/Windows/WinUIWebView2Wrapper.cs @@ -8,39 +8,45 @@ namespace Microsoft.AspNetCore.Components.WebView.Maui { internal class WinUIWebView2Wrapper : IWebView2Wrapper { - private readonly WebView2Control _webView2; - private bool _hasInitialized; + private readonly WinUICoreWebView2Wrapper _coreWebView2Wrapper; public WinUIWebView2Wrapper(WebView2Control webView2) { - _webView2 = webView2 ?? throw new ArgumentNullException(nameof(webView2)); + if (webView2 is null) + { + throw new ArgumentNullException(nameof(webView2)); + } + + WebView2 = webView2; + _coreWebView2Wrapper = new WinUICoreWebView2Wrapper(this); } - public CoreWebView2 CoreWebView2 => _webView2.CoreWebView2; + public ICoreWebView2Wrapper CoreWebView2 => _coreWebView2Wrapper; public Uri Source { - get => _webView2.Source; - set => _webView2.Source = value; + get => WebView2.Source; + set => WebView2.Source = value; } - public event EventHandler AcceleratorKeyPressed + public WebView2Control WebView2 { get; } + + public CoreWebView2Environment? Environment { get; set; } + + public Action AddAcceleratorKeyPressedHandler(EventHandler eventHandler) { - add => throw new NotSupportedException(); //_webView2.AcceleratorKeyPressed += value; - remove => throw new NotSupportedException(); //_webView2.AcceleratorKeyPressed -= value; + // This event is not supported in WinUI, so we ignore it + return () => { }; } - public Task EnsureCoreWebView2Async(CoreWebView2Environment? environment = null) + public async Task CreateEnvironmentAsync() { - if (_hasInitialized) - { - // We don't want people to think they can set more than one environment - throw new InvalidOperationException($"{nameof(EnsureCoreWebView2Async)} may only be called once per control."); - } + Environment = await CoreWebView2Environment.CreateAsync(); + } - _hasInitialized = true; - // TODO: We don't pass in the 'environment' parameter here because it seems WinUI doesn't support that - return _webView2.EnsureCoreWebView2Async().AsTask(); + public Task EnsureCoreWebView2Async() + { + return WebView2.EnsureCoreWebView2Async().AsTask(); } } } diff --git a/src/BlazorWebView/src/core/Windows/WinUIWebViewManager.cs b/src/BlazorWebView/src/core/Windows/WinUIWebViewManager.cs new file mode 100644 index 000000000000..1efb44ff55e8 --- /dev/null +++ b/src/BlazorWebView/src/core/Windows/WinUIWebViewManager.cs @@ -0,0 +1,32 @@ +using System; +using Microsoft.AspNetCore.Components.WebView.WebView2; +using Microsoft.Extensions.FileProviders; +using WebView2Control = Microsoft.UI.Xaml.Controls.WebView2; + +namespace Microsoft.AspNetCore.Components.WebView.Maui +{ + /// + /// An implementation of that uses the Edge WebView2 browser control + /// to render web content in WinUI applications. + /// + public class WinUIWebViewManager : WebView2WebViewManager + { + readonly WebView2Control _nativeWebView2; + + public WinUIWebViewManager(WebView2Control nativeWebView2, IWebView2Wrapper webview, IServiceProvider services, Dispatcher dispatcher, IFileProvider fileProvider, string hostPageRelativePath) : base(webview, services, dispatcher, fileProvider, hostPageRelativePath) + { + _nativeWebView2 = nativeWebView2; + } + + protected override void QueueBlazorStart() + { + // In .NET MAUI we use autostart='false' for the Blazor script reference, so we start it up manually in this event + _nativeWebView2.CoreWebView2.DOMContentLoaded += async (_, __) => + { + await _nativeWebView2.CoreWebView2!.ExecuteScriptAsync(@" + Blazor.start(); + "); + }; + } + } +}