mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-02 10:24:35 -05:00
* Switch to JCEF This is a full implementation, but it does not yet include downloading CEF as KCEF did * Download CEF automatically * Handle and propagate CEF init errors * Lint * Simplify jcef version extract * CEF: Download async * Copy StartupAsync to support handling errors Startup failures are simply swallowed, since they are recorded in the future, but there is no way to get that exception * CEF: Search for release file recursively On Mac, the file is buried a bit deeper than first level, like on Win and Linux * KcefWebViewProvider: Suppress deprecation We need to send those events, even if they are deprecated * Update readme * Optimize imports * Suggestion Co-authored-by: Mitchell Syer <syer10@users.noreply.github.com> * Refactor: stick to `Path` instead of `File` Also extracts the downloading of CEF to a separate method * Lint * Support disabling CEF Co-authored-by: Kolby Moroz Liebl <31669092+kolbyml@users.noreply.github.com> * Move JBR version to build constants Allows embedding into Manifest so docker can later extract the proper version * Create test to verify JCEF dependency matches downloaded JBR * Update server/src/main/kotlin/suwayomi/tachidesk/server/util/CEFManager.kt Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Fix compile, apply Path suggestions * Download progress * Lint * Fix exception on non-posix * Delete recursively Others can be non-empty * Support disabling CEF at will Not really functional, but nice * Fix test * Exclude masstest unless explicitly requested * PR-CI: Run tests * Add Changelog entry --------- Co-authored-by: Mitchell Syer <syer10@users.noreply.github.com> Co-authored-by: Kolby Moroz Liebl <31669092+kolbyml@users.noreply.github.com>
137 lines
4.1 KiB
Kotlin
137 lines
4.1 KiB
Kotlin
package xyz.nulldev.androidcompat.webkit
|
|
|
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
|
import kotlinx.serialization.Serializable
|
|
import kotlinx.serialization.json.Json
|
|
import org.cef.CefClient
|
|
import org.cef.browser.CefBrowser
|
|
import org.cef.browser.CefFrame
|
|
import org.cef.browser.CefMessageRouter
|
|
import org.cef.callback.CefQueryCallback
|
|
import org.cef.handler.CefMessageRouterHandlerAdapter
|
|
import kotlin.random.Random
|
|
|
|
private val logger = KotlinLogging.logger {}
|
|
private val jsHandler: MutableMap<CefClient, JsHandler> = mutableMapOf()
|
|
|
|
fun CefBrowser.evaluateJavaScript(
|
|
expression: String,
|
|
cb: (String?) -> Unit,
|
|
) = jsHandler[this.client]!!.eval(this, expression, cb)
|
|
|
|
fun CefBrowser.dispose() {
|
|
stopLoad()
|
|
setCloseAllowed()
|
|
close(true)
|
|
}
|
|
|
|
class JsHandler : CefMessageRouterHandlerAdapter {
|
|
private val handler: MutableMap<String, (String?) -> Unit> = mutableMapOf()
|
|
|
|
constructor(client: CefClient) {
|
|
val config = CefMessageRouter.CefMessageRouterConfig()
|
|
config.jsQueryFunction = QUERY_FN
|
|
config.jsCancelFunction = QUERY_CANCEL_FN
|
|
client.addMessageRouter(CefMessageRouter.create(config, this))
|
|
jsHandler[client] = this
|
|
}
|
|
|
|
fun eval(
|
|
frame: CefFrame,
|
|
expression: String,
|
|
cb: (String?) -> Unit,
|
|
) {
|
|
val id = Random.nextBytes(48).toHexString()
|
|
handler[id] = cb
|
|
frame.executeJavaScript(expression.toCode(id), "about:cef", 0)
|
|
}
|
|
|
|
fun eval(
|
|
browser: CefBrowser,
|
|
expression: String,
|
|
cb: (String?) -> Unit,
|
|
) {
|
|
val id = Random.nextBytes(48).toHexString()
|
|
handler[id] = cb
|
|
browser.executeJavaScript(expression.toCode(id), "about:cef", 0)
|
|
}
|
|
|
|
override fun onQuery(
|
|
browser: CefBrowser?,
|
|
frame: CefFrame?,
|
|
queryId: Long,
|
|
request: String?,
|
|
persistent: Boolean,
|
|
callback: CefQueryCallback?,
|
|
): Boolean {
|
|
super.onQuery(browser, frame, queryId, request, persistent, callback)
|
|
|
|
if (request != null) {
|
|
val invoke =
|
|
try {
|
|
Json.decodeFromString<FunctionCall>(request)
|
|
} catch (e: Exception) {
|
|
logger.warn(e) { "Invalid request received" }
|
|
return false
|
|
}
|
|
val handler = handler.remove(invoke.id) ?: return false
|
|
handler(invoke.result)
|
|
callback?.success("")
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
@Serializable
|
|
private data class FunctionCall(
|
|
val id: String,
|
|
val result: String? = null,
|
|
)
|
|
|
|
companion object {
|
|
const val QUERY_FN = "__\$_evalQuery"
|
|
const val QUERY_CANCEL_FN = "__\$_evalQueryCancel"
|
|
|
|
private fun Char.isLineBreak(): Boolean = this == '\n' || this == '\r'
|
|
|
|
private fun String.containsLineBreak(): Boolean =
|
|
this.any {
|
|
it.isLineBreak()
|
|
}
|
|
|
|
private fun String.asFunctionBody(): String =
|
|
let { expression ->
|
|
when {
|
|
expression.containsLineBreak() -> expression
|
|
expression.trim().startsWith("return", false) -> expression
|
|
else -> "return $expression"
|
|
}
|
|
}
|
|
|
|
private fun String.toCode(id: String): String =
|
|
"""
|
|
function payload() {
|
|
${this.asFunctionBody()}
|
|
}
|
|
|
|
try {
|
|
var result = payload();
|
|
|
|
window.${QUERY_FN}({
|
|
request: JSON.stringify({ id: "$id", result }),
|
|
onSuccess: function (response) {},
|
|
onFailure: function (error_code, error_message) {}
|
|
});
|
|
} catch (e) {
|
|
console.error("Failed to eval $id", e)
|
|
window.${QUERY_CANCEL_FN}({
|
|
request: JSON.stringify({ id: "$id", error: ""+e }),
|
|
onSuccess: function (response) {},
|
|
onFailure: function (error_code, error_message) {}
|
|
});
|
|
}
|
|
""".trimIndent()
|
|
}
|
|
}
|