changeset 9894:cf3016aa23f2 jdk-9+127

Merge
author ddhill
date Sat, 09 Jul 2016 10:26:50 -0400
parents 2b08224078ff 7a4c2a65b597
children 7f96cacb5da0 1f2ced63f52d
files modules/controls/src/main/resources/javafx/scene/control/skin/EmailBoard.txt modules/controls/src/main/resources/javafx/scene/control/skin/NumericBoard.txt modules/controls/src/main/resources/javafx/scene/control/skin/TextBoard.txt modules/controls/src/main/resources/javafx/scene/control/skin/UrlBoard.txt
diffstat 37 files changed, 529 insertions(+), 347 deletions(-) [+]
line wrap: on
line diff
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/ContextMenuContent.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/ContextMenuContent.java	Sat Jul 09 10:26:50 2016 -0400
@@ -28,12 +28,12 @@
 import com.sun.javafx.scene.NodeHelper;
 import com.sun.javafx.scene.control.behavior.TwoLevelFocusPopupBehavior;
 import com.sun.javafx.scene.control.skin.Utils;
+import com.sun.javafx.scene.traversal.Direction;
 import javafx.animation.Animation.Status;
 import javafx.animation.KeyFrame;
 import javafx.animation.Timeline;
 import javafx.beans.InvalidationListener;
 import javafx.beans.WeakInvalidationListener;
-import javafx.beans.binding.Bindings;
 import javafx.beans.property.ReadOnlyBooleanProperty;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
@@ -461,18 +461,6 @@
     private double ty;
 
     private void initialize() {
-        // keyboard navigation support. Initially focus goes to this ContextMenu,
-        // but when the user first hits the up or down arrow keys, the focus
-        // is transferred to the first or last item respectively. Once this
-        // happens, it is up to the menu items to navigate between themselves.
-        contextMenu.focusedProperty().addListener((observable, oldValue, newValue) -> {
-            if (newValue) {
-                // initialize the focused index for keyboard navigation.
-                currentFocusedIndex = -1;
-                requestFocus();
-            }
-        });
-
         // RT-19624 calling requestFocus inside layout was casuing repeated layouts.
         contextMenu.addEventHandler(Menu.ON_SHOWN, event -> {
             for (Node child : itemsContainer.getChildren()) {
@@ -530,12 +518,12 @@
                         break;
                     case DOWN:
                         // move to the next sibling
-                        moveToNextSibling();
+                        move(Direction.NEXT);
                         ke.consume();
                         break;
                     case UP:
                         // move to previous sibling
-                        moveToPreviousSibling();
+                        move(Direction.PREVIOUS);
                         ke.consume();
                         break;
                     case SPACE:
@@ -691,81 +679,40 @@
             }
         });
     }
