Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.web; 027 028import javafx.animation.AnimationTimer; 029import javafx.beans.property.ObjectProperty; 030import javafx.beans.property.ReadOnlyBooleanProperty; 031import javafx.beans.property.ReadOnlyDoubleProperty; 032import javafx.beans.property.ReadOnlyObjectProperty; 033import javafx.beans.property.ReadOnlyStringProperty; 034import javafx.beans.property.ReadOnlyBooleanWrapper; 035import javafx.beans.property.ReadOnlyDoubleWrapper; 036import javafx.beans.property.ReadOnlyObjectWrapper; 037import javafx.beans.property.ReadOnlyStringWrapper; 038import javafx.beans.property.SimpleObjectProperty; 039import javafx.concurrent.Worker; 040import javafx.event.EventHandler; 041import javafx.util.Callback; 042 043import org.w3c.dom.Document; 044 045import com.sun.javafx.tk.TKPulseListener; 046import com.sun.javafx.tk.Toolkit; 047import java.security.AccessController; 048import java.security.PrivilegedAction; 049import javafx.beans.property.*; 050import javafx.geometry.Rectangle2D; 051 052/** 053 * {@code WebEngine} is a non-visual object capable of managing one Web page 054 * at a time. One can load Web pages into an engine, track loading progress, 055 * access document model of a loaded page, and execute JavaScript on the page. 056 * 057 * <p>Loading is always asynchronous. Methods that initiate loading return 058 * immediately after scheduling a job, so one must not assume loading is 059 * complete by that time. {@link #getLoadWorker} method can be used to track 060 * loading status. 061 * 062 * <p>A number of JavaScript handlers and callbacks may be registered with a 063 * {@code WebEngine}. These are invoked when a script running on the page 064 * accesses user interface elements that lie beyond the control of the 065 * {@code WebEngine}, such as browser window, toolbar or status line. 066 * 067 * <p>{@code WebEngine} objects must be created and accessed solely from the 068 * FXthread. 069 */ 070final public class WebEngine { 071 072 /** 073 * The node associated with this engine. There is a one-to-one correspondance 074 * between the WebView and its WebEngine (although not all WebEngines have 075 * a WebView, every WebView has one and only one WebEngine). 076 */ 077 private final ObjectProperty<WebView> view = new SimpleObjectProperty<WebView>(this, "view"); 078 079 /** 080 * The Worker which shows progress of the web engine as it loads pages. 081 */ 082 private final LoadWorker loadWorker = new LoadWorker(); 083 084 /** 085 * Returns a {@link javafx.concurrent.Worker} object that can be used to 086 * track loading progress. 087 */ 088 public final Worker<Void> getLoadWorker() { 089 return loadWorker; 090 } 091 092 /** 093 * The final document. This may be null if no document has been loaded. 094 */ 095 private final DocumentProperty document = new DocumentProperty(); 096 097 /** 098 * Returns the document object for the current Web page. If the Web page 099 * failed to load, returns {@code null}. 100 */ 101 public final Document getDocument() { return document.getValue(); } 102 103 /** 104 * Document object for the current Web page. The value is {@code null} 105 * if the Web page failed to load. 106 */ 107 public final ReadOnlyObjectProperty<Document> documentProperty() { 108 return document; 109 } 110 111 /** 112 * The location of the current page. This may return null. 113 */ 114 private final ReadOnlyStringWrapper location = new ReadOnlyStringWrapper(this, "location"); 115 116 /** 117 * Returns URL of the current Web page. If the current page has no URL, 118 * returns an empty String. 119 */ 120 public final String getLocation() { return location.getValue(); } 121 122 /** 123 * URL of the current Web page. If the current page has no URL, 124 * the value is an empty String. 125 */ 126 public final ReadOnlyStringProperty locationProperty() { return location.getReadOnlyProperty(); } 127 128 /** 129 * The page title. 130 */ 131 private final ReadOnlyStringWrapper title = new ReadOnlyStringWrapper(this, "title"); 132 133 /** 134 * Returns title of the current Web page. If the current page has no title, 135 * returns {@code null}. 136 */ 137 public final String getTitle() { return title.getValue(); } 138 139 /** 140 * Title of the current Web page. If the current page has no title, 141 * the value is {@code null}. 142 */ 143 public final ReadOnlyStringProperty titleProperty() { return title.getReadOnlyProperty(); } 144 145 // 146 // Settings 147 148 /** 149 * Specifies whether JavaScript execution is enabled. 150 * 151 * @defaultValue true 152 * @since 2.2 153 */ 154 private BooleanProperty javaScriptEnabled; 155 156 public final void setJavaScriptEnabled(boolean value) { 157 javaScriptEnabledProperty().set(value); 158 } 159 160 public final boolean isJavaScriptEnabled() { 161 return javaScriptEnabled == null ? true : javaScriptEnabled.get(); 162 } 163 164 public final BooleanProperty javaScriptEnabledProperty() { 165 if (javaScriptEnabled == null) { 166 javaScriptEnabled = new BooleanPropertyBase(true) { 167 @Override public void invalidated() { 168 checkThread(); 169 } 170 171 @Override public Object getBean() { 172 return WebEngine.this; 173 } 174 175 @Override public String getName() { 176 return "javaScriptEnabled"; 177 } 178 }; 179 } 180 return javaScriptEnabled; 181 } 182 183 /** 184 * Location of the user stylesheet as a string URL. 185 * 186 * <p>This should be a local URL, i.e. either {@code 'data:'}, 187 * {@code 'file:'}, or {@code 'jar:'}. Remote URLs are not allowed 188 * for security reasons. 189 * 190 * @defaultValue null 191 * @since 2.2 192 */ 193 private StringProperty userStyleSheetLocation; 194 195 public final void setUserStyleSheetLocation(String value) { 196 userStyleSheetLocationProperty().set(value); 197 } 198 199 public final String getUserStyleSheetLocation() { 200 return userStyleSheetLocation == null ? null : userStyleSheetLocation.get(); 201 } 202 203 public final StringProperty userStyleSheetLocationProperty() { 204 if (userStyleSheetLocation == null) { 205 userStyleSheetLocation = new StringPropertyBase(null) { 206 private final static String DATA_PREFIX = "data:text/css;charset=utf-8;base64,"; 207 208 @Override public void invalidated() { 209 checkThread(); 210 String url = get(); 211 String dataUrl; 212 if (url == null || url.length() <= 0) { 213 dataUrl = null; 214 } else if (url.startsWith(DATA_PREFIX)) { 215 dataUrl = url; 216 } else if (url.startsWith("file:") || 217 url.startsWith("jar:") || 218 url.startsWith("data:")) { 219 220 } else { 221 throw new IllegalArgumentException("Invalid stylesheet URL"); 222 } 223 } 224 225 @Override public Object getBean() { 226 return WebEngine.this; 227 } 228 229 @Override public String getName() { 230 return "userStyleSheetLocation"; 231 } 232 }; 233 } 234 return userStyleSheetLocation; 235 } 236 237 /** 238 * Specifies user agent ID string. This string is the value of the 239 * {@code User-Agent} HTTP header. 240 * 241 * @defaultValue system dependent 242 * @since 8.0 243 */ 244 private StringProperty userAgent; 245 246 public final void setUserAgent(String value) { 247 userAgentProperty().set(value); 248 } 249 250 public final String getUserAgent() { 251 return userAgent == null ? null : userAgent.get(); 252 } 253 254 public final StringProperty userAgentProperty() { 255 if (userAgent == null) { 256 userAgent = new StringPropertyBase() { 257 @Override public void invalidated() { 258 checkThread(); 259 } 260 261 @Override public Object getBean() { 262 return WebEngine.this; 263 } 264 265 @Override public String getName() { 266 return "userAgent"; 267 } 268 }; 269 } 270 return userAgent; 271 } 272 273 private final ObjectProperty<EventHandler<WebEvent<String>>> onAlert 274 = new SimpleObjectProperty<EventHandler<WebEvent<String>>>(this, "onAlert"); 275 276 /** 277 * Returns the JavaScript {@code alert} handler. 278 * @see #onAlertProperty 279 * @see #setOnAlert 280 */ 281 public final EventHandler<WebEvent<String>> getOnAlert() { return onAlert.get(); } 282 283 /** 284 * Sets the JavaScript {@code alert} handler. 285 * @see #onAlertProperty 286 * @see #getOnAlert 287 */ 288 public final void setOnAlert(EventHandler<WebEvent<String>> handler) { onAlert.set(handler); } 289 290 /** 291 * JavaScript {@code alert} handler property. This handler is invoked 292 * when a script running on the Web page calls the {@code alert} function. 293 */ 294 public final ObjectProperty<EventHandler<WebEvent<String>>> onAlertProperty() { return onAlert; } 295 296 297 private final ObjectProperty<EventHandler<WebEvent<String>>> onStatusChanged 298 = new SimpleObjectProperty<EventHandler<WebEvent<String>>>(this, "onStatusChanged"); 299 300 /** 301 * Returns the JavaScript status handler. 302 * @see #onStatusChangedProperty 303 * @see #setOnStatusChanged 304 */ 305 public final EventHandler<WebEvent<String>> getOnStatusChanged() { return onStatusChanged.get(); } 306 307 /** 308 * Sets the JavaScript status handler. 309 * @see #onStatusChangedProperty 310 * @see #getOnStatusChanged 311 */ 312 public final void setOnStatusChanged(EventHandler<WebEvent<String>> handler) { onStatusChanged.set(handler); } 313 314 /** 315 * JavaScript status handler property. This handler is invoked when 316 * a script running on the Web page sets {@code window.status} property. 317 */ 318 public final ObjectProperty<EventHandler<WebEvent<String>>> onStatusChangedProperty() { return onStatusChanged; } 319 320 321 private final ObjectProperty<EventHandler<WebEvent<Rectangle2D>>> onResized 322 = new SimpleObjectProperty<EventHandler<WebEvent<Rectangle2D>>>(this, "onResized"); 323 324 /** 325 * Returns the JavaScript window resize handler. 326 * @see #onResizedProperty 327 * @see #setOnResized 328 */ 329 public final EventHandler<WebEvent<Rectangle2D>> getOnResized() { return onResized.get(); } 330 331 /** 332 * Sets the JavaScript window resize handler. 333 * @see #onResizedProperty 334 * @see #getOnResized 335 */ 336 public final void setOnResized(EventHandler<WebEvent<Rectangle2D>> handler) { onResized.set(handler); } 337 338 /** 339 * JavaScript window resize handler property. This handler is invoked 340 * when a script running on the Web page moves or resizes the 341 * {@code window} object. 342 */ 343 public final ObjectProperty<EventHandler<WebEvent<Rectangle2D>>> onResizedProperty() { return onResized; } 344 345 346 private final ObjectProperty<EventHandler<WebEvent<Boolean>>> onVisibilityChanged 347 = new SimpleObjectProperty<EventHandler<WebEvent<Boolean>>>(this, "onVisibilityChanged"); 348 349 /** 350 * Returns the JavaScript window visibility handler. 351 * @see #onVisibilityChangedProperty 352 * @see #setOnVisibilityChanged 353 */ 354 public final EventHandler<WebEvent<Boolean>> getOnVisibilityChanged() { return onVisibilityChanged.get(); } 355 356 /** 357 * Sets the JavaScript window visibility handler. 358 * @see #onVisibilityChangedProperty 359 * @see #getOnVisibilityChanged 360 */ 361 public final void setOnVisibilityChanged(EventHandler<WebEvent<Boolean>> handler) { onVisibilityChanged.set(handler); } 362 363 /** 364 * JavaScript window visibility handler property. This handler is invoked 365 * when a script running on the Web page changes visibility of the 366 * {@code window} object. 367 */ 368 public final ObjectProperty<EventHandler<WebEvent<Boolean>>> onVisibilityChangedProperty() { return onVisibilityChanged; } 369 370 371 private ObjectProperty<Callback> createPopupHandler 372 = new SimpleObjectProperty<Callback>(this, "createPopupHandler", 373 new Callback() { 374 public WebEngine call(Object o) { 375 return WebEngine.this; 376 } 377 }); 378 379 /** 380 * Returns the JavaScript popup handler. 381 * @see #createPopupHandlerProperty 382 * @see #setCreatePopupHandler 383 */ 384 public final Callback getCreatePopupHandler() { return createPopupHandler.get(); } 385 386 /** 387 * Sets the JavaScript popup handler. 388 * @see #createPopupHandlerProperty 389 * @see #getCreatePopupHandler 390 * @see PopupFeatures 391 */ 392 public final void setCreatePopupHandler(Callback handler) { createPopupHandler.set(handler); } 393 394 /** 395 * JavaScript popup handler property. This handler is invoked when a script 396 * running on the Web page requests a popup to be created. 397 * <p>To satisfy this request a handler may create a new {@code WebEngine}, 398 * attach a visibility handler and optionally a resize handler, and return 399 * the newly created engine. To block the popup, a handler should return 400 * {@code null}. 401 * <p>By default, a popup handler is installed that opens popups in this 402 * {@code WebEngine}. 403 * 404 * @see PopupFeatures 405 */ 406 public final ObjectProperty<Callback> createPopupHandlerProperty() { return createPopupHandler; } 407 408 409 private ObjectProperty<Callback<String, Boolean>> confirmHandler 410 = new SimpleObjectProperty<Callback<String, Boolean>>(this, "confirmHandler"); 411 412 /** 413 * Returns the JavaScript {@code confirm} handler. 414 * @see #confirmHandlerProperty 415 * @see #setConfirmHandler 416 */ 417 public final Callback<String, Boolean> getConfirmHandler() { return confirmHandler.get(); } 418 419 /** 420 * Sets the JavaScript {@code confirm} handler. 421 * @see #confirmHandlerProperty 422 * @see #getConfirmHandler 423 */ 424 public final void setConfirmHandler(Callback<String, Boolean> handler) { confirmHandler.set(handler); } 425 426 /** 427 * JavaScript {@code confirm} handler property. This handler is invoked 428 * when a script running on the Web page calls the {@code confirm} function. 429 * <p>An implementation may display a dialog box with Yes and No options, 430 * and return the user's choice. 431 */ 432 public final ObjectProperty<Callback<String, Boolean>> confirmHandlerProperty() { return confirmHandler; } 433 434 435 private ObjectProperty<Callback> promptHandler 436 = new SimpleObjectProperty<Callback>(this, "promptHandler"); 437 438 /** 439 * Returns the JavaScript {@code prompt} handler. 440 * @see #promptHandlerProperty 441 * @see #setPromptHandler 442 * @see PromptData 443 */ 444 public final Callback getPromptHandler() { return promptHandler.get(); } 445 446 /** 447 * Sets the JavaScript {@code prompt} handler. 448 * @see #promptHandlerProperty 449 * @see #getPromptHandler 450 * @see PromptData 451 */ 452 public final void setPromptHandler(Callback handler) { promptHandler.set(handler); } 453 454 /** 455 * JavaScript {@code prompt} handler property. This handler is invoked 456 * when a script running on the Web page calls the {@code prompt} function. 457 * <p>An implementation may display a dialog box with an text field, 458 * and return the user's input. 459 * 460 * @see PromptData 461 */ 462 public final ObjectProperty<Callback> promptHandlerProperty() { return promptHandler; } 463 464 /** 465 * Creates a new engine. 466 */ 467 public WebEngine() { 468 this(null); 469 } 470 471 /** 472 * Creates a new engine and loads a Web page into it. 473 */ 474 public WebEngine(String url) { 475 js2javaBridge = new JS2JavaBridge(this); 476 load(url); 477 } 478 479 /** 480 * Loads a Web page into this engine. This method starts asynchronous 481 * loading and returns immediately. 482 * @param url URL of the web page to load 483 */ 484 public void load(String url) { 485 checkThread(); 486 487 if (url == null) { 488 url = ""; 489 } 490 491 if (view.get() != null) { 492 _loadUrl(view.get().getNativeHandle(), url); 493 } 494 } 495 496 /* Loads a web page */ 497 private native void _loadUrl(long handle, String url); 498 499 /** 500 * Loads the given HTML content directly. This method is useful when you have an HTML 501 * String composed in memory, or loaded from some system which cannot be reached via 502 * a URL (for example, the HTML text may have come from a database). As with 503 * {@link #load(String)}, this method is asynchronous. 504 */ 505 public void loadContent(String content) { 506 loadContent(content, "text/html"); 507 } 508 509 /** 510 * Loads the given content directly. This method is useful when you have content 511 * composed in memory, or loaded from some system which cannot be reached via 512 * a URL (for example, the SVG text may have come from a database). As with 513 * {@link #load(String)}, this method is asynchronous. This method also allows you to 514 * specify the content type of the string being loaded, and so may optionally support 515 * other types besides just HTML. 516 */ 517 public void loadContent(String content, String contentType) { 518 checkThread(); 519 _loadContent(view.get().getNativeHandle(), content); 520 } 521 522 /* Loads the given content directly */ 523 private native void _loadContent(long handle, String content); 524 525 /** 526 * Reloads the current page, whether loaded from URL or directly from a String in 527 * one of the {@code loadContent} methods. 528 */ 529 public void reload() { 530 checkThread(); 531 } 532 533 /** 534 * Executes a script in the context of the current page. 535 * @return execution result, converted to a Java object using the following 536 * rules: 537 * <ul> 538 * <li>JavaScript Int32 is converted to {@code java.lang.Integer} 539 * <li>Other JavaScript numbers to {@code java.lang.Double} 540 * <li>JavaScript string to {@code java.lang.String} 541 * <li>JavaScript boolean to {@code java.lang.Boolean} 542 * <li>JavaScript {@code null} to {@code null} 543 * <li>Most JavaScript objects get wrapped as 544 * {@code netscape.javascript.JSObject} 545 * <li>JavaScript JSNode objects get mapped to instances of 546 * {@code netscape.javascript.JSObject}, that also implement 547 * {@code org.w3c.dom.Node} 548 * <li>A special case is the JavaScript class {@code JavaRuntimeObject} 549 * which is used to wrap a Java object as a JavaScript value - in this 550 * case we just extract the original Java value. 551 * </ul> 552 */ 553 public Object executeScript(String script) { 554 checkThread(); 555 556 StringBuilder b = new StringBuilder("fxEvaluate('"); 557 b.append(escapeScript(script)); 558 b.append("')"); 559 String retVal = _executeScript(view.get().getNativeHandle(), b.toString()); 560 561 try { 562 return js2javaBridge.decode(retVal); 563 } catch (Exception ex) { 564 System.err.println("Couldn't parse arguments. " + ex); 565 } 566 return null; 567 } 568 569 void executeScriptDirect(String script) { 570 _executeScript(view.get().getNativeHandle(), script); 571 } 572 573 /* Executes a script in the context of the current page */ 574 private native String _executeScript(long handle, String script); 575 576 void setView(WebView view) { 577 this.view.setValue(view); 578 } 579 580 private void stop() { 581 checkThread(); 582 } 583 584 private String escapeScript(String script) { 585 return script.replace((CharSequence) "\\", (CharSequence) "\\\\") 586 .replace((CharSequence) "'", (CharSequence) "\\'") 587 .replace((CharSequence) "\"", (CharSequence) "\\\"") 588 .replace((CharSequence) "\n", (CharSequence) "\\n") 589 .replace((CharSequence) "\r", (CharSequence) "\\r") 590 .replace((CharSequence) "\t", (CharSequence) "\\t"); 591 } 592 593 /** 594 * Drives the {@code Timer} when {@code Timer.Mode.PLATFORM_TICKS} is set. 595 */ 596 private static class PulseTimer { 597 598 // Used just to guarantee constant pulse activity. See RT-14433. 599 private static AnimationTimer animation = 600 new AnimationTimer() { 601 @Override public void handle(long l) {} 602 }; 603 604 private static TKPulseListener listener = 605 new TKPulseListener() { 606 public void pulse() { 607 // Note, the timer event is executed right in the notifyTick(), 608 // that is during the pulse event. This makes the timer more 609 // repsonsive, though prolongs the pulse. So far it causes no 610 // problems but nevertheless it should be kept in mind. 611 //Timer.getTimer().notifyTick(); 612 } 613 }; 614 615 public static void start(){ 616 Toolkit.getToolkit().addSceneTkPulseListener(listener); 617 animation.start(); 618 } 619 620 public static void stop() { 621 Toolkit.getToolkit().removeSceneTkPulseListener(listener); 622 animation.stop(); 623 } 624 } 625 626 static void checkThread() { 627 Toolkit.getToolkit().checkFxUserThread(); 628 } 629 630 private class LoadWorker implements Worker<Void> { 631 632 private ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<State>(this, "state", State.READY); 633 @Override public final State getState() { checkThread(); return state.get(); } 634 @Override public final ReadOnlyObjectProperty<State> stateProperty() { checkThread(); return state.getReadOnlyProperty(); } 635 private final void updateState(State value) { 636 checkThread(); 637 this.state.set(value); 638 running.set(value == State.SCHEDULED || value == State.RUNNING); 639 } 640 641 /** 642 * @InheritDoc 643 */ 644 private ReadOnlyObjectWrapper<Void> value = new ReadOnlyObjectWrapper<Void>(this, "value", null); 645 @Override public final Void getValue() { checkThread(); return value.get(); } 646 @Override public final ReadOnlyObjectProperty<Void> valueProperty() { checkThread(); return value.getReadOnlyProperty(); } 647 648 /** 649 * @InheritDoc 650 */ 651 private ReadOnlyObjectWrapper<Throwable> exception = new ReadOnlyObjectWrapper<Throwable>(this, "exception"); 652 @Override public final Throwable getException() { checkThread(); return exception.get(); } 653 @Override public final ReadOnlyObjectProperty<Throwable> exceptionProperty() { checkThread(); return exception.getReadOnlyProperty(); } 654 655 /** 656 * @InheritDoc 657 */ 658 private ReadOnlyDoubleWrapper workDone = new ReadOnlyDoubleWrapper(this, "workDone", -1); 659 @Override public final double getWorkDone() { checkThread(); return workDone.get(); } 660 @Override public final ReadOnlyDoubleProperty workDoneProperty() { checkThread(); return workDone.getReadOnlyProperty(); } 661 662 /** 663 * @InheritDoc 664 */ 665 private ReadOnlyDoubleWrapper totalWorkToBeDone = new ReadOnlyDoubleWrapper(this, "totalWork", -1); 666 @Override public final double getTotalWork() { checkThread(); return totalWorkToBeDone.get(); } 667 @Override public final ReadOnlyDoubleProperty totalWorkProperty() { checkThread(); return totalWorkToBeDone.getReadOnlyProperty(); } 668 669 /** 670 * @InheritDoc 671 */ 672 private ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, "progress", -1); 673 @Override public final double getProgress() { checkThread(); return progress.get(); } 674 @Override public final ReadOnlyDoubleProperty progressProperty() { checkThread(); return progress.getReadOnlyProperty(); } 675 private void updateProgress(double p) { 676 totalWorkToBeDone.set(100.0); 677 workDone.set(p * 100.0); 678 progress.set(p); 679 } 680 681 /** 682 * @InheritDoc 683 */ 684 private ReadOnlyBooleanWrapper running = new ReadOnlyBooleanWrapper(this, "running", false); 685 @Override public final boolean isRunning() { checkThread(); return running.get(); } 686 @Override public final ReadOnlyBooleanProperty runningProperty() { checkThread(); return running.getReadOnlyProperty(); } 687 688 /** 689 * @InheritDoc 690 */ 691 private ReadOnlyStringWrapper message = new ReadOnlyStringWrapper(this, "message", ""); 692 @Override public final String getMessage() { return message.get(); } 693 @Override public final ReadOnlyStringProperty messageProperty() { return message.getReadOnlyProperty(); } 694 695 /** 696 * @InheritDoc 697 */ 698 private ReadOnlyStringWrapper title = new ReadOnlyStringWrapper(this, "title", "WebEngine Loader"); 699 @Override public final String getTitle() { return title.get(); } 700 @Override public final ReadOnlyStringProperty titleProperty() { return title.getReadOnlyProperty(); } 701 702 /** 703 * Cancels the loading of the page. If called after the page has already 704 * been loaded, then this call takes no effect. 705 */ 706 @Override public boolean cancel() { 707 if (isRunning()) { 708 stop(); // this call indirectly sets state 709 return true; 710 } else { 711 return false; 712 } 713 } 714 715 private void cancelAndReset() { 716 cancel(); 717 exception.set(null); 718 message.set(""); 719 totalWorkToBeDone.set(-1); 720 workDone.set(-1); 721 progress.set(-1); 722 updateState(State.READY); 723 running.set(false); 724 } 725 726 private void dispatchLoadEvent(long frame, int state, 727 String url, String contentType, double workDone, int errorCode) { 728 } 729 730 Throwable describeError(int errorCode) { 731 String reason = "Unknown error"; 732 733 return new Throwable(reason); 734 } 735 } 736 737 private final class DocumentProperty 738 extends ReadOnlyObjectPropertyBase<Document> { 739 740 private boolean available; 741 private Document document; 742 743 private void invalidate(boolean available) { 744 if (this.available || available) { 745 this.available = available; 746 this.document = null; 747 fireValueChangedEvent(); 748 } 749 } 750 751 public Document get() { 752 if (!this.available) { 753 return null; 754 } 755 if (this.document == null) { 756 if (this.document == null) { 757 this.available = false; 758 } 759 } 760 return this.document; 761 } 762 763 public Object getBean() { 764 return WebEngine.this; 765 } 766 767 public String getName() { 768 return "document"; 769 } 770 } 771 772 /////////////////////////////////////////////// 773 // JavaScript to Java bridge 774 /////////////////////////////////////////////// 775 776 private JS2JavaBridge js2javaBridge = null; 777 778 public void exportObject(String jsName, Object object) { 779 synchronized (loadedLock) { 780 if (js2javaBridge == null) { 781 js2javaBridge = new JS2JavaBridge(this); 782 } 783 js2javaBridge.exportObject(jsName, object); 784 } 785 } 786 787 788 interface PageListener { 789 void onLoadStarted(); 790 void onLoadFinished(); 791 void onLoadFailed(); 792 void onJavaCall(String args); 793 } 794 795 private PageListener pageListener = null; 796 private boolean loaded = false; 797 private final Object loadedLock = new Object(); 798 799 void setPageListener(PageListener listener) { 800 synchronized (loadedLock) { 801 pageListener = listener; 802 if (loaded) { 803 pageListener.onLoadStarted(); 804 pageListener.onLoadFinished(); 805 } 806 } 807 } 808 809 boolean isLoaded() { 810 return loaded; 811 } 812 813 // notifications are called from WebView 814 void notifyLoadStarted() { 815 synchronized (loadedLock) { 816 loaded = false; 817 if (pageListener != null) { 818 pageListener.onLoadStarted(); 819 } 820 } 821 } 822 823 void notifyLoadFinished() { 824 synchronized (loadedLock) { 825 loaded = true; 826 if (pageListener != null) { 827 pageListener.onLoadFinished(); 828 } 829 } 830 } 831 832 void notifyLoadFailed() { 833 synchronized (loadedLock) { 834 loaded = false; 835 if (pageListener != null) { 836 pageListener.onLoadFailed(); 837 } 838 } 839 } 840 841 void notifyJavaCall(String arg) { 842 if (pageListener != null) { 843 pageListener.onJavaCall(arg); 844 } 845 } 846 847 void onAlertNotify(String text) { 848 if (getOnAlert() != null) { 849 dispatchWebEvent( 850 getOnAlert(), 851 new WebEvent<String>(this, WebEvent.ALERT, text)); 852 } 853 } 854 855 private void dispatchWebEvent(final EventHandler handler, final WebEvent ev) { 856 AccessController.doPrivileged(new PrivilegedAction<Void>() { 857 @Override 858 public Void run() { 859 handler.handle(ev); 860 return null; 861 } 862 }); 863 } 864}