Handle DOM Events happening in the Embedded Browser (JBCefBrowser)
Hi There!
I'm in my initial phases of using JCEF in an IntelliJ Plugin. As a first step, I am able to render HTML contents in an embedded browser using JBCefBrowser. Now, I'm trying to capture the click events that can happen in the browser. I tried JSQuery callback mechanism.
I don't see the handler getting executed and the print statement (I added) during button click.
I'm sharing my scenario here for a better picture so that you can recommend the intended APIs/classes. After capturing the click event happening the embedded browser, my next steps are:
1.) Start a local server and register a callback in the plugin
2.) Open a new browser window (External browser and not the Chromium based) with the call back URL
3.) Executing plugin code based on user events going to happen further
Are CefBrowser and JBCefJSQuery sufficient to deal with such button click and further user events or are JBCefClient/handlers required?
Code Snippet:
JBCefBrowser jbCefBrowser = new JBCefBrowser();
jbCefBrowser.loadHTML(getHtmlContents());
CefBrowser cefBrowser = jbCefBrowser.getCefBrowser();
JBCefJSQuery buttonClickQuery = JBCefJSQuery.create((JBCefBrowserBase) jbCefBrowser);
buttonClickQuery.addHandler(data -> {
System.out.println(data);
return null;
});
cefBrowser.executeJavaScript(
"window.handleButtonClick = function(event) {" +
buttonClickQuery.inject("event") +
"};",
cefBrowser.getURL(), 0
);
String javaScript = "document.addEventListener('click', function (event) {" +
"window.handleButtonClick(event);" +
"});";
cefBrowser.executeJavaScript(javaScript, cefBrowser.getURL(), 0
);
Version Info:
IntelliJ IDE → 2024.2.2
Gradle → 8.10.2-all
IntelliJ Gradle Platform Plugin → org.jetbrains.intellij.platform:2.0.1
Java → 21
Please sign in to leave a comment.
Can anyone support this issue?
Checking..
`JBCefJSQuery` is sufficient for sending any kind of information that can be serialized as a string. It could be just a JSON string.
I'd recommend to try debugging using chromium DevTools. See https://plugins.jetbrains.com/docs/intellij/jcef.html#debugging . Set the debug port in the Registry(let's say 9222), restart the app, open in any external browser http://localhost:9222/.
1. Check if the mouse click actually leads to
window.handleButtonClick
call.2. Check if
window.handleButtonClick
is called w/o exceptions.3. Find the actual JS function name generated by
buttonClickQuery.inject
and try to call it manually with a simple string as the argument from the DevTools console. And check if you receive a callback in Java.It will probably then be clear what is not working as expected
You may also check an example of sending data from JS in the IJ platform:
Frontend - https://github.com/JetBrains/intellij-community/blob/69b39c923a9a18bc9409c7b3bf02ace5cb678f1e/images/src/org/intellij/images/editor/impl/jcef/resources/image_viewer.html#L230
Backend - https://github.com/JetBrains/intellij-community/blob/69b39c923a9a18bc9409c7b3bf02ace5cb678f1e/images/src/org/intellij/images/editor/impl/jcef/JCefImageViewer.kt#L87
Thanks for picking this up and recommending a few steps.
I captured the function name from IntelliJ using debug mode and am able to call it manually from the DevTools console. I get a callback in Java (it arrives at addHandler() method). Screenshots attached.
By the way, I tried to call the function with a simple String argument. It expected an object as an argument. Then, I called the method by following the cefQuery function's signature and it ran by giving me a callback.
But, the mouse clicks are not leading to window.handleButtonClick. I think the executeJavascript() method bound to the cefbrowser instance is not effective.
I took a look at the Kotlin samples you've shared for reference. From the sample, I can infer that the JS functions are defined in a HTML page. A CefLoadHandlerAdapter is implemented to call the JS function using CefBrowser's execute() and the handler is attached to the CefBrowser.
1.) Is it really required to define the JS functions in a HTML page and then use cefBrowser.executeJavascript() to call them or Can we just write JS functions inside cefBrowser.executeJavascript() without having to define them in a HTML page?
2.) And, I understood from you that we do not need any handlers unlike the reference and hence I'm not raising a question on this.
3.) During the debug, when I expanded and checked out the event listeners in the DevToolsConsole, I came across this. Are the JS functions, by default, prefixed with “use strict” to run in strict mode? By any chance, is the strict mode preventing the JS functions being called? Can we run our app/JS functions without using strict mode?
There is no such requirement. It could be in an HTML file, JS file or could be defined by calling
CefBrowser#executeJavascript
.I think it should be the same as in other browsers.
If I understand correctly the callback worked during the debugging. Thus, the goal is to properly serialize the message into a string and pass it to a function
cefQuery_*()
I think that it must be something like.
eventToString()
is to be implemented.I followed the Kotlin reference you suggested, implemented a CefLoadHandlerAdapter with onLoadEnd() and attached it to the cefBrowser. I moved executeJavascript() calls to onLoadEnd(). Now, the JS statements are becoming effective. Earlier, when I created the browser, loaded HTML contents and then called executeJavascript(), the JS statements had never become effective. Now, I'm able to listen to the window events by moving the executeJavascript calls to CefLoadHandler.
Coming to the function argument, I'll have to figure out a way to convert the event object to a type that can help me parse the event object easily in Java. Thanks for pointing this out and sharing the Kotlin reference!