Selenium/WebView get clicked element - java

Is it possible to open a browser using the WebDriver class in Selenium and get the elements that the user is clicking?
I already looked through the documentation of selenium and found nothing useful.
I already thought about inserting a javascript function into the webpage, that gets called whenever a clickable element is clicked, but I dont know how I would then retrieve that information into my java programm. Any ideas on how to solve this problem?

Managed to solve this problem with the hint that Saurabh Gaur gave me
Here is my HTML document that I tested the application with, its called Index.html:
<html>
<head>
<title>I am the title, haha!</title>
</head>
<body>
<p id="id1">I am id1</p>
end my suffering
<body>
</html>
and here is my java code. all it does is add a listener to the HTML elements:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws MalformedURLException {
WebView webView = new WebView();
WebEngine engine = webView.getEngine();
engine.load(new File("PATH/TO/Index.html").toURI().toURL().toExternalForm());
Scene scene = new Scene(webView);
stage.setScene(scene);
stage.show();
//we need this to check if the document has finished loading, otherwise it would be null and throw a exception
engine.getLoadWorker().stateProperty().addListener((obs, oldState, currentState) -> {
if (currentState == State.SUCCEEDED) {
Document doc = engine.getDocument();
addListeners(doc);
}
});
}
private void addListeners(Document doc) {
Element link1 = doc.getElementById("id1");
((EventTarget) link1).addEventListener("click", e -> {
System.out.println("id1 was clicked!");
}, false);
Element link2 = doc.getElementById("ihatejava");
((EventTarget) link2).addEventListener("click", e -> {
System.out.println("ihatejava was clicked!");
}, false);
}
}

Related

Cannot add CSS classes to my JavaFX Label

I am making a simple todolist app. Here is a watered-down version of it:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
ListView<Label> todoListView = new ListView();
Label doneTodo = new Label ("This todo is meant to be finished and struck through");
doneTodo.getStyleClass().add("done"); // Add the "done" class to this to-do
Label undoneTodo = new Label("This todo is meant to be undone and therefore isn't struck through");
// Add both to-dos to the listview
addTodo(doneTodo, todoListView);
addTodo(undoneTodo, todoListView);
// Set the listview as the scene, and add the stylesheet
Scene scene = new Scene(todoListView, 600, 550);
scene.getStylesheets().add(getClass().getResource("styles.css").toExternalForm());
primaryStage.setTitle("Label not taking on Strikethrough");
primaryStage.setScene(scene);
primaryStage.show();
}
// Adds the to-do (in this case just a simple Label) to the listview
private static void addTodo(Label todo, ListView<Label> todoList) {
todoList.getItems().add(todo);
}
public static void main(String[] args) {
launch(args);
}
}
Here is my CSS class:
.done {
-fx-strikethrough: true;
}
When I run the code, the -fx-strikethrough property does not show up on my Label. Note that although this is a simplified version of my app, the issue remains the same: The text inside the JavaFX Label is not being struck through.
Again, sorry for any inadequacies in my question. I am fairly new to Stack Overflow!
Thanks in advance.
The CSS rule should apply to the text node under the label node:
.done .text {
-fx-strikethrough: true;
}

How do I load live DOM in Java Application?

I'm not really sure how to ask this question but here is what I am trying to do. I am looking for the ability to load a website in java application and being able to click the buttons, text, box, etc and get the underlying DOM code.
For example:
-It loads google.com the webpage as well as the LIVE DOM under it, different frame. It can't be HTML since google is javascript.
-I want to click the google search box, or anything on the page, and application will print any attribute, such as name="q" or just q.
I've looked into XULrunner but it is deprecated for java.
You can use JavascriptExecutor for that. It is implemented by all WebDrivers. See JavaDoc for details. Use documentto refer to the DOM.
You can use JavaFX WebView.
You can call back to your Java code from Javascript. So you could write a small amount of Javascript that detects when the user clicked on element, and call some Java code that shows attribute value, for example. See "Calling back to Java from Javascript" in the WebEngine API docs.
Sample code:
public class HTMLMouseOverTest extends Application {
#Override
public void start(Stage primaryStage) {
final BorderPane root = new BorderPane();
final WebView webView = new WebView();
final WebEngine engine = webView.getEngine();
final BooleanProperty mouseOver = new SimpleBooleanProperty();
engine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
#Override
public void changed(ObservableValue<? extends State> observable,
State oldValue, State newValue) {
if (newValue == State.SUCCEEDED) {
// Here's how to add the Javascript if you don't have
// direct access to the HTML:
// final Document doc = engine.getDocument();
// Element div = doc.getElementById("important-div");
// div.setAttribute("onmouseover", "mouseOverProperty.set(true)");
// div.setAttribute("onmouseout", "mouseOverProperty.set(false)");
final JSObject window = (JSObject) engine.executeScript("window");
window.setMember("mouseOverProperty", mouseOver);
}
}
});
engine.loadContent("<html><body style='font-family:sans-serif';><h2>Hello World</h2>"+
"<div id='important-div' onmouseover='mouseOverProperty.set(true)'"+
"onmouseout='mouseOverProperty.set(false)' style='background: #ffd; padding:40px;'>"+
"Move mouse here</div>"+
"<h3>Thanks and good night</h3></body></html>");
root.setCenter(webView);
final Label label = new Label();
label.textProperty().bind(Bindings.when(mouseOver).then("Mouse in position").otherwise("Mouse out of area"));
root.setBottom(label);
final Scene scene = new Scene(root, 400, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}

