KevinnZou / compose-webview-multiplatform

WebView for JetBrains Compose Multiplatform
https://kevinnzou.github.io/compose-webview-multiplatform/
Apache License 2.0
496 stars 63 forks source link

JavaScript injection timing #238

Open minarja1 opened 2 weeks ago

minarja1 commented 2 weeks ago

Hi @KevinnZou,

I am trying to use the js bridge to establish communication between native and webview. The implementation works on iOS but not on Android. The problem seems to be in the sequence of operations.

The website loads and expects the js bridge to provide it with authorisation tokens. However, the js bridge is only injected after the url is finished loading. What is the reason behind the decision to inject the bridge after the page is fully loaded as seen in the snippet of WebView.kt:

        if (webViewJsBridge != null && !getPlatform().isDesktop()) {
            LaunchedEffect(wv, state) {
                val loadingStateFlow =
                    snapshotFlow { state.loadingState }.filter { it is LoadingState.Finished }
                val lastLoadedUrFlow =
                    snapshotFlow { state.lastLoadedUrl }.filter { !it.isNullOrEmpty() }

                // Only inject the js bridge when url is changed and the loading state is finished.
                merge(loadingStateFlow, lastLoadedUrFlow).collect {
                    // double check the loading state to make sure the WebView is loaded.
                    if (state.loadingState is LoadingState.Finished) {
                        wv.injectJsBridge()
                    }
                }
            }
        }

My implementation follows the docs with nothing special about it.

This is my logcat output:

2024-10-09 19:51:47.575 30643-30643 ComposeWebView          com.example.app                  D  onPageStarted: https://...
2024-10-09 19:51:47.584 30643-30643 ComposeWebView          com.example.app                  D  doUpdateVisitedHistory: https://...
2024-10-09 19:51:47.584 30643-30643 ComposeWebView          com.example.app                  D  onReceivedTitle: ...
2024-10-09 19:51:47.584 30643-30643 ComposeWebView          com.example.app                  D  onPageFinished: https://...
2024-10-09 19:51:47.584 30643-30643 ComposeWebView          com.example.app                  D  evaluateJavaScript: javascript:var meta = document.createElement('meta');meta.setAttribute('name', 'viewport');meta.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=10.0, minimum-scale=0.1,user-scalable=yes');document.getElementsByTagName('head')[0].appendChild(meta);
2024-10-09 19:51:47.585 30643-30643 ComposeWebView          com.example.app                  D  IWebView injectJsBridge
2024-10-09 19:51:47.585 30643-30643 ComposeWebView          com.example.app                  D  evaluateJavaScript: javascript:window.kmpJsBridge = { ...
2024-10-09 19:51:47.585 30643-30643 ComposeWebView          com.example.app                  D  evaluateJavaScript: javascript:window.kmpJsBridge.postMessage = function (message) {
                                                                                                            window.androidJsBridge.call(message)
                                                                                                        };
2024-10-09 19:51:47.594 30643-30643 chromium                com.example.app                  I  [INFO:CONSOLE(79)] "No KMP JS bridge available ..."

You can see there are only a couple of milliseconds between the reported injection of the bridge and chromium trying to use it and reporting it is as unavailable.

Thanks in advance for your reply and this library!

KevinnZou commented 2 weeks ago

@minarja1 Thanks for your feedback! JsBridge will be cleared when the page is fully loaded, so it will be useless to inject it early.

minarja1 commented 2 weeks ago

@KevinnZou Thank you for the reply. Can you please explain in a bit more detail? Why would it be cleared after the page is loaded?