diff --git a/src/Core/src/Platform/Android/MauiWebView.cs b/src/Core/src/Platform/Android/MauiWebView.cs index 807e7d705e83..03a3d80fe5d1 100644 --- a/src/Core/src/Platform/Android/MauiWebView.cs +++ b/src/Core/src/Platform/Android/MauiWebView.cs @@ -32,7 +32,7 @@ void IWebViewDelegate.LoadUrl(string? url) _handler.CurrentNavigationEvent = WebNavigationEvent.NewPage; } - if (url != null && !url.StartsWith('/') && !Uri.IsWellFormedUriString(url, UriKind.Absolute)) + if (url is not null && !url.StartsWith('/') && !Uri.TryCreate(url, UriKind.Absolute, out _)) { // URLs like "index.html" can't possibly load, so try "file:///android_asset/index.html" url = AssetBaseUrl + url; diff --git a/src/Core/tests/DeviceTests/Handlers/WebView/WebViewHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/WebView/WebViewHandlerTests.cs index 9dd89c8dd016..fabe42c79718 100644 --- a/src/Core/tests/DeviceTests/Handlers/WebView/WebViewHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/WebView/WebViewHandlerTests.cs @@ -113,5 +113,40 @@ await img.AssertContainsColor(expectedColorInPlayingVideo, imageRect => }); }); } + + [Theory(DisplayName = "WebView loads non-Western character encoded URLs correctly" +#if WINDOWS + , Skip = "Skipping this test on Windows due to WebView's OnNavigated event returning WebNavigationResult.Failure for URLs with non-Western characters. More information: https://github.com/dotnet/maui/issues/27425" +#endif + )] + [InlineData("https://example.com/test-Ağ-Sistem%20Bilgi%20Güvenliği%20Md/Guide.pdf")] // Non-ASCII character + space (%20) (Outside IRI range) + [InlineData("https://google.com/[]")] // Reserved set (`;/?:@&=+$,#[]!'()*%`) + [InlineData("https://example.com/test/%3Cvalue%3E")] // Escaped character from " <>`^{|} set (e.g., < >) + [InlineData("https://example.com/path/%09text")] // Escaped character from [0, 1F] range (e.g., tab %09) + [InlineData("https://example.com/test?query=%26value")] // Another escaped character from reserved set (e.g., & as %26) + public async Task WebViewShouldLoadEncodedUrl(string encodedUrl) + { + if (await AssertionExtensions.SkipTestIfNoInternetConnection()) + { + return; + } + var webView = new WebView(); + var tcs = new TaskCompletionSource(); + + webView.Navigated += (sender, args) => + { + tcs.TrySetResult(args.Result); + }; + + await InvokeOnMainThreadAsync(() => + { + var handler = CreateHandler(webView); + (handler.PlatformView as IWebViewDelegate)?.LoadUrl(encodedUrl); + }); + + var navigationResult = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.Equal(WebNavigationResult.Success, navigationResult); + } } } \ No newline at end of file diff --git a/src/TestUtils/src/DeviceTests/AssertionExtensions.cs b/src/TestUtils/src/DeviceTests/AssertionExtensions.cs index 02ed2a55c906..feeb7e140452 100644 --- a/src/TestUtils/src/DeviceTests/AssertionExtensions.cs +++ b/src/TestUtils/src/DeviceTests/AssertionExtensions.cs @@ -3,6 +3,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Net.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.Dispatching; using Microsoft.Maui.Platform; @@ -266,5 +267,39 @@ public static async Task AssertEventually(this Func assertion, int timeout throw new XunitException(message); } } + // Add these methods to AssertionExtensions class + /// + /// Checks if internet connection is available by making an HTTP request + /// + /// True if internet connection is available + public static async Task HasInternetConnection() + { + try + { + using var httpClient = new HttpClient(); + httpClient.Timeout = TimeSpan.FromSeconds(5); + using var response = await httpClient.GetAsync("https://1.1.1.1"); + return response.IsSuccessStatusCode; + } + catch + { + return false; + } + } + + /// + /// Skips the current test if no internet connection is available + /// + /// Custom message to display when skipping the test + /// True if test should be skipped (no internet), false if test can continue + public static async Task SkipTestIfNoInternetConnection(string message = "Test requires internet connection") + { + if (!await HasInternetConnection()) + { + Assert.True(true, $"TEST SKIPPED: {message}"); + return true; // Test should be skipped + } + return false; // Test can proceed + } } }