Files
harmness-browser/src/main/kotlin/com/harmness/toolwindow/EmbeddedJsBridge.kt
T

86 lines
3.5 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.harmness.toolwindow
import com.intellij.openapi.Disposable
import com.intellij.openapi.diagnostic.Logger
import java.lang.reflect.InvocationTargetException
import java.util.function.Function
import javax.swing.JComponent
/**
* JCEF JS↔Java 桥的反射封装。
*
* 负责两件事:
* 1. 通过反射创建 JBCefJSQuery 并注册 handler(把网页发来的字符串请求转发给 [onRequest]
* 2. 通过反射调用 JBCefJSQuery.inject() 生成注入到页面的 JS 代码
*
* 与 [EmbeddedBrowser] 一样,编译期零 JCEF 依赖。类缺失/调用失败时
* 仅返回 null / 空串,不影响浏览器主体功能。
*/
class EmbeddedJsBridge private constructor(
private val queryInstance: Any,
private val onRequest: (String) -> Any?
) {
/**
* 返回可注入到页面 JS 的代码片段(调用插件 handler),
* 失败返回空串。
*/
fun injectJs(): String = try {
val m = queryInstance.javaClass.getMethod(
"inject", String::class.java, String::class.java, String::class.java
)
(m.invoke(queryInstance, "payload.query",
"function(response){ if (payload.callbacks && payload.callbacks.onSuccess) payload.callbacks.onSuccess(response); }",
"function(code,msg){ if (payload.callbacks && payload.callbacks.onFailure) payload.callbacks.onFailure(code,msg); }"
) as? String) ?: ""
} catch (t: Throwable) {
log.warn("injectJs failed", t)
""
}
fun dispose() {
try {
queryInstance.javaClass.getMethod("dispose").invoke(queryInstance)
} catch (t: Throwable) {
log.warn("bridge dispose failed", t)
}
}
companion object {
private val log = Logger.getInstance(EmbeddedJsBridge::class.java)
/**
* 为已创建的 [browser] 建立 JS 桥。
* @return 成功返回桥对象;失败返回 null(浏览器仍可用,仅网页无法调用插件 API)
*/
fun create(browser: EmbeddedBrowser, onRequest: (String) -> Any?): EmbeddedJsBridge? {
try {
val queryClass = Class.forName("com.intellij.ui.jcef.JBCefJSQuery")
// create(JBCefBrowserBase) — 用可赋值参数类型查找
val browserInst = browser.instance
val createMethod = queryClass.methods.firstOrNull { m ->
m.name == "create" && m.parameterCount == 1 &&
m.parameterTypes[0].isAssignableFrom(browserInst.javaClass)
} ?: return null
val queryInstance = createMethod.invoke(null, browserInst)
// addHandler(java.util.function.Function<String, Response>)
val handlerFn = Function<String, Any?> { req -> onRequest(req ?: "") }
val addMethod = queryClass.methods.firstOrNull { m ->
m.name == "addHandler" && m.parameterCount == 1 &&
m.parameterTypes[0] == Function::class.java
} ?: return null
addMethod.invoke(queryInstance, handlerFn)
return EmbeddedJsBridge(queryInstance, onRequest)
} catch (t: Throwable) {
val real = when (t) {
is InvocationTargetException -> t.targetException ?: t
else -> t
}
log.warn("EmbeddedJsBridge.create failed: ${real.javaClass.simpleName}: ${real.message}", real)
return null
}
}
}
}