All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javafx.scene.web.WebEngine Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 */
package javafx.scene.web;

import com.sun.javafx.geom.BaseBounds;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.javafx.jmx.MXNodeAlgorithm;
import com.sun.javafx.jmx.MXNodeAlgorithmContext;
import com.sun.javafx.scene.web.Debugger;
import com.sun.javafx.sg.PGNode;
import com.sun.javafx.sg.prism.NGNode;
import com.sun.javafx.tk.TKPulseListener;
import com.sun.javafx.tk.Toolkit;
import com.sun.javafx.webkit.Accessor;
import com.sun.javafx.webkit.CursorManagerImpl;
import com.sun.javafx.webkit.EventLoopImpl;
import com.sun.javafx.webkit.PolicyClientImpl;
import com.sun.javafx.webkit.ThemeClientImpl;
import com.sun.javafx.webkit.UIClientImpl;
import com.sun.javafx.webkit.UtilitiesImpl;
import com.sun.javafx.webkit.WebPageClientImpl;
import com.sun.javafx.webkit.prism.PrismGraphicsManager;
import com.sun.javafx.webkit.prism.PrismInvoker;
import com.sun.javafx.webkit.prism.theme.PrismRenderer;
import com.sun.javafx.webkit.theme.RenderThemeImpl;
import com.sun.javafx.webkit.theme.Renderer;
import com.sun.prism.Graphics;
import com.sun.prism.paint.Color;
import com.sun.webkit.CursorManager;
import com.sun.webkit.Disposer;
import com.sun.webkit.DisposerRecord;
import com.sun.webkit.InspectorClient;
import com.sun.webkit.Invoker;
import com.sun.webkit.LoadListenerClient;
import static com.sun.webkit.LoadListenerClient.*;
import com.sun.webkit.ThemeClient;
import com.sun.webkit.Timer;
import com.sun.webkit.Utilities;
import com.sun.webkit.WebPage;
import com.sun.webkit.graphics.WCGraphicsContext;
import com.sun.webkit.graphics.WCGraphicsManager;
import com.sun.webkit.network.URLs;
import com.sun.webkit.network.Util;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javafx.animation.AnimationTimer;
import javafx.beans.InvalidationListener;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.BooleanPropertyBase;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectPropertyBase;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.StringPropertyBase;
import javafx.concurrent.Worker;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.print.PageLayout;
import javafx.print.PrinterJob;
import javafx.scene.Node;
import javafx.util.Callback;
import org.w3c.dom.Document;

