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