-
Notifications
You must be signed in to change notification settings - Fork 2k
[iOS] Fixed Webview LoadFile ignore directories. #31040
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
Changes from all commits
e904e90
45e2a4d
1221e4d
c389325
5ae9e08
039eaaa
b962a7d
783c895
730d6bf
b7e30d6
b0c18cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| namespace Maui.Controls.Sample.Issues; | ||
|
|
||
| [Issue(IssueTracker.Github, 23315, "LoadFile in src/Core/src/Platform/iOS/MauiWKWebView.cs ignore directories", PlatformAffected.iOS | PlatformAffected.macOS)] | ||
| public class Issue23315 : TestContentPage | ||
| { | ||
| public Issue23315() | ||
| { | ||
| } | ||
|
|
||
| protected override void Init() | ||
| { | ||
| var statusLabel = new Label | ||
| { | ||
| AutomationId = "StatusLabel", | ||
| HorizontalOptions = LayoutOptions.Center, | ||
| VerticalOptions = LayoutOptions.Center | ||
| }; | ||
|
|
||
| WebView webView = new WebView | ||
| { | ||
| Source = "foo/bar/baz/test.html" | ||
| }; | ||
|
|
||
| webView.Navigated += async (sender, e) => | ||
| { | ||
| if (e.Result != WebNavigationResult.Success) | ||
| { | ||
| statusLabel.Text = $"Failed"; | ||
| } | ||
| else if (e.Result == WebNavigationResult.Success) | ||
| { | ||
| statusLabel.Text = $"Success"; | ||
| } | ||
| }; | ||
|
|
||
| var grid = new Grid | ||
| { | ||
| RowDefinitions = | ||
| { | ||
| new RowDefinition(), | ||
| new RowDefinition { Height = 100 } | ||
| } | ||
| }; | ||
|
|
||
| grid.Add(webView, 0, 0); | ||
| grid.Add(statusLabel, 0, 1); | ||
|
|
||
| Content = grid; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <title>Nested Subdirectory Test File</title> | ||
| </head> | ||
| <body> | ||
| <h1>Nested Subdirectory Test File</h1> | ||
| <p>This is test.html from the foo/bar/baz nested subdirectories.</p> | ||
| </body> | ||
| </html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| using NUnit.Framework; | ||
| using UITest.Appium; | ||
| using UITest.Core; | ||
|
|
||
| namespace Microsoft.Maui.TestCases.Tests.Issues | ||
| { | ||
| public class Issue23315(TestDevice device) : _IssuesUITest(device) | ||
| { | ||
| public override string Issue => "LoadFile in src/Core/src/Platform/iOS/MauiWKWebView.cs ignore directories"; | ||
|
|
||
| [Test] | ||
| [Category(UITestCategories.WebView)] | ||
| public void WebViewCanLoadFileFromSubdirectory() | ||
| { | ||
| // The HostApp loads `foo/bar/baz/test.html` whose <title> is "Nested Subdirectory Test File". | ||
| // When the bug is present on iOS/MacCatalyst, LoadFile strips the directory part | ||
| // and tries to load only `test.html`, so the navigation fails and the label | ||
| // never reports the expected title. | ||
| var statusLabel = App.WaitForElement("StatusLabel", timeout: TimeSpan.FromSeconds(10)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [moderate] Regression Prevention - Waiting only for the StatusLabel element races the WebView navigation: the label exists as soon as the page is displayed, before Navigated updates it to Success. On slower iOS/MacCatalyst runs this can read the initial empty text and fail despite a correct fix. Wait for the label text to become Success before asserting. |
||
| var text = statusLabel.GetText(); | ||
| Assert.That(text, Is.EqualTo("Success"), $"Expected to load the file from the subdirectory, but got '{text}' instead."); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| using System; | ||
| using System.IO; | ||
| using System.Threading.Tasks; | ||
| using Foundation; | ||
| using Microsoft.Extensions.Logging; | ||
| using WebKit; | ||
|
|
||
| namespace Microsoft.Maui.Platform | ||
|
|
@@ -141,5 +143,42 @@ internal static string HandleWKWebViewResult(NSObject? result) | |
|
|
||
| return result.ToString() ?? "null"; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Loads a local file URL into the WebView using NSBundle resource loading. | ||
| /// </summary> | ||
| /// <param name="webView">The WKWebView instance to load the file into</param> | ||
| /// <param name="url">The local file URL to load</param> | ||
| /// <param name="logger">Optional logger for error reporting</param> | ||
| /// <returns>True if the file was successfully loaded, false otherwise</returns> | ||
| internal static bool LoadFile(this WKWebView webView, string url, ILogger? logger = null) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [major] Backward Compatibility - This shared helper only fixes the handler path; the registered iOS compatibility WebView renderer still has the duplicated old LoadFile implementation that strips directories before calling NSBundle.MainBundle.GetUrlForResource(file, ext). Concrete scenario: an app using UseMauiCompatibility() or a compatibility WebView renderer with Source = foo/bar/baz/test.html on iOS/MacCatalyst still fails issue #23315. Please either reuse/apply the directory-preserving logic in src/Compatibility/Core/src/iOS/Renderers/WkWebViewRenderer.cs or explicitly scope this PR/test to handler-only behavior. |
||
| { | ||
| try | ||
| { | ||
| var file = Path.GetFileNameWithoutExtension(url); | ||
| var ext = Path.GetExtension(url); | ||
| var directory = Path.GetDirectoryName(url); | ||
|
|
||
| // If there's a subdirectory, use the overload that accepts a subdirectory parameter else fallback to the original method if subdirectory method fails or if no subdirectory | ||
| NSUrl? nsUrl = string.IsNullOrEmpty(directory) | ||
| ? NSBundle.MainBundle.GetUrlForResource(file, ext) | ||
| : NSBundle.MainBundle.GetUrlForResource(file, ext, directory); | ||
|
|
||
| if (nsUrl is null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| webView.LoadFileUrl(nsUrl, nsUrl); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [major] iOS/MacCatalyst WebView - LoadFileUrl(nsUrl, nsUrl) grants WKWebView read access only to the HTML file URL. Nested pages that load relative sibling resources (CSS/JS/images) from the same bundle directory can still render broken even though the top-level HTML loads. Use the containing directory or intended bundle root as allowingReadAccessToURL, and add coverage with a relative subresource under foo/bar/baz/. |
||
|
|
||
| return true; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [major] Compatibility renderer coverage — this helper fixes only the handler path. |
||
| logger?.LogWarning($"Could not load {url} as local file: {ex}"); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[moderate] Regression test coverage — the regression page contains only inline HTML, so it does not catch the current
LoadFileUrl(nsUrl, nsUrl)read-access scope problem. Add a sibling resource (CSS/image/script) referenced relatively from this nested page and assert it renders, so the test covers local-file loading beyond the top-level HTML.