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

Blazor Windows Open Links in Browser with Configurability #4680

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ private void StartWebViewCoreIfPossible()

var fileProvider = VirtualView.CreateFileProvider(contentRootDir);

_webviewManager = new WinUIWebViewManager(NativeView, Services!, ComponentsDispatcher, fileProvider, VirtualView.JSComponents, hostPageRelativePath, contentRootDir);
_webviewManager = new WinUIWebViewManager(NativeView,
Services!,
ComponentsDispatcher,
fileProvider,
VirtualView.JSComponents,
hostPageRelativePath,
contentRootDir,
ExternalLinkMode);

if (RootComponents != null)
{
Expand Down
40 changes: 39 additions & 1 deletion src/BlazorWebView/src/Maui/Windows/WinUIWebViewManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Web.WebView2.Core;
using Windows.ApplicationModel;
using Windows.Storage.Streams;
using Launcher = Windows.System.Launcher;
using WebView2Control = Microsoft.UI.Xaml.Controls.WebView2;

namespace Microsoft.AspNetCore.Components.WebView.Maui
Expand All @@ -21,13 +22,25 @@ public class WinUIWebViewManager : WebView2WebViewManager
private readonly WebView2Control _webview;
private readonly string _hostPageRelativePath;
private readonly string _contentRootDir;
private readonly ExternalLinkMode _externalLinkMode;

public WinUIWebViewManager(WebView2Control webview, IServiceProvider services, Dispatcher dispatcher, IFileProvider fileProvider, JSComponentConfigurationStore jsComponents, string hostPageRelativePath, string contentRootDir)
public WinUIWebViewManager(
WebView2Control webview,
IServiceProvider services,
Dispatcher dispatcher,
IFileProvider fileProvider,
JSComponentConfigurationStore jsComponents,
string hostPageRelativePath,
string contentRootDir,
ExternalLinkMode externalLinkMode)
: base(webview, services, dispatcher, fileProvider, jsComponents, hostPageRelativePath)
{
_webview = webview;
_hostPageRelativePath = hostPageRelativePath;
_contentRootDir = contentRootDir;
_externalLinkMode = externalLinkMode;

_webview.CoreWebView2Initialized += Webview_CoreWebView2Initialized;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to unsubscribed when this type is disposed?

Copy link
Contributor Author

@TanayParikh TanayParikh Feb 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was under the impression the lifecycle of the WinUIWebViewManager and the associated WebView2Control would be essentially the same, hence I didn't bother with unsubscribing (given if they get disposed at the same time then there shouldn't be any stray references preventing GC). This is in line with the current async lambda we have for Blazor.Start():

_webview.CoreWebView2.DOMContentLoaded += async (_, __) =>
{
await _webview.CoreWebView2!.ExecuteScriptAsync(@"
Blazor.start();
");
};

I can add it in if you think it'd be beneficial.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, instead of hooking the Initialized event at all, you can wait until we force initialization and then hook the events you actually want directly. Here's where we force initialization in the shared code: https://github.com/dotnet/maui/blob/main/src/BlazorWebView/src/SharedSource/WebView2WebViewManager.cs#L93

Because presumably we want exactly this logic on WinForms and WPF as well. Launching the browser will be different between WinUI and non-WinUI, but that could be abstracted via a delegate or something.

}

protected override async Task HandleWebResourceRequest(CoreWebView2WebResourceRequestedEventArgs eventArgs)
Expand Down Expand Up @@ -102,5 +115,30 @@ protected override void QueueBlazorStart()
");
};
}

private void Webview_CoreWebView2Initialized(WebView2Control sender, UI.Xaml.Controls.CoreWebView2InitializedEventArgs args)
{
_webview.CoreWebView2.NavigationStarting += CoreWebView2_NavigationStarting;
_webview.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;
}

private void CoreWebView2_NavigationStarting(CoreWebView2 sender, CoreWebView2NavigationStartingEventArgs args)
{
var uri = new Uri(args.Uri);
TanayParikh marked this conversation as resolved.
Show resolved Hide resolved
if (uri.Host != "0.0.0.0" && _externalLinkMode == ExternalLinkMode.OpenInExternalBrowser)
{
_ = Launcher.LaunchUriAsync(uri);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we log errors thrown by these tasks or do they appear somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just searched through, there doesn't seem to be an existing logging mechanism. Is there a standardized mechanism we could use here (or perhaps #4441 would cover it?).

args.Cancel = true;
}
}

private void CoreWebView2_NewWindowRequested(CoreWebView2 sender, CoreWebView2NewWindowRequestedEventArgs args)
{
// Intercept _blank target <a> tags to always open in device browser
// regardless of ExternalLinkMode.OpenInWebview
var uri = new Uri(args.Uri);
_ = Launcher.LaunchUriAsync(uri);
args.Handled = true;
}
}
}