/**
 * {@code 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 {@code WebEngine} class provides two ways to load content into a * {@code WebEngine} object: *

    *
  • From an arbitrary URL using the {@link #load} method. This method uses * the {@code java.net} package for network access and protocol handling. *
  • From an in-memory String using the * {@link #loadContent(java.lang.String, java.lang.String)} and * {@link #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 {@link javafx.concurrent.Worker} * instance available from the {@link #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 * {@code 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 {@code 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 {@code WebEngine} callbacks: *

*
JavaScript method/property * WebEngine callback *
{@code window.alert()}{@code onAlert} *
{@code window.confirm()}{@code confirmHandler} *
{@code window.open()}{@code createPopupHandler} *
{@code window.open()} and
* {@code window.close()}
{@code onVisibilityChanged} *
{@code window.prompt()}{@code promptHandler} *
Setting {@code window.status}{@code onStatusChanged} *
Setting any of the following:
* {@code window.innerWidth}, {@code window.innerHeight},
* {@code window.outerWidth}, {@code window.outerHeight},
* {@code window.screenX}, {@code window.screenY},
* {@code window.screenLeft}, {@code window.screenTop} *
{@code 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 {@code 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 {@link #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 {@link #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 {@code java.lang.Boolean}; * and a string becomes a {@code java.lang.String}. * A number can be {@code java.lang.Double} or a {@code java.lang.Integer}, * depending. * The undefined value maps to a specific unique String * object whose value is {@code "undefined"}. *

* If the result is a * JavaScript object, it is wrapped as an instance of the * {@link netscape.javascript.JSObject} class. * (As a special case, if the JavaScript object is * a {@code JavaRuntimeObject} as discussed in the next section, * then the original Java object is extracted instead.) * The {@code JSObject} class is a proxy that provides access to * methods and properties of its underlying JavaScript object. * The most commonly used {@code JSObject} methods are * {@link netscape.javascript.JSObject#getMember getMember} * (to read a named property), * {@link netscape.javascript.JSObject#setMember setMember} * (to set or define a property), * and {@link netscape.javascript.JSObject#call call} * (to call a function-valued property). *

* A DOM {@code Node} is mapped to an object that both extends * {@code JSObject} and implements the appropriate DOM interfaces. * To get a {@code JSObject} object for a {@code 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 {@code 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 {@code JSObject} methods {@code setMember} and * {@code call} pass Java objects to the JavaScript environment. * This is roughly the inverse of the JavaScript-to-Java mapping * described above: * Java {@code String}, {@code Number}, or {@code Boolean} objects * are converted to the obvious JavaScript values. A {@code JSObject} * object is converted to the original wrapped JavaScript object. * Otherwise a {@code JavaRuntimeObject} is created. This is * a JavaScript object that acts as a proxy for the Java object, * in that accessing properties of the {@code JavaRuntimeObject} * causes the Java field or method with the same name to be accessed. * *

Calling back to Java from JavaScript

* *

The {@link netscape.javascript.JSObject#setMember 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 {@code app}. This object has one * public member, the method {@code exit}. *


public class JavaApplication {
    public void exit() {
        Platform.exit();
    }
}
...
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", new JavaApplication());
 * 
* You can then refer to the object and the method from your HTML page: *

<a href="" onclick="app.exit()">Click here to exit application</a>
 * 
*

When a user clicks the link the application is closed. *

* 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)
 * 
* *

Threading

*

{@code 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 {@code WebEngine} object. * @since JavaFX 2.0 */ final public class WebEngine { static { Accessor.setPageAccessor(new Accessor.PageAccessor() { @Override public WebPage getPage(WebEngine w) { return w == null ? null : w.getPage(); } }); Invoker.setInvoker(new PrismInvoker()); Renderer.setRenderer(new PrismRenderer()); WCGraphicsManager.setGraphicsManager(new PrismGraphicsManager()); CursorManager.setCursorManager(new CursorManagerImpl()); com.sun.webkit.EventLoop.setEventLoop(new EventLoopImpl()); ThemeClient.setDefaultRenderTheme(new RenderThemeImpl()); Utilities.setUtilities(new UtilitiesImpl()); } /** * The number of instances of this class. * Used to start and stop the pulse timer. */ private static int instanceCount = 0; /** * The node associated with this engine. There is a one-to-one correspondence * between the WebView and its WebEngine (although not all WebEngines have * a WebView, every WebView has one and only one WebEngine). */ private final ObjectProperty view = new SimpleObjectProperty(this, "view"); /** * The Worker which shows progress of the web engine as it loads pages. */ private final LoadWorker loadWorker = new LoadWorker(); /** * The object that provides interaction with the native webkit core. */ private final WebPage page; /** * The debugger associated with this web engine. */ private final DebuggerImpl debugger = new DebuggerImpl(); /** * Returns a {@link javafx.concurrent.Worker} object that can be used to * track loading progress. */ public final Worker getLoadWorker() { return loadWorker; } /** * The final document. This may be null if no document has been loaded. */ private final DocumentProperty document = new DocumentProperty(); /** * Returns the document object for the current Web page. If the Web page * failed to load, returns {@code null}. */ public final Document getDocument() { return document.getValue(); } /** * Document object for the current Web page. The value is {@code null} * if the Web page failed to load. */ public final ReadOnlyObjectProperty documentProperty() { return document; } /** * The location of the current page. This may return null. */ private final ReadOnlyStringWrapper location = new ReadOnlyStringWrapper(this, "location"); /** * Returns URL of the current Web page. If the current page has no URL, * returns an empty String. */ public final String getLocation() { return location.getValue(); } /** * URL of the current Web page. If the current page has no URL, * the value is an empty String. */ public final ReadOnlyStringProperty locationProperty() { return location.getReadOnlyProperty(); } private void updateLocation(String value) { this.location.set(value); this.document.invalidate(false); this.title.set(null); } /** * The page title. */ private final ReadOnlyStringWrapper title = new ReadOnlyStringWrapper(this, "title"); /** * Returns title of the current Web page. If the current page has no title, * returns {@code null}. */ public final String getTitle() { return title.getValue(); } /** * Title of the current Web page. If the current page has no title, * the value is {@code null}. */ public final ReadOnlyStringProperty titleProperty() { return title.getReadOnlyProperty(); } private void updateTitle() { title.set(page.getTitle(page.getMainFrame())); } // // Settings /** * Specifies whether JavaScript execution is enabled. * * @defaultValue true * @since JavaFX 2.2 */ private BooleanProperty javaScriptEnabled; public final void setJavaScriptEnabled(boolean value) { javaScriptEnabledProperty().set(value); } public final boolean isJavaScriptEnabled() { return javaScriptEnabled == null ? true : javaScriptEnabled.get(); } public final BooleanProperty javaScriptEnabledProperty() { if (javaScriptEnabled == null) { javaScriptEnabled = new BooleanPropertyBase(true) { @Override public void invalidated() { checkThread(); page.setJavaScriptEnabled(get()); } @Override public Object getBean() { return WebEngine.this; } @Override public String getName() { return "javaScriptEnabled"; } }; } return javaScriptEnabled; } /** * Location of the user stylesheet as a string URL. * *

This should be a local URL, i.e. either {@code 'data:'}, * {@code 'file:'}, or {@code 'jar:'}. Remote URLs are not allowed * for security reasons. * * @defaultValue null * @since JavaFX 2.2 */ private StringProperty userStyleSheetLocation; public final void setUserStyleSheetLocation(String value) { userStyleSheetLocationProperty().set(value); } public final String getUserStyleSheetLocation() { return userStyleSheetLocation == null ? null : userStyleSheetLocation.get(); } public final StringProperty userStyleSheetLocationProperty() { if (userStyleSheetLocation == null) { userStyleSheetLocation = new StringPropertyBase(null) { private final static String DATA_PREFIX = "data:text/css;charset=utf-8;base64,"; @Override public void invalidated() { checkThread(); String url = get(); String dataUrl; if (url == null || url.length() <= 0) { dataUrl = null; } else if (url.startsWith(DATA_PREFIX)) { dataUrl = url; } else if (url.startsWith("file:") || url.startsWith("jar:") || url.startsWith("data:")) { try { URLConnection conn = URLs.newURL(url).openConnection(); conn.connect(); BufferedInputStream in = new BufferedInputStream(conn.getInputStream()); ByteArrayOutputStream out = new ByteArrayOutputStream(); new sun.misc.BASE64Encoder().encodeBuffer(in, out); dataUrl = DATA_PREFIX + out.toString(); } catch (IOException e) { throw new RuntimeException(e); } } else { throw new IllegalArgumentException("Invalid stylesheet URL"); } page.setUserStyleSheetLocation(dataUrl); } @Override public Object getBean() { return WebEngine.this; } @Override public String getName() { return "userStyleSheetLocation"; } }; } return userStyleSheetLocation; } /** * Specifies user agent ID string. This string is the value of the * {@code User-Agent} HTTP header. * * @defaultValue system dependent * @since JavaFX 8.0 */ private StringProperty userAgent; public final void setUserAgent(String value) { userAgentProperty().set(value); } public final String getUserAgent() { return userAgent == null ? page.getUserAgent() : userAgent.get(); } public final StringProperty userAgentProperty() { if (userAgent == null) { userAgent = new StringPropertyBase(page.getUserAgent()) { @Override public void invalidated() { checkThread(); page.setUserAgent(get()); } @Override public Object getBean() { return WebEngine.this; } @Override public String getName() { return "userAgent"; } }; } return userAgent; } private final ObjectProperty>> onAlert = new SimpleObjectProperty>>(this, "onAlert"); /** * Returns the JavaScript {@code alert} handler. * @see #onAlertProperty * @see #setOnAlert */ public final EventHandler> getOnAlert() { return onAlert.get(); } /** * Sets the JavaScript {@code alert} handler. * @see #onAlertProperty * @see #getOnAlert */ public final void setOnAlert(EventHandler> handler) { onAlert.set(handler); } /** * JavaScript {@code alert} handler property. This handler is invoked * when a script running on the Web page calls the {@code alert} function. */ public final ObjectProperty>> onAlertProperty() { return onAlert; } private final ObjectProperty>> onStatusChanged = new SimpleObjectProperty>>(this, "onStatusChanged"); /** * Returns the JavaScript status handler. * @see #onStatusChangedProperty * @see #setOnStatusChanged */ public final EventHandler> getOnStatusChanged() { return onStatusChanged.get(); } /** * Sets the JavaScript status handler. * @see #onStatusChangedProperty * @see #getOnStatusChanged */ public final void setOnStatusChanged(EventHandler> handler) { onStatusChanged.set(handler); } /** * JavaScript status handler property. This handler is invoked when * a script running on the Web page sets {@code window.status} property. */ public final ObjectProperty>> onStatusChangedProperty() { return onStatusChanged; } private final ObjectProperty>> onResized = new SimpleObjectProperty>>(this, "onResized"); /** * Returns the JavaScript window resize handler. * @see #onResizedProperty * @see #setOnResized */ public final EventHandler> getOnResized() { return onResized.get(); } /** * Sets the JavaScript window resize handler. * @see #onResizedProperty * @see #getOnResized */ public final void setOnResized(EventHandler> handler) { onResized.set(handler); } /** * JavaScript window resize handler property. This handler is invoked * when a script running on the Web page moves or resizes the * {@code window} object. */ public final ObjectProperty>> onResizedProperty() { return onResized; } private final ObjectProperty>> onVisibilityChanged = new SimpleObjectProperty>>(this, "onVisibilityChanged"); /** * Returns the JavaScript window visibility handler. * @see #onVisibilityChangedProperty * @see #setOnVisibilityChanged */ public final EventHandler> getOnVisibilityChanged() { return onVisibilityChanged.get(); } /** * Sets the JavaScript window visibility handler. * @see #onVisibilityChangedProperty * @see #getOnVisibilityChanged */ public final void setOnVisibilityChanged(EventHandler> handler) { onVisibilityChanged.set(handler); } /** * JavaScript window visibility handler property. This handler is invoked * when a script running on the Web page changes visibility of the * {@code window} object. */ public final ObjectProperty>> onVisibilityChangedProperty() { return onVisibilityChanged; } private final ObjectProperty> createPopupHandler = new SimpleObjectProperty>(this, "createPopupHandler", new Callback() { public WebEngine call(PopupFeatures p) { return WebEngine.this; } }); /** * Returns the JavaScript popup handler. * @see #createPopupHandlerProperty * @see #setCreatePopupHandler */ public final Callback getCreatePopupHandler() { return createPopupHandler.get(); } /** * Sets the JavaScript popup handler. * @see #createPopupHandlerProperty * @see #getCreatePopupHandler * @see PopupFeatures */ public final void setCreatePopupHandler(Callback handler) { createPopupHandler.set(handler); } /** * JavaScript popup handler property. This handler is invoked when a script * running on the Web page requests a popup to be created. *

To satisfy this request a handler may create a new {@code WebEngine}, * attach a visibility handler and optionally a resize handler, and return * the newly created engine. To block the popup, a handler should return * {@code null}. *

By default, a popup handler is installed that opens popups in this * {@code WebEngine}. * * @see PopupFeatures */ public final ObjectProperty> createPopupHandlerProperty() { return createPopupHandler; } private final ObjectProperty> confirmHandler = new SimpleObjectProperty>(this, "confirmHandler"); /** * Returns the JavaScript {@code confirm} handler. * @see #confirmHandlerProperty * @see #setConfirmHandler */ public final Callback getConfirmHandler() { return confirmHandler.get(); } /** * Sets the JavaScript {@code confirm} handler. * @see #confirmHandlerProperty * @see #getConfirmHandler */ public final void setConfirmHandler(Callback handler) { confirmHandler.set(handler); } /** * JavaScript {@code confirm} handler property. This handler is invoked * when a script running on the Web page calls the {@code confirm} function. *

An implementation may display a dialog box with Yes and No options, * and return the user's choice. */ public final ObjectProperty> confirmHandlerProperty() { return confirmHandler; } private final ObjectProperty> promptHandler = new SimpleObjectProperty>(this, "promptHandler"); /** * Returns the JavaScript {@code prompt} handler. * @see #promptHandlerProperty * @see #setPromptHandler * @see PromptData */ public final Callback getPromptHandler() { return promptHandler.get(); } /** * Sets the JavaScript {@code prompt} handler. * @see #promptHandlerProperty * @see #getPromptHandler * @see PromptData */ public final void setPromptHandler(Callback handler) { promptHandler.set(handler); } /** * JavaScript {@code prompt} handler property. This handler is invoked * when a script running on the Web page calls the {@code prompt} function. *

An implementation may display a dialog box with an text field, * and return the user's input. * * @see PromptData */ public final ObjectProperty> promptHandlerProperty() { return promptHandler; } /** * Creates a new engine. */ public WebEngine() { this(null); } /** * Creates a new engine and loads a Web page into it. */ public WebEngine(String url) { if (instanceCount == 0 && Timer.getMode() == Timer.Mode.PLATFORM_TICKS) { PulseTimer.start(); } Accessor accessor = new AccessorImpl(this); page = new WebPage( new WebPageClientImpl(accessor), new UIClientImpl(accessor), new PolicyClientImpl(), new InspectorClientImpl(this), new ThemeClientImpl(accessor), false); page.addLoadListenerClient(new PageLoadListener(this)); history = new WebHistory(page); Disposer.addRecord(this, new SelfDisposer(page)); load(url); instanceCount++; } /** * Loads a Web page into this engine. This method starts asynchronous * loading and returns immediately. * @param url URL of the web page to load */ public void load(String url) { checkThread(); loadWorker.cancelAndReset(); if (url == null) { url = ""; } else { // verify and, if possible, adjust the url on the Java // side, otherwise it may crash native code try { url = Util.adjustUrlForWebKit(url); } catch (MalformedURLException e) { loadWorker.dispatchLoadEvent(getMainFrame(), PAGE_STARTED, url, null, 0.0, 0); loadWorker.dispatchLoadEvent(getMainFrame(), LOAD_FAILED, url, null, 0.0, MALFORMED_URL); } } page.open(page.getMainFrame(), url); } /** * Loads the given HTML content directly. This method is useful when you have an HTML * String composed in memory, or loaded from some system which cannot be reached via * a URL (for example, the HTML text may have come from a database). As with * {@link #load(String)}, this method is asynchronous. */ public void loadContent(String content) { loadContent(content, "text/html"); } /** * Loads the given content directly. This method is useful when you have content * composed in memory, or loaded from some system which cannot be reached via * a URL (for example, the SVG text may have come from a database). As with * {@link #load(String)}, this method is asynchronous. This method also allows you to * specify the content type of the string being loaded, and so may optionally support * other types besides just HTML. */ public void loadContent(String content, String contentType) { checkThread(); loadWorker.cancelAndReset(); page.load(page.getMainFrame(), content, contentType); } /** * Reloads the current page, whether loaded from URL or directly from a String in * one of the {@code loadContent} methods. */ public void reload() { // TODO what happens if this is called while currently loading a page? checkThread(); page.refresh(page.getMainFrame()); } private final WebHistory history; /** * Returns the session history object. * * @return history object * @since JavaFX 2.2 */ public WebHistory getHistory() { return history; } /** * Executes a script in the context of the current page. * @return execution result, converted to a Java object using the following * rules: *

    *
  • JavaScript Int32 is converted to {@code java.lang.Integer} *
  • Other JavaScript numbers to {@code java.lang.Double} *
  • JavaScript string to {@code java.lang.String} *
  • JavaScript boolean to {@code java.lang.Boolean} *
  • JavaScript {@code null} to {@code null} *
  • Most JavaScript objects get wrapped as * {@code netscape.javascript.JSObject} *
  • JavaScript JSNode objects get mapped to instances of * {@code netscape.javascript.JSObject}, that also implement * {@code org.w3c.dom.Node} *
  • A special case is the JavaScript class {@code JavaRuntimeObject} * which is used to wrap a Java object as a JavaScript value - in this * case we just extract the original Java value. *
*/ public Object executeScript(String script) { checkThread(); return page.executeScript(page.getMainFrame(), script); } private long getMainFrame() { return page.getMainFrame(); } WebPage getPage() { return page; } void setView(WebView view) { this.view.setValue(view); } private void stop() { checkThread(); page.stop(page.getMainFrame()); } private static final class SelfDisposer implements DisposerRecord { private final WebPage page; private SelfDisposer(WebPage page) { this.page = page; } @Override public void dispose() { page.dispose(); instanceCount--; if (instanceCount == 0 && Timer.getMode() == Timer.Mode.PLATFORM_TICKS) { PulseTimer.stop(); } } } private static final class AccessorImpl extends Accessor { private final WeakReference engine; private AccessorImpl(WebEngine w) { this.engine = new WeakReference(w); } @Override public WebEngine getEngine() { return engine.get(); } @Override public WebPage getPage() { WebEngine w = getEngine(); return w == null ? null : w.page; } @Override public WebView getView() { WebEngine w = getEngine(); return w == null ? null : w.view.get(); } @Override public void addChild(Node child) { WebView view = getView(); if (view != null) { view.getChildren().add(child); } } @Override public void removeChild(Node child) { WebView view = getView(); if (view != null) { view.getChildren().remove(child); } } @Override public void addViewListener(InvalidationListener l) { WebEngine w = getEngine(); if (w != null) { w.view.addListener(l); } } } /** * Drives the {@code Timer} when {@code Timer.Mode.PLATFORM_TICKS} is set. */ private static final class PulseTimer { // Used just to guarantee constant pulse activity. See RT-14433. private static final AnimationTimer animation = new AnimationTimer() { @Override public void handle(long l) {} }; private static final TKPulseListener listener = new TKPulseListener() { public void pulse() { // Note, the timer event is executed right in the notifyTick(), // that is during the pulse event. This makes the timer more // repsonsive, though prolongs the pulse. So far it causes no // problems but nevertheless it should be kept in mind. Timer.getTimer().notifyTick(); } }; private static void start(){ Toolkit.getToolkit().addSceneTkPulseListener(listener); animation.start(); } private static void stop() { Toolkit.getToolkit().removeSceneTkPulseListener(listener); animation.stop(); } } static void checkThread() { Toolkit.getToolkit().checkFxUserThread(); } /** * The page load event listener. This object references the owner * WebEngine weakly so as to avoid referencing WebEngine from WebPage * strongly. */ private static final class PageLoadListener implements LoadListenerClient { private final WeakReference engine; private PageLoadListener(WebEngine engine) { this.engine = new WeakReference(engine); } @Override public void dispatchLoadEvent(long frame, int state, String url, String contentType, double progress, int errorCode) { WebEngine w = engine.get(); if (w != null) { w.loadWorker.dispatchLoadEvent(frame, state, url, contentType, progress, errorCode); } } @Override public void dispatchResourceLoadEvent(long frame, int state, String url, String contentType, double progress, int errorCode) { } } private final class LoadWorker implements Worker { private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper(this, "state", State.READY); @Override public final State getState() { checkThread(); return state.get(); } @Override public final ReadOnlyObjectProperty stateProperty() { checkThread(); return state.getReadOnlyProperty(); } private void updateState(State value) { checkThread(); this.state.set(value); running.set(value == State.SCHEDULED || value == State.RUNNING); } /** * @InheritDoc */ private final ReadOnlyObjectWrapper value = new ReadOnlyObjectWrapper(this, "value", null); @Override public final Void getValue() { checkThread(); return value.get(); } @Override public final ReadOnlyObjectProperty valueProperty() { checkThread(); return value.getReadOnlyProperty(); } /** * @InheritDoc */ private final ReadOnlyObjectWrapper exception = new ReadOnlyObjectWrapper(this, "exception"); @Override public final Throwable getException() { checkThread(); return exception.get(); } @Override public final ReadOnlyObjectProperty exceptionProperty() { checkThread(); return exception.getReadOnlyProperty(); } /** * @InheritDoc */ private final ReadOnlyDoubleWrapper workDone = new ReadOnlyDoubleWrapper(this, "workDone", -1); @Override public final double getWorkDone() { checkThread(); return workDone.get(); } @Override public final ReadOnlyDoubleProperty workDoneProperty() { checkThread(); return workDone.getReadOnlyProperty(); } /** * @InheritDoc */ private final ReadOnlyDoubleWrapper totalWorkToBeDone = new ReadOnlyDoubleWrapper(this, "totalWork", -1); @Override public final double getTotalWork() { checkThread(); return totalWorkToBeDone.get(); } @Override public final ReadOnlyDoubleProperty totalWorkProperty() { checkThread(); return totalWorkToBeDone.getReadOnlyProperty(); } /** * @InheritDoc */ private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, "progress", -1); @Override public final double getProgress() { checkThread(); return progress.get(); } @Override public final ReadOnlyDoubleProperty progressProperty() { checkThread(); return progress.getReadOnlyProperty(); } private void updateProgress(double p) { totalWorkToBeDone.set(100.0); workDone.set(p * 100.0); progress.set(p); } /** * @InheritDoc */ private final ReadOnlyBooleanWrapper running = new ReadOnlyBooleanWrapper(this, "running", false); @Override public final boolean isRunning() { checkThread(); return running.get(); } @Override public final ReadOnlyBooleanProperty runningProperty() { checkThread(); return running.getReadOnlyProperty(); } /** * @InheritDoc */ private final ReadOnlyStringWrapper message = new ReadOnlyStringWrapper(this, "message", ""); @Override public final String getMessage() { return message.get(); } @Override public final ReadOnlyStringProperty messageProperty() { return message.getReadOnlyProperty(); } /** * @InheritDoc */ private final ReadOnlyStringWrapper title = new ReadOnlyStringWrapper(this, "title", "WebEngine Loader"); @Override public final String getTitle() { return title.get(); } @Override public final ReadOnlyStringProperty titleProperty() { return title.getReadOnlyProperty(); } /** * Cancels the loading of the page. If called after the page has already * been loaded, then this call takes no effect. */ @Override public boolean cancel() { if (isRunning()) { stop(); // this call indirectly sets state return true; } else { return false; } } private void cancelAndReset() { cancel(); exception.set(null); message.set(""); totalWorkToBeDone.set(-1); workDone.set(-1); progress.set(-1); updateState(State.READY); running.set(false); } private void dispatchLoadEvent(long frame, int state, String url, String contentType, double workDone, int errorCode) { if (frame != getMainFrame()) { return; } switch (state) { case PAGE_STARTED: message.set("Loading " + url); updateLocation(url); updateProgress(0.0); updateState(State.SCHEDULED); updateState(State.RUNNING); break; case PAGE_REDIRECTED: message.set("Loading " + url); updateLocation(url); break; case PAGE_FINISHED: message.set("Loading complete"); updateProgress(1.0); updateState(State.SUCCEEDED); break; case LOAD_FAILED: message.set("Loading failed"); exception.set(describeError(errorCode)); updateState(State.FAILED); break; case LOAD_STOPPED: message.set("Loading stopped"); updateState(State.CANCELLED); break; case PROGRESS_CHANGED: updateProgress(workDone); break; case TITLE_RECEIVED: updateTitle(); break; case DOCUMENT_AVAILABLE: document.invalidate(true); break; } } private Throwable describeError(int errorCode) { String reason = "Unknown error"; switch (errorCode) { case UNKNOWN_HOST: reason = "Unknown host"; break; case MALFORMED_URL: reason = "Malformed URL"; break; case SSL_HANDSHAKE: reason = "SSL handshake failed"; break; case CONNECTION_REFUSED: reason = "Connection refused by server"; break; case CONNECTION_RESET: reason = "Connection reset by server"; break; case NO_ROUTE_TO_HOST: reason = "No route to host"; break; case CONNECTION_TIMED_OUT: reason = "Connection timed out"; break; case PERMISSION_DENIED: reason = "Permission denied"; break; case INVALID_RESPONSE: reason = "Invalid response from server"; break; case TOO_MANY_REDIRECTS: reason = "Too many redirects"; break; case FILE_NOT_FOUND: reason = "File not found"; break; } return new Throwable(reason); } } private final class DocumentProperty extends ReadOnlyObjectPropertyBase { private boolean available; private Document document; private void invalidate(boolean available) { if (this.available || available) { this.available = available; this.document = null; fireValueChangedEvent(); } } public Document get() { if (!this.available) { return null; } if (this.document == null) { this.document = page.getDocument(page.getMainFrame()); if (this.document == null) { this.available = false; } } return this.document; } public Object getBean() { return WebEngine.this; } public String getName() { return "document"; } } /** * Returns the debugger associated with this web engine. * The debugger is an object that can be used to debug * the web page currently loaded into the web engine. *

* All methods of the debugger must be called on * the JavaFX Application Thread. * The message callback object registered with the debugger * is always called on the JavaFX Application Thread. * @return the debugger associated with this web engine. * The return value cannot be {@code null}. * @treatAsPrivate This is an internal API that can be changed or * removed in the future. * @deprecated This is an internal API that can be changed or * removed in the future. */ @Deprecated public Debugger impl_getDebugger() { return debugger; } /** * The debugger implementation. */ private final class DebuggerImpl implements Debugger { private boolean enabled; private Callback messageCallback; @Override public boolean isEnabled() { checkThread(); return enabled; } @Override public void setEnabled(boolean enabled) { checkThread(); if (enabled != this.enabled) { if (enabled) { page.setDeveloperExtrasEnabled(true); page.connectInspectorFrontend(); } else { page.disconnectInspectorFrontend(); page.setDeveloperExtrasEnabled(false); } this.enabled = enabled; } } @Override public void sendMessage(String message) { checkThread(); if (!enabled) { throw new IllegalStateException("Debugger is not enabled"); } if (message == null) { throw new NullPointerException("message is null"); } page.dispatchInspectorMessageFromFrontend(message); } @Override public Callback getMessageCallback() { checkThread(); return messageCallback; } @Override public void setMessageCallback(Callback callback) { checkThread(); messageCallback = callback; } } /** * The inspector client implementation. This object references the owner * WebEngine weakly so as to avoid referencing WebEngine from WebPage * strongly. */ private static final class InspectorClientImpl implements InspectorClient { private final WeakReference engine; private InspectorClientImpl(WebEngine engine) { this.engine = new WeakReference(engine); } @Override public boolean sendMessageToFrontend(final String message) { boolean result = false; WebEngine webEngine = engine.get(); if (webEngine != null) { final Callback messageCallback = webEngine.debugger.messageCallback; if (messageCallback != null) { AccessController.doPrivileged(new PrivilegedAction() { @Override public Void run() { messageCallback.call(message); return null; } }, webEngine.page.getAccessControlContext()); result = true; } } return result; } } /** * Prints the current Web page using the given printer job. *

This method does not modify the state of the job, nor does it call * {@link PrinterJob#endJob}, so the job may be safely reused afterwards. * * @param job printer job used for printing * @since JavaFX 8.0 */ public void print(PrinterJob job) { PageLayout pl = job.getJobSettings().getPageLayout(); int pageCount = page.beginPrinting( (float) pl.getPrintableWidth(), (float) pl.getPrintableHeight()); for (int i = 0; i < pageCount; i++) { Node printable = new Printable(i); job.printPage(printable); } page.endPrinting(); } final class Printable extends Node { private final PGNode peer; Printable(int pageIndex) { peer = new Peer(pageIndex); } @Override protected PGNode impl_createPGNode() { return peer; } @Override public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) { return null; } @Override public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { return bounds; } @Override protected boolean impl_computeContains(double d, double d1) { return false; } private final class Peer extends NGNode { private final int pageIndex; Peer(int pageIndex) { this.pageIndex = pageIndex; } @Override protected void renderContent(Graphics g) { WCGraphicsContext gc = WCGraphicsManager.getGraphicsManager(). createGraphicsContext(g); page.print(gc, pageIndex); } @Override protected boolean hasOverlappingContents() { return false; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy