/*
* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.scene.web;
import com.sun.javafx.scene.web.Debugger;
import javafx.animation.AnimationTimer;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.Worker;
import javafx.event.EventHandler;
import javafx.util.Callback;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import com.sun.javafx.tk.TKPulseListener;
import com.sun.javafx.tk.Toolkit;
import java.io.StringReader;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javafx.beans.property.*;
import javafx.geometry.Rectangle2D;
/**
* {@code WebEngine} is a non-visual object capable of managing one Web page
* at a time. One can load Web pages into an engine, track loading progress,
* access document model of a loaded page, and execute JavaScript on the page.
*
*
Loading is always asynchronous. Methods that initiate loading return
* immediately after scheduling a job, so one must not assume loading is
* complete by that time. {@link #getLoadWorker} method can be used to track
* loading status.
*
*
A number of JavaScript handlers and callbacks may be registered with a
* {@code WebEngine}. These are invoked when a script running on the page
* accesses user interface elements that lie beyond the control of the
* {@code WebEngine}, such as browser window, toolbar or status line.
*
*
{@code WebEngine} objects must be created and accessed solely from the
* FXthread.
* @since JavaFX 2.0
*/
final public class WebEngine {
/**
* The node associated with this engine. There is a one-to-one correspondance
* 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();
/**
* Returns a {@link javafx.concurrent.Worker} object that can be used to
* track loading progress.
*/
public final Worker getLoadWorker() {
return loadWorker;
}
private void updateProgress(double p) {
LoadWorker lw = (LoadWorker) getLoadWorker();
if (lw != null) {
lw.updateProgress(p);
}
}
private void updateState(Worker.State s) {
LoadWorker lw = (LoadWorker) getLoadWorker();
if (lw != null) {
lw.updateState(s);
}
}
/**
* 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(); }
/**
* 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(); }
//
// 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();
}
private Debugger debugger = new DebuggerImpl();
Debugger getDebugger(){
return debugger;
}
public final BooleanProperty javaScriptEnabledProperty() {
if (javaScriptEnabled == null) {
javaScriptEnabled = new BooleanPropertyBase(true) {
@Override public void invalidated() {
checkThread();
}
@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:")) {
} else {
throw new IllegalArgumentException("Invalid stylesheet URL");
}
}
@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 ? null : userAgent.get();
}
public final StringProperty userAgentProperty() {
if (userAgent == null) {
userAgent = new StringPropertyBase() {
@Override public void invalidated() {
checkThread();
}
@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",
p -> 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; }
/**
* The event handler called when an error occurs.
*
* @defaultValue {@code null}
* @since JavaFX 8.0
*/
private final ObjectProperty> onError =
new SimpleObjectProperty<>(this, "onError");
public final EventHandler getOnError() {
return onError.get();
}
public final void setOnError(EventHandler handler) {
onError.set(handler);
}
public final ObjectProperty> onErrorProperty() {
return onError;
}
/**
* Creates a new engine.
*/
public WebEngine() {
this(null);
}
/**
* Creates a new engine and loads a Web page into it.
*/
public WebEngine(String url) {
accessControlContext = AccessController.getContext();
js2javaBridge = new JS2JavaBridge(this);
load(url);
}
/**
* 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();
if (url == null) {
url = "";
}
if (view.get() != null) {
_loadUrl(view.get().getNativeHandle(), url);
}
}
/* Loads a web page */
private native void _loadUrl(long handle, String 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();
_loadContent(view.get().getNativeHandle(), content);
}
/* Loads the given content directly */
private native void _loadContent(long handle, String content);
/**
* Reloads the current page, whether loaded from URL or directly from a String in
* one of the {@code loadContent} methods.
*/
public void reload() {
checkThread();
}
/**
* 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();
StringBuilder b = new StringBuilder(js2javaBridge.getJavaBridge()).append(".fxEvaluate('");
b.append(escapeScript(script));
b.append("')");
String retVal = _executeScript(view.get().getNativeHandle(), b.toString());
try {
return js2javaBridge.decode(retVal);
} catch (Exception ex) {
System.err.println("Couldn't parse arguments. " + ex);
}
return null;
}
void executeScriptDirect(String script) {
_executeScript(view.get().getNativeHandle(), script);
}
/* Executes a script in the context of the current page */
private native String _executeScript(long handle, String script);
void setView(WebView view) {
this.view.setValue(view);
}
private void stop() {
checkThread();
}
private String escapeScript(String script) {
final int len = script.length();
StringBuilder sb = new StringBuilder((int) (len * 1.2));
for (int i = 0; i < len; i++) {
char ch = script.charAt(i);
switch (ch) {
case '\\': sb.append("\\\\"); break;
case '\'': sb.append("\\'"); break;
case '"': sb.append("\\\""); break;
case '\n': sb.append("\\n"); break;
case '\r': sb.append("\\r"); break;
case '\t': sb.append("\\t"); break;
default: sb.append(ch);
}
}
return sb.toString();
}
/**
* 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 =
() -> {
// 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();
}
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)
{
}
Throwable describeError(int errorCode) {
String reason = "Unknown error";
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;
}
this.document = getCurrentDocument();
// if (this.document == null) {
// if (this.document == null) {
// this.available = false;
// }
// }
return this.document;
}
public Object getBean() {
return WebEngine.this;
}
public String getName() {
return "document";
}
}
///////////////////////////////////////////////
// JavaScript to Java bridge
///////////////////////////////////////////////
private JS2JavaBridge js2javaBridge = null;
public void exportObject(String jsName, Object object) {
synchronized (loadedLock) {
if (js2javaBridge == null) {
js2javaBridge = new JS2JavaBridge(this);
}
js2javaBridge.exportObject(jsName, object);
}
}
interface PageListener {
void onLoadStarted();
void onLoadFinished();
void onLoadFailed();
void onJavaCall(String args);
}
private PageListener pageListener = null;
private boolean loaded = false;
private final Object loadedLock = new Object();
void setPageListener(PageListener listener) {
synchronized (loadedLock) {
pageListener = listener;
if (loaded) {
updateProgress(0.0);
updateState(Worker.State.SCHEDULED);
updateState(Worker.State.RUNNING);
pageListener.onLoadStarted();
updateProgress(1.0);
updateState(Worker.State.SUCCEEDED);
pageListener.onLoadFinished();
}
}
}
boolean isLoaded() {
return loaded;
}
// notifications are called from WebView
void notifyLoadStarted() {
synchronized (loadedLock) {
loaded = false;
updateProgress(0.0);
updateState(Worker.State.SCHEDULED);
updateState(Worker.State.RUNNING);
if (pageListener != null) {
pageListener.onLoadStarted();
}
}
}
private String pageContent;
Document getCurrentDocument () {
Document document = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse(new InputSource(new StringReader(pageContent)));
}
catch (Exception e) {
e.printStackTrace();
}
return document;
}
void notifyLoadFinished(String loc, String content) {
synchronized (loadedLock) {
this.pageContent = ""+content+"";
loaded = true;
updateProgress(1.0);
updateState(Worker.State.SUCCEEDED);
location.set(loc);
document.invalidate(true);
if (pageListener != null) {
pageListener.onLoadFinished();
}
}
}
void notifyLoadFailed() {
synchronized (loadedLock) {
loaded = false;
updateProgress(0.0);
updateState(Worker.State.FAILED);
if (pageListener != null) {
pageListener.onLoadFailed();
}
}
}
void notifyJavaCall(String arg) {
if (pageListener != null) {
pageListener.onJavaCall(arg);
}
}
void onAlertNotify(String text) {
if (getOnAlert() != null) {
dispatchWebEvent(
getOnAlert(),
new WebEvent(this, WebEvent.ALERT, text));
}
}
final private AccessControlContext accessControlContext;
AccessControlContext getAccessControlContext() {
return accessControlContext;
}
private void dispatchWebEvent(final EventHandler handler, final WebEvent ev) {
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Void run() {
handler.handle(ev);
return null;
}
}, getAccessControlContext());
}
private class DebuggerImpl implements Debugger {
private Callback callback;
@Override
public boolean isEnabled() {
return false;
}
@Override
public void setEnabled(boolean enabled) {
}
@Override
public void sendMessage(String message) {
}
@Override
public Callback getMessageCallback() {
return callback;
}
@Override
public void setMessageCallback(Callback callback) {
this.callback = callback;
}
};
}