maui
maui copied to clipboard
Webview on iOS and Android does not allow local HTML to load additional resources
Description
In the documentation it describes
The local HTML file can load Cascading Style Sheets (CSS), JavaScript, and images, if they've also been added to your app project with the MauiAsset build action.
I tried the following:
- Create a new web application using Angular, and build this template
- Insert the build files in the Resources/Raw folder and make sure they use the MauiAsset build action. This consists of an HTML page, two Javascript documents, one CSS document and an image
- Load the local page in via a webview
In a Windows build, it displays the Angular application as expected. On Android and iOS however, the result is a blank page. Even when some additional HTML is added such as
<p>Hello World</p>
the text Hello World does not display, suggesting to me that it's not just a CORS issue - something seems to break.
Steps to Reproduce
See description
Link to public reproduction project repository
No response
Version with bug
8.0.3 GA
Is this a regression from previous behavior?
Not sure, did not test other versions
Last version that worked well
Unknown/Other
Affected platforms
iOS, Android
Affected platform versions
Android 13, iOS 17
Did you find any workaround?
No response
Relevant log output
No response
Hi @Wolfos. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md
This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
Reproduction project: https://github.com/Wolfos/maui-repro
Build for Windows - will show webpage as expected Build for Android or iOS - white screen
For iOS, you're probably hitting this:
https://github.com/dotnet/maui/issues/19486#issuecomment-1863719542
You need to enable local access to file URLs by setting it for WKWebView. MAUI's WebView wrapper uses the default (`false') so you would need to set it yourself.
As for Android, I looked into this and I think the answers are... complex. I was able to get it to work, but this is using .NET Android directly without using the MAUI UI Framework and its WebView wrapper. Just to note though that I'm not an Android Expert and I'm not on the MAUI Team (I just work within Microsoft and .NET).
https://github.com/drasticactions/MauiRepros/tree/main/AndroidWebViewAssetTest
Looking through your logs, the first thing you see is that CORS is being hit. You can see it in the device output and you can enable the Web Debugging flag in your Android Activity
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
Android.Webkit.WebView.SetWebContentsDebuggingEnabled(true);
}
...
And use chrome://inspect
or edge://inspect
to access the WebView console. It then spouts errors saying that it can't access those javascript and CSS files because of CORS. By default, the Android WebView doesn't allow for local file access and the MAUI wrapper follows this and doesn't change it. Using the WebView handler mapping, you can update the underlying Android WebView to let it work
webView.Settings.JavaScriptEnabled = true;
webView.Settings.AllowFileAccess = true;
webView.Settings.AllowFileAccessFromFileURLs = true;
webView.Settings.AllowUniversalAccessFromFileURLs = true;
That would get past the CORS error (By brute force) but then you would hit another issue, the WebView still can't find the files, so it will still 404. I believe that's also due to how the MAUI WebView wrapping works.
https://developer.android.com/develop/ui/views/layout/webapps/load-local-content
If you check the Android docs for local content, it mentions using WebViewAssetLoader
and setting up paths for where assets and resource paths are located. The idea being that when you get a request for a specific address (local or remote) it can redirect the paths to files on disk. Also, by implementing these APIs, you wouldn't need to enable the "AllowFileAccess" APIs I posted above, since that would be handled by Android itself.
The MAUI WebView version doesn't implement this:
https://github.com/dotnet/maui/blob/7773f909f93dabef282e0f6228081e3f04eafe2a/src/Core/src/Platform/Android/MauiWebView.cs#L7
https://github.com/dotnet/maui/blob/7773f909f93dabef282e0f6228081e3f04eafe2a/src/Core/src/Platform/Android/MauiWebViewClient.cs
It's a very small wrapping, loading either HTML Text or URLs. It doesn't handle local assets on its own. So you can load HTML files fine, but not the content it references unless you make very specific references to the file system for Android.
It may have been fine during the time of Xamarin.Forms or when this code was originally written. I'm not sure it does today.
Now you may be asking yourself, as I was, "Then how could BlazorWebView possibly work??? That can access local javascript and CSS just fine". It turns out, it does implement those APIs
https://github.com/dotnet/maui/blob/7773f909f93dabef282e0f6228081e3f04eafe2a/src/BlazorWebView/src/Maui/Android/WebKitWebViewClient.cs#L78-L87
So when you use BlazorWebView, its internal address is https://0.0.0.0
and properly maps the assets to the correct locations. The MAUI WebView implementation uses file://android_assets
with no mapping to local assets.
So is this a bug? Depends. The docs say that it should work, but as far as I can tell it was never written to work like that. The default iOS and Android WebViews (As in, the platform ones, not MAUIs) disable local file access unless you turn it on. BlazorWebView does this (as that's how it has to work since it is local) and implements the APIs to do it, but MAUIs WebView wrapping does not. You can enable it, but you need to modify the Handlers or make new ones to do it, and that's not obvious especially looking at the docs.
IMO, to fix this correctly, there would need to be a new API in the wrapper to allow for local content and handle the mapping per platform. Just enabling WebViewAssetLoader or implementing the injection APIs doesn't "fix" it since they do bring security implications allowing local files, and if you don't want nor need local files that should not be forced on you (Blazor Hybrid is a local app by default, so it enabling it makes sense and is the right thing to do). Likewise, telling people to enable AllowUniversalAccessFromFileURLs
is brute force and the Android docs discourage it since it's a blanket switch.
I'm not sure if this helps or not (again, I'm not an Android expert) but hopefully this points those who can fix it internally on the right path, or at least they can tell me where I messed up in my research.
CC @Eilon @mattleibow, what do you think? I'm not sure if I fully understand it.
Even after setting this property I'm getting the same result (white screen) on iOS. There's nothing in the logs. The network tab in Safari devtools only says "the resource failed to load" for the CSS and doesn't even appear to attempt to load the JS.
@drasticactions I'm not super familiar with how the regular WebView works, but your comments are correct for BlazorWebView. In BlazorWebView there are never 'local' requests because all requests either go to BlazorWebView itself (even to seemingly local static files), or over HTTP to a remote server.
I thought that this type of scenario was supposed to work even from Xamarin days, where you can place a bunch of static files as resources, and they can reference each other - but I'm not certain.
Verified this issue with Visual Studio 17.10.0 Preview 2. Can repro on Android and iOS platforms with sample project. https://github.com/Wolfos/maui-repro