changeset 2360:63d458f96141 8.0-b74

branch merge
author David Grieve<david.grieve@oracle.com>
date Wed, 23 Jan 2013 11:39:06 -0500
parents 33627ef7cf21 ea96f2c95dba
children 61ccba4d6c12 d32d3206f3e8 768175f757de 0ccbad343185
files javafx-ui-common/src/javafx/scene/layout/Region.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ProgressIndicatorSkin.java
diffstat 23 files changed, 1790 insertions(+), 291 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Wed Jan 23 18:53:46 2013 +0400
+++ b/.hgtags	Wed Jan 23 11:39:06 2013 -0500
@@ -60,3 +60,4 @@
 cb178c197204c27013eb658d80dbb0f4e17ca1fb 8.0-b70
 c80842bf169ab1d4d2232069dfed37f016bdb239 8.0-b71
 3819833c0c51b638a5d4a3c35016636786b1c60a 8.0-b72
+7847b4bf9ec54ec4018af1e568cbf480327d89be 8.0-b73
--- a/javafx-fxml/src/com/sun/javafx/fxml/BeanAdapter.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-fxml/src/com/sun/javafx/fxml/BeanAdapter.java	Wed Jan 23 11:39:06 2013 -0500
@@ -54,7 +54,7 @@
  * corresponding property model.
  */
 public class BeanAdapter extends AbstractMap<String, Object> {
-    private Object bean;
+    private final Object bean;
 
     private static ClassLoader contextClassLoader;
     
--- a/javafx-fxml/src/com/sun/javafx/fxml/expression/ExpressionValue.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-fxml/src/com/sun/javafx/fxml/expression/ExpressionValue.java	Wed Jan 23 11:39:06 2013 -0500
@@ -118,7 +118,10 @@
             this.namespace = namespace;
 
             if (next != null) {
-                next.monitor(Expression.get(namespace, key));
+                Object value = Expression.get(namespace, key);
+                if (value != null) {
+                    next.monitor(value);
+                }
             }
         }
 
@@ -128,7 +131,7 @@
                 ((ObservableList<Object>)namespace).removeListener(listChangeListener);
             } else if (namespace instanceof ObservableMap<?, ?>) {
                 ((ObservableMap<String, Object>)namespace).removeListener(mapChangeListener);
-            } else {
+            } else if (namespace != null) {
                 BeanAdapter namespaceAdapter = (BeanAdapter)namespace;
                 ObservableValue<Object> propertyModel = namespaceAdapter.getPropertyModel(key);
 
@@ -147,7 +150,10 @@
         public void remonitor() {
             if (next != null) {
                 next.unmonitor();
-                next.monitor(Expression.get(namespace, key));
+                Object value = Expression.get(namespace, key);
+                if (value != null) {
+                    next.monitor(value);
+                }
             }
         }
     }
--- a/javafx-fxml/src/javafx/fxml/doc-files/introduction_to_fxml.html	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-fxml/src/javafx/fxml/doc-files/introduction_to_fxml.html	Wed Jan 23 11:39:06 2013 -0500
@@ -1015,7 +1015,7 @@
 
 <hr>
 <p>
-<small>Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. Use is subject to <a href="http://docs.oracle.com/javafx/2/api/license.html">license terms</a>.
+<small>Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. Use is subject to <a href="http://docs.oracle.com/javafx/2/api/license.html">license terms</a>.
 </small>
 </p>
 </body>
--- a/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGRegion.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGRegion.java	Wed Jan 23 11:39:06 2013 -0500
@@ -196,6 +196,7 @@
         this.width = width;
         this.height = height;
         invalidateOpaqueRegion();
+        backgroundInsets = null;
     }
 
     /**
@@ -232,23 +233,15 @@
         final Background old = background;
         background = b == null ? Background.EMPTY : (Background) b;
 
-        float top=0, right=0, bottom=0, left=0;
         final List<BackgroundFill> fills = background.getFills();
         backgroundCanBeCached = !PrismSettings.disableRegionCaching && !fills.isEmpty() && (shape == null || cacheShape);
         if (backgroundCanBeCached) {
-            for (int i = 0, max = fills.size(); i < max; i++) {
-                final BackgroundFill fill = fills.get(i);
-                final Insets insets = fill.getInsets();
-                final CornerRadii radii = fill.getRadii();
-                top = (float) Math.max(top, insets.getTop() + Math.max(radii.getTopLeftVerticalRadius(), radii.getTopRightVerticalRadius()));
-                right = (float) Math.max(right, insets.getRight() + Math.max(radii.getTopRightHorizontalRadius(), radii.getBottomRightHorizontalRadius()));
-                bottom = (float) Math.max(bottom, insets.getBottom() + Math.max(radii.getBottomRightVerticalRadius(), radii.getBottomLeftVerticalRadius()));
-                left = (float) Math.max(left, insets.getLeft() + Math.max(radii.getTopLeftHorizontalRadius(), radii.getBottomLeftHorizontalRadius()));
-
+            for (int i=0, max=fills.size(); i<max && backgroundCanBeCached; i++) {
                 // We need to now inspect the paint to determine whether we can use a cache for this background.
                 // If a shape is being used, we don't care about gradients (we cache 'em both), but for a rectangle
                 // fill we omit these (so we can do 3-patch scaling). An ImagePattern is deadly to either
                 // (well, only deadly to a shape if it turns out to be a writable image).
+                final BackgroundFill fill = fills.get(i);
                 javafx.scene.paint.Paint paint = fill.getFill();
                 if ((shape == null && paint instanceof RadialGradient) || paint instanceof javafx.scene.paint.ImagePattern) {
                     backgroundCanBeCached = false;
@@ -261,7 +254,7 @@
                 }
             }
         }
-        backgroundInsets = new Insets(roundUp(top), roundUp(right), roundUp(bottom), roundUp(left));
+        backgroundInsets = null;
 
         // Only update the geom if the new background is geometrically different from the old
         if (!background.getOutsets().equals(old.getOutsets())) {
@@ -272,6 +265,31 @@
     }
 
     /**
+     * Visits each of the background fills and takes their raddi into account to determine the insets.
+     * The backgroundInsets variable is cleared whenever the fills change, or whenever the size of the
+     * region has changed (because if the size of the region changed and a radius is percentage based
+     * then we need to recompute the insets).
+     */
+    private void updateBackgroundInsets() {
+        float top=0, right=0, bottom=0, left=0;
+        final List<BackgroundFill> fills = background.getFills();
+        for (int i=0, max=fills.size(); i<max; i++) {
+            // We need to now inspect the paint to determine whether we can use a cache for this background.
+            // If a shape is being used, we don't care about gradients (we cache 'em both), but for a rectangle
+            // fill we omit these (so we can do 3-patch scaling). An ImagePattern is deadly to either
+            // (well, only deadly to a shape if it turns out to be a writable image).
+            final BackgroundFill fill = fills.get(i);
+            final Insets insets = fill.getInsets();
+            final CornerRadii radii = normalize(fill.getRadii());
+            top = (float) Math.max(top, insets.getTop() + Math.max(radii.getTopLeftVerticalRadius(), radii.getTopRightVerticalRadius()));
+            right = (float) Math.max(right, insets.getRight() + Math.max(radii.getTopRightHorizontalRadius(), radii.getBottomRightHorizontalRadius()));
+            bottom = (float) Math.max(bottom, insets.getBottom() + Math.max(radii.getBottomRightVerticalRadius(), radii.getBottomLeftVerticalRadius()));
+            left = (float) Math.max(left, insets.getLeft() + Math.max(radii.getTopLeftHorizontalRadius(), radii.getBottomLeftHorizontalRadius()));
+        }
+        backgroundInsets = new Insets(roundUp(top), roundUp(right), roundUp(bottom), roundUp(left));
+    }
+
+    /**
      * Called by the Region whenever it knows that the opaque insets have changed. The
      * Region <strong>must</strong> make sure that these opaque insets include the opaque
      * inset information from the Border and Background as well, the NGRegion will not
@@ -596,6 +614,7 @@
                 // example, drawing the focus rectangle extends beyond the width of the region).
                 // left + right background insets give us the left / right slice locations, plus 1 pixel for the center.
                 // Round the whole thing up to be a whole number.
+                if (backgroundInsets == null) updateBackgroundInsets();
                 final double leftInset = backgroundInsets.getLeft() + 1;
                 final double rightInset = backgroundInsets.getRight() + 1;
                 int cacheWidth = (int) (leftInset + rightInset);
@@ -674,7 +693,7 @@
                                 } else {
                                     // Fix the horizontal and vertical radii if they are percentage based
                                     if (radii.isTopLeftHorizontalRadiusAsPercentage()) tlhr = tlhr * width;
-                                    if (radii.isTopLeftVerticalRadiusAsPercentage()) tlhr = tlhr * height;
+                                    if (radii.isTopLeftVerticalRadiusAsPercentage()) tlvr = tlvr * height;
                                     // The edges are rounded, so we need to compute the arc width and arc height
                                     // and fill a round rect
                                     float arcWidth = tlhr + tlhr;
@@ -691,7 +710,7 @@
                                 // TODO document the issue number which will give us a fast path for rendering
                                 // non-uniform corners, and that we want to implement that instead of createPath2
                                 // below in such cases. (RT-26979)
-                                g.fill(createPath(t, l, b, r, radii));
+                                g.fill(createPath(t, l, b, r, normalize(radii)));
                             }
                         }
                     }
@@ -889,7 +908,7 @@
                 for (int i = 0, max = strokes.size(); i < max; i++) {
                     final BorderStroke stroke = strokes.get(i);
                     final BorderWidths widths = stroke.getWidths();
-                    final CornerRadii radii = stroke.getRadii();
+                    final CornerRadii radii = normalize(stroke.getRadii());
                     final Insets insets = stroke.getInsets();
 
                     final javafx.scene.paint.Paint topStroke = stroke.getTopStroke();
@@ -902,10 +921,10 @@
                     final float bottomInset = (float) insets.getBottom();
                     final float leftInset = (float) insets.getLeft();
 
-                    final float topWidth = (float) widths.getTop();
-                    final float rightWidth = (float) widths.getRight();
-                    final float bottomWidth = (float) widths.getBottom();
-                    final float leftWidth = (float) widths.getLeft();
+                    final float topWidth = (float) (widths.isTopAsPercentage() ? height * widths.getTop() : widths.getTop());
+                    final float rightWidth = (float) (widths.isRightAsPercentage() ? width * widths.getRight() : widths.getRight());
+                    final float bottomWidth = (float) (widths.isBottomAsPercentage() ? height * widths.getBottom() : widths.getBottom());
+                    final float leftWidth = (float) (widths.isLeftAsPercentage() ? width * widths.getLeft() : widths.getLeft());
 
                     final BorderStrokeStyle topStyle = stroke.getTopStyle();
                     final BorderStrokeStyle rightStyle = stroke.getRightStyle();
@@ -1088,15 +1107,23 @@
                         final int bottomInset = (int) Math.round(insets.getBottom());
                         final int leftInset = (int) Math.round(insets.getLeft());
 
-                        final int topWidth = (int) Math.round(widths.getTop());
-                        final int rightWidth = (int) Math.round(widths.getRight());
-                        final int bottomWidth = (int) Math.round(widths.getBottom());
-                        final int leftWidth = (int) Math.round(widths.getLeft());
+                        final int topWidth = (int) Math.round(
+                                widths.isTopAsPercentage() ? height * widths.getTop() : widths.getTop());
+                        final int rightWidth = (int) Math.round(
+                                widths.isRightAsPercentage() ? width * widths.getRight() : widths.getRight());
+                        final int bottomWidth = (int) Math.round(
+                                widths.isBottomAsPercentage() ? height * widths.getBottom() : widths.getBottom());
+                        final int leftWidth = (int) Math.round(
+                                widths.isLeftAsPercentage() ? width * widths.getLeft() : widths.getLeft());
 
-                        final int topSlice = (int) Math.round(slices.getTop());
-                        final int rightSlice = (int) Math.round(slices.getRight());
-                        final int bottomSlice = (int) Math.round(slices.getBottom());
-                        final int leftSlice = (int) Math.round(slices.getLeft());
+                        final int topSlice = (int) Math.round(
+                                slices.isTopAsPercentage() ? height * slices.getTop() : slices.getTop());
+                        final int rightSlice = (int) Math.round(
+                                slices.isRightAsPercentage() ? width * slices.getRight() : slices.getRight());
+                        final int bottomSlice = (int) Math.round(
+                                slices.isBottomAsPercentage() ? height * slices.getBottom() : slices.getBottom());
+                        final int leftSlice = (int) Math.round(
+                                slices.isLeftAsPercentage() ? width * slices.getLeft() : slices.getLeft());
 
                         // handle case where region is too small to fit in borders
                         if ((leftInset + leftWidth + rightInset + rightWidth) > width
@@ -1194,6 +1221,18 @@
         return (d - (int)d) == 0 ? (int) d : (int) (d + 1);
     }
 
+    private CornerRadii normalize(CornerRadii radii) {
+        final double tlvr = radii.isTopLeftVerticalRadiusAsPercentage() ? height * radii.getTopLeftVerticalRadius() : radii.getTopLeftVerticalRadius();
+        final double tlhr = radii.isTopLeftHorizontalRadiusAsPercentage() ? width * radii.getTopLeftHorizontalRadius() : radii.getTopLeftHorizontalRadius();
+        final double trvr = radii.isTopRightVerticalRadiusAsPercentage() ? height * radii.getTopRightVerticalRadius() : radii.getTopRightVerticalRadius();
+        final double trhr = radii.isTopRightHorizontalRadiusAsPercentage() ? width * radii.getTopRightHorizontalRadius() : radii.getTopRightHorizontalRadius();
+        final double brvr = radii.isBottomRightVerticalRadiusAsPercentage() ? height * radii.getBottomRightVerticalRadius() : radii.getBottomRightVerticalRadius();
+        final double brhr = radii.isBottomRightHorizontalRadiusAsPercentage() ? width * radii.getBottomRightHorizontalRadius() : radii.getBottomRightHorizontalRadius();
+        final double blvr = radii.isBottomLeftVerticalRadiusAsPercentage() ? height * radii.getBottomLeftVerticalRadius() : radii.getBottomLeftVerticalRadius();
+        final double blhr = radii.isBottomLeftHorizontalRadiusAsPercentage() ? width * radii.getBottomLeftHorizontalRadius() : radii.getBottomLeftHorizontalRadius();
+        return new CornerRadii(tlhr, tlvr, trvr, trhr, brhr, brvr, blvr, blhr, false, false, false, false, false, false, false, false);
+    }
+
     /**
      * Creates a Prism BasicStroke based on the stroke style, width, and line length.
      *
@@ -1303,19 +1342,20 @@
         // Take the first side that isn't.
         final BorderWidths widths = sb.getWidths();
         BorderStrokeStyle bs = sb.getTopStyle();
-        double sbWidth = widths.getTop();
+        double sbWidth = widths.isTopAsPercentage() ? height * widths.getTop() : widths.getTop();
         Object sbFill = sb.getTopStroke().impl_getPlatformPaint();
         if (bs == null) {
             bs = sb.getLeftStyle();
-            sbWidth = widths.getLeft();
+            sbWidth = widths.isLeftAsPercentage() ? width * widths.getLeft() : widths.getLeft();
             sbFill = sb.getLeftStroke().impl_getPlatformPaint();
             if (bs == null) {
                 bs = sb.getBottomStyle();
-                sbWidth = widths.getBottom();
+                sbWidth = widths.isBottomAsPercentage() ? height * widths.getBottom() : widths.getBottom();
                 sbFill = sb.getBottomStroke().impl_getPlatformPaint();
                 if (bs == null) {
                     bs = sb.getRightStyle();
-                    sbWidth = widths.getRight();
+                    sbWidth = widths.isRightAsPercentage() ? width * widths.getRight() : widths.getRight();
+                    sbWidth = widths.isRightAsPercentage() ? width * widths.getRight() : widths.getRight();
                     sbFill = sb.getRightStroke();
                 }
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/com/sun/javafx/scene/SceneHelper.java	Wed Jan 23 11:39:06 2013 -0500
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.scene;
+
+import javafx.scene.Scene;
+
+/**
+ * Used to access internal scene methods.
+ */
+public final class SceneHelper {
+    private static SceneAccessor sceneAccessor;
+
+    static {
+        forceInit(Scene.class);
+    }
+
+    private SceneHelper() {
+    }
+
+    public static void setPaused(final boolean paused) {
+        sceneAccessor.setPaused(paused);
+    }
+
+    public static void parentEffectiveOrientationChanged(final Scene scene) {
+        sceneAccessor.parentEffectiveOrientationChanged(scene);
+    }
+
+    public static void setSceneAccessor(final SceneAccessor newAccessor) {
+        if (sceneAccessor != null) {
+            throw new IllegalStateException();
+        }
+
+        sceneAccessor = newAccessor;
+    }
+
+    public interface SceneAccessor {
+        void setPaused(boolean paused);
+
+        void parentEffectiveOrientationChanged(Scene scene);
+    }
+
+    private static void forceInit(final Class<?> classToInit) {
+        try {
+            Class.forName(classToInit.getName(), true,
+                          classToInit.getClassLoader());
+        } catch (final ClassNotFoundException e) {
+            throw new AssertionError(e);  // Can't happen
+        }
+    }
+}
--- a/javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java	Wed Jan 23 11:39:06 2013 -0500
@@ -25,6 +25,7 @@
 
 package com.sun.javafx.tk;
 