JavaFX Webview : load succeeds but render fails

I have a Webview (JavaFX 8) that load an article from Wikipedia.
I put a refresh button to allow a refresh, basically, it does another call to the load method of the webEngine of the WebView with the same URL. But about 50% of the time the article is never rendered. In this case, I can right-click on the web view to manually refresh, then it will be rendered successfully.
I tried to look at the LoadWorker state, it always says "SUCCEED"...
Below is a short runnable Test class that demonstrates my point.
public class Test1 extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
final WebView webView = new WebView();
webView.getEngine()
.load("http://fr.wikipedia.org/wiki/Sp%C3%A9cial:Page_au_hasard");
Tab tab = new Tab("webView", webView);
TabPane tabPane = new TabPane(tab);
BorderPane borderPane = new BorderPane(tabPane);
Button buttonRefresh = new Button("Refresh");
buttonRefresh.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
webView.getEngine()
.load("http://fr.wikipedia.org/wiki/Sp%C3%A9cial:Page_au_hasard");
}
});
borderPane.setBottom(buttonRefresh);
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main (String[] args) {
launch(args);
}
}
Am I doing something wrong? Does anyone know where this problem might come from?
EDIT
I added a few lines to bypass the problem, I check the header in the DOM when the state of the loadWorker becomes SUCCESS. If it is empty, I reload. Now it (looks) 100% ok, but still I am very curious why it didn't simply work all the time first.
Some thoughts that may be useful
The link http://fr.wikipedia.org/wiki/Sp%C3%A9cial:Page_au_hasard redirects to a random article (that's why my refresh button can't use webView.getEngine.reload()).
Putting a ChangeListener on the stateProperty of the workLoader to call the reload method of webEngine actually works. Each page is rendered successfully, but already rendered page will also be re-rendered, which is terrible.

javafx snapshot without showing application or Scene

Hi i'm using JavaFx WebView to create Screenshot of HTML pages and it works fine but i wanted to know is it possible to do this without launching the application in Graphical Windows!! I mean aren't there any more lightweight method to get the screenshot then this:
public class WebViewSample extends Application {
private Scene scene;
#Override
public void start(Stage stage) {
// create scene
scene = new Scene(new Browser(snapshot), 750, 500, Color.web("#666970"));
stage.setScene(scene);
// show stage
stage.show();
}
WritableImage snapshot;
public static void main(String[] args) {
launch(args);
System.err.println("launched!");
}
}
class Browser extends Region {
final ImageView selectedImage = new ImageView();
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
private final WritableImage snapshotImage;
public Browser(WritableImage snapshot) {
this.snapshotImage= snapshot;
// process page loading
webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<State>() {
#Override
public void changed(ObservableValue<? extends State> ov,
State oldState, State newState) {
if (newState == State.SUCCEEDED) {
WritableImage newSnapshot = browser.snapshot(null, snapshotImage);
File file = new File("test2.png");
RenderedImage renderedImage = SwingFXUtils.fromFXImage(newSnapshot, null);
try {
ImageIO.write(renderedImage, "png", file);
} catch (IOException e1) {
e1.printStackTrace();
}
System.exit(0);
}
}
}
);
// load the home page
webEngine.load("http://localhost/");
//add components
getChildren().add(browser);
}
}
For JavaFX 2.2 and below, there's not functionality of the kind.
Headless JavaFX applications are currently not possible, and a main JavaFX thread is mandatory.
The best you can do is read upon several workarounds for achieving this.
Related StackOverflow questions:
JavaFX for server-side image generation
Generating image at server side using Java FX
JavaFx in headless mode
How to test JavaFX 2 in a headless environment?

Java GUI to display webpages and return HTML

