the problem I'm having is injecting some JavaScript into a WebView. I only have this problem on Android version 2.3.6 and below I'm assuming, don't have a test device lower than that though. My code works fine in Android 4+ so I'm not quite sure why it's failing. It seems to "submit" the form but doesn't fill out the username and password field in 2.3.6 so it always fails. My main goal is to simulate a form fill-out and submit in a WebView with two EditTexts so the user doesn't have to interact with the WebView itself. When the user hits the login button it runs this code:
//Set what needs to be filled out in the WebView
String javaScript = "(function(){ " +
"document.getElementById('user').value = '" + mUsername + "'; " +
"document.getElementById('password').value = '" + mPassword + "'; " +
"document.getElementById('form').submit(); " +
"})()";
//Load the javascript here
mWebView.loadUrl("javascript: " + javaScript);
I'm also setting my WebView settings to this:
//Setup the WebView options/settings
...
mWebView.getSettings().setSaveFormData(false);
mWebView.getSettings().setJavaScriptEnabled(true);
...
I've tried searching for any reason why this wouldn't work on 2.3.6 but haven't come up with anything. Is there some restriction in version 2.* that isn't present in 4+?
Edit: The button that runs the above code is only enabled once the page is finished loading. So when this button is clicked the above JS injection is run.
#Override
public void onPageFinished(WebView view, String url) {
...
//Only allow the user to inject the JS when the page has loaded
if(url.equals("...")
button.setEnabled(true);
...
}
SOLUTION: I'm not sure if this actually was the cause but changing the single quotes in the JS code to use double quotes seemed to make it run. Something like:
String javaScript = ... + "document.getElementById(\"user\").value = \"" + mUsername + "\"; "
There aren't any restrictions that I'm aware of but it's not uncommon to encounter issues stemming from differing Javascript engines in various versions of Android. It is likely simply a matter of trying different syntax to achieve the same result in an attempt to hit upon a version of the logic that makes that version of Javascript happy.
Including JQuery and writing your function using Jquery syntax might be a solution, too. As JQuery itself seems pretty good at dealing with these differences.
At which point in your flow does the JavaScript URL get loaded? Are you certain that at this time the DOM is loaded and the elements you reference are valid?
Do you see anything output in logcat when trying to execute the JavaScript? That should show JavaScript errors.
Related
I want to use JavaFX WebEngine in such a way that I can execute Javascript code in order to fill one form and click submit button on the website I opened. I was using ChromeDriver but I didn't like certain aspects of it, WebView fits better for my needs, but as far as I researched, the only way to make sure a page is loaded is to add a state listener to WebEngine and when it's SUCCEEDED, make operations on webpage. However, when I execute Javascript and submit a form, a new webpage opens so it's SUCCEEDED for the second time (first time, opens webpage that contains the form) and it executes the same form-filling code in listener. I want to execute other Javascript code after submitting the form but I couldn't find a good way to do that. In ChromeDriver, I could simply wait for certain amount of time, but with WebView case, because it executes in UI thread, I can't do that. Thanks.
I'm not sure if that's what you mean. SUCCEEDED is just a information about state of engine, to be more accurate you should check other parameters, for instance: If you want to handle different pages inside one listener, you can use location-based (currently loaded url) verification.
WebEngine engine = webView.getEngine();
engine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != State.SUCCEEDED) {
return;
}
String location = engine.getLocation();
if (location.contains("page1")) {
// Do something
return;
}
if (location.contains("page2")) {
// Do something
return;
}
System.out.println("Unhandled location: " + location);
});
I get a white page after filling in some forms and uploading a photo on https://indianvisaonline.gov.in/visa/index.html. I use JavaFX’s WebView to load the website. Everything works fine until I hit the photo upload button.
Filling in the form works perfectly fine with other browsers and was successful with Chrome, Firefox and Safari. Therefore I suspect it’s something specific to JavaFX’s WebView.
I tried ruling out any bugs by writing only the WebView part. Yet the white page is still displayed.
public class SimpleTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
WebView webView = new WebView();
webView.getEngine().load("https://indianvisaonline.gov.in/visa/indianVisaReg.jsp");
Scene scene = new Scene(webView);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
}
JavaFX WebEngine’s HTML 5 support
I notice the lack of HTML5 support compared to other browsers. JavaFX’s WebView (WebEngine) scores just 275 out of 555 points, whereas other browsers score way better. The things that I feel can break something is the lack of Web Cryptography API, Database storage, File Reading or limited ECMAScript 6 support. Yet I have no idea how to test this?
Testing with Firebug Lite
When embedding Firebug, something weird happens. Instead of showing a blank white page, it redirects me to the homepage. So Firebug can’t help me here either.
Debug
Trying to find something in the logs I added: -Djavax.net.debug=all. However this doesn’t return any errors either (like HTTPS related).
At this point I’d by happy with any advice!
On a project with JavaFX Webview we found the following useful:
webView.setContextMenuEnabled(true);
webView.getEngine().setOnError(event -> System.out.println(event.getMessage()));
webView.getEngine().setOnAlert(event -> System.out.println(event.getData()));
// local error console
com.sun.javafx.webkit.WebConsoleListener.setDefaultListener(
(webview, message, lineNumber, sourceId) -> System.out
.println("Console: [" + sourceId + ":" + lineNumber + "] " + message));
// Firebug lite
webView.getEngine().getLoadWorker().stateProperty().addListener(
(ChangeListener<State>) (ov, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
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');}");
}
});
webView.getEngine().locationProperty().addListener(
(observable, oldValue, newValue) -> System.out.println(newValue));
Also it seems the browser that behaves similarly to Webview is Safari. If something doesn't work on Safari it generally doesn't on Webview. Safari has better tools for development however.
Also you might pay attention to popups and sites that open new windows, this must be handled manually with Webview.
I'm facing an issue when application I'm testing is opening a second level popup (I mean, a popup that rises another popup). I'm using Selenium for Java, version 2.42.2, with IE Driver.
My code currently manages all other flows and single pop ups windows, but when a second level popup appears my webdriver object gets irresponsive and just stuck in any method that try to get invoked. When this happens, I need to manually close the popup window and then driver method continues the execution of invoked method.
Maybe it worth mentioning, if I call the second popup from the main page (not from another popup) I am able to manage it without problems, so the problem seems to be related with the number of nested windows more than a specific problem for the page.
Take a look on below code:
String url = "/example.jsp"
int counter = 0;
boolean windowDetected = false;
// Waiting until page with given url gets detected
// It tries 10 times before it continues
while (counter++ < 10 && !windowDetected) {
waitForSeconds(1); // Just invokes Thread.sleep method,
System.out.println("before getting handlers, iteration " + counter + " of 10");
Set<String> handlers = driver.getWindowHandles();
System.out.println("after getting handlers, number of handlers: " + handlers.size());
for (String handler : handlers) {
try {
System.out.println("Moving to " + handler);
driver.switchTo().window(handler);
}
catch (NoSuchWindowException e) {
System.out.println("Window not found, it probably redirected to a different window");
break;
}
if (driver.getCurrentUrl().contains(url)) {
windowDetected = true;
waitForPageLoading(); // It just verify current page status to be completed
break;
}
}
}
I am able to position properly the driver in specific window with above code in other application pages, but when having the second level popup it just hangs on line Set handlers = driver.getWindowHandles(); that is the first method called on driver object after popup appeared. Similarly, if I had called another driver method like driver.getPageSource() the same would have happened in that method.
I am not able to change application structure (my scope is only for testing it), so doing changes in web application code is not an option for me now. I have read on different options like trying Robot class, but I think it will be difficult to implement with more complex pages and difficult to maintain.
Any idea of how can I solve this issue? Many thanks in advance.
Best regards,
Alberto.
After taking a look on IEDriverServer changelog, moving to a newer version of this driver solved the issue. Previous was 2.24.2, and now using 2.44. A slightly different behavior but that accomplishes what I wanted
I'm looking for a way to do file uploads, with a custom progress bar, with google web toolkit. Not looking for a plugin which shows its own progress bar, I'm rather looking for something which will call my callback method and notify it of progress, so I can show my own progress bar.
If that's not possible, then I'd like to know how to access the HTML5 File API so I can build my own file uploader widget.
Any ideas?
Thanks.
There are many issues in GWT which makes difficult deal with this:
There is no FormData object in GWT, so you have to use JSNI to get one.
RequestBuilder does not support sending FormaData but only strings.
RequestBuilder does not support upload or download progress notifications.
FileUpload does not support the multiple attribute.
The Elemental package in GWT only works with webkit browsers, and I had some issues last time I tried to use the File Api.
Fortunately, gwtquery 1.0.0 has a bunch of features which facilitates to solve the problem.
Here you have a working example (with very few lines of code), which works in all browsers supporting the HTML5 file api:
import static com.google.gwt.query.client.GQuery.*;
[...]
final FileUpload fileUpload = new FileUpload();
RootPanel.get().add(fileUpload);
$(fileUpload)
// FileUpload does not have a way to set the multiple attribute,
// we use gQuery instead
.prop("multiple", true)
// We use gQuery events mechanism
.on("change", new Function() {
public void f() {
// Use gQuery utils to create a FormData object instead of JSNI
JavaScriptObject form = JsUtils.runJavascriptFunction(window, "eval", "new FormData()");
// Get an array with the files selected
JsArray<JavaScriptObject> files = $(fileUpload).prop("files");
// Add each file to the FormData
for (int i = 0, l = files.length(); i < l; i++) {
JsUtils.runJavascriptFunction(form, "append", "file-" + i, files.get(i));
}
// Use gQuery ajax instead of RequestBuilder because it
// supports FormData and progress
Ajax.ajax(Ajax.createSettings()
.setUrl(url)
.setData((Properties)form))
// gQuery ajax returns a promise, making the code more declarative
.progress(new Function() {
public void f() {
double total = arguments(0);
double done = arguments(1);
double percent = arguments(2);
// Here you can update your progress bar
console.log("Uploaded " + done + "/" + total + " (" + percent + "%)");
}
})
.done(new Function() {
public void f() {
console.log("Files uploaded, server response is: " + arguments(0));
}
})
.fail(new Function() {
public void f() {
console.log("Something went wrong: " + arguments(0));
}
});
}
});
Another option is to use gwtupload, which supports any browser, and it comes with some nice widgets and progress bar, even you can plug your own progress and status widgets.
It does not use HTML5 File api yet but an ajax solution based on a server listener. It will though, support html5 in future versions, falling back to ajax mechanism for old browsers. When gwtupload supports this, you wont have to modify anything in your code.
All the building blocks are in Elemental but might not work everywhere (Elemental is "close to the metal", without any support detection or hiding/working around browser bugs/discrepancies).
Or you can use JSNI.
I have been asked by my friend to make an application for Chrome and it requires me to have context-sensitive menus as below:
I have never really made anything for Chrome before and I have a few questions regarding it:
I will have to develop a plug-in, right ?
If so, is there a specific set of rules I have to follow ?
I know I can use GWT to compile Java to JavaScript
3. This context sensitive menu is the same as JPopupMenu ?
The application I want to develop is simple:
Copy some text,
right-click, click on the context sensitive menu
apply simple Caesar's cipher to the text
open a new JFrame with JtextArea in it to display the encrypted text.
What you're creating is called an "extension", not a "plug-in". A browser extension is written using HTML, CSS and Javascript, and got access to APIs for direct interaction with the browser.
Plug-ins, on the other hand, are compiled binaries such as Flash and Java.
Drop the idea of using GWT for Chrome extensions. It makes development of the extension harder, not easier (open issue).
Especially because you'll find plenty of vanilla JavaScript examples and tutorials in the documentation and Stack Overflow.
You just have to know the relevant APIs:
Copy some text,
right-click, click on the context sensitive menu
Use chrome.contextMenus. There's no need to copy, the selected text is available in the callback (examples).
apply simple Caesar's cipher to the text
Create a JavaScript function to achieve this.
open a new JFrame with JtextArea in it to display the encrypted text.
Create a new window using chrome.windows.create. You could include an extra HTML page in your extension, and use the message passing APIs to populate the text field, but since you appear to be a complete newbie, I show a simple copy-paste method to create and populate this window:
function displayText(title, text) {
var escapeHTML = function(s) { return (s+'').replace(/</g, '<'); };
var style = '*{width:100%;height:100%;box-sizing:border-box}';
style += 'html,body{margin:0;padding:0;}';
style += 'textarea{display:block;}';
var html = '<!DOCTYPE html>';
html += '<html><head><title>';
html += escapeHTML(title);
html += '</title>';
html += '<style>' + style + '</style>';
html += '</head><body><textarea>';
html += escapeHTML(text);
html += '</body></html>'
var url = 'data:text/html,' + encodeURIComponent(html);
chrome.windows.create({
url: url,
focused: true
});
}
Don't forget to read Getting started to learn more about the extension's infrastructure.
Check out Google Chrome Extensions Chrome Extensions
The Getting Started will help you Getting Started
You will find a section on how to use Context Menus.