1 /*
   2  * Copyright (c) 2011, 2016, 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 import com.sun.java.scene.web.WebViewHelper;
  29 import javafx.beans.property.BooleanProperty;
  30 import javafx.beans.property.DoubleProperty;
  31 import javafx.beans.property.ObjectProperty;
  32 import javafx.beans.property.ReadOnlyDoubleProperty;
  33 import javafx.beans.property.ReadOnlyDoubleWrapper;
  34 import javafx.collections.ObservableList;
  35 import javafx.css.CssMetaData;
  36 import javafx.css.Styleable;
  37 import javafx.css.StyleableBooleanProperty;
  38 import javafx.css.StyleableDoubleProperty;
  39 import javafx.css.StyleableObjectProperty;
  40 import javafx.css.StyleableProperty;
  41 import javafx.event.EventHandler;
  42 import javafx.event.EventType;
  43 import javafx.geometry.NodeOrientation;
  44 import javafx.geometry.Point2D;
  45 import javafx.scene.Node;
  46 import javafx.scene.Parent;
  47 import javafx.scene.Scene;
  48 import javafx.scene.input.DataFormat;
  49 import javafx.scene.input.DragEvent;
  50 import javafx.scene.input.Dragboard;
  51 import javafx.scene.input.InputMethodEvent;
  52 import javafx.scene.input.KeyEvent;
  53 import javafx.scene.input.MouseButton;
  54 import javafx.scene.input.MouseEvent;
  55 import javafx.scene.input.ScrollEvent;
  56 import javafx.scene.input.TransferMode;
  57 import javafx.scene.text.FontSmoothingType;
  58 import javafx.stage.Stage;
  59 import javafx.stage.Window;
  60 import java.util.ArrayList;
  61 import java.util.Collections;
  62 import java.util.HashMap;
  63 import java.util.LinkedList;
  64 import java.util.List;
  65 import java.util.Map;
  66 import javafx.css.converter.BooleanConverter;
  67 import javafx.css.converter.EnumConverter;
  68 import javafx.css.converter.SizeConverter;
  69 import com.sun.javafx.geom.BaseBounds;
  70 import com.sun.javafx.geom.PickRay;
  71 import com.sun.javafx.geom.transform.BaseTransform;
  72 import com.sun.javafx.scene.DirtyBits;
  73 import com.sun.javafx.scene.NodeHelper;
  74 import com.sun.javafx.scene.SceneHelper;
  75 import com.sun.javafx.scene.input.PickResultChooser;
  76 import com.sun.javafx.sg.prism.NGNode;
  77 import com.sun.javafx.sg.prism.web.NGWebView;
  78 import com.sun.javafx.tk.TKPulseListener;
  79 import com.sun.javafx.tk.Toolkit;
  80 import com.sun.javafx.webkit.InputMethodClientImpl;
  81 import com.sun.javafx.webkit.KeyCodeMap;
  82 import com.sun.webkit.WebPage;
  83 import com.sun.webkit.event.WCFocusEvent;
  84 import com.sun.webkit.event.WCInputMethodEvent;
  85 import com.sun.webkit.event.WCKeyEvent;
  86 import com.sun.webkit.event.WCMouseEvent;
  87 import com.sun.webkit.event.WCMouseWheelEvent;
  88 
  89 /**
  90  * {@code WebView} is a {@link javafx.scene.Node} that manages a
  91  * {@link WebEngine} and displays its content. The associated {@code WebEngine}
  92  * is created automatically at construction time and cannot be changed
  93  * afterwards. {@code WebView} handles mouse and some keyboard events, and
  94  * manages scrolling automatically, so there's no need to put it into a
  95  * {@code ScrollPane}.
  96  *
  97  * <p>{@code WebView} objects must be created and accessed solely from the
  98  * FX thread.
  99  * @since JavaFX 2.0
 100  */
 101 final public class WebView extends Parent {
 102 
 103     private static final Map<Object, Integer> idMap = new HashMap<Object, Integer>();
 104 
 105     private static final boolean DEFAULT_CONTEXT_MENU_ENABLED = true;
 106     private static final FontSmoothingType DEFAULT_FONT_SMOOTHING_TYPE = FontSmoothingType.LCD;
 107     private static final double DEFAULT_ZOOM = 1.0;
 108     private static final double DEFAULT_FONT_SCALE = 1.0;
 109     private static final double DEFAULT_MIN_WIDTH = 0;
 110     private static final double DEFAULT_MIN_HEIGHT = 0;
 111     private static final double DEFAULT_PREF_WIDTH = 800;
 112     private static final double DEFAULT_PREF_HEIGHT = 600;
 113     private static final double DEFAULT_MAX_WIDTH = Double.MAX_VALUE;
 114     private static final double DEFAULT_MAX_HEIGHT = Double.MAX_VALUE;
 115 
 116     private final WebPage page;
 117     private final WebEngine engine;
 118     private volatile InputMethodClientImpl imClient;
 119 
 120     /**
 121      * The stage pulse listener registered with the toolkit.
 122      * This field guarantees that the listener will exist throughout
 123      * the whole lifetime of the WebView node. This field is necessary
 124      * because the toolkit references its stage pulse listeners weakly.
 125      */
 126     private final TKPulseListener stagePulseListener;
 127 
 128     /**
 129      * Returns the {@code WebEngine} object.
 130      */
 131     public final WebEngine getEngine() {
 132         return engine;
 133     }
 134 
 135     private final ReadOnlyDoubleWrapper width = new ReadOnlyDoubleWrapper(this, "width");
 136 
 137     /**
 138      * Returns width of this {@code WebView}.
 139      */
 140     public final double getWidth() {
 141         return width.get();
 142     }
 143 
 144     /**
 145      * Width of this {@code WebView}.
 146      */
 147     public ReadOnlyDoubleProperty widthProperty() {
 148         return width.getReadOnlyProperty();
 149     }
 150 
 151     private final ReadOnlyDoubleWrapper height = new ReadOnlyDoubleWrapper(this, "height");
 152 
 153     /**
 154      * Returns height of this {@code WebView}.
 155      */
 156     public final double getHeight() {
 157         return height.get();
 158     }
 159 
 160     /**
 161      * Height of this {@code WebView}.
 162      */
 163     public ReadOnlyDoubleProperty heightProperty() {
 164         return height.getReadOnlyProperty();
 165     }
 166 
 167     /**
 168      * Zoom factor applied to the whole page contents.
 169      *
 170      * @defaultValue 1.0
 171      */
 172     private DoubleProperty zoom;
 173 
 174     /**
 175      * Sets current zoom factor applied to the whole page contents.
 176      * @param value zoom factor to be set
 177      * @see #zoomProperty()
 178      * @see #getZoom()
 179      * @since JavaFX 8.0
 180      */
 181     public final void setZoom(double value) {
 182         WebEngine.checkThread();
 183         zoomProperty().set(value);
 184     }
 185 
 186     /**
 187      * Returns current zoom factor applied to the whole page contents.
 188      * @return current zoom factor
 189      * @see #zoomProperty()
 190      * @see #setZoom(double value)
 191      * @since JavaFX 8.0
 192      */
 193     public final double getZoom() {
 194         return (this.zoom != null)
 195                 ? this.zoom.get()
 196                 : DEFAULT_ZOOM;
 197     }
 198 
 199     /**
 200      * Returns zoom property object.
 201      * @return zoom property object
 202      * @see #getZoom()
 203      * @see #setZoom(double value)
 204      * @since JavaFX 8.0
 205      */
 206     public final DoubleProperty zoomProperty() {
 207         if (zoom == null) {
 208             zoom = new StyleableDoubleProperty(DEFAULT_ZOOM) {
 209                 @Override public void invalidated() {
 210                     Toolkit.getToolkit().checkFxUserThread();
 211                     page.setZoomFactor((float) get(), false);
 212                 }
 213 
 214                 @Override public CssMetaData<WebView, Number> getCssMetaData() {
 215                     return StyleableProperties.ZOOM;
 216                 }
 217                 @Override public Object getBean() {
 218                     return WebView.this;
 219                 }
 220                 @Override public String getName() {
 221                     return "zoom";
 222                 }
 223             };
 224         }
 225         return zoom;
 226     }
 227 
 228     /**
 229      * Specifies scale factor applied to font. This setting affects
 230      * text content but not images and fixed size elements.
 231      *
 232      * @defaultValue 1.0
 233      */
 234     private DoubleProperty fontScale;
 235 
 236     public final void setFontScale(double value) {
 237         WebEngine.checkThread();
 238         fontScaleProperty().set(value);
 239     }
 240 
 241     public final double getFontScale() {
 242         return (this.fontScale != null)
 243                 ? this.fontScale.get()
 244                 : DEFAULT_FONT_SCALE;
 245     }
 246 
 247     public DoubleProperty fontScaleProperty() {
 248         if (fontScale == null) {
 249             fontScale = new StyleableDoubleProperty(DEFAULT_FONT_SCALE) {
 250                 @Override public void invalidated() {
 251                     Toolkit.getToolkit().checkFxUserThread();
 252                     page.setZoomFactor((float)get(), true);
 253                 }
 254                 @Override public CssMetaData<WebView, Number> getCssMetaData() {
 255                     return StyleableProperties.FONT_SCALE;
 256                 }
 257                 @Override public Object getBean() {
 258                     return WebView.this;
 259                 }
 260                 @Override public String getName() {
 261                     return "fontScale";
 262                 }
 263             };
 264         }
 265         return fontScale;
 266     }
 267 
 268     {
 269         // To initialize the class helper at the begining each constructor of this class
 270         WebViewHelper.initHelper(this);
 271     }
 272     /**
 273      * Creates a {@code WebView} object.
 274      */
 275     public WebView() {
 276         setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT);
 277         getStyleClass().add("web-view");
 278         engine = new WebEngine();
 279         engine.setView(this);
 280         page = engine.getPage();
 281         page.setFontSmoothingType(DEFAULT_FONT_SMOOTHING_TYPE.ordinal());
 282 
 283         registerEventHandlers();
 284         stagePulseListener = () -> {
 285             handleStagePulse();
 286         };
 287         focusedProperty().addListener((ov, t, t1) -> {
 288             if (page != null) {
 289                 // Traversal direction is not currently available in FX.
 290                 WCFocusEvent focusEvent = new WCFocusEvent(
 291                     isFocused() ? WCFocusEvent.FOCUS_GAINED
 292                             : WCFocusEvent.FOCUS_LOST,
 293                 WCFocusEvent.UNKNOWN);
 294                 page.dispatchFocusEvent(focusEvent);
 295             }
 296         });
 297         setFocusTraversable(true);
 298         Toolkit.getToolkit().addStageTkPulseListener(stagePulseListener);
 299     }
 300 
 301     // Resizing support. Allows arbitrary growing and shrinking.
 302     // Designed after javafx.scene.control.Control
 303 
 304     @Override public boolean isResizable() {
 305         return true;
 306     }
 307 
 308     @Override public void resize(double width, double height) {
 309         if ((width != this.width.get()) || (height != this.height.get())) {
 310             this.width.set(width);
 311             this.height.set(height);
 312             NodeHelper.markDirty(this, DirtyBits.NODE_GEOMETRY);
 313             NodeHelper.geomChanged(this);
 314         }
 315     }
 316 
 317     /**
 318      * Called during layout to determine the minimum width for this node.
 319      *
 320      * @return the minimum width that this node should be resized to during layout
 321      */
 322     @Override public final double minWidth(double height) {
 323         final double result = getMinWidth();
 324         return Double.isNaN(result) || result < 0 ? 0 : result;
 325     }
 326 
 327     /**
 328      * Called during layout to determine the minimum height for this node.
 329      *
 330      * @return the minimum height that this node should be resized to during layout
 331      */
 332     @Override public final double minHeight(double width) {
 333         final double result = getMinHeight();
 334         return Double.isNaN(result) || result < 0 ? 0 : result;
 335     }
 336 
 337 
 338     /**
 339      * Called during layout to determine the preferred width for this node.
 340      *
 341      * @return the preferred width that this node should be resized to during layout
 342      */
 343     @Override public final double prefWidth(double height) {
 344         final double result = getPrefWidth();
 345         return Double.isNaN(result) || result < 0 ? 0 : result;
 346     }
 347 
 348     /**
 349      * Called during layout to determine the preferred height for this node.
 350      *
 351      * @return the preferred height that this node should be resized to during layout
 352      */
 353     @Override public final double prefHeight(double width) {
 354         final double result = getPrefHeight();
 355         return Double.isNaN(result) || result < 0 ? 0 : result;
 356     }
 357     /**
 358      * Called during layout to determine the maximum width for this node.
 359      *
 360      * @return the maximum width that this node should be resized to during layout
 361      */
 362     @Override public final double maxWidth(double height) {
 363         final double result = getMaxWidth();
 364         return Double.isNaN(result) || result < 0 ? 0 : result;
 365     }
 366 
 367     /**
 368      * Called during layout to determine the maximum height for this node.
 369      *
 370      * @return the maximum height that this node should be resized to during layout
 371      */
 372     @Override public final double maxHeight(double width) {
 373         final double result = getMaxHeight();
 374         return Double.isNaN(result) || result < 0 ? 0 : result;
 375     }
 376 
 377     /**
 378      * Minimum width property.
 379      */
 380     public DoubleProperty minWidthProperty() {
 381         if (minWidth == null) {
 382             minWidth = new StyleableDoubleProperty(DEFAULT_MIN_WIDTH) {
 383                 @Override
 384                 public void invalidated() {
 385                     if (getParent() != null) {
 386                         getParent().requestLayout();
 387                     }
 388                 }
 389                 @Override
 390                 public CssMetaData<WebView, Number> getCssMetaData() {
 391                     return StyleableProperties.MIN_WIDTH;
 392                 }
 393                 @Override
 394                 public Object getBean() {
 395                     return WebView.this;
 396                 }
 397                 @Override
 398                 public String getName() {
 399                     return "minWidth";
 400                 }
 401             };
 402         }
 403         return minWidth;
 404     }
 405     private DoubleProperty minWidth;
 406 
 407     /**
 408      * Sets minimum width.
 409      */
 410     public final void setMinWidth(double value) {
 411         minWidthProperty().set(value);
 412     }
 413 
 414     /**
 415      * Returns minimum width.
 416      */
 417     public final double getMinWidth() {
 418         return (this.minWidth != null)
 419                 ? this.minWidth.get()
 420                 : DEFAULT_MIN_WIDTH;
 421     }
 422 
 423     /**
 424      * Minimum height property.
 425      */
 426     public DoubleProperty minHeightProperty() {
 427         if (minHeight == null) {
 428             minHeight = new StyleableDoubleProperty(DEFAULT_MIN_HEIGHT) {
 429                 @Override
 430                 public void invalidated() {
 431                     if (getParent() != null) {
 432                         getParent().requestLayout();
 433                     }
 434                 }
 435                 @Override
 436                 public CssMetaData<WebView, Number> getCssMetaData() {
 437                     return StyleableProperties.MIN_HEIGHT;
 438                 }
 439                 @Override
 440                 public Object getBean() {
 441                     return WebView.this;
 442                 }
 443                 @Override
 444                 public String getName() {
 445                     return "minHeight";
 446                 }
 447             };
 448         }
 449         return minHeight;
 450     }
 451     private DoubleProperty minHeight;
 452 
 453     /**
 454      * Sets minimum height.
 455      */
 456     public final void setMinHeight(double value) {
 457         minHeightProperty().set(value);
 458     }
 459 
 460     /**
 461      * Sets minimum height.
 462      */
 463     public final double getMinHeight() {
 464         return (this.minHeight != null)
 465                 ? this.minHeight.get()
 466                 : DEFAULT_MIN_HEIGHT;
 467     }
 468 
 469     /**
 470      * Convenience method for setting minimum width and height.
 471      */
 472     public void setMinSize(double minWidth, double minHeight) {
 473         setMinWidth(minWidth);
 474         setMinHeight(minHeight);
 475     }
 476 
 477     /**
 478      * Preferred width property.
 479      */
 480     public DoubleProperty prefWidthProperty() {
 481         if (prefWidth == null) {
 482             prefWidth = new StyleableDoubleProperty(DEFAULT_PREF_WIDTH) {
 483                 @Override
 484                 public void invalidated() {
 485                     if (getParent() != null) {
 486                         getParent().requestLayout();
 487                     }
 488                 }
 489                 @Override
 490                 public CssMetaData<WebView, Number> getCssMetaData() {
 491                     return StyleableProperties.PREF_WIDTH;
 492                 }
 493                 @Override
 494                 public Object getBean() {
 495                     return WebView.this;
 496                 }
 497                 @Override
 498                 public String getName() {
 499                     return "prefWidth";
 500                 }
 501             };
 502         }
 503         return prefWidth;
 504     }
 505     private DoubleProperty prefWidth;
 506 
 507     /**
 508      * Sets preferred width.
 509      */
 510     public final void setPrefWidth(double value) {
 511         prefWidthProperty().set(value);
 512     }
 513 
 514     /**
 515      * Returns preferred width.
 516      */
 517     public final double getPrefWidth() {
 518         return (this.prefWidth != null)
 519                 ? this.prefWidth.get()
 520                 : DEFAULT_PREF_WIDTH;
 521     }
 522 
 523     /**
 524      * Preferred height property.
 525      */
 526     public DoubleProperty prefHeightProperty() {
 527         if (prefHeight == null) {
 528             prefHeight = new StyleableDoubleProperty(DEFAULT_PREF_HEIGHT) {
 529                 @Override
 530                 public void invalidated() {
 531                     if (getParent() != null) {
 532                         getParent().requestLayout();
 533                     }
 534                 }
 535                 @Override
 536                 public CssMetaData<WebView, Number> getCssMetaData() {
 537                     return StyleableProperties.PREF_HEIGHT;
 538                 }
 539                 @Override
 540                 public Object getBean() {
 541                     return WebView.this;
 542                 }
 543                 @Override
 544                 public String getName() {
 545                     return "prefHeight";
 546                 }
 547             };
 548         }
 549         return prefHeight;
 550     }
 551     private DoubleProperty prefHeight;
 552 
 553     /**
 554      * Sets preferred height.
 555      */
 556     public final void setPrefHeight(double value) {
 557         prefHeightProperty().set(value);
 558     }
 559 
 560     /**
 561      * Returns preferred height.
 562      */
 563     public final double getPrefHeight() {
 564         return (this.prefHeight != null)
 565                 ? this.prefHeight.get()
 566                 : DEFAULT_PREF_HEIGHT;
 567     }
 568 
 569     /**
 570      * Convenience method for setting preferred width and height.
 571      */
 572     public void setPrefSize(double prefWidth, double prefHeight) {
 573         setPrefWidth(prefWidth);
 574         setPrefHeight(prefHeight);
 575     }
 576 
 577     /**
 578      * Maximum width property.
 579      */
 580     public DoubleProperty maxWidthProperty() {
 581         if (maxWidth == null) {
 582             maxWidth = new StyleableDoubleProperty(DEFAULT_MAX_WIDTH) {
 583                 @Override
 584                 public void invalidated() {
 585                     if (getParent() != null) {
 586                         getParent().requestLayout();
 587                     }
 588                 }
 589                 @Override
 590                 public CssMetaData<WebView, Number> getCssMetaData() {
 591                     return StyleableProperties.MAX_WIDTH;
 592                 }
 593                 @Override
 594                 public Object getBean() {
 595                     return WebView.this;
 596                 }
 597                 @Override
 598                 public String getName() {
 599                     return "maxWidth";
 600                 }
 601             };
 602         }
 603         return maxWidth;
 604     }
 605     private DoubleProperty maxWidth;
 606 
 607     /**
 608      * Sets maximum width.
 609      */
 610     public final void setMaxWidth(double value) {
 611         maxWidthProperty().set(value);
 612     }
 613 
 614     /**
 615      * Returns maximum width.
 616      */
 617     public final double getMaxWidth() {
 618         return (this.maxWidth != null)
 619                 ? this.maxWidth.get()
 620                 : DEFAULT_MAX_WIDTH;
 621     }
 622 
 623     /**
 624      * Maximum height property.
 625      */
 626     public DoubleProperty maxHeightProperty() {
 627         if (maxHeight == null) {
 628             maxHeight = new StyleableDoubleProperty(DEFAULT_MAX_HEIGHT) {
 629                 @Override
 630                 public void invalidated() {
 631                     if (getParent() != null) {
 632                         getParent().requestLayout();
 633                     }
 634                 }
 635                 @Override
 636                 public CssMetaData<WebView, Number> getCssMetaData() {
 637                     return StyleableProperties.MAX_HEIGHT;
 638                 }
 639                 @Override
 640                 public Object getBean() {
 641                     return WebView.this;
 642                 }
 643                 @Override
 644                 public String getName() {
 645                     return "maxHeight";
 646                 }
 647             };
 648         }
 649         return maxHeight;
 650     }
 651     private DoubleProperty maxHeight;
 652 
 653     /**
 654      * Sets maximum height.
 655      */
 656     public final void setMaxHeight(double value) {
 657         maxHeightProperty().set(value);
 658     }
 659 
 660     /**
 661      * Returns maximum height.
 662      */
 663     public final double getMaxHeight() {
 664         return (this.maxHeight != null)
 665                 ? this.maxHeight.get()
 666                 : DEFAULT_MAX_HEIGHT;
 667     }
 668 
 669     /**
 670      * Convenience method for setting maximum width and height.
 671      */
 672     public void setMaxSize(double maxWidth, double maxHeight) {
 673         setMaxWidth(maxWidth);
 674         setMaxHeight(maxHeight);
 675     }
 676 
 677 
 678     /**
 679      * Specifies a requested font smoothing type : gray or LCD.
 680      *
 681      * The width of the bounding box is defined by the widest row.
 682      *
 683      * Note: LCD mode doesn't apply in numerous cases, such as various
 684      * compositing modes, where effects are applied and very large glyphs.
 685      *
 686      * @defaultValue FontSmoothingType.LCD
 687      * @since JavaFX 2.2
 688      */
 689     private ObjectProperty<FontSmoothingType> fontSmoothingType;
 690 
 691     public final void setFontSmoothingType(FontSmoothingType value) {
 692         fontSmoothingTypeProperty().set(value);
 693     }
 694 
 695     public final FontSmoothingType getFontSmoothingType() {
 696         return (this.fontSmoothingType != null)
 697                 ? this.fontSmoothingType.get()
 698                 : DEFAULT_FONT_SMOOTHING_TYPE;
 699     }
 700 
 701     public final ObjectProperty<FontSmoothingType> fontSmoothingTypeProperty() {
 702         if (this.fontSmoothingType == null) {
 703             this.fontSmoothingType = new StyleableObjectProperty<FontSmoothingType>(DEFAULT_FONT_SMOOTHING_TYPE) {
 704                 @Override
 705                 public void invalidated() {
 706                     Toolkit.getToolkit().checkFxUserThread();
 707                     page.setFontSmoothingType(get().ordinal());
 708                 }
 709                 @Override
 710                 public CssMetaData<WebView, FontSmoothingType> getCssMetaData() {
 711                     return StyleableProperties.FONT_SMOOTHING_TYPE;
 712                 }
 713                 @Override
 714                 public Object getBean() {
 715                     return WebView.this;
 716                 }
 717                 @Override
 718                 public String getName() {
 719                     return "fontSmoothingType";
 720                 }
 721             };
 722         }
 723         return this.fontSmoothingType;
 724     }
 725 
 726     /**
 727      * Specifies whether context menu is enabled.
 728      *
 729      * @defaultValue true
 730      * @since JavaFX 2.2
 731      */
 732     private BooleanProperty contextMenuEnabled;
 733 
 734     public final void setContextMenuEnabled(boolean value) {
 735         contextMenuEnabledProperty().set(value);
 736     }
 737 
 738     public final boolean isContextMenuEnabled() {
 739         return contextMenuEnabled == null
 740                 ? DEFAULT_CONTEXT_MENU_ENABLED
 741                 : contextMenuEnabled.get();
 742     }
 743 
 744     public final BooleanProperty contextMenuEnabledProperty() {
 745         if (contextMenuEnabled == null) {
 746             contextMenuEnabled = new StyleableBooleanProperty(DEFAULT_CONTEXT_MENU_ENABLED) {
 747                 @Override public void invalidated() {
 748                     Toolkit.getToolkit().checkFxUserThread();
 749                     page.setContextMenuEnabled(get());
 750                 }
 751 
 752                 @Override public CssMetaData<WebView, Boolean> getCssMetaData() {
 753                     return StyleableProperties.CONTEXT_MENU_ENABLED;
 754                 }
 755 
 756                 @Override public Object getBean() {
 757                     return WebView.this;
 758                 }
 759 
 760                 @Override public String getName() {
 761                     return "contextMenuEnabled";
 762                 }
 763             };
 764         }
 765         return contextMenuEnabled;
 766     }
 767 
 768     /**
 769      * Super-lazy instantiation pattern from Bill Pugh.
 770      */
 771     private static final class StyleableProperties {
 772 
 773         private static final CssMetaData<WebView, Boolean> CONTEXT_MENU_ENABLED
 774                 = new CssMetaData<WebView, Boolean>(
 775                 "-fx-context-menu-enabled",
 776                 BooleanConverter.getInstance(),
 777                 DEFAULT_CONTEXT_MENU_ENABLED)
 778         {
 779             @Override public boolean isSettable(WebView view) {
 780                 return view.contextMenuEnabled == null || !view.contextMenuEnabled.isBound();
 781             }
 782             @Override public StyleableProperty<Boolean> getStyleableProperty(WebView view) {
 783                 return (StyleableProperty<Boolean>)view.contextMenuEnabledProperty();
 784             }
 785         };
 786 
 787         private static final CssMetaData<WebView, FontSmoothingType> FONT_SMOOTHING_TYPE
 788                 = new CssMetaData<WebView, FontSmoothingType>(
 789                 "-fx-font-smoothing-type",
 790                 new EnumConverter<FontSmoothingType>(FontSmoothingType.class),
 791                 DEFAULT_FONT_SMOOTHING_TYPE) {
 792             @Override
 793             public boolean isSettable(WebView view) {
 794                 return view.fontSmoothingType == null || !view.fontSmoothingType.isBound();
 795             }
 796             @Override
 797             public StyleableProperty<FontSmoothingType> getStyleableProperty(WebView view) {
 798                 return (StyleableProperty<FontSmoothingType>)view.fontSmoothingTypeProperty();
 799             }
 800         };
 801 
 802         private static final CssMetaData<WebView, Number> ZOOM
 803                 = new CssMetaData<WebView, Number>(
 804                 "-fx-zoom",
 805                 SizeConverter.getInstance(),
 806                 DEFAULT_ZOOM) {
 807             @Override public boolean isSettable(WebView view) {
 808                 return view.zoom == null || !view.zoom.isBound();
 809             }
 810             @Override public StyleableProperty<Number> getStyleableProperty(WebView view) {
 811                 return (StyleableProperty<Number>)view.zoomProperty();
 812             }
 813         };
 814 
 815         private static final CssMetaData<WebView, Number> FONT_SCALE
 816                 = new CssMetaData<WebView, Number>(
 817                 "-fx-font-scale",
 818                 SizeConverter.getInstance(),
 819                 DEFAULT_FONT_SCALE) {
 820             @Override
 821             public boolean isSettable(WebView view) {
 822                 return view.fontScale == null || !view.fontScale.isBound();
 823             }
 824             @Override
 825             public StyleableProperty<Number> getStyleableProperty(WebView view) {
 826                 return (StyleableProperty<Number>)view.fontScaleProperty();
 827             }
 828         };
 829 
 830         private static final CssMetaData<WebView, Number> MIN_WIDTH
 831                 = new CssMetaData<WebView, Number>(
 832                 "-fx-min-width",
 833                 SizeConverter.getInstance(),
 834                 DEFAULT_MIN_WIDTH) {
 835             @Override
 836             public boolean isSettable(WebView view) {
 837                 return view.minWidth == null || !view.minWidth.isBound();
 838             }
 839             @Override
 840             public StyleableProperty<Number> getStyleableProperty(WebView view) {
 841                 return (StyleableProperty<Number>)view.minWidthProperty();
 842             }
 843         };
 844 
 845         private static final CssMetaData<WebView, Number> MIN_HEIGHT
 846                 = new CssMetaData<WebView, Number>(
 847                 "-fx-min-height",
 848                 SizeConverter.getInstance(),
 849                 DEFAULT_MIN_HEIGHT) {
 850             @Override
 851             public boolean isSettable(WebView view) {
 852                 return view.minHeight == null || !view.minHeight.isBound();
 853             }
 854             @Override
 855             public StyleableProperty<Number> getStyleableProperty(WebView view) {
 856                 return (StyleableProperty<Number>)view.minHeightProperty();
 857             }
 858         };
 859 
 860         private static final CssMetaData<WebView, Number> MAX_WIDTH
 861                 = new CssMetaData<WebView, Number>(
 862                 "-fx-max-width",
 863                 SizeConverter.getInstance(),
 864                 DEFAULT_MAX_WIDTH) {
 865             @Override
 866             public boolean isSettable(WebView view) {
 867                 return view.maxWidth == null || !view.maxWidth.isBound();
 868             }
 869             @Override
 870             public StyleableProperty<Number> getStyleableProperty(WebView view) {
 871                 return (StyleableProperty<Number>)view.maxWidthProperty();
 872             }
 873         };
 874 
 875         private static final CssMetaData<WebView, Number> MAX_HEIGHT
 876                 = new CssMetaData<WebView, Number>(
 877                 "-fx-max-height",
 878                 SizeConverter.getInstance(),
 879                 DEFAULT_MAX_HEIGHT) {
 880             @Override
 881             public boolean isSettable(WebView view) {
 882                 return view.maxHeight == null || !view.maxHeight.isBound();
 883             }
 884             @Override
 885             public StyleableProperty<Number> getStyleableProperty(WebView view) {
 886                 return (StyleableProperty<Number>)view.maxHeightProperty();
 887             }
 888         };
 889 
 890         private static final CssMetaData<WebView, Number> PREF_WIDTH
 891                 = new CssMetaData<WebView, Number>(
 892                 "-fx-pref-width",
 893                 SizeConverter.getInstance(),
 894                 DEFAULT_PREF_WIDTH) {
 895             @Override
 896             public boolean isSettable(WebView view) {
 897                 return view.prefWidth == null || !view.prefWidth.isBound();
 898             }
 899             @Override
 900             public StyleableProperty<Number> getStyleableProperty(WebView view) {
 901                 return (StyleableProperty<Number>)view.prefWidthProperty();
 902             }
 903         };
 904 
 905         private static final CssMetaData<WebView, Number> PREF_HEIGHT
 906                 = new CssMetaData<WebView, Number>(
 907                 "-fx-pref-height",
 908                 SizeConverter.getInstance(),
 909                 DEFAULT_PREF_HEIGHT) {
 910             @Override
 911             public boolean isSettable(WebView view) {
 912                 return view.prefHeight == null || !view.prefHeight.isBound();
 913             }
 914             @Override
 915             public StyleableProperty<Number> getStyleableProperty(WebView view) {
 916                 return (StyleableProperty<Number>)view.prefHeightProperty();
 917             }
 918         };
 919 
 920         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 921 
 922         static {
 923             List<CssMetaData<? extends Styleable, ?>> styleables
 924                     = new ArrayList<CssMetaData<? extends Styleable, ?>>(Parent.getClassCssMetaData());
 925             styleables.add(CONTEXT_MENU_ENABLED);
 926             styleables.add(FONT_SMOOTHING_TYPE);
 927             styleables.add(ZOOM);
 928             styleables.add(FONT_SCALE);
 929             styleables.add(MIN_WIDTH);
 930             styleables.add(PREF_WIDTH);
 931             styleables.add(MAX_WIDTH);
 932             styleables.add(MIN_HEIGHT);
 933             styleables.add(PREF_HEIGHT);
 934             styleables.add(MAX_HEIGHT);
 935             STYLEABLES = Collections.unmodifiableList(styleables);
 936          }
 937     }
 938 
 939     /**
 940      * @return The CssMetaData associated with this class, which may include the
 941      * CssMetaData of its super classes.
 942      * @since JavaFX 8.0
 943      */
 944     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 945         return StyleableProperties.STYLEABLES;
 946     }
 947 
 948     /**
 949      * {@inheritDoc}
 950      * @since JavaFX 8.0
 951      */
 952     @Override
 953     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 954         return getClassCssMetaData();
 955     }
 956 
 957     // event handling
 958 
 959     // To handle stage pulse we need to know if currently webview and
 960     // tree is visible or not
 961     private boolean isTreeReallyVisible() {
 962         if (getScene() == null) {
 963             return false;
 964         }
 965 
 966         final Window window = getScene().getWindow();
 967 
 968         if (window == null) {
 969             return false;
 970         }
 971 
 972         boolean iconified = (window instanceof Stage) ? ((Stage)window).isIconified() : false;
 973 
 974         return NodeHelper.isTreeShowing(this)
 975                && window.getWidth() > 0
 976                && window.getHeight() > 0
 977                && !iconified;
 978     }
 979 
 980     private void handleStagePulse() {
 981         // The stage pulse occurs before the scene pulse.
 982         // Here the page content is updated before CSS/Layout/Sync pass
 983         // is initiated by the scene pulse. The update may
 984         // change the WebView children and, if so, the children should be
 985         // processed right away during the scene pulse.
 986 
 987         // The WebView node does not render its pending render queues
 988         // while it is invisible. Therefore, we should not schedule new
 989         // render queues while the WebView is invisible to prevent
 990         // the list of render queues from growing infinitely.
 991         // Also, if and when the WebView becomes invisible, the currently
 992         // pending render queues, if any, become obsolete and should be
 993         // discarded.
 994 
 995         if (page == null) return;
 996 
 997         boolean reallyVisible = isTreeReallyVisible();
 998 
 999         if (reallyVisible) {
1000             if (page.isDirty()) {
1001                 SceneHelper.setAllowPGAccess(true);
1002                 final NGWebView peer = NodeHelper.getPeer(this);
1003                 peer.update(); // creates new render queues
1004                 if (page.isRepaintPending()) {
1005                     NodeHelper.markDirty(this, DirtyBits.WEBVIEW_VIEW);
1006                 }
1007                 SceneHelper.setAllowPGAccess(false);
1008             }
1009         } else {
1010             page.dropRenderFrames();
1011         }
1012     }
1013 
1014     private void processMouseEvent(MouseEvent ev) {
1015         if (page == null) {
1016             return;
1017         }
1018 
1019         // RT-24511
1020         EventType<? extends MouseEvent> type = ev.getEventType();
1021         double x = ev.getX();
1022         double y = ev.getY();
1023         double screenX = ev.getScreenX();
1024         double screenY = ev.getScreenY();
1025         if (type == MouseEvent.MOUSE_EXITED) {
1026             type = MouseEvent.MOUSE_MOVED;
1027             x = Short.MIN_VALUE;
1028             y = Short.MIN_VALUE;
1029             Point2D screenPoint = localToScreen(x, y);
1030             if (screenPoint == null) {
1031                 return;
1032             }
1033             screenX = screenPoint.getX();
1034             screenY = screenPoint.getY();
1035         }
1036 
1037         final Integer id = idMap.get(type);
1038         if (id == null) {
1039             // not supported by webkit
1040             return;
1041         }
1042         WCMouseEvent mouseEvent =
1043                 new WCMouseEvent(id, idMap.get(ev.getButton()),
1044                     ev.getClickCount(), (int) x, (int) y,
1045                     (int) screenX, (int) screenY,
1046                     System.currentTimeMillis(),
1047                     ev.isShiftDown(), ev.isControlDown(), ev.isAltDown(),
1048                     ev.isMetaDown(), ev.isPopupTrigger());
1049         page.dispatchMouseEvent(mouseEvent);
1050         ev.consume();
1051     }
1052 
1053     private void processScrollEvent(ScrollEvent ev) {
1054         if (page == null) {
1055             return;
1056         }
1057         double dx = - ev.getDeltaX() * getFontScale() * getScaleX();
1058         double dy = - ev.getDeltaY() * getFontScale() * getScaleY();
1059         WCMouseWheelEvent wheelEvent =
1060                 new WCMouseWheelEvent((int)ev.getX(), (int)ev.getY(),
1061                     (int)ev.getScreenX(), (int)ev.getScreenY(),
1062                     System.currentTimeMillis(),
1063                     ev.isShiftDown(), ev.isControlDown(), ev.isAltDown(),
1064                     ev.isMetaDown(), (float)dx, (float)dy);
1065         page.dispatchMouseWheelEvent(wheelEvent);
1066         ev.consume();
1067     }
1068 
1069     private void processKeyEvent(KeyEvent ev) {
1070         if (page == null) return;
1071 
1072         String text = null;
1073         String keyIdentifier = null;
1074         int windowsVirtualKeyCode = 0;
1075         if(ev.getEventType() == KeyEvent.KEY_TYPED) {
1076             text = ev.getCharacter();
1077         } else {
1078             KeyCodeMap.Entry keyCodeEntry = KeyCodeMap.lookup(ev.getCode());
1079             keyIdentifier = keyCodeEntry.getKeyIdentifier();
1080             windowsVirtualKeyCode = keyCodeEntry.getWindowsVirtualKeyCode();
1081         }
1082 
1083         WCKeyEvent keyEvent = new WCKeyEvent(
1084                 idMap.get(ev.getEventType()),
1085                 text,
1086                 keyIdentifier,
1087                 windowsVirtualKeyCode,
1088                 ev.isShiftDown(), ev.isControlDown(),
1089                 ev.isAltDown(), ev.isMetaDown(), System.currentTimeMillis());
1090         if (page.dispatchKeyEvent(keyEvent)) {
1091             ev.consume();
1092         }
1093     }
1094 
1095     private InputMethodClientImpl getInputMethodClient() {
1096          if (imClient == null) {
1097              synchronized (this) {
1098                  if (imClient == null) {
1099                      imClient = new InputMethodClientImpl(this, page);
1100                  }
1101              }
1102          }
1103          return imClient;
1104     }
1105 
1106     private void processInputMethodEvent(InputMethodEvent ie) {
1107         if (page == null) {
1108             return;
1109         }
1110 
1111         if (!getInputMethodClient().getInputMethodState()) {
1112             ie.consume();
1113             return;
1114         }
1115 
1116         WCInputMethodEvent imEvent = InputMethodClientImpl.convertToWCInputMethodEvent(ie);
1117         if (page.dispatchInputMethodEvent(imEvent)) {
1118             ie.consume();
1119             return;
1120         }
1121     }
1122 
1123     private static final int WK_DND_ACTION_NONE = 0x0;
1124     private static final int WK_DND_ACTION_COPY = 0x1;
1125     private static final int WK_DND_ACTION_MOVE = 0x2;
1126     private static final int WK_DND_ACTION_LINK = 0x40000000;
1127 
1128     private static int getWKDndEventType(EventType  et) {
1129         int commandId = 0;
1130         if (et == DragEvent.DRAG_ENTERED)
1131             commandId = WebPage.DND_DST_ENTER;
1132         else if (et == DragEvent.DRAG_EXITED)
1133             commandId = WebPage.DND_DST_EXIT;
1134         else if (et == DragEvent.DRAG_OVER)
1135             commandId = WebPage.DND_DST_OVER;
1136         else if (et == DragEvent.DRAG_DROPPED)
1137             commandId = WebPage.DND_DST_DROP;
1138         return commandId;
1139     }
1140 
1141     private static int getWKDndAction(TransferMode... tms) {
1142         int dndActionId = WK_DND_ACTION_NONE;
1143         for (TransferMode tm : tms) {
1144            if (tm == TransferMode.COPY)
1145                dndActionId |=  WK_DND_ACTION_COPY;
1146            else if (tm == TransferMode.MOVE)
1147                dndActionId |=  WK_DND_ACTION_MOVE;
1148            else if (tm == TransferMode.LINK)
1149                dndActionId |=  WK_DND_ACTION_LINK;
1150         }
1151         return dndActionId;
1152     }
1153 
1154     private static TransferMode[] getFXDndAction(int wkDndAction) {
1155         LinkedList<TransferMode> tms = new LinkedList<TransferMode>();
1156         if ((wkDndAction & WK_DND_ACTION_COPY) != 0)
1157             tms.add(TransferMode.COPY);
1158         if ((wkDndAction & WK_DND_ACTION_MOVE) != 0)
1159             tms.add(TransferMode.MOVE);
1160         if ((wkDndAction & WK_DND_ACTION_LINK) != 0)
1161             tms.add(TransferMode.LINK);
1162         return tms.toArray(new TransferMode[0]);
1163     }
1164 
1165     private void registerEventHandlers() {
1166         addEventHandler(KeyEvent.ANY,
1167                 event -> {
1168                     processKeyEvent(event);
1169                 });
1170         addEventHandler(MouseEvent.ANY,
1171                 event -> {
1172                     processMouseEvent(event);
1173                     if (event.isDragDetect() && !page.isDragConfirmed()) {
1174                         //postpone drag recognition:
1175                         //Webkit cannot resolve here is it a drag
1176                         //or selection.
1177                         event.setDragDetect(false);
1178                     }
1179                 });
1180         addEventHandler(ScrollEvent.SCROLL,
1181                 event -> {
1182                     processScrollEvent(event);
1183                 });
1184         setOnInputMethodTextChanged(
1185                 event -> {
1186                     processInputMethodEvent(event);
1187                 });
1188 
1189         //Drop target implementation:
1190         EventHandler<DragEvent> destHandler = event -> {
1191             try {
1192                 Dragboard db = event.getDragboard();
1193                 LinkedList<String> mimes = new LinkedList<String>();
1194                 LinkedList<String> values = new LinkedList<String>();
1195                 for (DataFormat df : db.getContentTypes()) {
1196                     //TODO: extend to non-string serialized values.
1197                     //Please, look at the native code.
1198                     Object content = db.getContent(df);
1199                     if (content != null) {
1200                         for (String mime : df.getIdentifiers()) {
1201                             mimes.add(mime);
1202                             values.add(content.toString());
1203                         }
1204                     }
1205                 }
1206                 if (!mimes.isEmpty()) {
1207                     int wkDndEventType = getWKDndEventType(event.getEventType());
1208                     int wkDndAction = page.dispatchDragOperation(
1209                         wkDndEventType,
1210                         mimes.toArray(new String[0]), values.toArray(new String[0]),
1211                         (int)event.getX(), (int)event.getY(),
1212                         (int)event.getScreenX(), (int)event.getScreenY(),
1213                         getWKDndAction(db.getTransferModes().toArray(new TransferMode[0])));
1214 
1215                     //we cannot accept nothing on drop (we skip FX exception)
1216                     if (!(wkDndEventType == WebPage.DND_DST_DROP && wkDndAction == WK_DND_ACTION_NONE)) {
1217                         event.acceptTransferModes(getFXDndAction(wkDndAction));
1218                     }
1219                     event.consume();
1220                 }
1221             } catch (SecurityException ex) {
1222                 // Just ignore the exception
1223                 //ex.printStackTrace();
1224             }
1225         };
1226         setOnDragEntered(destHandler);
1227         setOnDragExited(destHandler);
1228         setOnDragOver(destHandler);
1229         setOnDragDropped(destHandler);
1230 
1231         //Drag source implementation:
1232         setOnDragDetected(event -> {
1233                if (page.isDragConfirmed()) {
1234                    page.confirmStartDrag();
1235                    event.consume();
1236                }
1237            });
1238         setOnDragDone(event -> {
1239                 page.dispatchDragOperation(
1240                     WebPage.DND_SRC_DROP,
1241                     null, null,
1242                     (int)event.getX(), (int)event.getY(),
1243                     (int)event.getScreenX(), (int)event.getScreenY(),
1244                     getWKDndAction(event.getAcceptedTransferMode()));
1245                 event.consume();
1246             });
1247 
1248         setInputMethodRequests(getInputMethodClient());
1249     }
1250 
1251     /*
1252      * Note: This method MUST only be called via its accessor method.
1253      */
1254     private void doPickNodeLocal(PickRay pickRay, PickResultChooser result) {
1255         NodeHelper.intersects(this, pickRay, result);
1256     }
1257 
1258     @Override protected ObservableList<Node> getChildren() {
1259         return super.getChildren();
1260     }
1261 
1262     // Node stuff
1263 
1264     /*
1265      * Note: This method MUST only be called via its accessor method.
1266      */
1267     private NGNode doCreatePeer() {
1268         return new NGWebView();
1269     }
1270 
1271     /*
1272      * Note: This method MUST only be called via its accessor method.
1273      */
1274     private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) {
1275         bounds.deriveWithNewBounds(0, 0, 0, (float) getWidth(), (float)getHeight(), 0);
1276         tx.transform(bounds, bounds);
1277         return bounds;
1278     }
1279 
1280     /*
1281      * Note: This method MUST only be called via its accessor method.
1282      */
1283     private void doTransformsChanged() {
1284     }
1285 
1286     /*
1287      * Note: This method MUST only be called via its accessor method.
1288      */
1289     private boolean doComputeContains(double localX, double localY) {
1290         // Note: Local bounds contain test is already done by the caller. (Node.contains()).
1291         return true;
1292     }
1293 
1294     /*
1295      * Note: This method MUST only be called via its accessor method.
1296      */
1297     private void doUpdatePeer() {
1298         final NGWebView peer = NodeHelper.getPeer(this);
1299 
1300         if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
1301             peer.setPage(page);
1302         }
1303         if (NodeHelper.isDirty(this, DirtyBits.NODE_GEOMETRY)) {
1304             peer.resize((float)getWidth(), (float)getHeight());
1305         }
1306         if (NodeHelper.isDirty(this, DirtyBits.WEBVIEW_VIEW)) {
1307             peer.requestRender();
1308         }
1309     }
1310 
1311     static {
1312         WebViewHelper.setWebViewAccessor(new WebViewHelper.WebViewAccessor() {
1313             @Override
1314             public NGNode doCreatePeer(Node node) {
1315                 return ((WebView) node).doCreatePeer();
1316             }
1317 
1318             @Override
1319             public void doUpdatePeer(Node node) {
1320                 ((WebView) node).doUpdatePeer();
1321             }
1322 
1323             @Override
1324             public void doTransformsChanged(Node node) {
1325                 ((WebView) node).doTransformsChanged();
1326             }
1327 
1328             @Override
1329             public BaseBounds doComputeGeomBounds(Node node,
1330                     BaseBounds bounds, BaseTransform tx) {
1331                 return ((WebView) node).doComputeGeomBounds(bounds, tx);
1332             }
1333 
1334             @Override
1335             public boolean doComputeContains(Node node, double localX, double localY) {
1336                 return ((WebView) node).doComputeContains(localX, localY);
1337             }
1338 
1339             @Override
1340             public void doPickNodeLocal(Node node, PickRay localPickRay,
1341                     PickResultChooser result) {
1342                 ((WebView) node).doPickNodeLocal(localPickRay, result);
1343             }
1344         });
1345 
1346         idMap.put(MouseButton.NONE, WCMouseEvent.NOBUTTON);
1347         idMap.put(MouseButton.PRIMARY, WCMouseEvent.BUTTON1);
1348         idMap.put(MouseButton.MIDDLE, WCMouseEvent.BUTTON2);
1349         idMap.put(MouseButton.SECONDARY, WCMouseEvent.BUTTON3);
1350 
1351         idMap.put(MouseEvent.MOUSE_PRESSED, WCMouseEvent.MOUSE_PRESSED);
1352         idMap.put(MouseEvent.MOUSE_RELEASED, WCMouseEvent.MOUSE_RELEASED);
1353         idMap.put(MouseEvent.MOUSE_MOVED, WCMouseEvent.MOUSE_MOVED);
1354         idMap.put(MouseEvent.MOUSE_DRAGGED, WCMouseEvent.MOUSE_DRAGGED);
1355 
1356         idMap.put(KeyEvent.KEY_PRESSED, WCKeyEvent.KEY_PRESSED);
1357         idMap.put(KeyEvent.KEY_RELEASED, WCKeyEvent.KEY_RELEASED);
1358         idMap.put(KeyEvent.KEY_TYPED, WCKeyEvent.KEY_TYPED);
1359     }
1360 }