I need a workflow like below:
// load xyz.com in the browser window
// the browser is live, meaning users can interact with it
browser.load("http://www.google.com");
// return the HTML of the initially loaded page
String page = browser.getHTML();
// after some time
// user might have navigated to a new page, get HTML again
String newpage = browser.getHTML();
I am surprised to see how hard this is to do with Java GUIs such as JavaFX (http://lexandera.com/2009/01/extracting-html-from-a-webview/) and Swing.
Is there some simple way to get this functionality in Java?
Here is a contrived example using JavaFX that prints the html content to System.out - it should not be too complicated to adapt to create a getHtml() method. (I have tested it with JavaFX 8 but it should work with JavaFX 2 too).
The code will print the HTML content everytime a new page is loaded.
Note: I have borrowed the printDocument code from this answer.
public class TestFX extends Application {
#Override
public void start(Stage stage) throws Exception {
try {
final WebView webView = new WebView();
final WebEngine webEngine = webView.getEngine();
Scene scene = new Scene(webView);
stage.setScene(scene);
stage.setWidth(1200);
stage.setHeight(600);
stage.show();
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends State> ov, State t, State t1) {
if (t1 == Worker.State.SUCCEEDED) {
try {
printDocument(webEngine.getDocument(), System.out);
} catch (Exception e) { e.printStackTrace(); }
}
}
});
webView.getEngine().load("http://www.google.com");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void printDocument(Document doc, OutputStream out) throws IOException, TransformerException {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(out, "UTF-8")));
}
public static void main(String[] args) {
launch(args);
}
}
Below you will find a SimpleBrowser component which is a Pane containing a WebView.
Source code at gist.
Sample usage:
SimpleBrowser browser = new SimpleBrowser()
.useFirebug(true);
// ^ useFirebug(true) option - will enable Firebug Lite which can be helpful for
// | debugging - i.e. to inspect a DOM tree or to view console messages
Scene scene = new Scene(browser);
browser.load("http://stackoverflow.com", new Runnable() {
#Override
public void run() {
System.out.println(browser.getHTML());
}
});
browser.getHTML() is put inside a Runnable because one needs to wait for a web page to download and render. Trying to invoke this method before page loading will return an empty page, so wrapping this into a runnable is a simple way I came up with to wait for a page to load.
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.scene.layout.*;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
public class SimpleBrowser extends Pane {
protected final WebView webView = new WebView();
protected final WebEngine webEngine = webView.getEngine();
protected boolean useFirebug;
public WebView getWebView() {
return webView;
}
public WebEngine getEngine() {
return webView.getEngine();
}
public SimpleBrowser load(String location) {
return load(location, null);
}
public SimpleBrowser load(String location, final Runnable onLoad) {
webEngine.load(location);
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State t1) {
if (t1 == Worker.State.SUCCEEDED) {
if(useFirebug){
webEngine.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');}");
}
if(onLoad != null){
onLoad.run();
}
}
}
});
return this;
}
public String getHTML() {
return (String)webEngine.executeScript("document.getElementsByTagName('html')[0].innerHTML");
}
public SimpleBrowser useFirebug(boolean useFirebug) {
this.useFirebug = useFirebug;
return this;
}
public SimpleBrowser() {
this(false);
}
public SimpleBrowser(boolean useFirebug) {
this.useFirebug = useFirebug;
getChildren().add(webView);
webView.prefWidthProperty().bind(widthProperty());
webView.prefHeightProperty().bind(heightProperty());
}
}
Demo Browser:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
import javafx.stage.Stage;
public class FXBrowser {
public static class TestOnClick extends Application {
#Override
public void start(Stage stage) throws Exception {
try {
SimpleBrowser browser = new SimpleBrowser()
.useFirebug(true);
final TextField location = new TextField("http://stackoverflow.com");
Button go = new Button("Go");
go.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
browser.load(location.getText(), new Runnable() {
#Override
public void run() {
System.out.println("---------------");
System.out.println(browser.getHTML());
}
});
}
});
HBox toolbar = new HBox();
toolbar.getChildren().addAll(location, go);
toolbar.setFillHeight(true);
VBox vBox = VBoxBuilder.create().children(toolbar, browser)
.fillWidth(true)
.build();
Scene scene = new Scene( vBox);
stage.setScene(scene);
stage.setWidth(1024);
stage.setHeight(768);
stage.show();
VBox.setVgrow(browser, Priority.ALWAYS);
browser.load("http://stackoverflow.com");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
}
There is not a simple solution. In fact, there might not even be a solution at all short of building your own browser.
The key issue is interaction. If you want to display content only, then JEditorPane and many third party libs make that a more attainable goal. If you really need a user interacting with a webpage then either:
Have the user use a normal browser to interact
Build a GUI that makes calls to web services/urls to do the interaction, but the display is up to you.
On the returning the HTML side of things, it sounds like you are trying to capture history or refresh the page. In either case, it sounds like you are in the wrong technology. Either modify the original site, or add in some java script in the browser with Greasemonkey or something similar.
You may want to see to djproject. But possibly you'll find JavaFX usage easier.
Depending on stuff I don't know about your project this is either genious or moronic, but you could use a real browser in stead and instrument it with Selenium Webdriver. Only suggesting this as it appears from the other answer that you are going down a difficult path.
There's another question about extracting html with webdriver here. It's about using python, but webdriver has a java api as well.
I was able to get the executed html. I kept the alert statement after the html is loaded in JavaScript. I used webEngine.setOnAlert method to check if the alert was executed and then printed the html. I got the correct response. Below is the code
HTML
alert("ready");
JavaFx Application
webEngine.setOnAlert(new EventHandler<WebEvent<String>>(){
#Override
public void handle(WebEvent<String> event) {
//labelWebTitle.setText(webEngine.getTitle());
if("ready".equals(event.getData())){
//TODO: initialize
System.out.println("HTML Ready");
WebEngine engine = (WebEngine)event.getSource();
String html = (String) engine.executeScript("document.documentElement.outerHTML");
org.jsoup.nodes.Document doc = Jsoup.parse(html);
Element image = doc.getElementById("canvasImage");
System.out.println(image.attr("src"));
}
}
});

Categories

Resources