Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Wait for browser to load before sending messages
- Add retries to forward search

## 0.18.2
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pluginName = PDF Viewer
group = com.firsttimeinforever.intellij.pdf.viewer
version = 0.18.3-alpha.1
version = 0.18.3-alpha.2

# To run with AS 2021.3.1 Canary 5
#platformVersion = 213.6777.52
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import com.firsttimeinforever.intellij.pdf.viewer.tex.SynctexViewCoordinates
import kotlinx.serialization.Serializable

object BrowserMessages {
@Serializable
class BrowserReady

@Serializable
data class InitialViewProperties(val properties: ViewProperties)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.firsttimeinforever.intellij.pdf.viewer.jcef

import com.firsttimeinforever.intellij.pdf.viewer.jcef.JcefUtils.addLoadEndHandler
import com.firsttimeinforever.intellij.pdf.viewer.BrowserMessages
import com.firsttimeinforever.intellij.pdf.viewer.jcef.JcefUtils.addLoadHandler
import com.firsttimeinforever.intellij.pdf.viewer.jcef.JcefUtils.addUnitHandler
import com.firsttimeinforever.intellij.pdf.viewer.jcef.JcefUtils.executeJavaScript
import com.firsttimeinforever.intellij.pdf.viewer.mpi.MessagePipe
Expand All @@ -12,27 +13,59 @@ import com.intellij.openapi.diagnostic.logger
import com.intellij.ui.jcef.JBCefBrowser
import com.intellij.ui.jcef.JBCefBrowserBase
import com.intellij.ui.jcef.JBCefJSQuery
import org.cef.browser.CefBrowser
import org.cef.browser.CefFrame
import org.cef.handler.CefLoadHandlerAdapter
import org.cef.network.CefRequest

class JcefBrowserMessagePipe(private val browser: JBCefBrowser) : MessagePipe {
private val query = checkNotNull(JBCefJSQuery.create(browser as JBCefBrowserBase))
private val receiveSubscribers = hashMapOf<String, MutableList<MessageReceivedHandler>>()
private val pendingMessages = ArrayDeque<String>()
private val lock = Any()
@Volatile
private var isBrowserReady = false

init {
query.addUnitHandler(::receiveHandler)
browser.addLoadEndHandler {
// FIXME: Implement generic way of packing/unpacking messages into strings
val code = query.inject("raw")
browser.executeJavaScript("window['$ideSendFunctionName'] = raw => $code;")
browser.executeJavaScript("window.dispatchEvent(new Event('IdeReady'));")
}
browser.addLoadHandler(object : CefLoadHandlerAdapter() {
override fun onLoadStart(browser: CefBrowser, frame: CefFrame, transitionType: CefRequest.TransitionType) {
if (!frame.isMain) return
synchronized(lock) {
isBrowserReady = false
}
}

override fun onLoadEnd(browser: CefBrowser, frame: CefFrame, httpStatusCode: Int) {
if (!frame.isMain) return
val code = query.inject("raw")
this@JcefBrowserMessagePipe.browser.executeJavaScript("window['$ideSendFunctionName'] = raw => $code;")
this@JcefBrowserMessagePipe.browser.executeJavaScript("window.dispatchEvent(new Event('IdeReady'));")
}
})
}

private fun receiveHandler(raw: String) {
logger.debug(raw)
val (type, data) = MessagePipeSupport.MessagePacker.unpack(raw)
if (type == BrowserMessages.BrowserReady::class.simpleName) {
onBrowserReady()
return
}
callSubscribers(type, data)
}

private fun onBrowserReady() {
val messagesToFlush = mutableListOf<String>()
synchronized(lock) {
isBrowserReady = true
while (pendingMessages.isNotEmpty()) {
messagesToFlush += pendingMessages.removeFirst()
}
}
messagesToFlush.forEach { browser.executeJavaScript(it) }
}

private fun callSubscribers(type: String, data: String) {
when (val subscribers = receiveSubscribers[type]) {
null -> logger.warn("No subscribers for $type!\nAttached data: $data")
Expand All @@ -43,7 +76,14 @@ class JcefBrowserMessagePipe(private val browser: JBCefBrowser) : MessagePipe {
override fun send(type: String, data: String) {
val raw = MessagePipeSupport.MessagePacker.pack(type, data)
logger.debug("Sending message: $raw")
browser.executeJavaScript("$browserSendFunctionName('$raw')")
val messageCall = "$browserSendFunctionName('$raw')"
synchronized(lock) {
if (!isBrowserReady) {
pendingMessages.addLast(messageCall)
return
}
}
browser.executeJavaScript(messageCall)
}

override fun subscribe(type: String, handler: MessageReceivedHandler) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.firsttimeinforever.intellij.pdf.viewer.jcef

import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.util.registry.Registry
import com.intellij.ui.jcef.JBCefBrowser
import com.intellij.ui.jcef.JBCefJSQuery
import org.cef.CefSettings
Expand All @@ -12,8 +10,6 @@ import org.cef.browser.CefFrame
import org.cef.handler.CefDisplayHandlerAdapter
import org.cef.handler.CefLoadHandler
import org.cef.handler.CefLoadHandlerAdapter
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

internal object JcefUtils {
@Suppress("NOTHING_TO_INLINE")
Expand All @@ -30,39 +26,6 @@ internal object JcefUtils {
})
}

private class LatchedLoadEndHandler(private val latch: CountDownLatch): CefLoadHandlerAdapter() {
override fun onLoadError(
browser: CefBrowser?,
frame: CefFrame?,
errorCode: CefLoadHandler.ErrorCode?,
errorText: String?,
failedUrl: String?
) {
logger.warn("Failed to load page.\n\tUrl: $failedUrl\n\tError text: $errorText")
latch.countDown()
}

override fun onLoadEnd(browser: CefBrowser?, frame: CefFrame?, httpStatusCode: Int) {
latch.countDown()
}
}

fun JBCefBrowser.invokeAndWaitForLoadEnd(block: () -> Unit) {
ApplicationManager.getApplication().invokeAndWait {
val latch = CountDownLatch(1)
val handler = LatchedLoadEndHandler(latch)
block()
try {
latch.countDown()
latch.await(loadLatchTimeout, TimeUnit.MILLISECONDS)
} catch (exception: Throwable) {
logger.error(exception)
} finally {
jbCefClient.removeLoadHandler(handler, cefBrowser)
}
}
}

@Suppress("NOTHING_TO_INLINE")
inline fun CefBrowser.executeJavaScript(code: String) {
executeJavaScript(code, null, 0)
Expand Down Expand Up @@ -132,8 +95,5 @@ internal object JcefUtils {
}
}

private val loadLatchTimeout: Long
get() = Registry.intValue("pdf.viewer.jcefLatchTimeout", 10000).toLong()

private val logger = thisLogger()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.firsttimeinforever.intellij.pdf.viewer.IdeMessages
import com.firsttimeinforever.intellij.pdf.viewer.jcef.JcefBrowserMessagePipe
import com.firsttimeinforever.intellij.pdf.viewer.jcef.JcefUtils.addConsoleMessageListener
import com.firsttimeinforever.intellij.pdf.viewer.jcef.JcefUtils.createDefaultConsoleMessageListener
import com.firsttimeinforever.intellij.pdf.viewer.jcef.JcefUtils.invokeAndWaitForLoadEnd
import com.firsttimeinforever.intellij.pdf.viewer.jcef.PdfStaticServer
import com.firsttimeinforever.intellij.pdf.viewer.model.*
import com.firsttimeinforever.intellij.pdf.viewer.model.ViewThemeUtils.create
Expand Down Expand Up @@ -176,10 +175,8 @@ class PdfJcefPreviewController(val project: Project, val virtualFile: VirtualFil
tryToPreserveState -> buildUrlWithState(base, viewState)
else -> "$base#${createPageModeParameter(PdfViewerSettings.instance.defaultSidebarViewMode)}"
}
browser.invokeAndWaitForLoadEnd {
logger.debug("Loading url $url")
browser.loadURL(url)
}
logger.debug("Loading url $url")
browser.loadURL(url)
} catch(exception: Throwable) {
logger.error(exception)
} finally {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.firsttimeinforever.intellij.pdf.viewer.application

import com.firsttimeinforever.intellij.pdf.viewer.BrowserMessages
import com.firsttimeinforever.intellij.pdf.viewer.mpi.MessagePipe
import com.firsttimeinforever.intellij.pdf.viewer.mpi.MessagePipe.Companion.browserSendFunctionName
import com.firsttimeinforever.intellij.pdf.viewer.mpi.MessagePipe.Companion.ideSendFunctionName
import com.firsttimeinforever.intellij.pdf.viewer.mpi.MessagePipeSupport
import com.firsttimeinforever.intellij.pdf.viewer.mpi.MessagePipeSupport.send
import com.firsttimeinforever.intellij.pdf.viewer.mpi.MessageReceivedHandler
import kotlinx.browser.window
import org.w3c.dom.get
Expand All @@ -18,6 +20,7 @@ class BrowserMessagePipe : MessagePipe {
console.log()
callSubscribers(type, data)
}
send(BrowserMessages.BrowserReady())
}

private fun callSubscribers(type: String, data: String) {
Expand Down
Loading