+import com.sun.javafx.scene.SceneHelper;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -827,9 +828,7 @@
             }
         }
         this.getMasterTimer().pause();
-        if (sceneAccessor != null) {
-            sceneAccessor.setPaused(true);
-        }
+        SceneHelper.setPaused(true);
     }
 
     /*
@@ -837,9 +836,7 @@
      * It is used by Scenegraph-JMX bean.
      */
     public void resumeScenes() {
-        if (sceneAccessor != null) {
-            sceneAccessor.setPaused(false);
-        }
+        SceneHelper.setPaused(false);
         this.getMasterTimer().resume();
         Iterator<Window> i = Window.impl_getWindows();
         while (i.hasNext()) {
@@ -883,18 +880,6 @@
         return highlightRegions;
     }
 
-    // Accessor for scene class
-    public interface SceneAccessor {
-        // Pause all scenes
-        public void setPaused(boolean paused);
-    }
-
-    private static SceneAccessor sceneAccessor = null;
-
-    public static void setSceneAccessor(SceneAccessor accessor) {
-        sceneAccessor = accessor;
-    }
-
     public interface WritableImageAccessor {
         public void loadTkImage(WritableImage wimg, Object loader);
         public Object getTkImageLoader(WritableImage wimg);
--- a/javafx-ui-common/src/javafx/application/ConditionalFeature.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/javafx/application/ConditionalFeature.java	Wed Jan 23 11:39:06 2013 -0500
@@ -117,5 +117,16 @@
      * except Linux systems without the XComposite extension. The
      * XShape extension is used in that case, so the window edges are aliased.
      */
-    TRANSPARENT_WINDOW
+    TRANSPARENT_WINDOW,
+
+    /**
+     *  Indicates that a system supports {@link javafx.stage.StageStyle#UNIFIED}
+     *  <p>
+     *  NOTE: Currently, supported on:
+     *  <ul>
+     *      <li>Windows Vista+: a window is completely filled with the frozen glass effect</li>
+     *      <li>Mac OS X: a window has the brushed-metal textured background</li>
+     *  </ul>
+     */
+    UNIFIED_WINDOW
 }
--- a/javafx-ui-common/src/javafx/scene/Node.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/Node.java	Wed Jan 23 11:39:06 2013 -0500
@@ -146,6 +146,8 @@
 import javafx.scene.transform.Affine;
 import javafx.scene.transform.NonInvertibleTransformException;
 import javafx.geometry.NodeOrientation;
+import javafx.stage.Stage;
+import javafx.stage.Window;
 
 /**
  * Base class for scene graph nodes. A scene graph is a set of tree data structures
@@ -702,6 +704,7 @@
                     updateTreeVisible();
                     oldParent = newParent;
                     invalidateLocalToSceneTransform();
+                    parentEffectiveOrientationChanged();
                 }
 
                 @Override
@@ -769,6 +772,11 @@
                     peer.release();
                 }
             }
+            if (getParent() == null) {
+                // if we are the root we need to handle scene change
+                parentEffectiveOrientationChanged();
+            }
+
             oldScene = _scene;
             // we need to check if a override has been set, if so we need to apply it to the node now
             // that it may have a valid scene
@@ -3622,8 +3630,70 @@
     }
 
     /**
+     * Transforms a point from the coordinate space of the {@link Screen}
+     * into the local coordinate space of this {@code Node}.
+     * @param screenX x coordinate of a point on a Screen
+     * @param screenY y coordinate of a point on a Screen
+     * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
+     * Null is also returned if the transformation from local to Scene is not invertible.
+     */
+    public Point2D screenToLocal(double screenX, double screenY) {
+        Scene scene = getScene();
+        Window window = scene.getWindow();
+        if (scene == null || window == null) {
+            return null;
+        }
+        final com.sun.javafx.geom.Point2D tempPt =
+                TempState.getInstance().point;
+        tempPt.setLocation((float)(screenX - scene.getX() - window.getX()),
+                (float)(screenY - scene.getY() - window.getY()));
+        try {
+            sceneToLocal(tempPt);
+        } catch (NoninvertibleTransformException e) {
+            return null;
+        }
+        return new Point2D(tempPt.x, tempPt.y);
+    }
+
+    /**
+     * Transforms a point from the coordinate space of the {@link javafx.scene.Screen}
+     * into the local coordinate space of this {@code Node}.
+     * @param screenPoint a point on a Screen
+     * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
+     * Null is also returned if the transformation from local to Scene is not invertible.
+     */
+    public Point2D screenToLocal(Point2D screenPoint) {
+        return screenToLocal(screenPoint.getX(), screenPoint.getY());
+    }
+
+    /**
+     * Transforms a rectangle from the coordinate space of the
+     * {@link javafx.scene.Screen} into the local coordinate space of this
+     * {@code Node}.
+     * @param screenBounds bounds on a Screen
+     * @return bounds in the local Node'space or null if Node is not in a {@link Window}.
+     * Null is also returned if the transformation from local to Scene is not invertible.
+     */
+    public Bounds screenToLocal(Bounds screenBounds) {
+        Scene scene = getScene();
+        Window window = scene.getWindow();
+        if (scene == null || window == null) {
+            return null;
+        }
+        Bounds sceneBounds = new BoundingBox(screenBounds.getMinX() - scene.getX() - window.getX(),
+                screenBounds.getMinY() - scene.getY() - window.getY(),
+                screenBounds.getMinZ(),
+                screenBounds.getWidth(), screenBounds.getHeight(), screenBounds.getDepth());
+        return sceneToLocal(sceneBounds);
+    }
+
+    /**
      * Transforms a point from the coordinate space of the {@link Scene}
      * into the local coordinate space of this {@code Node}.
+     * @param sceneX x coordinate of a point on a Scene
+     * @param sceneY y coordinate of a point on a Scene
+     * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
+     * Null is also returned if the transformation from local to Scene is not invertible.
      */
     public Point2D sceneToLocal(double sceneX, double sceneY) {
         final com.sun.javafx.geom.Point2D tempPt =
@@ -3640,21 +3710,20 @@
     /**
      * Transforms a point from the coordinate space of the {@link javafx.scene.Scene}
      * into the local coordinate space of this {@code Node}.
+     * @param scenePoint a point on a Scene
+     * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
+     * Null is also returned if the transformation from local to Scene is not invertible.
      */
     public Point2D sceneToLocal(Point2D scenePoint) {
         return sceneToLocal(scenePoint.getX(), scenePoint.getY());
     }
 
     // Why is this method private?
-    private Point3D sceneToLocal(double x, double y, double z) {
+    private Point3D sceneToLocal(double x, double y, double z) throws NoninvertibleTransformException{
         final com.sun.javafx.geom.Vec3d tempV3D =
                 TempState.getInstance().vec3d;
         tempV3D.set(x, y, z);
-        try {
-            sceneToLocal(tempV3D);
-        } catch (NoninvertibleTransformException e) {
-            return null;
-        }
+        sceneToLocal(tempV3D);
         return new Point3D(tempV3D.x, tempV3D.y, tempV3D.z);
     }
 
@@ -3662,6 +3731,9 @@
      * Transforms a rectangle from the coordinate space of the
      * {@link javafx.scene.Scene} into the local coordinate space of this
      * {@code Node}.
+     * @param sceneBounds bounds on a Scene
+     * @return bounds in the local Node'space or null if Node is not in a {@link Window}.
+     * Null is also returned if the transformation from local to Scene is not invertible.
      */
     public Bounds sceneToLocal(Bounds sceneBounds) {
         // Do a quick update of localToParentTransform so that we can determine
@@ -3675,20 +3747,80 @@
 
             return createBoundingBox(p1, p2, p3, p4);
         }
-        Point3D p1 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
-        Point3D p2 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
-        Point3D p3 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
-        Point3D p4 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
-        Point3D p5 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
-        Point3D p6 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
-        Point3D p7 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
-        Point3D p8 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
-        return createBoundingBox(p1, p2, p3, p4, p5, p6, p7, p8);
+        try {
+            Point3D p1 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
+            Point3D p2 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
+            Point3D p3 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
+            Point3D p4 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
+            Point3D p5 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
+            Point3D p6 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
+            Point3D p7 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
+            Point3D p8 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
+            return createBoundingBox(p1, p2, p3, p4, p5, p6, p7, p8);
+        } catch (NoninvertibleTransformException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Transforms a point from the local coordinate space of this {@code Node}
+     * into the coordinate space of its {@link javafx.scene.Screen}.
+     * @param localX x coordinate of a point in Node's space
+     * @param localY y coordinate of a point in Node's space
+     * @return screen coordinates of the point or null if Node is not in a {@link Window}
+     */
+    public Point2D localToScreen(double localX, double localY) {
+        Scene scene = getScene();
+        Window window = scene.getWindow();
+        if (scene == null || window == null) {
+            return null;
+        }
+
+        final com.sun.javafx.geom.Point2D tempPt =
+                TempState.getInstance().point;
+        tempPt.setLocation((float)localX, (float)localY);
+        localToScene(tempPt);
+
+        return new Point2D(tempPt.x + scene.getX() + window.getX(),
+                tempPt.y + scene.getY() + window.getY());
+    }
+
+    /**
+     * Transforms a point from the local coordinate space of this {@code Node}
+     * into the coordinate space of its {@link javafx.scene.Screen}.
+     * @param localPoint a point in Node's space
+     * @return screen coordinates of the point or null if Node is not in a {@link Window}
+     */
+    public Point2D localToScreen(Point2D localPoint) {
+        return localToScreen(localPoint.getX(), localPoint.getY());
+    }
+
+    /**
+     * Transforms a bounds from the local coordinate space of this
+     * {@code Node} into the coordinate space of its {@link javafx.scene.Screen}.
+     * @param localBounds bounds in Node's space
+     * @return the bounds in screen coordinates or null if Node is not in a {@link Window}
+     */
+    public Bounds localToScreen(Bounds localBounds) {
+        Scene scene = getScene();
+        Window window = scene.getWindow();
+        if (scene == null || window == null) {
+            return  null;
+        }
+
+        Bounds sceneBounds = localToScene(localBounds);
+        return new BoundingBox(sceneBounds.getMinX() + scene.getX() + window.getX(),
+                sceneBounds.getMinY() + scene.getY() + window.getY(),
+                sceneBounds.getMinZ(),
+                sceneBounds.getWidth(), sceneBounds.getHeight(), sceneBounds.getDepth());
     }
 
     /**
      * Transforms a point from the local coordinate space of this {@code Node}
      * into the coordinate space of its {@link javafx.scene.Scene}.
+     * @param localX x coordinate of a point in Node's space
+     * @param localY y coordinate of a point in Node's space
+     * @return scene coordinates of the point or null if Node is not in a {@link Window}
      */
     public Point2D localToScene(double localX, double localY) {
         final com.sun.javafx.geom.Point2D tempPt =
@@ -3701,6 +3833,8 @@
     /**
      * Transforms a point from the local coordinate space of this {@code Node}
      * into the coordinate space of its {@link javafx.scene.Scene}.
+     * @param localPoint a point in Node's space
+     * @return scene coordinates of the point or null if Node is not in a {@link Window}
      */
     public Point2D localToScene(Point2D localPoint) {
         return localToScene(localPoint.getX(), localPoint.getY());
@@ -3717,6 +3851,8 @@
     /**
      * Transforms a bounds from the local coordinate space of this
      * {@code Node} into the coordinate space of its {@link javafx.scene.Scene}.
+     * @param localBounds bounds in Node's space
+     * @return the bounds in the scene coordinates or null if Node is not in a {@link Window}
      */
     public Bounds localToScene(Bounds localBounds) {
         // Do a quick update of localToParentTransform so that we can determine
@@ -3953,31 +4089,24 @@
             }
 
             // Check to see whether the node requires mirroring
-            if (getParent() == null) {
-                Scene scene = getScene();
-                if (scene != null && equals(scene.getRoot())) {
-                    // Mirror the root node
-                    if (hasMirroring()) {
-                        double xOffset = scene.getWidth() / 2;
-                        localToParentTx.translate(xOffset, 0, 0);
-                        localToParentTx.scale(-1, 1);
-                        localToParentTx.translate(-xOffset, 0, 0);
-                    }
-                }
-            } else {
-                // Mirror a leaf node
-                if (hasMirroring()) {
-                    double xOffset = impl_getPivotX();
-                    localToParentTx.translate(xOffset, 0, 0);
-                    localToParentTx.scale(-1, 1);
-                    localToParentTx.translate(-xOffset, 0, 0);
-                }
+            if (hasMirroring()) {
+                final double xOffset = getMirroringCenter();
+                localToParentTx.translate(xOffset, 0, 0);
+                localToParentTx.scale(-1, 1);
+                localToParentTx.translate(-xOffset, 0, 0);
             }
-            
+
             transformDirty = false;
         }
     }
 
+    private double getMirroringCenter() {
+        final Scene sceneValue = getScene();
+        return ((sceneValue != null) && (sceneValue.getRoot() == this))
+                   ? sceneValue.getWidth() / 2
+                   : impl_getPivotX();
+    }
+
     /**
      * Transforms in place the specified point from parent coords to local
      * coords. Made package private for the sake of testing.
@@ -5063,7 +5192,10 @@
      **************************************************************************/
     
     private ObjectProperty<NodeOrientation> nodeOrientation;
-    
+
+    private NodeOrientation effectiveNodeOrientation;
+    private NodeOrientation automaticNodeOrientation;
+
     public final void setNodeOrientation(NodeOrientation orientation) {
         nodeOrientationProperty().set(orientation);
     }
@@ -5088,16 +5220,7 @@
             nodeOrientation = new StyleableObjectProperty<NodeOrientation>(NodeOrientation.INHERIT) {
                 @Override
                 protected void invalidated() {
-                    impl_transformsChanged();
-                    // Apply the transform to all the children of this node
-                    if (Node.this instanceof Parent) {
-                        Parent p = (Parent)Node.this;
-                        List<Node> children = p.getChildren();
-                        for (int i = 0, max = children.size(); i < max; i++) {
-                            Node n = p.getChildren().get(i);
-                            n.impl_transformsChanged();
-                        }
-                    }
+                    nodeEffectiveOrientationChanged();
                 }
                 
                 @Override
@@ -5129,9 +5252,13 @@
      * </p>
      */
     public final NodeOrientation getEffectiveNodeOrientation() {
-        return getEffectiveNodeOrientation(false);
-    }
-    
+        if (effectiveNodeOrientation == null) {
+            effectiveNodeOrientation = calcEffectiveNodeOrientation();
+        }
+
+        return effectiveNodeOrientation;
+    }
+
     /**
      * Determines whether a node should be mirrored when node orientation
      * is right-to-left.
@@ -5147,36 +5274,90 @@
     public boolean isAutomaticallyMirrored() {
         return true;
     }
-    
-    NodeOrientation getNodeOrientation(boolean actual) {
-        if (actual && !isAutomaticallyMirrored()) return NodeOrientation.LEFT_TO_RIGHT;
-        return nodeOrientation == null ? NodeOrientation.INHERIT : nodeOrientation.get();
-    }
-    
-    final NodeOrientation getEffectiveNodeOrientation(boolean actual) {
-        NodeOrientation orientation = getNodeOrientation(actual);
-        if (orientation == NodeOrientation.INHERIT) {
-            Parent parent = getParent();
-            if (parent != null) return parent.getEffectiveNodeOrientation(actual);
-            Scene scene = getScene();
-            if (scene != null) return scene.getEffectiveNodeOrientation();
+
+    NodeOrientation getAutomaticNodeOrientation() {
+        if (automaticNodeOrientation == null) {
+            automaticNodeOrientation = calcAutomaticNodeOrientation();
+        }
+
+        return automaticNodeOrientation;
+    }
+
+    final void parentEffectiveOrientationChanged() {
+        if (getNodeOrientation() == NodeOrientation.INHERIT) {
+            nodeEffectiveOrientationChanged();
+        } else {
+            // mirroring changed
+            impl_transformsChanged();
+        }
+    }
+
+    void nodeEffectiveOrientationChanged() {
+        effectiveNodeOrientation = null;
+        automaticNodeOrientation = null;
+        // mirroring changed
+        impl_transformsChanged();
+    }
+
+    private NodeOrientation calcEffectiveNodeOrientation() {
+        final NodeOrientation nodeOrientationValue = getNodeOrientation();
+        if (nodeOrientationValue != NodeOrientation.INHERIT) {
+            return nodeOrientationValue;
+        }
+
+        final Node parentValue = getParent();
+        if (parentValue != null) {
+            return parentValue.getEffectiveNodeOrientation();
+        }
+
+        final Scene sceneValue = getScene();
+        if (sceneValue != null) {
+            return sceneValue.getEffectiveNodeOrientation();
+        }
+
+        return NodeOrientation.LEFT_TO_RIGHT;
+    }
+
+    private NodeOrientation calcAutomaticNodeOrientation() {
+        if (!isAutomaticallyMirrored()) {
             return NodeOrientation.LEFT_TO_RIGHT;
         }
-        return orientation;
-    }
-    
+
+        final NodeOrientation nodeOrientationValue = getNodeOrientation();
+        if (nodeOrientationValue != NodeOrientation.INHERIT) {
+            return nodeOrientationValue;
+        }
+
+        final Node parentValue = getParent();
+        if (parentValue != null) {
+            // automatic node orientation is inherited
+            return parentValue.getAutomaticNodeOrientation();
+        }
+
+        final Scene sceneValue = getScene();
+        if (sceneValue != null) {
+            return sceneValue.getEffectiveNodeOrientation();
+        }
+
+        return NodeOrientation.LEFT_TO_RIGHT;
+    }
+
     // Return true if the node needs to be mirrored.
     // A node has mirroring if the orientation differs from the parent
-    private boolean hasMirroring() {
-        Parent parent = getParent();
-        if (parent == null) {
-            return getEffectiveNodeOrientation(true) == NodeOrientation.RIGHT_TO_LEFT;
-        }
-        NodeOrientation orientation = getNodeOrientation(true);
-        if (orientation == NodeOrientation.INHERIT) return false;
-        return orientation != parent.getEffectiveNodeOrientation(true);
-    }
-    
+    // package private for testing
+    boolean hasMirroring() {
+        final Parent parentValue = getParent();
+
+        final NodeOrientation thisOrientation =
+                getAutomaticNodeOrientation();
+        final NodeOrientation parentOrientation =
+                (parentValue != null)
+                    ? parentValue.getAutomaticNodeOrientation()
+                    : NodeOrientation.LEFT_TO_RIGHT;
+
+        return thisOrientation != parentOrientation;
+    }
+
     /***************************************************************************
      *                                                                         *
      *                       Misc Seldom Used Properties                       *
--- a/javafx-ui-common/src/javafx/scene/Parent.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/Parent.java	Wed Jan 23 11:39:06 2013 -0500
@@ -409,14 +409,9 @@
             }
 
             try {
-                childSet.addAll(newNodes);
-                if (childSet.size() != newLength) {
-                    throw new IllegalArgumentException(
-                            constructExceptionMessage(
-                                "duplicate children added", null));
-                }
-
                 if (childrenModified) {
+                    // check individual children before duplication test
+                    // if done in this order, the exception is more specific
                     for (int i = newNodes.size() - 1; i >= 0; --i ) {
                         Node node = newNodes.get(i);
                         if (node == null) {
@@ -436,6 +431,13 @@
                         }
                     }
                 }
+
+                childSet.addAll(newNodes);
+                if (childSet.size() != newLength) {
+                    throw new IllegalArgumentException(
+                            constructExceptionMessage(
+                                "duplicate children added", null));
+                }
             } catch (RuntimeException e) {
                 //Return children to it's original state
                 childSet.clear();
@@ -1220,6 +1222,14 @@
         return ((PGGroup) impl_getPGNode());
     }
 
+    @Override
+    void nodeEffectiveOrientationChanged() {
+        super.nodeEffectiveOrientationChanged();
+
+        for (int i = 0, max = children.size(); i < max; ++i) {
+            children.get(i).parentEffectiveOrientationChanged();
+        }
+    }
 
     /***************************************************************************
      *                                                                         *
--- a/javafx-ui-common/src/javafx/scene/Scene.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/Scene.java	Wed Jan 23 11:39:06 2013 -0500
@@ -26,6 +26,7 @@
 
 package javafx.scene;
 
+import com.sun.javafx.scene.SceneHelper;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
@@ -290,6 +291,10 @@
     private Scene(Parent root, double width, double height,
             @Default("javafx.scene.paint.Color.WHITE") Paint fill,
             boolean depthBuffer) {
+        if (root == null) {
+            throw new NullPointerException("Root cannot be null");
+        }
+
         Toolkit.getToolkit().checkFxUserThread();
         styleManager = new StyleManager(this);
         setRoot(root);
@@ -327,11 +332,19 @@
                     return scene.snapshot(null).impl_getPlatformImage();
                 }
             });
-            Toolkit.setSceneAccessor(new Toolkit.SceneAccessor() {
-                @Override public void setPaused(boolean paused) {
-                    Scene.paused = paused;
-                }
-            });
+            SceneHelper.setSceneAccessor(
+                    new SceneHelper.SceneAccessor() {
+                        @Override
+                        public void setPaused(boolean paused) {
+                            Scene.paused = paused;
+                        }
+                        
+                        @Override
+                        public void parentEffectiveOrientationChanged(
+                                final Scene scene) {
+                            scene.parentEffectiveOrientationChanged();
+                        }
+                    });
         }
 
         // Reserve space for 30 nodes in the dirtyNodes set.
@@ -595,6 +608,7 @@
                     if (newWindow != null) {
                         impl_initPeer();
                     }
+                    parentEffectiveOrientationChanged();
 
                     oldWindow = newWindow;
                 }
@@ -5570,7 +5584,8 @@
 
     
     private ObjectProperty<NodeOrientation> nodeOrientation;
-    
+    private NodeOrientation effectiveNodeOrientation;
+
     public final void setNodeOrientation(NodeOrientation orientation) {
         nodeOrientationProperty().set(orientation);
     }
@@ -5593,12 +5608,12 @@
      */
     public final ObjectProperty<NodeOrientation> nodeOrientationProperty() {
         if (nodeOrientation == null) {
-            nodeOrientation = new StyleableObjectProperty<NodeOrientation>(NodeOrientation.INHERIT) {       
+            nodeOrientation = new StyleableObjectProperty<NodeOrientation>(defaultNodeOrientation) {
                 @Override
                 protected void invalidated() {
-                    
-                }
-                
+                    sceneEffectiveOrientationChanged();
+                }
+
                 @Override
                 public Object getBean() {
                     return Scene.this;
@@ -5620,6 +5635,25 @@
     }
 
     public final NodeOrientation getEffectiveNodeOrientation() {
+        if (effectiveNodeOrientation == null) {
+            effectiveNodeOrientation = calcEffectiveNodeOrientation();
+        }
+
+        return effectiveNodeOrientation;
+    }
+
+    private void parentEffectiveOrientationChanged() {
+        if (getNodeOrientation() == NodeOrientation.INHERIT) {
+            sceneEffectiveOrientationChanged();
+        }
+    }
+
+    private void sceneEffectiveOrientationChanged() {
+        effectiveNodeOrientation = null;
+        getRoot().parentEffectiveOrientationChanged();
+    }
+
+    private NodeOrientation calcEffectiveNodeOrientation() {
         NodeOrientation orientation = getNodeOrientation();
         if (orientation == NodeOrientation.INHERIT) {
             Window window = getWindow();
--- a/javafx-ui-common/src/javafx/scene/doc-files/cssref.html	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/doc-files/cssref.html	Wed Jan 23 11:39:06 2013 -0500
@@ -5413,7 +5413,7 @@
         target="_blank">http://www.w3.org/TR/css3-background/</a>
     </p>
     <hr>
-    <p> <small>Copyright (c) 2008, 2012 Oracle and/or its affiliates. All
+    <p> <small>Copyright (c) 2008, 2013 Oracle and/or its affiliates. All
         rights reserved. Use is subject to <a href="http://download.oracle.com/javafx/2.0/api/license.html">license
           terms</a>. </small> </p>
     <br>
--- a/javafx-ui-common/src/javafx/scene/layout/Region.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/layout/Region.java	Wed Jan 23 11:39:06 2013 -0500
@@ -177,7 +177,7 @@
      * @param min The minimum bound
      * @param pref The value to be clamped between the min and max
      * @param max the maximum bound
-     * @return
+     * @return the size bounded by min, pref, and max.
      */
     static double boundedSize(double min, double pref, double max) {
         double a = pref >= min ? pref : min;
@@ -1100,7 +1100,7 @@
         setHeight(height);
         PlatformLogger logger = Logging.getLayoutLogger();
         if (logger.isLoggable(PlatformLogger.FINER)) {
-            logger.finer(this.toString()+" resized to "+width+" x "+height);
+            logger.finer(this.toString() + " resized to " + width + " x " + height);
         }
     }
 
@@ -1246,8 +1246,8 @@
      * @return the computed preferred width for this region
      */
     @Override protected double computePrefWidth(double height) {
-        double pwidth = super.computePrefWidth(height);
-        return getInsets().getLeft() + pwidth + getInsets().getRight();
+        final double w = super.computePrefWidth(height);
+        return getInsets().getLeft() + w + getInsets().getRight();
     }
 
     /**
@@ -1260,8 +1260,8 @@
      * @return the computed preferred height for this region
      */
     @Override protected double computePrefHeight(double width) {
-        double pheight = super.computePrefHeight(width);
-        return getInsets().getTop() + pheight + getInsets().getBottom();
+        final double h = super.computePrefHeight(width);
+        return getInsets().getTop() + h + getInsets().getBottom();
     }
 
     /**
@@ -1928,19 +1928,24 @@
         // occurred, so there is no need to test against bound again. We know that the
         // point (localX, localY) falls within the bounds of this node, now we need
         // to determine if it falls within the geometry of this node.
+        // Also note that because Region defaults pickOnBounds to true, this code is
+        // not usually executed. It will only be executed if pickOnBounds is set to false.
 
-        // Establish our top-left and bottom-right corners
-        double bx0 = 0;
-        double by0 = 0;
-        double bx1 = getWidth();
-        double by1 = getHeight();
+        final double x2 = getWidth();
+        final double y2 = getHeight();
+        // Figure out what the maximum possible radius value is.
+        final double maxRadius = Math.min(x2 / 2.0, y2 / 2.0);
 
-        // First check the shape. If it is specified, then we just check the shape
-        // itself. Note that this is not 100% accurate, as the bounds / fills used
-        // for the shape may extend beyond it, but I don't have a quick alternative
-        // handy to use.
-        if (_shape != null && isScaleShape() == false) {
-            return _shape.contains(localX,localY);
+        // First check the shape. Shape could be impacted by scaleShape & positionShape properties.
+        // This is going to be ugly! The problem is that basically all the scale / position operations
+        // have to be implemented here in Region, whereas right now they are all implemented in
+        // NGRegion. Drat. Basically I can't implement this properly until I have a way to get the
+        // geometry backing an arbitrary FX shape. For example, in this case I need an NGShape peer
+        // of this shape so that I can resize it as appropriate for these picking tests.
+        // Lacking that, for now, I will simply check the shape (so that picking works for pie charts)
+        // Bug is filed as RT-27775.
+        if (_shape != null) {
+            return _shape.contains(localX, localY);
         }
 
         // OK, there was no background shape, so I'm going to work on the principle of
@@ -1951,124 +1956,191 @@
             final List<BackgroundFill> fills = background.getFills();
             for (int i = 0, max = fills.size(); i < max; i++) {
                 final BackgroundFill bgFill = fills.get(i);
-                final Insets insets = bgFill.getInsets();
+                if (contains(localX, localY, 0, 0, x2, y2, bgFill.getInsets(), bgFill.getRadii(), maxRadius)) {
+                    return true;
+                }
+            }
+        }
 
-                final double rrx0 = bx0 + insets.getLeft();
-                final double rry0 = by0 + insets.getTop();
-                final double rrx1 = bx1 - insets.getRight();
-                final double rry1 = by1 - insets.getBottom();
-
-                // Check for trivial rejection - point is inside bounding rectangle
-                if (localX >= rrx0 && localY >= rry0 && localX < rrx1 && localY < rry1) {
-                    final CornerRadii rad = bgFill.getRadii();
-                    // TODO teach it how to handle vertical
-                    double tlr = rad.getTopLeftHorizontalRadius() / 2;
-                    double trr = rad.getTopRightHorizontalRadius() / 2;
-                    double blr = rad.getBottomLeftHorizontalRadius() / 2;
-                    double brr = rad.getBottomRightHorizontalRadius() / 2;
-
-                    // need to check if pt is outside rounded corners
-                    double x;
-                    double y;
-
-                    if (tlr != 0 && localX < rrx0 + tlr && localY < rry0 + tlr) {
-                        // pt is in top-left corner
-                        x = (localX - (rrx0 + tlr)) / tlr;
-                        y = (localY - (rry0 + tlr)) / tlr;
-
-                    } else if (blr != 0 && localX < rrx0 + blr && localY > rry1 - blr) {
-                        // pt is in bottom-left corner
-                        x = (localX - (rrx0 + blr)) / blr;
-                        y = (localY - (rry1 - blr)) / blr;
-
-                    } else if (trr != 0 && localX > rrx1 - trr && localY < rry0 + trr) {
-                        // pt is in top-right corner
-                        x = (localX - (rrx1 - trr)) / trr;
-                        y = (localY - (rry0 + trr)) / trr;
-
-                    } else if (brr != 0 && localX > rrx1 - brr && localY > rry1 - brr) {
-                        // pt is in bottom-right corner
-                        x = (localX - (rrx1 - brr)) / brr;
-                        y = (localY - (rry1 - brr)) / brr;
-
-                    } else {
-                        // pt within fill and not within area with a rounded corner either because
-                        // there are no rounded corners or the pt doesn't fall inside one
-                        // break and try background images, image border or stroke border
-                        // to determine if the pt is within the area.
-                        break;
-                    }
-
-                    if (x * x + y * y < .50) {
-                        // pt within rounded corner!
-                        return true;
-                    }
-                    // pt outside of rounded corner, so no hit within this fill
+        // If we are here then either there were no background fills or there were no background
+        // fills which contained the point, and the region is not defined by a shape.
+        final Border border = getBorder();
+        if (border != null) {
+            // Check all the stroke borders first. If the pick occurs on any stroke border
+            // then we consider the contains test to have passed. Semantically we will treat a Region
+            // with a border as if it were a rectangle with a stroke but no fill.
+            final List<BorderStroke> strokes = border.getStrokes();
+            for (int i=0, max=strokes.size(); i<max; i++) {
+                final BorderStroke strokeBorder = strokes.get(i);
+                if (contains(localX, localY, 0, 0, x2, y2, strokeBorder.getWidths(), false, strokeBorder.getInsets(),
+                             strokeBorder.getRadii(), maxRadius)) {
+                    return true;
                 }
             }
 
-            // If we are here, then we did not find a BackgroundFill which contains the
-            // point (localX, localY), so we need to check the background images. These are
-            // simpler in that we will simply check the position of the background image.
-            // TODO for now, I am simply going to compare the bounds, but really this should
-            // take into account the background position, repeatX, repeatY, etc.
-            if (localX >= bx0 &&
-                localX <= bx1 &&
-                localY >= by0 &&
-                localY <= by1) {
-                return true;
-            }
-        }
-
-        final Border border = getBorder();
-        if (border != null) {
-            // Check all the stroke borders first.
-            final List<BorderStroke> strokes = border.getStrokes();
-            for (int i = 0, max = strokes.size(); i < max; i++) {
-                BorderStroke strokeBorder = strokes.get(i);
-                // TODO This should take into account the border radii, but doesn't
-                final BorderWidths widths = strokeBorder.getWidths();
-                if (borderContains(localX, localY, bx0, by0, bx1, by1,
-                                   widths.getTop(), widths.getRight(), widths.getBottom(), widths.getLeft())) {
+            // Check the image borders. We treat the image border as though it is opaque.
+            final List<BorderImage> images = border.getImages();
+            for (int i = 0, max = images.size(); i < max; i++) {
+                final BorderImage borderImage = images.get(i);
+                if (contains(localX, localY, 0, 0, x2, y2, borderImage.getWidths(), borderImage.isFilled(),
+                             borderImage.getInsets(), CornerRadii.EMPTY, maxRadius)) {
                     return true;
                 }
-
-                    // TODO I'm not sure what these offsets were...?
-//                Insets offsets = strokeBorder.getOffsets();
-//                if (borderContains(localX, localY,
-//                        bx0 + offsets.getLeft(), by0 + offsets.getTop(),
-//                        bx1 - offsets.getRight(), by1 - offsets.getBottom(),
-//                        strokeBorder.getTopWidth(), strokeBorder.getRightWidth(),
-//                        strokeBorder.getBottomWidth(), strokeBorder.getLeftWidth())) {
-//                     return true;
-//                }
-            }
-
-            final List<BorderImage> images = border.getImages();
-            for (int i = 0, max = images.size(); i < max; i++) {
-                BorderImage borderImage = images.get(i);
-                // TODO not sure how to handle these
-//                Insets offsets = borderImage.getInsets();
-//                if (borderContains(localX, localY,
-//                        bx0 + offsets.getLeft(), by0 + offsets.getTop(),
-//                        bx1 - offsets.getRight(), by1 - offsets.getBottom(),
-//                        borderImage.getTopWidth(), borderImage.getRightWidth(),
-//                        borderImage.getBottomWidth(), borderImage.getLeftWidth())) {
-//                     return true;
-//                }
             }
         }
         return false;
     }
 
-    // tests to see if x,y is within border with top, right, bottom, left thicknesses
-    private boolean borderContains(double x, double y,
-                                   double bx0, double by0, double bx1, double by1,
-                                   double top, double right, double bottom, double left) {
+    /**
+     * Basically we will perform two contains tests. For a point to be on the stroke, it must
+     * be within the outermost edge of the stroke, but outside the innermost edge of the stroke.
+     * Unless it is filled, in which case it is really just a normal contains test.
+     *
+     * @param px        The x position of the point to test
+     * @param py        The y position of the point to test
+     * @param x1        The x1 position of the bounds to test
+     * @param y1        The y1 position of the bounds to test
+     * @param x2        The x2 position of the bounds to test
+     * @param y2        The y2 position of the bounds to test
+     * @param widths    The widths of the stroke on each side
+     * @param filled    Whether the area is filled or is just stroked
+     * @param insets    The insets to apply to (x1,y1)-(x2,y2) to get the final bounds to test
+     * @param rad       The corner radii to test with. Must not be null.
+     * @param maxRadius The maximum possible radius value
+     * @return True if (px, py) is within the stroke, taking into account insets and corner radii.
+     */
+    private boolean contains(final double px, final double py,
+                             final double x1, final double y1, final double x2, final double y2,
+                             BorderWidths widths, boolean filled,
+                             final Insets insets, final CornerRadii rad, final double maxRadius) {
+        if (filled) {
+            if (contains(px, py, x1, y1, x2, y2, insets, rad, maxRadius)) {
+                return true;
+            }
+        } else {
+            boolean insideOuterEdge = contains(px, py, x1, y1, x2, y2, insets, rad, maxRadius);
+            if (insideOuterEdge) {
+                boolean outsideInnerEdge = !contains(px, py,
+                    x1 + (widths.isLeftAsPercentage() ? getWidth() * widths.getLeft() : widths.getLeft()),
+                    y1 + (widths.isTopAsPercentage() ? getHeight() * widths.getTop() : widths.getTop()),
+                    x2 - (widths.isRightAsPercentage() ? getWidth() * widths.getRight() : widths.getRight()),
+                    y2 - (widths.isBottomAsPercentage() ? getHeight() * widths.getBottom() : widths.getBottom()),
+                    insets, rad, maxRadius);
+                if (outsideInnerEdge) return true;
+            }
+        }
+        return false;
+    }
 
-        return (((x >= bx0 && x <= bx0 + left || ((x >= bx1 - right) && x <= bx1)) && y >= by0 && y <= by1) ||
-                (((y >= by0 && y <= by0 + top) || ((y >= by1 - bottom) && y <= by1)) && x >= bx0 && x <= bx1));
+    /**
+     * Determines whether the point (px, py) is contained within the the bounds (x1, y1)-(x2, y2),
+     * after taking into account the insets and the corner radii.
+     *
+     * @param px        The x position of the point to test
+     * @param py        The y position of the point to test
+     * @param x1        The x1 position of the bounds to test
+     * @param y1        The y1 position of the bounds to test
+     * @param x2        The x2 position of the bounds to test
+     * @param y2        The y2 position of the bounds to test
+     * @param insets    The insets to apply to (x1,y1)-(x2,y2) to get the final bounds to test
+     * @param rad       The corner radii to test with. Must not be null.
+     * @param maxRadius The maximum possible radius value
+     * @return True if (px, py) is within the bounds, taking into account insets and corner radii.
+     */
+    private boolean contains(final double px, final double py,
+                             final double x1, final double y1, final double x2, final double y2,
+                             final Insets insets, CornerRadii rad, final double maxRadius) {
+        // These four values are the x0, y0, x1, y1 bounding box after
+        // having taken into account the insets of this particular
+        // background fill.
+        final double rrx0 = x1 + insets.getLeft();
+        final double rry0 = y1 + insets.getTop();
+        final double rrx1 = x2 - insets.getRight();
+        final double rry1 = y2 - insets.getBottom();
 
+        // Adjust based on whether it is % based radii
+        rad = normalize(rad);
+
+        // Check for trivial rejection - point is inside bounding rectangle
+        if (px >= rrx0 && py >= rry0 && px <= rrx1 && py <= rry1) {
+            // The point was within the index bounding box. Now we need to analyze the
+            // corner radii to see if the point lies within the corners or not. If the
+            // point is within a corner then we reject this one.
+            final double tlhr = Math.min(rad.getTopLeftHorizontalRadius(), maxRadius);
+            if (rad.isUniform() && tlhr == 0) {
+                // This is a simple square! Since we know the point is already within
+                // the insets of this fill, we can simply return true.
+                return true;
+            } else {
+                final double tlvr = Math.min(rad.getTopLeftVerticalRadius(), maxRadius);
+                final double trhr = Math.min(rad.getTopRightHorizontalRadius(), maxRadius);
+                final double trvr = Math.min(rad.getTopRightVerticalRadius(), maxRadius);
+                final double blhr = Math.min(rad.getBottomLeftHorizontalRadius(), maxRadius);
+                final double blvr = Math.min(rad.getBottomLeftVerticalRadius(), maxRadius);
+                final double brhr = Math.min(rad.getBottomRightHorizontalRadius(), maxRadius);
+                final double brvr = Math.min(rad.getBottomRightVerticalRadius(), maxRadius);
+
+                // The four corners can each be described as a quarter of an ellipse
+                double centerX, centerY, a, b;
+
+                if (px <= rrx0 + tlhr && py <= rry0 + tlvr) {
+                    // Point is in the top left corner
+                    centerX = rrx0 + tlhr;
+                    centerY = rry0 + tlvr;
+                    a = tlhr;
+                    b = tlvr;
+                } else if (px >= rrx1 - trhr && py <= rry0 + trvr) {
+                    // Point is in the top right corner
+                    centerX = rrx1 - trhr;
+                    centerY = rry0 + trvr;
+                    a = trhr;
+                    b = trvr;
+                } else if (px >= rrx1 - brhr && py >= rry1 - brvr) {
+                    // Point is in the bottom right corner
+                    centerX = rrx1 - brhr;
+                    centerY = rry1 - brvr;
+                    a = brhr;
+                    b = brvr;
+                } else if (px <= rrx0 + blhr && py >= rry1 - blvr) {
+                    // Point is in the bottom left corner
+                    centerX = rrx0 + blhr;
+                    centerY = rry1 - blvr;
+                    a = blhr;
+                    b = blvr;
+                } else {
+                    // The point must have been in the solid body someplace
+                    return true;
+                }
+
+                double x = px - centerX;
+                double y = py - centerY;
+                double result = ((x*x)/(a*a) + (y*y)/(b*b));
+                // The .0000001 is fudge to help in cases where double arithmetic isn't quite right
+                if (result - .0000001 <= 1) return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Direct copy of a method in NGRegion. If NGRegion were part of the core graphics module (coming!)
+     * then this method could be removed.
+     *
+     * @param radii    The radii.
+     * @return Normalized radii.
+     */
+    private CornerRadii normalize(CornerRadii radii) {
+        final double width = getWidth();
+        final double height = getHeight();
+        final double tlvr = radii.isTopLeftVerticalRadiusAsPercentage() ? height * radii.getTopLeftVerticalRadius() : radii.getTopLeftVerticalRadius();
+        final double tlhr = radii.isTopLeftHorizontalRadiusAsPercentage() ? width * radii.getTopLeftHorizontalRadius() : radii.getTopLeftHorizontalRadius();
+        final double trvr = radii.isTopRightVerticalRadiusAsPercentage() ? height * radii.getTopRightVerticalRadius() : radii.getTopRightVerticalRadius();
+        final double trhr = radii.isTopRightHorizontalRadiusAsPercentage() ? width * radii.getTopRightHorizontalRadius() : radii.getTopRightHorizontalRadius();
+        final double brvr = radii.isBottomRightVerticalRadiusAsPercentage() ? height * radii.getBottomRightVerticalRadius() : radii.getBottomRightVerticalRadius();
+        final double brhr = radii.isBottomRightHorizontalRadiusAsPercentage() ? width * radii.getBottomRightHorizontalRadius() : radii.getBottomRightHorizontalRadius();
+        final double blvr = radii.isBottomLeftVerticalRadiusAsPercentage() ? height * radii.getBottomLeftVerticalRadius() : radii.getBottomLeftVerticalRadius();
+        final double blhr = radii.isBottomLeftHorizontalRadiusAsPercentage() ? width * radii.getBottomLeftHorizontalRadius() : radii.getBottomLeftHorizontalRadius();
+        return new CornerRadii(tlhr, tlvr, trvr, trhr, brhr, brvr, blvr, blhr, false, false, false, false, false, false, false, false);
     }
 
     /**
--- a/javafx-ui-common/src/javafx/stage/PopupWindow.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/javafx/stage/PopupWindow.java	Wed Jan 23 11:39:06 2013 -0500
@@ -53,6 +53,7 @@
 import com.sun.javafx.event.EventRedirector;
 import com.sun.javafx.event.EventUtil;
 import com.sun.javafx.perf.PerformanceTracker;
+import com.sun.javafx.scene.SceneHelper;
 import com.sun.javafx.stage.FocusUngrabEvent;
 import com.sun.javafx.stage.PopupWindowPeerListener;
 import com.sun.javafx.stage.WindowCloseRequestHandler;
@@ -106,13 +107,14 @@
             };
 
     public PopupWindow() {
-        final Scene scene = new Scene(null);
+        final Scene scene = new Scene(new Group());
         scene.setFill(null);
         super.setScene(scene);
 
+        scene.getRoot().layoutBoundsProperty().addListener(rootBoundsListener);
         scene.rootProperty().addListener(
                 new InvalidationListener() {
-                    private Node oldRoot;
+                    private Node oldRoot = scene.getRoot();
 
                     @Override
                     public void invalidated(final Observable observable) {
@@ -133,7 +135,7 @@
                         }
                     }
                 });
-        scene.setRoot(new Group());
+        updateDimensions();
     }
 
     /**
@@ -359,6 +361,11 @@
             ((PopupWindow)owner).children.add(this);
         }
 
+        final Scene sceneValue = getScene();
+        if (sceneValue != null) {
+            SceneHelper.parentEffectiveOrientationChanged(sceneValue);
+        }
+
         // It is required that the root window exist and be visible to show the popup.
         if (getRootWindow(owner).isShowing()) {
             // We do show() first so that the width and height of the
@@ -449,16 +456,14 @@
 
     private void updateDimensions() {
         final Parent rootNode = getScene().getRoot();
-        if (rootNode != null) {
-            final Bounds layoutBounds = rootNode.getLayoutBounds();
+        final Bounds layoutBounds = rootNode.getLayoutBounds();
 
-            // update popup dimensions
-            setWidth(layoutBounds.getMaxX() - layoutBounds.getMinX());
-            setHeight(layoutBounds.getMaxY() - layoutBounds.getMinY());
-            // update transform
-            rootNode.setTranslateX(-layoutBounds.getMinX());
-            rootNode.setTranslateY(-layoutBounds.getMinY());
-        }
+        // update popup dimensions
+        setWidth(layoutBounds.getMaxX() - layoutBounds.getMinX());
+        setHeight(layoutBounds.getMaxY() - layoutBounds.getMinY());
+        // update transform
+        rootNode.setTranslateX(-layoutBounds.getMinX());
+        rootNode.setTranslateY(-layoutBounds.getMinY());
     }
 
     /**
--- a/javafx-ui-common/src/javafx/stage/Stage.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/javafx/stage/Stage.java	Wed Jan 23 11:39:06 2013 -0500
@@ -43,6 +43,7 @@
 import com.sun.javafx.beans.annotations.Default;
 import com.sun.javafx.collections.TrackableObservableList;
 import com.sun.javafx.robot.impl.FXRobotHelper;
+import com.sun.javafx.scene.SceneHelper;
 import com.sun.javafx.stage.StageHelper;
 import com.sun.javafx.stage.StagePeerListener;
 import com.sun.javafx.tk.TKPulseListener;
@@ -493,7 +494,11 @@
         }
 
         this.owner = owner;
-
+        
+        final Scene sceneValue = getScene();
+        if (sceneValue != null) {
+            SceneHelper.parentEffectiveOrientationChanged(sceneValue);
+        }
     }
 
     /**
--- a/javafx-ui-common/src/javafx/stage/StageStyle.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/src/javafx/stage/StageStyle.java	Wed Jan 23 11:39:06 2013 -0500
@@ -49,6 +49,16 @@
      * Defines a {@code Stage} style with a solid white background and minimal
      * platform decorations used for a utility window.
      */
-    UTILITY
+    UTILITY,
 
+    /**
+     * Defines a {@code Stage} style with platform decorations and eliminates the border between
+     * client area and decorations. The client area background is unified with the decorations.
+     * This is a conditional feature, to check if it is supported see
+     * {@link javafx.application.Platform#isSupported(javafx.application.ConditionalFeature)}.
+     * If the feature is not supported by the platform, this style downgrades to {@code StageStyle.DECORATED}
+     * <p>                                                   `
+     * NOTE: To see the effect the {@code Scene} covering the {@code Stage} should have {@code Color.TRANSPARENT}
+     */
+    UNIFIED
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/test/unit/com/sun/javafx/test/OrientationHelper.java	Wed Jan 23 11:39:06 2013 -0500
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.test;
+
+import java.util.List;
+import javafx.geometry.NodeOrientation;
+import javafx.scene.Node;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+
+public final class OrientationHelper {
+    private OrientationHelper() {
+    }
+
+    public interface StateEncoder {
+        char map(Scene scene);
+        char map(Node node);
+    }
+
+    public static void updateOrientation(final Scene scene,
+                                         final String updateString) {
+        final NodeOrientation update =
+                decode(updateString.charAt(0));
+        if (update != null) {
+            scene.setNodeOrientation(update);
+        }
+
+        final Node rootNode = scene.getRoot();
+        if (rootNode != null) {
+            updateOrientation(rootNode, updateString, 1);
+        }
+    }
+
+    public static String collectState(final Scene scene,
+                                      final StateEncoder encoder) {
+        final StringBuilder dest = new StringBuilder();
+        collectState(dest, scene, encoder);
+        return dest.toString();
+    }
+
+    public static String collectState(final Node node,
+                                      final StateEncoder encoder) {
+        final StringBuilder dest = new StringBuilder();
+        collectState(dest, node, encoder);
+        return dest.toString();
+    }
+
+    private static int updateOrientation(final Node node,
+                                         final String updateString,
+                                         final int index) {
+        final NodeOrientation update =
+                decode(updateString.charAt(index));
+        if (update != null) {
+            node.setNodeOrientation(update);
+        }
+
+        int nextIndex = index + 1;
+        if (node instanceof Parent) {
+            final List<Node> childNodes =
+                    ((Parent) node).getChildrenUnmodifiable();
+            for (final Node childNode: childNodes) {
+                nextIndex = updateOrientation(childNode, updateString,
+                                              nextIndex);
+            }
+        }
+
+        return nextIndex;
+    }
+
+    private static NodeOrientation decode(final char updateChar) {
+        switch (updateChar) {
+            case '.':
+                return null;
+            case 'L':
+                return NodeOrientation.LEFT_TO_RIGHT;
+            case 'R':
+                return NodeOrientation.RIGHT_TO_LEFT;
+            case 'I':
+                return NodeOrientation.INHERIT;
+            default:
+                throw new IllegalArgumentException("Invalid update character");
+        }
+    }
+
+    private static void collectState(final StringBuilder dest,
+                                     final Scene scene,
+                                     final StateEncoder encoder) {
+        dest.append(encoder.map(scene));
+        final Node rootNode = scene.getRoot();
+        if (rootNode != null) {
+            collectState(dest, rootNode, encoder);
+        }
+    }
+
+    private static void collectState(final StringBuilder dest,
+                                     final Node node,
+                                     final StateEncoder encoder) {
+        dest.append(encoder.map(node));
+        if (node instanceof Parent) {
+            final List<Node> childNodes =
+                    ((Parent) node).getChildrenUnmodifiable();
+            for (final Node childNode: childNodes) {
+                collectState(dest, childNode, encoder);
+            }
+        }
+    }
+
+}
--- a/javafx-ui-common/test/unit/javafx/scene/NodeTest.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/test/unit/javafx/scene/NodeTest.java	Wed Jan 23 11:39:06 2013 -0500
@@ -61,6 +61,9 @@
 
 import com.sun.javafx.scene.DirtyBits;
 import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.test.objects.TestScene;
+import com.sun.javafx.test.objects.TestStage;
+import javafx.geometry.BoundingBox;
 import javafx.scene.shape.Circle;
 /**
  * Tests various aspects of Node.
@@ -1027,4 +1030,41 @@
         assertEquals(100.0, sc.getRadius(), 0.01);
         
     }
+
+    @Test
+    public void testLocalToScreen() {
+        Rectangle rect = new Rectangle();
+        Group g = new Group(rect);
+
+        rect.setTranslateX(10);
+        rect.setTranslateY(20);
+
+        TestScene scene = new TestScene(new Group(rect));
+        final TestStage testStage = new TestStage("");
+        testStage.setX(100);
+        testStage.setY(200);
+        scene.set_window(testStage);
+
+        assertEquals(new Point2D(111, 222), rect.localToScreen(new Point2D(1, 2)));
+        assertEquals(new BoundingBox(111, 222, 3, 4), rect.localToScreen(new BoundingBox(1, 2, 3, 4)));
+    }
+
+    @Test
+    public void testScreenToLocal() {
+        Rectangle rect = new Rectangle();
+        Group g = new Group(rect);
+
+        rect.setTranslateX(10);
+        rect.setTranslateY(20);
+
+        TestScene scene = new TestScene(new Group(rect));
+        final TestStage testStage = new TestStage("");
+        testStage.setX(100);
+        testStage.setY(200);
+        scene.set_window(testStage);
+
+        assertEquals(new Point2D(1, 2), rect.screenToLocal(new Point2D(111, 222)));
+        assertEquals(new BoundingBox(1, 2, 3, 4), rect.screenToLocal(new BoundingBox(111, 222, 3, 4)));
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/test/unit/javafx/scene/Node_effectiveOrientation_Test.java	Wed Jan 23 11:39:06 2013 -0500
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.scene;
+
+import com.sun.javafx.test.OrientationHelper;
+import com.sun.javafx.test.OrientationHelper.StateEncoder;
+import java.util.Arrays;
+import java.util.Collection;
+import javafx.geometry.NodeOrientation;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public final class Node_effectiveOrientation_Test {
+    private final Scene testScene;
+    private final String orientationUpdate;
+    private final String expectedOrientation;
+
+    private static Scene lriiliScene() {
+        return ltrScene(rtlGroup(inhGroup(inhGroup(ltrGroup(inhGroup())))));
+    }
+
+    private static Scene liirliPrecachedScene() {
+        final Scene scene =
+                ltrScene(inhGroup(inhGroup(rtlGroup(ltrGroup(inhGroup())))));
+        // force caching
+        collectOrientation(scene);
+        return scene;
+    }
+
+    private static Scene riirliPlugedPrecachedScenegraphScene() {
+        final Group root =
+                inhGroup(inhGroup(rtlGroup(ltrGroup(inhGroup()))));
+        // force caching
+        collectOrientation(root);
+
+        final Scene scene = new Scene(new Group());
+        scene.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT);
+        scene.setRoot(root);
+
+        return scene;
+    }
+
+    /*
+     * Parameters: [testScene], [orientationUpdate], [expectedOrientation]
+     */
+    @Parameters
+    public static Collection data() {
+        return Arrays.asList(
+                new Object[][] {
+                        { lriiliScene(), "......", "LRRRLL" },
+                        { lriiliScene(), ".I....", "LLLLLL" },
+                        { lriiliScene(), "...L..", "LRRLLL" },
+                        { lriiliScene(), "....I.", "LRRRRR" },
+                        { lriiliScene(), "RIIIII", "RRRRRR" },
+
+                        { liirliPrecachedScene(), "......", "LLLRLL" },
+                        { liirliPrecachedScene(), "R.....", "RRRRLL" },
+                        { liirliPrecachedScene(), "...I..", "LLLLLL" },
+                        { liirliPrecachedScene(), "R..IR.", "RRRRRR" },
+
+                        {
+                            riirliPlugedPrecachedScenegraphScene(),
+                            "......", "RRRRLL"
+                        }
+                    });
+    }
+
+    public Node_effectiveOrientation_Test(
+            final Scene testScene,
+            final String orientationUpdate,
+            final String expectedOrientation) {
+        this.testScene = testScene;
+        this.orientationUpdate = orientationUpdate;
+        this.expectedOrientation = expectedOrientation;
+    }
+
+    @Test
+    public void effectiveOrientationTest() {
+        OrientationHelper.updateOrientation(testScene, orientationUpdate);
+        assertOrientation(testScene, expectedOrientation);
+    }
+
+    private static Scene ltrScene(final Parent rootNode) {
+        final Scene scene = new Scene(rootNode);
+        scene.setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT);
+        return scene;
+    }
+
+    private static Group ltrGroup(final Node... childNodes) {
+        return group(NodeOrientation.LEFT_TO_RIGHT, childNodes);
+    }
+
+    private static Group rtlGroup(final Node... childNodes) {
+        return group(NodeOrientation.RIGHT_TO_LEFT, childNodes);
+    }
+
+    private static Group inhGroup(final Node... childNodes) {
+        return group(NodeOrientation.INHERIT, childNodes);
+    }
+
+    private static Group group(final NodeOrientation nodeOrientation,
+                               final Node... childNodes) {
+        final Group group = new Group();
+        group.setNodeOrientation(nodeOrientation);
+        group.getChildren().setAll(childNodes);
+
+        return group;
+    }
+
+    private static void assertOrientation(
+            final Scene scene,
+            final String expectedOrientation) {
+        final String actualOrientation = collectOrientation(scene);
+        Assert.assertEquals("Orientation mismatch",
+                            expectedOrientation, actualOrientation);
+    }
+
+    private static final StateEncoder EFFECTIVE_ORIENTATION_ENCODER =
+            new StateEncoder() {
+                @Override
+                public char map(final Scene scene) {
+                    return map(scene.getEffectiveNodeOrientation());
+                }
+
+                @Override
+                public char map(final Node node) {
+                    return map(node.getEffectiveNodeOrientation());
+                }
+
+                private char map(final NodeOrientation effectiveOrientation) {
+                    switch (effectiveOrientation) {
+                        case LEFT_TO_RIGHT:
+                            return 'L';
+                        case RIGHT_TO_LEFT:
+                            return 'R';
+                        default:
+                            throw new IllegalArgumentException(
+                                          "Invalid orientation");
+                    }
+                }
+            };
+
+    private static String collectOrientation(final Scene scene) {
+        return OrientationHelper.collectState(scene,
+                                              EFFECTIVE_ORIENTATION_ENCODER);
+    }
+
+    private static String collectOrientation(final Node node) {
+        return OrientationHelper.collectState(node,
+                                              EFFECTIVE_ORIENTATION_ENCODER);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/test/unit/javafx/scene/Node_hasMirroring_Test.java	Wed Jan 23 11:39:06 2013 -0500
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.scene;
+
+import com.sun.javafx.test.OrientationHelper;
+import com.sun.javafx.test.OrientationHelper.StateEncoder;
+import java.util.Arrays;
+import java.util.Collection;
+import javafx.geometry.NodeOrientation;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public final class Node_hasMirroring_Test {
+    private final Scene testScene;
+    private final String orientationUpdate;
+    private final String expectedMirroring;
+
+    private static Scene lriiliScene() {
+        return ltrScene(
+                   rtlAutGroup(
+                       inhAutGroup(
+                           inhAutGroup(
+                               ltrAutGroup(
+                                   inhAutGroup())))));
+    }
+
+    private static Scene lrIiliScene() {
+        return ltrScene(
+                   rtlAutGroup(
+                       inhManGroup(
+                           inhAutGroup(
+                               ltrAutGroup(
+                                   inhAutGroup())))));
+    }
+
+    private static Scene lrLRlrScene() {
+        return ltrScene(
+                   rtlAutGroup(
+                       ltrManGroup(
+                           rtlManGroup(
+                               ltrAutGroup(
+                                   rtlAutGroup())))));
+    }
+
+    /*
+     * Parameters: [testScene], [orientationUpdate], [expectedMirroring]
+     */
+    @Parameters
+    public static Collection data() {
+        return Arrays.asList(
+                new Object[][] {
+                        { lriiliScene(), "......", ".M..M." }, // LRRRLL
+                        { lriiliScene(), ".I....", "......" }, // LLLLLL
+                        { lriiliScene(), "...L..", ".M.M.." }, // LRRLLL
+                        { lriiliScene(), "....I.", ".M...." }, // LRRRRR
+                        { lriiliScene(), "RIIIII", ".M...." }, // RRRRRR
+
+                        /* effective: LRRRLL, automatic: LRLLLL */
+                        { lrIiliScene(), "......", ".MM..." },
+                        /* effective: LRLRLR, automatic: LRLLLR */
+                        { lrLRlrScene(), "......", ".MM..M" }
+                    });
+    }
+
+    public Node_hasMirroring_Test(
+            final Scene testScene,
+            final String orientationUpdate,
+            final String expectedMirroring) {
+        this.testScene = testScene;
+        this.orientationUpdate = orientationUpdate;
+        this.expectedMirroring = expectedMirroring;
+    }
+
+    @Test
+    public void hasMirroringTest() {
+        OrientationHelper.updateOrientation(testScene, orientationUpdate);
+        assertMirroring(testScene, expectedMirroring);
+    }
+
+    private static Scene ltrScene(final Parent rootNode) {
+        final Scene scene = new Scene(rootNode);
+        scene.setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT);
+        return scene;
+    }
+
+    private static Group ltrAutGroup(final Node... childNodes) {
+        return autGroup(NodeOrientation.LEFT_TO_RIGHT, childNodes);
+    }
+
+    private static Group rtlAutGroup(final Node... childNodes) {
+        return autGroup(NodeOrientation.RIGHT_TO_LEFT, childNodes);
+    }
+
+    private static Group inhAutGroup(final Node... childNodes) {
+        return autGroup(NodeOrientation.INHERIT, childNodes);
+    }
+
+    private static Group ltrManGroup(final Node... childNodes) {
+        return manGroup(NodeOrientation.LEFT_TO_RIGHT, childNodes);
+    }
+
+    private static Group rtlManGroup(final Node... childNodes) {
+        return manGroup(NodeOrientation.RIGHT_TO_LEFT, childNodes);
+    }
+
+    private static Group inhManGroup(final Node... childNodes) {
+        return manGroup(NodeOrientation.INHERIT, childNodes);
+    }
+
+    private static Group autGroup(final NodeOrientation nodeOrientation,
+                                  final Node... childNodes) {
+        final Group group = new Group();
+        group.setNodeOrientation(nodeOrientation);
+        group.getChildren().setAll(childNodes);
+
+        return group;
+    }
+
+    private static Group manGroup(final NodeOrientation nodeOrientation,
+                                  final Node... childNodes) {
+        final Group group = new Group() {
+                                    @Override
+                                    public boolean isAutomaticallyMirrored() {
+                                        return false;
+                                    }
+                                };
+        group.setNodeOrientation(nodeOrientation);
+        group.getChildren().setAll(childNodes);
+
+        return group;
+    }
+
+    private static void assertMirroring(
+            final Scene scene,
+            final String expectedMirroring) {
+        final String actualMirroring = collectMirroring(scene);
+        Assert.assertEquals("Mirroring mismatch",
+                            expectedMirroring, actualMirroring);
+    }
+
+    private static final StateEncoder HAS_MIRRORING_ENCODER =
+            new StateEncoder() {
+                @Override
+                public char map(final Scene scene) {
+                    // no mirroring on scene
+                    return map(false);
+                }
+
+                @Override
+                public char map(final Node node) {
+                    return map(node.hasMirroring());
+                }
+
+                private char map(final boolean hasMirroring) {
+                    return hasMirroring ? 'M' : '.';
+                }
+            };
+
+    private static String collectMirroring(final Scene scene) {
+        return OrientationHelper.collectState(scene, HAS_MIRRORING_ENCODER);
+    }
+}
--- a/javafx-ui-common/test/unit/javafx/scene/ParentTest.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/test/unit/javafx/scene/ParentTest.java	Wed Jan 23 11:39:06 2013 -0500
@@ -371,6 +371,12 @@
         g.getChildren().add(null);
     }
 
+    @Test(expected=NullPointerException.class)
+    public void testNullCheckIsDoneBeforeTestForDuplicates() {
+        Group g = new Group();
+        g.getChildren().addAll(null, new Rectangle(), null);
+    }
+
     @Test(expected=IllegalArgumentException.class)
     public void testAddingClipNodeTwice() {
         Group g = new Group();
--- a/javafx-ui-common/test/unit/javafx/scene/SceneTest.java	Wed Jan 23 18:53:46 2013 +0400
+++ b/javafx-ui-common/test/unit/javafx/scene/SceneTest.java	Wed Jan 23 11:39:06 2013 -0500
@@ -40,7 +40,9 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 /**
  * Tests various aspects of Scene.
@@ -131,14 +133,16 @@
      *                          Scene Content Tests                            *
      *                                                                         *
      **************************************************************************/
-    //TODO(aim): fix once we allow null roots
-//    @Test
-//    public void testDefaultRoot() {
-//        Scene scene = new Scene();
-//
-//        assertTrue(scene.getRoot() != null);
-//        assertTrue(scene.getRoot() instanceof Group);
-//    }
+    @Test(expected=NullPointerException.class)
+    public void testNullRoot() {
+        Scene scene = new Scene(null);
+    }
+
+    @Test(expected=NullPointerException.class)
+    public void testSetNullRoot() {
+        Scene scene = new Scene(new Group());
+        scene.setRoot(null);
+    }
 
     @Test
     public void testRootInitializedInConstructor() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/test/unit/javafx/scene/layout/RegionPickTest.java	Wed Jan 23 11:39:06 2013 -0500
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.scene.layout;
+
+import javafx.geometry.Insets;
+import javafx.scene.paint.Color;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for Region picking. By default, Region has pickOnBounds set to true, so picking
+ * anything within the bounds of the region will return true, anything outside the bounds
+ * false. However, due to RT-25066, the bounds of a region defined by a shape will be 0x0,
+ * in which case it will never be picked.
+ *
+ * If pickOnBounds is false, then an entire different code path is executed. We don't care
+ * whether a fill is transparent or not (transparent fills can still cause the region to
+ * pick), but we do care about picking within the "shape" and picking within the rounded
+ * rectangle which is created as a result of background fills / background images.
+ *
+ * In the case of background images, we don't care about whether we are picking a transparent
+ * pixel or not, but just based on the insets etc (since we don't have a good means for testing
+ * the image pixel value).
+ */
+public class RegionPickTest {
+    private static final double X = 0;
+    private static final double Y = 0;
+    private static final double WIDTH = 100;
+    private static final double HEIGHT = 100;
+    private static final double CENTER_X = X + (WIDTH / 2.0);
+    private static final double CENTER_Y = Y + (HEIGHT / 2.0);
+    private static final double LEFT_OF = X - 10;
+    private static final double ABOVE = Y - 10;
+    private static final double RIGHT_OF = X + WIDTH + 10;
+    private static final double BELOW = Y + HEIGHT + 10;
+
+    private Region region;
+
+    @Before public void setup() {
+        region = new Region();
+        region.resizeRelocate(X, Y, WIDTH, HEIGHT);
+        region.setPickOnBounds(false);
+    }
+
+    /**************************************************************************
+     *                                                                        *
+     * Set of tests to ensure that picking within / without a region with     *
+     * pickOnBounds set to true (the normal default) results in expected      *
+     * behavior (pick when within bounds, not when without bounds)            *
+     *                                                                        *
+     *************************************************************************/
+
+    @Test public void pickingNormalRegion() {
+        region.setPickOnBounds(true);
+        assertFalse(region.contains(LEFT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, ABOVE));
+        assertFalse(region.contains(RIGHT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, BELOW));
+        assertTrue(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    /**************************************************************************
+     *                                                                        *
+     * Test for a Region which has no fills of any kind, but has              *
+     * pickOnBounds set to false. Such a Region should never pick.            *
+     *                                                                        *
+     *************************************************************************/
+
+    @Test public void pickingEmptyRegionDoesNotWork() {
+        assertFalse(region.contains(LEFT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, ABOVE));
+        assertFalse(region.contains(RIGHT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, BELOW));
+        assertFalse(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    /**************************************************************************
+     *                                                                        *
+     * Test behavior when picking a region with fills, but no shape or border *
+     * or images.                                                             *
+     *                                                                        *
+     *************************************************************************/
+
+    @Test public void pickingRectangularFillWorks() {
+        region.setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY)));
+        assertFalse(region.contains(LEFT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, ABOVE));
+        assertFalse(region.contains(RIGHT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, BELOW));
+        assertTrue(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularFillWithInsetsWorks() {
+        // With insets of 10, we ought to not pick inside the region until we get to position 10
+        region.setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, new Insets(10))));
+        assertFalse(region.contains(X + 9, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, Y + 9));
+        assertFalse(region.contains(X + WIDTH - 9, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, Y + HEIGHT - 9));
+        assertTrue(region.contains(X + 10, CENTER_Y));
+        assertTrue(region.contains(CENTER_X, Y + 10));
+        assertTrue(region.contains(X + WIDTH - 10, CENTER_Y));
+        assertTrue(region.contains(CENTER_X, Y + HEIGHT - 10));
+        assertTrue(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularFillWithUniformRadiusWorks() {
+        region.setBackground(new Background(new BackgroundFill(Color.RED, new CornerRadii(10), Insets.EMPTY)));
+        // Check points in the top-left corner area
+        assertTrue(region.contains(X, Y + 10));
+        assertTrue(region.contains(X + 10, Y));
+        assertTrue(region.contains(X + 10 - (10 * Math.cos(45)), Y + 10 - (10 * Math.sin(45))));
+        assertTrue(region.contains(X + 10 - (9 * Math.cos(45)), Y + 10 - (9 * Math.sin(45))));
+        assertFalse(region.contains(X + 10 - (11 * Math.cos(45)), Y + 10 - (11 * Math.sin(45))));
+        // Check points in the top-right corner area
+        assertTrue(region.contains(X + WIDTH, Y + 10));
+        assertTrue(region.contains(X + WIDTH - 10, Y));
+        assertTrue(region.contains(X + WIDTH - 10 + (10 * Math.cos(45)), Y + 10 - (10 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 10 + (9 * Math.cos(45)), Y + 10 - (9 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 10 + (11 * Math.cos(45)), Y + 10 - (11 * Math.sin(45))));
+        // Check points in the bottom-right corner area
+        assertTrue(region.contains(X + WIDTH, Y + HEIGHT - 10));
+        assertTrue(region.contains(X + WIDTH - 10, Y + HEIGHT));
+        assertTrue(region.contains(X + WIDTH - 10 + (10 * Math.cos(45)), Y + HEIGHT - 10 + (10 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 10 + (9 * Math.cos(45)), Y + HEIGHT - 10 + (9 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 10 + (11 * Math.cos(45)), Y + HEIGHT - 10 + (11 * Math.sin(45))));
+        // Check points in the bottom-left corner area
+        assertTrue(region.contains(X, Y + HEIGHT - 10));
+        assertTrue(region.contains(X + 10, Y + HEIGHT));
+        assertTrue(region.contains(X + 10 - (10 * Math.cos(45)), Y + HEIGHT - 10 + (10 * Math.sin(45))));
+        assertTrue(region.contains(X + 10 - (9 * Math.cos(45)), Y + HEIGHT - 10 + (9 * Math.sin(45))));
+        assertFalse(region.contains(X + 10 - (11 * Math.cos(45)), Y + HEIGHT - 10 + (11 * Math.sin(45))));
+        // Check the center
+        assertTrue(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularFillWithUniformRadiusWithInsetsWorks() {
+        region.setBackground(new Background(new BackgroundFill(Color.RED, new CornerRadii(10), new Insets(10))));
+        // Check points in the top-left corner area
+        assertTrue(region.contains(X + 10, Y + 20));
+        assertTrue(region.contains(X + 20, Y + 10));
+        assertTrue(region.contains(X + 20 - (10 * Math.cos(45)), Y + 20 - (10 * Math.sin(45))));
+        assertTrue(region.contains(X + 20 - (9 * Math.cos(45)), Y + 20 - (9 * Math.sin(45))));
+        assertFalse(region.contains(X + 20 - (11 * Math.cos(45)), Y + 20 - (11 * Math.sin(45))));
+        // Check points in the top-right corner area
+        assertTrue(region.contains(X + WIDTH - 10, Y + 20));
+        assertTrue(region.contains(X + WIDTH - 20, Y + 10));
+        assertTrue(region.contains(X + WIDTH - 20 + (10 * Math.cos(45)), Y + 20 - (10 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 20 + (9 * Math.cos(45)), Y + 20 - (9 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 20 + (11 * Math.cos(45)), Y + 20 - (11 * Math.sin(45))));
+        // Check points in the bottom-right corner area
+        assertTrue(region.contains(X + WIDTH - 10, Y + HEIGHT - 20));
+        assertTrue(region.contains(X + WIDTH - 20, Y + HEIGHT - 10));
+        assertTrue(region.contains(X + WIDTH - 20 + (10 * Math.cos(45)), Y + HEIGHT - 20 + (10 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 20 + (9 * Math.cos(45)), Y + HEIGHT - 20 + (9 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 20 + (11 * Math.cos(45)), Y + HEIGHT - 20 + (11 * Math.sin(45))));
+        // Check points in the bottom-left corner area
+        assertTrue(region.contains(X + 10, Y + HEIGHT - 20));
+        assertTrue(region.contains(X + 20, Y + HEIGHT - 10));
+        assertTrue(region.contains(X + 20 - (10 * Math.cos(45)), Y + HEIGHT - 20 + (10 * Math.sin(45))));
+        assertTrue(region.contains(X + 20 - (9 * Math.cos(45)), Y + HEIGHT - 20 + (9 * Math.sin(45))));
+        assertFalse(region.contains(X + 20 - (11 * Math.cos(45)), Y + HEIGHT - 20 + (11 * Math.sin(45))));
+        // Check the center
+        assertTrue(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    // test with really really large corner radius
+    @Test public void pickingRectangularFillWithUniformVERYLARGERadiusWorks() {
+        region.setBackground(new Background(new BackgroundFill(Color.RED, new CornerRadii(10000000), Insets.EMPTY)));
+        // This produces an effective radius of 50 due to my width/height being 100x100
+        // Check points in the top-left corner area
+        assertTrue(region.contains(X, Y + 50));
+        assertTrue(region.contains(X + 50, Y));
+        assertTrue(region.contains(X + 50 - (50 * Math.cos(45)), Y + 50 - (50 * Math.sin(45))));
+        assertTrue(region.contains(X + 50 - (49 * Math.cos(45)), Y + 50 - (49 * Math.sin(45))));
+        assertFalse(region.contains(X + 50 - (51 * Math.cos(45)), Y + 50 - (51 * Math.sin(45))));
+        // Check points in the top-right corner area
+        assertTrue(region.contains(X + WIDTH, Y + 50));
+        assertTrue(region.contains(X + WIDTH - 50, Y));
+        assertTrue(region.contains(X + WIDTH - 50 + (50 * Math.cos(45)), Y + 50 - (50 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 50 + (49 * Math.cos(45)), Y + 50 - (49 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 50 + (51 * Math.cos(45)), Y + 50 - (51 * Math.sin(45))));
+        // Check points in the bottom-right corner area
+        assertTrue(region.contains(X + WIDTH, Y + HEIGHT - 50));
+        assertTrue(region.contains(X + WIDTH - 50, Y + HEIGHT));
+        assertTrue(region.contains(X + WIDTH - 50 + (50 * Math.cos(45)), Y + HEIGHT - 50 + (50 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 50 + (49 * Math.cos(45)), Y + HEIGHT - 50 + (49 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 50 + (51 * Math.cos(45)), Y + HEIGHT - 50 + (51 * Math.sin(45))));
+        // Check points in the bottom-left corner area
+        assertTrue(region.contains(X, Y + HEIGHT - 50));
+        assertTrue(region.contains(X + 50, Y + HEIGHT));
+        assertTrue(region.contains(X + 50 - (50 * Math.cos(45)), Y + HEIGHT - 50 + (50 * Math.sin(45))));
+        assertTrue(region.contains(X + 50 - (49 * Math.cos(45)), Y + HEIGHT - 50 + (49 * Math.sin(45))));
+        assertFalse(region.contains(X + 50 - (51 * Math.cos(45)), Y + HEIGHT - 50 + (51 * Math.sin(45))));
+        // Check the center
+        assertTrue(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularFillWithIndependentRadiusWorks() {
+        region.setBackground(new Background(new BackgroundFill(Color.RED, new CornerRadii(1, 2, 3, 4, false),
+                                                               Insets.EMPTY)));
+        // Check points in the top-left corner area
+        assertTrue(region.contains(X, Y + 1));
+        assertTrue(region.contains(X + 1, Y));
+        assertTrue(region.contains(X + 1 - (1 * Math.cos(45)), Y + 1 - (1 * Math.sin(45))));
+        assertTrue(region.contains(X + 1 - (.5 * Math.cos(45)), Y + 1 - (.5 * Math.sin(45))));
+        assertFalse(region.contains(X + 1 - (2 * Math.cos(45)), Y + 1 - (2 * Math.sin(45))));
+        // Check points in the top-right corner area
+        assertTrue(region.contains(X + WIDTH, Y + 2));
+        assertTrue(region.contains(X + WIDTH - 2, Y));
+        assertTrue(region.contains(X + WIDTH - 2 + (2 * Math.cos(45)), Y + 2 - (2 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 2 + (1 * Math.cos(45)), Y + 2 - (1 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 2 + (3 * Math.cos(45)), Y + 2 - (3 * Math.sin(45))));
+        // Check points in the bottom-right corner area
+        assertTrue(region.contains(X + WIDTH, Y + HEIGHT - 3));
+        assertTrue(region.contains(X + WIDTH - 3, Y + HEIGHT));
+        assertTrue(region.contains(X + WIDTH - 3 + (3 * Math.cos(45)), Y + HEIGHT - 3 + (3 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 3 + (2 * Math.cos(45)), Y + HEIGHT - 3 + (2 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 3 + (4 * Math.cos(45)), Y + HEIGHT - 3 + (4 * Math.sin(45))));
+        // Check points in the bottom-left corner area
+        assertTrue(region.contains(X, Y + HEIGHT - 4));
+        assertTrue(region.contains(X + 4, Y + HEIGHT));
+        assertTrue(region.contains(X + 4 - (4 * Math.cos(45)), Y + HEIGHT - 4 + (4 * Math.sin(45))));
+        assertTrue(region.contains(X + 4 - (3 * Math.cos(45)), Y + HEIGHT - 4 + (3 * Math.sin(45))));
+        assertFalse(region.contains(X + 4 - (5 * Math.cos(45)), Y + HEIGHT - 4 + (5 * Math.sin(45))));
+        // Check the center
+        assertTrue(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularFillWithIndependentRadiusWorks2() {
+        region.setBackground(new Background(new BackgroundFill(Color.RED,
+            new CornerRadii(1, 2, 3, 4, 5, 6, 7, 8, false, false, false, false, false, false, false, false),
+            Insets.EMPTY)));
+        // Check points in the top-left corner area
+        assertTrue(region.contains(X, Y + 2));
+        assertTrue(region.contains(X + 1, Y));
+        assertTrue(region.contains(X + 1 - (1 * Math.cos(45)), Y + 2 - (2 * Math.sin(45))));
+        assertTrue(region.contains(X + 1 - (.5 * Math.cos(45)), Y + 2 - (1 * Math.sin(45))));
+        assertFalse(region.contains(X + 1 - (2 * Math.cos(45)), Y + 2 - (3 * Math.sin(45))));
+        // Check points in the top-right corner area
+        assertTrue(region.contains(X + WIDTH, Y + 3));
+        assertTrue(region.contains(X + WIDTH - 4, Y));
+        assertTrue(region.contains(X + WIDTH - 4 + (4 * Math.cos(45)), Y + 3 - (3 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 4 + (3 * Math.cos(45)), Y + 3 - (2 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 4 + (5 * Math.cos(45)), Y + 3 - (4 * Math.sin(45))));
+        // Check points in the bottom-right corner area
+        assertTrue(region.contains(X + WIDTH, Y + HEIGHT - 6));
+        assertTrue(region.contains(X + WIDTH - 5, Y + HEIGHT));
+        assertTrue(region.contains(X + WIDTH - 5 + (5 * Math.cos(45)), Y + HEIGHT - 6 + (6 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 5 + (4 * Math.cos(45)), Y + HEIGHT - 6 + (5 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 5 + (6 * Math.cos(45)), Y + HEIGHT - 6 + (7 * Math.sin(45))));
+        // Check points in the bottom-left corner area
+        assertTrue(region.contains(X, Y + HEIGHT - 7));
+        assertTrue(region.contains(X + 8, Y + HEIGHT));
+        assertTrue(region.contains(X + 8 - (8 * Math.cos(45)), Y + HEIGHT - 7 + (7 * Math.sin(45))));
+        assertTrue(region.contains(X + 8 - (7 * Math.cos(45)), Y + HEIGHT - 7 + (6 * Math.sin(45))));
+        assertFalse(region.contains(X + 8 - (9 * Math.cos(45)), Y + HEIGHT - 7 + (8 * Math.sin(45))));
+        // Check the center
+        assertTrue(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularFillWithIndependentRadiusWithInsetsWorks() {
+        region.setBackground(new Background(new BackgroundFill(Color.RED,
+            new CornerRadii(1, 2, 3, 4, 5, 6, 7, 8, false, false, false, false, false, false, false, false),
+            new Insets(4, 3, 2, 1))));
+        // Check points in the top-left corner area
+        assertTrue(region.contains(X + 1, Y + 2 + 4));
+        assertTrue(region.contains(X + 1 + 1, Y + 4));
+        assertTrue(region.contains(X + 1 + 1 - (1 * Math.cos(45)), Y + 2 + 4 - (2 * Math.sin(45))));
+        assertTrue(region.contains(X + 1 + 1 - (.5 * Math.cos(45)), Y + 2 + 4 - (1 * Math.sin(45))));
+        assertFalse(region.contains(X + 1 + 1 - (2 * Math.cos(45)), Y + 2 + 4 - (3 * Math.sin(45))));
+        // Check points in the top-right corner area
+        assertTrue(region.contains(X + WIDTH - 3, Y + 3 + 4));
+        assertTrue(region.contains(X + WIDTH - 4 - 3, Y + 4));
+        assertTrue(region.contains(X + WIDTH - 4 - 3 + (4 * Math.cos(45)), Y + 4 + 3 - (3 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 4 - 3 + (3 * Math.cos(45)), Y + 4 + 3 - (2 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 4 - 3 + (5 * Math.cos(45)), Y + 4 + 3 - (4 * Math.sin(45))));
+        // Check points in the bottom-right corner area
+        assertTrue(region.contains(X + WIDTH - 3, Y + HEIGHT - 2 - 6));
+        assertTrue(region.contains(X + WIDTH - 3 - 5, Y + HEIGHT - 2));
+        assertTrue(region.contains(X + WIDTH - 3 - 5 + (5 * Math.cos(45)), Y + HEIGHT - 2 - 6 + (6 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 3 - 5 + (4 * Math.cos(45)), Y + HEIGHT - 2 - 6 + (5 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 3 - 5 + (6 * Math.cos(45)), Y + HEIGHT - 2 - 6 + (7 * Math.sin(45))));
+        // Check points in the bottom-left corner area
+        assertTrue(region.contains(X + 1, Y + HEIGHT - 2 - 7));
+        assertTrue(region.contains(X + 1 + 8, Y + HEIGHT - 2));
+        assertTrue(region.contains(X + 1 + 8 - (8 * Math.cos(45)), Y + HEIGHT - 2 - 7 + (7 * Math.sin(45))));
+        assertTrue(region.contains(X + 1 + 8 - (7 * Math.cos(45)), Y + HEIGHT - 2 - 7 + (6 * Math.sin(45))));
+        assertFalse(region.contains(X + 1 + 8 - (9 * Math.cos(45)), Y + HEIGHT - 2 - 7 + (8 * Math.sin(45))));
+        // Check the center
+        assertTrue(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    // TODO test with really really large corner radius
+
+    /**************************************************************************
+     *                                                                        *
+     * Test behavior when picking a region with borders, but no shape or      *
+     * or images.                                                             *
+     *                                                                        *
+     *************************************************************************/
+
+    @Test public void pickingRectangularBorderWorks() {
+        region.setBorder(new Border(new BorderStroke(Color.GREEN, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,
+                                                     new BorderWidths(1))));
+        assertFalse(region.contains(LEFT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, ABOVE));
+        assertFalse(region.contains(RIGHT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, BELOW));
+        // Note that the center is empty and should not be picked
+        assertFalse(region.contains(CENTER_X, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularBorderWithThickBorder() {
+        region.setBorder(new Border(new BorderStroke(Color.GREEN, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,
+                                                     new BorderWidths(10))));
+        assertFalse(region.contains(LEFT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, ABOVE));
+        assertFalse(region.contains(RIGHT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, BELOW));
+        assertFalse(region.contains(CENTER_X, CENTER_Y));
+
+        assertTrue(region.contains(X, Y));
+        assertTrue(region.contains(X+5, Y+5));
+        assertFalse(region.contains(X+10, Y+10));
+    }
+
+    @Test public void pickingRectangularBorderWithIndependentBorderWidths() {
+        region.setBorder(new Border(new BorderStroke(Color.GREEN, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,
+                                                     new BorderWidths(5, 10, 15, 20))));
+        assertFalse(region.contains(LEFT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, ABOVE));
+        assertFalse(region.contains(RIGHT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, BELOW));
+        assertFalse(region.contains(CENTER_X, CENTER_Y));
+
+        // Top. Test first and last pixels, and one-past
+        assertTrue(region.contains(CENTER_X, Y));
+        assertTrue(region.contains(CENTER_X, Y + 4));
+        assertFalse(region.contains(CENTER_X, Y + 5));
+
+        // Right. Test first and last pixels, and one-past
+        assertTrue(region.contains(WIDTH, CENTER_Y));
+        assertTrue(region.contains(WIDTH - 9, CENTER_Y));
+        assertFalse(region.contains(WIDTH - 10, CENTER_Y));
+
+        // Bottom. Test first and last pixels, and one-past
+        assertTrue(region.contains(CENTER_X, HEIGHT));
+        assertTrue(region.contains(CENTER_X, HEIGHT - 14));
+        assertFalse(region.contains(CENTER_X, HEIGHT - 15));
+
+        // Left. Test first and last pixels, and one-past
+        assertTrue(region.contains(X, CENTER_Y));
+        assertTrue(region.contains(X + 19, CENTER_Y));
+        assertFalse(region.contains(X + 20, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularBorderWithIndependentPercentageBorderWidths() {
+        region.setBorder(new Border(new BorderStroke(Color.GREEN, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,
+                                                     new BorderWidths(.05, .10, .15, .20, true, true, true, true))));
+        assertFalse(region.contains(LEFT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, ABOVE));
+        assertFalse(region.contains(RIGHT_OF, CENTER_Y));
+        assertFalse(region.contains(CENTER_X, BELOW));
+        assertFalse(region.contains(CENTER_X, CENTER_Y));
+
+        // Top. Test first and last pixels, and one-past
+        assertTrue(region.contains(CENTER_X, Y));
+        assertTrue(region.contains(CENTER_X, Y + 4));
+        assertFalse(region.contains(CENTER_X, Y + 5));
+
+        // Right. Test first and last pixels, and one-past
+        assertTrue(region.contains(WIDTH, CENTER_Y));
+        assertTrue(region.contains(WIDTH - 9, CENTER_Y));
+        assertFalse(region.contains(WIDTH - 10, CENTER_Y));
+
+        // Bottom. Test first and last pixels, and one-past
+        assertTrue(region.contains(CENTER_X, HEIGHT));
+        assertTrue(region.contains(CENTER_X, HEIGHT - 14));
+        assertFalse(region.contains(CENTER_X, HEIGHT - 15));
+
+        // Left. Test first and last pixels, and one-past
+        assertTrue(region.contains(X, CENTER_Y));
+        assertTrue(region.contains(X + 19, CENTER_Y));
+        assertFalse(region.contains(X + 20, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularBorderWithIndependentBorderWidthsAndInsets() {
+        region.setBorder(new Border(new BorderStroke(Color.GREEN, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,
+                                                     new BorderWidths(5, 10, 15, 20), new Insets(1, 2, 3, 4))));
+        // Top. Test first and last pixels, and one-past
+        assertFalse(region.contains(CENTER_X, Y));
+        assertTrue(region.contains(CENTER_X, Y+1));
+        assertTrue(region.contains(CENTER_X, Y+1 + 4));
+        assertFalse(region.contains(CENTER_X, Y+1 + 5));
+
+        // Right. Test first and last pixels, and one-past
+        assertFalse(region.contains(WIDTH-1, CENTER_Y));
+        assertTrue(region.contains(WIDTH-2, CENTER_Y));
+        assertTrue(region.contains(WIDTH-2 - 9, CENTER_Y));
+        assertFalse(region.contains(WIDTH-2 - 10, CENTER_Y));
+
+        // Bottom. Test first and last pixels, and one-past
+        assertFalse(region.contains(CENTER_X, HEIGHT-2));
+        assertTrue(region.contains(CENTER_X, HEIGHT-3));
+        assertTrue(region.contains(CENTER_X, HEIGHT-3 - 14));
+        assertFalse(region.contains(CENTER_X, HEIGHT-3 - 15));
+
+        // Left. Test first and last pixels, and one-past
+        assertFalse(region.contains(X+3, CENTER_Y));
+        assertTrue(region.contains(X+4, CENTER_Y));
+        assertTrue(region.contains(X+4 + 19, CENTER_Y));
+        assertFalse(region.contains(X+4 + 20, CENTER_Y));
+    }
+
+    @Test public void pickingRectangularBorderWithIndependentRadiusWithInsetsWorks() {
+        region.setBorder(new Border(new BorderStroke(Color.RED, BorderStrokeStyle.SOLID,
+            new CornerRadii(1, 2, 3, 4, 5, 6, 7, 8, false, false, false, false, false, false, false, false),
+            new BorderWidths(5, 10, 15, 20), new Insets(4, 3, 2, 1))));
+        // Check points in the top-left corner area
+        assertTrue(region.contains(X + 1, Y + 2 + 4));
+        assertTrue(region.contains(X + 1 + 1, Y + 4));
+        assertTrue(region.contains(X + 1 + 1 - (1 * Math.cos(45)), Y + 2 + 4 - (2 * Math.sin(45))));
+        assertTrue(region.contains(X + 1 + 1 - (.5 * Math.cos(45)), Y + 2 + 4 - (1 * Math.sin(45))));
+        assertFalse(region.contains(X + 1 + 1 - (2 * Math.cos(45)), Y + 2 + 4 - (3 * Math.sin(45))));
+        // Check points in the top-right corner area
+        assertTrue(region.contains(X + WIDTH - 3, Y + 3 + 4));
+        assertTrue(region.contains(X + WIDTH - 4 - 3, Y + 4));
+        assertTrue(region.contains(X + WIDTH - 4 - 3 + (4 * Math.cos(45)), Y + 4 + 3 - (3 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 4 - 3 + (3 * Math.cos(45)), Y + 4 + 3 - (2 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 4 - 3 + (5 * Math.cos(45)), Y + 4 + 3 - (4 * Math.sin(45))));
+        // Check points in the bottom-right corner area
+        assertTrue(region.contains(X + WIDTH - 3, Y + HEIGHT - 2 - 6));
+        assertTrue(region.contains(X + WIDTH - 3 - 5, Y + HEIGHT - 2));
+        assertTrue(region.contains(X + WIDTH - 3 - 5 + (5 * Math.cos(45)), Y + HEIGHT - 2 - 6 + (6 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 3 - 5 + (4 * Math.cos(45)), Y + HEIGHT - 2 - 6 + (5 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 3 - 5 + (6 * Math.cos(45)), Y + HEIGHT - 2 - 6 + (7 * Math.sin(45))));
+        // Check points in the bottom-left corner area
+        assertTrue(region.contains(X + 1, Y + HEIGHT - 2 - 7));
+        assertTrue(region.contains(X + 1 + 8, Y + HEIGHT - 2));
+        assertTrue(region.contains(X + 1 + 8 - (8 * Math.cos(45)), Y + HEIGHT - 2 - 7 + (7 * Math.sin(45))));
+        assertTrue(region.contains(X + 1 + 8 - (7 * Math.cos(45)), Y + HEIGHT - 2 - 7 + (6 * Math.sin(45))));
+        assertFalse(region.contains(X + 1 + 8 - (9 * Math.cos(45)), Y + HEIGHT - 2 - 7 + (8 * Math.sin(45))));
+        // Check the center
+        assertFalse(region.contains(CENTER_X, CENTER_Y));
+        // TODO Could stand to have more tests testing the inside hit edge
+    }
+
+    @Test public void pickingRectangularBorderWithIndependentPercentageRadiusWithInsetsWorks() {
+        region.setBorder(new Border(new BorderStroke(Color.RED, BorderStrokeStyle.SOLID,
+            new CornerRadii(.01, .02, .03, .04, .05, .06, .07, .08, true, true, true, true, true, true, true, true),
+            new BorderWidths(5, 10, 15, 20), new Insets(4, 3, 2, 1))));
+        // Check points in the top-left corner area
+        assertTrue(region.contains(X + 1, Y + 2 + 4));
+        assertTrue(region.contains(X + 1 + 1, Y + 4));
+        assertTrue(region.contains(X + 1 + 1 - (1 * Math.cos(45)), Y + 2 + 4 - (2 * Math.sin(45))));
+        assertTrue(region.contains(X + 1 + 1 - (.5 * Math.cos(45)), Y + 2 + 4 - (1 * Math.sin(45))));
+        assertFalse(region.contains(X + 1 + 1 - (2 * Math.cos(45)), Y + 2 + 4 - (3 * Math.sin(45))));
+        // Check points in the top-right corner area
+        assertTrue(region.contains(X + WIDTH - 3, Y + 3 + 4));
+        assertTrue(region.contains(X + WIDTH - 4 - 3, Y + 4));
+        assertTrue(region.contains(X + WIDTH - 4 - 3 + (4 * Math.cos(45)), Y + 4 + 3 - (3 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 4 - 3 + (3 * Math.cos(45)), Y + 4 + 3 - (2 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 4 - 3 + (5 * Math.cos(45)), Y + 4 + 3 - (4 * Math.sin(45))));
+        // Check points in the bottom-right corner area
+        assertTrue(region.contains(X + WIDTH - 3, Y + HEIGHT - 2 - 6));
+        assertTrue(region.contains(X + WIDTH - 3 - 5, Y + HEIGHT - 2));
+        assertTrue(region.contains(X + WIDTH - 3 - 5 + (5 * Math.cos(45)), Y + HEIGHT - 2 - 6 + (6 * Math.sin(45))));
+        assertTrue(region.contains(X + WIDTH - 3 - 5 + (4 * Math.cos(45)), Y + HEIGHT - 2 - 6 + (5 * Math.sin(45))));
+        assertFalse(region.contains(X + WIDTH - 3 - 5 + (6 * Math.cos(45)), Y + HEIGHT - 2 - 6 + (7 * Math.sin(45))));
+        // Check points in the bottom-left corner area
+        assertTrue(region.contains(X + 1, Y + HEIGHT - 2 - 7));
+        assertTrue(region.contains(X + 1 + 8, Y + HEIGHT - 2));
+        assertTrue(region.contains(X + 1 + 8 - (8 * Math.cos(45)), Y + HEIGHT - 2 - 7 + (7 * Math.sin(45))));
+        assertTrue(region.contains(X + 1 + 8 - (7 * Math.cos(45)), Y + HEIGHT - 2 - 7 + (6 * Math.sin(45))));
+        assertFalse(region.contains(X + 1 + 8 - (9 * Math.cos(45)), Y + HEIGHT - 2 - 7 + (8 * Math.sin(45))));
+        // Check the center
+        assertFalse(region.contains(CENTER_X, CENTER_Y));
+        // TODO Could stand to have more tests testing the inside hit edge
+    }
+
+    /**************************************************************************
+     *                                                                        *
+     * Test behavior when picking a shaped region. We have to test all the    *
+     * positionShape / scaleShape variants to make sure we are always picking *
+     * based on the perceived (rendered) shape                                *
+     *                                                                        *
+     *************************************************************************/
+
+    // TODO implement along with fix for RT-27775
+}