# HG changeset patch
# User Alexander Nyßen <alexander@nyssen.org>
# Date 1470980430 -7200
#      Fri Aug 12 07:40:30 2016 +0200
# Node ID f341411e790737e8080530f2a13a0db77e6b04f6
# Parent  b52e1e2cd728466f845d44c0edbdc6a874cf7b81
8160325: [SWT] Provide public API to access FXCanvas for embedded scene

- Provided static FXCanvas#getFXCanvas(Scene) method to obtain the FXCanvas instance embedding the given Scene instance.
- Added EmbeddedWindow.getHost() so the HostInterface can be retrieved.
- Added FXCanvasTest with a test method to test correct behavior of FXCanvas#getFXCanvas(Scene).
- Introduced SwtTest JUnit MethodRule to have more concise tests and ensure it is also used by SWTCursorsTest.

diff --git a/build.gradle b/build.gradle
--- a/build.gradle
+++ b/build.gradle
@@ -1860,9 +1860,6 @@
                 enabled = false
                 logger.info("SWT tests are disabled on MAC, because Gradle test runner does not handle -XstartOnFirstThread properly (https://issues.gradle.org/browse/GRADLE-3290).")
             }
-            if (IS_LINUX) {
-                logger.info("SWT tests may cause 'Gtk-CRITICAL **: IA__gtk_main_quit: assertion 'main_loops != NULL' failed' LINUX (https://bugs.eclipse.org/bugs/show_bug.cgi?id=435066). This happens on shutdown, the tests are not affected.")
-            }
         }
     }
 }
diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/EmbeddedWindow.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/EmbeddedWindow.java
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/EmbeddedWindow.java
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/EmbeddedWindow.java
@@ -53,6 +53,10 @@
         EmbeddedWindowHelper.initHelper(this);
     }
 
+    public HostInterface getHost(){
+        return host;
+    }
+
     /**
      * Specify the scene to be used on this stage.
      */
diff --git a/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java b/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java
--- a/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java
+++ b/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java
@@ -53,6 +53,7 @@
 import javafx.beans.NamedArg;
 import javafx.scene.Scene;
 import javafx.scene.input.TransferMode;
+import javafx.stage.Window;
 import javafx.util.FXPermission;
 
 import org.eclipse.swt.SWT;
@@ -100,7 +101,7 @@
  * {@code FXCanvas} is a component to embed JavaFX content into
  * SWT applications. The content to be displayed is specified
  * with the {@link #setScene} method that accepts an instance of
- * JavaFX {@code Scene}. After the scene is assigned, it gets
+ * JavaFX {@code Scene}. After the scene is attached, it gets
  * repainted automatically. All the input and focus events are
  * forwarded to the scene transparently to the developer.
  * <p>
@@ -258,6 +259,32 @@
         display.addFilter(SWT.Move, moveFilter);
     }
 
