From 4254ce1d9ccc593c230415da1722b64f5e9184b2 Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 13:40:53 +0300 Subject: [PATCH 01/11] Add a websocket data interceptor examples for Java and Kotlin --- .../examples/WebSocketDataInterceptor.java | 112 ++++++++++++++++++ .../examples/WebSocketDataInterceptor.kt | 104 ++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java create mode 100644 examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt diff --git a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java new file mode 100644 index 0000000..531227e --- /dev/null +++ b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java @@ -0,0 +1,112 @@ +/* + * Copyright 2025, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.teamdev.jxbrowser.examples; + +import static com.teamdev.jxbrowser.engine.RenderingMode.OFF_SCREEN; + +import com.teamdev.jxbrowser.browser.callback.InjectJsCallback; +import com.teamdev.jxbrowser.engine.Engine; +import com.teamdev.jxbrowser.js.JsAccessible; +import com.teamdev.jxbrowser.js.JsObject; + +/** + * This example demonstrates how to intercept web socket data by using JS-Java + * bridget capabilities. + */ +public class WebSocketDataInterceptor { + private static final String JAVA_SCRIPT = """ + var oldSocket = window.WebSocket; + window.WebSocket = function (url){ + var socket = new oldSocket(url); + socket.onopen = () => { + window.websocketCallback.socketOpened(socket); + this.onopen(); + }; + socket.onmessage = (message) => { + window.websocketCallback.messageReceived(socket,message.data); + this.onmessage(message); + }; + var onclose = socket.onclose; + socket.onclose = (closeEvent) => { + this.onclose(); + window.websocketCallback.socketClosed(closeEvent); + this.close(closeEvent); + }; + this.close = (event)=> {socket.close();}; + this.send = (data) => { + window.websocketCallback.dataSend(socket,data); + socket.send(data); + }; + };"""; + + public static void main(String[] args) { + try (var engine = Engine.newInstance(OFF_SCREEN)) { + var browser = engine.newBrowser(); + browser.set(InjectJsCallback.class, params -> { + var frame = params.frame(); + JsObject jsObject = frame.executeJavaScript("window"); + if (jsObject != null) { + // Inject Java object into JavaScript and associate it + // with the "window.java" JavaScript property. + jsObject.putProperty("websocketCallback", new WebSocketCallback()); + } + // Call the public method of the injected Java object from JS. + frame.executeJavaScript(JAVA_SCRIPT); + return InjectJsCallback.Response.proceed(); + }); + + browser.navigation().loadUrlAndWait("https://www.teamdev.com/jxbrowser#features"); + } + } + + /** + * A JS-accessible class used as web socket events listener in JS. + * + *

The class is marked with [JsAccessible] annotation to tell JavaScript + * that all public methods of the injected object can be invoked from + * the JavaScript side. + * + *

Please note, only public classes and static nested classes can be injected + * into JavaScript. + */ + @JsAccessible + private static class WebSocketCallback { + @SuppressWarnings("unused") // To be called from JavaScript. + public void socketClosed(JsObject closeEvent) { + System.out.println("WebSocketCallback.socketClosed"); + } + + @SuppressWarnings("unused") // To be called from JavaScript. + public void messageReceived(JsObject socket, Object data) { + System.out.println("WebSocketCallback.messageReceived: " + data); + } + + @SuppressWarnings("unused") // To be called from JavaScript. + public void socketOpened(JsObject socket) { + System.out.println("WebSocketCallback.socketOpened"); + } + + @SuppressWarnings("unused") // To be called from JavaScript. + public void dataSend(JsObject socket, Object data) { + System.out.println("WebSocketCallback.dataSend: " + data); + } + } +} diff --git a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt new file mode 100644 index 0000000..37cbc6a --- /dev/null +++ b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2025, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.teamdev.jxbrowser.examples + +import com.teamdev.jxbrowser.browser.callback.InjectJsCallback +import com.teamdev.jxbrowser.dsl.Engine +import com.teamdev.jxbrowser.dsl.register +import com.teamdev.jxbrowser.engine.RenderingMode +import com.teamdev.jxbrowser.js.JsAccessible +import com.teamdev.jxbrowser.js.JsObject + +private const val JAVA_SCRIPT = ("var oldSocket = window.WebSocket;\n" + + " window.WebSocket = function (url){\n" + + " var socket = new oldSocket(url);\n" + + " socket.onopen = () => {\n" + + " window.websocketCallback.socketOpened(socket);\n" + + " this.onopen();\n" + + " };\n" + + " socket.onmessage = (message) => {\n" + + " window.websocketCallback.messageReceived(socket,message.data);\n" + + " this.onmessage(message);\n" + + " };\n" + + " var onclose = socket.onclose;\n" + + " socket.onclose = (closeEvent) => {\n" + + " this.onclose();\n" + + " window.websocketCallback.socketClosed(closeEvent);\n" + + " this.close(closeEvent);\n" + + " };\n" + + " this.close = (event)=> {socket.close();};\n" + + " this.send = (data) => {\n" + + " window.websocketCallback.dataSend(socket,data);\n" + + " socket.send(data);\n" + + " };\n" + + " };") + +/** + * This example demonstrates how to intercept web socket data by using JS-Java + * bridget capabilities. + */ +fun main() { + val engine = Engine(RenderingMode.OFF_SCREEN) + val browser = engine.newBrowser() + browser.register(InjectJsCallback { params -> + val frame = params.frame() + val jsWindow = frame.executeJavaScript("window")!! + jsWindow.putProperty("websocketCallback", WebSocketCallback()) + frame.executeJavaScript(JAVA_SCRIPT) + InjectJsCallback.Response.proceed() + }) + browser.navigation() + .loadUrlAndWait("https://www.teamdev.com/jxbrowser#features") + engine.close() +} + +/** + * A JS-accessible class used as web socket events listener in JS. + * + * The class is marked with [JsAccessible] annotation to tell JavaScript + * that all public methods of the injected object can be invoked from + * the JavaScript side. + * + * Please note, only public classes and static nested classes can be injected + * into JavaScript. + */ +@JsAccessible +class WebSocketCallback { + + @Suppress("unused") // To be called from JavaScript. + fun socketClosed(closeEvent: JsObject?) { + println("WebSocketCallback.socketClosed: $closeEvent") + } + + @Suppress("unused") // To be called from JavaScript. + fun messageReceived(socket: JsObject?, data: Any) { + println("WebSocketCallback.messageReceived: $socket $data") + } + + @Suppress("unused") // To be called from JavaScript. + fun socketOpened(socket: JsObject?) { + println("WebSocketCallback.socketOpened : $socket") + } + + @Suppress("unused") // To be called from JavaScript. + fun dataSend(socket: JsObject?, data: Any) { + println("WebSocketCallback.dataSend: $socket $data") + } +} From e0af385ffd6928b82872aec8981e3243afaa6b79 Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 13:43:34 +0300 Subject: [PATCH 02/11] Place the JAVA_SCRIPT into the EOF --- .../examples/WebSocketDataInterceptor.kt | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt index 37cbc6a..0749f1f 100644 --- a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt +++ b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt @@ -26,30 +26,6 @@ import com.teamdev.jxbrowser.engine.RenderingMode import com.teamdev.jxbrowser.js.JsAccessible import com.teamdev.jxbrowser.js.JsObject -private const val JAVA_SCRIPT = ("var oldSocket = window.WebSocket;\n" - + " window.WebSocket = function (url){\n" - + " var socket = new oldSocket(url);\n" - + " socket.onopen = () => {\n" - + " window.websocketCallback.socketOpened(socket);\n" - + " this.onopen();\n" - + " };\n" - + " socket.onmessage = (message) => {\n" - + " window.websocketCallback.messageReceived(socket,message.data);\n" - + " this.onmessage(message);\n" - + " };\n" - + " var onclose = socket.onclose;\n" - + " socket.onclose = (closeEvent) => {\n" - + " this.onclose();\n" - + " window.websocketCallback.socketClosed(closeEvent);\n" - + " this.close(closeEvent);\n" - + " };\n" - + " this.close = (event)=> {socket.close();};\n" - + " this.send = (data) => {\n" - + " window.websocketCallback.dataSend(socket,data);\n" - + " socket.send(data);\n" - + " };\n" - + " };") - /** * This example demonstrates how to intercept web socket data by using JS-Java * bridget capabilities. @@ -102,3 +78,27 @@ class WebSocketCallback { println("WebSocketCallback.dataSend: $socket $data") } } + +private const val JAVA_SCRIPT = ("var oldSocket = window.WebSocket;\n" + + " window.WebSocket = function (url){\n" + + " var socket = new oldSocket(url);\n" + + " socket.onopen = () => {\n" + + " window.websocketCallback.socketOpened(socket);\n" + + " this.onopen();\n" + + " };\n" + + " socket.onmessage = (message) => {\n" + + " window.websocketCallback.messageReceived(socket,message.data);\n" + + " this.onmessage(message);\n" + + " };\n" + + " var onclose = socket.onclose;\n" + + " socket.onclose = (closeEvent) => {\n" + + " this.onclose();\n" + + " window.websocketCallback.socketClosed(closeEvent);\n" + + " this.close(closeEvent);\n" + + " };\n" + + " this.close = (event)=> {socket.close();};\n" + + " this.send = (data) => {\n" + + " window.websocketCallback.dataSend(socket,data);\n" + + " socket.send(data);\n" + + " };\n" + + " };") From 18dad41d5100455a2f7fc1f2d5d7611bfb2bb8bb Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 13:50:33 +0300 Subject: [PATCH 03/11] Rename dataSend to beforeSendData --- .../jxbrowser/examples/WebSocketDataInterceptor.java | 6 +++--- .../teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java index 531227e..5aa44e3 100644 --- a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java +++ b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java @@ -52,7 +52,7 @@ public class WebSocketDataInterceptor { }; this.close = (event)=> {socket.close();}; this.send = (data) => { - window.websocketCallback.dataSend(socket,data); + window.websocketCallback.beforeSendData(socket,data); socket.send(data); }; };"""; @@ -105,8 +105,8 @@ public void socketOpened(JsObject socket) { } @SuppressWarnings("unused") // To be called from JavaScript. - public void dataSend(JsObject socket, Object data) { - System.out.println("WebSocketCallback.dataSend: " + data); + public void beforeSendData(JsObject socket, Object data) { + System.out.println("WebSocketCallback.beforeSendData: " + data); } } } diff --git a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt index 0749f1f..28e8362 100644 --- a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt +++ b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt @@ -74,8 +74,8 @@ class WebSocketCallback { } @Suppress("unused") // To be called from JavaScript. - fun dataSend(socket: JsObject?, data: Any) { - println("WebSocketCallback.dataSend: $socket $data") + fun beforeSendData(socket: JsObject?, data: Any) { + println("WebSocketCallback.beforeSendData: $socket $data") } } @@ -98,7 +98,7 @@ private const val JAVA_SCRIPT = ("var oldSocket = window.WebSocket;\n" + " };\n" + " this.close = (event)=> {socket.close();};\n" + " this.send = (data) => {\n" - + " window.websocketCallback.dataSend(socket,data);\n" + + " window.websocketCallback.beforeSendData(socket,data);\n" + " socket.send(data);\n" + " };\n" + " };") From 152625264c28d3f615162aa42f49b621ae0d44f7 Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 13:52:00 +0300 Subject: [PATCH 04/11] Fix typo in WebSocketDataInterceptor.kt doc --- .../com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt index 28e8362..ec06fd7 100644 --- a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt +++ b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt @@ -27,7 +27,7 @@ import com.teamdev.jxbrowser.js.JsAccessible import com.teamdev.jxbrowser.js.JsObject /** - * This example demonstrates how to intercept web socket data by using JS-Java + * This example demonstrates how to intercept web socket data by using JS-Kotlin * bridget capabilities. */ fun main() { From 0e339776f4e5c55e1ed84385e6fa52753d9c4960 Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 13:53:25 +0300 Subject: [PATCH 05/11] Remove excessive comments --- .../teamdev/jxbrowser/examples/WebSocketDataInterceptor.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java index 5aa44e3..089a9c1 100644 --- a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java +++ b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java @@ -64,11 +64,8 @@ public static void main(String[] args) { var frame = params.frame(); JsObject jsObject = frame.executeJavaScript("window"); if (jsObject != null) { - // Inject Java object into JavaScript and associate it - // with the "window.java" JavaScript property. jsObject.putProperty("websocketCallback", new WebSocketCallback()); } - // Call the public method of the injected Java object from JS. frame.executeJavaScript(JAVA_SCRIPT); return InjectJsCallback.Response.proceed(); }); From 121ff4ff4c88b0cd388d662caaa289e8d0023eb1 Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi <45938288+kyrylo-pshenychnyi@users.noreply.github.com> Date: Tue, 12 Aug 2025 14:17:21 +0300 Subject: [PATCH 06/11] Fix formatting in comments, JS and doc Co-authored-by: Vladimir Ikryanov --- .../jxbrowser/examples/WebSocketDataInterceptor.java | 12 ++++++------ .../jxbrowser/examples/WebSocketDataInterceptor.kt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java index 089a9c1..480f04b 100644 --- a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java +++ b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2025, TeamDev. All rights reserved. * * Redistribution and use in source and/or binary forms, with or without * modification, must retain the above copyright notice and the following @@ -31,10 +31,10 @@ * This example demonstrates how to intercept web socket data by using JS-Java * bridget capabilities. */ -public class WebSocketDataInterceptor { +public final class WebSocketDataInterceptor { private static final String JAVA_SCRIPT = """ var oldSocket = window.WebSocket; - window.WebSocket = function (url){ + window.WebSocket = function (url) { var socket = new oldSocket(url); socket.onopen = () => { window.websocketCallback.socketOpened(socket); @@ -64,7 +64,7 @@ public static void main(String[] args) { var frame = params.frame(); JsObject jsObject = frame.executeJavaScript("window"); if (jsObject != null) { - jsObject.putProperty("websocketCallback", new WebSocketCallback()); + jsObject.putProperty("webSocketCallback", new WebSocketCallback()); } frame.executeJavaScript(JAVA_SCRIPT); return InjectJsCallback.Response.proceed(); @@ -75,9 +75,9 @@ public static void main(String[] args) { } /** - * A JS-accessible class used as web socket events listener in JS. + * A JS-accessible class used as a web socket event listener in JS. * - *

The class is marked with [JsAccessible] annotation to tell JavaScript + *

The class is marked with the {@code @JsAccessible} annotation to tell JavaScript * that all public methods of the injected object can be invoked from * the JavaScript side. * diff --git a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt index ec06fd7..ef2c9ed 100644 --- a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt +++ b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2025, TeamDev. All rights reserved. * * Redistribution and use in source and/or binary forms, with or without * modification, must retain the above copyright notice and the following From 4285da669ed62433085e26bc7a6f73a515b2d7a4 Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 14:19:31 +0300 Subject: [PATCH 07/11] Reformat file --- .../examples/WebSocketDataInterceptor.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java index 480f04b..1ea831a 100644 --- a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java +++ b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java @@ -32,6 +32,7 @@ * bridget capabilities. */ public final class WebSocketDataInterceptor { + private static final String JAVA_SCRIPT = """ var oldSocket = window.WebSocket; window.WebSocket = function (url) { @@ -64,28 +65,31 @@ public static void main(String[] args) { var frame = params.frame(); JsObject jsObject = frame.executeJavaScript("window"); if (jsObject != null) { - jsObject.putProperty("webSocketCallback", new WebSocketCallback()); + jsObject.putProperty("webSocketCallback", + new WebSocketCallback()); } frame.executeJavaScript(JAVA_SCRIPT); return InjectJsCallback.Response.proceed(); }); - browser.navigation().loadUrlAndWait("https://www.teamdev.com/jxbrowser#features"); + browser.navigation().loadUrlAndWait( + "https://www.teamdev.com/jxbrowser#features"); } } /** * A JS-accessible class used as a web socket event listener in JS. * - *

The class is marked with the {@code @JsAccessible} annotation to tell JavaScript - * that all public methods of the injected object can be invoked from - * the JavaScript side. + *

The class is marked with the {@code @JsAccessible} annotation to tell + * JavaScript that all public methods of the injected object can be invoked + * from the JavaScript side. * - *

Please note, only public classes and static nested classes can be injected - * into JavaScript. + *

Please note, only public classes and static nested classes can be + * injected into JavaScript. */ @JsAccessible private static class WebSocketCallback { + @SuppressWarnings("unused") // To be called from JavaScript. public void socketClosed(JsObject closeEvent) { System.out.println("WebSocketCallback.socketClosed"); From 6bdd47be373760893f0d49d1e849d082186fc6b2 Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 18:27:09 +0300 Subject: [PATCH 08/11] Add UI to examples Use local HTML for testing --- .../examples/WebSocketDataInterceptor.java | 77 +++++++++++++----- .../examples/WebSocketDataInterceptor.kt | 75 +++++++++++------- examples/src/main/resources/echo.html | 78 +++++++++++++++++++ 3 files changed, 183 insertions(+), 47 deletions(-) create mode 100644 examples/src/main/resources/echo.html diff --git a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java index 1ea831a..01e0e8b 100644 --- a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java +++ b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java @@ -20,12 +20,20 @@ package com.teamdev.jxbrowser.examples; -import static com.teamdev.jxbrowser.engine.RenderingMode.OFF_SCREEN; +import static com.teamdev.jxbrowser.engine.RenderingMode.HARDWARE_ACCELERATED; import com.teamdev.jxbrowser.browser.callback.InjectJsCallback; import com.teamdev.jxbrowser.engine.Engine; import com.teamdev.jxbrowser.js.JsAccessible; import com.teamdev.jxbrowser.js.JsObject; +import com.teamdev.jxbrowser.view.swing.BrowserView; +import java.awt.BorderLayout; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.net.URL; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; /** * This example demonstrates how to intercept web socket data by using JS-Java @@ -38,43 +46,72 @@ public final class WebSocketDataInterceptor { window.WebSocket = function (url) { var socket = new oldSocket(url); socket.onopen = () => { - window.websocketCallback.socketOpened(socket); + window.webSocketCallback.socketOpened(socket); this.onopen(); }; socket.onmessage = (message) => { - window.websocketCallback.messageReceived(socket,message.data); + window.webSocketCallback.messageReceived(socket,message.data); this.onmessage(message); }; var onclose = socket.onclose; socket.onclose = (closeEvent) => { this.onclose(); - window.websocketCallback.socketClosed(closeEvent); + window.webSocketCallback.socketClosed(closeEvent); this.close(closeEvent); }; - this.close = (event)=> {socket.close();}; + this.close = (event) => { + window.webSocketCallback.socketClosed(event); + socket.close(); + }; this.send = (data) => { - window.websocketCallback.beforeSendData(socket,data); + window.webSocketCallback.beforeSendData(socket,data); socket.send(data); }; };"""; + private static String getUrl() { + ClassLoader classLoader = WebSocketDataInterceptor.class.getClassLoader(); + if (classLoader != null) { + URL resource = classLoader.getResource("echo.html"); + if (resource != null) { + return resource.toString(); + } + } + throw new IllegalArgumentException("Resource not found: echo.html"); + } + public static void main(String[] args) { - try (var engine = Engine.newInstance(OFF_SCREEN)) { - var browser = engine.newBrowser(); - browser.set(InjectJsCallback.class, params -> { - var frame = params.frame(); - JsObject jsObject = frame.executeJavaScript("window"); - if (jsObject != null) { - jsObject.putProperty("webSocketCallback", - new WebSocketCallback()); + var engine = Engine.newInstance(HARDWARE_ACCELERATED); + var browser = engine.newBrowser(); + browser.set(InjectJsCallback.class, params -> { + var frame = params.frame(); + JsObject jsObject = frame.executeJavaScript("window"); + if (jsObject != null) { + jsObject.putProperty("webSocketCallback", + new WebSocketCallback()); + } + frame.executeJavaScript(JAVA_SCRIPT); + return InjectJsCallback.Response.proceed(); + }); + + SwingUtilities.invokeLater(() -> { + var view = BrowserView.newInstance(browser); + + var frame = new JFrame("File Upload"); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + engine.close(); } - frame.executeJavaScript(JAVA_SCRIPT); - return InjectJsCallback.Response.proceed(); }); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.add(view, BorderLayout.CENTER); + frame.setSize(700, 500); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); - browser.navigation().loadUrlAndWait( - "https://www.teamdev.com/jxbrowser#features"); - } + browser.navigation().loadUrlAndWait(getUrl()); } /** @@ -88,7 +125,7 @@ public static void main(String[] args) { * injected into JavaScript. */ @JsAccessible - private static class WebSocketCallback { + public final static class WebSocketCallback { @SuppressWarnings("unused") // To be called from JavaScript. public void socketClosed(JsObject closeEvent) { diff --git a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt index ef2c9ed..1c58440 100644 --- a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt +++ b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt @@ -19,12 +19,16 @@ */ package com.teamdev.jxbrowser.examples +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.window.singleWindowApplication import com.teamdev.jxbrowser.browser.callback.InjectJsCallback import com.teamdev.jxbrowser.dsl.Engine +import com.teamdev.jxbrowser.dsl.browser.navigation import com.teamdev.jxbrowser.dsl.register import com.teamdev.jxbrowser.engine.RenderingMode import com.teamdev.jxbrowser.js.JsAccessible import com.teamdev.jxbrowser.js.JsObject +import com.teamdev.jxbrowser.view.compose.BrowserView /** * This example demonstrates how to intercept web socket data by using JS-Kotlin @@ -36,13 +40,25 @@ fun main() { browser.register(InjectJsCallback { params -> val frame = params.frame() val jsWindow = frame.executeJavaScript("window")!! - jsWindow.putProperty("websocketCallback", WebSocketCallback()) + jsWindow.putProperty("webSocketCallback", WebSocketCallback()) frame.executeJavaScript(JAVA_SCRIPT) InjectJsCallback.Response.proceed() }) - browser.navigation() - .loadUrlAndWait("https://www.teamdev.com/jxbrowser#features") - engine.close() + singleWindowApplication(title = "Print from JavaScript") { + BrowserView(browser) + LaunchedEffect(Unit) { + browser.navigation.loadUrlAndWait(getUrl()) + } + } +} + +private fun getUrl(): String { + val classLoader = WebSocketDataInterceptor::class.java.classLoader + val resource = classLoader.getResource("echo.html") + if (resource != null) { + return resource.toString() + } + throw IllegalArgumentException("Resource not found: echo.html") } /** @@ -79,26 +95,31 @@ class WebSocketCallback { } } -private const val JAVA_SCRIPT = ("var oldSocket = window.WebSocket;\n" - + " window.WebSocket = function (url){\n" - + " var socket = new oldSocket(url);\n" - + " socket.onopen = () => {\n" - + " window.websocketCallback.socketOpened(socket);\n" - + " this.onopen();\n" - + " };\n" - + " socket.onmessage = (message) => {\n" - + " window.websocketCallback.messageReceived(socket,message.data);\n" - + " this.onmessage(message);\n" - + " };\n" - + " var onclose = socket.onclose;\n" - + " socket.onclose = (closeEvent) => {\n" - + " this.onclose();\n" - + " window.websocketCallback.socketClosed(closeEvent);\n" - + " this.close(closeEvent);\n" - + " };\n" - + " this.close = (event)=> {socket.close();};\n" - + " this.send = (data) => {\n" - + " window.websocketCallback.beforeSendData(socket,data);\n" - + " socket.send(data);\n" - + " };\n" - + " };") +private val JAVA_SCRIPT = """ + var oldSocket = window.WebSocket; + window.WebSocket = function (url) { + var socket = new oldSocket(url); + socket.onopen = () => { + window.webSocketCallback.socketOpened(socket); + this.onopen(); + }; + socket.onmessage = (message) => { + window.webSocketCallback.messageReceived(socket,message.data); + this.onmessage(message); + }; + var onclose = socket.onclose; + socket.onclose = (closeEvent) => { + this.onclose(); + window.webSocketCallback.socketClosed(closeEvent); + this.close(closeEvent); + }; + this.close = (event) => { + window.webSocketCallback.socketClosed(event); + socket.close(); + }; + this.send = (data) => { + window.webSocketCallback.beforeSendData(socket,data); + socket.send(data); + }; + }; +""".trimIndent() diff --git a/examples/src/main/resources/echo.html b/examples/src/main/resources/echo.html new file mode 100644 index 0000000..2c9bf1c --- /dev/null +++ b/examples/src/main/resources/echo.html @@ -0,0 +1,78 @@ + + + + + + + + + + + +

+

Enter WebSocket URL and click "Connect"

+
+
+ + +
+
+ + + +
+
+
+
- Connection log will appear here
+
+
+ + + \ No newline at end of file From 3773e568d53ab0c2468f0c405ec90d838bfad928 Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 18:29:59 +0300 Subject: [PATCH 09/11] Change app title --- .../com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt index 1c58440..bcb7d51 100644 --- a/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt +++ b/examples/src/main/kotlin/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.kt @@ -44,7 +44,7 @@ fun main() { frame.executeJavaScript(JAVA_SCRIPT) InjectJsCallback.Response.proceed() }) - singleWindowApplication(title = "Print from JavaScript") { + singleWindowApplication(title = "Web socket data interceptor") { BrowserView(browser) LaunchedEffect(Unit) { browser.navigation.loadUrlAndWait(getUrl()) From 4ab5168b1959cdbe06916b4684633a6a4336867f Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 18:30:24 +0300 Subject: [PATCH 10/11] Add missing EOF --- examples/src/main/resources/echo.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/main/resources/echo.html b/examples/src/main/resources/echo.html index 2c9bf1c..539c9a2 100644 --- a/examples/src/main/resources/echo.html +++ b/examples/src/main/resources/echo.html @@ -75,4 +75,4 @@

Enter WebSocket URL and click "Connect"

hideElement(document.getElementById("connected")); - \ No newline at end of file + From ca9b4fd1d343cc2158eeb8d50f5d98d40a1bb0a8 Mon Sep 17 00:00:00 2001 From: Kyrylo Pshenychnyi Date: Tue, 12 Aug 2025 18:31:29 +0300 Subject: [PATCH 11/11] Update title --- .../teamdev/jxbrowser/examples/WebSocketDataInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java index 01e0e8b..83c2bd7 100644 --- a/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java +++ b/examples/src/main/java/com/teamdev/jxbrowser/examples/WebSocketDataInterceptor.java @@ -97,7 +97,7 @@ public static void main(String[] args) { SwingUtilities.invokeLater(() -> { var view = BrowserView.newInstance(browser); - var frame = new JFrame("File Upload"); + var frame = new JFrame("Web socket data interceptor"); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) {