maui icon indicating copy to clipboard operation
maui copied to clipboard

Webview on iOS and Android does not allow local HTML to load additional resources

Open Wolfos opened this issue 1 year ago • 5 comments

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

Wolfos avatar Feb 15 '24 15:02 Wolfos

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.

ghost avatar Feb 15 '24 19:02 ghost

Reproduction project: https://github.com/Wolfos/maui-repro

Build for Windows - will show webpage as expected Build for Android or iOS - white screen

Wolfos avatar Feb 16 '24 08:02 Wolfos

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).

スクリーンショット 2024-02-18 015558

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.

drasticactions avatar Feb 18 '24 06:02 drasticactions

CC @Eilon @mattleibow, what do you think? I'm not sure if I fully understand it.

drasticactions avatar Feb 18 '24 06:02 drasticactions

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.

Wolfos avatar Feb 20 '24 09:02 Wolfos

@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.

Eilon avatar Mar 07 '24 19:03 Eilon

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

Zhanglirong-Winnie avatar Mar 27 '24 01:03 Zhanglirong-Winnie