From 1a711a512fb7caf4db25e815a0a89d858e3811b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 31 Oct 2023 15:25:43 +0800 Subject: [PATCH 01/65] feat:Import KotlinX Serialization --- build.gradle.kts | 1 + settings.gradle.kts | 1 + webview/build.gradle.kts | 1 + 3 files changed, 3 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 1b6ac51b..105c0821 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ plugins { // this is necessary to avoid the plugins to be loaded multiple times // in each subproject's classloader kotlin("multiplatform").apply(false) + kotlin("plugin.serialization") id("com.android.application").apply(false) id("com.android.library").apply(false) id("org.jetbrains.compose").apply(false) diff --git a/settings.gradle.kts b/settings.gradle.kts index 852ec120..c8caf2c4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,6 +20,7 @@ pluginManagement { kotlin("jvm").version(kotlinVersion) kotlin("multiplatform").version(kotlinVersion) + kotlin("plugin.serialization").version(kotlinVersion) kotlin("android").version(kotlinVersion) id("com.android.application").version(agpVersion) diff --git a/webview/build.gradle.kts b/webview/build.gradle.kts index 85cdf2e1..7c6d2b6e 100644 --- a/webview/build.gradle.kts +++ b/webview/build.gradle.kts @@ -43,6 +43,7 @@ kotlin { implementation(compose.components.resources) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("co.touchlab:kermit:2.0.0-RC5") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") } } val androidMain by getting { From c6285d8c8e6eb7eba010f4b3fbad9646f92b51d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 10:27:55 +0800 Subject: [PATCH 02/65] feat:Base JsHandler and JsMessage --- .../multiplatform/webview/jsbridge/IJsHandler.kt | 15 +++++++++++++++ .../multiplatform/webview/jsbridge/JsMessage.kt | 10 ++++++++++ 2 files changed, 25 insertions(+) create mode 100644 webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt create mode 100644 webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt new file mode 100644 index 00000000..2e2c4ee6 --- /dev/null +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt @@ -0,0 +1,15 @@ +package com.multiplatform.webview.jsbridge + +/** + * Created By Kevin Zou On 2023/10/31 + */ +interface IJsHandler { + fun id(): String + + fun canHandle(id: String) = id() == id + + fun handle( + message: JsMessage, + callback: (Any) -> Unit, + ) +} diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt new file mode 100644 index 00000000..72a2b496 --- /dev/null +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt @@ -0,0 +1,10 @@ +package com.multiplatform.webview.jsbridge + +/** + * Created By Kevin Zou On 2023/10/31 + */ +data class JsMessage( + val id: Int, + val methodName: String, + val params: String, +) From d5abe4be5fac2138c255d90866e2911ddbf15c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 10:28:27 +0800 Subject: [PATCH 03/65] feat:Base JsDispatcher for dispatch messages --- .../webview/jsbridge/JsDispatcher.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt new file mode 100644 index 00000000..b721d565 --- /dev/null +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt @@ -0,0 +1,25 @@ +package com.multiplatform.webview.jsbridge + +/** + * Created By Kevin Zou On 2023/10/31 + */ +class JsDispatcher { + private val jsHandlerMap = mutableMapOf() + + fun registerJSHandler(handler: IJsHandler) { + jsHandlerMap[handler.id()] = handler + } + + fun dispatch( + message: JsMessage, + callback: (Any) -> Unit, + ) { + jsHandlerMap[message.methodName]?.handle(message, callback) + } + + fun canHandle(id: String) = jsHandlerMap.containsKey(id) + + fun unregisterJSHandler(handler: IJsHandler) { + jsHandlerMap.remove(handler.id()) + } +} From 95a051f90c41dc6b862ffa2cefe4f25def50cbcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 10:29:10 +0800 Subject: [PATCH 04/65] feat:Base JsBridge for communication between Native and Js --- .../webview/jsbridge/JsBridge.kt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt new file mode 100644 index 00000000..8d4a04c0 --- /dev/null +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt @@ -0,0 +1,48 @@ +package com.multiplatform.webview.jsbridge + +import com.multiplatform.webview.web.IWebView +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.jetbrains.compose.resources.ExperimentalResourceApi +import org.jetbrains.compose.resources.resource + +/** + * Created By Kevin Zou On 2023/10/31 + */ +class JsBridge { + private val jsDispatcher = JsDispatcher() + private var initJs = "" + private var webView: IWebView? = null + + fun register(handler: IJsHandler) { + jsDispatcher.registerJSHandler(handler) + } + + fun unregister(handler: IJsHandler) { + jsDispatcher.unregisterJSHandler(handler) + } + + @OptIn(ExperimentalResourceApi::class) + private suspend fun injectInitJS() { + if (initJs.isEmpty()) + { + val res = resource("jsbridge.js") + initJs = res.readBytes().decodeToString() + } + webView?.evaluateJavaScript(initJs) + } + + fun dispatch(message: JsMessage) { + jsDispatcher.dispatch(message) { + onCallback(it, message.id) + } + } + + fun onCallback( + data: Any, + callbackId: Int, + ) { + val res = Json.encodeToString(data) + webView?.evaluateJavaScript("window.jsBridge.onCallback('$callbackId', $res)") + } +} From 6cc402a74f804670f16b012806ff562c62fbd539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 10:33:17 +0800 Subject: [PATCH 05/65] feat:Init Jsbridge config in WebView --- .../com/multiplatform/webview/web/IWebView.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index 02fc4e3e..5b281ceb 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -1,5 +1,8 @@ package com.multiplatform.webview.web +import com.multiplatform.webview.jsbridge.JsBridge +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.resource @@ -11,6 +14,10 @@ import org.jetbrains.compose.resources.resource * Interface for WebView */ interface IWebView { + var scope: CoroutineScope + + var jsBridge: JsBridge + /** * True when the web view is able to navigate backwards, false otherwise. */ @@ -145,4 +152,20 @@ interface IWebView { script: String, callback: ((String) -> Unit)? = null, ) + + @OptIn(ExperimentalResourceApi::class) + suspend fun injectInitJS() { + val res = resource("jsbridge.js") + val initJs = res.readBytes().decodeToString() + evaluateJavaScript(initJs) + } + + fun injectBridge(jsBridge: JsBridge) + + fun init() { + scope.launch { + injectInitJS() + injectBridge(jsBridge) + } + } } From 61c2457d4c3f4aee7bb8722ead7564298e942e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 10:35:22 +0800 Subject: [PATCH 06/65] feat:Basic JsBridge setup in Android with JavaScriptInterface --- .../webview/web/AccompanistWebView.kt | 4 +++- .../webview/web/AndroidWebView.kt | 24 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt index 736fae91..2f21e8df 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt @@ -15,6 +15,7 @@ import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView import com.multiplatform.webview.util.KLogger @@ -137,6 +138,7 @@ fun AccompanistWebView( factory: ((Context) -> WebView)? = null, ) { val webView = state.webView + val scope = rememberCoroutineScope() BackHandler(captureBackPresses && navigator.canGoBack) { webView?.goBack() @@ -189,7 +191,7 @@ fun AccompanistWebView( domStorageEnabled = it.domStorageEnabled } } - }.also { state.webView = AndroidWebView(it) } + }.also { state.webView = AndroidWebView(it, scope, state.jsBridge) } }, modifier = modifier, onRelease = { diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index c21aa771..b7f8e101 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -1,7 +1,11 @@ package com.multiplatform.webview.web +import android.webkit.JavascriptInterface import android.webkit.WebView +import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.util.KLogger +import kotlinx.coroutines.CoroutineScope /** * Created By Kevin Zou On 2023/9/5 @@ -10,7 +14,11 @@ import com.multiplatform.webview.util.KLogger /** * Android implementation of [IWebView] */ -class AndroidWebView(private val webView: WebView) : IWebView { +class AndroidWebView( + private val webView: WebView, + override var scope: CoroutineScope, + override var jsBridge: JsBridge, +) : IWebView { override fun canGoBack() = webView.canGoBack() override fun canGoForward() = webView.canGoForward() @@ -75,4 +83,18 @@ class AndroidWebView(private val webView: WebView) : IWebView { webView.evaluateJavascript(androidScript, callback) } } + + override fun injectBridge(jsBridge: JsBridge) { + webView.addJavascriptInterface(this, "jsBridge") + } + + @JavascriptInterface + fun call( + id: Int, + method: String, + params: String, + ) { + KLogger.d { "call from JS: $id, $method, $params" } + jsBridge.dispatch(JsMessage(id, method, params)) + } } From 168edcf91a28295bc12b2549679eb06be1b2b57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 10:35:55 +0800 Subject: [PATCH 07/65] feat:Basic JsBridge setup in iOS with WKScriptMessageHandler --- .../multiplatform/webview/web/IOSWebView.kt | 41 ++++++++++++++++++- .../multiplatform/webview/web/WebView.ios.kt | 7 +++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index 8b38fa08..f58ea607 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -1,10 +1,14 @@ package com.multiplatform.webview.web +import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.util.KLogger import kotlinx.cinterop.BetaInteropApi import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.allocArrayOf import kotlinx.cinterop.memScoped +import kotlinx.coroutines.CoroutineScope +import kotlinx.serialization.json.Json import platform.Foundation.HTTPBody import platform.Foundation.HTTPMethod import platform.Foundation.NSBundle @@ -13,6 +17,9 @@ import platform.Foundation.NSMutableURLRequest import platform.Foundation.NSURL import platform.Foundation.create import platform.Foundation.setValue +import platform.WebKit.WKScriptMessage +import platform.WebKit.WKScriptMessageHandlerProtocol +import platform.WebKit.WKUserContentController import platform.WebKit.WKWebView import platform.darwin.NSObject import platform.darwin.NSObjectMeta @@ -24,7 +31,15 @@ import platform.darwin.NSObjectMeta /** * iOS implementation of [IWebView] */ -class IOSWebView(private val wkWebView: WKWebView) : IWebView { +class IOSWebView( + private val wkWebView: WKWebView, + override var scope: CoroutineScope, + override var jsBridge: JsBridge, +) : IWebView { + init { + init() + } + override fun canGoBack() = wkWebView.canGoBack override fun canGoForward() = wkWebView.canGoForward @@ -125,6 +140,30 @@ class IOSWebView(private val wkWebView: WKWebView) : IWebView { } } + override fun injectBridge(jsBridge: JsBridge) { + val userController = + WKUserContentController().apply { + addScriptMessageHandler( + object : WKScriptMessageHandlerProtocol, NSObject() { + override fun userContentController( + userContentController: WKUserContentController, + didReceiveScriptMessage: WKScriptMessage, + ) { + KLogger.d { "didReceiveScriptMessage: $didReceiveScriptMessage" } + val body = didReceiveScriptMessage.body + val method = didReceiveScriptMessage.name + if (body is String) { + val message = Json.decodeFromString(body) + jsBridge.dispatch(message) + } + } + }, + "jsBridge", + ) + } + wkWebView.configuration.userContentController = userController + } + private class BundleMarker : NSObject() { companion object : NSObjectMeta() } diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt index 1099f582..17742281 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt @@ -2,6 +2,7 @@ package com.multiplatform.webview.web import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.interop.UIKitView import kotlinx.cinterop.ExperimentalForeignApi @@ -55,6 +56,7 @@ fun IOSWebView( ) } val navigationDelegate = remember { WKNavigationDelegate(state, navigator) } + val scope = rememberCoroutineScope() UIKitView( factory = { @@ -88,7 +90,10 @@ fun IOSWebView( ) this.navigationDelegate = navigationDelegate onCreated() - }.also { state.webView = IOSWebView(it) } + }.also { + val iosWebView = IOSWebView(it, scope, state.jsBridge) + state.webView = iosWebView + } }, modifier = modifier, onRelease = { From 3d2016a86078e44a2730e4d6e6946e41e5138eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 10:36:20 +0800 Subject: [PATCH 08/65] feat:add JsBridge in WebViewState --- .../kotlin/com/multiplatform/webview/web/WebViewState.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt index fe2b930f..5720a2f0 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateList import com.multiplatform.webview.cookie.CookieManager import com.multiplatform.webview.cookie.WebViewCookieManager +import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.setting.WebSettings /** @@ -62,6 +63,8 @@ class WebViewState(webContent: WebContent) { */ val webSettings: WebSettings by mutableStateOf(WebSettings()) + val jsBridge: JsBridge by mutableStateOf(JsBridge()) + /** * Whether the WebView should capture back presses and navigate back. * We need access to this in the state saver. An internal DisposableEffect or AndroidView From e8675e95d1eec0b1ccd9869fd181b35acd085e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 10:37:35 +0800 Subject: [PATCH 09/65] feat:Leave desktop implementation as TODO --- .../com/multiplatform/webview/web/DesktopWebView.kt | 11 ++++++++++- .../com/multiplatform/webview/web/WebView.desktop.kt | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index 374b238e..9f527173 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -1,7 +1,9 @@ package com.multiplatform.webview.web +import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.util.KLogger import dev.datlag.kcef.KCEFBrowser +import kotlinx.coroutines.CoroutineScope import org.cef.network.CefPostData import org.cef.network.CefPostDataElement import org.cef.network.CefRequest @@ -9,7 +11,11 @@ import org.cef.network.CefRequest /** * Created By Kevin Zou On 2023/9/12 */ -class DesktopWebView(private val webView: KCEFBrowser) : IWebView { +class DesktopWebView( + private val webView: KCEFBrowser, + override var scope: CoroutineScope, + override var jsBridge: JsBridge, +) : IWebView { override fun canGoBack() = webView.canGoBack() override fun canGoForward() = webView.canGoForward() @@ -89,4 +95,7 @@ class DesktopWebView(private val webView: KCEFBrowser) : IWebView { } } } + + override fun injectBridge(jsBridge: JsBridge) { + } } diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt index 9157f48f..51ce4ac1 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.awt.SwingPanel @@ -61,6 +62,7 @@ fun DesktopWebView( } } } + val scope = rememberCoroutineScope() val fileContent by produceState("", state.content) { value = if (state.content is WebContent.File) { @@ -118,7 +120,7 @@ fun DesktopWebView( } } }?.also { - state.webView = DesktopWebView(it) + state.webView = DesktopWebView(it, scope, state.jsBridge) } browser?.let { From 5c99039f88fc155e9a1f38752b5e4e8536563b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 11:26:24 +0800 Subject: [PATCH 10/65] feat:add init in Android WebView --- .../kotlin/com/multiplatform/webview/web/AndroidWebView.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index b7f8e101..7f9ec230 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -19,6 +19,9 @@ class AndroidWebView( override var scope: CoroutineScope, override var jsBridge: JsBridge, ) : IWebView { + init { + initWebView() + } override fun canGoBack() = webView.canGoBack() override fun canGoForward() = webView.canGoForward() From 28851b97dbed8dc54aa924d26567285f029234f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 11:27:12 +0800 Subject: [PATCH 11/65] feat:Add test sample for JsBridge --- sample/shared/src/commonMain/resources/assets/index.html | 1 + sample/shared/src/commonMain/resources/assets/script.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/sample/shared/src/commonMain/resources/assets/index.html b/sample/shared/src/commonMain/resources/assets/index.html index 19273827..37f1f518 100644 --- a/sample/shared/src/commonMain/resources/assets/index.html +++ b/sample/shared/src/commonMain/resources/assets/index.html @@ -8,5 +8,6 @@

Compose WebView Multiplatform

Basic Html Test

+ \ No newline at end of file diff --git a/sample/shared/src/commonMain/resources/assets/script.js b/sample/shared/src/commonMain/resources/assets/script.js index eb6c9692..dcfbbf83 100644 --- a/sample/shared/src/commonMain/resources/assets/script.js +++ b/sample/shared/src/commonMain/resources/assets/script.js @@ -1,3 +1,7 @@ function callJS() { return 'Response from JS'; +} + +function callNative() { + window.jsBridge.call('1', 'callNative', '{"name":"callNative"}'); } \ No newline at end of file From e2e8cb1021ec31f93132142c281b6079c431b9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 11:28:45 +0800 Subject: [PATCH 12/65] feat:Rename to initWebView --- .../kotlin/com/multiplatform/webview/web/IWebView.kt | 4 ++-- .../kotlin/com/multiplatform/webview/web/IOSWebView.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index 5b281ceb..b50ddf8a 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -162,9 +162,9 @@ interface IWebView { fun injectBridge(jsBridge: JsBridge) - fun init() { + fun initWebView() { scope.launch { - injectInitJS() +// injectInitJS() injectBridge(jsBridge) } } diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index f58ea607..3ea76a64 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -37,7 +37,7 @@ class IOSWebView( override var jsBridge: JsBridge, ) : IWebView { init { - init() + initWebView() } override fun canGoBack() = wkWebView.canGoBack From 652b077ecfee87e0c5e5757176080f66d457bd33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 1 Nov 2023 11:29:18 +0800 Subject: [PATCH 13/65] feat:Sample for test --- .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 9 +++++---- .../commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 7c397309..e6ecd36f 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -18,6 +18,7 @@ import com.multiplatform.webview.util.KLogSeverity import com.multiplatform.webview.web.WebView import com.multiplatform.webview.web.rememberWebViewNavigator import com.multiplatform.webview.web.rememberWebViewStateWithHTMLData +import com.multiplatform.webview.web.rememberWebViewStateWithHTMLFile /** * Created By Kevin Zou On 2023/9/8 @@ -56,10 +57,10 @@ internal fun BasicWebViewWithHTMLSample() { """.trimIndent() -// val webViewState = rememberWebViewStateWithHTMLFile( -// fileName = "index.html", -// ) - val webViewState = rememberWebViewStateWithHTMLData(html) + val webViewState = rememberWebViewStateWithHTMLFile( + fileName = "index.html", + ) +// val webViewState = rememberWebViewStateWithHTMLData(html) LaunchedEffect(Unit) { webViewState.webSettings.apply { zoomLevel = 1.0 diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt index 3e262090..7932bbf7 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt @@ -12,8 +12,8 @@ import com.multiplatform.webview.web.rememberWebViewState @Composable internal fun WebViewApp() { // WebViewSample() - BasicWebViewSample() -// BasicWebViewWithHTMLSample() +// BasicWebViewSample() + BasicWebViewWithHTMLSample() } @Composable From ea70bb67478a9910ddfd14f9c2f68a9f57812e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Mon, 4 Dec 2023 17:25:48 +0800 Subject: [PATCH 14/65] feat:Apply KotlinX Serialization plugin --- webview/build.gradle.kts | 1 + .../kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt | 3 +++ 2 files changed, 4 insertions(+) diff --git a/webview/build.gradle.kts b/webview/build.gradle.kts index 7c6d2b6e..b9c31768 100644 --- a/webview/build.gradle.kts +++ b/webview/build.gradle.kts @@ -6,6 +6,7 @@ plugins { id("org.jetbrains.compose") id("org.jetbrains.dokka") id("com.vanniktech.maven.publish") + kotlin("plugin.serialization") version "1.9.10" } kotlin { diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt index 72a2b496..11003f87 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt @@ -1,8 +1,11 @@ package com.multiplatform.webview.jsbridge +import kotlinx.serialization.Serializable + /** * Created By Kevin Zou On 2023/10/31 */ +@Serializable data class JsMessage( val id: Int, val methodName: String, From 9eb905087446dad448276826ee3a207f539133af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Mon, 4 Dec 2023 19:37:27 +0800 Subject: [PATCH 15/65] feat:Setup WKJsMessageHandler --- .../webview/jsbridge/WKJsMessageHandler.kt | 30 +++++++++++++++++ .../multiplatform/webview/web/IOSWebView.kt | 32 ++++--------------- .../multiplatform/webview/web/WKWebViewExt.kt | 29 +++++++++++++++++ .../multiplatform/webview/web/WebView.ios.kt | 20 ++---------- 4 files changed, 67 insertions(+), 44 deletions(-) create mode 100644 webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt new file mode 100644 index 00000000..7c03ce78 --- /dev/null +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt @@ -0,0 +1,30 @@ +package com.multiplatform.webview.jsbridge + +import com.multiplatform.webview.util.KLogger +import kotlinx.serialization.json.Json +import platform.WebKit.WKScriptMessage +import platform.WebKit.WKScriptMessageHandlerProtocol +import platform.WebKit.WKUserContentController +import platform.darwin.NSObject + +/** + * Created By Kevin Zou On 2023/11/1 + */ +class WKJsMessageHandler(private val jsBridge: JsBridge) : WKScriptMessageHandlerProtocol, + NSObject() { + override fun userContentController( + userContentController: WKUserContentController, + didReceiveScriptMessage: WKScriptMessage + ) { + val body = didReceiveScriptMessage.body + val method = didReceiveScriptMessage.name + KLogger.i { "didReceiveScriptMessage: $body, $method" } + (body as String).apply { + val message = Json.decodeFromString(body) + KLogger.i { + "WKJsMessageHandler: $message" + } +// jsBridge.dispatch(message) + } + } +} \ No newline at end of file diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index 3ea76a64..ca30d5aa 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -1,14 +1,13 @@ package com.multiplatform.webview.web import com.multiplatform.webview.jsbridge.JsBridge -import com.multiplatform.webview.jsbridge.JsMessage +import com.multiplatform.webview.jsbridge.WKJsMessageHandler import com.multiplatform.webview.util.KLogger import kotlinx.cinterop.BetaInteropApi import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.allocArrayOf import kotlinx.cinterop.memScoped import kotlinx.coroutines.CoroutineScope -import kotlinx.serialization.json.Json import platform.Foundation.HTTPBody import platform.Foundation.HTTPMethod import platform.Foundation.NSBundle @@ -17,9 +16,6 @@ import platform.Foundation.NSMutableURLRequest import platform.Foundation.NSURL import platform.Foundation.create import platform.Foundation.setValue -import platform.WebKit.WKScriptMessage -import platform.WebKit.WKScriptMessageHandlerProtocol -import platform.WebKit.WKUserContentController import platform.WebKit.WKWebView import platform.darwin.NSObject import platform.darwin.NSObjectMeta @@ -141,27 +137,11 @@ class IOSWebView( } override fun injectBridge(jsBridge: JsBridge) { - val userController = - WKUserContentController().apply { - addScriptMessageHandler( - object : WKScriptMessageHandlerProtocol, NSObject() { - override fun userContentController( - userContentController: WKUserContentController, - didReceiveScriptMessage: WKScriptMessage, - ) { - KLogger.d { "didReceiveScriptMessage: $didReceiveScriptMessage" } - val body = didReceiveScriptMessage.body - val method = didReceiveScriptMessage.name - if (body is String) { - val message = Json.decodeFromString(body) - jsBridge.dispatch(message) - } - } - }, - "jsBridge", - ) - } - wkWebView.configuration.userContentController = userController + KLogger.i { "injectBridge" } + val jsMessageHandler = WKJsMessageHandler(jsBridge) + wkWebView.configuration.userContentController.apply { + addScriptMessageHandler(jsMessageHandler, "jsBridge") + } } private class BundleMarker : NSObject() { diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewExt.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewExt.kt index e3c340fe..0898d6b1 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewExt.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewExt.kt @@ -9,6 +9,13 @@ import platform.darwin.NSObject /** * Created By Kevin Zou On 2023/9/13 */ +val observedProgressList = listOf( + "estimatedProgress", + "title", + "URL", + "canGoBack", + "canGoForward", +) /** * Adds observers for the given properties @@ -39,3 +46,25 @@ fun WKWebView.removeObservers( this.removeObserver(observer, forKeyPath = it) } } + +@OptIn(ExperimentalForeignApi::class) +fun WKWebView.addProgressObservers( + observer: NSObject, +) { + this.addObservers( + observer = observer, + properties = observedProgressList, + ) +} + +/** + * Removes observers for the given properties + */ +fun WKWebView.removeProgressObservers( + observer: NSObject, +) { + this.removeObservers( + observer = observer, + properties = observedProgressList, + ) +} diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt index 17742281..eebf9dcb 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt @@ -77,16 +77,8 @@ fun IOSWebView( userInteractionEnabled = captureBackPresses allowsBackForwardNavigationGestures = captureBackPresses customUserAgent = state.webSettings.customUserAgentString - this.addObservers( + this.addProgressObservers( observer = observer, - properties = - listOf( - "estimatedProgress", - "title", - "URL", - "canGoBack", - "canGoForward", - ), ) this.navigationDelegate = navigationDelegate onCreated() @@ -98,16 +90,8 @@ fun IOSWebView( modifier = modifier, onRelease = { state.webView = null - it.removeObservers( + it.removeProgressObservers( observer = observer, - properties = - listOf( - "estimatedProgress", - "title", - "URL", - "canGoBack", - "canGoForward", - ), ) it.navigationDelegate = null onDispose() From ce0d389383b7cf2056ed17993a8cb53aa33324b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Mon, 4 Dec 2023 19:38:02 +0800 Subject: [PATCH 16/65] feat:Test Sample for iOS JsBridge --- .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 2 +- sample/shared/src/commonMain/resources/assets/index.html | 3 ++- sample/shared/src/commonMain/resources/assets/script.js | 8 ++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index e6ecd36f..38cc74ed 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -17,7 +17,6 @@ import androidx.compose.ui.unit.dp import com.multiplatform.webview.util.KLogSeverity import com.multiplatform.webview.web.WebView import com.multiplatform.webview.web.rememberWebViewNavigator -import com.multiplatform.webview.web.rememberWebViewStateWithHTMLData import com.multiplatform.webview.web.rememberWebViewStateWithHTMLFile /** @@ -90,6 +89,7 @@ internal fun BasicWebViewWithHTMLSample() { webViewNavigator.evaluateJavaScript( """ document.getElementById("subtitle").innerText = "Hello from KMM!"; + callIOS(); callJS(); """.trimIndent(), ) { diff --git a/sample/shared/src/commonMain/resources/assets/index.html b/sample/shared/src/commonMain/resources/assets/index.html index 37f1f518..60a82b23 100644 --- a/sample/shared/src/commonMain/resources/assets/index.html +++ b/sample/shared/src/commonMain/resources/assets/index.html @@ -8,6 +8,7 @@

Compose WebView Multiplatform

Basic Html Test

- + + \ No newline at end of file diff --git a/sample/shared/src/commonMain/resources/assets/script.js b/sample/shared/src/commonMain/resources/assets/script.js index dcfbbf83..5002ac06 100644 --- a/sample/shared/src/commonMain/resources/assets/script.js +++ b/sample/shared/src/commonMain/resources/assets/script.js @@ -2,6 +2,10 @@ function callJS() { return 'Response from JS'; } -function callNative() { - window.jsBridge.call('1', 'callNative', '{"name":"callNative"}'); +function callAndroid() { + window.jsBridge.call('1', 'callAndroid', '{"name":"callAndroid"}'); +} + +function callIOS() { + window.webkit.messageHandlers.jsBridge.postMessage("{\"id\":\"1\",\"methodName\":\"callIOS\",\"params\":\"{\\\"type\\\":\\\"1\\\"}\"}"); } \ No newline at end of file From 9cc9a070464c12fe3ff9f0c57735707bf69c8772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Mon, 4 Dec 2023 20:13:36 +0800 Subject: [PATCH 17/65] feat:Basic structure for Desktop by CefMessageRouterHandlerAdapter --- .../webview/web/DesktopWebView.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index 9f527173..d4aeed14 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -4,6 +4,11 @@ import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.util.KLogger import dev.datlag.kcef.KCEFBrowser import kotlinx.coroutines.CoroutineScope +import org.cef.browser.CefBrowser +import org.cef.browser.CefFrame +import org.cef.browser.CefMessageRouter +import org.cef.callback.CefQueryCallback +import org.cef.handler.CefMessageRouterHandlerAdapter import org.cef.network.CefPostData import org.cef.network.CefPostDataElement import org.cef.network.CefRequest @@ -16,6 +21,10 @@ class DesktopWebView( override var scope: CoroutineScope, override var jsBridge: JsBridge, ) : IWebView { + init { + initWebView() + } + override fun canGoBack() = webView.canGoBack() override fun canGoForward() = webView.canGoForward() @@ -97,5 +106,23 @@ class DesktopWebView( } override fun injectBridge(jsBridge: JsBridge) { + val router = CefMessageRouter.create() + val handler = object : CefMessageRouterHandlerAdapter() { + override fun onQuery( + browser: CefBrowser?, + frame: CefFrame?, + queryId: Long, + request: String?, + persistent: Boolean, + callback: CefQueryCallback? + ): Boolean { + KLogger.d { + "onQuery: $request" + } + return super.onQuery(browser, frame, queryId, request, persistent, callback) + } + } + router.addHandler(handler, false) + webView.client.addMessageRouter(router) } } From aa21ec22060c35381d356a00a450a080e84d9888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Mon, 4 Dec 2023 20:14:15 +0800 Subject: [PATCH 18/65] feat:Test Sample for Desktop JsBridge --- .../com/kevinnzou/sample/HtmlWebViewSample.kt | 24 ++++++++++++++----- .../src/commonMain/resources/assets/script.js | 12 ++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 38cc74ed..d275814b 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.unit.dp import com.multiplatform.webview.util.KLogSeverity import com.multiplatform.webview.web.WebView import com.multiplatform.webview.web.rememberWebViewNavigator -import com.multiplatform.webview.web.rememberWebViewStateWithHTMLFile +import com.multiplatform.webview.web.rememberWebViewStateWithHTMLData /** * Created By Kevin Zou On 2023/9/8 @@ -50,16 +50,28 @@ internal fun BasicWebViewWithHTMLSample() { function callJS() { return 'Response from JS'; } + function callDesktop() { + window.cefQuery({ + request: "{\"id\":\"1\",\"methodName\":\"callIOS\",\"params\":\"{\\\"type\\\":\\\"1\\\"}\"}", + onSuccess: function(response) { + // 处理Java应用程序的响应 + }, + onFailure: function(errorCode, errorMessage) { + // 处理错误 + } + }); + }

Compose WebView Multiplatform

Basic Html Test

+ """.trimIndent() - val webViewState = rememberWebViewStateWithHTMLFile( - fileName = "index.html", - ) -// val webViewState = rememberWebViewStateWithHTMLData(html) +// val webViewState = rememberWebViewStateWithHTMLFile( +// fileName = "index.html", +// ) + val webViewState = rememberWebViewStateWithHTMLData(html) LaunchedEffect(Unit) { webViewState.webSettings.apply { zoomLevel = 1.0 @@ -89,7 +101,7 @@ internal fun BasicWebViewWithHTMLSample() { webViewNavigator.evaluateJavaScript( """ document.getElementById("subtitle").innerText = "Hello from KMM!"; - callIOS(); + callDesktop(); callJS(); """.trimIndent(), ) { diff --git a/sample/shared/src/commonMain/resources/assets/script.js b/sample/shared/src/commonMain/resources/assets/script.js index 5002ac06..e0883414 100644 --- a/sample/shared/src/commonMain/resources/assets/script.js +++ b/sample/shared/src/commonMain/resources/assets/script.js @@ -8,4 +8,16 @@ function callAndroid() { function callIOS() { window.webkit.messageHandlers.jsBridge.postMessage("{\"id\":\"1\",\"methodName\":\"callIOS\",\"params\":\"{\\\"type\\\":\\\"1\\\"}\"}"); +} + +function callDesktop() { + window.cefQuery({ + request: "{\"id\":\"1\",\"methodName\":\"callIOS\",\"params\":\"{\\\"type\\\":\\\"1\\\"}\"}", + onSuccess: function(response) { + // 处理Java应用程序的响应 + }, + onFailure: function(errorCode, errorMessage) { + // 处理错误 + } + }); } \ No newline at end of file From 0fa09dd1c64ca9a92fe122b64108ada8d62dad87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Mon, 4 Dec 2023 21:48:50 +0800 Subject: [PATCH 19/65] feat:Desktop JsBridge Parameter Handle --- .../com/kevinnzou/sample/HtmlWebViewSample.kt | 2 +- .../src/commonMain/resources/assets/script.js | 2 +- .../com/multiplatform/webview/web/DesktopWebView.kt | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index d275814b..f1ab65f7 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -52,7 +52,7 @@ internal fun BasicWebViewWithHTMLSample() { } function callDesktop() { window.cefQuery({ - request: "{\"id\":\"1\",\"methodName\":\"callIOS\",\"params\":\"{\\\"type\\\":\\\"1\\\"}\"}", + request: "1_callDesktop_{\"type\":\"1\"}", onSuccess: function(response) { // 处理Java应用程序的响应 }, diff --git a/sample/shared/src/commonMain/resources/assets/script.js b/sample/shared/src/commonMain/resources/assets/script.js index e0883414..07f28d5f 100644 --- a/sample/shared/src/commonMain/resources/assets/script.js +++ b/sample/shared/src/commonMain/resources/assets/script.js @@ -12,7 +12,7 @@ function callIOS() { function callDesktop() { window.cefQuery({ - request: "{\"id\":\"1\",\"methodName\":\"callIOS\",\"params\":\"{\\\"type\\\":\\\"1\\\"}\"}", + request: "1_callDesktop_{\"type\":\"1\"}", onSuccess: function(response) { // 处理Java应用程序的响应 }, diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index d4aeed14..8097f0a1 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -1,6 +1,7 @@ package com.multiplatform.webview.web import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.util.KLogger import dev.datlag.kcef.KCEFBrowser import kotlinx.coroutines.CoroutineScope @@ -119,6 +120,18 @@ class DesktopWebView( KLogger.d { "onQuery: $request" } + val id = request?.substringBefore('_') + val methodName = request?.substringAfter('_')?.substringBefore('_') + val params = request?.substringAfterLast('_') + val message = JsMessage( + id?.toInt() ?: 0, + methodName ?: "", + params ?: "", + ) + KLogger.d { + "onQuery Message: $message" + } + jsBridge.dispatch(message) return super.onQuery(browser, frame, queryId, request, persistent, callback) } } From 0ac8dde2c7585eadfe0afcb52976b4e8c1a975c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 11:04:23 +0800 Subject: [PATCH 20/65] feat:Android JsBridge Parameter Handle --- .../multiplatform/webview/web/AndroidWebView.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index 7f9ec230..5ec1f786 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -6,6 +6,7 @@ import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.util.KLogger import kotlinx.coroutines.CoroutineScope +import kotlinx.serialization.json.Json /** * Created By Kevin Zou On 2023/9/5 @@ -93,11 +94,23 @@ class AndroidWebView( @JavascriptInterface fun call( + request: String + ) { + KLogger.d { "call from JS: $request" } + val message = Json.decodeFromString(request) + KLogger.i { + "call from JS: $message" + } +// jsBridge.dispatch(JsMessage(id, method, params)) + } + + @JavascriptInterface + fun callAndroid( id: Int, method: String, params: String, ) { - KLogger.d { "call from JS: $id, $method, $params" } + KLogger.d { "callAndroid call from JS: $id, $method, $params" } jsBridge.dispatch(JsMessage(id, method, params)) } } From c0a47523b233a256318ea9980ef8e50c5b88d100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 11:05:09 +0800 Subject: [PATCH 21/65] feat:change JsBridge id signature --- .../kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt | 4 ++-- .../kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt index 2e2c4ee6..a5b8f721 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt @@ -4,9 +4,9 @@ package com.multiplatform.webview.jsbridge * Created By Kevin Zou On 2023/10/31 */ interface IJsHandler { - fun id(): String + fun methodName(): String - fun canHandle(id: String) = id() == id + fun canHandle(methodName: String) = methodName() == methodName fun handle( message: JsMessage, diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt index b721d565..d51964df 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt @@ -7,7 +7,7 @@ class JsDispatcher { private val jsHandlerMap = mutableMapOf() fun registerJSHandler(handler: IJsHandler) { - jsHandlerMap[handler.id()] = handler + jsHandlerMap[handler.methodName()] = handler } fun dispatch( @@ -20,6 +20,6 @@ class JsDispatcher { fun canHandle(id: String) = jsHandlerMap.containsKey(id) fun unregisterJSHandler(handler: IJsHandler) { - jsHandlerMap.remove(handler.id()) + jsHandlerMap.remove(handler.methodName()) } } From 50738ed690ec3ead84974f847d52e600895442c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 11:18:28 +0800 Subject: [PATCH 22/65] feat:Platform Util --- .../com/multiplatform/webview/util/getPlatform.kt | 5 +++++ .../com/multiplatform/webview/util/Platform.kt | 12 ++++++++++++ .../com/multiplatform/webview/util/getPlatform.kt | 5 +++++ .../com/multiplatform/webview/util/getPlatform.kt | 5 +++++ 4 files changed, 27 insertions(+) create mode 100644 webview/src/androidMain/kotlin/com/multiplatform/webview/util/getPlatform.kt create mode 100644 webview/src/commonMain/kotlin/com/multiplatform/webview/util/Platform.kt create mode 100644 webview/src/desktopMain/kotlin/com/multiplatform/webview/util/getPlatform.kt create mode 100644 webview/src/iosMain/kotlin/com/multiplatform/webview/util/getPlatform.kt diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/util/getPlatform.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/util/getPlatform.kt new file mode 100644 index 00000000..fd81afa0 --- /dev/null +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/util/getPlatform.kt @@ -0,0 +1,5 @@ +package com.multiplatform.webview.util + +internal actual fun getPlatform(): Platform { + return Platform.Android +} \ No newline at end of file diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/util/Platform.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/Platform.kt new file mode 100644 index 00000000..5d974006 --- /dev/null +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/Platform.kt @@ -0,0 +1,12 @@ +package com.multiplatform.webview.util + +/** + * Created By Kevin Zou On 2023/12/5 + */ +internal sealed class Platform { + data object Android : Platform() + data object Desktop : Platform() + data object IOS : Platform() +} + +internal expect fun getPlatform(): Platform \ No newline at end of file diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/util/getPlatform.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/util/getPlatform.kt new file mode 100644 index 00000000..24b70a29 --- /dev/null +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/util/getPlatform.kt @@ -0,0 +1,5 @@ +package com.multiplatform.webview.util + +internal actual fun getPlatform(): Platform { + return Platform.Desktop +} \ No newline at end of file diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/util/getPlatform.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/util/getPlatform.kt new file mode 100644 index 00000000..c6bfc376 --- /dev/null +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/util/getPlatform.kt @@ -0,0 +1,5 @@ +package com.multiplatform.webview.util + +internal actual fun getPlatform(): Platform { + return Platform.IOS +} \ No newline at end of file From c20693c0024101bb2080cd11190c1a10ac255db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 11:20:40 +0800 Subject: [PATCH 23/65] feat:Desktop JsBridge parameter change to Json string --- .../src/commonMain/resources/assets/script.js | 2 +- .../webview/web/DesktopWebView.kt | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/sample/shared/src/commonMain/resources/assets/script.js b/sample/shared/src/commonMain/resources/assets/script.js index 07f28d5f..e0883414 100644 --- a/sample/shared/src/commonMain/resources/assets/script.js +++ b/sample/shared/src/commonMain/resources/assets/script.js @@ -12,7 +12,7 @@ function callIOS() { function callDesktop() { window.cefQuery({ - request: "1_callDesktop_{\"type\":\"1\"}", + request: "{\"id\":\"1\",\"methodName\":\"callIOS\",\"params\":\"{\\\"type\\\":\\\"1\\\"}\"}", onSuccess: function(response) { // 处理Java应用程序的响应 }, diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index 8097f0a1..c5e7aa10 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -5,6 +5,7 @@ import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.util.KLogger import dev.datlag.kcef.KCEFBrowser import kotlinx.coroutines.CoroutineScope +import kotlinx.serialization.json.Json import org.cef.browser.CefBrowser import org.cef.browser.CefFrame import org.cef.browser.CefMessageRouter @@ -117,17 +118,18 @@ class DesktopWebView( persistent: Boolean, callback: CefQueryCallback? ): Boolean { + if (request == null) return super.onQuery( + browser, + frame, + queryId, + request, + persistent, + callback + ) KLogger.d { "onQuery: $request" } - val id = request?.substringBefore('_') - val methodName = request?.substringAfter('_')?.substringBefore('_') - val params = request?.substringAfterLast('_') - val message = JsMessage( - id?.toInt() ?: 0, - methodName ?: "", - params ?: "", - ) + val message = Json.decodeFromString(request) KLogger.d { "onQuery Message: $message" } From 110e8852fbf05c0661a05dba089dd0d3694fef7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 17:59:08 +0800 Subject: [PATCH 24/65] feat:Desktop clear error for new request --- .../kotlin/com/multiplatform/webview/web/WebEngineExt.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt index 834b3b96..515a7368 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt @@ -107,6 +107,7 @@ internal fun CefBrowser.addLoadListener( ) { KLogger.d { "Load Start ${browser?.url}" } state.loadingState = LoadingState.Loading(0F) + state.errorsForCurrentRequest.clear() } override fun onLoadEnd( @@ -129,8 +130,8 @@ internal fun CefBrowser.addLoadListener( failedUrl: String?, ) { state.loadingState = LoadingState.Finished - KLogger.e { - "Failed to load url: ${failedUrl}\n$errorText" + KLogger.i { + "Failed to load url: $errorCode ${failedUrl}\n$errorText" } state.errorsForCurrentRequest.add( WebViewError( From 88d6a010a1dfe5196414d63940eb74cc72992f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 18:00:58 +0800 Subject: [PATCH 25/65] feat:inject js to setup JsBridge --- .../webview/web/AndroidWebView.kt | 10 ++++++++ .../com/multiplatform/webview/web/IWebView.kt | 24 ++++++++++++++++--- .../com/multiplatform/webview/web/WebView.kt | 6 +++++ .../webview/web/DesktopWebView.kt | 13 ++++++++++ .../multiplatform/webview/web/IOSWebView.kt | 10 ++++++++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index 5ec1f786..af995e6a 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -88,6 +88,16 @@ class AndroidWebView( } } + override suspend fun injectInitJS() { + super.injectInitJS() + val callAndroid = """ + window.JsBridge.postMessage = function (message) { + window.jsBridge.call(message) + }; + """.trimIndent() + evaluateJavaScript(callAndroid) + } + override fun injectBridge(jsBridge: JsBridge) { webView.addJavascriptInterface(this, "jsBridge") } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index b50ddf8a..78b1b905 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -1,6 +1,7 @@ package com.multiplatform.webview.web import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.util.KLogger import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.jetbrains.compose.resources.ExperimentalResourceApi @@ -155,8 +156,26 @@ interface IWebView { @OptIn(ExperimentalResourceApi::class) suspend fun injectInitJS() { - val res = resource("jsbridge.js") - val initJs = res.readBytes().decodeToString() + KLogger.d { + "IWebView injectInitJS" + } + val initJs = """ + window.JsBridge = { + callbacks: {}, + callbackId: 0, + callNative: function (methodName, params, callback) { + var message = { + methodName: methodName, + params: params, + id: callback ? window.JsBridge.callbackId++ : -1 + }; + if (callback) { + window.JsBridge.callbacks[message.callbackId] = callback; + } + window.JsBridge.postMessage(JSON.stringify(message)); + }, + }; + """.trimIndent() evaluateJavaScript(initJs) } @@ -164,7 +183,6 @@ interface IWebView { fun initWebView() { scope.launch { -// injectInitJS() injectBridge(jsBridge) } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index 0b713757..fc1314a5 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -80,6 +80,12 @@ fun WebView( } } + LaunchedEffect(state.loadingState) { + if (state.loadingState is LoadingState.Finished) { + webView?.injectInitJS() + } + } + ActualWebView( state = state, modifier = modifier, diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index c5e7aa10..eb1483b3 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -107,6 +107,19 @@ class DesktopWebView( } } + override suspend fun injectInitJS() { + super.injectInitJS() + KLogger.d { + "DesktopWebView injectInitJS" + } + val callDesktop = """ + window.JsBridge.postMessage = function (message) { + window.cefQuery({request:message}); + }; + """.trimIndent() + evaluateJavaScript(callDesktop) + } + override fun injectBridge(jsBridge: JsBridge) { val router = CefMessageRouter.create() val handler = object : CefMessageRouterHandlerAdapter() { diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index ca30d5aa..0ef5225e 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -136,6 +136,16 @@ class IOSWebView( } } + override suspend fun injectInitJS() { + super.injectInitJS() + val callIOS = """ + window.JsBridge.postMessage = function (message) { + window.webkit.messageHandlers.jsBridge.postMessage(message); + }; + """.trimIndent() + evaluateJavaScript(callIOS) + } + override fun injectBridge(jsBridge: JsBridge) { KLogger.i { "injectBridge" } val jsMessageHandler = WKJsMessageHandler(jsBridge) From 701662e86d15fe906dffc6f336b3823bb87f4f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 18:01:42 +0800 Subject: [PATCH 26/65] feat:Add example for callNative --- .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index f1ab65f7..dc9f0a10 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -61,10 +61,13 @@ internal fun BasicWebViewWithHTMLSample() { } }); } + function callNative() { + window.JsBridge.callNative("Greet","hello") + }

Compose WebView Multiplatform

Basic Html Test

- + """.trimIndent() @@ -101,7 +104,7 @@ internal fun BasicWebViewWithHTMLSample() { webViewNavigator.evaluateJavaScript( """ document.getElementById("subtitle").innerText = "Hello from KMM!"; - callDesktop(); + window.JsBridge.callNative("Greet","hello") callJS(); """.trimIndent(), ) { From 392d1d0bcfb7e77dfd666118f9c83d3799d21bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 18:23:15 +0800 Subject: [PATCH 27/65] feat:Dispatch JSMessage to Handler --- .../com/kevinnzou/sample/HtmlWebViewSample.kt | 15 +++++++++++++++ .../multiplatform/webview/web/AndroidWebView.kt | 2 +- .../webview/jsbridge/WKJsMessageHandler.kt | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index dc9f0a10..8ba24fe1 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -14,6 +14,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import co.touchlab.kermit.Logger +import com.multiplatform.webview.jsbridge.IJsHandler +import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.util.KLogSeverity import com.multiplatform.webview.web.WebView import com.multiplatform.webview.web.rememberWebViewNavigator @@ -89,6 +92,18 @@ internal fun BasicWebViewWithHTMLSample() { } } } + webViewState.jsBridge.register(object : IJsHandler { + override fun methodName(): String { + return "Greet" + } + + override fun handle(message: JsMessage, callback: (Any) -> Unit) { + Logger.i { + "Greet Handler Get Message: $message" + } + } + + }) val webViewNavigator = rememberWebViewNavigator() var jsRes by mutableStateOf("Evaluate JavaScript") MaterialTheme { diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index af995e6a..d08f286f 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -111,7 +111,7 @@ class AndroidWebView( KLogger.i { "call from JS: $message" } -// jsBridge.dispatch(JsMessage(id, method, params)) + jsBridge.dispatch(message) } @JavascriptInterface diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt index 7c03ce78..db5af4a0 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt @@ -24,7 +24,7 @@ class WKJsMessageHandler(private val jsBridge: JsBridge) : WKScriptMessageHandle KLogger.i { "WKJsMessageHandler: $message" } -// jsBridge.dispatch(message) + jsBridge.dispatch(message) } } } \ No newline at end of file From 68df728b18b08b8f509d56eb41bbcba53e4b56f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 21:38:36 +0800 Subject: [PATCH 28/65] feat:Change sample to Json params --- .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 8ba24fe1..c69a32e1 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -65,7 +65,7 @@ internal fun BasicWebViewWithHTMLSample() { }); } function callNative() { - window.JsBridge.callNative("Greet","hello") + window.JsBridge.callNative("Greet",JSON.stringify({type: "1"})); }

Compose WebView Multiplatform

@@ -119,7 +119,7 @@ internal fun BasicWebViewWithHTMLSample() { webViewNavigator.evaluateJavaScript( """ document.getElementById("subtitle").innerText = "Hello from KMM!"; - window.JsBridge.callNative("Greet","hello") + window.JsBridge.callNative("Greet",JSON.stringify({type: "1"})); callJS(); """.trimIndent(), ) { From 534e044c2278fc61db3c1562156bb723be3410f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 21:43:04 +0800 Subject: [PATCH 29/65] feat:Add KLogger.info for iOS debug. --- .../kotlin/com/multiplatform/webview/web/AndroidWebView.kt | 4 ++-- .../kotlin/com/multiplatform/webview/util/KLogger.kt | 5 +++++ .../com/multiplatform/webview/cookie/DesktopCookieManager.kt | 4 ++-- .../kotlin/com/multiplatform/webview/web/WebEngineExt.kt | 1 + .../com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt | 4 ++-- .../kotlin/com/multiplatform/webview/web/IOSWebView.kt | 4 ++-- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index d08f286f..53a6510f 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -80,7 +80,7 @@ class AndroidWebView( callback: ((String) -> Unit)?, ) { val androidScript = "javascript:$script" - KLogger.i { + KLogger.d { "evaluateJavaScript: $androidScript" } webView.post { @@ -108,7 +108,7 @@ class AndroidWebView( ) { KLogger.d { "call from JS: $request" } val message = Json.decodeFromString(request) - KLogger.i { + KLogger.d { "call from JS: $message" } jsBridge.dispatch(message) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt index ef8c8b17..b0b1462b 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt @@ -20,6 +20,11 @@ internal object KLogger : Logger( fun setMinSeverity(severity: KLogSeverity) { mutableConfig.minSeverity = severity.toKermitSeverity() } + + // For iOS, it will not print out the log if the severity is upper than Debug in AS. + fun info(msg: () -> String) { + d { msg() } + } } enum class KLogSeverity { diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/cookie/DesktopCookieManager.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/cookie/DesktopCookieManager.kt index 534a7d0d..ff481db0 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/cookie/DesktopCookieManager.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/cookie/DesktopCookieManager.kt @@ -28,11 +28,11 @@ object DesktopCookieManager : CookieManager { Date(cookie.expiresDate ?: System.currentTimeMillis()), ) val addedCookie = KCEFCookieManager.instance.setCookie(url, cefCookie) - KLogger.i(tag = "DesktopCookieManager") { "Added Cookie: $addedCookie" } + KLogger.d(tag = "DesktopCookieManager") { "Added Cookie: $addedCookie" } } override suspend fun getCookies(url: String): List { - KLogger.i(tag = "DesktopCookieManager") { "DesktopCookieManager getCookies: $url" } + KLogger.d(tag = "DesktopCookieManager") { "DesktopCookieManager getCookies: $url" } return KCEFCookieManager.instance.getCookiesWhile(url, true).map { Cookie( diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt index 515a7368..9d9581d5 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt @@ -130,6 +130,7 @@ internal fun CefBrowser.addLoadListener( failedUrl: String?, ) { state.loadingState = LoadingState.Finished + // TODO Error KLogger.i { "Failed to load url: $errorCode ${failedUrl}\n$errorText" } diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt index db5af4a0..a6f182fd 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt @@ -18,10 +18,10 @@ class WKJsMessageHandler(private val jsBridge: JsBridge) : WKScriptMessageHandle ) { val body = didReceiveScriptMessage.body val method = didReceiveScriptMessage.name - KLogger.i { "didReceiveScriptMessage: $body, $method" } + KLogger.info { "didReceiveScriptMessage: $body, $method" } (body as String).apply { val message = Json.decodeFromString(body) - KLogger.i { + KLogger.info { "WKJsMessageHandler: $message" } jsBridge.dispatch(message) diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index 0ef5225e..ae12d89d 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -130,7 +130,7 @@ class IOSWebView( KLogger.e { "evaluateJavaScript error: $error" } callback?.invoke(error.localizedDescription()) } else { - KLogger.i { "evaluateJavaScript result: $result" } + KLogger.info { "evaluateJavaScript result: $result" } callback?.invoke(result?.toString() ?: "") } } @@ -147,7 +147,7 @@ class IOSWebView( } override fun injectBridge(jsBridge: JsBridge) { - KLogger.i { "injectBridge" } + KLogger.info { "injectBridge" } val jsMessageHandler = WKJsMessageHandler(jsBridge) wkWebView.configuration.userContentController.apply { addScriptMessageHandler(jsMessageHandler, "jsBridge") From c64b9bacc6a3a57ec64e1fcd5c401af776f7ee09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 5 Dec 2023 21:51:05 +0800 Subject: [PATCH 30/65] feat:iOS Evaluate JS support null callback. --- .../kotlin/com/multiplatform/webview/web/IOSWebView.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index ae12d89d..5cb35028 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -126,12 +126,13 @@ class IOSWebView( callback: ((String) -> Unit)?, ) { wkWebView.evaluateJavaScript(script) { result, error -> + if (callback == null) return@evaluateJavaScript if (error != null) { KLogger.e { "evaluateJavaScript error: $error" } - callback?.invoke(error.localizedDescription()) + callback.invoke(error.localizedDescription()) } else { KLogger.info { "evaluateJavaScript result: $result" } - callback?.invoke(result?.toString() ?: "") + callback.invoke(result?.toString() ?: "") } } } From 1c88c67328dabcf34c8fae55f0ba06c471838219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 6 Dec 2023 08:21:00 +0800 Subject: [PATCH 31/65] feat:IJsHandler add processParams support --- .../com/multiplatform/webview/jsbridge/IJsHandler.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt index a5b8f721..8c60ef7d 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt @@ -1,5 +1,7 @@ package com.multiplatform.webview.jsbridge +import kotlinx.serialization.json.Json + /** * Created By Kevin Zou On 2023/10/31 */ @@ -12,4 +14,9 @@ interface IJsHandler { message: JsMessage, callback: (Any) -> Unit, ) + +} + +inline fun IJsHandler.processParams(message: JsMessage): T { + return Json.decodeFromString(message.params) } From 4e65bfda955ce9ebbd730482103933cc1194b556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 6 Dec 2023 08:21:20 +0800 Subject: [PATCH 32/65] feat:IJsHandler add processParams support sample --- sample/shared/build.gradle.kts | 2 ++ .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 6 ++++++ .../kotlin/com/kevinnzou/sample/model/GreetModel.kt | 9 +++++++++ 3 files changed, 17 insertions(+) create mode 100644 sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/model/GreetModel.kt diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts index f1f3096c..8b8dbcd1 100644 --- a/sample/shared/build.gradle.kts +++ b/sample/shared/build.gradle.kts @@ -2,6 +2,7 @@ plugins { kotlin("multiplatform") id("com.android.library") id("org.jetbrains.compose") + kotlin("plugin.serialization") version "1.9.10" } @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) @@ -38,6 +39,7 @@ kotlin { implementation(compose.components.resources) implementation("co.touchlab:kermit:2.0.0-RC5") api(project(":webview")) + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") } } val androidMain by getting { diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index c69a32e1..e7033d27 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -15,8 +15,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import co.touchlab.kermit.Logger +import com.kevinnzou.sample.model.GreetModel import com.multiplatform.webview.jsbridge.IJsHandler import com.multiplatform.webview.jsbridge.JsMessage +import com.multiplatform.webview.jsbridge.processParams import com.multiplatform.webview.util.KLogSeverity import com.multiplatform.webview.web.WebView import com.multiplatform.webview.web.rememberWebViewNavigator @@ -101,6 +103,10 @@ internal fun BasicWebViewWithHTMLSample() { Logger.i { "Greet Handler Get Message: $message" } + val param = processParams(message) + Logger.i { + "Greet Handler Get Param: $param" + } } }) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/model/GreetModel.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/model/GreetModel.kt new file mode 100644 index 00000000..7ca63e51 --- /dev/null +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/model/GreetModel.kt @@ -0,0 +1,9 @@ +package com.kevinnzou.sample.model + +import kotlinx.serialization.Serializable + +/** + * Created By Kevin Zou On 2023/12/6 + */ +@Serializable +data class GreetModel(val type: String) From 663e795a67501e458b389f7a0b71876b0a63614d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 6 Dec 2023 10:44:15 +0800 Subject: [PATCH 33/65] feat:JsBridge support callback --- .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 8 +++++++- .../multiplatform/webview/web/AccompanistWebView.kt | 6 +++++- .../com/multiplatform/webview/jsbridge/JsBridge.kt | 11 +++++------ .../kotlin/com/multiplatform/webview/web/IWebView.kt | 11 ++++++++++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index e7033d27..6377a8e5 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -107,6 +107,7 @@ internal fun BasicWebViewWithHTMLSample() { Logger.i { "Greet Handler Get Param: $param" } + callback("KMM ${param.type}") } }) @@ -125,7 +126,12 @@ internal fun BasicWebViewWithHTMLSample() { webViewNavigator.evaluateJavaScript( """ document.getElementById("subtitle").innerText = "Hello from KMM!"; - window.JsBridge.callNative("Greet",JSON.stringify({type: "1"})); + window.JsBridge.callNative("Greet",JSON.stringify({type: "1"}), + function (data) { + document.getElementById("subtitle").innerText = data; + console.log("Greet from Native: " + data); + } + ); callJS(); """.trimIndent(), ) { diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt index 2f21e8df..b3c5a6d0 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt @@ -191,7 +191,11 @@ fun AccompanistWebView( domStorageEnabled = it.domStorageEnabled } } - }.also { state.webView = AndroidWebView(it, scope, state.jsBridge) } + }.also { + val androidWebView = AndroidWebView(it, scope, state.jsBridge) + state.webView = androidWebView + state.jsBridge.webView = androidWebView + } }, modifier = modifier, onRelease = { diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt index 8d4a04c0..da30d475 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt @@ -1,8 +1,6 @@ package com.multiplatform.webview.jsbridge import com.multiplatform.webview.web.IWebView -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.resource @@ -12,7 +10,7 @@ import org.jetbrains.compose.resources.resource class JsBridge { private val jsDispatcher = JsDispatcher() private var initJs = "" - private var webView: IWebView? = null + var webView: IWebView? = null fun register(handler: IJsHandler) { jsDispatcher.registerJSHandler(handler) @@ -38,11 +36,12 @@ class JsBridge { } } - fun onCallback( + private fun onCallback( data: Any, callbackId: Int, ) { - val res = Json.encodeToString(data) - webView?.evaluateJavaScript("window.jsBridge.onCallback('$callbackId', $res)") +// val res = Json.encodeToString(data) + val res = data.toString() + webView?.evaluateJavaScript("window.JsBridge.onCallback($callbackId, '$res')") } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index 78b1b905..e906e39c 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -170,10 +170,19 @@ interface IWebView { id: callback ? window.JsBridge.callbackId++ : -1 }; if (callback) { - window.JsBridge.callbacks[message.callbackId] = callback; + window.JsBridge.callbacks[message.id] = callback; + console.log('add callback: ' + message.id + ', ' + JSON.stringify(window.JsBridge.callbacks)); } window.JsBridge.postMessage(JSON.stringify(message)); }, + onCallback: function (callbackId, data) { + var callback = window.JsBridge.callbacks[callbackId]; + console.log('onCallback: ' + callbackId + ', ' + data + ', ' + JSON.stringify(window.JsBridge.callbacks)); + if (callback) { + callback(data); + delete window.JsBridge.callbacks[callbackId]; + } + } }; """.trimIndent() evaluateJavaScript(initJs) From 30ece48abda629ead027ba7f57eabb97e3b6a299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 6 Dec 2023 15:22:31 +0800 Subject: [PATCH 34/65] feat:Extract JsBridge from WebViewState --- .../com/kevinnzou/sample/HtmlWebViewSample.kt | 68 +++++++++++-------- .../webview/web/AccompanistWebView.kt | 8 ++- .../webview/web/AndroidWebView.kt | 6 +- .../webview/web/WebView.android.kt | 3 + .../webview/jsbridge/JsBridge.kt | 8 ++- .../com/multiplatform/webview/web/IWebView.kt | 11 ++- .../com/multiplatform/webview/web/WebView.kt | 6 +- .../multiplatform/webview/web/WebViewState.kt | 3 - .../webview/web/DesktopWebView.kt | 2 +- .../webview/web/WebView.desktop.kt | 8 ++- .../multiplatform/webview/web/IOSWebView.kt | 2 +- .../multiplatform/webview/web/WebView.ios.kt | 7 +- 12 files changed, 83 insertions(+), 49 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 6377a8e5..c396fbb7 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -17,10 +17,13 @@ import androidx.compose.ui.unit.dp import co.touchlab.kermit.Logger import com.kevinnzou.sample.model.GreetModel import com.multiplatform.webview.jsbridge.IJsHandler +import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.jsbridge.processParams +import com.multiplatform.webview.jsbridge.rememberWebViewJsBridge import com.multiplatform.webview.util.KLogSeverity import com.multiplatform.webview.web.WebView +import com.multiplatform.webview.web.WebViewState import com.multiplatform.webview.web.rememberWebViewNavigator import com.multiplatform.webview.web.rememberWebViewStateWithHTMLData @@ -80,37 +83,11 @@ internal fun BasicWebViewWithHTMLSample() { // fileName = "index.html", // ) val webViewState = rememberWebViewStateWithHTMLData(html) + val jsBridge = rememberWebViewJsBridge() LaunchedEffect(Unit) { - webViewState.webSettings.apply { - zoomLevel = 1.0 - isJavaScriptEnabled = true - logSeverity = KLogSeverity.Debug - allowFileAccessFromFileURLs = true - allowUniversalAccessFromFileURLs = true - androidWebSettings.apply { - isAlgorithmicDarkeningAllowed = true - safeBrowsingEnabled = true - allowFileAccess = true - } - } + initWebView(webViewState) + initJsBridge(jsBridge) } - webViewState.jsBridge.register(object : IJsHandler { - override fun methodName(): String { - return "Greet" - } - - override fun handle(message: JsMessage, callback: (Any) -> Unit) { - Logger.i { - "Greet Handler Get Message: $message" - } - val param = processParams(message) - Logger.i { - "Greet Handler Get Param: $param" - } - callback("KMM ${param.type}") - } - - }) val webViewNavigator = rememberWebViewNavigator() var jsRes by mutableStateOf("Evaluate JavaScript") MaterialTheme { @@ -120,6 +97,7 @@ internal fun BasicWebViewWithHTMLSample() { modifier = Modifier.fillMaxSize(), captureBackPresses = false, navigator = webViewNavigator, + jsBridge = jsBridge, ) Button( onClick = { @@ -145,3 +123,35 @@ internal fun BasicWebViewWithHTMLSample() { } } } + +fun initWebView(webViewState: WebViewState) { + webViewState.webSettings.apply { + zoomLevel = 1.0 + isJavaScriptEnabled = true + logSeverity = KLogSeverity.Debug + allowFileAccessFromFileURLs = true + allowUniversalAccessFromFileURLs = true + androidWebSettings.apply { + isAlgorithmicDarkeningAllowed = true + safeBrowsingEnabled = true + allowFileAccess = true + } + } +} + +fun initJsBridge(jsBridge: JsBridge) { + jsBridge.register(object : IJsHandler { + override fun methodName(): String { + return "Greet" + } + + override fun handle(message: JsMessage, callback: (Any) -> Unit) { + Logger.i { + "Greet Handler Get Message: $message" + } + val param = processParams(message) + callback("KMM ${param.type}") + } + + }) +} diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt index b3c5a6d0..eea3e239 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt @@ -18,6 +18,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView +import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.util.KLogger /** @@ -55,6 +56,7 @@ fun AccompanistWebView( modifier: Modifier = Modifier, captureBackPresses: Boolean = true, navigator: WebViewNavigator = rememberWebViewNavigator(), + jsBridge: JsBridge? = null, onCreated: (WebView) -> Unit = {}, onDispose: (WebView) -> Unit = {}, client: AccompanistWebViewClient = remember { AccompanistWebViewClient() }, @@ -90,6 +92,7 @@ fun AccompanistWebView( Modifier, captureBackPresses, navigator, + jsBridge, onCreated, onDispose, client, @@ -131,6 +134,7 @@ fun AccompanistWebView( modifier: Modifier = Modifier, captureBackPresses: Boolean = true, navigator: WebViewNavigator = rememberWebViewNavigator(), + jsBridge: JsBridge? = null, onCreated: (WebView) -> Unit = {}, onDispose: (WebView) -> Unit = {}, client: AccompanistWebViewClient = remember { AccompanistWebViewClient() }, @@ -192,9 +196,9 @@ fun AccompanistWebView( } } }.also { - val androidWebView = AndroidWebView(it, scope, state.jsBridge) + val androidWebView = AndroidWebView(it, scope, jsBridge) state.webView = androidWebView - state.jsBridge.webView = androidWebView + jsBridge?.webView = androidWebView } }, modifier = modifier, diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index 53a6510f..e7a768dc 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -18,7 +18,7 @@ import kotlinx.serialization.json.Json class AndroidWebView( private val webView: WebView, override var scope: CoroutineScope, - override var jsBridge: JsBridge, + override var jsBridge: JsBridge?, ) : IWebView { init { initWebView() @@ -111,7 +111,7 @@ class AndroidWebView( KLogger.d { "call from JS: $message" } - jsBridge.dispatch(message) + jsBridge?.dispatch(message) } @JavascriptInterface @@ -121,6 +121,6 @@ class AndroidWebView( params: String, ) { KLogger.d { "callAndroid call from JS: $id, $method, $params" } - jsBridge.dispatch(JsMessage(id, method, params)) + jsBridge?.dispatch(JsMessage(id, method, params)) } } diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt index 736d5d19..949409b0 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt @@ -2,6 +2,7 @@ package com.multiplatform.webview.web import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.multiplatform.webview.jsbridge.JsBridge /** * Android WebView implementation. @@ -12,6 +13,7 @@ actual fun ActualWebView( modifier: Modifier, captureBackPresses: Boolean, navigator: WebViewNavigator, + jsBridge: JsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -20,6 +22,7 @@ actual fun ActualWebView( modifier, captureBackPresses, navigator, + jsBridge, onCreated = { _ -> onCreated() }, onDispose = { _ -> onDispose() }, ) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt index da30d475..fd1527f1 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt @@ -1,5 +1,7 @@ package com.multiplatform.webview.jsbridge +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import com.multiplatform.webview.web.IWebView import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.resource @@ -7,7 +9,7 @@ import org.jetbrains.compose.resources.resource /** * Created By Kevin Zou On 2023/10/31 */ -class JsBridge { +open class JsBridge { private val jsDispatcher = JsDispatcher() private var initJs = "" var webView: IWebView? = null @@ -45,3 +47,7 @@ class JsBridge { webView?.evaluateJavaScript("window.JsBridge.onCallback($callbackId, '$res')") } } + +@Composable +fun rememberWebViewJsBridge(): JsBridge = + remember { JsBridge() } \ No newline at end of file diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index e906e39c..3ea1a239 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -3,7 +3,6 @@ package com.multiplatform.webview.web import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.util.KLogger import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.resource @@ -17,7 +16,7 @@ import org.jetbrains.compose.resources.resource interface IWebView { var scope: CoroutineScope - var jsBridge: JsBridge + var jsBridge: JsBridge? /** * True when the web view is able to navigate backwards, false otherwise. @@ -171,13 +170,13 @@ interface IWebView { }; if (callback) { window.JsBridge.callbacks[message.id] = callback; - console.log('add callback: ' + message.id + ', ' + JSON.stringify(window.JsBridge.callbacks)); + console.log('add callback: ' + message.id + ', ' + callback); } window.JsBridge.postMessage(JSON.stringify(message)); }, onCallback: function (callbackId, data) { var callback = window.JsBridge.callbacks[callbackId]; - console.log('onCallback: ' + callbackId + ', ' + data + ', ' + JSON.stringify(window.JsBridge.callbacks)); + console.log('onCallback: ' + callbackId + ', ' + data + ', ' + callback); if (callback) { callback(data); delete window.JsBridge.callbacks[callbackId]; @@ -191,8 +190,8 @@ interface IWebView { fun injectBridge(jsBridge: JsBridge) fun initWebView() { - scope.launch { - injectBridge(jsBridge) + jsBridge?.apply { + injectBridge(this) } } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index fc1314a5..9d9305f2 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier +import com.multiplatform.webview.jsbridge.JsBridge import org.jetbrains.compose.resources.ExperimentalResourceApi /** @@ -31,6 +32,7 @@ fun WebView( modifier: Modifier = Modifier, captureBackPresses: Boolean = true, navigator: WebViewNavigator = rememberWebViewNavigator(), + jsBridge: JsBridge? = null, onCreated: () -> Unit = {}, onDispose: () -> Unit = {}, ) { @@ -81,7 +83,7 @@ fun WebView( } LaunchedEffect(state.loadingState) { - if (state.loadingState is LoadingState.Finished) { + if (state.loadingState is LoadingState.Finished && jsBridge != null) { webView?.injectInitJS() } } @@ -91,6 +93,7 @@ fun WebView( modifier = modifier, captureBackPresses = captureBackPresses, navigator = navigator, + jsBridge = jsBridge, onCreated = onCreated, onDispose = onDispose, ) @@ -105,6 +108,7 @@ expect fun ActualWebView( modifier: Modifier = Modifier, captureBackPresses: Boolean = true, navigator: WebViewNavigator = rememberWebViewNavigator(), + jsBridge: JsBridge? = null, onCreated: () -> Unit = {}, onDispose: () -> Unit = {}, ) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt index 5720a2f0..fe2b930f 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewState.kt @@ -9,7 +9,6 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateList import com.multiplatform.webview.cookie.CookieManager import com.multiplatform.webview.cookie.WebViewCookieManager -import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.setting.WebSettings /** @@ -63,8 +62,6 @@ class WebViewState(webContent: WebContent) { */ val webSettings: WebSettings by mutableStateOf(WebSettings()) - val jsBridge: JsBridge by mutableStateOf(JsBridge()) - /** * Whether the WebView should capture back presses and navigate back. * We need access to this in the state saver. An internal DisposableEffect or AndroidView diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index eb1483b3..0c95414a 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -21,7 +21,7 @@ import org.cef.network.CefRequest class DesktopWebView( private val webView: KCEFBrowser, override var scope: CoroutineScope, - override var jsBridge: JsBridge, + override var jsBridge: JsBridge?, ) : IWebView { init { initWebView() diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt index 51ce4ac1..2ef7db38 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.awt.SwingPanel +import com.multiplatform.webview.jsbridge.JsBridge import dev.datlag.kcef.KCEF import dev.datlag.kcef.KCEFBrowser import org.cef.browser.CefRendering @@ -25,6 +26,7 @@ actual fun ActualWebView( modifier: Modifier, captureBackPresses: Boolean, navigator: WebViewNavigator, + jsBridge: JsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -32,6 +34,7 @@ actual fun ActualWebView( state, modifier, navigator, + jsBridge, onCreated = onCreated, onDispose = onDispose, ) @@ -46,6 +49,7 @@ fun DesktopWebView( state: WebViewState, modifier: Modifier, navigator: WebViewNavigator, + jsBridge: JsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -120,7 +124,9 @@ fun DesktopWebView( } } }?.also { - state.webView = DesktopWebView(it, scope, state.jsBridge) + val desktopWebView = DesktopWebView(it, scope, jsBridge) + state.webView = desktopWebView + jsBridge?.webView = desktopWebView } browser?.let { diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index 5cb35028..a95ff0c7 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -30,7 +30,7 @@ import platform.darwin.NSObjectMeta class IOSWebView( private val wkWebView: WKWebView, override var scope: CoroutineScope, - override var jsBridge: JsBridge, + override var jsBridge: JsBridge?, ) : IWebView { init { initWebView() diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt index eebf9dcb..d5aa63bd 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.interop.UIKitView +import com.multiplatform.webview.jsbridge.JsBridge import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.readValue import platform.CoreGraphics.CGRectZero @@ -22,6 +23,7 @@ actual fun ActualWebView( modifier: Modifier, captureBackPresses: Boolean, navigator: WebViewNavigator, + jsBridge: JsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -30,6 +32,7 @@ actual fun ActualWebView( modifier = modifier, captureBackPresses = captureBackPresses, navigator = navigator, + jsBridge = jsBridge, onCreated = onCreated, onDispose = onDispose, ) @@ -45,6 +48,7 @@ fun IOSWebView( modifier: Modifier, captureBackPresses: Boolean, navigator: WebViewNavigator, + jsBridge: JsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -83,8 +87,9 @@ fun IOSWebView( this.navigationDelegate = navigationDelegate onCreated() }.also { - val iosWebView = IOSWebView(it, scope, state.jsBridge) + val iosWebView = IOSWebView(it, scope, jsBridge) state.webView = iosWebView + jsBridge?.webView = iosWebView } }, modifier = modifier, From 7d8b42af45af58af940a8c0d53527704c1e217bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 6 Dec 2023 15:23:43 +0800 Subject: [PATCH 35/65] feat:Support Custom JsBridge --- .../sample/jsbridge/CustomJsBridge.kt | 12 ++++++++++ .../sample/jsbridge/GreetJsHandler.kt | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomJsBridge.kt create mode 100644 sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsHandler.kt diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomJsBridge.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomJsBridge.kt new file mode 100644 index 00000000..46491c40 --- /dev/null +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomJsBridge.kt @@ -0,0 +1,12 @@ +package com.kevinnzou.sample.jsbridge + +import com.multiplatform.webview.jsbridge.JsBridge + +/** + * Created By Kevin Zou On 2023/12/6 + */ +class CustomJsBridge : JsBridge() { + init { + register(GreetJsHandler()) + } +} \ No newline at end of file diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsHandler.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsHandler.kt new file mode 100644 index 00000000..f329bdf5 --- /dev/null +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsHandler.kt @@ -0,0 +1,24 @@ +package com.kevinnzou.sample.jsbridge + +import co.touchlab.kermit.Logger +import com.kevinnzou.sample.model.GreetModel +import com.multiplatform.webview.jsbridge.IJsHandler +import com.multiplatform.webview.jsbridge.JsMessage +import com.multiplatform.webview.jsbridge.processParams + +/** + * Created By Kevin Zou On 2023/12/6 + */ +class GreetJsHandler : IJsHandler { + override fun methodName(): String { + return "Greet" + } + + override fun handle(message: JsMessage, callback: (Any) -> Unit) { + Logger.i { + "Greet Handler Get Message: $message" + } + val param = processParams(message) + callback("KMM ${param.type}") + } +} \ No newline at end of file From 9011ee81812542bfa9fd6ac8ab80d6ee58a5df2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 6 Dec 2023 16:28:05 +0800 Subject: [PATCH 36/65] feat:Work Around for Desktop not loading issue --- .../com/kevinnzou/sample/HtmlWebViewSample.kt | 7 ++++++- .../webview/web/AndroidWebView.kt | 2 +- .../com/multiplatform/webview/util/KLogger.kt | 2 +- .../com/multiplatform/webview/web/IWebView.kt | 4 ++-- .../com/multiplatform/webview/web/WebView.kt | 19 ++++++++++++++++--- .../webview/web/DesktopWebView.kt | 2 +- .../multiplatform/webview/web/WebEngineExt.kt | 1 + .../multiplatform/webview/web/IOSWebView.kt | 2 +- 8 files changed, 29 insertions(+), 10 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index c396fbb7..4a7b09e2 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -70,7 +70,12 @@ internal fun BasicWebViewWithHTMLSample() { }); } function callNative() { - window.JsBridge.callNative("Greet",JSON.stringify({type: "1"})); + window.JsBridge.callNative("Greet",JSON.stringify({type: "1"}), + function (data) { + document.getElementById("subtitle").innerText = data; + console.log("Greet from Native: " + data); + } + ); }

Compose WebView Multiplatform

diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index e7a768dc..41ef66c3 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -88,7 +88,7 @@ class AndroidWebView( } } - override suspend fun injectInitJS() { + override fun injectInitJS() { super.injectInitJS() val callAndroid = """ window.JsBridge.postMessage = function (message) { diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt index b0b1462b..2daed0a7 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt @@ -23,7 +23,7 @@ internal object KLogger : Logger( // For iOS, it will not print out the log if the severity is upper than Debug in AS. fun info(msg: () -> String) { - d { msg() } + i { msg() } } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index 3ea1a239..b1ed6750 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -153,8 +153,8 @@ interface IWebView { callback: ((String) -> Unit)? = null, ) - @OptIn(ExperimentalResourceApi::class) - suspend fun injectInitJS() { + fun injectInitJS() { + if (jsBridge == null) return KLogger.d { "IWebView injectInitJS" } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index 9d9305f2..7cefa287 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -2,9 +2,15 @@ package com.multiplatform.webview.web import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.util.Platform +import com.multiplatform.webview.util.getPlatform import org.jetbrains.compose.resources.ExperimentalResourceApi /** @@ -37,6 +43,9 @@ fun WebView( onDispose: () -> Unit = {}, ) { val webView = state.webView + var isJsBridgeInjected by remember { + mutableStateOf(false) + } webView?.let { wv -> LaunchedEffect(wv, navigator) { @@ -82,9 +91,13 @@ fun WebView( } } - LaunchedEffect(state.loadingState) { - if (state.loadingState is LoadingState.Finished && jsBridge != null) { - webView?.injectInitJS() + // TODO WorkAround for Desktop not working issue. + if (jsBridge != null && !isJsBridgeInjected && getPlatform() != Platform.Desktop) { + LaunchedEffect(state.loadingState, jsBridge) { + if (state.loadingState is LoadingState.Finished && !isJsBridgeInjected) { + webView?.injectInitJS() + isJsBridgeInjected = true + } } } diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index 0c95414a..4a06eb76 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -107,7 +107,7 @@ class DesktopWebView( } } - override suspend fun injectInitJS() { + override fun injectInitJS() { super.injectInitJS() KLogger.d { "DesktopWebView injectInitJS" diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt index 9d9581d5..bc8e9221 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt @@ -120,6 +120,7 @@ internal fun CefBrowser.addLoadListener( navigator.canGoBack = canGoBack() navigator.canGoBack = canGoForward() state.lastLoadedUrl = getCurrentUrl() + state.webView?.injectInitJS() } override fun onLoadError( diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index a95ff0c7..92d2ab3f 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -137,7 +137,7 @@ class IOSWebView( } } - override suspend fun injectInitJS() { + override fun injectInitJS() { super.injectInitJS() val callIOS = """ window.JsBridge.postMessage = function (message) { From e0bb01a0e520f1d4df5fa7febf9d2618d062d75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 7 Dec 2023 08:29:25 +0800 Subject: [PATCH 37/65] feat:Rename id to callbackId --- .../kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt | 2 +- .../kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt | 2 +- .../kotlin/com/multiplatform/webview/web/IWebView.kt | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt index fd1527f1..afa766e4 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt @@ -34,7 +34,7 @@ open class JsBridge { fun dispatch(message: JsMessage) { jsDispatcher.dispatch(message) { - onCallback(it, message.id) + onCallback(it, message.callbackId) } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt index 11003f87..93679f11 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessage.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable */ @Serializable data class JsMessage( - val id: Int, + val callbackId: Int, val methodName: String, val params: String, ) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index b1ed6750..b9e2e2d5 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -166,11 +166,11 @@ interface IWebView { var message = { methodName: methodName, params: params, - id: callback ? window.JsBridge.callbackId++ : -1 + callbackId: callback ? window.JsBridge.callbackId++ : -1 }; if (callback) { - window.JsBridge.callbacks[message.id] = callback; - console.log('add callback: ' + message.id + ', ' + callback); + window.JsBridge.callbacks[message.callbackId] = callback; + console.log('add callback: ' + message.callbackId + ', ' + callback); } window.JsBridge.postMessage(JSON.stringify(message)); }, From 4ab1dff186cb2ed8fd7d286bb3e07f4d2e81b1c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 7 Dec 2023 10:01:50 +0800 Subject: [PATCH 38/65] feat:Rename injectJsBridge --- .../com/multiplatform/webview/web/AndroidWebView.kt | 2 +- .../com/multiplatform/webview/jsbridge/JsBridge.kt | 12 ------------ .../kotlin/com/multiplatform/webview/web/IWebView.kt | 4 ++-- .../com/multiplatform/webview/web/DesktopWebView.kt | 7 ++----- .../com/multiplatform/webview/web/IOSWebView.kt | 2 +- 5 files changed, 6 insertions(+), 21 deletions(-) diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index 41ef66c3..244437c4 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -98,7 +98,7 @@ class AndroidWebView( evaluateJavaScript(callAndroid) } - override fun injectBridge(jsBridge: JsBridge) { + override fun injectJsBridge(jsBridge: JsBridge) { webView.addJavascriptInterface(this, "jsBridge") } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt index afa766e4..828e5014 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt @@ -3,8 +3,6 @@ package com.multiplatform.webview.jsbridge import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import com.multiplatform.webview.web.IWebView -import org.jetbrains.compose.resources.ExperimentalResourceApi -import org.jetbrains.compose.resources.resource /** * Created By Kevin Zou On 2023/10/31 @@ -22,16 +20,6 @@ open class JsBridge { jsDispatcher.unregisterJSHandler(handler) } - @OptIn(ExperimentalResourceApi::class) - private suspend fun injectInitJS() { - if (initJs.isEmpty()) - { - val res = resource("jsbridge.js") - initJs = res.readBytes().decodeToString() - } - webView?.evaluateJavaScript(initJs) - } - fun dispatch(message: JsMessage) { jsDispatcher.dispatch(message) { onCallback(it, message.callbackId) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index b9e2e2d5..9ecb635d 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -187,11 +187,11 @@ interface IWebView { evaluateJavaScript(initJs) } - fun injectBridge(jsBridge: JsBridge) + fun injectJsBridge(jsBridge: JsBridge) fun initWebView() { jsBridge?.apply { - injectBridge(this) + injectJsBridge(this) } } } diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index 4a06eb76..f1211f67 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -120,7 +120,7 @@ class DesktopWebView( evaluateJavaScript(callDesktop) } - override fun injectBridge(jsBridge: JsBridge) { + override fun injectJsBridge(jsBridge: JsBridge) { val router = CefMessageRouter.create() val handler = object : CefMessageRouterHandlerAdapter() { override fun onQuery( @@ -139,15 +139,12 @@ class DesktopWebView( persistent, callback ) - KLogger.d { - "onQuery: $request" - } val message = Json.decodeFromString(request) KLogger.d { "onQuery Message: $message" } jsBridge.dispatch(message) - return super.onQuery(browser, frame, queryId, request, persistent, callback) + return true } } router.addHandler(handler, false) diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index 92d2ab3f..11358ffd 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -147,7 +147,7 @@ class IOSWebView( evaluateJavaScript(callIOS) } - override fun injectBridge(jsBridge: JsBridge) { + override fun injectJsBridge(jsBridge: JsBridge) { KLogger.info { "injectBridge" } val jsMessageHandler = WKJsMessageHandler(jsBridge) wkWebView.configuration.userContentController.apply { From bfd17cd007644edaf26b0572d25ea05e89b279dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 7 Dec 2023 10:07:09 +0800 Subject: [PATCH 39/65] feat:Rename WebViewJsBridge --- .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 8 ++++---- .../{CustomJsBridge.kt => CustomWebViewJsBridge.kt} | 4 ++-- .../multiplatform/webview/web/AccompanistWebView.kt | 12 ++++++------ .../com/multiplatform/webview/web/AndroidWebView.kt | 10 +++++----- .../com/multiplatform/webview/web/WebView.android.kt | 6 +++--- .../jsbridge/{JsBridge.kt => WebViewJsBridge.kt} | 6 +++--- .../kotlin/com/multiplatform/webview/web/IWebView.kt | 10 +++++----- .../kotlin/com/multiplatform/webview/web/WebView.kt | 12 ++++++------ .../com/multiplatform/webview/web/DesktopWebView.kt | 8 ++++---- .../com/multiplatform/webview/web/WebView.desktop.kt | 12 ++++++------ .../webview/jsbridge/WKJsMessageHandler.kt | 5 +++-- .../com/multiplatform/webview/web/IOSWebView.kt | 8 ++++---- .../com/multiplatform/webview/web/WebView.ios.kt | 12 ++++++------ 13 files changed, 57 insertions(+), 56 deletions(-) rename sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/{CustomJsBridge.kt => CustomWebViewJsBridge.kt} (56%) rename webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/{JsBridge.kt => WebViewJsBridge.kt} (89%) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 4a7b09e2..6ec6fc8a 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -17,8 +17,8 @@ import androidx.compose.ui.unit.dp import co.touchlab.kermit.Logger import com.kevinnzou.sample.model.GreetModel import com.multiplatform.webview.jsbridge.IJsHandler -import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.jsbridge.JsMessage +import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.jsbridge.processParams import com.multiplatform.webview.jsbridge.rememberWebViewJsBridge import com.multiplatform.webview.util.KLogSeverity @@ -102,7 +102,7 @@ internal fun BasicWebViewWithHTMLSample() { modifier = Modifier.fillMaxSize(), captureBackPresses = false, navigator = webViewNavigator, - jsBridge = jsBridge, + webViewJsBridge = jsBridge, ) Button( onClick = { @@ -144,8 +144,8 @@ fun initWebView(webViewState: WebViewState) { } } -fun initJsBridge(jsBridge: JsBridge) { - jsBridge.register(object : IJsHandler { +fun initJsBridge(webViewJsBridge: WebViewJsBridge) { + webViewJsBridge.register(object : IJsHandler { override fun methodName(): String { return "Greet" } diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomJsBridge.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt similarity index 56% rename from sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomJsBridge.kt rename to sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt index 46491c40..4c80301b 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomJsBridge.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt @@ -1,11 +1,11 @@ package com.kevinnzou.sample.jsbridge -import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.WebViewJsBridge /** * Created By Kevin Zou On 2023/12/6 */ -class CustomJsBridge : JsBridge() { +class CustomWebViewJsBridge : WebViewJsBridge() { init { register(GreetJsHandler()) } diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt index eea3e239..993050a6 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt @@ -18,7 +18,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView -import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.util.KLogger /** @@ -56,7 +56,7 @@ fun AccompanistWebView( modifier: Modifier = Modifier, captureBackPresses: Boolean = true, navigator: WebViewNavigator = rememberWebViewNavigator(), - jsBridge: JsBridge? = null, + webViewJsBridge: WebViewJsBridge? = null, onCreated: (WebView) -> Unit = {}, onDispose: (WebView) -> Unit = {}, client: AccompanistWebViewClient = remember { AccompanistWebViewClient() }, @@ -92,7 +92,7 @@ fun AccompanistWebView( Modifier, captureBackPresses, navigator, - jsBridge, + webViewJsBridge, onCreated, onDispose, client, @@ -134,7 +134,7 @@ fun AccompanistWebView( modifier: Modifier = Modifier, captureBackPresses: Boolean = true, navigator: WebViewNavigator = rememberWebViewNavigator(), - jsBridge: JsBridge? = null, + webViewJsBridge: WebViewJsBridge? = null, onCreated: (WebView) -> Unit = {}, onDispose: (WebView) -> Unit = {}, client: AccompanistWebViewClient = remember { AccompanistWebViewClient() }, @@ -196,9 +196,9 @@ fun AccompanistWebView( } } }.also { - val androidWebView = AndroidWebView(it, scope, jsBridge) + val androidWebView = AndroidWebView(it, scope, webViewJsBridge) state.webView = androidWebView - jsBridge?.webView = androidWebView + webViewJsBridge?.webView = androidWebView } }, modifier = modifier, diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index 244437c4..7034a32f 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -2,8 +2,8 @@ package com.multiplatform.webview.web import android.webkit.JavascriptInterface import android.webkit.WebView -import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.jsbridge.JsMessage +import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.util.KLogger import kotlinx.coroutines.CoroutineScope import kotlinx.serialization.json.Json @@ -18,7 +18,7 @@ import kotlinx.serialization.json.Json class AndroidWebView( private val webView: WebView, override var scope: CoroutineScope, - override var jsBridge: JsBridge?, + override var webViewJsBridge: WebViewJsBridge?, ) : IWebView { init { initWebView() @@ -98,7 +98,7 @@ class AndroidWebView( evaluateJavaScript(callAndroid) } - override fun injectJsBridge(jsBridge: JsBridge) { + override fun injectJsBridge(webViewJsBridge: WebViewJsBridge) { webView.addJavascriptInterface(this, "jsBridge") } @@ -111,7 +111,7 @@ class AndroidWebView( KLogger.d { "call from JS: $message" } - jsBridge?.dispatch(message) + webViewJsBridge?.dispatch(message) } @JavascriptInterface @@ -121,6 +121,6 @@ class AndroidWebView( params: String, ) { KLogger.d { "callAndroid call from JS: $id, $method, $params" } - jsBridge?.dispatch(JsMessage(id, method, params)) + webViewJsBridge?.dispatch(JsMessage(id, method, params)) } } diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt index 949409b0..00ffbde4 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt @@ -2,7 +2,7 @@ package com.multiplatform.webview.web import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.WebViewJsBridge /** * Android WebView implementation. @@ -13,7 +13,7 @@ actual fun ActualWebView( modifier: Modifier, captureBackPresses: Boolean, navigator: WebViewNavigator, - jsBridge: JsBridge?, + webViewJsBridge: WebViewJsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -22,7 +22,7 @@ actual fun ActualWebView( modifier, captureBackPresses, navigator, - jsBridge, + webViewJsBridge, onCreated = { _ -> onCreated() }, onDispose = { _ -> onDispose() }, ) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt similarity index 89% rename from webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt rename to webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt index 828e5014..051d2233 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt @@ -7,7 +7,7 @@ import com.multiplatform.webview.web.IWebView /** * Created By Kevin Zou On 2023/10/31 */ -open class JsBridge { +open class WebViewJsBridge { private val jsDispatcher = JsDispatcher() private var initJs = "" var webView: IWebView? = null @@ -37,5 +37,5 @@ open class JsBridge { } @Composable -fun rememberWebViewJsBridge(): JsBridge = - remember { JsBridge() } \ No newline at end of file +fun rememberWebViewJsBridge(): WebViewJsBridge = + remember { WebViewJsBridge() } \ No newline at end of file diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index 9ecb635d..13341fc6 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -1,6 +1,6 @@ package com.multiplatform.webview.web -import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.util.KLogger import kotlinx.coroutines.CoroutineScope import org.jetbrains.compose.resources.ExperimentalResourceApi @@ -16,7 +16,7 @@ import org.jetbrains.compose.resources.resource interface IWebView { var scope: CoroutineScope - var jsBridge: JsBridge? + var webViewJsBridge: WebViewJsBridge? /** * True when the web view is able to navigate backwards, false otherwise. @@ -154,7 +154,7 @@ interface IWebView { ) fun injectInitJS() { - if (jsBridge == null) return + if (webViewJsBridge == null) return KLogger.d { "IWebView injectInitJS" } @@ -187,10 +187,10 @@ interface IWebView { evaluateJavaScript(initJs) } - fun injectJsBridge(jsBridge: JsBridge) + fun injectJsBridge(webViewJsBridge: WebViewJsBridge) fun initWebView() { - jsBridge?.apply { + webViewJsBridge?.apply { injectJsBridge(this) } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index 7cefa287..8e536f0a 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -8,7 +8,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier -import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.util.Platform import com.multiplatform.webview.util.getPlatform import org.jetbrains.compose.resources.ExperimentalResourceApi @@ -38,7 +38,7 @@ fun WebView( modifier: Modifier = Modifier, captureBackPresses: Boolean = true, navigator: WebViewNavigator = rememberWebViewNavigator(), - jsBridge: JsBridge? = null, + webViewJsBridge: WebViewJsBridge? = null, onCreated: () -> Unit = {}, onDispose: () -> Unit = {}, ) { @@ -92,8 +92,8 @@ fun WebView( } // TODO WorkAround for Desktop not working issue. - if (jsBridge != null && !isJsBridgeInjected && getPlatform() != Platform.Desktop) { - LaunchedEffect(state.loadingState, jsBridge) { + if (webViewJsBridge != null && !isJsBridgeInjected && getPlatform() != Platform.Desktop) { + LaunchedEffect(state.loadingState, webViewJsBridge) { if (state.loadingState is LoadingState.Finished && !isJsBridgeInjected) { webView?.injectInitJS() isJsBridgeInjected = true @@ -106,7 +106,7 @@ fun WebView( modifier = modifier, captureBackPresses = captureBackPresses, navigator = navigator, - jsBridge = jsBridge, + webViewJsBridge = webViewJsBridge, onCreated = onCreated, onDispose = onDispose, ) @@ -121,7 +121,7 @@ expect fun ActualWebView( modifier: Modifier = Modifier, captureBackPresses: Boolean = true, navigator: WebViewNavigator = rememberWebViewNavigator(), - jsBridge: JsBridge? = null, + webViewJsBridge: WebViewJsBridge? = null, onCreated: () -> Unit = {}, onDispose: () -> Unit = {}, ) diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index f1211f67..064985e8 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -1,7 +1,7 @@ package com.multiplatform.webview.web -import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.jsbridge.JsMessage +import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.util.KLogger import dev.datlag.kcef.KCEFBrowser import kotlinx.coroutines.CoroutineScope @@ -21,7 +21,7 @@ import org.cef.network.CefRequest class DesktopWebView( private val webView: KCEFBrowser, override var scope: CoroutineScope, - override var jsBridge: JsBridge?, + override var webViewJsBridge: WebViewJsBridge?, ) : IWebView { init { initWebView() @@ -120,7 +120,7 @@ class DesktopWebView( evaluateJavaScript(callDesktop) } - override fun injectJsBridge(jsBridge: JsBridge) { + override fun injectJsBridge(webViewJsBridge: WebViewJsBridge) { val router = CefMessageRouter.create() val handler = object : CefMessageRouterHandlerAdapter() { override fun onQuery( @@ -143,7 +143,7 @@ class DesktopWebView( KLogger.d { "onQuery Message: $message" } - jsBridge.dispatch(message) + webViewJsBridge.dispatch(message) return true } } diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt index 2ef7db38..366d9db9 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebView.desktop.kt @@ -10,7 +10,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.awt.SwingPanel -import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.WebViewJsBridge import dev.datlag.kcef.KCEF import dev.datlag.kcef.KCEFBrowser import org.cef.browser.CefRendering @@ -26,7 +26,7 @@ actual fun ActualWebView( modifier: Modifier, captureBackPresses: Boolean, navigator: WebViewNavigator, - jsBridge: JsBridge?, + webViewJsBridge: WebViewJsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -34,7 +34,7 @@ actual fun ActualWebView( state, modifier, navigator, - jsBridge, + webViewJsBridge, onCreated = onCreated, onDispose = onDispose, ) @@ -49,7 +49,7 @@ fun DesktopWebView( state: WebViewState, modifier: Modifier, navigator: WebViewNavigator, - jsBridge: JsBridge?, + webViewJsBridge: WebViewJsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -124,9 +124,9 @@ fun DesktopWebView( } } }?.also { - val desktopWebView = DesktopWebView(it, scope, jsBridge) + val desktopWebView = DesktopWebView(it, scope, webViewJsBridge) state.webView = desktopWebView - jsBridge?.webView = desktopWebView + webViewJsBridge?.webView = desktopWebView } browser?.let { diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt index a6f182fd..7d0cdfe8 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt @@ -10,7 +10,8 @@ import platform.darwin.NSObject /** * Created By Kevin Zou On 2023/11/1 */ -class WKJsMessageHandler(private val jsBridge: JsBridge) : WKScriptMessageHandlerProtocol, +class WKJsMessageHandler(private val webViewJsBridge: WebViewJsBridge) : + WKScriptMessageHandlerProtocol, NSObject() { override fun userContentController( userContentController: WKUserContentController, @@ -24,7 +25,7 @@ class WKJsMessageHandler(private val jsBridge: JsBridge) : WKScriptMessageHandle KLogger.info { "WKJsMessageHandler: $message" } - jsBridge.dispatch(message) + webViewJsBridge.dispatch(message) } } } \ No newline at end of file diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index 11358ffd..a71501a8 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -1,7 +1,7 @@ package com.multiplatform.webview.web -import com.multiplatform.webview.jsbridge.JsBridge import com.multiplatform.webview.jsbridge.WKJsMessageHandler +import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.util.KLogger import kotlinx.cinterop.BetaInteropApi import kotlinx.cinterop.ExperimentalForeignApi @@ -30,7 +30,7 @@ import platform.darwin.NSObjectMeta class IOSWebView( private val wkWebView: WKWebView, override var scope: CoroutineScope, - override var jsBridge: JsBridge?, + override var webViewJsBridge: WebViewJsBridge?, ) : IWebView { init { initWebView() @@ -147,9 +147,9 @@ class IOSWebView( evaluateJavaScript(callIOS) } - override fun injectJsBridge(jsBridge: JsBridge) { + override fun injectJsBridge(webViewJsBridge: WebViewJsBridge) { KLogger.info { "injectBridge" } - val jsMessageHandler = WKJsMessageHandler(jsBridge) + val jsMessageHandler = WKJsMessageHandler(webViewJsBridge) wkWebView.configuration.userContentController.apply { addScriptMessageHandler(jsMessageHandler, "jsBridge") } diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt index d5aa63bd..1bc88840 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WebView.ios.kt @@ -5,7 +5,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.interop.UIKitView -import com.multiplatform.webview.jsbridge.JsBridge +import com.multiplatform.webview.jsbridge.WebViewJsBridge import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.readValue import platform.CoreGraphics.CGRectZero @@ -23,7 +23,7 @@ actual fun ActualWebView( modifier: Modifier, captureBackPresses: Boolean, navigator: WebViewNavigator, - jsBridge: JsBridge?, + webViewJsBridge: WebViewJsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -32,7 +32,7 @@ actual fun ActualWebView( modifier = modifier, captureBackPresses = captureBackPresses, navigator = navigator, - jsBridge = jsBridge, + webViewJsBridge = webViewJsBridge, onCreated = onCreated, onDispose = onDispose, ) @@ -48,7 +48,7 @@ fun IOSWebView( modifier: Modifier, captureBackPresses: Boolean, navigator: WebViewNavigator, - jsBridge: JsBridge?, + webViewJsBridge: WebViewJsBridge?, onCreated: () -> Unit, onDispose: () -> Unit, ) { @@ -87,9 +87,9 @@ fun IOSWebView( this.navigationDelegate = navigationDelegate onCreated() }.also { - val iosWebView = IOSWebView(it, scope, jsBridge) + val iosWebView = IOSWebView(it, scope, webViewJsBridge) state.webView = iosWebView - jsBridge?.webView = iosWebView + webViewJsBridge?.webView = iosWebView } }, modifier = modifier, From 0c9d877c75b491a9cb89e03be98bee6d2b043f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 7 Dec 2023 10:13:48 +0800 Subject: [PATCH 40/65] feat:Rename IJsMessageHandler & JsMessageDispatcher --- .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 4 ++-- .../sample/jsbridge/CustomWebViewJsBridge.kt | 2 +- .../{GreetJsHandler.kt => GreetJsMessageHandler.kt} | 4 ++-- .../jsbridge/{IJsHandler.kt => IJsMessageHandler.kt} | 4 ++-- .../{JsDispatcher.kt => JsMessageDispatcher.kt} | 8 ++++---- .../webview/jsbridge/WebViewJsBridge.kt | 12 ++++++------ 6 files changed, 17 insertions(+), 17 deletions(-) rename sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/{GreetJsHandler.kt => GreetJsMessageHandler.kt} (84%) rename webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/{IJsHandler.kt => IJsMessageHandler.kt} (75%) rename webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/{JsDispatcher.kt => JsMessageDispatcher.kt} (66%) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 6ec6fc8a..f7dfa22c 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import co.touchlab.kermit.Logger import com.kevinnzou.sample.model.GreetModel -import com.multiplatform.webview.jsbridge.IJsHandler +import com.multiplatform.webview.jsbridge.IJsMessageHandler import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.jsbridge.processParams @@ -145,7 +145,7 @@ fun initWebView(webViewState: WebViewState) { } fun initJsBridge(webViewJsBridge: WebViewJsBridge) { - webViewJsBridge.register(object : IJsHandler { + webViewJsBridge.register(object : IJsMessageHandler { override fun methodName(): String { return "Greet" } diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt index 4c80301b..6b4836ae 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt @@ -7,6 +7,6 @@ import com.multiplatform.webview.jsbridge.WebViewJsBridge */ class CustomWebViewJsBridge : WebViewJsBridge() { init { - register(GreetJsHandler()) + register(GreetJsMessageHandler()) } } \ No newline at end of file diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsHandler.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt similarity index 84% rename from sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsHandler.kt rename to sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt index f329bdf5..33b01e3a 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsHandler.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt @@ -2,14 +2,14 @@ package com.kevinnzou.sample.jsbridge import co.touchlab.kermit.Logger import com.kevinnzou.sample.model.GreetModel -import com.multiplatform.webview.jsbridge.IJsHandler +import com.multiplatform.webview.jsbridge.IJsMessageHandler import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.jsbridge.processParams /** * Created By Kevin Zou On 2023/12/6 */ -class GreetJsHandler : IJsHandler { +class GreetJsMessageHandler : IJsMessageHandler { override fun methodName(): String { return "Greet" } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt similarity index 75% rename from webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt rename to webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt index 8c60ef7d..5e62f2cc 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsHandler.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt @@ -5,7 +5,7 @@ import kotlinx.serialization.json.Json /** * Created By Kevin Zou On 2023/10/31 */ -interface IJsHandler { +interface IJsMessageHandler { fun methodName(): String fun canHandle(methodName: String) = methodName() == methodName @@ -17,6 +17,6 @@ interface IJsHandler { } -inline fun IJsHandler.processParams(message: JsMessage): T { +inline fun IJsMessageHandler.processParams(message: JsMessage): T { return Json.decodeFromString(message.params) } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt similarity index 66% rename from webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt rename to webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt index d51964df..e7b78b6d 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsDispatcher.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt @@ -3,10 +3,10 @@ package com.multiplatform.webview.jsbridge /** * Created By Kevin Zou On 2023/10/31 */ -class JsDispatcher { - private val jsHandlerMap = mutableMapOf() +class JsMessageDispatcher { + private val jsHandlerMap = mutableMapOf() - fun registerJSHandler(handler: IJsHandler) { + fun registerJSHandler(handler: IJsMessageHandler) { jsHandlerMap[handler.methodName()] = handler } @@ -19,7 +19,7 @@ class JsDispatcher { fun canHandle(id: String) = jsHandlerMap.containsKey(id) - fun unregisterJSHandler(handler: IJsHandler) { + fun unregisterJSHandler(handler: IJsMessageHandler) { jsHandlerMap.remove(handler.methodName()) } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt index 051d2233..79e4bf71 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt @@ -8,20 +8,20 @@ import com.multiplatform.webview.web.IWebView * Created By Kevin Zou On 2023/10/31 */ open class WebViewJsBridge { - private val jsDispatcher = JsDispatcher() + private val jsMessageDispatcher = JsMessageDispatcher() private var initJs = "" var webView: IWebView? = null - fun register(handler: IJsHandler) { - jsDispatcher.registerJSHandler(handler) + fun register(handler: IJsMessageHandler) { + jsMessageDispatcher.registerJSHandler(handler) } - fun unregister(handler: IJsHandler) { - jsDispatcher.unregisterJSHandler(handler) + fun unregister(handler: IJsMessageHandler) { + jsMessageDispatcher.unregisterJSHandler(handler) } fun dispatch(message: JsMessage) { - jsDispatcher.dispatch(message) { + jsMessageDispatcher.dispatch(message) { onCallback(it, message.callbackId) } } From 71b9698887003f69bdc4f9d5fa76eb0102813793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 7 Dec 2023 10:22:53 +0800 Subject: [PATCH 41/65] feat:clear jsHandlerMap when dispose --- .../multiplatform/webview/jsbridge/JsMessageDispatcher.kt | 4 ++++ .../com/multiplatform/webview/jsbridge/WebViewJsBridge.kt | 5 ++++- .../kotlin/com/multiplatform/webview/web/WebView.kt | 5 ++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt index e7b78b6d..75030745 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt @@ -22,4 +22,8 @@ class JsMessageDispatcher { fun unregisterJSHandler(handler: IJsMessageHandler) { jsHandlerMap.remove(handler.methodName()) } + + fun clear() { + jsHandlerMap.clear() + } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt index 79e4bf71..df2ce441 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt @@ -9,7 +9,6 @@ import com.multiplatform.webview.web.IWebView */ open class WebViewJsBridge { private val jsMessageDispatcher = JsMessageDispatcher() - private var initJs = "" var webView: IWebView? = null fun register(handler: IJsMessageHandler) { @@ -20,6 +19,10 @@ open class WebViewJsBridge { jsMessageDispatcher.unregisterJSHandler(handler) } + fun clear() { + jsMessageDispatcher.clear() + } + fun dispatch(message: JsMessage) { jsMessageDispatcher.dispatch(message) { onCallback(it, message.callbackId) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index 8e536f0a..63542904 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -108,7 +108,10 @@ fun WebView( navigator = navigator, webViewJsBridge = webViewJsBridge, onCreated = onCreated, - onDispose = onDispose, + onDispose = { + webViewJsBridge?.clear() + onDispose() + }, ) } From fef8abce0945f64f9a1aa4ab81635a3c6a2ba615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 7 Dec 2023 10:38:36 +0800 Subject: [PATCH 42/65] feat:Encode result to json string --- .../com/kevinnzou/sample/HtmlWebViewSample.kt | 27 ++++--------------- .../sample/jsbridge/GreetJsMessageHandler.kt | 6 +++-- .../com/kevinnzou/sample/model/GreetModel.kt | 2 +- .../webview/jsbridge/IJsMessageHandler.kt | 7 ++++- .../webview/jsbridge/JsMessageDispatcher.kt | 2 +- .../webview/jsbridge/WebViewJsBridge.kt | 6 ++--- 6 files changed, 19 insertions(+), 31 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index f7dfa22c..59e4a4bd 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -14,12 +14,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import co.touchlab.kermit.Logger -import com.kevinnzou.sample.model.GreetModel -import com.multiplatform.webview.jsbridge.IJsMessageHandler -import com.multiplatform.webview.jsbridge.JsMessage +import com.kevinnzou.sample.jsbridge.GreetJsMessageHandler import com.multiplatform.webview.jsbridge.WebViewJsBridge -import com.multiplatform.webview.jsbridge.processParams import com.multiplatform.webview.jsbridge.rememberWebViewJsBridge import com.multiplatform.webview.util.KLogSeverity import com.multiplatform.webview.web.WebView @@ -60,7 +56,7 @@ internal fun BasicWebViewWithHTMLSample() { } function callDesktop() { window.cefQuery({ - request: "1_callDesktop_{\"type\":\"1\"}", + request: "1_callDesktop_{\"message\":\"1\"}", onSuccess: function(response) { // 处理Java应用程序的响应 }, @@ -70,7 +66,7 @@ internal fun BasicWebViewWithHTMLSample() { }); } function callNative() { - window.JsBridge.callNative("Greet",JSON.stringify({type: "1"}), + window.JsBridge.callNative("Greet",JSON.stringify({message: "1"}), function (data) { document.getElementById("subtitle").innerText = data; console.log("Greet from Native: " + data); @@ -109,7 +105,7 @@ internal fun BasicWebViewWithHTMLSample() { webViewNavigator.evaluateJavaScript( """ document.getElementById("subtitle").innerText = "Hello from KMM!"; - window.JsBridge.callNative("Greet",JSON.stringify({type: "1"}), + window.JsBridge.callNative("Greet",JSON.stringify({message: "1"}), function (data) { document.getElementById("subtitle").innerText = data; console.log("Greet from Native: " + data); @@ -145,18 +141,5 @@ fun initWebView(webViewState: WebViewState) { } fun initJsBridge(webViewJsBridge: WebViewJsBridge) { - webViewJsBridge.register(object : IJsMessageHandler { - override fun methodName(): String { - return "Greet" - } - - override fun handle(message: JsMessage, callback: (Any) -> Unit) { - Logger.i { - "Greet Handler Get Message: $message" - } - val param = processParams(message) - callback("KMM ${param.type}") - } - - }) + webViewJsBridge.register(GreetJsMessageHandler()) } diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt index 33b01e3a..85e26118 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt @@ -4,6 +4,7 @@ import co.touchlab.kermit.Logger import com.kevinnzou.sample.model.GreetModel import com.multiplatform.webview.jsbridge.IJsMessageHandler import com.multiplatform.webview.jsbridge.JsMessage +import com.multiplatform.webview.jsbridge.dataToJsonString import com.multiplatform.webview.jsbridge.processParams /** @@ -14,11 +15,12 @@ class GreetJsMessageHandler : IJsMessageHandler { return "Greet" } - override fun handle(message: JsMessage, callback: (Any) -> Unit) { + override fun handle(message: JsMessage, callback: (String) -> Unit) { Logger.i { "Greet Handler Get Message: $message" } val param = processParams(message) - callback("KMM ${param.type}") + val data = GreetModel("KMM ${param.message}") + callback(dataToJsonString(data)) } } \ No newline at end of file diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/model/GreetModel.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/model/GreetModel.kt index 7ca63e51..affad370 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/model/GreetModel.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/model/GreetModel.kt @@ -6,4 +6,4 @@ import kotlinx.serialization.Serializable * Created By Kevin Zou On 2023/12/6 */ @Serializable -data class GreetModel(val type: String) +data class GreetModel(val message: String) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt index 5e62f2cc..cf3380e6 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt @@ -1,5 +1,6 @@ package com.multiplatform.webview.jsbridge +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json /** @@ -12,7 +13,7 @@ interface IJsMessageHandler { fun handle( message: JsMessage, - callback: (Any) -> Unit, + callback: (String) -> Unit, ) } @@ -20,3 +21,7 @@ interface IJsMessageHandler { inline fun IJsMessageHandler.processParams(message: JsMessage): T { return Json.decodeFromString(message.params) } + +inline fun IJsMessageHandler.dataToJsonString(res: T): String { + return Json.encodeToString(res) +} diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt index 75030745..22bfb458 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt @@ -12,7 +12,7 @@ class JsMessageDispatcher { fun dispatch( message: JsMessage, - callback: (Any) -> Unit, + callback: (String) -> Unit, ) { jsHandlerMap[message.methodName]?.handle(message, callback) } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt index df2ce441..a10f4fcf 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt @@ -30,12 +30,10 @@ open class WebViewJsBridge { } private fun onCallback( - data: Any, + data: String, callbackId: Int, ) { -// val res = Json.encodeToString(data) - val res = data.toString() - webView?.evaluateJavaScript("window.JsBridge.onCallback($callbackId, '$res')") + webView?.evaluateJavaScript("window.JsBridge.onCallback($callbackId, '$data')") } } From 65756dd1135b422bcfcbae9ff135d042dc2c4e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Sun, 10 Dec 2023 09:27:27 +0800 Subject: [PATCH 43/65] feat:Rename platform JsBridge --- sample/shared/src/commonMain/resources/assets/script.js | 4 ++-- .../kotlin/com/multiplatform/webview/web/AndroidWebView.kt | 4 ++-- .../kotlin/com/multiplatform/webview/web/IOSWebView.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sample/shared/src/commonMain/resources/assets/script.js b/sample/shared/src/commonMain/resources/assets/script.js index e0883414..fcca6768 100644 --- a/sample/shared/src/commonMain/resources/assets/script.js +++ b/sample/shared/src/commonMain/resources/assets/script.js @@ -3,11 +3,11 @@ function callJS() { } function callAndroid() { - window.jsBridge.call('1', 'callAndroid', '{"name":"callAndroid"}'); + window.androidJsBridge.call('1', 'callAndroid', '{"name":"callAndroid"}'); } function callIOS() { - window.webkit.messageHandlers.jsBridge.postMessage("{\"id\":\"1\",\"methodName\":\"callIOS\",\"params\":\"{\\\"type\\\":\\\"1\\\"}\"}"); + window.webkit.messageHandlers.iosJsBridge.postMessage("{\"id\":\"1\",\"methodName\":\"callIOS\",\"params\":\"{\\\"type\\\":\\\"1\\\"}\"}"); } function callDesktop() { diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index 7034a32f..daf9fb04 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -92,14 +92,14 @@ class AndroidWebView( super.injectInitJS() val callAndroid = """ window.JsBridge.postMessage = function (message) { - window.jsBridge.call(message) + window.androidJsBridge.call(message) }; """.trimIndent() evaluateJavaScript(callAndroid) } override fun injectJsBridge(webViewJsBridge: WebViewJsBridge) { - webView.addJavascriptInterface(this, "jsBridge") + webView.addJavascriptInterface(this, "androidJsBridge") } @JavascriptInterface diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index a71501a8..095b644b 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -141,7 +141,7 @@ class IOSWebView( super.injectInitJS() val callIOS = """ window.JsBridge.postMessage = function (message) { - window.webkit.messageHandlers.jsBridge.postMessage(message); + window.webkit.messageHandlers.iosJsBridge.postMessage(message); }; """.trimIndent() evaluateJavaScript(callIOS) @@ -151,7 +151,7 @@ class IOSWebView( KLogger.info { "injectBridge" } val jsMessageHandler = WKJsMessageHandler(webViewJsBridge) wkWebView.configuration.userContentController.apply { - addScriptMessageHandler(jsMessageHandler, "jsBridge") + addScriptMessageHandler(jsMessageHandler, "iosJsBridge") } } From 4863f473bc398d0d6b36cad5e8cb0f16feb82b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Sun, 10 Dec 2023 09:38:17 +0800 Subject: [PATCH 44/65] feat:Rename main JsBridge to kmpJsBridge --- .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 4 ++-- .../com/multiplatform/webview/web/AndroidWebView.kt | 2 +- .../webview/jsbridge/WebViewJsBridge.kt | 2 +- .../kotlin/com/multiplatform/webview/web/IWebView.kt | 12 ++++++------ .../com/multiplatform/webview/web/DesktopWebView.kt | 2 +- .../com/multiplatform/webview/web/IOSWebView.kt | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 59e4a4bd..ad7c0f11 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -66,7 +66,7 @@ internal fun BasicWebViewWithHTMLSample() { }); } function callNative() { - window.JsBridge.callNative("Greet",JSON.stringify({message: "1"}), + window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "1"}), function (data) { document.getElementById("subtitle").innerText = data; console.log("Greet from Native: " + data); @@ -105,7 +105,7 @@ internal fun BasicWebViewWithHTMLSample() { webViewNavigator.evaluateJavaScript( """ document.getElementById("subtitle").innerText = "Hello from KMM!"; - window.JsBridge.callNative("Greet",JSON.stringify({message: "1"}), + window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "1"}), function (data) { document.getElementById("subtitle").innerText = data; console.log("Greet from Native: " + data); diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index daf9fb04..257f95ef 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -91,7 +91,7 @@ class AndroidWebView( override fun injectInitJS() { super.injectInitJS() val callAndroid = """ - window.JsBridge.postMessage = function (message) { + window.kmpJsBridge.postMessage = function (message) { window.androidJsBridge.call(message) }; """.trimIndent() diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt index a10f4fcf..4b5de2bc 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt @@ -33,7 +33,7 @@ open class WebViewJsBridge { data: String, callbackId: Int, ) { - webView?.evaluateJavaScript("window.JsBridge.onCallback($callbackId, '$data')") + webView?.evaluateJavaScript("window.kmpJsBridge.onCallback($callbackId, '$data')") } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index 13341fc6..66c157e5 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -159,27 +159,27 @@ interface IWebView { "IWebView injectInitJS" } val initJs = """ - window.JsBridge = { + window.kmpJsBridge = { callbacks: {}, callbackId: 0, callNative: function (methodName, params, callback) { var message = { methodName: methodName, params: params, - callbackId: callback ? window.JsBridge.callbackId++ : -1 + callbackId: callback ? window.kmpJsBridge.callbackId++ : -1 }; if (callback) { - window.JsBridge.callbacks[message.callbackId] = callback; + window.kmpJsBridge.callbacks[message.callbackId] = callback; console.log('add callback: ' + message.callbackId + ', ' + callback); } - window.JsBridge.postMessage(JSON.stringify(message)); + window.kmpJsBridge.postMessage(JSON.stringify(message)); }, onCallback: function (callbackId, data) { - var callback = window.JsBridge.callbacks[callbackId]; + var callback = window.kmpJsBridge.callbacks[callbackId]; console.log('onCallback: ' + callbackId + ', ' + data + ', ' + callback); if (callback) { callback(data); - delete window.JsBridge.callbacks[callbackId]; + delete window.kmpJsBridge.callbacks[callbackId]; } } }; diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index 064985e8..67929c83 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -113,7 +113,7 @@ class DesktopWebView( "DesktopWebView injectInitJS" } val callDesktop = """ - window.JsBridge.postMessage = function (message) { + window.kmpJsBridge.postMessage = function (message) { window.cefQuery({request:message}); }; """.trimIndent() diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index 095b644b..4fb49f05 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -140,7 +140,7 @@ class IOSWebView( override fun injectInitJS() { super.injectInitJS() val callIOS = """ - window.JsBridge.postMessage = function (message) { + window.kmpJsBridge.postMessage = function (message) { window.webkit.messageHandlers.iosJsBridge.postMessage(message); }; """.trimIndent() From d2a3099373b832ded2f9e017e5cc6ea790ad0e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Sun, 10 Dec 2023 09:43:26 +0800 Subject: [PATCH 45/65] feat:Clean Html Sample --- .../com/kevinnzou/sample/HtmlWebViewSample.kt | 54 +----------------- .../com/kevinnzou/sample/res/HtmlRes.kt | 56 +++++++++++++++++++ .../commonMain/resources/assets/index.html | 3 +- .../src/commonMain/resources/assets/script.js | 9 +++ 4 files changed, 68 insertions(+), 54 deletions(-) create mode 100644 sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/res/HtmlRes.kt diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index ad7c0f11..c4471aaa 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.kevinnzou.sample.jsbridge.GreetJsMessageHandler +import com.kevinnzou.sample.res.HtmlRes import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.jsbridge.rememberWebViewJsBridge import com.multiplatform.webview.util.KLogSeverity @@ -28,58 +29,7 @@ import com.multiplatform.webview.web.rememberWebViewStateWithHTMLData */ @Composable internal fun BasicWebViewWithHTMLSample() { - val html = - """ - - - Compose WebView Multiplatform - - - - -

Compose WebView Multiplatform

-

Basic Html Test

- - - - """.trimIndent() + val html = HtmlRes.html // val webViewState = rememberWebViewStateWithHTMLFile( // fileName = "index.html", // ) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/res/HtmlRes.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/res/HtmlRes.kt new file mode 100644 index 00000000..250e1dd8 --- /dev/null +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/res/HtmlRes.kt @@ -0,0 +1,56 @@ +package com.kevinnzou.sample.res + +object HtmlRes { + val html = + """ + + + Compose WebView Multiplatform + + + + +

Compose WebView Multiplatform

+

Basic Html Test

+ + + + """.trimIndent() +} diff --git a/sample/shared/src/commonMain/resources/assets/index.html b/sample/shared/src/commonMain/resources/assets/index.html index 60a82b23..17bd845f 100644 --- a/sample/shared/src/commonMain/resources/assets/index.html +++ b/sample/shared/src/commonMain/resources/assets/index.html @@ -8,7 +8,6 @@

Compose WebView Multiplatform

Basic Html Test

- - + \ No newline at end of file diff --git a/sample/shared/src/commonMain/resources/assets/script.js b/sample/shared/src/commonMain/resources/assets/script.js index fcca6768..b97e25ee 100644 --- a/sample/shared/src/commonMain/resources/assets/script.js +++ b/sample/shared/src/commonMain/resources/assets/script.js @@ -2,6 +2,15 @@ function callJS() { return 'Response from JS'; } +function callNative() { + window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "1"}), + function (data) { + document.getElementById("subtitle").innerText = data; + console.log("Greet from Native: " + data); + } + ); +} + function callAndroid() { window.androidJsBridge.call('1', 'callAndroid', '{"name":"callAndroid"}'); } From 2896a8091850ad3bf574c65dbb5e8154cf4568ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Sun, 10 Dec 2023 20:21:38 +0800 Subject: [PATCH 46/65] feat:Clean Html Sample --- .../com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt | 2 +- .../src/commonMain/kotlin/com/kevinnzou/sample/res/HtmlRes.kt | 2 +- sample/shared/src/commonMain/resources/assets/script.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt index 85e26118..599a3fca 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt @@ -20,7 +20,7 @@ class GreetJsMessageHandler : IJsMessageHandler { "Greet Handler Get Message: $message" } val param = processParams(message) - val data = GreetModel("KMM ${param.message}") + val data = GreetModel("KMM Received ${param.message}") callback(dataToJsonString(data)) } } \ No newline at end of file diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/res/HtmlRes.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/res/HtmlRes.kt index 250e1dd8..0d74043e 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/res/HtmlRes.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/res/HtmlRes.kt @@ -39,7 +39,7 @@ object HtmlRes { }); } function callNative() { - window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "1"}), + window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "Hello"}), function (data) { document.getElementById("subtitle").innerText = data; console.log("Greet from Native: " + data); diff --git a/sample/shared/src/commonMain/resources/assets/script.js b/sample/shared/src/commonMain/resources/assets/script.js index b97e25ee..654feac3 100644 --- a/sample/shared/src/commonMain/resources/assets/script.js +++ b/sample/shared/src/commonMain/resources/assets/script.js @@ -3,7 +3,7 @@ function callJS() { } function callNative() { - window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "1"}), + window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "Hello"}), function (data) { document.getElementById("subtitle").innerText = data; console.log("Greet from Native: " + data); From adc959338af1cbe3c2197fb6eeb32ff3f976bbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Mon, 11 Dec 2023 16:52:27 +0800 Subject: [PATCH 47/65] feat:kotlin("plugin.serialization").apply(false) --- build.gradle.kts | 2 +- sample/shared/build.gradle.kts | 2 +- .../commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 2 +- webview/build.gradle.kts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 105c0821..72998f4d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { // this is necessary to avoid the plugins to be loaded multiple times // in each subproject's classloader kotlin("multiplatform").apply(false) - kotlin("plugin.serialization") + kotlin("plugin.serialization").apply(false) id("com.android.application").apply(false) id("com.android.library").apply(false) id("org.jetbrains.compose").apply(false) diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts index 8b8dbcd1..f12a1cf1 100644 --- a/sample/shared/build.gradle.kts +++ b/sample/shared/build.gradle.kts @@ -2,7 +2,7 @@ plugins { kotlin("multiplatform") id("com.android.library") id("org.jetbrains.compose") - kotlin("plugin.serialization") version "1.9.10" + kotlin("plugin.serialization") } @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index c4471aaa..63efc4b2 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -55,7 +55,7 @@ internal fun BasicWebViewWithHTMLSample() { webViewNavigator.evaluateJavaScript( """ document.getElementById("subtitle").innerText = "Hello from KMM!"; - window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "1"}), + window.kmpJsBridge.callNative("Greet",JSON.stringify({message: "Hello"}), function (data) { document.getElementById("subtitle").innerText = data; console.log("Greet from Native: " + data); diff --git a/webview/build.gradle.kts b/webview/build.gradle.kts index b9c31768..0aa8371b 100644 --- a/webview/build.gradle.kts +++ b/webview/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.jetbrains.compose") id("org.jetbrains.dokka") id("com.vanniktech.maven.publish") - kotlin("plugin.serialization") version "1.9.10" + kotlin("plugin.serialization") } kotlin { From 55d1b100c7d04881d3aa3cfac1d0e62db4d768dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Mon, 11 Dec 2023 20:22:32 +0800 Subject: [PATCH 48/65] feat:Update README.md for JsBridge --- README.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 9f7c203d..9cbed4c3 100644 --- a/README.md +++ b/README.md @@ -245,27 +245,94 @@ Column { val loadingState = state.loadingState if (loadingState is LoadingState.Loading) { LinearProgressIndicator( - progress = loadingState.progress, - modifier = Modifier.fillMaxWidth() + progress = loadingState.progress, + modifier = Modifier.fillMaxWidth() ) } - WebView( - state = state, - navigator = navigator - ) + WebView( + state = state, + navigator = navigator + ) +} +``` + +## Communication between WebView and Native + +Starting from version 1.8.0, this library provides a `WebViewJsBridge` to allow developers to +communicate between the WebView and Native. +Developers can use the JsBridge to register a handler to handle the message from the WebView. + +```kotlin +val jsBridge = rememberWebViewJsBridge() + +LaunchedEffect(jsBridge) { + jsBridge.register(GreetJsMessageHandler()) } ``` +The handler should implement the `IJsMessageHandler` interface. + +```kotlin +interface IJsMessageHandler { + fun methodName(): String + + fun canHandle(methodName: String) = methodName() == methodName + + fun handle( + message: JsMessage, + callback: (String) -> Unit, + ) + +} + +class GreetJsMessageHandler : IJsMessageHandler { + override fun methodName(): String { + return "Greet" + } + + override fun handle(message: JsMessage, callback: (String) -> Unit) { + Logger.i { + "Greet Handler Get Message: $message" + } + val param = processParams(message) + val data = GreetModel("KMM Received ${param.message}") + callback(dataToJsonString(data)) + } +} +``` + +Developers can use the `window.kmpJsBridge.callNative` to send a message to the Native. +It receives three parameters: + +* methodName: the name of the handler registered in the Native. +* params: the parameters to send to the Native. It needs to be a JSON string. +* callback: the callback function to handle the response from the Native. It receives a JSON string + as the parameter. Pass null if no callback is needed. + +```javascript +window.kmpJsBridge.callNative = function (methodName, params, callback) {} + +window.kmpJsBridge.callNative("Greet",JSON.stringify({message:"Hello"}), + function (data) { + document.getElementById("subtitle").innerText = data; + console.log("Greet from Native: " + data); + } +); +``` + ## WebSettings -Starting from version 1.3.0, this library allows users to customize web settings. -There are some common web settings that can be shared across different platforms, such as isJavaScriptEnabled and userAgent. + +Starting from version 1.3.0, this library allows users to customize web settings. +There are some common web settings that can be shared across different platforms, such as +isJavaScriptEnabled and userAgent. + ```kotlin class WebSettings { - var isJavaScriptEnabled = true + var isJavaScriptEnabled = true - var customUserAgentString: String? = null + var customUserAgentString: String? = null - /** + /** * Android platform specific settings */ val androidWebSettings = PlatformWebSettings.AndroidWebSettings() From 1280ac3e4da61efa2f7fbbcd4cfff61d8113bfef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Mon, 11 Dec 2023 20:28:16 +0800 Subject: [PATCH 49/65] feat:Make JsMessageDispatcher internal --- .../com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt index 22bfb458..96be138f 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt @@ -3,7 +3,7 @@ package com.multiplatform.webview.jsbridge /** * Created By Kevin Zou On 2023/10/31 */ -class JsMessageDispatcher { +internal class JsMessageDispatcher { private val jsHandlerMap = mutableMapOf() fun registerJSHandler(handler: IJsMessageHandler) { From 6837bc4b1544598f09d94e16bb08f787c7114c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 13 Dec 2023 17:47:34 +0800 Subject: [PATCH 50/65] feat:Provide WebViewNavigator to IJsMessageHandler --- .../kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt | 4 ++-- .../kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt | 9 +++++++-- .../multiplatform/webview/jsbridge/IJsMessageHandler.kt | 3 ++- .../webview/jsbridge/JsMessageDispatcher.kt | 5 ++++- .../multiplatform/webview/jsbridge/WebViewJsBridge.kt | 8 ++++---- .../kotlin/com/multiplatform/webview/web/WebView.kt | 4 ++++ 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 63efc4b2..7c5a9bc3 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -34,12 +34,12 @@ internal fun BasicWebViewWithHTMLSample() { // fileName = "index.html", // ) val webViewState = rememberWebViewStateWithHTMLData(html) - val jsBridge = rememberWebViewJsBridge() + val webViewNavigator = rememberWebViewNavigator() + val jsBridge = rememberWebViewJsBridge(webViewNavigator) LaunchedEffect(Unit) { initWebView(webViewState) initJsBridge(jsBridge) } - val webViewNavigator = rememberWebViewNavigator() var jsRes by mutableStateOf("Evaluate JavaScript") MaterialTheme { Box(Modifier.fillMaxSize()) { diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt index 599a3fca..59e61c2f 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt @@ -6,6 +6,7 @@ import com.multiplatform.webview.jsbridge.IJsMessageHandler import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.jsbridge.dataToJsonString import com.multiplatform.webview.jsbridge.processParams +import com.multiplatform.webview.web.WebViewNavigator /** * Created By Kevin Zou On 2023/12/6 @@ -15,7 +16,11 @@ class GreetJsMessageHandler : IJsMessageHandler { return "Greet" } - override fun handle(message: JsMessage, callback: (String) -> Unit) { + override fun handle( + message: JsMessage, + navigator: WebViewNavigator?, + callback: (String) -> Unit, + ) { Logger.i { "Greet Handler Get Message: $message" } @@ -23,4 +28,4 @@ class GreetJsMessageHandler : IJsMessageHandler { val data = GreetModel("KMM Received ${param.message}") callback(dataToJsonString(data)) } -} \ No newline at end of file +} diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt index cf3380e6..61134b89 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/IJsMessageHandler.kt @@ -1,5 +1,6 @@ package com.multiplatform.webview.jsbridge +import com.multiplatform.webview.web.WebViewNavigator import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -13,9 +14,9 @@ interface IJsMessageHandler { fun handle( message: JsMessage, + navigator: WebViewNavigator?, callback: (String) -> Unit, ) - } inline fun IJsMessageHandler.processParams(message: JsMessage): T { diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt index 96be138f..9c6c9b37 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt @@ -1,5 +1,7 @@ package com.multiplatform.webview.jsbridge +import com.multiplatform.webview.web.WebViewNavigator + /** * Created By Kevin Zou On 2023/10/31 */ @@ -12,9 +14,10 @@ internal class JsMessageDispatcher { fun dispatch( message: JsMessage, + navigator: WebViewNavigator? = null, callback: (String) -> Unit, ) { - jsHandlerMap[message.methodName]?.handle(message, callback) + jsHandlerMap[message.methodName]?.handle(message, navigator, callback) } fun canHandle(id: String) = jsHandlerMap.containsKey(id) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt index 4b5de2bc..148153be 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt @@ -3,11 +3,12 @@ package com.multiplatform.webview.jsbridge import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import com.multiplatform.webview.web.IWebView +import com.multiplatform.webview.web.WebViewNavigator /** * Created By Kevin Zou On 2023/10/31 */ -open class WebViewJsBridge { +open class WebViewJsBridge(val navigator: WebViewNavigator? = null) { private val jsMessageDispatcher = JsMessageDispatcher() var webView: IWebView? = null @@ -24,7 +25,7 @@ open class WebViewJsBridge { } fun dispatch(message: JsMessage) { - jsMessageDispatcher.dispatch(message) { + jsMessageDispatcher.dispatch(message, navigator) { onCallback(it, message.callbackId) } } @@ -38,5 +39,4 @@ open class WebViewJsBridge { } @Composable -fun rememberWebViewJsBridge(): WebViewJsBridge = - remember { WebViewJsBridge() } \ No newline at end of file +fun rememberWebViewJsBridge(navigator: WebViewNavigator? = null): WebViewJsBridge = remember { WebViewJsBridge(navigator) } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index 63542904..df6aba41 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import com.multiplatform.webview.jsbridge.WebViewJsBridge +import com.multiplatform.webview.util.KLogger import com.multiplatform.webview.util.Platform import com.multiplatform.webview.util.getPlatform import org.jetbrains.compose.resources.ExperimentalResourceApi @@ -50,6 +51,9 @@ fun WebView( webView?.let { wv -> LaunchedEffect(wv, navigator) { with(navigator) { + KLogger.d { + "wv.handleNavigationEvents()" + } wv.handleNavigationEvents() } } From d5e74039b7bbb0d5480516c068269b73b66506c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Fri, 15 Dec 2023 17:02:13 +0800 Subject: [PATCH 51/65] feat:Import atomicfu --- build.gradle.kts | 1 + sample/shared/build.gradle.kts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 72998f4d..df0f0b60 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,6 +9,7 @@ plugins { id("org.jetbrains.dokka") id("com.vanniktech.maven.publish") version "0.25.3" apply false id("org.jlleitschuh.gradle.ktlint") version "11.6.1" + id("org.jetbrains.kotlin.plugin.atomicfu") version "1.9.20" } subprojects { diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts index f12a1cf1..9f94ac5f 100644 --- a/sample/shared/build.gradle.kts +++ b/sample/shared/build.gradle.kts @@ -3,6 +3,7 @@ plugins { id("com.android.library") id("org.jetbrains.compose") kotlin("plugin.serialization") + id("org.jetbrains.kotlin.plugin.atomicfu") } @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) @@ -40,6 +41,7 @@ kotlin { implementation("co.touchlab:kermit:2.0.0-RC5") api(project(":webview")) implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") + implementation("org.jetbrains.kotlinx:atomicfu:0.21.0") } } val androidMain by getting { From 6211741ca9e8fdad6c7815b25d93d58199248016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Fri, 15 Dec 2023 17:02:50 +0800 Subject: [PATCH 52/65] feat:Add Simple EventBus --- .../com/kevinnzou/sample/HtmlWebViewSample.kt | 10 +++- .../com/kevinnzou/sample/eventbus/EventBus.kt | 60 +++++++++++++++++++ .../sample/eventbus/NavigationEvent.kt | 6 ++ .../sample/jsbridge/GreetJsMessageHandler.kt | 3 + 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/EventBus.kt create mode 100644 sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/NavigationEvent.kt diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 7c5a9bc3..f1b96d65 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -14,6 +14,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import co.touchlab.kermit.Logger +import com.kevinnzou.sample.eventbus.EventBus +import com.kevinnzou.sample.eventbus.NavigationEvent import com.kevinnzou.sample.jsbridge.GreetJsMessageHandler import com.kevinnzou.sample.res.HtmlRes import com.multiplatform.webview.jsbridge.WebViewJsBridge @@ -36,11 +39,16 @@ internal fun BasicWebViewWithHTMLSample() { val webViewState = rememberWebViewStateWithHTMLData(html) val webViewNavigator = rememberWebViewNavigator() val jsBridge = rememberWebViewJsBridge(webViewNavigator) + var jsRes by mutableStateOf("Evaluate JavaScript") LaunchedEffect(Unit) { initWebView(webViewState) initJsBridge(jsBridge) + EventBus.observe { + Logger.d { + "Received NavigationEvent" + } + } } - var jsRes by mutableStateOf("Evaluate JavaScript") MaterialTheme { Box(Modifier.fillMaxSize()) { WebView( diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/EventBus.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/EventBus.kt new file mode 100644 index 00000000..95e9f9e1 --- /dev/null +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/EventBus.kt @@ -0,0 +1,60 @@ +package com.kevinnzou.sample.eventbus + +import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.getAndUpdate +import kotlin.reflect.KClass + +/** + * Created By Kevin Zou On 2023/12/15 + */ + +typealias Observer = (Any) -> Unit + +object EventBus { + private val observers = atomic(mutableMapOf, List>()) + + fun observe( + clazz: KClass, + obs: (T) -> Unit, + ) { + if (!observers.value.containsKey(clazz)) { + observers.getAndUpdate { cur -> + cur.toMutableMap().also { upd -> + upd[clazz] = listOf(obs as Observer) + } + } + } else { + observers.getAndUpdate { cur -> + cur.toMutableMap().also { upd -> + upd[clazz] = upd[clazz]!! + listOf(obs as Observer) + } + } + } + } + + inline fun observe(noinline obs: (T) -> Unit) { + observe(T::class, obs) + } + + fun removeObserver( + clazz: KClass, + obs: (T) -> Unit, + ) { + observers.getAndUpdate { cur -> + cur.toMutableMap().also { upd -> + upd.remove(clazz) + } + } + } + + fun post( + clazz: KClass, + event: T, + ) { + observers.value[clazz]?.forEach { it.invoke(event) } + } + + inline fun post(event: T) { + post(T::class, event) + } +} diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/NavigationEvent.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/NavigationEvent.kt new file mode 100644 index 00000000..a0c7affd --- /dev/null +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/NavigationEvent.kt @@ -0,0 +1,6 @@ +package com.kevinnzou.sample.eventbus + +/** + * Created By Kevin Zou On 2023/12/15 + */ +class NavigationEvent diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt index 59e61c2f..3e93f5a3 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt @@ -1,6 +1,8 @@ package com.kevinnzou.sample.jsbridge import co.touchlab.kermit.Logger +import com.kevinnzou.sample.eventbus.EventBus +import com.kevinnzou.sample.eventbus.NavigationEvent import com.kevinnzou.sample.model.GreetModel import com.multiplatform.webview.jsbridge.IJsMessageHandler import com.multiplatform.webview.jsbridge.JsMessage @@ -27,5 +29,6 @@ class GreetJsMessageHandler : IJsMessageHandler { val param = processParams(message) val data = GreetModel("KMM Received ${param.message}") callback(dataToJsonString(data)) + EventBus.post(NavigationEvent()) } } From 676ff489e5e0c6d6716368e6f041334559709340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Fri, 15 Dec 2023 20:23:42 +0800 Subject: [PATCH 53/65] feat:Set version 1.8.0-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 68f01778..7350c497 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ compose.version=1.5.10 GROUP=io.github.kevinnzou POM_ARTIFACT_ID=compose-webview-multiplatform -VERSION_NAME=1.7.8 +VERSION_NAME=1.8.0-SNAPSHOT POM_NAME=Compose WebView Multiplatform POM_INCEPTION_YEAR=2023 From c8b19437c3af11f386908401a2ed62ea1c96a8f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Sat, 16 Dec 2023 08:43:50 +0800 Subject: [PATCH 54/65] feat:Add Flow version of EventBus and sample --- sample/shared/build.gradle.kts | 4 ++++ .../com/kevinnzou/sample/HtmlWebViewSample.kt | 10 ++++++++-- .../kevinnzou/sample/eventbus/FlowEventBus.kt | 16 ++++++++++++++++ .../com/kevinnzou/sample/eventbus/IEvent.kt | 6 ++++++ .../kevinnzou/sample/eventbus/NavigationEvent.kt | 2 +- .../sample/jsbridge/GreetJsMessageHandler.kt | 8 ++++++-- .../webview/web/WebViewNavigator.kt | 2 +- 7 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/FlowEventBus.kt create mode 100644 sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/IEvent.kt diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts index 9f94ac5f..9df94c05 100644 --- a/sample/shared/build.gradle.kts +++ b/sample/shared/build.gradle.kts @@ -31,6 +31,7 @@ kotlin { } sourceSets { + val coroutines = "1.7.3" val commonMain by getting { dependencies { implementation(compose.runtime) @@ -42,17 +43,20 @@ kotlin { api(project(":webview")) implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") implementation("org.jetbrains.kotlinx:atomicfu:0.21.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") } } val androidMain by getting { dependencies { api("androidx.activity:activity-compose:1.7.2") api("androidx.appcompat:appcompat:1.6.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines") } } val desktopMain by getting { dependencies { implementation(compose.desktop.common) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:$coroutines") } } val commonTest by getting { diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index f1b96d65..719354fb 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import co.touchlab.kermit.Logger -import com.kevinnzou.sample.eventbus.EventBus +import com.kevinnzou.sample.eventbus.FlowEventBus import com.kevinnzou.sample.eventbus.NavigationEvent import com.kevinnzou.sample.jsbridge.GreetJsMessageHandler import com.kevinnzou.sample.res.HtmlRes @@ -26,6 +26,7 @@ import com.multiplatform.webview.web.WebView import com.multiplatform.webview.web.WebViewState import com.multiplatform.webview.web.rememberWebViewNavigator import com.multiplatform.webview.web.rememberWebViewStateWithHTMLData +import kotlinx.coroutines.flow.filter /** * Created By Kevin Zou On 2023/9/8 @@ -43,7 +44,12 @@ internal fun BasicWebViewWithHTMLSample() { LaunchedEffect(Unit) { initWebView(webViewState) initJsBridge(jsBridge) - EventBus.observe { +// EventBus.observe { +// Logger.d { +// "Received NavigationEvent" +// } +// } + FlowEventBus.events.filter { it is NavigationEvent }.collect { Logger.d { "Received NavigationEvent" } diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/FlowEventBus.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/FlowEventBus.kt new file mode 100644 index 00000000..886ef967 --- /dev/null +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/FlowEventBus.kt @@ -0,0 +1,16 @@ +package com.kevinnzou.sample.eventbus + +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow + +/** + * Created By Kevin Zou On 2023/12/16 + */ +object FlowEventBus { + private val mEvents = MutableSharedFlow() + val events = mEvents.asSharedFlow() + + suspend fun publishEvent(event: IEvent) { + mEvents.emit(event) + } +} diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/IEvent.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/IEvent.kt new file mode 100644 index 00000000..b8d8a6b6 --- /dev/null +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/IEvent.kt @@ -0,0 +1,6 @@ +package com.kevinnzou.sample.eventbus + +/** + * Created By Kevin Zou On 2023/12/16 + */ +interface IEvent diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/NavigationEvent.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/NavigationEvent.kt index a0c7affd..b13f8d64 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/NavigationEvent.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/eventbus/NavigationEvent.kt @@ -3,4 +3,4 @@ package com.kevinnzou.sample.eventbus /** * Created By Kevin Zou On 2023/12/15 */ -class NavigationEvent +class NavigationEvent : IEvent diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt index 3e93f5a3..29a9ae56 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/GreetJsMessageHandler.kt @@ -1,7 +1,7 @@ package com.kevinnzou.sample.jsbridge import co.touchlab.kermit.Logger -import com.kevinnzou.sample.eventbus.EventBus +import com.kevinnzou.sample.eventbus.FlowEventBus import com.kevinnzou.sample.eventbus.NavigationEvent import com.kevinnzou.sample.model.GreetModel import com.multiplatform.webview.jsbridge.IJsMessageHandler @@ -9,6 +9,7 @@ import com.multiplatform.webview.jsbridge.JsMessage import com.multiplatform.webview.jsbridge.dataToJsonString import com.multiplatform.webview.jsbridge.processParams import com.multiplatform.webview.web.WebViewNavigator +import kotlinx.coroutines.launch /** * Created By Kevin Zou On 2023/12/6 @@ -29,6 +30,9 @@ class GreetJsMessageHandler : IJsMessageHandler { val param = processParams(message) val data = GreetModel("KMM Received ${param.message}") callback(dataToJsonString(data)) - EventBus.post(NavigationEvent()) +// EventBus.post(NavigationEvent()) + navigator?.coroutineScope?.launch { + FlowEventBus.publishEvent(NavigationEvent()) + } } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewNavigator.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewNavigator.kt index ec38241c..44742bdc 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewNavigator.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebViewNavigator.kt @@ -24,7 +24,7 @@ import kotlinx.coroutines.withContext * @see [rememberWebViewNavigator] */ @Stable -class WebViewNavigator(private val coroutineScope: CoroutineScope) { +class WebViewNavigator(val coroutineScope: CoroutineScope) { /** * Sealed class for constraining possible navigation events. */ From bab9225ac92725cb64ed338e1c95c5bb5c51380e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Sat, 16 Dec 2023 20:35:26 +0800 Subject: [PATCH 55/65] feat:inject JSBridge for each loaded pages. --- .../webview/web/AndroidWebView.kt | 11 ++-- .../com/multiplatform/webview/web/WebView.kt | 5 +- .../webview/web/DesktopWebView.kt | 55 ++++++++++--------- .../multiplatform/webview/web/IOSWebView.kt | 6 +- 4 files changed, 42 insertions(+), 35 deletions(-) diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt index 257f95ef..1e1e5b24 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AndroidWebView.kt @@ -23,6 +23,7 @@ class AndroidWebView( init { initWebView() } + override fun canGoBack() = webView.canGoBack() override fun canGoForward() = webView.canGoForward() @@ -89,12 +90,14 @@ class AndroidWebView( } override fun injectInitJS() { + if (webViewJsBridge == null) return super.injectInitJS() - val callAndroid = """ + val callAndroid = + """ window.kmpJsBridge.postMessage = function (message) { window.androidJsBridge.call(message) }; - """.trimIndent() + """.trimIndent() evaluateJavaScript(callAndroid) } @@ -103,9 +106,7 @@ class AndroidWebView( } @JavascriptInterface - fun call( - request: String - ) { + fun call(request: String) { KLogger.d { "call from JS: $request" } val message = Json.decodeFromString(request) KLogger.d { diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index df6aba41..c6be61f4 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -96,11 +96,10 @@ fun WebView( } // TODO WorkAround for Desktop not working issue. - if (webViewJsBridge != null && !isJsBridgeInjected && getPlatform() != Platform.Desktop) { + if (webViewJsBridge != null && getPlatform() != Platform.Desktop) { LaunchedEffect(state.loadingState, webViewJsBridge) { - if (state.loadingState is LoadingState.Finished && !isJsBridgeInjected) { + if (state.loadingState is LoadingState.Finished) { webView?.injectInitJS() - isJsBridgeInjected = true } } } diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index 67929c83..a7a0e4c7 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -108,45 +108,50 @@ class DesktopWebView( } override fun injectInitJS() { + if (webViewJsBridge == null) return super.injectInitJS() KLogger.d { "DesktopWebView injectInitJS" } - val callDesktop = """ + val callDesktop = + """ window.kmpJsBridge.postMessage = function (message) { window.cefQuery({request:message}); }; - """.trimIndent() + """.trimIndent() evaluateJavaScript(callDesktop) } override fun injectJsBridge(webViewJsBridge: WebViewJsBridge) { val router = CefMessageRouter.create() - val handler = object : CefMessageRouterHandlerAdapter() { - override fun onQuery( - browser: CefBrowser?, - frame: CefFrame?, - queryId: Long, - request: String?, - persistent: Boolean, - callback: CefQueryCallback? - ): Boolean { - if (request == null) return super.onQuery( - browser, - frame, - queryId, - request, - persistent, - callback - ) - val message = Json.decodeFromString(request) - KLogger.d { - "onQuery Message: $message" + val handler = + object : CefMessageRouterHandlerAdapter() { + override fun onQuery( + browser: CefBrowser?, + frame: CefFrame?, + queryId: Long, + request: String?, + persistent: Boolean, + callback: CefQueryCallback?, + ): Boolean { + if (request == null) { + return super.onQuery( + browser, + frame, + queryId, + request, + persistent, + callback, + ) + } + val message = Json.decodeFromString(request) + KLogger.d { + "onQuery Message: $message" + } + webViewJsBridge.dispatch(message) + return true } - webViewJsBridge.dispatch(message) - return true } - } router.addHandler(handler, false) webView.client.addMessageRouter(router) } diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index 4fb49f05..693e88fa 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -138,12 +138,14 @@ class IOSWebView( } override fun injectInitJS() { + if (webViewJsBridge == null) return super.injectInitJS() - val callIOS = """ + val callIOS = + """ window.kmpJsBridge.postMessage = function (message) { window.webkit.messageHandlers.iosJsBridge.postMessage(message); }; - """.trimIndent() + """.trimIndent() evaluateJavaScript(callIOS) } From 3a83eea9a4b295c31befcda750cfdec5c2d06b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Tue, 19 Dec 2023 10:43:10 +0800 Subject: [PATCH 56/65] feat:inject JSBridge when Url changes --- .../webview/web/AccompanistWebView.kt | 15 ++++++++++++--- .../com/multiplatform/webview/web/WebView.kt | 9 +-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt index 993050a6..9851e06c 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/AccompanistWebView.kt @@ -252,6 +252,7 @@ open class AccompanistWebViewClient : WebViewClient() { "onPageFinished: $url" } state.loadingState = LoadingState.Finished + state.lastLoadedUrl = url } override fun doUpdateVisitedHistory( @@ -296,6 +297,7 @@ open class AccompanistWebViewClient : WebViewClient() { open class AccompanistWebChromeClient : WebChromeClient() { open lateinit var state: WebViewState internal set + private var lastLoadedUrl = "" override fun onReceivedTitle( view: WebView, @@ -303,9 +305,10 @@ open class AccompanistWebChromeClient : WebChromeClient() { ) { super.onReceivedTitle(view, title) KLogger.d { - "onReceivedTitle: $title" + "onReceivedTitle: $title url:${view.url}" } state.pageTitle = title + state.lastLoadedUrl = view.url ?: "" } override fun onReceivedIcon( @@ -321,7 +324,13 @@ open class AccompanistWebChromeClient : WebChromeClient() { newProgress: Int, ) { super.onProgressChanged(view, newProgress) - if (state.loadingState is LoadingState.Finished) return - state.loadingState = LoadingState.Loading(newProgress / 100.0f) + if (state.loadingState is LoadingState.Finished && view.url == lastLoadedUrl) return + state.loadingState = + if (newProgress == 100) { + LoadingState.Finished + } else { + LoadingState.Loading(newProgress / 100.0f) + } + lastLoadedUrl = view.url ?: "" } } diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index c6be61f4..829c85f4 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -2,10 +2,6 @@ package com.multiplatform.webview.web import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import com.multiplatform.webview.jsbridge.WebViewJsBridge @@ -44,9 +40,6 @@ fun WebView( onDispose: () -> Unit = {}, ) { val webView = state.webView - var isJsBridgeInjected by remember { - mutableStateOf(false) - } webView?.let { wv -> LaunchedEffect(wv, navigator) { @@ -97,7 +90,7 @@ fun WebView( // TODO WorkAround for Desktop not working issue. if (webViewJsBridge != null && getPlatform() != Platform.Desktop) { - LaunchedEffect(state.loadingState, webViewJsBridge) { + LaunchedEffect(state.loadingState, webViewJsBridge, state.lastLoadedUrl) { if (state.loadingState is LoadingState.Finished) { webView?.injectInitJS() } From 2f572e917be045e5f0d7544faf78447e85afbe18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 21 Dec 2023 13:42:39 +0800 Subject: [PATCH 57/65] feat:mark WebViewJsBridge and JsMessageDispatcher Immutable --- .../com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt | 2 ++ .../com/multiplatform/webview/jsbridge/WebViewJsBridge.kt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt index 9c6c9b37..dceff659 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/JsMessageDispatcher.kt @@ -1,10 +1,12 @@ package com.multiplatform.webview.jsbridge +import androidx.compose.runtime.Immutable import com.multiplatform.webview.web.WebViewNavigator /** * Created By Kevin Zou On 2023/10/31 */ +@Immutable internal class JsMessageDispatcher { private val jsHandlerMap = mutableMapOf() diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt index 148153be..a1d805c5 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/jsbridge/WebViewJsBridge.kt @@ -1,6 +1,7 @@ package com.multiplatform.webview.jsbridge import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.remember import com.multiplatform.webview.web.IWebView import com.multiplatform.webview.web.WebViewNavigator @@ -8,6 +9,7 @@ import com.multiplatform.webview.web.WebViewNavigator /** * Created By Kevin Zou On 2023/10/31 */ +@Immutable open class WebViewJsBridge(val navigator: WebViewNavigator? = null) { private val jsMessageDispatcher = JsMessageDispatcher() var webView: IWebView? = null From c3ef2932b6d38dd091fb16ad5c9a412979a35d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 21 Dec 2023 13:43:27 +0800 Subject: [PATCH 58/65] feat:Platform isDesktop --- .../com/multiplatform/webview/util/Platform.kt | 10 +++++++++- .../com/multiplatform/webview/web/WebView.kt | 18 ++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/util/Platform.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/Platform.kt index 5d974006..d04b4315 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/util/Platform.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/Platform.kt @@ -5,8 +5,16 @@ package com.multiplatform.webview.util */ internal sealed class Platform { data object Android : Platform() + data object Desktop : Platform() + data object IOS : Platform() + + fun isAndroid() = this is Android + + fun isDesktop() = this is Desktop + + fun isIOS() = this is IOS } -internal expect fun getPlatform(): Platform \ No newline at end of file +internal expect fun getPlatform(): Platform diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index 829c85f4..e59dadc7 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -1,12 +1,12 @@ package com.multiplatform.webview.web import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import com.multiplatform.webview.jsbridge.WebViewJsBridge import com.multiplatform.webview.util.KLogger -import com.multiplatform.webview.util.Platform import com.multiplatform.webview.util.getPlatform import org.jetbrains.compose.resources.ExperimentalResourceApi @@ -89,7 +89,7 @@ fun WebView( } // TODO WorkAround for Desktop not working issue. - if (webViewJsBridge != null && getPlatform() != Platform.Desktop) { + if (webViewJsBridge != null && !getPlatform().isDesktop()) { LaunchedEffect(state.loadingState, webViewJsBridge, state.lastLoadedUrl) { if (state.loadingState is LoadingState.Finished) { webView?.injectInitJS() @@ -104,11 +104,17 @@ fun WebView( navigator = navigator, webViewJsBridge = webViewJsBridge, onCreated = onCreated, - onDispose = { - webViewJsBridge?.clear() - onDispose() - }, + onDispose = onDispose, ) + + DisposableEffect(Unit) { + onDispose { + KLogger.d { + "WebView DisposableEffect" + } + webViewJsBridge?.clear() + } + } } /** From 3c2ec5cde938a35cbeedfd9214017f206b2de015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 21 Dec 2023 13:44:10 +0800 Subject: [PATCH 59/65] feat:Desktop WebView Test Log --- .../kotlin/com/multiplatform/webview/web/DesktopWebView.kt | 3 +++ .../kotlin/com/multiplatform/webview/web/WebEngineExt.kt | 1 + 2 files changed, 4 insertions(+) diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt index a7a0e4c7..c39fc98a 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/DesktopWebView.kt @@ -123,6 +123,9 @@ class DesktopWebView( } override fun injectJsBridge(webViewJsBridge: WebViewJsBridge) { + KLogger.d { + "DesktopWebView injectJsBridge" + } val router = CefMessageRouter.create() val handler = object : CefMessageRouterHandlerAdapter() { diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt index bc8e9221..8ce4b2a6 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt @@ -25,6 +25,7 @@ internal fun CefBrowser.addDisplayHandler(state: WebViewState) { frame: CefFrame?, url: String?, ) { + KLogger.d { "onAddressChange: $url" } state.lastLoadedUrl = getCurrentUrl() } From 7f5941a855b52202d590e2c9351fc207cd90bdfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 21 Dec 2023 14:17:15 +0800 Subject: [PATCH 60/65] feat:Desktop WebView inject JS when Url changes --- .../com/multiplatform/webview/web/WebEngineExt.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt index 8ce4b2a6..b197edf4 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt @@ -42,7 +42,7 @@ internal fun CefBrowser.addDisplayHandler(state: WebViewState) { } else { -ln(abs(givenZoomLevel)) / ln(1.2) } - KLogger.d { "titleProperty: $title $realZoomLevel" } + KLogger.d { "titleProperty: $title" } zoomLevel = realZoomLevel state.pageTitle = title } @@ -86,16 +86,25 @@ internal fun CefBrowser.addLoadListener( ) { this.client.addLoadHandler( object : CefLoadHandler { + private var lastLoadedUrl = "" + override fun onLoadingStateChange( browser: CefBrowser?, isLoading: Boolean, canGoBack: Boolean, canGoForward: Boolean, ) { + KLogger.d { + "onLoadingStateChange: $url, $isLoading $canGoBack $canGoForward" + } if (isLoading) { state.loadingState = LoadingState.Initializing } else { state.loadingState = LoadingState.Finished + if (url != null && url != lastLoadedUrl) { + state.webView?.injectInitJS() + lastLoadedUrl = url + } } navigator.canGoBack = canGoBack navigator.canGoForward = canGoForward @@ -121,7 +130,6 @@ internal fun CefBrowser.addLoadListener( navigator.canGoBack = canGoBack() navigator.canGoBack = canGoForward() state.lastLoadedUrl = getCurrentUrl() - state.webView?.injectInitJS() } override fun onLoadError( From 8c4c9b20dc438bde36faba3a981823bacc403eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 21 Dec 2023 14:35:56 +0800 Subject: [PATCH 61/65] feat:Desktop WebView inject JS when reload --- .../kotlin/com/multiplatform/webview/web/WebEngineExt.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt index b197edf4..4b0e2e0a 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/web/WebEngineExt.kt @@ -116,6 +116,7 @@ internal fun CefBrowser.addLoadListener( transitionType: CefRequest.TransitionType?, ) { KLogger.d { "Load Start ${browser?.url}" } + lastLoadedUrl = "" // clean last loaded url for reload to work state.loadingState = LoadingState.Loading(0F) state.errorsForCurrentRequest.clear() } From 943e76d4e6511d9d14dc8b3b6aecda33f8bb1735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Thu, 21 Dec 2023 15:03:31 +0800 Subject: [PATCH 62/65] feat:iOS WebView Log --- .../kotlin/com/multiplatform/webview/web/WebView.kt | 2 +- .../com/multiplatform/webview/web/WKNavigationDelegate.kt | 6 +++--- .../com/multiplatform/webview/web/WKWebViewObserver.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt index e59dadc7..abc90461 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/WebView.kt @@ -90,7 +90,7 @@ fun WebView( // TODO WorkAround for Desktop not working issue. if (webViewJsBridge != null && !getPlatform().isDesktop()) { - LaunchedEffect(state.loadingState, webViewJsBridge, state.lastLoadedUrl) { + LaunchedEffect(state.loadingState, state.lastLoadedUrl) { if (state.loadingState is LoadingState.Finished) { webView?.injectInitJS() } diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKNavigationDelegate.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKNavigationDelegate.kt index 65232348..9a752226 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKNavigationDelegate.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKNavigationDelegate.kt @@ -29,7 +29,7 @@ class WKNavigationDelegate( state.loadingState = LoadingState.Loading(0f) state.lastLoadedUrl = webView.URL.toString() state.errorsForCurrentRequest.clear() - KLogger.d { + KLogger.info { "didStartProvisionalNavigation" } } @@ -44,7 +44,7 @@ class WKNavigationDelegate( @Suppress("ktlint:standard:max-line-length") val script = "var meta = document.createElement('meta');meta.setAttribute('name', 'viewport');meta.setAttribute('content', 'width=device-width, initial-scale=${state.webSettings.zoomLevel}, maximum-scale=10.0, minimum-scale=0.1,user-scalable=yes');document.getElementsByTagName('head')[0].appendChild(meta);" webView.evaluateJavaScript(script) { _, _ -> } - KLogger.d { "didCommitNavigation" } + KLogger.info { "didCommitNavigation" } } /** @@ -59,7 +59,7 @@ class WKNavigationDelegate( state.loadingState = LoadingState.Finished navigator.canGoBack = webView.canGoBack navigator.canGoForward = webView.canGoForward - KLogger.d { "didFinishNavigation" } + KLogger.info { "didFinishNavigation" } } /** diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewObserver.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewObserver.kt index 0616f5bc..ece27121 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewObserver.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewObserver.kt @@ -36,13 +36,13 @@ class WKWebViewObserver(private val state: WebViewState, private val navigator: } } else if (keyPath == "title") { val title = change?.get("new") as? String - KLogger.d { "Observe title Changed $title" } + KLogger.info { "Observe title Changed $title" } if (title != null) { state.pageTitle = title } } else if (keyPath == "URL") { val url = change?.get("new") as? NSURL - KLogger.d { "Observe URL Changed ${url?.absoluteString}" } + KLogger.info { "Observe URL Changed ${url?.absoluteString}" } if (url != null) { state.lastLoadedUrl = url.absoluteString } From 208c2792c733ba39af0e00af070de47faabb42e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Fri, 22 Dec 2023 10:56:24 +0800 Subject: [PATCH 63/65] feat:iOS WebView Log --- .../iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt index 693e88fa..521e2c9b 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/IOSWebView.kt @@ -139,6 +139,9 @@ class IOSWebView( override fun injectInitJS() { if (webViewJsBridge == null) return + KLogger.info { + "iOS WebView injectInitJS" + } super.injectInitJS() val callIOS = """ From f438a521a4edec2f64fb8ef6acb853605e7e17a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 27 Dec 2023 10:30:52 +0800 Subject: [PATCH 64/65] feat:Clean Sample --- .../com/kevinnzou/sample/HtmlWebViewSample.kt | 22 +++++++++---------- .../kotlin/com/kevinnzou/sample/WebViewApp.kt | 4 ++-- .../com/multiplatform/webview/util/KLogger.kt | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt index 719354fb..5fa17318 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/HtmlWebViewSample.kt @@ -44,16 +44,6 @@ internal fun BasicWebViewWithHTMLSample() { LaunchedEffect(Unit) { initWebView(webViewState) initJsBridge(jsBridge) -// EventBus.observe { -// Logger.d { -// "Received NavigationEvent" -// } -// } - FlowEventBus.events.filter { it is NavigationEvent }.collect { - Logger.d { - "Received NavigationEvent" - } - } } MaterialTheme { Box(Modifier.fillMaxSize()) { @@ -104,6 +94,16 @@ fun initWebView(webViewState: WebViewState) { } } -fun initJsBridge(webViewJsBridge: WebViewJsBridge) { +suspend fun initJsBridge(webViewJsBridge: WebViewJsBridge) { webViewJsBridge.register(GreetJsMessageHandler()) + // EventBus.observe { +// Logger.d { +// "Received NavigationEvent" +// } +// } + FlowEventBus.events.filter { it is NavigationEvent }.collect { + Logger.d { + "Received NavigationEvent" + } + } } diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt index 7932bbf7..3e262090 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt @@ -12,8 +12,8 @@ import com.multiplatform.webview.web.rememberWebViewState @Composable internal fun WebViewApp() { // WebViewSample() -// BasicWebViewSample() - BasicWebViewWithHTMLSample() + BasicWebViewSample() +// BasicWebViewWithHTMLSample() } @Composable diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt index 2daed0a7..b0b1462b 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/util/KLogger.kt @@ -23,7 +23,7 @@ internal object KLogger : Logger( // For iOS, it will not print out the log if the severity is upper than Debug in AS. fun info(msg: () -> String) { - i { msg() } + d { msg() } } } From 1b30695fc44cee4d85a8f5dacd22979c16c1b0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E9=93=AD=E6=9D=B0?= Date: Wed, 27 Dec 2023 11:03:43 +0800 Subject: [PATCH 65/65] feat:Ktlint format --- .../sample/jsbridge/CustomWebViewJsBridge.kt | 2 +- .../multiplatform/webview/util/getPlatform.kt | 2 +- .../com/multiplatform/webview/web/IWebView.kt | 5 ++-- .../multiplatform/webview/util/getPlatform.kt | 2 +- .../webview/jsbridge/WKJsMessageHandler.kt | 4 ++-- .../multiplatform/webview/util/getPlatform.kt | 2 +- .../multiplatform/webview/web/WKWebViewExt.kt | 23 ++++++++----------- 7 files changed, 19 insertions(+), 21 deletions(-) diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt index 6b4836ae..2bbe7fb2 100644 --- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt +++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/jsbridge/CustomWebViewJsBridge.kt @@ -9,4 +9,4 @@ class CustomWebViewJsBridge : WebViewJsBridge() { init { register(GreetJsMessageHandler()) } -} \ No newline at end of file +} diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/util/getPlatform.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/util/getPlatform.kt index fd81afa0..1d2f61ff 100644 --- a/webview/src/androidMain/kotlin/com/multiplatform/webview/util/getPlatform.kt +++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/util/getPlatform.kt @@ -2,4 +2,4 @@ package com.multiplatform.webview.util internal actual fun getPlatform(): Platform { return Platform.Android -} \ No newline at end of file +} diff --git a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt index 66c157e5..42a93b40 100644 --- a/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt +++ b/webview/src/commonMain/kotlin/com/multiplatform/webview/web/IWebView.kt @@ -158,7 +158,8 @@ interface IWebView { KLogger.d { "IWebView injectInitJS" } - val initJs = """ + val initJs = + """ window.kmpJsBridge = { callbacks: {}, callbackId: 0, @@ -183,7 +184,7 @@ interface IWebView { } } }; - """.trimIndent() + """.trimIndent() evaluateJavaScript(initJs) } diff --git a/webview/src/desktopMain/kotlin/com/multiplatform/webview/util/getPlatform.kt b/webview/src/desktopMain/kotlin/com/multiplatform/webview/util/getPlatform.kt index 24b70a29..cd92a262 100644 --- a/webview/src/desktopMain/kotlin/com/multiplatform/webview/util/getPlatform.kt +++ b/webview/src/desktopMain/kotlin/com/multiplatform/webview/util/getPlatform.kt @@ -2,4 +2,4 @@ package com.multiplatform.webview.util internal actual fun getPlatform(): Platform { return Platform.Desktop -} \ No newline at end of file +} diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt index 7d0cdfe8..986593b3 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/jsbridge/WKJsMessageHandler.kt @@ -15,7 +15,7 @@ class WKJsMessageHandler(private val webViewJsBridge: WebViewJsBridge) : NSObject() { override fun userContentController( userContentController: WKUserContentController, - didReceiveScriptMessage: WKScriptMessage + didReceiveScriptMessage: WKScriptMessage, ) { val body = didReceiveScriptMessage.body val method = didReceiveScriptMessage.name @@ -28,4 +28,4 @@ class WKJsMessageHandler(private val webViewJsBridge: WebViewJsBridge) : webViewJsBridge.dispatch(message) } } -} \ No newline at end of file +} diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/util/getPlatform.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/util/getPlatform.kt index c6bfc376..5255bfba 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/util/getPlatform.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/util/getPlatform.kt @@ -2,4 +2,4 @@ package com.multiplatform.webview.util internal actual fun getPlatform(): Platform { return Platform.IOS -} \ No newline at end of file +} diff --git a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewExt.kt b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewExt.kt index 0898d6b1..5ac96ab9 100644 --- a/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewExt.kt +++ b/webview/src/iosMain/kotlin/com/multiplatform/webview/web/WKWebViewExt.kt @@ -9,13 +9,14 @@ import platform.darwin.NSObject /** * Created By Kevin Zou On 2023/9/13 */ -val observedProgressList = listOf( - "estimatedProgress", - "title", - "URL", - "canGoBack", - "canGoForward", -) +val observedProgressList = + listOf( + "estimatedProgress", + "title", + "URL", + "canGoBack", + "canGoForward", + ) /** * Adds observers for the given properties @@ -48,9 +49,7 @@ fun WKWebView.removeObservers( } @OptIn(ExperimentalForeignApi::class) -fun WKWebView.addProgressObservers( - observer: NSObject, -) { +fun WKWebView.addProgressObservers(observer: NSObject) { this.addObservers( observer = observer, properties = observedProgressList, @@ -60,9 +59,7 @@ fun WKWebView.addProgressObservers( /** * Removes observers for the given properties */ -fun WKWebView.removeProgressObservers( - observer: NSObject, -) { +fun WKWebView.removeProgressObservers(observer: NSObject) { this.removeObservers( observer = observer, properties = observedProgressList,