I am using JxBrowser in a project. I am only working with local HTML files, and therefore using the following method to render my HTML files:
public static void loadHTMLFile(Browser browser, String filename){
String current = PathUtils.getCurrentDir();
browser.loadURL("file:///" + current + "/some/path/in/my/project/resources/web/" + filename);
}
In some of the cases I have to use the invokeAndWaitFinishLoadingMainFrame-method since I have to initialize buttons which have to be loaded completely first.
An example from the official JxBrowser website (https://jxbrowser.support.teamdev.com/support/solutions/articles/9000013107-loading-waiting) looks like this:
// Blocks current thread execution and waits until http://www.google.com web page is loaded completely
Browser.invokeAndWaitFinishLoadingMainFrame(browser, new Callback<Browser>() {
@Override
public void invoke(Browser value) {
value.loadURL("http://www.google.com");
}
});
In my case it looks like this:
public static void loadHTMLFileComplete(Browser browser, String filename){
Browser.invokeAndWaitFinishLoadingMainFrame(browser, new Callback<Browser>() {
@Override
public void invoke(Browser value) {
loadHTMLFile(value, filename);
}
});
}
Nothing surprising so far I guess...
Now comes the tricky part:
The invokeAndWaitFinishLoadingMainFrame-method is the problem:
there is a little hack which I found accidentally to get it working for the moment (now it gets creepy):
this is how I do it
public static void loadGoogle(Browser browser){
Browser.invokeAndWaitFinishLoadingMainFrame(browser, new Callback<Browser>() {
@Override
public void invoke(Browser value) {
value.loadURL("http://www.google.com");
}
});
}
here comes the timeoutException I mentioned earlier:
-- Product name: JxBrowser
-- Licensed version: 6.x
-- Licensed to:
-- License type: Evaluation
-- Generation date: 21.04.2019
-- Expiration date: 21.05.2019
-- License info: Single-user license
-- Current date: 30.04.2019
JxBrowser license valid.
11:55:59 SCHWERWIEGEND:[0430/235559.671039:ERROR:sandbox_linux.cc(379)] InitializeSandbox() called with multiple threads in process gpu-process.
com.teamdev.jxbrowser.chromium.Browser@6b9651f3
11:56:49 SCHWERWIEGEND: Unexpected exception in DOMEventListener.handleEvent() method.
java.lang.RuntimeException: java.util.concurrent.TimeoutException
at com.teamdev.jxbrowser.chromium.Browser.invokeAndWaitFinishLoadingMainFrame(SourceFile:570)
at com.teamdev.jxbrowser.chromium.Browser.invokeAndWaitFinishLoadingMainFrame(SourceFile:532)
at browserActions.PageLoader.loadHTMLFileComplete(PageLoader.java:15)
at browserViews.OrderCreationView.loadView(OrderCreationView.java:21)
at browserActions.ButtonInitializer.lambda$0(ButtonInitializer.java:26)
at com.teamdev.jxbrowser.chromium.dom.internal.EventTarget$a.onMessageReceived(SourceFile:179)
at com.teamdev.jxbrowser.chromium.internal.ipc.q.a(SourceFile:1085)
at com.teamdev.jxbrowser.chromium.internal.ipc.r.run(SourceFile:69)
at com.teamdev.jxbrowser.chromium.internal.s.run(SourceFile:79)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.util.concurrent.TimeoutException
... 14 more
It does not work:
It works:
What is happening here :O ?
Any help would be awesome!
JxBrowser is based on the Chromium engine and inherits its multi-process architecture. Each Browser
instance is associated with at least one render process that handles DOM and JavaScript related functionality. When you navigate to a different domain, the Chromium engine creates a new render process and kills the old one. If you navigate within one domain, the render process remains the same.
From the call stack, I see that you try to load a URL from within a DOM listener. DOM listeners are invoked synchronously, so the render process remains blocked while the event is being processed by Java.
When you load a URL, e.g. google.com, everything works just fine, because a new render process is created and, as soon as the page is loaded, the Browser.invokeAndWaitFinishLoadingMainFrame
method returns control and the old render process is killed. However, if you try to load a file:///
URL, the render process remains the same and the loading cannot start, because the render process is blocked by the Browser.invokeAndWaitFinishLoadingMainFrame
method called from a DOM listener, so you’ll get a deadlock.
In order to avoid this exception, you should not start synchronous navigation from within a DOM listener.
According to the call stack, you call the loadView
method from a DOMEventListener
. In order to avoid the deadlock you should call this method asynchronously, for example:
import com.teamdev.jxbrowser.chromium.dom.events.DOMEvent;
import com.teamdev.jxbrowser.chromium.dom.events.DOMEventListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyDomEventListener implements DOMEventListener {
private final ExecutorService executorService;
private final OrderCreationView view;
public MyDomEventListener(OrderCreationView view) {
this.view = view;
this.executorService = Executors.newCachedThreadPool();
}
@Override
public void handleEvent(DOMEvent domEvent) {
// Do not block the current thread and invoke the loadView method asynchronously.
executorService.execute(view::loadView);
}
}
In the loadView
method you can load an HTML synchronously:
public void loadView() {
Browser.invokeAndWaitFinishLoadingMainFrame(browser, new Callback<Browser>() {
@Override
public void invoke(Browser browser) {
browser.loadHTML("");
}
});
initLoginButton();
}