|
|
@@ -13,9 +13,9 @@ import com.intellij.openapi.vfs.LocalFileSystem
|
|
|
import com.intellij.ui.jcef.JBCefBrowser
|
|
|
import com.intellij.ui.jcef.JBCefJSQuery
|
|
|
import org.cef.browser.CefBrowser
|
|
|
+import org.cef.browser.CefFrame
|
|
|
import org.cef.handler.CefLoadHandlerAdapter
|
|
|
import javax.swing.SwingUtilities
|
|
|
-import java.util.concurrent.atomic.AtomicBoolean
|
|
|
|
|
|
object IdeBridge {
|
|
|
private val logger = Logger.getInstance(IdeBridge::class.java)
|
|
|
@@ -32,50 +32,13 @@ object IdeBridge {
|
|
|
private val states = java.util.concurrent.ConcurrentHashMap<Project, ProjectState>()
|
|
|
|
|
|
fun install(browser: JBCefBrowser, project: Project) {
|
|
|
- // Create placeholder state without query - will be set up on page load
|
|
|
- val state = ProjectState(browser, null)
|
|
|
- states[project] = state
|
|
|
-
|
|
|
- val installed = AtomicBoolean(false)
|
|
|
-
|
|
|
- // Use load handler to ensure browser is fully ready before creating JBCefJSQuery
|
|
|
- // This fixes race condition on cold IDE start in IDEA 2024.3
|
|
|
- val loadHandler = object : CefLoadHandlerAdapter() {
|
|
|
- override fun onLoadingStateChange(
|
|
|
- cefBrowser: CefBrowser?,
|
|
|
- isLoading: Boolean,
|
|
|
- canGoBack: Boolean,
|
|
|
- canGoForward: Boolean
|
|
|
- ) {
|
|
|
- // Install once when loading starts (browser is ready)
|
|
|
- if (isLoading && installed.compareAndSet(false, true)) {
|
|
|
- SwingUtilities.invokeLater {
|
|
|
- doInstall(browser, project)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- browser.jbCefClient.addLoadHandler(loadHandler, browser.cefBrowser)
|
|
|
-
|
|
|
- // Also try immediate install in case page already loading
|
|
|
- SwingUtilities.invokeLater {
|
|
|
- if (installed.compareAndSet(false, true)) {
|
|
|
- doInstall(browser, project)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun doInstall(browser: JBCefBrowser, project: Project) {
|
|
|
+ // Create JBCefJSQuery immediately (requires JS_QUERY_POOL_SIZE set on client)
|
|
|
val q = try { JBCefJSQuery.create(browser) } catch (t: Throwable) {
|
|
|
logger.warn("Failed to create JBCefJSQuery", t)
|
|
|
null
|
|
|
}
|
|
|
|
|
|
- // Update state with query
|
|
|
- val oldState = states[project]
|
|
|
- val state = ProjectState(browser, q, oldState?.outbox ?: mutableListOf())
|
|
|
- state.ready = oldState?.ready ?: false
|
|
|
+ val state = ProjectState(browser, q)
|
|
|
states[project] = state
|
|
|
|
|
|
if (q != null) {
|
|
|
@@ -90,8 +53,27 @@ object IdeBridge {
|
|
|
}
|
|
|
} catch (_: Throwable) {}
|
|
|
}
|
|
|
+
|
|
|
+ val sendInvoke = try { q?.inject("String(json)") } catch (_: Throwable) { null } ?: "void 0"
|
|
|
+
|
|
|
+ // Use onLoadEnd to inject JS after page loads - handles race condition on Windows IDEA 2024.3
|
|
|
+ // Re-injects on every page load since JS context resets on navigation
|
|
|
+ val loadHandler = object : CefLoadHandlerAdapter() {
|
|
|
+ override fun onLoadEnd(cefBrowser: CefBrowser?, frame: CefFrame?, httpStatusCode: Int) {
|
|
|
+ // Only inject in main frame
|
|
|
+ if (frame?.isMain == true) {
|
|
|
+ SwingUtilities.invokeLater {
|
|
|
+ injectBridgeJs(browser, project, sendInvoke)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ browser.jbCefClient.addLoadHandler(loadHandler, browser.cefBrowser)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun injectBridgeJs(browser: JBCefBrowser, project: Project, sendInvoke: String) {
|
|
|
try {
|
|
|
- val sendInvoke = try { q?.inject("String(json)") } catch (_: Throwable) { null } ?: "void 0"
|
|
|
val js = (
|
|
|
"(function(){" +
|
|
|
"if(!window.ideBridge){" +
|
|
|
@@ -104,7 +86,11 @@ object IdeBridge {
|
|
|
"})();"
|
|
|
)
|
|
|
browser.cefBrowser.executeJavaScript(js, browser.cefBrowser.url, 0)
|
|
|
- SwingUtilities.invokeLater { flushOutbox(project) }
|
|
|
+ val state = states[project]
|
|
|
+ if (state != null) {
|
|
|
+ state.ready = true
|
|
|
+ flushOutbox(project)
|
|
|
+ }
|
|
|
} catch (t: Throwable) {
|
|
|
logger.warn("Failed to inject ideBridge", t)
|
|
|
}
|