-    /*
-     * Find the index of the next MenuItemContainer in the itemsContainer children.
-     */
-    private int findNext(int from) {
-        for (int i = from; i < itemsContainer.getChildren().size(); i++) {
+
+    private void move(Direction dir) {
+        int startIndex = currentFocusedIndex != -1 ? currentFocusedIndex : itemsContainer.getChildren().size();
+        requestFocusOnIndex(findSibling(dir, startIndex));
+    }
+
+    private int findSibling(final Direction dir, final int startIndex) {
+        final int childCount = itemsContainer.getChildren().size();
+        int i = startIndex;
+        do {
+            if (dir.isForward() && i >= childCount - 1) {
+                // loop to zero
+                i = 0;
+            } else if (!dir.isForward() && i == 0) {
+                // loop to end
+                i = childCount - 1;
+            } else {
+                i += (dir.isForward() ? 1 : -1);
+            }
+
             Node n = itemsContainer.getChildren().get(i);
             if (n instanceof MenuItemContainer && n.isVisible()) {
                 return i;
             }
-        }
-        // find from top
-        for (int i = 0; i < from; i++) {
-            Node n = itemsContainer.getChildren().get(i);
-            if (n instanceof MenuItemContainer && n.isVisible()) {
-                return i;
-            }
-        }
-        return -1; // should not happen
-    }
-
-    private void moveToNextSibling() {
-        // If focusedIndex is -1 then start from 0th menu item.
-        // Note that this will cycle through such that when you move to last item,
-        // it will move to 1st item on the next Down key press.
-        if (currentFocusedIndex != -1) {
-            currentFocusedIndex = findNext(currentFocusedIndex + 1);
-        } else if (currentFocusedIndex == -1 || currentFocusedIndex == (itemsContainer.getChildren().size() - 1)) {
-            currentFocusedIndex = findNext(0);
-        }
-
-        // request focus on the next sibling which currentFocusIndex points to
-        if (currentFocusedIndex != -1) {
-            Node n = itemsContainer.getChildren().get(currentFocusedIndex);
-            selectedBackground = ((MenuItemContainer)n);
-            n.requestFocus();
-            ensureFocusedMenuItemIsVisible(n);
-        }
-    }
-
-    /*
-     * Find the index the previous MenuItemContaner in the itemsContainer children.
-     */
-    private int findPrevious(int from) {
-        for (int i = from; i >= 0; i--) {
-            Node n = itemsContainer.getChildren().get(i);
-            if (n instanceof MenuItemContainer && n.isVisible()) {
-                return(i);
-            }
-        }
-        for (int i = itemsContainer.getChildren().size() - 1 ; i > from; i--) {
-            Node n = itemsContainer.getChildren().get(i);
-            if (n instanceof MenuItemContainer && n.isVisible()) {
-                return(i);
-            }
-        }
+        } while (i != startIndex);
         return -1;
     }
 
-     private void moveToPreviousSibling() {
-        // If focusedIndex is -1 then start from the last menu item to go up.
-        // Note that this will cycle through such that when you move to first item,
-        // it will move to last item on the next Up key press.
-        if (currentFocusedIndex != -1) {
-            currentFocusedIndex = findPrevious(currentFocusedIndex - 1);
-        } else if(currentFocusedIndex == -1 || currentFocusedIndex == 0) {
-            currentFocusedIndex = findPrevious(itemsContainer.getChildren().size() - 1);
-        }
-
-        // request focus on the previous sibling which currentFocusIndex points to
-        if (currentFocusedIndex != -1) {
-            Node n = itemsContainer.getChildren().get(currentFocusedIndex);
-            selectedBackground = ((MenuItemContainer)n);
-            n.requestFocus();
-            ensureFocusedMenuItemIsVisible(n);
-        }
+    public void requestFocusOnIndex(int index) {
+        currentFocusedIndex = index;
+        Node n = itemsContainer.getChildren().get(index);
+        selectedBackground = ((MenuItemContainer)n);
+        n.requestFocus();
+        ensureFocusedMenuItemIsVisible(n);
     }
 
     /*
--- a/modules/controls/src/main/java/javafx/scene/chart/Axis.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/chart/Axis.java	Sat Jul 09 10:26:50 2016 -0400
@@ -438,7 +438,7 @@
      * @return true if animations should happen
      */
     protected final boolean shouldAnimate(){
-        return getAnimated() && NodeHelper.isTreeVisible(this) && getScene() != null;
+        return getAnimated() && NodeHelper.isTreeShowing(this);
     }
 
     /**
@@ -655,7 +655,7 @@
                 // auto range
                 range = autoRange(length);
                 // set current range to new range
-                setRange(range, getAnimated() && !isFirstPass && NodeHelper.isTreeVisible(this) && rangeInvalid);
+                setRange(range, getAnimated() && !isFirstPass && NodeHelper.isTreeShowing(this) && rangeInvalid);
             } else {
                 range = getRange();
             }
--- a/modules/controls/src/main/java/javafx/scene/chart/Chart.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/chart/Chart.java	Sat Jul 09 10:26:50 2016 -0400
@@ -311,7 +311,7 @@
      * is visible and in a scene.
      */
     protected final boolean shouldAnimate(){
-        return getAnimated() && NodeHelper.isTreeVisible(this) && getScene() != null;
+        return getAnimated() && NodeHelper.isTreeShowing(this);
     }
 
     /**
--- a/modules/controls/src/main/java/javafx/scene/control/TableView.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/TableView.java	Sat Jul 09 10:26:50 2016 -0400
@@ -635,10 +635,6 @@
             }
             c.reset();
 
-            // We don't maintain a bind for leafColumns, we simply call this update
-            // function behind the scenes in the appropriate places.
-            updateVisibleLeafColumns();
-
             // Fix for RT-15194: Need to remove removed columns from the
             // sortOrder list.
             List<TableColumn<S,?>> toRemove = new ArrayList<>();
@@ -676,6 +672,10 @@
                         weakColumnComparatorObserver);
             }
 
+            // We don't maintain a bind for leafColumns, we simply call this update
+            // function behind the scenes in the appropriate places.
+            updateVisibleLeafColumns();
+
             sortOrder.removeAll(toRemove);
 
             // Fix for RT-38892.
--- a/modules/controls/src/main/java/javafx/scene/control/TreeTableView.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/TreeTableView.java	Sat Jul 09 10:26:50 2016 -0400
@@ -695,10 +695,6 @@
             }
             c.reset();
 
-            // We don't maintain a bind for leafColumns, we simply call this update
-            // function behind the scenes in the appropriate places.
-            updateVisibleLeafColumns();
-
             // Fix for RT-15194: Need to remove removed columns from the
             // sortOrder list.
             List<TreeTableColumn<S,?>> toRemove = new ArrayList<TreeTableColumn<S,?>>();
@@ -736,6 +732,10 @@
                         weakColumnComparatorObserver);
             }
 
+            // We don't maintain a bind for leafColumns, we simply call this update
+            // function behind the scenes in the appropriate places.
+            updateVisibleLeafColumns();
+
             sortOrder.removeAll(toRemove);
 
             // Fix for RT-38892.
--- a/modules/controls/src/main/java/javafx/scene/control/skin/ButtonSkin.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/skin/ButtonSkin.java	Sat Jul 09 10:26:50 2016 -0400
@@ -28,7 +28,6 @@
 import com.sun.javafx.scene.NodeHelper;
 import com.sun.javafx.scene.control.behavior.BehaviorBase;
 import com.sun.javafx.scene.control.skin.Utils;
-import javafx.scene.Node;
 import javafx.scene.Scene;
 import javafx.scene.control.Button;
 import javafx.scene.control.ContextMenu;
@@ -64,13 +63,13 @@
      **************************************************************************/
 
     Runnable defaultButtonRunnable = () -> {
-        if (getSkinnable().getScene() != null && NodeHelper.isTreeVisible(getSkinnable()) && !getSkinnable().isDisabled()) {
+        if (NodeHelper.isTreeShowing(getSkinnable()) && !getSkinnable().isDisabled()) {
             getSkinnable().fire();
         }
     };
 
     Runnable cancelButtonRunnable = () -> {
-        if (getSkinnable().getScene() != null && NodeHelper.isTreeVisible(getSkinnable()) && !getSkinnable().isDisabled()) {
+        if (NodeHelper.isTreeShowing(getSkinnable()) && !getSkinnable().isDisabled()) {
             getSkinnable().fire();
         }
     };
--- a/modules/controls/src/main/java/javafx/scene/control/skin/ContextMenuSkin.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/skin/ContextMenuSkin.java	Sat Jul 09 10:26:50 2016 -0400
@@ -130,7 +130,7 @@
             @Override public void handle(Event event) {
                 Node cmContent = popupMenu.getSkin().getNode();
                 if (cmContent != null) {
-                    cmContent.requestFocus();
+//                    cmContent.requestFocus();
                     if (cmContent instanceof ContextMenuContent) {
                         Node accMenu = ((ContextMenuContent)cmContent).getItemsContainer();
                         accMenu.notifyAccessibleAttributeChanged(AccessibleAttribute.VISIBLE);
--- a/modules/controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java	Sat Jul 09 10:26:50 2016 -0400
@@ -26,6 +26,8 @@
 package javafx.scene.control.skin;
 
 import static com.sun.javafx.FXPermissions.ACCESS_WINDOW_LIST_PERMISSION;
+
+import com.sun.javafx.scene.traversal.Direction;
 import javafx.css.converter.EnumConverter;
 import javafx.css.converter.SizeConverter;
 import com.sun.javafx.scene.control.MenuBarButton;
@@ -61,6 +63,7 @@
 import javafx.scene.control.MenuButton;
 import javafx.scene.control.MenuItem;
 import javafx.scene.control.SeparatorMenuItem;
+import javafx.scene.control.Skin;
 import javafx.scene.control.SkinBase;
 import javafx.scene.input.KeyCombination;
 import javafx.scene.input.KeyEvent;
@@ -77,6 +80,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.WeakHashMap;
 
 import com.sun.javafx.menu.MenuBase;
@@ -86,6 +90,8 @@
 import com.sun.javafx.tk.Toolkit;
 import java.util.function.Predicate;
 import javafx.stage.Window;
+import javafx.util.Pair;
+
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
@@ -118,8 +124,13 @@
 
     private final HBox container;
 
+    // represents the currently _open_ menu
     private Menu openMenu;
     private MenuBarButton openMenuButton;
+
+    // represents the currently _focused_ menu. If openMenu is non-null, this should equal
+    // openMenu. If openMenu is null, this can be any menu in the menu bar.
+    private Menu focusedMenu;
     private int focusedMenuIndex = -1;
 
     private static WeakHashMap<Stage, Reference<MenuBarSkin>> systemMenuMap;
@@ -182,7 +193,7 @@
                         unSelectMenus();
                         menuModeStart(0);
                         openMenuButton = ((MenuBarButton)container.getChildren().get(0));
-                        openMenu = getSkinnable().getMenus().get(0);
+//                        openMenu = getSkinnable().getMenus().get(0);
                         openMenuButton.setHover();
                     }
                     else {
@@ -218,25 +229,24 @@
         // Key navigation
         keyEventHandler = event -> {
             // process right left and may be tab key events
-            if (openMenu != null) {
+            if (focusedMenu != null) {
                 switch (event.getCode()) {
                     case LEFT: {
                         boolean isRTL = control.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT;
                         if (control.getScene().getWindow().isFocused()) {
-                            if (openMenu == null) return;
-                            if ( !openMenu.isShowing()) {
+                            if (openMenu != null && !openMenu.isShowing()) {
                                 if (isRTL) {
-                                    selectNextMenu(); // just move the selection bar
+                                    moveToMenu(Direction.NEXT, false); // just move the selection bar
                                 } else {
-                                    selectPrevMenu(); // just move the selection bar
+                                    moveToMenu(Direction.PREVIOUS, false); // just move the selection bar
                                 }
                                 event.consume();
                                 return;
                             }
                             if (isRTL) {
-                                showNextMenu();
+                                moveToMenu(Direction.NEXT, true);
                             } else {
-                                showPrevMenu();
+                                moveToMenu(Direction.PREVIOUS, true);
                             }
                         }
                         event.consume();
@@ -246,20 +256,19 @@
                     {
                         boolean isRTL = control.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT;
                         if (control.getScene().getWindow().isFocused()) {
-                            if (openMenu == null) return;
-                            if (! openMenu.isShowing()) {
+                            if (openMenu != null && !openMenu.isShowing()) {
                                 if (isRTL) {
-                                    selectPrevMenu(); // just move the selection bar
+                                    moveToMenu(Direction.PREVIOUS, false); // just move the selection bar
                                 } else {
-                                    selectNextMenu(); // just move the selection bar
+                                    moveToMenu(Direction.NEXT, false); // just move the selection bar
                                 }
                                 event.consume();
                                 return;
                             }
                             if (isRTL) {
-                                showPrevMenu();
+                                moveToMenu(Direction.PREVIOUS, true);
                             } else {
-                                showNextMenu();
+                                moveToMenu(Direction.NEXT, true);
                             }
                         }
                         event.consume();
@@ -270,11 +279,9 @@
                     //case ENTER:
                         // RT-18859: Doing nothing for space and enter
                         if (control.getScene().getWindow().isFocused()) {
-                            if (focusedMenuIndex != -1 && openMenu != null) {
-                                openMenu = getSkinnable().getMenus().get(focusedMenuIndex);
-                                if (!isMenuEmpty(getSkinnable().getMenus().get(focusedMenuIndex))) {
-                                    openMenu.show();
-                                }
+                            if (focusedMenuIndex != -1) {
+                                Menu menuToOpen = getSkinnable().getMenus().get(focusedMenuIndex);
+                                showMenu(menuToOpen, true);
                                 event.consume();
                             }
                         }
@@ -295,7 +302,7 @@
                 unSelectMenus();
                 menuModeStart(0);
                 openMenuButton = ((MenuBarButton)container.getChildren().get(0));
-                openMenu = getSkinnable().getMenus().get(0);
+                setFocusedMenuIndex(0);
                 openMenuButton.setHover();
             } else {
                 unSelectMenus();
@@ -401,7 +408,7 @@
         ParentTraversalEngine engine = new ParentTraversalEngine(getSkinnable());
         engine.addTraverseListener((node, bounds) -> {
             if (openMenu != null) openMenu.hide();
-            focusedMenuIndex = 0;
+            setFocusedMenuIndex(0);
         });
         ParentHelper.setTraversalEngine(getSkinnable(), engine);
 
@@ -430,6 +437,38 @@
         });
     }
 
+    private void showMenu(Menu menu, boolean keyboardDriven) {
+        // hide the currently visible menu, and move to the next one
+        if (openMenu == menu) return;
+        if (openMenu != null) {
+            openMenu.hide();
+        }
+
+        openMenu = menu;
+        if (!menu.isShowing() && !isMenuEmpty(menu)) {
+            if (keyboardDriven) {
+                // put selection / focus on first item in menu
+                MenuButton menuButton = getNodeForMenu(focusedMenuIndex);
+                Skin<?> skin = menuButton.getSkin();
+                if (skin instanceof MenuButtonSkinBase) {
+                    ((MenuButtonSkinBase)skin).requestFocusOnFirstMenuItem();
+                }
+            }
+
+            openMenu.show();
+        }
+    }
+
+    private void setFocusedMenuIndex(int index) {
+        this.focusedMenuIndex = index;
+        focusedMenu = index == -1 ? null : getSkinnable().getMenus().get(index);
+
+        if (focusedMenu != null && focusedMenuIndex != -1) {
+            openMenuButton = (MenuBarButton)container.getChildren().get(focusedMenuIndex);
+            openMenuButton.setHover();
+        }
+    }
+
 
 
     /***************************************************************************
@@ -900,8 +939,7 @@
                         openMenuButton.hide();
                     }
                     openMenuButton = menuButton;
-                    openMenu = menu;
-                    if (!menu.isShowing())menu.show();
+                    showMenu(menu, false);
                 }
             });
 
@@ -910,10 +948,7 @@
 
                 // check if the owner window has focus
                 if (menuButton.getScene().getWindow().isFocused()) {
-                    openMenu = menu;
-                    if (!isMenuEmpty(menu)){
-                        openMenu.show();
-                    }
+                    showMenu(menu, false);
                     // update FocusedIndex
                     menuModeStart(getMenuBarButtonIndex(menuButton));
                 }
@@ -924,74 +959,11 @@
                 if (menuButton.getScene().getWindow().isFocused()) {
                     if (pendingDismiss) {
                         resetOpenMenu();
-//                            menuButton.hide();
                     }
                 }
                 pendingDismiss = false;
             });
 
-//            menuButton. setOnKeyPressed(new EventHandler<javafx.scene.input.KeyEvent>() {
-//                @Override public void handle(javafx.scene.input.KeyEvent ke) {
-//                    switch (ke.getCode()) {
-//                        case LEFT:
-//                            if (menuButton.getScene().getWindow().isFocused()) {
-//                                Menu prevMenu = findPreviousSibling();
-//                                if (openMenu == null || ! openMenu.isShowing()) {
-//                                    return;
-//                                }
-////                                if (focusedMenuIndex == container.getChildren().size() - 1) {
-////                                   ((MenuBarButton)container.getChildren().get(focusedMenuIndex)).requestFocus();
-////                                }
-//                                 // hide the currently visible menu, and move to the previous one
-//                                openMenu.hide();
-//                                if (!isMenuEmpty(prevMenu)) {
-//                                    openMenu = prevMenu;
-//                                    openMenu.show();
-//                                } else {
-//                                    openMenu = null;
-//                                }
-//                            }
-//                            ke.consume();
-//                            break;
-//                        case RIGHT:
-//                            if (menuButton.getScene().getWindow().isFocused()) {
-//                                Menu nextMenu = findNextSibling();
-//                                if (openMenu == null || ! openMenu.isShowing()) {
-//                                    return;
-//                                }
-////                                if (focusedMenuIndex == 0) {
-////                                    ((MenuBarButton)container.getChildren().get(focusedMenuIndex)).requestFocus();
-////                                }
-//                                 // hide the currently visible menu, and move to the next one
-//                                openMenu.hide();
-//                                if (!isMenuEmpty(nextMenu)) {
-//                                    openMenu = nextMenu;
-//                                    openMenu.show();
-//                                } else {
-//                                    openMenu = null;
-//                                }
-//                            }
-//                            ke.consume();
-//                            break;
-//
-//                        case DOWN:
-//                        case SPACE:
-//                        case ENTER:
-//                            if (menuButton.getScene().getWindow().isFocused()) {
-//                                if (focusedMenuIndex != -1) {
-//                                    if (!isMenuEmpty(getSkinnable().getMenus().get(focusedMenuIndex))) {
-//                                        openMenu = getSkinnable().getMenus().get(focusedMenuIndex);
-//                                        openMenu.show();
-//                                    } else {
-//                                        openMenu = null;
-//                                    }
-//                                    ke.consume();
-//                                }
-//                            }
-//                            break;
-//                    }
-//                }
-//            });
             menuButton.setOnMouseEntered(event -> {
                 // check if the owner window has focus
                 if (menuButton.getScene() != null && menuButton.getScene().getWindow() != null &&
@@ -1003,13 +975,7 @@
                     }
                     updateFocusedIndex();
                     if (openMenu != null && openMenu != menu) {
-                     // hide the currently visible menu, and move to the new one
-                        openMenu.hide();
-                        openMenu = menu;
-                        updateFocusedIndex();
-                        if (!isMenuEmpty(menu)) {
-                            openMenu.show();
-                        }
+                        showMenu(menu, false);
                     }
                 }
             });
@@ -1083,7 +1049,7 @@
         if (focusedMenuIndex == -1) {
             SceneHelper.getSceneAccessor().setTransientFocusContainer(getSkinnable().getScene(), getSkinnable());
         }
-        focusedMenuIndex = newIndex;
+        setFocusedMenuIndex(newIndex);
     }
 
     private void menuModeEnd() {
@@ -1093,78 +1059,42 @@
             /* Return the a11y focus to a control in the scene. */
             getSkinnable().notifyAccessibleAttributeChanged(AccessibleAttribute.FOCUS_NODE);
         }
-        focusedMenuIndex = -1;
+        setFocusedMenuIndex(-1);
     }
 
-    private void selectNextMenu() {
-        Menu nextMenu = findNextSibling();
-        if (nextMenu != null && focusedMenuIndex != -1) {
-            openMenuButton = (MenuBarButton)container.getChildren().get(focusedMenuIndex);
-            openMenuButton.setHover();
-            openMenu = nextMenu;
-        }
+    private void moveToMenu(Direction dir, boolean doShow) {
+        findSibling(dir, focusedMenuIndex).ifPresent(p -> {
+            setFocusedMenuIndex(p.getValue());
+            if (doShow) {
+                showMenu(p.getKey(), true);
+            }
+        });
     }
 
-    private void selectPrevMenu() {
-        Menu prevMenu = findPreviousSibling();
-        if (prevMenu != null && focusedMenuIndex != -1) {
-            openMenuButton = (MenuBarButton)container.getChildren().get(focusedMenuIndex);
-            openMenuButton.setHover();
-            openMenu = prevMenu;
-        }
-    }
-
-    private void showNextMenu() {
-        Menu nextMenu = findNextSibling();
-        // hide the currently visible menu, and move to the next one
-        if (openMenu != null) openMenu.hide();
-        openMenu = nextMenu;
-        if (!isMenuEmpty(nextMenu)) {
-            openMenu.show();
-        }
-    }
-
-    private void showPrevMenu() {
-        Menu prevMenu = findPreviousSibling();
-        // hide the currently visible menu, and move to the next one
-        if (openMenu != null) openMenu.hide();
-        openMenu = prevMenu;
-        if (!isMenuEmpty(prevMenu)) {
-            openMenu.show();
-        }
-    }
-
-    private Menu findPreviousSibling() {
-        if (focusedMenuIndex == -1) return null;
-        if (focusedMenuIndex == 0) {
-            focusedMenuIndex = container.getChildren().size() - 1;
+    private Optional<Pair<Menu,Integer>> findSibling(Direction dir, int startIndex) {
+        final int childCount = container.getChildren().size();
+        int nextIndex = startIndex;
+        if (nextIndex == -1) return Optional.empty();
+        if (dir.isForward() && nextIndex == childCount - 1) {
+            // loop forwards to start
+            nextIndex = 0;
+        } else if (!dir.isForward() && nextIndex == 0) {
+            // loop backwards to end
+            nextIndex = childCount - 1;
         } else {
-            focusedMenuIndex--;
-        }
-        // RT-19359
-        if (getSkinnable().getMenus().get(focusedMenuIndex).isDisable()) return findPreviousSibling();
-        clearMenuButtonHover();
-        return getSkinnable().getMenus().get(focusedMenuIndex);
-    }
-
-    private Menu findNextSibling() {
-        if (focusedMenuIndex == -1) return null;
-        if (focusedMenuIndex == container.getChildren().size() - 1) {
-            focusedMenuIndex = 0;
-        } else {
-            focusedMenuIndex++;
+            nextIndex += dir.isForward() ? 1 : -1;
         }
         // RT_19359
-        if (getSkinnable().getMenus().get(focusedMenuIndex).isDisable()) return findNextSibling();
+        if (getSkinnable().getMenus().get(nextIndex).isDisable()) return findSibling(dir, nextIndex);
         clearMenuButtonHover();
-        return getSkinnable().getMenus().get(focusedMenuIndex);
+        return Optional.of(new Pair<>(getSkinnable().getMenus().get(nextIndex), nextIndex));
     }
 
     private void updateFocusedIndex() {
         int index = 0;
         for(Node n : container.getChildren()) {
             if (n.isHover()) {
-                focusedMenuIndex = index;
+                setFocusedMenuIndex(index);
                 return;
             }
             index++;
--- a/modules/controls/src/main/java/javafx/scene/control/skin/MenuButtonSkin.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/skin/MenuButtonSkin.java	Sat Jul 09 10:26:50 2016 -0400
@@ -26,7 +26,6 @@
 package javafx.scene.control.skin;
 
 import com.sun.javafx.scene.control.ContextMenuContent;
-import javafx.scene.Node;
 import javafx.scene.control.Control;
 import javafx.scene.control.MenuButton;
 
@@ -94,8 +93,15 @@
 
         // request focus on content when the popup is shown
         popup.setOnShown(event -> {
-            ContextMenuContent cmContent = (ContextMenuContent)popup.getSkin().getNode();
-            if (cmContent != null) cmContent.requestFocus();
+            if (requestFocusOnFirstMenuItem) {
+                requestFocusOnFirstMenuItem();
+                requestFocusOnFirstMenuItem = false;
+            } else {
+                ContextMenuContent cmContent = (ContextMenuContent) popup.getSkin().getNode();
+                if (cmContent != null) {
+                    cmContent.requestFocus();
+                }
+            }
         });
 
         if (control.getOnAction() == null) {
--- a/modules/controls/src/main/java/javafx/scene/control/skin/MenuButtonSkinBase.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/skin/MenuButtonSkinBase.java	Sat Jul 09 10:26:50 2016 -0400
@@ -33,9 +33,11 @@
 import javafx.application.Platform;
 import javafx.collections.ListChangeListener;
 import javafx.event.ActionEvent;
+import javafx.scene.Node;
 import javafx.scene.control.ContextMenu;
 import javafx.scene.control.MenuButton;
 import javafx.scene.control.MenuItem;
+import javafx.scene.control.Skin;
 import javafx.scene.control.SkinBase;
 import javafx.scene.input.MouseEvent;
 import javafx.scene.layout.Region;
@@ -145,19 +147,6 @@
             }
         });
 
-//        If setOnAction() is overridden the code below causes the popup to show and hide.
-//        control.addEventHandler(ActionEvent.ACTION, new EventHandler<ActionEvent>() {
-//                @Override public void handle(ActionEvent e) {
-//                    if (!popup.isVisible()) {
-//                        show();
-//                    }
-//                    else {
-//                        hide();
-//                    }
-//
-//                }
-//            });
-
         // Register listeners
         registerChangeListener(control.showingProperty(), e -> {
             if (getSkinnable().isShowing()) {
@@ -286,22 +275,27 @@
     private void show() {
         if (!popup.isShowing()) {
             popup.show(getSkinnable(), getSkinnable().getPopupSide(), 0, 0);
-
-//            if (getSkinnable().isOpenVertically()) {
-//                // FIXME ugly hack - need to work out why we need '12' for
-//                // MenuButton/SplitMenuButton, but not for Menus
-//                double indent = getSkinnable().getStyleClass().contains("menu") ? 0 : 12;
-//                popup.show(getSkinnable(), Side.BOTTOM, indent, 0);
-//            } else {
-//                popup.show(getSkinnable(), Side.RIGHT, 0, 12);
-//            }
         }
     }
 
     private void hide() {
         if (popup.isShowing()) {
             popup.hide();
-//            popup.getAnchor().requestFocus();
+        }
+    }
+
+    boolean requestFocusOnFirstMenuItem = false;
+    void requestFocusOnFirstMenuItem() {
+        this.requestFocusOnFirstMenuItem = true;
+    }
+
+    void putFocusOnFirstMenuItem() {
+        Skin<?> popupSkin = popup.getSkin();
+        if (popupSkin instanceof ContextMenuSkin) {
+            Node node = popupSkin.getNode();
+            if (node instanceof ContextMenuContent) {
+                ((ContextMenuContent)node).requestFocusOnIndex(0);
+            }
         }
     }
 
--- a/modules/controls/src/main/java/javafx/scene/control/skin/ProgressBarSkin.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/skin/ProgressBarSkin.java	Sat Jul 09 10:26:50 2016 -0400
@@ -298,7 +298,7 @@
         // width might have changed so recreate our animation if needed
         if (isIndeterminate) {
             createIndeterminateTimeline();
-            if (NodeHelper.isTreeVisible(getSkinnable())) {
+            if (NodeHelper.isTreeShowing(getSkinnable())) {
                 indeterminateTransition.play();
             }
 
--- a/modules/controls/src/main/java/javafx/scene/control/skin/ProgressIndicatorSkin.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/skin/ProgressIndicatorSkin.java	Sat Jul 09 10:26:50 2016 -0400
@@ -132,11 +132,10 @@
         // register listeners
         registerChangeListener(control.indeterminateProperty(), e -> initialize());
         registerChangeListener(control.progressProperty(), e -> updateProgress());
-        registerChangeListener(control.visibleProperty(), e -> updateAnimation());
-        registerChangeListener(control.parentProperty(), e -> updateAnimation());
-        registerChangeListener(control.sceneProperty(), e -> updateAnimation());
+        registerChangeListener(NodeHelper.treeShowingProperty(control), e -> updateAnimation());
 
         initialize();
+        updateAnimation();
     }
 
 
@@ -276,7 +275,7 @@
             // create spinner
             spinner = new IndeterminateSpinner(spinEnabled.get(), progressColor.get());
             getChildren().setAll(spinner);
-            if (NodeHelper.isTreeVisible(control)) {
+            if (NodeHelper.isTreeShowing(control)) {
                 if (indeterminateTransition != null) {
                     indeterminateTransition.play();
                 }
@@ -323,12 +322,10 @@
 
     void updateAnimation() {
         ProgressIndicator control = getSkinnable();
-        final boolean isTreeVisible = control.isVisible() &&
-                control.getParent() != null &&
-                control.getScene() != null;
+        final boolean isTreeShowing = NodeHelper.isTreeShowing(control);
         if (indeterminateTransition != null) {
-            pauseTimeline(!isTreeVisible);
-        } else if (isTreeVisible) {
+            pauseTimeline(!isTreeShowing);
+        } else if (isTreeShowing) {
             createIndeterminateTimeline();
         }
     }
@@ -650,6 +647,10 @@
         }
 
         private void rebuildTimeline() {
+            if (!NodeHelper.isTreeShowing(control)) {
+                return;
+            }
+
             if (spinEnabled) {
                 if (indeterminateTransition == null) {
                     indeterminateTransition = new Timeline();
--- a/modules/controls/src/main/java/javafx/scene/control/skin/ToolBarSkin.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/main/java/javafx/scene/control/skin/ToolBarSkin.java	Sat Jul 09 10:26:50 2016 -0400
@@ -136,7 +136,7 @@
             private Node selectPrev(int from, TraversalContext context) {
                 for (int i = from; i >= 0; --i) {
                     Node n = box.getChildren().get(i);
-                    if (n.isDisabled() || !NodeHelper.isTreeVisible(n)) continue;
+                    if (n.isDisabled() || !NodeHelper.isTreeShowing(n)) continue;
                     if (n instanceof Parent) {
                         Node selected = context.selectLastInParent((Parent)n);
                         if (selected != null) return selected;
@@ -151,7 +151,7 @@
             private Node selectNext(int from, TraversalContext context) {
                 for (int i = from, max = box.getChildren().size(); i < max; ++i) {
                     Node n = box.getChildren().get(i);
-                    if (n.isDisabled() || !NodeHelper.isTreeVisible(n)) continue;
+                    if (n.isDisabled() || !NodeHelper.isTreeShowing(n)) continue;
                     if (n.isFocusTraversable()) {
                         return n;
                     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/controls/src/main/resources/com/sun/javafx/scene/control/skin/EmailBoard.txt	Sat Jul 09 10:26:50 2016 -0400
@@ -0,0 +1,8 @@
+#removed unsupported ymacron, yhook
+[q|1|\[ ][w|2|\] ][e|3|{|egrave|eacute|ecirc|euml ][r|4|}|reg ][t|5|<|tm ][y|6|>|ygrave|yacute|ycirc|yuml ][u|7|laquo|ugrave|uacute|ucirc|uuml ][i|8|raquo|igrave|iacute|icirc|iuml ][o|9|`|ograve|oacute|ocirc|otilde|ouml|oslash|deg ][p|0|~|para|pi ]
+
+ [a|#|agrave|aacute|acirc|atilde|auml|aring ][s|\$|euro|pound|yen|scedil|scaron|szlig|sigma ][d|%|eth ][f|& ][g|*|sect ][h|(|middot ][j|)|deg ][k|_|neq ][l|" ]
+
+[$shift ][z|/|iexcl ][x|\\|iquest ][c|;|permil|ccedil|copy|cent ][v|:|reg ][b|=|tm ][n|+|ntilde ][m|-|micro ][@|' ][$backspace ]
+
+[$hide][$SymbolABC ][$space       ][,|! ][.|? ][.com|.org|.net ][$enter  ]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/controls/src/main/resources/com/sun/javafx/scene/control/skin/NumericBoard.txt	Sat Jul 09 10:26:50 2016 -0400
@@ -0,0 +1,9 @@
+[1 ][2 ][3 ][/ ]
+
+[4 ][5 ][6 ][- ]
+
+[7 ][8 ][9 ][$backspace ]
+
+[$hide][+][0 ][.|' ][$enter ]
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/controls/src/main/resources/com/sun/javafx/scene/control/skin/TextBoard.txt	Sat Jul 09 10:26:50 2016 -0400
@@ -0,0 +1,8 @@
+#removed unsupported ymacron, yhook
+[q|1|\[ ][w|2|\] ][e|3|{|egrave|eacute|ecirc|euml ][r|4|}|reg ][t|5|<|tm ][y|6|>|ygrave|yacute|ycirc|yuml ][u|7|laquo|ugrave|uacute|ucirc|uuml ][i|8|raquo|igrave|iacute|icirc|iuml ][o|9|`|ograve|oacute|ocirc|otilde|ouml|oslash|deg ][p|0|~|para|pi ]
+
+ [a|#|agrave|aacute|acirc|atilde|auml|aring ][s|\$|euro|pound|yen|scedil|scaron|szlig|sigma ][d|%|eth ][f|& ][g|*|sect ][h|(|middot ][j|)|deg ][k|_|neq ][l|@ ]
+
+[$shift ][z|/|iexcl ][x|\\|iquest ][c|;|permil|ccedil|copy|cent ][v|:|reg ][b|=|tm ][n|+|ntilde ][m|-|micro ]['|" ][$backspace ]
+
+[$hide][$SymbolABC ][$space         ][,|! ][.|? ][$enter  ]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/controls/src/main/resources/com/sun/javafx/scene/control/skin/UrlBoard.txt	Sat Jul 09 10:26:50 2016 -0400
@@ -0,0 +1,9 @@
+#removed unsupported ymacron, yhook
+[q|1|\[ ][w|2|\] ][e|3|{|egrave|eacute|ecirc|euml ][r|4|}|reg ][t|5|<|tm ][y|6|>|ygrave|yacute|ycirc|yuml ][u|7|laquo|ugrave|uacute|ucirc|uuml ][i|8|raquo|igrave|iacute|icirc|iuml ][o|9|`|ograve|oacute|ocirc|otilde|ouml|oslash|deg ][p|0|~|para|pi ]
+
+ [a|#|agrave|aacute|acirc|atilde|auml|aring ][s|\$|euro|pound|yen|scedil|scaron|szlig|sigma ][d|%|eth ][f|& ][g|*|sect ][h|(|middot ][j|)|deg ][k|"|neq ][l|@ ]
+
+[$shift ][z|iexcl ][x|iquest ][c|;|permil|ccedil|copy|cent ][v|:|reg ][b|=|tm ][n|+|ntilde ][m|'|micro ][-|_ ][$backspace ]
+
+[$hide ][$SymbolABC  ][www.|http://  ][.|,  ][.com|.org|.net  ][/|\\  ][$enter  ]
+
--- a/modules/controls/src/main/resources/javafx/scene/control/skin/EmailBoard.txt	Thu Jul 07 21:24:29 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#removed unsupported ymacron, yhook
-[q|1|\[ ][w|2|\] ][e|3|{|egrave|eacute|ecirc|euml ][r|4|}|reg ][t|5|<|tm ][y|6|>|ygrave|yacute|ycirc|yuml ][u|7|laquo|ugrave|uacute|ucirc|uuml ][i|8|raquo|igrave|iacute|icirc|iuml ][o|9|`|ograve|oacute|ocirc|otilde|ouml|oslash|deg ][p|0|~|para|pi ]
-
- [a|#|agrave|aacute|acirc|atilde|auml|aring ][s|\$|euro|pound|yen|scedil|scaron|szlig|sigma ][d|%|eth ][f|& ][g|*|sect ][h|(|middot ][j|)|deg ][k|_|neq ][l|" ]
-
-[$shift ][z|/|iexcl ][x|\\|iquest ][c|;|permil|ccedil|copy|cent ][v|:|reg ][b|=|tm ][n|+|ntilde ][m|-|micro ][@|' ][$backspace ]
-
-[$hide][$SymbolABC ][$space       ][,|! ][.|? ][.com|.org|.net ][$enter  ]
--- a/modules/controls/src/main/resources/javafx/scene/control/skin/NumericBoard.txt	Thu Jul 07 21:24:29 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-[1 ][2 ][3 ][/ ]
-
-[4 ][5 ][6 ][- ]
-
-[7 ][8 ][9 ][$backspace ]
-
-[$hide][+][0 ][.|' ][$enter ]
-
-
--- a/modules/controls/src/main/resources/javafx/scene/control/skin/TextBoard.txt	Thu Jul 07 21:24:29 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#removed unsupported ymacron, yhook
-[q|1|\[ ][w|2|\] ][e|3|{|egrave|eacute|ecirc|euml ][r|4|}|reg ][t|5|<|tm ][y|6|>|ygrave|yacute|ycirc|yuml ][u|7|laquo|ugrave|uacute|ucirc|uuml ][i|8|raquo|igrave|iacute|icirc|iuml ][o|9|`|ograve|oacute|ocirc|otilde|ouml|oslash|deg ][p|0|~|para|pi ]
-
- [a|#|agrave|aacute|acirc|atilde|auml|aring ][s|\$|euro|pound|yen|scedil|scaron|szlig|sigma ][d|%|eth ][f|& ][g|*|sect ][h|(|middot ][j|)|deg ][k|_|neq ][l|@ ]
-
-[$shift ][z|/|iexcl ][x|\\|iquest ][c|;|permil|ccedil|copy|cent ][v|:|reg ][b|=|tm ][n|+|ntilde ][m|-|micro ]['|" ][$backspace ]
-
-[$hide][$SymbolABC ][$space         ][,|! ][.|? ][$enter  ]
--- a/modules/controls/src/main/resources/javafx/scene/control/skin/UrlBoard.txt	Thu Jul 07 21:24:29 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#removed unsupported ymacron, yhook
-[q|1|\[ ][w|2|\] ][e|3|{|egrave|eacute|ecirc|euml ][r|4|}|reg ][t|5|<|tm ][y|6|>|ygrave|yacute|ycirc|yuml ][u|7|laquo|ugrave|uacute|ucirc|uuml ][i|8|raquo|igrave|iacute|icirc|iuml ][o|9|`|ograve|oacute|ocirc|otilde|ouml|oslash|deg ][p|0|~|para|pi ]
-
- [a|#|agrave|aacute|acirc|atilde|auml|aring ][s|\$|euro|pound|yen|scedil|scaron|szlig|sigma ][d|%|eth ][f|& ][g|*|sect ][h|(|middot ][j|)|deg ][k|"|neq ][l|@ ]
-
-[$shift ][z|iexcl ][x|iquest ][c|;|permil|ccedil|copy|cent ][v|:|reg ][b|=|tm ][n|+|ntilde ][m|'|micro ][-|_ ][$backspace ]
-
-[$hide ][$SymbolABC  ][www.|http://  ][.|,  ][.com|.org|.net  ][/|\\  ][$enter  ]
-
--- a/modules/controls/src/test/java/test/javafx/scene/control/TableViewTest.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/test/java/test/javafx/scene/control/TableViewTest.java	Sat Jul 09 10:26:50 2016 -0400
@@ -5376,4 +5376,17 @@
         assertEquals("Header should shrink to initial size.", initialHeight, row.getHeight(), 0.01);
         sl.dispose();
     }
+
+    @Test public void test_jdk_8160771() {
+        TableView table = new TableView();
+        TableColumn first = new TableColumn("First Name");
+        table.getColumns().add(first);
+        table.getVisibleLeafColumns().addListener((ListChangeListener) c -> {
+            c.next();
+            assertTrue(c.wasAdded());
+            assertSame(table, ((TableColumn) c.getAddedSubList().get(0)).getTableView());
+        });
+        TableColumn last = new TableColumn("Last Name");
+        table.getColumns().add(0, last);
+    }
 }
--- a/modules/controls/src/test/java/test/javafx/scene/control/TreeTableViewTest.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/controls/src/test/java/test/javafx/scene/control/TreeTableViewTest.java	Sat Jul 09 10:26:50 2016 -0400
@@ -6183,4 +6183,17 @@
 
         sl.dispose();
     }
+
+    @Test public void test_jdk_8160771() {
+        TreeTableView table = new TreeTableView();
+        TreeTableColumn first = new TreeTableColumn("First Name");
+        table.getColumns().add(first);
+        table.getVisibleLeafColumns().addListener((ListChangeListener) c -> {
+            c.next();
+            assertTrue(c.wasAdded());
+            assertSame(table, ((TreeTableColumn) c.getAddedSubList().get(0)).getTreeTableView());
+        });
+        TreeTableColumn last = new TreeTableColumn("Last Name");
+        table.getColumns().add(0, last);
+    }
 }
--- a/modules/graphics/src/main/java/com/sun/javafx/scene/NodeHelper.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/graphics/src/main/java/com/sun/javafx/scene/NodeHelper.java	Sat Jul 09 10:26:50 2016 -0400
@@ -304,6 +304,14 @@
         return nodeAccessor.treeVisibleProperty(node);
     }
 
+    public static boolean isTreeShowing(Node node) {
+        return nodeAccessor.isTreeShowing(node);
+    }
+
+    public static BooleanExpression treeShowingProperty(Node node) {
+        return nodeAccessor.treeShowingProperty(node);
+    }
+
     public static List<Style> getMatchingStyles(CssMetaData cssMetaData, Styleable styleable) {
         return nodeAccessor.getMatchingStyles(cssMetaData, styleable);
     }
@@ -366,6 +374,8 @@
         void reapplyCSS(Node node);
         boolean isTreeVisible(Node node);
         BooleanExpression treeVisibleProperty(Node node);
+        boolean isTreeShowing(Node node);
+        BooleanExpression treeShowingProperty(Node node);
         List<Style> getMatchingStyles(CssMetaData cssMetaData, Styleable styleable);
         Map<StyleableProperty<?>,List<Style>> findStyles(Node node,
                 Map<StyleableProperty<?>,List<Style>> styleMap);
--- a/modules/graphics/src/main/java/javafx/scene/Node.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/graphics/src/main/java/javafx/scene/Node.java	Sat Jul 09 10:26:50 2016 -0400
@@ -48,7 +48,6 @@
 import javafx.beans.property.StringProperty;
 import javafx.beans.property.StringPropertyBase;
 import javafx.beans.value.ChangeListener;
-import javafx.beans.value.WritableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.ListChangeListener.Change;
 import javafx.collections.ObservableList;
@@ -76,7 +75,6 @@
 import javafx.geometry.Point2D;
 import javafx.geometry.Point3D;
 import javafx.geometry.Rectangle2D;
-import javafx.scene.effect.Blend;
 import javafx.scene.effect.BlendMode;
 import javafx.scene.effect.Effect;
 import javafx.scene.image.WritableImage;
@@ -139,8 +137,6 @@
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.javafx.geom.transform.GeneralTransform3D;
 import com.sun.javafx.geom.transform.NoninvertibleTransformException;
-import com.sun.javafx.jmx.MXNodeAlgorithm;
-import com.sun.javafx.jmx.MXNodeAlgorithmContext;
 import com.sun.javafx.perf.PerformanceTracker;
 import com.sun.javafx.scene.BoundsAccessor;
 import com.sun.javafx.scene.CameraHelper;
@@ -582,6 +578,16 @@
             }
 
             @Override
+            public boolean isTreeShowing(Node node) {
+                return node.isTreeShowing();
+            }
+
+            @Override
+            public BooleanExpression treeShowingProperty(Node node) {
+                return node.treeShowingProperty();
+            }
+
+            @Override
             public List<Style> getMatchingStyles(CssMetaData cssMetaData,
                     Styleable styleable) {
                 return Node.getMatchingStyles(cssMetaData, styleable);
@@ -983,6 +989,20 @@
 
     private final InvalidationListener parentTreeVisibleChangedListener = valueModel -> updateTreeVisible(true);
 
+    private final ChangeListener<Boolean> windowShowingChangedListener
+            = (win, oldVal, newVal) -> updateTreeShowing();
+
+    private final ChangeListener<Window> sceneWindowChangedListener = (scene, oldWindow, newWindow) -> {
+        // Replace the windowShowingListener and call updateTreeShowing()
+        if (oldWindow != null) {
+            oldWindow.showingProperty().removeListener(windowShowingChangedListener);
+        }
+        if (newWindow != null) {
+            newWindow.showingProperty().addListener(windowShowingChangedListener);
+        }
+        updateTreeShowing();
+    };
+
     private SubScene subScene = null;
 
     /**
@@ -1045,6 +1065,16 @@
             focusSetDirty(newScene);
         }
         scenesChanged(newScene, newSubScene, oldScene, oldSubScene);
+
+        // isTreeShowing needs to take into account of Window's showing
+        if (oldScene != null) {
+            oldScene.windowProperty().removeListener(sceneWindowChangedListener);
+        }
+        if (newScene != null) {
+            newScene.windowProperty().addListener(sceneWindowChangedListener);
+        }
+        updateTreeShowing();
+
         if (sceneChanged && reapplyCSS) reapplyCSS();
 
         if (sceneChanged && !isDirtyEmpty()) {
@@ -8205,6 +8235,78 @@
 
     }
 
+    private boolean isWindowShowing() {
+        Scene s = getScene();
+        if (s == null) return false;
+        Window w = s.getWindow();
+        return w != null && w.isShowing();
+    }
+
+    private void updateTreeShowing() {
+        setTreeShowing(isTreeVisible() && isWindowShowing());
+    }
+
+    private boolean treeShowing;
+    private TreeShowingPropertyReadOnly treeShowingRO;
+
+    final void setTreeShowing(boolean value) {
+        if (treeShowing != value) {
+            treeShowing = value;
+            ((TreeShowingPropertyReadOnly) treeShowingProperty()).invalidate();
+        }
+    }
+
+    final boolean isTreeShowing() {
+        return treeShowingProperty().get();
+    }
+
+    final BooleanExpression treeShowingProperty() {
+        if (treeShowingRO == null) {
+            treeShowingRO = new TreeShowingPropertyReadOnly();
+        }
+        return treeShowingRO;
+    }
+
+    class TreeShowingPropertyReadOnly extends BooleanExpression {
+
+        private ExpressionHelper<Boolean> helper;
+        private boolean valid;
+
+        @Override
+        public void addListener(InvalidationListener listener) {
+            helper = ExpressionHelper.addListener(helper, this, listener);
+        }
+
+        @Override
+        public void removeListener(InvalidationListener listener) {
+            helper = ExpressionHelper.removeListener(helper, listener);
+        }
+
+        @Override
+        public void addListener(ChangeListener<? super Boolean> listener) {
+            helper = ExpressionHelper.addListener(helper, this, listener);
+        }
+
+        @Override
+        public void removeListener(ChangeListener<? super Boolean> listener) {
+            helper = ExpressionHelper.removeListener(helper, listener);
+        }
+
+        protected void invalidate() {
+            if (valid) {
+                valid = false;
+                ExpressionHelper.fireValueChangedEvent(helper);
+            }
+        }
+
+        @Override
+        public boolean get() {
+            valid = true;
+            return Node.this.treeShowing;
+        }
+
+    }
+
     private void updateTreeVisible(boolean parentChanged) {
         boolean isTreeVisible = isVisible();
         final Node parentNode = getParent() != null ? getParent() :
@@ -8221,6 +8323,8 @@
             addToSceneDirtyList();
         }
         setTreeVisible(isTreeVisible);
+
+        updateTreeShowing();
     }
 
     private boolean treeVisible;
--- a/modules/graphics/src/main/java/javafx/scene/SubScene.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/graphics/src/main/java/javafx/scene/SubScene.java	Sat Jul 09 10:26:50 2016 -0400
@@ -313,9 +313,10 @@
                                 "is already set as root of another scene or subScene");
                     }
 
-                    // disabled and isTreeVisible properties are inherrited
+                    // disabled, isTreeVisible and isTreeShowing properties are inherited
                     _value.setTreeVisible(isTreeVisible());
                     _value.setDisabled(isDisabled());
+                    _value.setTreeShowing(isTreeShowing());
 
                     if (oldRoot != null) {
                         StyleManager.getInstance().forget(SubScene.this);
--- a/modules/graphics/src/test/java/test/javafx/scene/NodeTest.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/graphics/src/test/java/test/javafx/scene/NodeTest.java	Sat Jul 09 10:26:50 2016 -0400
@@ -27,13 +27,11 @@
 
 import test.javafx.scene.shape.TestUtils;
 import test.javafx.scene.shape.CircleTest;
-import com.sun.javafx.geom.BoxBounds;
 import com.sun.javafx.geom.PickRay;
 import com.sun.javafx.geom.transform.Affine2D;
 import com.sun.javafx.geom.transform.Affine3D;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.javafx.geom.transform.Translate2D;
-import test.com.sun.javafx.pgstub.StubStage;
 import test.com.sun.javafx.pgstub.StubToolkit;
 import com.sun.javafx.scene.DirtyBits;
 import com.sun.javafx.scene.NodeHelper;
@@ -64,6 +62,7 @@
 import java.lang.reflect.Method;
 import java.util.Comparator;
 import javafx.scene.Group;
+import javafx.scene.GroupShim;
 import javafx.scene.Node;
 import javafx.scene.NodeShim;
 import javafx.scene.ParallelCamera;
@@ -80,7 +79,6 @@
 import javafx.stage.Stage;
 
 import static org.junit.Assert.*;
-import org.junit.Ignore;
 /**
  * Tests various aspects of Node.
  *
@@ -1083,6 +1081,94 @@
     }
 
     @Test
+    public void testIsTreeVisible() {
+        final Group g = new Group();
+        final Circle c = new CircleTest.StubCircle(50);
+
+        ParentShim.getChildren(g).add(c);
+
+        Scene s = new Scene(g);
+        Stage st = new Stage();
+
+        assertTrue(NodeHelper.isTreeVisible(g));
+        assertTrue(NodeHelper.isTreeVisible(c));
+        assertFalse(NodeHelper.isTreeShowing(g));
+        assertFalse(NodeHelper.isTreeShowing(c));
+
+        st.show();
+        st.setScene(s);
+
+        assertTrue(NodeHelper.isTreeVisible(g));
+        assertTrue(NodeHelper.isTreeVisible(c));
+        assertTrue(NodeHelper.isTreeShowing(g));
+        assertTrue(NodeHelper.isTreeShowing(c));
+
+        SceneShim.scenePulseListener_pulse(s);
+
+        assertTrue(NodeHelper.isTreeVisible(g));
+        assertTrue(NodeHelper.isTreeVisible(c));
+        assertTrue(NodeHelper.isTreeShowing(g));
+        assertTrue(NodeHelper.isTreeShowing(c));
+
+        g.setVisible(false);
+        SceneShim.scenePulseListener_pulse(s);
+
+        assertFalse(NodeHelper.isTreeVisible(g));
+        assertFalse(NodeHelper.isTreeVisible(c));
+        assertFalse(NodeHelper.isTreeShowing(g));
+        assertFalse(NodeHelper.isTreeShowing(c));
+
+        g.setVisible(true);
+        SceneShim.scenePulseListener_pulse(s);
+
+        assertTrue(NodeHelper.isTreeVisible(g));
+        assertTrue(NodeHelper.isTreeVisible(c));
+        assertTrue(NodeHelper.isTreeShowing(g));
+        assertTrue(NodeHelper.isTreeShowing(c));
+
+        c.setVisible(false);
+        SceneShim.scenePulseListener_pulse(s);
+
+        assertTrue(NodeHelper.isTreeVisible(g));
+        assertFalse(NodeHelper.isTreeVisible(c));
+        assertTrue(NodeHelper.isTreeShowing(g));
+        assertFalse(NodeHelper.isTreeShowing(c));
+
+        c.setVisible(true);
+        SceneShim.scenePulseListener_pulse(s);
+
+        assertTrue(NodeHelper.isTreeVisible(g));
+        assertTrue(NodeHelper.isTreeVisible(c));
+        assertTrue(NodeHelper.isTreeShowing(g));
+        assertTrue(NodeHelper.isTreeShowing(c));
+
+        s.setRoot(new Group());
+        SceneShim.scenePulseListener_pulse(s);
+
+        assertTrue(NodeHelper.isTreeVisible(g));
+        assertTrue(NodeHelper.isTreeVisible(c));
+        assertFalse(NodeHelper.isTreeShowing(g));
+        assertFalse(NodeHelper.isTreeShowing(c));
+
+        s.setRoot(g);
+        SceneShim.scenePulseListener_pulse(s);
+
+        assertTrue(NodeHelper.isTreeVisible(g));
+        assertTrue(NodeHelper.isTreeVisible(c));
+        assertTrue(NodeHelper.isTreeShowing(g));
+        assertTrue(NodeHelper.isTreeShowing(c));
+
+        st.hide();
+        SceneShim.scenePulseListener_pulse(s);
+
+        assertTrue(NodeHelper.isTreeVisible(g));
+        assertTrue(NodeHelper.isTreeVisible(c));
+        assertFalse(NodeHelper.isTreeShowing(g));
+        assertFalse(NodeHelper.isTreeShowing(c));
+
+    }
+
+    @Test
     public void testSynchronizationOfInvisibleNodes_2() {
         final Group g = new Group();
         final Circle c = new CircleTest.StubCircle(50);
--- a/modules/web/src/main/java/com/sun/javafx/webkit/drt/DumpRenderTree.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/java/com/sun/javafx/webkit/drt/DumpRenderTree.java	Sat Jul 09 10:26:50 2016 -0400
@@ -252,6 +252,11 @@
         drt.setWaiting(false);
     }
 
+    private static void overridePreference(String key, String value) {
+        mlog("overridePreference");
+        drt.webPage.overridePreference(key, value);
+    }
+
     private synchronized void setLoaded(boolean loaded) {
         this.loaded = loaded;
         done();
--- a/modules/web/src/main/java/com/sun/webkit/WebPage.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/java/com/sun/webkit/WebPage.java	Sat Jul 09 10:26:50 2016 -0400
@@ -1399,6 +1399,15 @@
         }
     }
 
+    public void overridePreference(String key, String value) {
+        lockPage();
+        try {
+            twkOverridePreference(getPage(), key, value);
+        } finally {
+            unlockPage();
+        }
+    }
+
     public float getZoomFactor(boolean textOnly) {
         lockPage();
         try {
@@ -2471,6 +2480,7 @@
     private native static Element twkGetOwnerElement(long pFrame);
 
     private native void twkOpen(long pFrame, String url);
+    private native void twkOverridePreference(long pPage, String key, String value);
     private native void twkLoad(long pFrame, String text, String contentType);
     private native boolean twkIsLoading(long pFrame);
     private native void twkStop(long pFrame);
--- a/modules/web/src/main/java/javafx/scene/web/WebView.java	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/java/javafx/scene/web/WebView.java	Sat Jul 09 10:26:50 2016 -0400
@@ -971,8 +971,7 @@
 
         boolean iconified = (window instanceof Stage) ? ((Stage)window).isIconified() : false;
 
-        return NodeHelper.isTreeVisible(this)
-               && window.isShowing()
+        return NodeHelper.isTreeShowing(this)
                && window.getWidth() > 0
                && window.getHeight() > 0
                && !iconified;
--- a/modules/web/src/main/native/Source/WebCore/mapfile-macosx	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/native/Source/WebCore/mapfile-macosx	Sat Jul 09 10:26:50 2016 -0400
@@ -1865,6 +1865,7 @@
                _Java_com_sun_webkit_WebPage_twkLoad
                _Java_com_sun_webkit_WebPage_twkIsLoading
                _Java_com_sun_webkit_WebPage_twkOpen
+               _Java_com_sun_webkit_WebPage_twkOverridePreference
                _Java_com_sun_webkit_WebPage_twkPostPaint
                _Java_com_sun_webkit_WebPage_twkPrePaint
                _Java_com_sun_webkit_WebPage_twkPrint
--- a/modules/web/src/main/native/Source/WebCore/mapfile-vers	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/native/Source/WebCore/mapfile-vers	Sat Jul 09 10:26:50 2016 -0400
@@ -1887,6 +1887,7 @@
                Java_com_sun_webkit_WebPage_twkIsJavaScriptEnabled;
                Java_com_sun_webkit_WebPage_twkLoad;
                Java_com_sun_webkit_WebPage_twkOpen;
+               Java_com_sun_webkit_WebPage_twkOverridePreference;
                Java_com_sun_webkit_WebPage_twkIsLoading;
                Java_com_sun_webkit_WebPage_twkPostPaint;
                Java_com_sun_webkit_WebPage_twkPrePaint;
--- a/modules/web/src/main/native/Source/WebCore/platform/graphics/GraphicsContext.h	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/native/Source/WebCore/platform/graphics/GraphicsContext.h	Sat Jul 09 10:26:50 2016 -0400
@@ -132,7 +132,7 @@
 #if PLATFORM(JAVA)
             , globalAlpha(1)
             , interpolationQuality(InterpolationDefault)
-            , transform()
+            , clipBounds(FloatRect::infiniteRect())
 #endif
         {
         }
@@ -179,6 +179,7 @@
         float globalAlpha;
         InterpolationQuality interpolationQuality;
         AffineTransform transform;
+        FloatRect clipBounds;
 #endif
     };
 
--- a/modules/web/src/main/native/Source/WebCore/platform/graphics/java/FontJava.cpp	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/native/Source/WebCore/platform/graphics/java/FontJava.cpp	Sat Jul 09 10:26:50 2016 -0400
@@ -27,10 +27,10 @@
     bool allowTabs = run.allowTabs();
     String ret = run.is8Bit()
         ? String(allowTabs
-                ? String(run.characters8())
+                ? run.string()
                 : FontCascade::normalizeSpaces(run.characters8(), length))
         : String(allowTabs
-                ? String(run.characters16())
+                ? run.string()
                 : FontCascade::normalizeSpaces(run.characters16(), length));
     return ret.toJavaString(WebCore_GetJavaEnv());
 }
--- a/modules/web/src/main/native/Source/WebCore/platform/graphics/java/GraphicsContextJava.cpp	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/native/Source/WebCore/platform/graphics/java/GraphicsContextJava.cpp	Sat Jul 09 10:26:50 2016 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, 2016, Oracle and/or its affiliates. All rights reserved.
  */
 #include "config.h"
 #include <math.h>
@@ -261,6 +261,7 @@
     if (paintingDisabled())
         return;
 
+    m_state.clipBounds.intersect(rect);
     platformContext()->rq().freeSpace(20)
     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SETCLIP_IIII
     << (jint)rect.x() << (jint)rect.y() << (jint)rect.width() << (jint)rect.height();
@@ -268,8 +269,8 @@
 
 IntRect GraphicsContext::clipBounds() const
 {
-    notImplemented(); // tav todo
-    return IntRect();
+    // Transformation has inverse effect on clip bounds.
+    return enclosingIntRect(m_state.transform.inverse().mapRect(m_state.clipBounds));
 }
 
 void GraphicsContext::clipConvexPolygon(size_t numberOfPoints, const FloatPoint* points, bool antialias)
@@ -764,13 +765,15 @@
 
 static void setClipPath(
     GraphicsContext &gc,
+    GraphicsContextState& state,
     const Path& path,
     WindRule wrule,
     bool isOut)
 {
-    if (gc.paintingDisabled())
+    if (gc.paintingDisabled() || path.isEmpty())
         return;
 
+    state.clipBounds.intersect(path.fastBoundingRect());
     gc.platformContext()->rq().freeSpace(16)
     << jint(com_sun_webkit_graphics_GraphicsDecoder_CLIP_PATH)
     << copyPath(path.platformPath())
@@ -787,17 +790,17 @@
 
 void GraphicsContext::clip(const Path& path, WindRule wrule)
 {
-    setClipPath(*this, path, wrule, false);
+    setClipPath(*this, m_state, path, wrule, false);
 }
 
 void GraphicsContext::clipPath(const Path &path, WindRule wrule)
 {
-    setClipPath(*this, path, wrule, false);
+    setClipPath(*this, m_state, path, wrule, false);
 }
 
 void GraphicsContext::clipOut(const Path& path)
 {
-    setClipPath(*this, path, RULE_EVENODD, true);
+    setClipPath(*this, m_state, path, RULE_EVENODD, true);
 }
 
 void GraphicsContext::clipOut(const FloatRect& rect)
--- a/modules/web/src/main/native/Source/WebCore/platform/java/WebPage.cpp	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/native/Source/WebCore/platform/java/WebPage.cpp	Sat Jul 09 10:26:50 2016 -0400
@@ -1235,6 +1235,57 @@
     return JNI_FALSE;
 }
 
+JNIEXPORT void JNICALL Java_com_sun_webkit_WebPage_twkOverridePreference
+    (JNIEnv* env, jobject self, jlong pPage, jstring propertyName, jstring propertyValue)
+{
+    Page* page = WebPage::pageFromJLong(pPage);
+    if (!page) {
+        return;
+    }
+
+    Settings& settings = page->settings();
+    String nativePropertyName(env, propertyName);
+    String nativePropertyValue(env, propertyValue);
+
+    if (nativePropertyName == "WebKitTextAreasAreResizable") {
+        settings.setTextAreasAreResizable(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitLoadsImagesAutomatically") {
+        settings.setLoadsImagesAutomatically(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitMinimumFontSize") {
+        settings.setMinimumFontSize(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitMinimumLogicalFontSize") {
+        settings.setMinimumLogicalFontSize(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitAcceleratedCompositingEnabled") {
+        settings.setAcceleratedCompositingEnabled(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitScriptEnabled") {
+        settings.setScriptEnabled(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitJavaScriptCanOpenWindowsAutomatically") {
+        settings.setJavaScriptCanOpenWindowsAutomatically(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitPluginsEnabled") {
+        settings.setPluginsEnabled(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitDefaultFixedFontSize") {
+        settings.setDefaultFixedFontSize(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitContextMenuEnabled") {
+        settings.setContextMenuEnabled(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitUserAgent") {
+        settings.setUserAgent(nativePropertyValue);
+    } else if (nativePropertyName == "WebKitMaximumHTMLParserDOMTreeDepth") {
+        settings.setMaximumHTMLParserDOMTreeDepth(nativePropertyValue.toUInt());
+    } else if (nativePropertyName == "WebKitXSSAuditorEnabled")  {
+        settings.setXSSAuditorEnabled(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitSerifFontFamily") {
+        settings.setSerifFontFamily(nativePropertyValue);
+    } else if (nativePropertyName == "WebKitSansSerifFontFamily") {
+        settings.setSansSerifFontFamily(nativePropertyValue);
+    } else if (nativePropertyName == "WebKitFixedFontFamily") {
+        settings.setFixedFontFamily(nativePropertyValue);
+    } else if (nativePropertyName == "WebKitShowsURLsInToolTips") {
+        settings.setShowsURLsInToolTips(nativePropertyValue.toInt());
+    } else if (nativePropertyName == "WebKitCSSRegionsEnabled") {
+        RuntimeEnabledFeatures::sharedFeatures().setCSSRegionsEnabled(nativePropertyValue.toInt());
+    }
+}
+
 JNIEXPORT jfloat JNICALL Java_com_sun_webkit_WebPage_twkGetZoomFactor
     (JNIEnv* env, jobject self, jlong pFrame, jboolean textOnly)
 {
--- a/modules/web/src/main/native/Tools/DumpRenderTree/java/TestRunnerJava.cpp	Thu Jul 07 21:24:29 2016 +0000
+++ b/modules/web/src/main/native/Tools/DumpRenderTree/java/TestRunnerJava.cpp	Sat Jul 09 10:26:50 2016 -0400
@@ -115,7 +115,14 @@
 
 void TestRunner::overridePreference(JSStringRef key, JSStringRef value)
 {
-    // FIXME: implement
+    JNIEnv* env = DumpRenderTree_GetJavaEnv();
+
+    JLString jRelKey(JSStringRef_to_jstring(key, env));
+    JLString jRelValue(JSStringRef_to_jstring(value, env));
+    static jmethodID overridePreferenceMID = env->GetStaticMethodID(getDRTClass(env), "overridePreference", "(Ljava/lang/String;Ljava/lang/String;)V");
+    ASSERT(overridePreferenceMID);
+    env->CallStaticVoidMethod(getDRTClass(env), overridePreferenceMID, (jstring)jRelKey, (jstring)jRelValue);
+    CheckAndClearException(env);
 }
 
 void TestRunner::removeAllVisitedLinks()