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