Are there any ways to debug javascript and html that is executed within a Javafx WebView? Something similar to Firebug or Chrome's developer console?
I have an application that renders fine in Firefox and Chrome, but does not render correctly inside a WebView. It really could be any number of things, but without some debugging tools I don't know how to track down the root cause.
Thanks.
Here is some Java code to make use of Firebug Lite in a JavaFX WebView without modifying the html of the target page.
webView.getEngine().executeScript("if (!document.getElementById('FirebugLite')){E = document['createElement' + 'NS'] && document.documentElement.namespaceURI;E = E ? document['createElement' + 'NS'](E, 'script') : document['createElement']('script');E['setAttribute']('id', 'FirebugLite');E['setAttribute']('src', 'https://getfirebug.com/' + 'firebug-lite.js' + '#startOpened');E['setAttribute']('FirebugLite', '4');(document['getElementsByTagName']('head')[0] || document['getElementsByTagName']('body')[0]).appendChild(E);E = new Image;E['setAttribute']('src', 'https://getfirebug.com/' + '#startOpened');}");
You can trigger the code using a JavaFX Button or any other mechanism you wish.
I am debugging JavaFx WebView with chrome DevTools and safari Web Inspector.
I created minimal project to help people debug with DevTools. Get it on GitHub. You can find there:
runnable javaFXWebKitDebugger.jar
source code of created javaFXWebKitDebugger.jar
The sample opens WebView and enables WebSocket Servlet. When you run javaFXWebKitDebugger.jar open Chrome browser and load: dev tools url
You can try Firebug Lite, which can be incorporated into any web-browser. See http://www.makeuseof.com/tag/install-firebug-for-browsers-other-than-firefox/
Maybe a bit late to answer, but I think this way is quite simple.
add javascript listener in java
Java :
webengine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
#Override
public void changed(ObservableValue<? extends State> observable,
State oldValue, State newValue) {
JSObject jsobj = (JSObject) webengine.executeScript("window");
jsobj.setMember("java", new JSListener());
}
});
Then create the JS listener class in java.
JSListener java:
public class JSListener {
public void log(String text){
System.out.println(text);
}
}
add javascript in html file
Javascript:
var javaReady = function(callback){
if(typeof callback =='function'){
if(typeof java !='undefined'){
callback();
} else {
var javaTimeout = 0;
var readycall = setInterval(function(){
javaTimeout++;
if(typeof java !='undefined' || javaTimeout > 1000){
try{
callback();
} catch(s){};
clearInterval(readycall);
}
},1);
}
}
};
var errorlistener = function(msg, url, line){
javaReady(function(){
java.log(msg +", url: "+url+ ", line:" + line);
});
};
//overide onerror
var onerror = errorlistener;
If you want to load html from the outside, you can not to change it, you can use code like this.
var testsss = window.open("http://testError.com");
testsss.onerror = errorlistener;
but first you need to add setCreatePopupHandler in java, to make it you can see here: webview not opening the popup window in javafx
Related
I'm using both JavaFX and the javascript engine inside JavaFX WebEngine to develop an application. I'd like to get feedback from javascript for debugging purposes. What happens to the console output inside the WebEngine? Is there any way I can access it, or redirect to System.out in java?
The following code redirects console.log() to JavaBridge.log():
import netscape.javascript.JSObject;
[...]
public class JavaBridge
{
public void log(String text)
{
System.out.println(text);
}
}
// Maintain a strong reference to prevent garbage collection:
// https://bugs.openjdk.java.net/browse/JDK-8154127
private final JavaBridge bridge = new JavaBridge();
[...]
webEngine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) ->
{
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("java", bridge);
webEngine.executeScript("console.log = function(message)\n" +
"{\n" +
" java.log(message);\n" +
"};");
});
You can just add message listener to see what's happening in your output. You don't have to inject js bridge with redefinition of functions like console.log for every single loaded page
WebConsoleListener.setDefaultListener((webView, message, lineNumber, sourceId) -> {
System.out.println(message + "[at " + lineNumber + "]");
});
I like to go the other direction. We use log4j so I created a javascript wrapper like the following:
module.exports = {
levels:[ "ALL", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"],
level:"INFO",
error:function(msg){
if(this.isErrorEnabled()){
console.error(msg)
}
},
warn:function(msg){
if(this.isWarnEnabled()){
console.warn(msg)
}
},
info:function(msg){
if(this.isInfoEnabled()){
console.log("INFO: "+msg)
}
},
debug:function(msg){
if(this.isDebugEnabled()){
console.log("DEBUG: "+msg)
}
},
trace:function(msg){
if(this.isTraceEnabled()){
console.log("TRACE: "+msg)
}
},
isErrorEnabled:function(){
return this.isEnabled("ERROR");
},
isWarnEnabled:function(){
return this.isEnabled("WARN");
},
isInfoEnabled:function(){
return this.isEnabled("INFO");
},
isDebugEnabled:function(){
return this.isEnabled("DEBUG");
},
isTraceEnabled:function(){
return this.isEnabled("TRACE");
},
isEnabled:function(statementLevel){
return this.levels.indexOf(this.level)<=this.levels.indexOf(statementLevel);
}
}
Then at the beginning of the javascript I check to see if the log is present and set it:
if(window.log == undefined){
window.log = require("./utils/log4j-wrapper")
window.log.level = "INFO"
}
And that way if you set the Log4j logger directly on the engine before you even load the url like:
WebEngine webEngine = webView.getEngine()
JSObject win = (JSObject) webEngine.executeScript("window")
win.setMember("log", log) //log being the java log4j logger
This way I can get logging in if I am opening directly in a browser or it is being run from a WebView in a JavaFX program. And has the added benefit of having levels to the logging in javascript that match your packages of the WebView controller. Just an alternative for larger javascript views.
I want to observe the upload percentage of a file upload from GWT.
In JavaScript you can use a XMLHttpRequest and add an event listener like this:
var oReq = new XMLHttpRequest();
oReq.upload.addEventListener("progress", updateProgress, false);
// progress on transfers from the server to the client (downloads)
function updateProgress (oEvent) {
if (oEvent.lengthComputable) {
var percentComplete = oEvent.loaded / oEvent.total;
// ...
} else {
// Unable to compute progress information since the total size is unknown
}
}
(The above code is from here.)
This is also done very easily in jQuery as:
var $request = $.ajax({
xhr: function() {
xhrNativeObject = new window.XMLHttpRequest();
//Upload progress
xhrNativeObject.upload.addEventListener("progress", function(event) { ... }
}
});
I want to do the same with GWT. I could use a RequestBuilder to send a request, but this is only a high level wrapper around the XMLHttpRequest JavaScriot object. Another possibility would be to use the GWT XMLHttpRequest class which is a JSNI wrapper of the JavaScript XMLHttpRequest.
My problem:
How can I add a progress listener to the XMLHttpRequest or the RequestBuilder?
I used before gwt-upload library.
You dont need to rediscover America.
Thanks for moxie group
gwt-upload-project page
//upload spring service conroller
#RequestBody public void uploadImage(#RequestParam("file") MultipartFile file ){
//what ever you want
}
XML Configuration
<bean id=multipartResolver" class ="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
GWT Elemental contains all you need already AFAICT.
I am working on an Android Application that opens an HTML page in a webview within the app. The HTML page is stored in the assets folder and I call it by means of
loadUrl("file:///android_asset/a.html");
Now, the page is such that it accepts parameters from the URL (javascript). I need to know how do we pass URL parameters to this html file that is stored inside the assets folder. Writing them like this:
loadUrl("file:///android_asset/a.html?q=2&w=3");
doesn't work.
Is there any other way?
Riya
If your HTML page handles the parameters via javascript (and i can't think of any other way it can handle them), you can call a javascript function in your code with the parameters after the page is loaded, and pass parameters to it.
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
webview.loadUrl("javascript:(function() { setParameters(2,3)})()");
}
});
webview.loadUrl("file:///android_asset/a.html");
I tried the method suggested by shaish and many others for passing url parameters to an 'asset folder' url. It seems that this is a well known bug for android 3 and 4 (http://code.google.com/p/android/issues/detail?id=17535). Simply calling webview.loadUrl("file:///android_asset/a.html?q=2&w=3") works on a blackberry 10 simulator.
A similar explanation like shaish but adapted to my own case:
My problem was that anytime I switched to another app, when coming to the webapp, the webview kept reloading. I guess that's because of the following line in my onCreate() method: myWebView.loadUrl(url); I had the idea to pass these state variables in the url, but as you know it is not possible yet.
What I did was to save the state of some variables using onSaveInstanceState(Bundle outState) {...} and restore them with onRestoreInstanceState(Bundle savedInstanceState){...}.
In onCreate method after setting up myWebView I did the following:
myWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String urlString)
{
Log.i("onPageFinished", "loadVariables("+newURL+")");
if(newURL!="")
myWebView.loadUrl("javascript:loadVariables("+"\""+newURL+"\")");
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
jsInterface = new JSInterface(this,myWebView);
myWebView.addJavascriptInterface(jsInterface, "Android");
if (savedInstanceState != null)
{
// retrieve saved variables and build a new URL
newURL = "www.yoururl.com";
newURL +="?var1=" + savedInstanceState.getInt("key1");
newURL +="?var2=" + savedInstanceState.getInt("key2");
Log.i("myWebApp","NEW URL = " + newURL);
}
myWebView.loadUrl("www.yoururl.com");
So, what it happens is that first I load the page and then I pass the variables when the page finished to load.
In javascript loadVariables function looks like this:
function loadVariables(urlString){
// if it is not the default URL
if(urlString!="www.yoururl.com")
{
console.log("loadVariables: " + urlString);
// parse the URL using a javascript url parser (here I use purl.js)
var source = $.url(urlString).attr('source');
var query = $.url(urlString).attr('query');
console.log("URL SOURCE = "+source + " URL QUERY = "+query);
//do something with the variables
}
}
In an iOS app, I used
stringFromJavaScript = [webView stringByEvaluatingJavascriptFromString:#"document.getElementById(\"image\").getAttribute(\"src")"];
To get the src directory of the image that was being displayed on the webView. I want to do the same for Android. What are my options?
Basically the intent is to capture the path so that I can email this same picture...
ie.
"picture.php?image=%#",stringFromJavascript
This way, that same image would be loaded when the user clicks the link, or posts it to facebook etc.
Yeah, I miss this method greatly in Android ;)
To execute JavaScript and get response you can do as follows:
Define JavaScript callback interface in your code:
class MyJavaScriptInterface {
#JavascriptInterface
public void someCallback(String jsResult) {
// your code...
}
}
Attach this callback to your WebView
MyJavaScriptInterface javaInterface = new MyJavaScriptInterface();
yourWebView.addJavascriptInterface(javaInterface, "HTMLOUT");
Run your JavaScript calling window.HTMLOUT.someCallback from the script:
yourWebView.loadUrl("javascript:( function () { var resultSrc = document.getElementById(\"image\").getAttribute(\"src\"); window.HTMLOUT.someCallback(resultSrc); } ) ()");
Hope this helps!
I am using HtmlUnit headless browser to browse this webpage (you can see the webpage to have a better understanding of the problem).
I have set the select's value to "1"
by the following commands
final WebClient webClient = new WebClient(BrowserVersion.INTERNET_EXPLORER_7);
try {
// Configuring the webClient
webClient.setJavaScriptEnabled(true);
webClient.setThrowExceptionOnScriptError(false);
webClient.setCssEnabled(true);
webClient.setUseInsecureSSL(true);
webClient.setRedirectEnabled(true);
webClient.setActiveXNative(true);
webClient.setAppletEnabled(true);
webClient.setPrintContentOnFailingStatusCode(true);
webClient.setAjaxController(new NicelyResynchronizingAjaxController());
// Adding listeners
webClient.addWebWindowListener(new com.gargoylesoftware.htmlunit.WebWindowListener() {
public void webWindowOpened(WebWindowEvent event) {
numberOfWebWindowOpened++;
System.out.println("Number of opened WebWindow: " + numberOfWebWindowOpened);
}
public void webWindowContentChanged(WebWindowEvent event) {
}
public void webWindowClosed(WebWindowEvent event) {
numberOfWebWindowClosed++;
System.out.println("Number of closed WebWindow: " + numberOfWebWindowClosed);
}
});
webClient.setWebConnection(new HttpWebConnection(webClient) {
public WebResponse getResponse(WebRequestSettings settings) throws IOException {
System.out.println(settings.getUrl());
return super.getResponse(settings);
}
});
CookieManager cm = new CookieManager();
webClient.setCookieManager(cm);
HtmlPage page = webClient.getPage("http://www.ticketmaster.com/event/0B004354D90759FD?artistid=1073053&majorcatid=10002&minorcatid=207");
HtmlSelect select = (HtmlSelect) page.getElementById("quantity_select");
select.setSelectedAttribute("1", true);
and then clicked on the following button
by the following commands
HtmlButtonInput button = (HtmlButtonInput) page.getElementById("find_tickets_button");
HtmlPage captchaPage = button.click();
Thread.sleep(60*1000);
System.out.println("======captcha page=======");
System.out.println(captchaPage.asXml());
but even after clicking on the button and waiting for 60 seconds through the Thread.sleep() method, I am getting the same HtmlPage.
But when I do the same thing through real browser then I get the page that contains CAPTCHA.
I think I am missing something in the htmlunit.
Q1. Why am I not getting the same page (that contains CAPTCHA) through htmlunit's browser?
The web form on that page requires the quantity_select drop-down to be filled in. You're attempting to do that in your code by assuming the drop-down is a select element. However, it's no longer a select element. Try using Firebug to inspect the drop-down and you'll see that JavaScript has replaced the select with a complex set of nested div elements.
If you figure out how to emulate each user click on the divs for that unusual drop-down then you should be able to submit the form.