WebEngine
is a non-visual object capable of managing one Web page
at a time. It loads Web pages, creates their document models, applies
styles as necessary, and runs JavaScript on pages. It provides access
to the document model of the current page, and enables two-way
communication between a Java application and JavaScript code of the page.
Loading Web Pages
The WebEngine
class provides two ways to load content into a
WebEngine
object:
load
method. This method uses
the java.net
package for network access and protocol handling.
loadContent(java.lang.String, java.lang.String)
and
loadContent(java.lang.String)
methods.
Loading always happens on a background thread. Methods that initiate
loading return immediately after scheduling a background job. To track
progress and/or cancel a job, use the javafx.concurrent.Worker
instance available from the getLoadWorker
method.
The following example changes the stage title when loading completes successfully:
import javafx.concurrent.Worker.State;
final Stage stage;
webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<State>() {
public void changed(ObservableValue ov, State oldState, State newState) {
if (newState == State.SUCCEEDED) {
stage.setTitle(webEngine.getLocation());
}
});
webEngine.load("http://javafx.com");
}
User Interface Callbacks
A number of user interface callbacks may be registered with a
WebEngine
object. These callbacks are invoked when a script running
on the page requests a user interface operation to be performed, for
example, opens a popup window or changes status text. A WebEngine
object cannot handle such requests internally, so it passes the request to
the corresponding callbacks. If no callback is defined for a specific
operation, the request is silently ignored.
The table below shows JavaScript user interface methods and properties
with their corresponding WebEngine
callbacks:
JavaScript method/property | WebEngine callback |
---|---|
window.alert() | onAlert |
window.confirm() | confirmHandler |
window.open() | createPopupHandler |
window.open() and window.close() | onVisibilityChanged |
window.prompt() | promptHandler |
Setting window.status | onStatusChanged |
Setting any of the following: window.innerWidth , window.innerHeight , window.outerWidth , window.outerHeight , window.screenX , window.screenY , window.screenLeft , window.screenTop |
onResized |
The following example shows a callback that resizes a browser window:
Stage stage;
webEngine.setOnResized(
new EventHandler<WebEvent<Rectangle2D>>() {
public void handle(WebEvent<Rectangle2D> ev) {
Rectangle2D r = ev.getData();
stage.setWidth(r.getWidth());
stage.setHeight(r.getHeight());
});
}
Access to Document Model
The WebEngine
objects create and manage a Document Object Model
(DOM) for their Web pages. The model can be accessed and modified using
Java DOM Core classes. The getDocument()
method provides access
to the root of the model. Additionally DOM Event specification is supported
to define event handlers in Java code.
The following example attaches a Java event listener to an element of a Web page. Clicking on the element causes the application to exit:
EventListener listener = new EventListener() {
public void handleEvent(Event ev) {
Platform.exit();
};
Document doc = webEngine.getDocument();
Element el = doc.getElementById("exit-app");
((EventTarget) el).addEventListener("click", listener, false);
}
Evaluating JavaScript expressions
It is possible to execute arbitrary JavaScript code in the context of
the current page using the executeScript
method. For example:
webEngine.executeScript("history.back()");
The execution result is returned to the caller, as described in the next section.
Mapping JavaScript values to Java objects
JavaScript values are represented using the obvious Java classes: null becomes Java null; a boolean becomes a java.lang.Boolean
;
and a string becomes a java.lang.String
.
A number can be java.lang.Double
or a java.lang.Integer
,
depending.
The undefined value maps to a specific unique String
object whose value is "undefined"
.
If the result is a
JavaScript object, it is wrapped as an instance of the
netscape.javascript.JSObject
class.
(As a special case, if the JavaScript object is
a JavaRuntimeObject
as discussed in the next section,
then the original Java object is extracted instead.)
The JSObject
class is a proxy that provides access to
methods and properties of its underlying JavaScript object.
The most commonly used JSObject
methods are
getMember
(to read a named property),
setMember
(to set or define a property),
and call
(to call a function-valued property).
A DOM Node
is mapped to an object that both extends
JSObject
and implements the appropriate DOM interfaces.
To get a JSObject
object for a Node
just do a cast:
JSObject jdoc = (JSObject) webEngine.getDocument();
In some cases the context provides a specific Java type that guides
the conversion.
For example if setting a Java String
field from a JavaScript
expression, then the JavaScript value is converted to a string.
Mapping Java objects to JavaScript values
The arguments of the JSObject
methods setMember
and
call
pass Java objects to the JavaScript environment.
This is roughly the inverse of the JavaScript-to-Java mapping
described above:
Java String
, Number
, or Boolean
objects
are converted to the obvious JavaScript values. A JSObject
object is converted to the original wrapped JavaScript object.
Otherwise a JavaRuntimeObject
is created. This is
a JavaScript object that acts as a proxy for the Java object,
in that accessing properties of the JavaRuntimeObject
causes the Java field or method with the same name to be accessed.
Note that the Java objects bound using
JSObject.setMember
,
JSObject.setSlot
, and
JSObject.call
are implemented using weak references. This means that the Java object
can be garbage collected, causing subsequent accesses to the JavaScript
objects to have no effect.
Calling back to Java from JavaScript
The JSObject.setMember
method is useful to enable upcalls from JavaScript
into Java code, as illustrated by the following example. The Java code
establishes a new JavaScript object named app
. This object has one
public member, the method exit
.
public class JavaApplication {
public void exit() {
Platform.exit();
}
}
...
JavaApplication javaApp = new JavaApplication();
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", javaApp);
You can then refer to the object and the method from your HTML page:
<a href="http://docs.oracle.com/javase/10/docs/api/javafx/scene/web/" onclick="app.exit()">Click here to exit application</a>
When a user clicks the link the application is closed.
Note that in the above example, the application holds a reference
to the JavaApplication
instance. This is required for the callback
from JavaScript to execute the desired method.
In the following example, the application does not hold a reference to the Java object:
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", new JavaApplication());
In this case, since the property value is a local object, "new JavaApplication()"
,
the value may be garbage collected in next GC cycle.
When a user clicks the link, it does not guarantee to execute the callback method exit
.
If there are multiple Java methods with the given name, then the engine selects one matching the number of parameters in the call. (Varargs are not handled.) An unspecified one is chosen if there are multiple ones with the correct number of parameters.
You can pick a specific overloaded method by listing the
parameter types in an "extended method name", which has the
form "method_name(param_type1,...,param_typen)"
. Typically you'd write the JavaScript expression:
receiver["method_name(param_type1,...,param_typeN)"](arg1,...,argN)
The Java class and method must both be declared public.
Deploying an Application as a Module
If any Java class passed to JavaScript is in a named module, then it must
be reflectively accessible to the javafx.web
module.
A class is reflectively accessible if the module
opens
the containing package to at
least the javafx.web
module.
Otherwise, the method will not be called, and no error or
warning will be produced.
For example, if com.foo.MyClass
is in the foo.app
module,
the module-info.java
might
look like this:
module foo.app {
opens com.foo to javafx.web;
}
Alternatively, a class is reflectively accessible if the module
exports
the containing package
unconditionally.
Threading
WebEngine
objects must be created and accessed solely from the
JavaFX Application thread. This rule also applies to any DOM and JavaScript
objects obtained from the WebEngine
object.