在 Android 的 WebView 中放松 MIME 类型检查?或者强制模块类型为常规 javascript?

Relaxing MIME type checking in Android's WebView? Or forcing module type for regular javascript?

我正在 Android 中开发和应用程序,其中有一段使用 javascript 和 python 使用 Transcrypt (it'd almost be the same as saying that I am developing that part straight in javascript, but I do not have full control on how things are made). Android displays that section in an Activity with a WebView 编写的。启动它的相关 java 代码只是

        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setAllowFileAccessFromFileURLs(true);
        webSettings.setAllowUniversalAccessFromFileURLs(true);
        mWebView.loadUrl("file:///android_asset/AndroidWebView.html");

AndroidWebView.html的相关部分类似于Transcript's demo for iOS

        <script type="module">
            import * as my_app from "./target/my_app.js"; window.my_app = my_app;
        </script>

该应用程序在模拟器上运行良好,但在真实设备上 WebView 拒绝加载 java 脚本并吐出以下错误:

Failed to load module script: The server responded with a non-JavaScript MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec.

如果我通过将 HTML 更改为

来强制使用 MIME 类型
        <script type="text/javascript">
            import * as my_app from "./target/my_app.js"; window.my_app = my_app;
        </script>

这似乎适用于我的页面的其他部分,这些部分是纯 java 脚本(不是模块),它显然因 Uncaught SyntaxError: Cannot use import statement outside a module.

而失败

有没有办法强制后者 HTML 成为一个模块,或者前者 HTML 不那么强烈地检查 MIME 类型?

正在查看 options for WebView I could not see anything that seems relevant. Looking around, this issue seems to be caused exactly by this bug

另一方面,在我看来,问题在于接受类型的 HTML script 标记的语法是 "text/javascript""module"但不能同时使用(我试过,它似乎只使用第一个),而在语义上有理由同时使用两者。是否有解决方法,甚至是同时拥有两者的黑客?我知道一种 hack 可以用于加载 my_app.js,但是那个将无法进一步加载其他模块(my_app.js 可以)。因此,除非有转译器或能够自动将我的所有模块合并为一个的东西,否则这将行不通。 FWIW,这些模块是从 Transcrypt python 生成的,因此它必须是一个自动化的过程,而不是手动过程。

PS:我不是 HTML 或 java 脚本开发人员,这些是我在这个领域的第一步,所以请不要怀疑我的猜测

PPS:我理解不在计算机上执行任何这些操作而是启动服务器的原因。这就是我为我的开发环境所做的。但是对于 Android 应用程序中的一个 Activity,HTTP 服务器似乎有点矫枉过正,令人头疼(跟踪端口、潜在的更多故障模式、可能在浏览器或其他应用程序中可见、额外的权限、额外的资源和电池使用,可能还有更多我没有想到的麻烦)。

您可以尝试一些事情。

1。 通过将 <!DOCTYPE html> 添加到 index.html

来确保它是 html 5

2。 您可以使用 WebViewAssetLoader 而不是使用 file://... 直接从系统加载文件,这将 return mimetype https://developer.android.com/reference/kotlin/androidx/webkit/WebViewAssetLoader

3。 如果这在 webViewClient 中不起作用,您可以拦截请求并手动添加 text/javascript mimetype:

val webViewAssetLoader = WebViewAssetLoader.Builder()
        .addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(context!!))
        .build()

val webViewClient = object : WebViewClient() {

    override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest): WebResourceResponse? {
        val interceptedWebRequest = webViewAssetLoader.shouldInterceptRequest(request.url)
        interceptedWebRequest?.let {
            if (request.url.toString().endsWith("js", true)) {
                it.mimeType = "text/javascript"
            }
        }
        return interceptedWebRequest
    }
}

我只是以一种更易于阅读的方式重申之前的答案(并且使用 Java 而不是 Kotlin)

  1. 不确定是否重要,但最好通过添加 <!DOCTYPE html>

  2. 使用 html 5
  3. import androidx.webkit.WebViewAssetLoader;

  4. 所述
  5. 强制使用类似

  6. 的正确 MIME 类型
        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
                WebResourceResponse intercepted = assetLoader.shouldInterceptRequest(request.getUrl());
                if (request.getUrl().toString().endsWith("js")) {
                        if (intercepted != null) {
                            intercepted.setMimeType("text/javascript");
                        }
                }
                return intercepted;
            }
        });
  1. 之类的东西加载HTML
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        mWebView.loadUrl("https://appassets.androidplatform.net/assets/AndroidWebView.html");

注意 HTTPS(非文件)协议,并且缺少 AllowFileAccessFromFileURLs