diff --git a/src/Controls/samples/Controls.Sample/Pages/Core/MultiWindowPage.xaml b/src/Controls/samples/Controls.Sample/Pages/Core/MultiWindowPage.xaml
index 26a69ac68560..4ba2119b2fcd 100644
--- a/src/Controls/samples/Controls.Sample/Pages/Core/MultiWindowPage.xaml
+++ b/src/Controls/samples/Controls.Sample/Pages/Core/MultiWindowPage.xaml
@@ -10,6 +10,11 @@
Padding="12"
Spacing="6">
+
+
diff --git a/src/Core/src/Handlers/WebView/WebViewHandler.Windows.cs b/src/Core/src/Handlers/WebView/WebViewHandler.Windows.cs
index 878bb1a90283..d985a7e3dea9 100644
--- a/src/Core/src/Handlers/WebView/WebViewHandler.Windows.cs
+++ b/src/Core/src/Handlers/WebView/WebViewHandler.Windows.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
+using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Windows.Web.Http;
@@ -12,6 +13,7 @@ public partial class WebViewHandler : ViewHandler
{
WebNavigationEvent _eventState;
readonly HashSet _loadedCookies = new HashSet();
+ Window? _window;
protected override WebView2 CreatePlatformView() => new MauiWebView();
@@ -24,21 +26,54 @@ internal WebNavigationEvent CurrentNavigationEvent
protected override void ConnectHandler(WebView2 platformView)
{
platformView.CoreWebView2Initialized += OnCoreWebView2Initialized;
-
base.ConnectHandler(platformView);
+
+ if (platformView.IsLoaded)
+ OnLoaded();
+ else
+ platformView.Loaded += OnWebViewLoaded;
}
- protected override void DisconnectHandler(WebView2 platformView)
+ void OnWebViewLoaded(object sender, UI.Xaml.RoutedEventArgs e)
+ {
+ OnLoaded();
+ }
+
+ void OnLoaded()
+ {
+ _window = MauiContext!.GetPlatformWindow();
+ _window.Closed += OnWindowClosed;
+ }
+
+ private void OnWindowClosed(object sender, UI.Xaml.WindowEventArgs args)
{
- if (platformView.CoreWebView2 != null)
+ Disconnect(PlatformView);
+ }
+
+ void Disconnect(WebView2 platformView)
+ {
+ if (_window is not null)
+ {
+ _window.Closed -= OnWindowClosed;
+ _window = null;
+ }
+
+ if (platformView.CoreWebView2 is not null)
{
platformView.CoreWebView2.HistoryChanged -= OnHistoryChanged;
platformView.CoreWebView2.NavigationStarting -= OnNavigationStarting;
platformView.CoreWebView2.NavigationCompleted -= OnNavigationCompleted;
+ platformView.CoreWebView2.Stop();
}
+ platformView.Loaded -= OnWebViewLoaded;
platformView.CoreWebView2Initialized -= OnCoreWebView2Initialized;
+ platformView.Close();
+ }
+ protected override void DisconnectHandler(WebView2 platformView)
+ {
+ DisconnectHandler(platformView);
base.DisconnectHandler(platformView);
}
@@ -66,7 +101,7 @@ void OnNavigationCompleted(CoreWebView2 sender, CoreWebView2NavigationCompletedE
void OnNavigationStarting(CoreWebView2 sender, CoreWebView2NavigationStartingEventArgs args)
{
- if (Uri.TryCreate(args.Uri, UriKind.Absolute, out Uri? uri) && uri != null)
+ if (Uri.TryCreate(args.Uri, UriKind.Absolute, out Uri? uri) && uri is not null)
{
bool cancel = VirtualView.Navigating(CurrentNavigationEvent, uri.AbsoluteUri);
@@ -126,7 +161,7 @@ void NavigationSucceeded(CoreWebView2 sender, CoreWebView2NavigationCompletedEve
{
var uri = sender.Source;
- if (uri != null)
+ if (uri is not null)
SendNavigated(uri, CurrentNavigationEvent, WebNavigationResult.Success);
if (VirtualView is null)
@@ -145,12 +180,11 @@ void NavigationFailed(CoreWebView2 sender, CoreWebView2NavigationCompletedEventA
void SendNavigated(string url, WebNavigationEvent evnt, WebNavigationResult result)
{
- if (VirtualView != null)
+ if (VirtualView is not null)
{
SyncPlatformCookiesToVirtualView(url);
VirtualView.Navigated(evnt, url, result);
-
PlatformView?.UpdateCanGoBackForward(VirtualView);
}
@@ -161,12 +195,12 @@ void SyncPlatformCookiesToVirtualView(string url)
{
var myCookieJar = VirtualView.Cookies;
- if (myCookieJar == null)
+ if (myCookieJar is null)
return;
var uri = CreateUriForCookies(url);
- if (uri == null)
+ if (uri is null)
return;
var cookies = myCookieJar.GetCookies(uri);
@@ -180,7 +214,7 @@ void SyncPlatformCookiesToVirtualView(string url)
var httpCookie = platformCookies
.FirstOrDefault(x => x.Name == cookie.Name);
- if (httpCookie == null)
+ if (httpCookie is null)
cookie.Expired = true;
else
cookie.Value = httpCookie.Value;
@@ -193,18 +227,18 @@ void SyncPlatformCookies(string url)
{
var uri = CreateUriForCookies(url);
- if (uri == null)
+ if (uri is null)
return;
var myCookieJar = VirtualView.Cookies;
- if (myCookieJar == null)
+ if (myCookieJar is null)
return;
InitialCookiePreloadIfNecessary(url);
var cookies = myCookieJar.GetCookies(uri);
- if (cookies == null)
+ if (cookies is null)
return;
var retrieveCurrentWebCookies = GetCookiesFromPlatformStore(url);
@@ -222,7 +256,7 @@ void SyncPlatformCookies(string url)
foreach (HttpCookie cookie in retrieveCurrentWebCookies)
{
- if (cookies[cookie.Name] != null)
+ if (cookies[cookie.Name] is not null)
continue;
filter.CookieManager.DeleteCookie(cookie);
@@ -233,7 +267,7 @@ void InitialCookiePreloadIfNecessary(string url)
{
var myCookieJar = VirtualView.Cookies;
- if (myCookieJar == null)
+ if (myCookieJar is null)
return;
var uri = new Uri(url);
@@ -243,12 +277,12 @@ void InitialCookiePreloadIfNecessary(string url)
var cookies = myCookieJar.GetCookies(uri);
- if (cookies != null)
+ if (cookies is not null)
{
var existingCookies = GetCookiesFromPlatformStore(url);
foreach (HttpCookie cookie in existingCookies)
{
- if (cookies[cookie.Name] == null)
+ if (cookies[cookie.Name] is null)
myCookieJar.SetCookies(uri, cookie.ToString());
}
}
@@ -265,7 +299,7 @@ HttpCookieCollection GetCookiesFromPlatformStore(string url)
Uri? CreateUriForCookies(string url)
{
- if (url == null)
+ if (url is null)
return null;
Uri? uri;
@@ -290,7 +324,7 @@ public static void MapEvaluateJavaScriptAsync(IWebViewHandler handler, IWebView
{
if (arg is EvaluateJavaScriptAsyncRequest request)
{
- if (handler.PlatformView == null)
+ if (handler.PlatformView is null)
{
request.SetCanceled();
return;
diff --git a/src/Core/tests/DeviceTests.Shared/Stubs/ContextStub.cs b/src/Core/tests/DeviceTests.Shared/Stubs/ContextStub.cs
index b81f8c2d8bdb..42b5e0c460a6 100644
--- a/src/Core/tests/DeviceTests.Shared/Stubs/ContextStub.cs
+++ b/src/Core/tests/DeviceTests.Shared/Stubs/ContextStub.cs
@@ -46,7 +46,7 @@ public object GetService(Type serviceType)
return _windowManager ??= new NavigationRootManager(MauiProgramDefaults.DefaultWindow);
if (serviceType == typeof(UI.Xaml.Window))
- return MauiProgramDefaults.DefaultWindow;
+ return _services.GetService(serviceType) ?? MauiProgramDefaults.DefaultWindow;
#endif
if (serviceType == typeof(IDispatcher))
return _services.GetService(serviceType) ?? TestDispatcher.Current;
diff --git a/src/Core/tests/DeviceTests/Handlers/WebView/WebViewHandlerTests.Windows.cs b/src/Core/tests/DeviceTests/Handlers/WebView/WebViewHandlerTests.Windows.cs
index efedc99bf5ce..909237121303 100644
--- a/src/Core/tests/DeviceTests/Handlers/WebView/WebViewHandlerTests.Windows.cs
+++ b/src/Core/tests/DeviceTests/Handlers/WebView/WebViewHandlerTests.Windows.cs
@@ -1,9 +1,46 @@
-using Microsoft.UI.Xaml.Controls;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.DeviceTests.Stubs;
+using Microsoft.UI.Xaml.Controls;
+using Xunit;
namespace Microsoft.Maui.DeviceTests
{
public partial class WebViewHandlerTests
{
+ [Fact(DisplayName = "Closing Window With WebView Doesnt Crash")]
+ public async Task ClosingWindowWithWebViewDoesntCrash()
+ {
+ EnsureHandlerCreated(builder =>
+ {
+ builder.Services.AddSingleton(typeof(UI.Xaml.Window), (services) => new UI.Xaml.Window());
+ });
+
+ var webView = new WebViewStub()
+ {
+ Source = new UrlWebViewSourceStub { Url = "https://dotnet.microsoft.com/" }
+ };
+
+ var handler = await CreateHandlerAsync(webView);
+
+ await InvokeOnMainThreadAsync(async () =>
+ {
+ TaskCompletionSource navigationComplete = new TaskCompletionSource();
+ handler.PlatformView.NavigationCompleted += (_, _) =>
+ {
+ navigationComplete?.SetResult();
+ navigationComplete = null;
+ };
+
+ await handler.PlatformView.AttachAndRun(async () =>
+ {
+ await handler.PlatformView.OnLoadedAsync();
+ await navigationComplete.Task;
+ }, MauiContext);
+ });
+ }
+
WebView2 GetNativeWebView(WebViewHandler webViewHandler) =>
webViewHandler.PlatformView;
diff --git a/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs b/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs
index 5c32f8944364..4bf62d305647 100644
--- a/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs
+++ b/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs
@@ -93,25 +93,25 @@ public static WColor ColorAtPoint(this CanvasBitmap bitmap, int x, int y, bool i
: WColor.FromArgb(255, pixel.R, pixel.G, pixel.B);
}
- public static Task AttachAndRun(this FrameworkElement view, Action action) =>
- view.AttachAndRun(window => action());
+ public static Task AttachAndRun(this FrameworkElement view, Action action, IMauiContext? mauiContext = null) =>
+ view.AttachAndRun(window => action(), mauiContext);
- public static Task AttachAndRun(this FrameworkElement view, Action action) =>
+ public static Task AttachAndRun(this FrameworkElement view, Action action, IMauiContext? mauiContext = null) =>
view.AttachAndRun((window) =>
{
action(window);
return Task.FromResult(true);
- });
+ }, mauiContext);
- public static Task AttachAndRun(this FrameworkElement view, Func action) =>
- view.AttachAndRun(window => action());
+ public static Task AttachAndRun(this FrameworkElement view, Func action, IMauiContext? mauiContext = null) =>
+ view.AttachAndRun(window => action(), mauiContext);
- public static Task AttachAndRun(this FrameworkElement view, Func action) =>
+ public static Task AttachAndRun(this FrameworkElement view, Func action, IMauiContext? mauiContext = null) =>
view.AttachAndRun((window) =>
{
var result = action(window);
return Task.FromResult(result);
- });
+ }, mauiContext);
public static Task AttachAndRun(this FrameworkElement view, Func action) =>
view.AttachAndRun(window => action());
@@ -132,7 +132,7 @@ public static Task AttachAndRun(this FrameworkElement view, Func a
public static Task AttachAndRun(this FrameworkElement view, Func> action) =>
view.AttachAndRun(window => action());
- public static async Task AttachAndRun(this FrameworkElement view, Func> action)
+ public static async Task AttachAndRun(this FrameworkElement view, Func> action, IMauiContext? mauiContext = null)
{
if (view.Parent is Border wrapper)
view = wrapper;
@@ -156,26 +156,27 @@ public static async Task AttachAndRun(this FrameworkElement view, Func(mauiContext.Services) : new Window();
+
+ window.Content = new Grid
{
- Content = new Grid
+ HorizontalAlignment = HorizontalAlignment.Center,
+ VerticalAlignment = VerticalAlignment.Center,
+ Children =
{
- HorizontalAlignment = HorizontalAlignment.Center,
- VerticalAlignment = VerticalAlignment.Center,
- Children =
+ (grid = new Grid
{
- (grid = new Grid
+ Width = view.Width,
+ Height = view.Height,
+ Children =
{
- Width = view.Width,
- Height = view.Height,
- Children =
- {
- view
- }
- })
- }
+ view
+ }
+ })
}
};
+
window.Activate();
// wait for element to be loaded