英文:
JCEF functions only work from the second call onwards (Java Chromium Embedded Framework)
问题
我最近在一个 Netbeans(RCP)应用项目(Java: JDK11)中集成了 Java Chromium Embedded Framework(https://github.com/chromiumembedded/java-cef)。
浏览器窗口显示在 Netbeans 的 TopComponent 中,总体上运行良好。
然而,我注意到了一些相当小但很讨厌的错误,它们总是遵循相同的模式:
无论我执行何种涉及 jcef 浏览器的代码,它总是在第二次执行时才起作用,并且在第一次执行时被完全忽略。
然而,没有错误消息、日志或任何类似的内容,而且在使用调试工具时代码似乎是被执行的。
以下是三个例子:
- 实现返回按钮:<br> 这只是检查 CefBrowser 实例是否对
browser.canGoBack()
返回true
。如果返回true
,则激活一个按钮。<br> 这个值在正确的情况下开始为false
,但在第二次更改 url 之后就返回true
。(显然应该在第一次之后发生)<br> 从那时开始,它一直正常工作... 除非你通过返回按钮回到首页。然后它显然应该再次返回false
- 但它没有这样做。<br> 再次调用不同的 URL 会为canGoBack()
返回false
(显然应该为true
),并且只有在第二次更改 URL 之后才返回true
。<br> 因此,canGoBack
的返回值总是显示出它应该在前一次执行中显示的返回值,就好像滞后了一次。 - 实现“转到”功能:<br> 这个函数只是将您带到一个指定的其他网站,而不是首页。同样,从第二次开始就正常工作,但第一次代码被简单地忽略了。(尽管我通过调试器确认,即使第一次执行时,代码也是没有错误的)
- 实现登录对话框:<br> 如果浏览器注意到需要身份验证,它会打开一个对话框,用户可以在其中输入基本凭据。<br> 同样,对话框会打开,凭证会被正确返回(通过日志确认),传递给适当的函数,并且第二次尝试可以工作,但第一次尝试不行。(凭证是相同的)<br> 实际上,我通过 Wireshark 确认,第一次执行
callback.Continue(ad.getUsername(), ad.getPassword());
时,实际上没有发送任何东西到服务器...
第三个示例的代码:
cefClient_.addRequestHandler(new CefRequestHandlerAdapter() {
@Override
public boolean getAuthCredentials(CefBrowser browser, String origin_url, boolean isProxy, String host,
int port, String realm, String scheme, CefAuthCallback callback) {
AuthenticationDialog ad = new AuthenticationDialog();
ad.authenticate(); // 显示登录对话框
LOGGER.log(Level.INFO, String.format("->%s:%s", ad.getUsername(), ad.getPassword())); // 这确认了即使第一次执行时,变量也是正确的
callback.Continue(ad.getUsername(), ad.getPassword());
return true;
}
});
遗憾的是,我再也不知道可能是什么原因导致了这些问题。
如果你们中的任何人曾经遇到过这些相同的问题,或者有任何想法如何修复它们,甚至从何处开始寻找修复方法,我将非常感谢您的评论。
提前感谢!
英文:
I recently integrated the Java Chromium Embedded Framework (https://github.com/chromiumembedded/java-cef) in a Netbeans (RCP) Application Project (Java: JDK11).
The Browser Window is shown in a Netbeans TopComponent and generally works just fine.
However I have several rather small, but nasty bugs that I noticed which are always following the same pattern:
Whatever code I execute that concerns the jcef browser always only works when I execute it a second time and is all but ignored the first time around.
However there are no error messages or logs or any thing like that and the code seems to be executed when I go through it with the debugging tool.
Three examples:
- Implementing a return button:<br> This just checks whether the CefBrowser instance returns
true
forbrowser.canGoBack()
. If it returnstrue
a button is activated.<br> The value starts correctly atfalse
, but it returns true just after the second change of url.(Should obviously happen after the first)<br> From there on out it works just fine....Unless you go back (with the return button) to the home page. It should then obviously returnfalse
again - Which it doesn't.<br> Calling a different URL again returnsfalse
forcanGoBack()
(which should then obviously betrue
) and only returnstrue
after the second URL change again.<br> So the return fromcanGoBack
always shows the return it should have shown from its previous execution, as if lagging behind one time. - Implementing a "go to" function:<br> This function just takes you to a specified other website than the home page. Again works fine from the 2nd time onwards, but the first time the code is simply ignored. (although I confirmed via Debugger that the code is executed without errors even the first time around)
- Implementing a login dialog:<br> If the browser notices that authentication is required, this opens up a dialog where the user can enter BASIC credentials.<br> And again the dialog gets opened, the credentials are returned correctly (confirmed via Log), are handed over to the appropriate function and it works for the second try but not the first. (credentials were identical)<br> Indeed I confirmed via Wireshark, that the first time that
callback.Continue(ad.getUsername(), ad.getPassword());
is executed, nothing at all is actually send to the server...
Code for the third example:
cefClient_.addRequestHandler(new CefRequestHandlerAdapter() {
@Override
public boolean getAuthCredentials(CefBrowser browser, String origin_url, boolean isProxy, String host,
int port, String realm, String scheme, CefAuthCallback callback) {
AuthenticationDialog ad = new AuthenticationDialog();
ad.authenticate(); // Shows Login Dialog
LOGGER.log(Level.INFO, String.format("--->%s:%s", ad.getUsername(), ad.getPassword())); //This confirms that the variables are indeed correct even the first time around
callback.Continue(ad.getUsername(), ad.getPassword());
return true;
}
});
Sadly I don't have any clue anymore what could possibly cause these problems.
If anyone of you has had these same problems or has any idea as to how to fix them or even where to start looking for a fix I would very much appreciate your comments.
Thanks in advance!
答案1
得分: 0
我找到了上述问题的答案。以防将来有其他人遇到类似的问题,我将简要总结一下为我解决问题的方法。
显然,我面临的大部分问题都源自线程问题。当您在 jcef 浏览器的线程仍在处理某些内容时执行命令,您的命令将不会被执行,但也不会出现任何错误消息或其他不良行为。
第二个问题的示例:
当执行 go(url)
命令时,浏览器尚未完全加载初始主页。这就是命令被忽略的原因。
为了解决这个问题,我添加了一个简单的字符串变量,它会保存某人想要加载的 URL,直到浏览器完成加载前一个页面。
只有在此之后,这个 URL 才会在浏览器中加载,并从变量中删除。
当然,这也可以使用类似列表的东西来实现,但我想不出会有什么有用的场景。
检查浏览器是否完成加载应该在 CefLoadHandlerAdapter
的重写方法 onLoadingStateChange
中进行。
client_.addLoadHandler(new CefLoadHandlerAdapter() {
@Override
public void onLoadingStateChange(CefBrowser browser, boolean isLoading,
boolean canGoBack, boolean canGoForward) {
if (!isLoading) {
browser_ready = true;
if (goOnReady != null) {
goURL(goOnReady);
}
}
}
});
第一个问题的原始问题也存在类似的问题。
通过在上述的 onLoadingStateChange
中通过 canGoBack
变量激活“返回”按钮,最终解决了我的问题。
现在第三个示例(登录对话框显示两次)也在 jcef github 页面的官方详细示例代码中出现了问题。
这个问题似乎又是一个线程问题,但这次我不太确定。
作为一种解决方法,我实施了以下解决方案:
- 为用户名和密码添加类变量
- 如果这些变量为空,在
getAuthCredentials
方法中启动登录对话框(见原始问题),并将给定的用户名和密码保存在这些类变量中。 - 然后执行
browser.reload();
- 浏览器将再次进行身份验证,但这次变量不会为空,您可以将它们传递给
callback.Continue(username, password);
,从而只向用户显示一次登录对话框。
这个过程也非常快,用户不会注意到重新加载。
英文:
I found the answer(s) to the afore mentioned questions. In case someone else will stumble upon similar issues in the future I'm going to shortly summarize what fixed it for me.
Apparently most of the problems I faced arose from threading problems. It seems like when you execute a command on the jcef browser while it's thread is still working on something, your command will not be executed, but there also won't be any error messages or other unwanted behaviour.
Example for the second problem:
When the go(url)
command was executed the browser had not finished loading the initial home page completely yet. That's why the command was ignored.
To fix this I added a simple String variable that will save the URL that someone wants to load until the browser has finished loading the previous page.
Only then will this url be loaded in the browser and consequently be deleted from the variable.
This could of course also be done with something like a list, but I can't think of a scenario where that would be useful.
Checking whether the browser is done loading should be done in the overridden method onLoadingStateChange
of the CefLoadHandlerAdapter
.
client_.addLoadHandler(new CefLoadHandlerAdapter() {
@Override
public void onLoadingStateChange(CefBrowser browser, boolean isLoading,
boolean canGoBack, boolean canGoForward) {
if (!isLoading) {
browser_ready = true;
if (goOnReady != null) {
goURL(goOnReady);
}
}
}
});
A similar issue was present with the first example from the original question.
Activating the "Back" button within the afore mentioned onLoadingStateChange
via the canGoBack
variable did the trick for me in the end.
Now the third example (the login dialog showing twice) turned out to be a problem that was also present in the official detailed example code from the jcef github page.
This problem seems like it might be a threading problem again, but I'm not sure this time.
As a workaround I implemented the following solution:
- add class variables for username and password
- if these variables are empty start the login dialog within the
getAuthCredentials
method (see original question) and save the given username and password in these class variables. - then execute
browser.reload();
- the browser will then go through authentication again, but this time the variables won't be empty and you can hand them to
callback.Continue(username, password);
thus only showing one login dialog to the user.
This process is also very quick so the user want be able to notice the reload.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论