I am working on a JavaFX application which contains few html,css,JS files which are rendered by the internal webkit browser. Now, the problem is the CSS animations which we have are not getting rendered smoothly in the webkit browser provided by JavaFX, but same code in Firefox or chrome is quite smoother.
Also, no persistence is available(currently using variables in Java, and communication via JS for persistence).
What I am looking for is there any way to integrate some headless browser, or some settings to make CSS animations smoother. Only thing I came across was JxBrowser, but it's toooooo costly for personal usage.
Code :
public class Main extends Application {
private Scene scene;
MyBrowser myBrowser;
String completeText = "";
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Frontend");
java.net.CookieManager manager = new java.net.CookieManager();
java.net.CookieHandler.setDefault(manager);
myBrowser = new MyBrowser();
scene = new Scene(myBrowser, 1080, 1920);
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.show();
// # being the escape character
scene.setOnKeyTyped(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
String text = event.getCharacter();
if (text.equals("0")) {
String tempText = completeText;
completeText = "";
processText(tempText);
}else {
completeText = completeText+text;
}
}
});
}
}
MyBrowser :
public class MyBrowser extends Region {
public MyBrowser() {
webEngine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", this);
}
});
URL urlHello = getClass().getResource(hellohtml);
webEngine.load(urlHello.toExternalForm());
webView.setPrefSize(1080, 1920);
webView.setContextMenuEnabled(false);
getChildren().add(webView);
}
CSS code which contains animation :
#ball-container.go #ball{
-webkit-animation: rotating-inverse 2s ease-out 0s 1 normal;
animation: rotating-inverse 2s ease-out 0s 1 normal;
}
#ball-container {
height: 102px;
width: 102px;
position: absolute;
top: -95px;
left: 480px;
-webkit-transition: all 0.9s ease-in-out 0s;
transition: all 0.9s ease-in-out 0s;
}
#ball-container.shake .ball-wrapper{
-webkit-animation: yAxis 0.9s ease-in-out;
animation: yAxis 0.9s ease-in-out;
}
Thank you.
Try using Java 8u112, based in your code I created a working sample (Tested using Java JDK 8u112 64bit):
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class Example extends Application
{
public static void main(String[] args)
{
launch(args);
}
class MyBrowser extends Parent
{
private WebEngine webEngine;
private WebView webView;
public MyBrowser()
{
webView = new WebView();
webEngine = webView.getEngine();
// Ugly (but easy to share) HTML content
String pageContents =
"<html>"
+ "<head>"
+ "<style>"
+ "#-webkit-keyframes mymove {"
+ "from {top: 0px;}"
+ "to {top: 50px;}"
+ "}"
+ ".box { "
+ "width: 150px; "
+ "position: relative; "
+ "height: 150px; "
+ "background: red; "
+ "margin-top: 35px; "
+ "margin-left: auto; "
+ "margin-right: auto; "
+ "-webkit-transition: background-color 2s ease-out; "
+ "-webkit-transition: all 1s ease-in-out; "
+ "}"
+".box:hover {"
+" background-color: green;"
+ "width:350px;"
+ "-webkit-animation: mymove 1s infinite;"
+"}"
+ "</style>"
+ "</head>"
+ "<body>"
+ "<div class='box'></div>"
+ "</body>"
+ "</html>";
webEngine.loadContent(pageContents);
webView.setContextMenuEnabled(false);
getChildren().add(webView);
}
}
private Scene scene;
MyBrowser myBrowser;
#Override
public void start(Stage primaryStage) throws Exception
{
primaryStage.setTitle("Frontend");
myBrowser = new MyBrowser();
scene = new Scene(myBrowser);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I suspect this is because they are now using a newer webkit JDK-8156698 but it might have been a bug before (you can take a look at the 8u112 bug fixes list.
I had same issue rendering the angular based web application using webview.and fixed it by upgrading the Java version to 1.8.0_121.
Related
Upcalls from JavaScript to JavaFX works fine. But if I add a variable containing the VLCJ player implementation then everything stops working.
Main.java:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import netscape.javascript.JSObject;
public class Main extends Application {
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
VLC player = new VLC(); // It doesn't work because of it. But if you remove that, then everything works.
#Override
public void start(final Stage stage) {
webEngine.setJavaScriptEnabled(true);
String HTML_STRING = //
"<html>"//
+ "<head> " //
+ " <script language='javascript'> " //
+ " function callToJavaFX() { "//
+ " myJavaMember.log('text'); " //
+ " } " //
+ " </script> "//
+ "</head> "//
+ "<body> "//
+ " <h2>This is Html content</h2> "//
+ " <button onclick='callToJavaFX();'>Call To JavaFX</button> "//
+ "</body> "//
+ "</html> "//
;
webEngine.loadContent(HTML_STRING);
Worker<Void> worker = webEngine.getLoadWorker();
worker.stateProperty().addListener(new ChangeListener<State>() {
#Override
public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
if (newValue == Worker.State.SUCCEEDED) {
JSObject jsobj = (JSObject) webEngine.executeScript("window");
jsobj.setMember("myJavaMember", new JSBridge());
}
}
});
StackPane stack_pane = new StackPane(browser);
stage.setScene(new Scene(stack_pane,700, 400, Color.BLACK));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
JSBridge.java:
public class JSBridge {
public void log(String text) {
System.out.println(text);
}
}
VLC.java:
import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
public class VLC{
private final MediaPlayerFactory mediaPlayerFactory;
private final EmbeddedMediaPlayer mediaPlayer;
public VLC() {
this.mediaPlayerFactory = new MediaPlayerFactory();
this.mediaPlayer = mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer(); // It doesn't work because of it. But if you remove that, then everything works.
}
}
My modules:
So far, I have only been able to figure out that upcalls is not working due to the declaration of the variable 'player' in Main.java. But it will work if you change this.mediaPlayer = mediaPlayerFactory.mediaPlayers (). NewEmbeddedMediaPlayer () in VLC.java; on this.mediaPlayer = null
If you remove the line 'VLC player = new VLC ();' then everything works. If it is not deleted, then the method 'myJavaMember.log (' text ')' will not work and there will be an error: 'TypeError: myJavaMember.log is not a function (In' myJavaMember.log ('text') ',' myJavaMember.log ' is undefined). Why is that?
Please tell me how to fix this and what could be the reason?
P.S.: I know how to implement VLCJ. It works for me. Here is only the code that can repeat the error that worries me.
The problem is that upcalls doesn't work. I repeat how to implement the VLCJ player I know.
Solved the problem thanks to this answer: https://stackoverflow.com/a/53618875/10946427
It turns out my JSBridge was being destroyed by the garbage collector and because of VLCJ it was faster.
If you create a JSBridge variable inside the class, then the garbage collector will not destroy it and everything will work.
I cannot sign in to Google in JavaFX WebView. The page doesn't load when I click the 'Next' button.
Other logins on different websites work fine.
Here is an example you can run:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class App extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
webEngine.load("https://calendar.google.com");
StackPane root = new StackPane();
root.getChildren().add(browser);
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
Screenshot here
Short Version:
Add the following line to your main method, before you load the page:
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
Long Version:
My first instinct was that JavaScript wasn't working, but I tested dummy emails and correctly got the error:
Couldn't find your Google Account
So it seemed like some JavaScript was working, but not the part which enabled the user to continue to enter their password. I added the following listener to listen for console errors, which I found here:
com.sun.javafx.webkit.WebConsoleListener.setDefaultListener(
(webView, message, lineNumber, sourceId) ->
System.out.println("Console: [" + sourceId + ":" + lineNumber + "] " + message)
);
This resulted in the following error:
Console: [null:0] XMLHttpRequest cannot load
https://ssl.gstatic.com/accounts/static/_/js/blahblahblah
Origin https://accounts.google.com is not allowed by
Access-Control-Allow-Origin.
This is a security feature called Same-Origin Policy. It's designed to stop pages being able to load scripts from potentially malicious third-party websites.
I searched for "Same Origin Policy JavaFX" and found the following question which will solve your problem.
The full application with both the fix and the additional logging is:
public class CalendarController extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
com.sun.javafx.webkit.WebConsoleListener.setDefaultListener(
(webView, message, lineNumber, sourceId)-> System.out.println("Console: [" + sourceId + ":" + lineNumber + "] " + message)
);
webEngine.load("http://calendar.google.com");
StackPane root = new StackPane();
root.getChildren().add(browser);
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
public static void main(String[] args)
{
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
launch(args);
}
}
I am creating a sample app in JavaFx.
I have loaded a local html file in webview in app. I want to apply style to that loaded html page from the app. When i try to do that the style is applied to entire webview.
I only want to apply on that loaded html page not the webview.
This is index.html page that I am loading.
<!DOCTYPE html>
<html>
<head>
<script>
function myFunction() {
document.getElementById("demo").innerHTML = "Paragraph changed.";
}
</script>
</head>
<body>
<h1>My Web Page</h1>
<p id="demo">A Paragraph</p>
<button type="button" onclick="myFunction()" id="btn">Try it</button>
</body>
</html>
This is demo.css
*{
padding: 0;
margin: 0;
}
#btn{
font-weight: bold;
font-size: 14px;
padding: 10px 20px;
}
body {
background-color: #00ff80;
font-family: Arial, Helvetica, san-serif;
}
This is my javafx app code.
Hyperlink hpl3 = new Hyperlink("Load Html File");
hpl3.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
String path = System.getProperty("user.dir");
path.replace("\\\\", "/");
path += "/html/index.html";
String path1 = System.getProperty("user.dir");
path1.replace("\\\\", "/");
path1 += "/css/demo.css";
webEngine.setUserStyleSheetLocation("file:///" + path1);
webEngine.load("file:///" + path);
}
});
As james-d said:
import javafx.application.Application;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
public class WebViewCssPlay extends Application {
private static final String CSS =
"body {"
+ " background-color: #00ff80; "
+ " font-family: Arial, Helvetica, san-serif;"
+ "}";
#Override
public void start(Stage stage) {
stage.setTitle("CSS Styling Test");
stage.setWidth(300);
stage.setHeight(200);
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
webEngine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
if (newState == State.SUCCEEDED) {
Document doc = webEngine.getDocument() ;
Element styleNode = doc.createElement("style");
Text styleContent = doc.createTextNode(CSS);
styleNode.appendChild(styleContent);
doc.getDocumentElement().getElementsByTagName("head").item(0).appendChild(styleNode);
System.out.println(webEngine.executeScript("document.documentElement.innerHTML"));
}
});
webEngine.loadContent("<html><body><h1>Hello!</h1>This is a <b>test</b></body></html>");
VBox root = new VBox();
root.getChildren().addAll(browser);
root.getStyleClass().add("browser");
Scene scene = new Scene(root);
stage.setScene(scene);
//scene.getStylesheets().add("/net/snortum/play/web_view.css");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Source:
Applying CSS file to JavaFX WebView
I would like to adjust the line height of an HTMLEditor in JavaFx with css rules but can't find the name of the rule.
I tried -fx-line-height and a few other but none of them worked. Is it even possible, or is the HTMLEditor too restricted?
The HTML editor edits HTML, you need to specify the line-height in HTML based CSS (not JavaFX CSS).
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.web.HTMLEditor;
import javafx.stage.Stage;
public class SpacedOut extends Application {
private static final String HTML_TEXT =
"<p style=\"line-height:1.5\">\n" +
" <span style=\"font-size:12pt\">The quick brown fox jumps over the lazy dog.</span><br />\n" +
" <span style=\"font-size:24pt\">The quick brown fox jumps over the lazy dog.</span>\n" +
"</p>";
#Override
public void start(Stage stage) throws Exception{
HTMLEditor editor = new HTMLEditor();
editor.setHtmlText(HTML_TEXT);
Scene scene = new Scene(new Pane(editor));
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
So here's my problem. I'm using WebView class from JavaFX in swing. The thing I want to do is that I want fields loaded in webview to be filled automatically with information stored in an array. Is it possible?
Thanks in advance
Here is an automated form fill example JavaFX app for WebView.
Values (login credentials) are entered into JavaFX fields in the yellow part of the screen and then automatically posted (using the w3c dom api) in the WebView (the white part of the screen) when the login page appears.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.web.*;
import javafx.stage.Stage;
import org.w3c.dom.*;
import org.w3c.dom.html.*;
public class WebViewFormPost extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
final TextField fxUsername = new TextField();
fxUsername.setPrefColumnCount(20);
final TextField fxPassword = new PasswordField();
final BooleanProperty loginAttempted = new SimpleBooleanProperty(false);
final WebView webView = new WebView();
webView.setPrefWidth(1000);
final WebEngine engine = webView.getEngine();
engine.documentProperty().addListener(new ChangeListener<Document>() {
#Override
public void changed(ObservableValue<? extends Document> ov, Document oldDoc, Document doc) {
if (doc != null && !loginAttempted.get()) {
if (doc.getElementsByTagName("form").getLength() > 0) {
HTMLFormElement form = (HTMLFormElement) doc.getElementsByTagName("form").item(0);
if ("/oam/server/sso/auth_cred_submit".equals(form.getAttribute("action"))) {
HTMLInputElement username = null;
HTMLInputElement password = null;
NodeList nodes = form.getElementsByTagName("input");
for (int i = 0; i < nodes.getLength(); i++) {
HTMLInputElement input = (HTMLInputElement) nodes.item(i);
switch (input.getName()) {
case "ssousername":
username = input;
break;
case "password":
password = input;
break;
}
}
if (username != null && password != null) {
loginAttempted.set(true);
username.setValue(fxUsername.getText());
password.setValue(fxPassword.getText());
form.submit();
}
}
}
}
}
});
engine.getLoadWorker().exceptionProperty().addListener(new ChangeListener<Throwable>() {
#Override
public void changed(ObservableValue<? extends Throwable> ov, Throwable oldException, Throwable exception) {
System.out.println("Load Exception: " + exception);
}
});
GridPane inputGrid = new GridPane();
inputGrid.setHgap(10);
inputGrid.setVgap(10);
inputGrid.addRow(0, new Label("Username: "), fxUsername);
inputGrid.addRow(0, new Label("Password: "), fxPassword);
Button fxLoginButton = new Button("Login to Oracle Forums");
fxLoginButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if (notEmpty(fxPassword.getText()) && notEmpty(fxPassword.getText())) {
loginAttempted.set(false);
engine.load("https://forums.oracle.com/community/developer/english/java/javafx/login.jspa");
}
}
});
fxLoginButton.setDefaultButton(true);
ProgressIndicator fxLoadProgress = new ProgressIndicator(0);
fxLoadProgress.progressProperty().bind(webView.getEngine().getLoadWorker().progressProperty());
fxLoadProgress.visibleProperty().bind(webView.getEngine().getLoadWorker().runningProperty());
HBox loginPane = new HBox(10);
loginPane.getChildren().setAll(
fxLoginButton,
fxLoadProgress
);
final VBox layout = new VBox(10);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
layout.getChildren().addAll(
new Label("Enter your Oracle Web Account credentials"),
inputGrid,
loginPane,
webView
);
VBox.setVgrow(webView, Priority.ALWAYS);
stage.setScene(new Scene(layout));
stage.show();
fxUsername.requestFocus();
}
private boolean notEmpty(String s) {
return s != null && !"".equals(s);
}
}
The above application is adapted from a previous Oracle forum question on Submitting HTML Forms with JavaFX Webview.
If you don't have an Oracle technology network account to test the above program, you can sign up for one here: https://myprofile.oracle.com/EndUser/faces/profile/createUser.jspx.
Posting to WebView using JQuery
An alternate implementation, that I would actually prefer is to use is JavaScript jQuery to introspect the DOM and perform the post rather than using the Java DOM apis. There is a sample for using jQuery on any arbitrary webpage hosted in a WebView. So you could combine the ideas from this automated WebView form post and the jQuery hosted WebView sample to create a version which uses JQuery to perform the post.
I fixed this with JavaFX webView Javascript engine.
If anyone is intersted here's code snippet.
String setLastName = "document.getElementsByName('lastName')[0].value='" + lastName + "';";
String setName = "document.getElementsByName('firstName')[0].value='" + name + "'";
String setDateBirth = "document.getElementsByName('birthdate')[0].value='" + datebirth + "';";
String setPhone = "document.getElementsByName('phone')[0].value='" + phone + "';";
String setEmail = "document.getElementsByName('email')[0].value='" + email + "';";
String setPassport = "document.getElementsByName('passport')[0].value='" + passport + "';";
Button button = new Button("Fill the form");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
webEngine.executeScript(setLastName);
webEngine.executeScript(setName);
webEngine.executeScript(setDateBirth);
webEngine.executeScript(setPhone);
webEngine.executeScript(setEmail);
webEngine.executeScript(setPassport);
}
});