From 78c1a33da8b1c6dacc37bdc327aecdc4ae669429 Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Mon, 14 Jun 2021 13:26:41 -0400 Subject: [PATCH 1/5] Move outboundMessageCatcher to WebBrowser.kt --- src/main/kotlin/kweb/WebBrowser.kt | 71 +++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/kweb/WebBrowser.kt b/src/main/kotlin/kweb/WebBrowser.kt index 51a8f5f184..bf65ade5d1 100755 --- a/src/main/kotlin/kweb/WebBrowser.kt +++ b/src/main/kotlin/kweb/WebBrowser.kt @@ -6,6 +6,7 @@ import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import kweb.client.FunctionCall import kweb.client.HttpRequestInfo +import kweb.client.Server2ClientMessage import kweb.html.Document import kweb.html.HtmlDocumentSupplier import kweb.plugins.KwebPlugin @@ -14,6 +15,7 @@ import kweb.state.ReversibleFunction import kweb.util.pathQueryFragment import kweb.util.random import mu.KotlinLogging +import java.time.Instant import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger @@ -29,7 +31,7 @@ import kotlin.reflect.jvm.jvmName private val logger = KotlinLogging.logger {} -class WebBrowser(val sessionId: String, val httpRequestInfo: HttpRequestInfo, val kweb: Kweb) { +class WebBrowser(private val sessionId: String, val httpRequestInfo: HttpRequestInfo, val kweb: Kweb) { private val idCounter = AtomicInteger(0) @@ -52,6 +54,46 @@ class WebBrowser(val sessionId: String, val httpRequestInfo: HttpRequestInfo, va HtmlDocumentSupplier.appliedPlugins.map { it::class to it }.toMap() } + //TODO I think some of these things could be renamed for clarity. I think it is understandable as is, but there is room for improvement + enum class CatcherType { + EVENT, IMMEDIATE_EVENT, RENDER + } + data class OutboundMessageCatcher(var catcherType: CatcherType, val functionList: MutableList) + + /** + * Allow us to catch outbound messages temporarily and only for this thread. This is used for immediate + * execution of event handlers, see `Element.onImmediate` + */ + val outboundMessageCatcher: ThreadLocal = ThreadLocal.withInitial { null } + + /** + * Are outbound messages being cached for this thread (for example, because we're inside an immediateEvent callback block)? + */ + fun isCatchingOutbound() = outboundMessageCatcher.get()?.catcherType + + /** + * Execute a block of code in which any JavaScript sent to the browser during the execution of the block will be stored + * and returned by this function. + * + * The main use-case is recording changes made to the DOM within an onImmediate event callback so that these can be + * replayed in the browser when an event is triggered without a server round-trip. + */ + fun catchOutbound(catchingType: CatcherType, f: () -> Unit): List { + require(outboundMessageCatcher.get() == null) { "Can't nest withThreadLocalOutboundMessageCatcher()" } + + val jsList = ArrayList() + outboundMessageCatcher.set(OutboundMessageCatcher(catchingType, jsList)) + f() + outboundMessageCatcher.set(null) + return jsList + } + + fun batch(catchingType: CatcherType, f: () -> Unit) { + val caughtMessages = catchOutbound(catchingType, f) + val server2ClientMessage = Server2ClientMessage(sessionId, caughtMessages) + kweb.sendMessage(sessionId, server2ClientMessage) + } + @Suppress("UNCHECKED_CAST") internal fun

plugin(plugin: KClass): P { return (plugins[plugin] ?: error("Plugin $plugin is missing")) as P @@ -97,37 +139,35 @@ class WebBrowser(val sessionId: String, val httpRequestInfo: HttpRequestInfo, va } fun callJsFunction(jsBody: String, vararg args: JsonElement) { - cachedFunctions[jsBody]?.let { - val cachedFunctionCall = FunctionCall(jsId = it, arguments = listOf(*args)) - kweb.callJs(sessionId, cachedFunctionCall, jsBody) - } ?: run { + val server2ClientMessage = if (cachedFunctions[jsBody] != null) { + FunctionCall(jsId = cachedFunctions[jsBody], arguments = listOf(*args)) + } else { val cacheId = generateCacheId() val func = makeJsFunction(jsBody) //we add the user's unmodified js as a key and the cacheId as it's value in the hashmap cachedFunctions[jsBody] = cacheId //we send the modified js to the client to be cached there. //we don't cache the modified js on the server, because then we'd have to modify JS on the server, everytime we want to check the server's cache - val cacheAndCallFunction = FunctionCall(jsId = cacheId, js = func.js, parameters = func.params, + FunctionCall(jsId = cacheId, js = func.js, parameters = func.params, arguments = listOf(*args)) - kweb.callJs(sessionId, cacheAndCallFunction, jsBody) } + kweb.callJs(sessionId, server2ClientMessage, jsBody) } fun callJsFunctionWithCallback(jsBody: String, callbackId: Int, callback: (JsonElement) -> Unit, vararg args: JsonElement) { - cachedFunctions[jsBody]?.let { - val cachedFunctionCall = FunctionCall(jsId = it, arguments = listOf(*args), callbackId = callbackId) - kweb.callJsWithCallback(sessionId, cachedFunctionCall, jsBody, callback) - } ?: run { + val server2ClientMessage = if (cachedFunctions[jsBody] != null) { + FunctionCall(jsId = cachedFunctions[jsBody], arguments = listOf(*args), callbackId = callbackId) + } else { val cacheId = generateCacheId() val func = makeJsFunction(jsBody) //we add the user's unmodified js as a key and the cacheId as it's value in the hashmap cachedFunctions[jsBody] = cacheId //we send the modified js to the client to be cached there. //we don't cache the modified js on the server, because then we'd have to modify JS on the server, everytime we want to check the server's cache - val cacheAndCallFunction = FunctionCall(jsId = cacheId, js = func.js, parameters = func.params, - arguments = listOf(*args), callbackId = callbackId) - kweb.callJsWithCallback(sessionId, cacheAndCallFunction, jsBody, callback) + FunctionCall(jsId = cacheId, js = func.js, parameters = func.params, + arguments = listOf(*args), callbackId = callbackId) } + kweb.callJsWithCallback(sessionId, server2ClientMessage, jsBody, callback) } fun removeCallback(callbackId: Int) { @@ -135,7 +175,7 @@ class WebBrowser(val sessionId: String, val httpRequestInfo: HttpRequestInfo, va } suspend fun callJsFunctionWithResult(jsBody: String, vararg args: JsonElement): JsonElement { - require(kweb.isCatchingOutbound() == null) { + require(isCatchingOutbound() == null) { "You can not read the DOM inside a batched code block" } val callbackId = abs(random.nextInt()) @@ -190,6 +230,5 @@ class WebBrowser(val sessionId: String, val httpRequestInfo: HttpRequestInfo, va return change.pathQueryFragment } } ) - } From 2ee3f957e5323cb2b483398abb5ccc0a327cfeba Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Mon, 14 Jun 2021 19:53:03 -0400 Subject: [PATCH 2/5] Rewrite callJs() in Kweb.kt --- src/main/kotlin/kweb/Kweb.kt | 65 +++++------------------------------- 1 file changed, 9 insertions(+), 56 deletions(-) diff --git a/src/main/kotlin/kweb/Kweb.kt b/src/main/kotlin/kweb/Kweb.kt index 4577f93f3f..8d5c840b23 100755 --- a/src/main/kotlin/kweb/Kweb.kt +++ b/src/main/kotlin/kweb/Kweb.kt @@ -142,34 +142,10 @@ class Kweb private constructor( private var server: JettyApplicationEngine? = null - /** - * Are outbound messages being cached for this thread (for example, because we're inside an immediateEvent callback block)? - */ - fun isCatchingOutbound() = outboundMessageCatcher.get()?.catcherType - - /** - * Execute a block of code in which any JavaScript sent to the browser during the execution of the block will be stored - * and returned by this function. - * - * The main use-case is recording changes made to the DOM within an onImmediate event callback so that these can be - * replayed in the browser when an event is triggered without a server round-trip. - */ - fun catchOutbound(catchingType: CatcherType, f: () -> Unit): List { - require(outboundMessageCatcher.get() == null) { "Can't nest withThreadLocalOutboundMessageCatcher()" } - - val jsList = ArrayList() - outboundMessageCatcher.set(OutboundMessageCatcher(catchingType, jsList)) - f() - outboundMessageCatcher.set(null) - return jsList - } - - fun batch(sessionId: String, catchingType: CatcherType, f: () -> Unit) { - val caughtMessages = catchOutbound(catchingType, f) + fun sendMessage(sessionId: String, server2ClientMessage: Server2ClientMessage) { val wsClientData = clientState.getIfPresent(sessionId) ?: error("Client id $sessionId not found") //TODO, do we need to change lastModified here? callJs will set it when the functionCall is originally created. wsClientData.lastModified = Instant.now() - val server2ClientMessage = Server2ClientMessage(sessionId, caughtMessages) wsClientData.send(server2ClientMessage) } @@ -177,41 +153,28 @@ class Kweb private constructor( FunctionCalls that point to functions already in the cache will not contain the jsCode needed to create the debugToken So we have to pass it separately here. */ - fun callJs(sessionId: String, funcCall: FunctionCall, javascript: String) { + fun callJs(sessionId: String, funcCall: FunctionCall, debugInfo: DebugInfo?) { val wsClientData = clientState.getIfPresent(sessionId) ?: error("Client id $sessionId not found") wsClientData.lastModified = Instant.now() - val debugToken: String? = if(!debug) null else { + if(debug) { val dt = abs(random.nextLong()).toString(16) - wsClientData.debugTokens[dt] = DebugInfo(javascript, "executing", Throwable()) - dt - } - val outboundMessageCatcher = outboundMessageCatcher.get() - //TODO to make debugToken and shouldExecute in Server2ClientMessage vals instead of vars I decided to - //make new functionCall objects. I don't know if changing these vals to vars is worth the overhead of creating - //a new object though. So we may want to revert this change. - if (outboundMessageCatcher == null) { - val jsFuncCall = FunctionCall(debugToken, funcCall) - wsClientData.send(Server2ClientMessage(sessionId, jsFuncCall)) - } else { - logger.debug("Temporarily storing message for $sessionId in threadlocal outboundMessageCatcher") - outboundMessageCatcher.functionList.add(funcCall) - //Setting `shouldExecute` to false tells the server not to add this jsFunction to the client's cache, - //but to not actually run the code. This is used to pre-cache functions on initial page render. - val jsFuncCall = FunctionCall(debugToken, shouldExecute = false, funcCall) - wsClientData.send(Server2ClientMessage(sessionId, jsFuncCall)) + debugInfo?.let { + wsClientData.debugTokens[dt] = it + } } + wsClientData.send(Server2ClientMessage(sessionId, funcCall)) } fun callJsWithCallback(sessionId: String, funcCall: FunctionCall, - javascript: String, callback: (JsonElement) -> Unit) { + debugInfo: DebugInfo?, callback: (JsonElement) -> Unit) { val wsClientData = clientState.getIfPresent(sessionId) ?: error("Client id $sessionId not found") wsClientData.lastModified = Instant.now() funcCall.callbackId?.let { wsClientData.handlers[it] = callback } ?: error("Javascript function callback wasn't given a callbackId") - callJs(sessionId, funcCall, javascript) + callJs(sessionId, funcCall, debugInfo) } fun removeCallback(clientId: String, callbackId: Int) { @@ -480,17 +443,7 @@ class Kweb private constructor( } } - //TODO I think some of these things could be renamed for clarity. I think it is understandable as is, but there is room for improvement - enum class CatcherType { - EVENT, IMMEDIATE_EVENT, RENDER - } - data class OutboundMessageCatcher(var catcherType: CatcherType, val functionList: MutableList) - /** - * Allow us to catch outbound messages temporarily and only for this thread. This is used for immediate - * execution of event handlers, see `Element.onImmediate` - */ - val outboundMessageCatcher: ThreadLocal = ThreadLocal.withInitial { null } } From eacfc0de13c97b19c3d14f37790a213ad15a6455 Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Tue, 15 Jun 2021 14:19:02 -0400 Subject: [PATCH 3/5] Move debugToken logic to WebBrowser.kt --- src/main/kotlin/kweb/Kweb.kt | 6 +++--- src/main/kotlin/kweb/WebBrowser.kt | 31 +++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/kweb/Kweb.kt b/src/main/kotlin/kweb/Kweb.kt index 8d5c840b23..5733cae2a0 100755 --- a/src/main/kotlin/kweb/Kweb.kt +++ b/src/main/kotlin/kweb/Kweb.kt @@ -153,7 +153,7 @@ class Kweb private constructor( FunctionCalls that point to functions already in the cache will not contain the jsCode needed to create the debugToken So we have to pass it separately here. */ - fun callJs(sessionId: String, funcCall: FunctionCall, debugInfo: DebugInfo?) { + fun callJs(sessionId: String, funcCall: FunctionCall, debugInfo: DebugInfo? = null) { val wsClientData = clientState.getIfPresent(sessionId) ?: error("Client id $sessionId not found") wsClientData.lastModified = Instant.now() @@ -167,7 +167,7 @@ class Kweb private constructor( } fun callJsWithCallback(sessionId: String, funcCall: FunctionCall, - debugInfo: DebugInfo?, callback: (JsonElement) -> Unit) { + debugInfo: DebugInfo? = null, callback: (JsonElement) -> Unit) { val wsClientData = clientState.getIfPresent(sessionId) ?: error("Client id $sessionId not found") wsClientData.lastModified = Instant.now() @@ -354,7 +354,7 @@ class Kweb private constructor( //A plugin with an empty js string was breaking functionality. if (js != "") { val pluginFunction = FunctionCall(js = js) - callJs(kwebSessionId, pluginFunction, js) + callJs(kwebSessionId, pluginFunction) } } diff --git a/src/main/kotlin/kweb/WebBrowser.kt b/src/main/kotlin/kweb/WebBrowser.kt index bf65ade5d1..5c5c0d346c 100755 --- a/src/main/kotlin/kweb/WebBrowser.kt +++ b/src/main/kotlin/kweb/WebBrowser.kt @@ -15,7 +15,6 @@ import kweb.state.ReversibleFunction import kweb.util.pathQueryFragment import kweb.util.random import mu.KotlinLogging -import java.time.Instant import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger @@ -139,7 +138,7 @@ class WebBrowser(private val sessionId: String, val httpRequestInfo: HttpRequest } fun callJsFunction(jsBody: String, vararg args: JsonElement) { - val server2ClientMessage = if (cachedFunctions[jsBody] != null) { + val functionCall = if (cachedFunctions[jsBody] != null) { FunctionCall(jsId = cachedFunctions[jsBody], arguments = listOf(*args)) } else { val cacheId = generateCacheId() @@ -151,11 +150,22 @@ class WebBrowser(private val sessionId: String, val httpRequestInfo: HttpRequest FunctionCall(jsId = cacheId, js = func.js, parameters = func.params, arguments = listOf(*args)) } - kweb.callJs(sessionId, server2ClientMessage, jsBody) + val debugInfo: DebugInfo? = if(!kweb.debug) null else { + DebugInfo(jsBody, "executing", Throwable()) + } + val outboundMessageCatcher = outboundMessageCatcher.get() + if (outboundMessageCatcher == null) { + kweb.callJs(sessionId, functionCall, debugInfo) + } else { + logger.debug("Temporarily storing message for $sessionId in threadlocal outboundMessageCatcher") + outboundMessageCatcher.functionList.add(functionCall) + val dontExecute = FunctionCall(debugToken = functionCall.debugToken, shouldExecute = false, functionCall) + kweb.callJs(sessionId, dontExecute, debugInfo) + } } fun callJsFunctionWithCallback(jsBody: String, callbackId: Int, callback: (JsonElement) -> Unit, vararg args: JsonElement) { - val server2ClientMessage = if (cachedFunctions[jsBody] != null) { + val functionCall = if (cachedFunctions[jsBody] != null) { FunctionCall(jsId = cachedFunctions[jsBody], arguments = listOf(*args), callbackId = callbackId) } else { val cacheId = generateCacheId() @@ -167,7 +177,18 @@ class WebBrowser(private val sessionId: String, val httpRequestInfo: HttpRequest FunctionCall(jsId = cacheId, js = func.js, parameters = func.params, arguments = listOf(*args), callbackId = callbackId) } - kweb.callJsWithCallback(sessionId, server2ClientMessage, jsBody, callback) + val debugInfo: DebugInfo? = if(!kweb.debug) null else { + DebugInfo(jsBody, "executing", Throwable()) + } + val outboundMessageCatcher = outboundMessageCatcher.get() + if (outboundMessageCatcher == null) { + kweb.callJsWithCallback(sessionId, functionCall, debugInfo, callback) + } else { + logger.debug("Temporarily storing message for $sessionId in threadlocal outboundMessageCatcher") + outboundMessageCatcher.functionList.add(functionCall) + val dontExecute = FunctionCall(debugToken = functionCall.debugToken, shouldExecute = false, functionCall) + kweb.callJsWithCallback(sessionId, dontExecute, debugInfo, callback) + } } fun removeCallback(callbackId: Int) { From 957ecef35f1ae84efb2a8007ee65d73c43fdec57 Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Tue, 15 Jun 2021 14:19:41 -0400 Subject: [PATCH 4/5] Correct calls to batch() and isCatchingOutbound() --- src/main/kotlin/kweb/Element.kt | 8 ++++---- src/main/kotlin/kweb/ElementCreator.kt | 2 +- src/main/kotlin/kweb/html/ElementReader.kt | 2 +- src/main/kotlin/kweb/html/events/OnImmediateReceiver.kt | 2 +- src/main/kotlin/kweb/html/events/OnReceiver.kt | 5 +++-- src/main/kotlin/kweb/state/render.kt | 6 +++--- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/kweb/Element.kt b/src/main/kotlin/kweb/Element.kt index 80e357ae21..ced57bbcf2 100644 --- a/src/main/kotlin/kweb/Element.kt +++ b/src/main/kotlin/kweb/Element.kt @@ -90,7 +90,7 @@ open class Element( fun setAttribute(name: String, value: JsonPrimitive): Element { val htmlDoc = browser.htmlDocument.get() when { - browser.kweb.isCatchingOutbound() != null -> { + browser.isCatchingOutbound() != null -> { callJsFunction("document.getElementById({}).setAttribute({}, {})", JsonPrimitive(id), JsonPrimitive(name), value) } @@ -146,7 +146,7 @@ open class Element( fun removeAttribute(name: String): Element { when { - browser.kweb.isCatchingOutbound() != null -> { + browser.isCatchingOutbound() != null -> { callJsFunction("document.getElementById({}).removeAttribute", JsonPrimitive(id), JsonPrimitive(name)) } else -> { @@ -309,7 +309,7 @@ open class Element( val jsoupDoc = browser.htmlDocument.get() val setTextJS = """document.getElementById({}).textContent = {};""".trimIndent() when { - browser.kweb.isCatchingOutbound() != null -> { + browser.isCatchingOutbound() != null -> { callJsFunction(setTextJS, JsonPrimitive(id), JsonPrimitive(value)) } jsoupDoc != null -> { @@ -355,7 +355,7 @@ open class Element( document.getElementById({}).appendChild(ntn); """.trimIndent() when { - browser.kweb.isCatchingOutbound() != null -> { + browser.isCatchingOutbound() != null -> { callJsFunction(createTextNodeJs, JsonPrimitive(value), JsonPrimitive(id)) } jsoupDoc != null -> { diff --git a/src/main/kotlin/kweb/ElementCreator.kt b/src/main/kotlin/kweb/ElementCreator.kt index b4bc7d2d61..b750ebddb5 100755 --- a/src/main/kotlin/kweb/ElementCreator.kt +++ b/src/main/kotlin/kweb/ElementCreator.kt @@ -58,7 +58,7 @@ open class ElementCreator( val id: String = mutAttributes.computeIfAbsent("id") { JsonPrimitive("K" + browser.generateId()) }.content val htmlDoc = browser.htmlDocument.get() when { - parent.browser.kweb.isCatchingOutbound() != null -> { + parent.browser.isCatchingOutbound() != null -> { val createElementJs = """ let tag = {}; let attributes = {}; diff --git a/src/main/kotlin/kweb/html/ElementReader.kt b/src/main/kotlin/kweb/html/ElementReader.kt index c3943aa620..1ca45a4204 100755 --- a/src/main/kotlin/kweb/html/ElementReader.kt +++ b/src/main/kotlin/kweb/html/ElementReader.kt @@ -15,7 +15,7 @@ open class ElementReader(protected val receiver: WebBrowser, internal val elemen init { //TODO I'm not sure if we want to allow reading the DOM during a render or non immediate event //require(receiver.kweb.isCatchingOutbound() != Kweb.CatcherType.IMMEDIATE_EVENT) - require(receiver.kweb.isCatchingOutbound() == null) { + require(receiver.isCatchingOutbound() == null) { """ Reading the DOM when an outboundMessageCatcher is set is likely to have unintended consequences. Most likely you are trying to read the DOM within an `onImmediate {...}` block. diff --git a/src/main/kotlin/kweb/html/events/OnImmediateReceiver.kt b/src/main/kotlin/kweb/html/events/OnImmediateReceiver.kt index 7425bf3edc..4ebfeee96f 100644 --- a/src/main/kotlin/kweb/html/events/OnImmediateReceiver.kt +++ b/src/main/kotlin/kweb/html/events/OnImmediateReceiver.kt @@ -12,7 +12,7 @@ import kweb.util.KWebDSL @KWebDSL class OnImmediateReceiver>(internal val source: T) { fun event(eventName: String, callback: () -> Unit): T { - val caughtJsFunctions = source.browser.kweb.catchOutbound(Kweb.CatcherType.IMMEDIATE_EVENT) { + val caughtJsFunctions = source.browser.catchOutbound(WebBrowser.CatcherType.IMMEDIATE_EVENT) { callback() } val immediateJs = mutableListOf() diff --git a/src/main/kotlin/kweb/html/events/OnReceiver.kt b/src/main/kotlin/kweb/html/events/OnReceiver.kt index efd0a2a8db..764d8cd9d4 100644 --- a/src/main/kotlin/kweb/html/events/OnReceiver.kt +++ b/src/main/kotlin/kweb/html/events/OnReceiver.kt @@ -4,6 +4,7 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.serializer import kweb.Kweb +import kweb.WebBrowser import kweb.util.KWebDSL import mu.KotlinLogging import java.util.concurrent.ConcurrentHashMap @@ -29,8 +30,8 @@ class OnReceiver>(val source: T, private val retrieveJs: S return event(eventName, eventPropertyNames) { propertiesAsElement -> val props = Json.decodeFromJsonElement(serializer, propertiesAsElement) try { - if (source.browser.kweb.isCatchingOutbound() == null) { - source.browser.kweb.batch(source.browser.sessionId, Kweb.CatcherType.EVENT) { + if (source.browser.isCatchingOutbound() == null) { + source.browser.batch(WebBrowser.CatcherType.EVENT) { callback(props) } } diff --git a/src/main/kotlin/kweb/state/render.kt b/src/main/kotlin/kweb/state/render.kt index abdb022523..b9832333d7 100755 --- a/src/main/kotlin/kweb/state/render.kt +++ b/src/main/kotlin/kweb/state/render.kt @@ -35,8 +35,8 @@ fun ElementCreator<*>.render( fun eraseAndRender() { do { - if (parent.browser.kweb.isCatchingOutbound() == null) { - parent.browser.kweb.batch(parent.browser.sessionId, Kweb.CatcherType.RENDER) { + if (parent.browser.isCatchingOutbound() == null) { + parent.browser.batch(WebBrowser.CatcherType.RENDER) { containerElement.removeChildren() // REMOVE ALL ELEMENTS BETWEEN startSpan and endSpan containerElement.new { previousElementCreator.getAndSet(this)?.cleanup() @@ -107,7 +107,7 @@ fun ElementCreator<*>.toVar(shoebox: Shoebox, key: String): KVar val value = shoebox[key] ?: throw NoSuchElementException("Key $key not found") val w = KVar(value) w.addListener { _, n -> - require(this.browser.kweb.isCatchingOutbound() != Kweb.CatcherType.IMMEDIATE_EVENT) { + require(this.browser.isCatchingOutbound() != WebBrowser.CatcherType.IMMEDIATE_EVENT) { """You appear to be modifying Shoebox state from within an onImmediate callback, which |should only make simple modifications to the DOM.""".trimMargin() } From d64b2ed9b72351ecca849d2e033a0a955e53e1db Mon Sep 17 00:00:00 2001 From: Derek Briggs Date: Tue, 15 Jun 2021 14:57:52 -0400 Subject: [PATCH 5/5] Add comments. --- src/main/kotlin/kweb/WebBrowser.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/kweb/WebBrowser.kt b/src/main/kotlin/kweb/WebBrowser.kt index 5c5c0d346c..3a7a951dbc 100755 --- a/src/main/kotlin/kweb/WebBrowser.kt +++ b/src/main/kotlin/kweb/WebBrowser.kt @@ -159,8 +159,11 @@ class WebBrowser(private val sessionId: String, val httpRequestInfo: HttpRequest } else { logger.debug("Temporarily storing message for $sessionId in threadlocal outboundMessageCatcher") outboundMessageCatcher.functionList.add(functionCall) - val dontExecute = FunctionCall(debugToken = functionCall.debugToken, shouldExecute = false, functionCall) - kweb.callJs(sessionId, dontExecute, debugInfo) + //funcToCache is a functionCall object with the shouldExecute parameter set to false + //This tells the client to cache the function call, but to not run the code. + //I believe this is used to cache js code that is called in events. + val funcToCache = FunctionCall(debugToken = functionCall.debugToken, shouldExecute = false, functionCall) + kweb.callJs(sessionId, funcToCache, debugInfo) } } @@ -186,8 +189,11 @@ class WebBrowser(private val sessionId: String, val httpRequestInfo: HttpRequest } else { logger.debug("Temporarily storing message for $sessionId in threadlocal outboundMessageCatcher") outboundMessageCatcher.functionList.add(functionCall) - val dontExecute = FunctionCall(debugToken = functionCall.debugToken, shouldExecute = false, functionCall) - kweb.callJsWithCallback(sessionId, dontExecute, debugInfo, callback) + //funcToCache is a functionCall object with the shouldExecute parameter set to false + //This tells the client to cache the function call, but to not run the code. + //I believe this is used to cache js code that is called in events. + val funcToCache = FunctionCall(debugToken = functionCall.debugToken, shouldExecute = false, functionCall) + kweb.callJsWithCallback(sessionId, funcToCache, debugInfo, callback) } }