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) val handlerFn = Function { 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 } } } }