+    /**
+     * Retrieves the {@code FXCanvas} embedding the given {@code Scene},
+     * that is the {@code FXCanvas} to which the given {@code Scene} was
+     * attached using {@link #setScene}.
+     *
+     * @param scene the {@code Scene} whose embedding {@code FXCanvas}
+     *              instance is to be retrieved
+     * @return the {@code FXCanvas} to which the given {@code Scene} is
+     * attached, or null if the given {@code Scene} is not attached to an
+     * {@code FXCanvas}.
+     *
+     * @since 9
+     */
+    public static FXCanvas getFXCanvas(Scene scene) {
+        Window window = scene.getWindow();
+        if (window != null && window instanceof EmbeddedWindow) {
+            HostInterface hostInterface = ((EmbeddedWindow) window).getHost();
+            if (hostInterface instanceof HostContainer) {
+                // Obtain FXCanvas as the enclosing instance of the FXCanvas$HostContainer
+                // that is host of the embedded Scene's EmbeddedWindow.
+                return ((HostContainer)hostInterface).fxCanvas;
+            }
+        }
+        return null;
+    }
+
     private static void initFx() {
         // NOTE: no internal "com.sun.*" packages can be accessed until after
         // the JavaFX platform is initialized. The list of needed internal
@@ -732,6 +759,8 @@
 
     private class HostContainer implements HostInterface {
 
+        final FXCanvas fxCanvas = FXCanvas.this;
+
         @Override
         public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) {
             stagePeer = embeddedStage;
diff --git a/modules/javafx.swt/src/test/java/test/javafx/embed/swt/FXCanvasTest.java b/modules/javafx.swt/src/test/java/test/javafx/embed/swt/FXCanvasTest.java
new file mode 100644
--- /dev/null
+++ b/modules/javafx.swt/src/test/java/test/javafx/embed/swt/FXCanvasTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016, 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 test.javafx.embed.swt;
+
+import javafx.embed.swt.FXCanvas;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertSame;
+
+public class FXCanvasTest {
+
+    @Rule
+    public SwtRule ctx = new SwtRule();
+
+    @Test(timeout = 10000)
+    public void getFXCanvas() throws Throwable {
+        final Shell shell = new Shell(Display.getCurrent());
+        final FXCanvas canvas = new FXCanvas(shell, SWT.NONE);
+        shell.open();
+
+        // create and hook scene
+        Scene scene = new Scene(new Group());
+        canvas.setScene(scene);
+
+        // check FXCanvas is properly retrieved
+        assertSame(canvas, FXCanvas.getFXCanvas(canvas.getScene()));
+
+        // FIXME: We cannot close the shell here because of https://bugs.eclipse.org/bugs/show_bug.cgi?id=435066.
+        // shell.close();
+    }
+}
diff --git a/modules/javafx.swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java b/modules/javafx.swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java
--- a/modules/javafx.swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java
+++ b/modules/javafx.swt/src/test/java/test/javafx/embed/swt/SWTCursorsTest.java
@@ -33,83 +33,35 @@
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
+import org.junit.Rule;
 import org.junit.Test;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicReference;
-
 import static org.junit.Assert.assertNotNull;
 
 public class SWTCursorsTest {
 
-    @Test(timeout=10000)
+    @Rule
+    public SwtRule ctx = new SwtRule();
+
+    @Test(timeout = 10000)
     public void testImageCursor() throws Throwable {
-        // create display on first thread (required when using SWT on Mac OS X)
-        final Display display = new Display();
-        final Shell shell = new Shell(display);
+        final Shell shell = new Shell(Display.getCurrent());
         final FXCanvas canvas = new FXCanvas(shell, SWT.NONE);
         shell.open();
 
-        // keep track of exceptions thrown in UI thread
-        final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
+        // create and hook scene
+        Scene scene = new Scene(new Group());
+        canvas.setScene(scene);
 
-        final CountDownLatch latch = new CountDownLatch(2);
-        display.asyncExec(() -> {
-            try {
-                // create and hook scene
-                Scene scene = new Scene(new Group());
-                canvas.setScene(scene);
+        // set image cursor to scene
+        Image cursorImage = new Image("test/javafx/embed/swt/cursor.png");
+        scene.setCursor(new ImageCursor(cursorImage));
 
-                // set image cursor to scene
-                Image cursorImage = new Image("test/javafx/embed/swt/cursor.png");
-                scene.setCursor(new ImageCursor(cursorImage));
+        Display.getCurrent().asyncExec(() -> {
+            assertNotNull(canvas.getCursor());
 
-            } catch (Throwable throwable) {
-                throwableRef.set(throwable);
-            } finally {
-                latch.countDown();
-            }
+            // FIXME: We cannot close the shell here because of https://bugs.eclipse.org/bugs/show_bug.cgi?id=435066.
+            //shell.close();
         });
-
-        while (latch.getCount() > 1) {
-            // run SWT event loop
-            if (!display.readAndDispatch()) {
-                display.sleep();
-            }
-        }
-
-        rethrow(throwableRef);
-
-        // ensure at least one pulse has passed before asserting the cursor is set,
-        // as the scene property synchronization is triggered by the pulse listener
-        display.asyncExec(() -> {
-            try {
-                assertNotNull(canvas.getCursor());
-            } catch (Throwable throwable) {
-                throwableRef.set(throwable);
-            } finally {
-                latch.countDown();
-            }
-        });
-
-        while (latch.getCount() > 0) {
-            // run SWT event loop (again)
-            if (!display.readAndDispatch()) {
-                display.sleep();
-            }
-        }
-
-        shell.close();
-        shell.dispose();
-        display.dispose();
-
-        rethrow(throwableRef);
-    }
-
-    private void rethrow(AtomicReference<Throwable> throwableRef) throws Throwable {
-        Throwable thrown = throwableRef.get();
-        if (thrown != null) {
-            throw thrown;
-        }
     }
 }
diff --git a/modules/javafx.swt/src/test/java/test/javafx/embed/swt/SwtRule.java b/modules/javafx.swt/src/test/java/test/javafx/embed/swt/SwtRule.java
new file mode 100644
--- /dev/null
+++ b/modules/javafx.swt/src/test/java/test/javafx/embed/swt/SwtRule.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2016, 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 test.javafx.embed.swt;
+
+import org.eclipse.swt.widgets.Display;
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A {@code MethodRule} to execute test methods synchronously on the SWT UI thread. The execution will wait for any
+ * asynchronous runnables scheduled by the test method on the SWT UI thread during its execution.
+ */
+public class SwtRule implements MethodRule {
+
+    private void rethrow(final AtomicReference<Throwable> throwableRef) throws Throwable {
+        Throwable thrown = throwableRef.get();
+        if (thrown != null) {
+            throw thrown;
+        }
+    }
+
+    @Override
+    public Statement apply(final Statement base, final FrameworkMethod testMethod, final Object target) {
+        return new Statement() {
+
+            public void evaluate() throws Throwable {
+                Display display = Display.getDefault();
+
+                // keep track of exceptions thrown in UI thread
+                final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
+
+                final CountDownLatch latch = new CountDownLatch(1);
+                display.asyncExec(() -> {
+                    try {
+                        // ensure test method is synchronously executed (without spawning a new thread)
+                        testMethod.invokeExplosively(target);
+                    } catch (Throwable throwable) {
+                        throwableRef.set(throwable);
+                    } finally {
+                        display.asyncExec(() -> {
+                            // wait for any runnables scheduled (asynchronously)
+                            // by test method on the UI thread
+                            latch.countDown();
+                        });
+                    }
+                });
+
+                while (latch.getCount() > 0) {
+                    // run SWT event loop
+                    if (!display.readAndDispatch()) {
+                        display.sleep();
+                    }
+                }
+
+                rethrow(throwableRef);
+            }
+        };
+    }
+}
