1 /* 2 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javafx.scene.web; 26 27 import com.sun.java.scene.web.WebViewHelper; 28 import com.sun.webkit.WebPage; 29 import javafx.css.converter.BooleanConverter; 30 import javafx.css.converter.EnumConverter; 31 import javafx.css.converter.SizeConverter; 32 import com.sun.javafx.geom.BaseBounds; 33 import com.sun.javafx.scene.DirtyBits; 34 import javafx.css.Styleable; 35 import com.sun.javafx.scene.NodeHelper; 36 import com.sun.javafx.sg.prism.NGNode; 37 import com.sun.javafx.sg.prism.NGWebView; 38 import com.sun.javafx.tk.TKPulseListener; 39 import com.sun.javafx.tk.Toolkit; 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.HashMap; 43 import java.util.LinkedList; 44 import java.util.List; 45 import java.util.Map; 46 import javafx.beans.property.BooleanProperty; 47 import javafx.beans.property.DoubleProperty; 48 import javafx.beans.property.ObjectProperty; 49 import javafx.beans.property.ReadOnlyDoubleProperty; 50 import javafx.beans.property.ReadOnlyDoubleWrapper; 51 import javafx.beans.value.ChangeListener; 52 import javafx.beans.value.ObservableValue; 53 import javafx.collections.ObservableList; 54 import javafx.css.CssMetaData; 55 import javafx.css.StyleableBooleanProperty; 56 import javafx.css.StyleableDoubleProperty; 57 import javafx.css.StyleableObjectProperty; 58 import javafx.css.StyleableProperty; 59 import javafx.event.EventType; 60 import javafx.geometry.NodeOrientation; 61 import com.sun.javafx.geom.transform.BaseTransform; 62 import javafx.scene.Node; 63 import javafx.scene.Parent; 64 import javafx.scene.Scene; 65 import javafx.scene.input.DragEvent; 66 import javafx.scene.input.TransferMode; 67 import javafx.scene.text.FontSmoothingType; 68 69 /** 70 * {@code WebView} is a {@link javafx.scene.Node} that manages a 71 * {@link WebEngine} and displays its content. The associated {@code WebEngine} 72 * is created automatically at construction time and cannot be changed 73 * afterwards. {@code WebView} handles mouse and some keyboard events, and 74 * manages scrolling automatically, so there's no need to put it into a 75 * {@code ScrollPane}. 76 * 77 * <p>{@code WebView} objects must be created and accessed solely from the 78 * FX thread. 79 */ 80 final public class WebView extends Parent { 81 static { 82 WebViewHelper.setWebViewAccessor(new WebViewHelper.WebViewAccessor() { 83 @Override 84 public NGNode doCreatePeer(Node node) { 85 return ((WebView) node).doCreatePeer(); 86 } 87 88 @Override 89 public void doUpdatePeer(Node node) { 90 ((WebView) node).doUpdatePeer(); 91 } 92 }); 93 } 94 95 private static final Map<Object, Integer> idMap = new HashMap<Object, Integer>(); 96 97 private static final boolean DEFAULT_CONTEXT_MENU_ENABLED = true; 98 private static final FontSmoothingType DEFAULT_FONT_SMOOTHING_TYPE = FontSmoothingType.LCD; 99 private static final double DEFAULT_ZOOM = 1.0; 100 private static final double DEFAULT_FONT_SCALE = 1.0; 101 private static final double DEFAULT_MIN_WIDTH = 0; 102 private static final double DEFAULT_MIN_HEIGHT = 0; 103 private static final double DEFAULT_PREF_WIDTH = 800; 104 private static final double DEFAULT_PREF_HEIGHT = 600; 105 private static final double DEFAULT_MAX_WIDTH = Double.MAX_VALUE; 106 private static final double DEFAULT_MAX_HEIGHT = Double.MAX_VALUE; 107 108 private final WebPage page; 109 private final WebEngine engine; 110 // private volatile InputMethodClientImpl imClient; 111 112 /** 113 * The stage pulse listener registered with the toolkit. 114 * This field guarantees that the listener will exist throughout 115 * the whole lifetime of the WebView node. This field is necessary 116 * because the toolkit references its stage pulse listeners weakly. 117 */ 118 private final TKPulseListener stagePulseListener; 119 120 /** 121 * Returns the {@code WebEngine} object. 122 */ 123 public final WebEngine getEngine() { 124 return engine; 125 } 126 127 private final ReadOnlyDoubleWrapper width = new ReadOnlyDoubleWrapper(this, "width"); 128 129 /** 130 * Returns width of this {@code WebView}. 131 */ 132 public final double getWidth() { 133 return width.get(); 134 } 135 136 /** 137 * Width of this {@code WebView}. 138 */ 139 public ReadOnlyDoubleProperty widthProperty() { 140 return width.getReadOnlyProperty(); 141 } 142 143 private final ReadOnlyDoubleWrapper height = new ReadOnlyDoubleWrapper(this, "height"); 144 145 /** 146 * Returns height of this {@code WebView}. 147 */ 148 public final double getHeight() { 149 return height.get(); 150 } 151 152 /** 153 * Height of this {@code WebView}. 154 */ 155 public ReadOnlyDoubleProperty heightProperty() { 156 return height.getReadOnlyProperty(); 157 } 158 159 /** 160 * Zoom factor applied to the whole page contents. 161 * 162 * @defaultValue 1.0 163 */ 164 private DoubleProperty zoom; 165 166 /** 167 * Sets current zoom factor applied to the whole page contents. 168 * @param value zoom factor to be set 169 * @see #zoomProperty() 170 * @see #getZoom() 171 */ 172 public final void setZoom(double value) { 173 WebEngine.checkThread(); 174 zoomProperty().set(value); 175 } 176 177 /** 178 * Returns current zoom factor applied to the whole page contents. 179 * @return current zoom factor 180 * @see #zoomProperty() 181 * @see #setZoom(double value) 182 */ 183 public final double getZoom() { 184 return (this.zoom != null) 185 ? this.zoom.get() 186 : DEFAULT_ZOOM; 187 } 188 189 /** 190 * Returns zoom property object. 191 * @return zoom property object 192 * @see #getZoom() 193 * @see #setZoom(double value) 194 */ 195 public final DoubleProperty zoomProperty() { 196 if (zoom == null) { 197 zoom = new StyleableDoubleProperty(DEFAULT_ZOOM) { 198 @Override public void invalidated() { 199 Toolkit.getToolkit().checkFxUserThread(); 200 page.setZoomFactor((float) get(), false); 201 } 202 203 @Override public CssMetaData<WebView, Number> getCssMetaData() { 204 return StyleableProperties.ZOOM; 205 } 206 @Override public Object getBean() { 207 return WebView.this; 208 } 209 @Override public String getName() { 210 return "zoom"; 211 } 212 }; 213 } 214 return zoom; 215 } 216 217 /** 218 * Specifies scale factor applied to font. This setting affects 219 * text content but not images and fixed size elements. 220 * 221 * @defaultValue 1.0 222 */ 223 private DoubleProperty fontScale; 224 225 public final void setFontScale(double value) { 226 WebEngine.checkThread(); 227 fontScaleProperty().set(value); 228 } 229 230 public final double getFontScale() { 231 return (this.fontScale != null) 232 ? this.fontScale.get() 233 : DEFAULT_FONT_SCALE; 234 } 235 236 public DoubleProperty fontScaleProperty() { 237 if (fontScale == null) { 238 fontScale = new StyleableDoubleProperty(DEFAULT_FONT_SCALE) { 239 @Override public void invalidated() { 240 Toolkit.getToolkit().checkFxUserThread(); 241 page.setZoomFactor((float)get(), true); 242 } 243 @Override public CssMetaData<WebView, Number> getCssMetaData() { 244 return StyleableProperties.FONT_SCALE; 245 } 246 @Override public Object getBean() { 247 return WebView.this; 248 } 249 @Override public String getName() { 250 return "fontScale"; 251 } 252 }; 253 } 254 return fontScale; 255 } 256 257 /** 258 * Creates a {@code WebView} object. 259 */ 260 public WebView() { 261 setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT); 262 // getStyleClass().add("web-view"); 263 engine = new WebEngine(); 264 engine.setView(this); 265 page = engine.getPage(); 266 // page.setFontSmoothingType(DEFAULT_FONT_SMOOTHING_TYPE.ordinal()); 267 268 // registerEventHandlers(); 269 stagePulseListener = new TKPulseListener() { 270 @Override public void pulse() { 271 handleStagePulse(); 272 } 273 }; 274 focusedProperty().addListener(new ChangeListener<Boolean>() { 275 276 public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) { 277 if (page != null) { 278 // Traversal direction is not currently available in FX. 279 // WCFocusEvent focusEvent = new WCFocusEvent( 280 // isFocused() ? WCFocusEvent.FOCUS_GAINED 281 // : WCFocusEvent.FOCUS_LOST, 282 // WCFocusEvent.UNKNOWN); 283 // page.dispatchFocusEvent(focusEvent); 284 } 285 } 286 }); 287 setFocusTraversable(true); 288 Toolkit.getToolkit().addStageTkPulseListener(stagePulseListener); 289 290 //android specific 291 parentProperty().addListener(new ChangeListener<Parent>() { 292 public void changed(ObservableValue<? extends Parent> observable, Parent oldValue, Parent newValue) { 293 if (oldValue != null && newValue == null) { 294 System.out.println("--> WebView Has been removed from scene"); 295 } 296 } 297 }); 298 } 299 300 // Resizing support. Allows arbitrary growing and shrinking. 301 // Designed after javafx.scene.control.Control 302 303 @Override public boolean isResizable() { 304 return true; 305 } 306 307 @Override public void resize(double width, double height) { 308 if ((width != this.width.get()) || (height != this.height.get())) { 309 this.width.set(width); 310 this.height.set(height); 311 NodeHelper.markDirty(this, DirtyBits.NODE_GEOMETRY); 312 impl_geomChanged(); 313 } 314 } 315 316 /** 317 * Called during layout to determine the minimum width for this node. 318 * 319 * @return the minimum width that this node should be resized to during layout 320 */ 321 @Override public final double minWidth(double height) { 322 return getMinWidth(); 323 } 324 325 /** 326 * Called during layout to determine the minimum height for this node. 327 * 328 * @return the minimum height that this node should be resized to during layout 329 */ 330 @Override public final double minHeight(double width) { 331 return getMinHeight(); 332 } 333 334 335 /** 336 * Called during layout to determine the preferred width for this node. 337 * 338 * @return the preferred width that this node should be resized to during layout 339 */ 340 @Override public final double prefWidth(double height) { 341 return getPrefWidth(); 342 } 343 344 /** 345 * Called during layout to determine the preferred height for this node. 346 * 347 * @return the preferred height that this node should be resized to during layout 348 */ 349 @Override public final double prefHeight(double width) { 350 return getPrefHeight(); 351 } 352 /** 353 * Called during layout to determine the maximum width for this node. 354 * 355 * @return the maximum width that this node should be resized to during layout 356 */ 357 @Override public final double maxWidth(double height) { 358 return getMaxWidth(); 359 } 360 361 /** 362 * Called during layout to determine the maximum height for this node. 363 * 364 * @return the maximum height that this node should be resized to during layout 365 */ 366 @Override public final double maxHeight(double width) { 367 return getMaxHeight(); 368 } 369 370 /** 371 * Minimum width property. 372 */ 373 public DoubleProperty minWidthProperty() { 374 if (minWidth == null) { 375 minWidth = new StyleableDoubleProperty(DEFAULT_MIN_WIDTH) { 376 @Override 377 public void invalidated() { 378 if (getParent() != null) { 379 getParent().requestLayout(); 380 } 381 } 382 @Override 383 public CssMetaData<WebView, Number> getCssMetaData() { 384 return StyleableProperties.MIN_WIDTH; 385 } 386 @Override 387 public Object getBean() { 388 return WebView.this; 389 } 390 @Override 391 public String getName() { 392 return "minWidth"; 393 } 394 }; 395 } 396 return minWidth; 397 } 398 private DoubleProperty minWidth; 399 400 /** 401 * Sets minimum width. 402 */ 403 public final void setMinWidth(double value) { 404 minWidthProperty().set(value); 405 } 406 407 /** 408 * Returns minimum width. 409 */ 410 public final double getMinWidth() { 411 return (this.minWidth != null) 412 ? this.minWidth.get() 413 : DEFAULT_MIN_WIDTH; 414 } 415 416 /** 417 * Minimum height property. 418 */ 419 public DoubleProperty minHeightProperty() { 420 if (minHeight == null) { 421 minHeight = new StyleableDoubleProperty(DEFAULT_MIN_HEIGHT) { 422 @Override 423 public void invalidated() { 424 if (getParent() != null) { 425 getParent().requestLayout(); 426 } 427 } 428 @Override 429 public CssMetaData<WebView, Number> getCssMetaData() { 430 return StyleableProperties.MIN_HEIGHT; 431 } 432 @Override 433 public Object getBean() { 434 return WebView.this; 435 } 436 @Override 437 public String getName() { 438 return "minHeight"; 439 } 440 }; 441 } 442 return minHeight; 443 } 444 private DoubleProperty minHeight; 445 446 /** 447 * Sets minimum height. 448 */ 449 public final void setMinHeight(double value) { 450 minHeightProperty().set(value); 451 } 452 453 /** 454 * Sets minimum height. 455 */ 456 public final double getMinHeight() { 457 return (this.minHeight != null) 458 ? this.minHeight.get() 459 : DEFAULT_MIN_HEIGHT; 460 } 461 462 /** 463 * Convenience method for setting minimum width and height. 464 */ 465 public void setMinSize(double minWidth, double minHeight) { 466 setMinWidth(minWidth); 467 setMinHeight(minHeight); 468 } 469 470 /** 471 * Preferred width property. 472 */ 473 public DoubleProperty prefWidthProperty() { 474 if (prefWidth == null) { 475 prefWidth = new StyleableDoubleProperty(DEFAULT_PREF_WIDTH) { 476 @Override 477 public void invalidated() { 478 if (getParent() != null) { 479 getParent().requestLayout(); 480 } 481 } 482 @Override 483 public CssMetaData<WebView, Number> getCssMetaData() { 484 return StyleableProperties.PREF_WIDTH; 485 } 486 @Override 487 public Object getBean() { 488 return WebView.this; 489 } 490 @Override 491 public String getName() { 492 return "prefWidth"; 493 } 494 }; 495 } 496 return prefWidth; 497 } 498 private DoubleProperty prefWidth; 499 500 /** 501 * Sets preferred width. 502 */ 503 public final void setPrefWidth(double value) { 504 prefWidthProperty().set(value); 505 } 506 507 /** 508 * Returns preferred width. 509 */ 510 public final double getPrefWidth() { 511 return (this.prefWidth != null) 512 ? this.prefWidth.get() 513 : DEFAULT_PREF_WIDTH; 514 } 515 516 /** 517 * Preferred height property. 518 */ 519 public DoubleProperty prefHeightProperty() { 520 if (prefHeight == null) { 521 prefHeight = new StyleableDoubleProperty(DEFAULT_PREF_HEIGHT) { 522 @Override 523 public void invalidated() { 524 if (getParent() != null) { 525 getParent().requestLayout(); 526 } 527 } 528 @Override 529 public CssMetaData<WebView, Number> getCssMetaData() { 530 return StyleableProperties.PREF_HEIGHT; 531 } 532 @Override 533 public Object getBean() { 534 return WebView.this; 535 } 536 @Override 537 public String getName() { 538 return "prefHeight"; 539 } 540 }; 541 } 542 return prefHeight; 543 } 544 private DoubleProperty prefHeight; 545 546 /** 547 * Sets preferred height. 548 */ 549 public final void setPrefHeight(double value) { 550 prefHeightProperty().set(value); 551 } 552 553 /** 554 * Returns preferred height. 555 */ 556 public final double getPrefHeight() { 557 return (this.prefHeight != null) 558 ? this.prefHeight.get() 559 : DEFAULT_PREF_HEIGHT; 560 } 561 562 /** 563 * Convenience method for setting preferred width and height. 564 */ 565 public void setPrefSize(double prefWidth, double prefHeight) { 566 setPrefWidth(prefWidth); 567 setPrefHeight(prefHeight); 568 } 569 570 /** 571 * Maximum width property. 572 */ 573 public DoubleProperty maxWidthProperty() { 574 if (maxWidth == null) { 575 maxWidth = new StyleableDoubleProperty(DEFAULT_MAX_WIDTH) { 576 @Override 577 public void invalidated() { 578 if (getParent() != null) { 579 getParent().requestLayout(); 580 } 581 } 582 @Override 583 public CssMetaData<WebView, Number> getCssMetaData() { 584 return StyleableProperties.MAX_WIDTH; 585 } 586 @Override 587 public Object getBean() { 588 return WebView.this; 589 } 590 @Override 591 public String getName() { 592 return "maxWidth"; 593 } 594 }; 595 } 596 return maxWidth; 597 } 598 private DoubleProperty maxWidth; 599 600 /** 601 * Sets maximum width. 602 */ 603 public final void setMaxWidth(double value) { 604 maxWidthProperty().set(value); 605 } 606 607 /** 608 * Returns maximum width. 609 */ 610 public final double getMaxWidth() { 611 return (this.maxWidth != null) 612 ? this.maxWidth.get() 613 : DEFAULT_MAX_WIDTH; 614 } 615 616 /** 617 * Maximum height property. 618 */ 619 public DoubleProperty maxHeightProperty() { 620 if (maxHeight == null) { 621 maxHeight = new StyleableDoubleProperty(DEFAULT_MAX_HEIGHT) { 622 @Override 623 public void invalidated() { 624 if (getParent() != null) { 625 getParent().requestLayout(); 626 } 627 } 628 @Override 629 public CssMetaData<WebView, Number> getCssMetaData() { 630 return StyleableProperties.MAX_HEIGHT; 631 } 632 @Override 633 public Object getBean() { 634 return WebView.this; 635 } 636 @Override 637 public String getName() { 638 return "maxHeight"; 639 } 640 }; 641 } 642 return maxHeight; 643 } 644 private DoubleProperty maxHeight; 645 646 /** 647 * Sets maximum height. 648 */ 649 public final void setMaxHeight(double value) { 650 maxHeightProperty().set(value); 651 } 652 653 /** 654 * Returns maximum height. 655 */ 656 public final double getMaxHeight() { 657 return (this.maxHeight != null) 658 ? this.maxHeight.get() 659 : DEFAULT_MAX_HEIGHT; 660 } 661 662 /** 663 * Convenience method for setting maximum width and height. 664 */ 665 public void setMaxSize(double maxWidth, double maxHeight) { 666 setMaxWidth(maxWidth); 667 setMaxHeight(maxHeight); 668 } 669 670 671 /** 672 * Specifies a requested font smoothing type : gray or LCD. 673 * 674 * The width of the bounding box is defined by the widest row. 675 * 676 * Note: LCD mode doesn't apply in numerous cases, such as various 677 * compositing modes, where effects are applied and very large glyphs. 678 * 679 * @defaultValue FontSmoothingType.LCD 680 * @since 2.2 681 */ 682 private ObjectProperty<FontSmoothingType> fontSmoothingType; 683 684 public final void setFontSmoothingType(FontSmoothingType value) { 685 fontSmoothingTypeProperty().set(value); 686 } 687 688 public final FontSmoothingType getFontSmoothingType() { 689 return (this.fontSmoothingType != null) 690 ? this.fontSmoothingType.get() 691 : DEFAULT_FONT_SMOOTHING_TYPE; 692 } 693 694 public final ObjectProperty<FontSmoothingType> fontSmoothingTypeProperty() { 695 if (this.fontSmoothingType == null) { 696 this.fontSmoothingType = new StyleableObjectProperty<FontSmoothingType>(DEFAULT_FONT_SMOOTHING_TYPE) { 697 @Override 698 public void invalidated() { 699 Toolkit.getToolkit().checkFxUserThread(); 700 page.setFontSmoothingType(get().ordinal()); 701 } 702 @Override 703 public CssMetaData<WebView, FontSmoothingType> getCssMetaData() { 704 return StyleableProperties.FONT_SMOOTHING_TYPE; 705 } 706 @Override 707 public Object getBean() { 708 return WebView.this; 709 } 710 @Override 711 public String getName() { 712 return "fontSmoothingType"; 713 } 714 }; 715 } 716 return this.fontSmoothingType; 717 } 718 719 /** 720 * Specifies whether context menu is enabled. 721 * 722 * @defaultValue true 723 * @since 2.2 724 */ 725 private BooleanProperty contextMenuEnabled; 726 727 public final void setContextMenuEnabled(boolean value) { 728 contextMenuEnabledProperty().set(value); 729 } 730 731 public final boolean isContextMenuEnabled() { 732 return contextMenuEnabled == null 733 ? DEFAULT_CONTEXT_MENU_ENABLED 734 : contextMenuEnabled.get(); 735 } 736 737 public final BooleanProperty contextMenuEnabledProperty() { 738 if (contextMenuEnabled == null) { 739 contextMenuEnabled = new StyleableBooleanProperty(DEFAULT_CONTEXT_MENU_ENABLED) { 740 @Override public void invalidated() { 741 Toolkit.getToolkit().checkFxUserThread(); 742 page.setContextMenuEnabled(get()); 743 } 744 745 @Override public CssMetaData<WebView, Boolean> getCssMetaData() { 746 return StyleableProperties.CONTEXT_MENU_ENABLED; 747 } 748 749 @Override public Object getBean() { 750 return WebView.this; 751 } 752 753 @Override public String getName() { 754 return "contextMenuEnabled"; 755 } 756 }; 757 } 758 return contextMenuEnabled; 759 } 760 761 /** 762 * Super-lazy instantiation pattern from Bill Pugh. 763 */ 764 private static final class StyleableProperties { 765 766 private static final CssMetaData<WebView, Boolean> CONTEXT_MENU_ENABLED 767 = new CssMetaData<WebView, Boolean>( 768 "-fx-context-menu-enabled", 769 BooleanConverter.getInstance(), 770 DEFAULT_CONTEXT_MENU_ENABLED) 771 { 772 @Override public boolean isSettable(WebView view) { 773 return view.contextMenuEnabled == null || !view.contextMenuEnabled.isBound(); 774 } 775 @Override public StyleableProperty<Boolean> getStyleableProperty(WebView view) { 776 return (StyleableProperty<Boolean>)view.contextMenuEnabledProperty(); 777 } 778 }; 779 780 private static final CssMetaData<WebView, FontSmoothingType> FONT_SMOOTHING_TYPE 781 = new CssMetaData<WebView, FontSmoothingType>( 782 "-fx-font-smoothing-type", 783 new EnumConverter<FontSmoothingType>(FontSmoothingType.class), 784 DEFAULT_FONT_SMOOTHING_TYPE) { 785 @Override 786 public boolean isSettable(WebView view) { 787 return view.fontSmoothingType == null || !view.fontSmoothingType.isBound(); 788 } 789 @Override 790 public StyleableProperty<FontSmoothingType> getStyleableProperty(WebView view) { 791 return (StyleableProperty<FontSmoothingType>)view.fontSmoothingTypeProperty(); 792 } 793 }; 794 795 private static final CssMetaData<WebView, Number> ZOOM 796 = new CssMetaData<WebView, Number>( 797 "-fx-zoom", 798 SizeConverter.getInstance(), 799 DEFAULT_ZOOM) { 800 @Override public boolean isSettable(WebView view) { 801 return view.zoom == null || !view.zoom.isBound(); 802 } 803 @Override public StyleableProperty<Number> getStyleableProperty(WebView view) { 804 return (StyleableProperty<Number>)view.zoomProperty(); 805 } 806 }; 807 808 private static final CssMetaData<WebView, Number> FONT_SCALE 809 = new CssMetaData<WebView, Number>( 810 "-fx-font-scale", 811 SizeConverter.getInstance(), 812 DEFAULT_FONT_SCALE) { 813 @Override 814 public boolean isSettable(WebView view) { 815 return view.fontScale == null || !view.fontScale.isBound(); 816 } 817 @Override 818 public StyleableProperty<Number> getStyleableProperty(WebView view) { 819 return (StyleableProperty<Number>)view.fontScaleProperty(); 820 } 821 }; 822 823 private static final CssMetaData<WebView, Number> MIN_WIDTH 824 = new CssMetaData<WebView, Number>( 825 "-fx-min-width", 826 SizeConverter.getInstance(), 827 DEFAULT_MIN_WIDTH) { 828 @Override 829 public boolean isSettable(WebView view) { 830 return view.minWidth == null || !view.minWidth.isBound(); 831 } 832 @Override 833 public StyleableProperty<Number> getStyleableProperty(WebView view) { 834 return (StyleableProperty<Number>)view.minWidthProperty(); 835 } 836 }; 837 838 private static final CssMetaData<WebView, Number> MIN_HEIGHT 839 = new CssMetaData<WebView, Number>( 840 "-fx-min-height", 841 SizeConverter.getInstance(), 842 DEFAULT_MIN_HEIGHT) { 843 @Override 844 public boolean isSettable(WebView view) { 845 return view.minHeight == null || !view.minHeight.isBound(); 846 } 847 @Override 848 public StyleableProperty<Number> getStyleableProperty(WebView view) { 849 return (StyleableProperty<Number>)view.minHeightProperty(); 850 } 851 }; 852 853 private static final CssMetaData<WebView, Number> MAX_WIDTH 854 = new CssMetaData<WebView, Number>( 855 "-fx-max-width", 856 SizeConverter.getInstance(), 857 DEFAULT_MAX_WIDTH) { 858 @Override 859 public boolean isSettable(WebView view) { 860 return view.maxWidth == null || !view.maxWidth.isBound(); 861 } 862 @Override 863 public StyleableProperty<Number> getStyleableProperty(WebView view) { 864 return (StyleableProperty<Number>)view.maxWidthProperty(); 865 } 866 }; 867 868 private static final CssMetaData<WebView, Number> MAX_HEIGHT 869 = new CssMetaData<WebView, Number>( 870 "-fx-max-height", 871 SizeConverter.getInstance(), 872 DEFAULT_MAX_HEIGHT) { 873 @Override 874 public boolean isSettable(WebView view) { 875 return view.maxHeight == null || !view.maxHeight.isBound(); 876 } 877 @Override 878 public StyleableProperty<Number> getStyleableProperty(WebView view) { 879 return (StyleableProperty<Number>)view.maxHeightProperty(); 880 } 881 }; 882 883 private static final CssMetaData<WebView, Number> PREF_WIDTH 884 = new CssMetaData<WebView, Number>( 885 "-fx-pref-width", 886 SizeConverter.getInstance(), 887 DEFAULT_PREF_WIDTH) { 888 @Override 889 public boolean isSettable(WebView view) { 890 return view.prefWidth == null || !view.prefWidth.isBound(); 891 } 892 @Override 893 public StyleableProperty<Number> getStyleableProperty(WebView view) { 894 return (StyleableProperty<Number>)view.prefWidthProperty(); 895 } 896 }; 897 898 private static final CssMetaData<WebView, Number> PREF_HEIGHT 899 = new CssMetaData<WebView, Number>( 900 "-fx-pref-height", 901 SizeConverter.getInstance(), 902 DEFAULT_PREF_HEIGHT) { 903 @Override 904 public boolean isSettable(WebView view) { 905 return view.prefHeight == null || !view.prefHeight.isBound(); 906 } 907 @Override 908 public StyleableProperty<Number> getStyleableProperty(WebView view) { 909 return (StyleableProperty<Number>)view.prefHeightProperty(); 910 } 911 }; 912 913 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 914 915 static { 916 List<CssMetaData<? extends Styleable, ?>> styleables 917 = new ArrayList<CssMetaData<? extends Styleable, ?>>(Parent.getClassCssMetaData()); 918 styleables.add(CONTEXT_MENU_ENABLED); 919 styleables.add(FONT_SMOOTHING_TYPE); 920 styleables.add(ZOOM); 921 styleables.add(FONT_SCALE); 922 styleables.add(MIN_WIDTH); 923 styleables.add(PREF_WIDTH); 924 styleables.add(MAX_WIDTH); 925 styleables.add(MIN_HEIGHT); 926 styleables.add(PREF_HEIGHT); 927 styleables.add(MAX_HEIGHT); 928 STYLEABLES = Collections.unmodifiableList(styleables); 929 } 930 } 931 932 /** 933 * @return The CssMetaData associated with this class, which may include the 934 * CssMetaData of its super classes. 935 */ 936 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 937 return StyleableProperties.STYLEABLES; 938 } 939 940 /** 941 * {@inheritDoc} 942 */ 943 @Override 944 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 945 return getClassCssMetaData(); 946 } 947 948 // event handling 949 950 private void handleStagePulse() { 951 // The stage pulse occurs before the scene pulse. 952 // Here the page content is updated before CSS/Layout/Sync pass 953 // is initiated by the scene pulse. The update may 954 // change the WebView children and, if so, the children should be 955 // processed right away during the scene pulse. 956 957 // The WebView node does not render its pending render queues 958 // while it is invisible. Therefore, we should not schedule new 959 // render queues while the WebView is invisible to prevent 960 // the list of render queues from growing infinitely. 961 // Also, if and when the WebView becomes invisible, the currently 962 // pending render queues, if any, become obsolete and should be 963 // discarded. 964 965 if (page == null) return; 966 967 boolean reallyVisible = impl_isTreeVisible() 968 && getScene() != null 969 && getScene().getWindow() != null 970 && getScene().getWindow().isShowing(); 971 972 if (reallyVisible) { 973 page.setVisible(true); 974 if (page.isDirty()) { 975 Scene.impl_setAllowPGAccess(true); 976 977 final NGWebView peer = NodeHelper.getPeer(this); 978 peer.update(); // creates new render queues 979 if (page.isRepaintPending()) { 980 NodeHelper.markDirty(this, DirtyBits.WEBVIEW_VIEW); 981 } 982 Scene.impl_setAllowPGAccess(false); 983 } 984 } else { 985 page.dropRenderFrames(); 986 page.setVisible(false); 987 } 988 } 989 990 991 private static final int WK_DND_ACTION_NONE = 0x0; 992 private static final int WK_DND_ACTION_COPY = 0x1; 993 private static final int WK_DND_ACTION_MOVE = 0x2; 994 private static final int WK_DND_ACTION_LINK = 0x40000000; 995 996 private static int getWKDndEventType(EventType et) { 997 int commandId = 0; 998 if (et == DragEvent.DRAG_ENTERED) 999 commandId = WebPage.DND_DST_ENTER; 1000 else if (et == DragEvent.DRAG_EXITED) 1001 commandId = WebPage.DND_DST_EXIT; 1002 else if (et == DragEvent.DRAG_OVER) 1003 commandId = WebPage.DND_DST_OVER; 1004 else if (et == DragEvent.DRAG_DROPPED) 1005 commandId = WebPage.DND_DST_DROP; 1006 return commandId; 1007 } 1008 1009 private static int getWKDndAction(TransferMode... tms) { 1010 int dndActionId = WK_DND_ACTION_NONE; 1011 for (TransferMode tm : tms) { 1012 if (tm == TransferMode.COPY) 1013 dndActionId |= WK_DND_ACTION_COPY; 1014 else if (tm == TransferMode.MOVE) 1015 dndActionId |= WK_DND_ACTION_MOVE; 1016 else if (tm == TransferMode.LINK) 1017 dndActionId |= WK_DND_ACTION_LINK; 1018 } 1019 return dndActionId; 1020 } 1021 1022 private static TransferMode[] getFXDndAction(int wkDndAction) { 1023 LinkedList<TransferMode> tms = new LinkedList<TransferMode>(); 1024 if ((wkDndAction & WK_DND_ACTION_COPY) != 0) 1025 tms.add(TransferMode.COPY); 1026 if ((wkDndAction & WK_DND_ACTION_MOVE) != 0) 1027 tms.add(TransferMode.MOVE); 1028 if ((wkDndAction & WK_DND_ACTION_LINK) != 0) 1029 tms.add(TransferMode.LINK); 1030 return tms.toArray(new TransferMode[0]); 1031 } 1032 1033 /** 1034 * @treatAsPrivate implementation detail 1035 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 1036 */ 1037 // @Deprecated 1038 // @Override 1039 // protected void impl_pickNodeLocal(PickRay pickRay, PickResultChooser result) { 1040 // impl_intersects(pickRay, result); 1041 // } 1042 1043 @Override protected ObservableList<Node> getChildren() { 1044 return super.getChildren(); 1045 } 1046 1047 // Node stuff 1048 1049 /* 1050 * Note: This method MUST only be called via its accessor method. 1051 */ 1052 private NGNode doCreatePeer() { 1053 return new NGWebView(); 1054 } 1055 1056 private NGWebView getNGWebView() { 1057 return (NGWebView)NodeHelper.getPeer(this); 1058 } 1059 1060 /** 1061 * @treatAsPrivate implementation detail 1062 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 1063 */ 1064 @Deprecated 1065 @Override 1066 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 1067 bounds.deriveWithNewBounds(0, 0, 0, (float) getWidth(), (float)getHeight(), 0); 1068 tx.transform(bounds, bounds); 1069 return bounds; 1070 } 1071 1072 /** 1073 * @treatAsPrivate implementation detail 1074 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 1075 */ 1076 @Deprecated 1077 @Override protected boolean impl_computeContains(double localX, double localY) { 1078 // Note: Local bounds contain test is already done by the caller. (Node.contains()). 1079 return true; 1080 } 1081 1082 /* 1083 * Note: This method MUST only be called via its accessor method. 1084 */ 1085 private void doUpdatePeer() { 1086 final NGWebView peer = NodeHelper.getPeer(this); 1087 1088 if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) { 1089 peer.setPage(page); 1090 } 1091 if (NodeHelper.isDirty(this, DirtyBits.NODE_GEOMETRY)) { 1092 peer.resize((float)getWidth(), (float)getHeight()); 1093 } 1094 if (NodeHelper.isDirty(this, DirtyBits.WEBVIEW_VIEW)) { 1095 peer.requestRender(); 1096 } 1097 } 1098 1099 }