Skip to content

Commit 198db70

Browse files
committed
Use kotlinx serialization for JsMessage parsing and added type validation in parseJsMessage
1 parent cda7e70 commit 198db70

5 files changed

Lines changed: 57 additions & 44 deletions

File tree

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ androidLibrary = { id = "com.android.library", version.ref = "androidGradlePlugi
3838
composeHotReload = { id = "org.jetbrains.compose.hot-reload", version.ref = "composeHotReload" }
3939
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
4040
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
41+
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
4142
kotlinAtomicfu = { id = "org.jetbrains.kotlin.plugin.atomicfu", version.ref = "kotlin" }
4243
kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
4344
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }

webview-compose/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
66
plugins {
77
alias(libs.plugins.androidLibrary)
88
alias(libs.plugins.kotlinMultiplatform)
9+
alias(libs.plugins.kotlinSerialization)
910
alias(libs.plugins.composeMultiplatform)
1011
alias(libs.plugins.composeCompiler)
1112
alias(libs.plugins.mavenPublish)
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package io.github.kdroidfilter.webview.jsbridge
22

3+
import kotlinx.serialization.Serializable
4+
35
/**
46
* Message dispatched from JS to native.
57
*
68
* `params` is expected to be a JSON string (API compatibility with compose-webview-multiplatform).
79
*/
10+
@Serializable
811
data class JsMessage(
912
val callbackId: Int,
1013
val methodName: String,
11-
val params: String,
14+
val params: String
1215
)
1316

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,45 @@
11
package io.github.kdroidfilter.webview.jsbridge
22

3+
import kotlinx.serialization.Serializable
34
import kotlinx.serialization.json.Json
4-
import kotlinx.serialization.json.jsonObject
5-
import kotlinx.serialization.json.jsonPrimitive
5+
import kotlinx.serialization.json.JsonElement
6+
import kotlinx.serialization.json.JsonPrimitive
67

78
private val jsBridgeJson = Json { ignoreUnknownKeys = true }
89

9-
internal fun parseJsMessage(raw: String): JsMessage? =
10-
runCatching {
11-
val obj = jsBridgeJson.parseToJsonElement(raw).jsonObject
12-
val callbackId = obj["callbackId"]?.jsonPrimitive?.content?.toIntOrNull() ?: -1
13-
val methodName = obj["methodName"]?.jsonPrimitive?.content ?: return null
14-
val params =
15-
obj["params"]?.jsonPrimitive?.content
16-
?: obj["params"]?.toString()
17-
?: ""
18-
JsMessage(callbackId = callbackId, methodName = methodName, params = params)
19-
}.getOrNull()
10+
@Serializable
11+
private data class ParsedJsBridgeMessage(
12+
val callbackId: Int? = null,
13+
val methodName: String? = null,
14+
val action: String? = null,
15+
val params: JsonElement? = null,
16+
val type: String? = null,
17+
)
2018

19+
internal fun parseJsMessage(
20+
raw: String,
21+
expectedType: String? = null,
22+
): JsMessage? = runCatching {
23+
val message = jsBridgeJson.decodeFromString<ParsedJsBridgeMessage>(raw)
24+
if (expectedType != null && message.type != expectedType) return null
25+
26+
val methodName = message.methodName ?: message.action ?: return null
27+
val isWasmBridgeMessage = message.action != null
28+
val params = message.params?.toJsMessageParams() ?: if (isWasmBridgeMessage) "{}" else ""
29+
val callbackId = message.callbackId ?: if (isWasmBridgeMessage) 0 else -1
30+
31+
JsMessage(
32+
callbackId = callbackId,
33+
methodName = methodName,
34+
params = params
35+
)
36+
}.getOrNull()
37+
38+
private fun JsonElement.toJsMessageParams(): String = if (
39+
this is JsonPrimitive &&
40+
isString
41+
) {
42+
content
43+
} else {
44+
toString()
45+
}

webview-compose/src/wasmJsMain/kotlin/io/github/kdroidfilter/webview/web/WebView.wasmJs.kt

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ package io.github.kdroidfilter.webview.web
33

44
import androidx.compose.runtime.*
55
import androidx.compose.ui.Modifier
6-
import io.github.kdroidfilter.webview.jsbridge.JsMessage
76
import io.github.kdroidfilter.webview.jsbridge.WebViewJsBridge
7+
import io.github.kdroidfilter.webview.jsbridge.parseJsMessage
88
import io.github.kdroidfilter.webview.setting.WebSettings
99
import io.github.kdroidfilter.webview.util.KLogger
1010
import kotlinx.browser.document
1111
import kotlinx.coroutines.launch
1212
import org.w3c.dom.HTMLIFrameElement
13+
import org.w3c.dom.MessageEvent
1314
import org.w3c.dom.Node
15+
import org.w3c.dom.events.Event
1416

1517
/**
1618
* Platform-specific parameters for the WebView factory in WebAssembly/JavaScript.
@@ -275,37 +277,18 @@ private fun setupJsBridgeForWasm(
275277
webViewJsBridge: WebViewJsBridge,
276278
webViewWrapper: WasmJsWebView
277279
): () -> Unit {
278-
val messageHandler: (org.w3c.dom.events.Event) -> Unit = { event ->
279-
val messageEvent = event as org.w3c.dom.MessageEvent
280+
val messageHandler: (Event) -> Unit = { event ->
281+
val messageEvent = event as MessageEvent
280282

281-
if (messageEvent.source == element.contentWindow && messageEvent.data != null) {
283+
if (
284+
messageEvent.source == element.contentWindow &&
285+
messageEvent.data != null
286+
) {
282287
try {
283-
val dataString = messageEvent.data.toString()
284-
285-
if (dataString.contains(webViewJsBridge.jsBridgeName) && dataString.startsWith("{")) {
286-
val actionPattern = """"action"\s*:\s*"([^"]*)"""".toRegex()
287-
val paramsPattern = """"params"\s*:\s*"((?:[^"\\]|\\.)*)"""".toRegex()
288-
val callbackPattern = """"callbackId"\s*:\s*(\d+)""".toRegex()
289-
290-
val actionMatch = actionPattern.find(dataString)
291-
val paramsMatch = paramsPattern.find(dataString)
292-
val callbackMatch = callbackPattern.find(dataString)
293-
294-
if (actionMatch != null) {
295-
val action = actionMatch.groupValues[1]
296-
val rawParams = paramsMatch?.groupValues?.get(1) ?: "{}"
297-
val params = rawParams.replace("\\\"", "\"").replace("\\\\", "\\")
298-
val callbackId = callbackMatch?.groupValues?.get(1)?.toIntOrNull() ?: 0
299-
300-
val jsMessage = JsMessage(
301-
callbackId = callbackId,
302-
methodName = action,
303-
params = params,
304-
)
305-
306-
webViewJsBridge.dispatch(jsMessage)
307-
}
308-
}
288+
parseJsMessage(
289+
raw = messageEvent.data.toString(),
290+
expectedType = webViewJsBridge.jsBridgeName,
291+
)?.let(webViewJsBridge::dispatch)
309292
} catch (e: Exception) {
310293
KLogger.e(
311294
t = e,

0 commit comments

Comments
 (0)