changeset 5990:6a89f7866850

RT-32349: Enhance animated properties Reviewed-by: Martin Sladecek
author kcr
date Thu, 12 Sep 2013 11:39:33 -0700
parents 4b224f556d88
children c0dd6c47fec3
files modules/graphics/src/main/java/com/sun/javafx/tk/Toolkit.java modules/graphics/src/main/java/com/sun/scenario/animation/AbstractMasterTimer.java modules/graphics/src/main/java/com/sun/scenario/animation/shared/TimerReceiver.java modules/graphics/src/main/java/javafx/animation/Animation.java modules/graphics/src/main/java/javafx/animation/AnimationTimer.java modules/graphics/src/main/java/javafx/scene/Scene.java modules/graphics/src/test/java/com/sun/scenario/animation/AbstractMasterTimerTest.java
diffstat 7 files changed, 185 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/modules/graphics/src/main/java/com/sun/javafx/tk/Toolkit.java	Tue Sep 10 20:25:41 2013 -0700
+++ b/modules/graphics/src/main/java/com/sun/javafx/tk/Toolkit.java	Thu Sep 12 11:39:33 2013 -0700
@@ -297,47 +297,68 @@
      */
     public abstract void closeAppletWindow();
 
-    private final Map<TKPulseListener,Object> stagePulseListeners =
-            new WeakHashMap<TKPulseListener,Object>();
-    private final Map<TKPulseListener,Object> scenePulseListeners =
-            new WeakHashMap<TKPulseListener,Object>();
-    private final Map<TKPulseListener,Object> postScenePulseListeners =
-            new WeakHashMap<TKPulseListener,Object>();
-    private final Map<TKListener,Object> toolkitListeners =
-            new WeakHashMap<TKListener,Object>();
+    private final Map<TKPulseListener,AccessControlContext> stagePulseListeners =
+            new WeakHashMap<TKPulseListener,AccessControlContext>();
+    private final Map<TKPulseListener,AccessControlContext> scenePulseListeners =
+            new WeakHashMap<TKPulseListener,AccessControlContext>();
+    private final Map<TKPulseListener,AccessControlContext> postScenePulseListeners =
+            new WeakHashMap<TKPulseListener,AccessControlContext>();
+    private final Map<TKListener,AccessControlContext> toolkitListeners =
+            new WeakHashMap<TKListener,AccessControlContext>();
 
     // The set of shutdown hooks is strongly held to avoid premature GC.
     private final Set<Runnable> shutdownHooks = new HashSet<Runnable>();
 
+    private void runPulse(final TKPulseListener listener,
+            final AccessControlContext acc) {
+
+        if (acc == null) {
+            throw new IllegalStateException("Invalid AccessControlContext");
+        }
+
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override public Void run() {
+                listener.pulse();
+                return null;
+            }
+        }, acc);
+    }
 
     public void firePulse() {
         // Stages need to be notified of pulses before scenes so the Stage can resized
         // and those changes propogated to scene before it gets its pulse to update
 
-        ArrayList<TKPulseListener> stagePulseList = new ArrayList<TKPulseListener>();
-        ArrayList<TKPulseListener> scenePulseList = new ArrayList<TKPulseListener>();
-        ArrayList<TKPulseListener> postScenePulseList = new ArrayList<TKPulseListener>();
+        // Copy of listener map
+        final Map<TKPulseListener,AccessControlContext> stagePulseList =
+                new WeakHashMap<TKPulseListener,AccessControlContext>();
+        final Map<TKPulseListener,AccessControlContext> scenePulseList =
+                new WeakHashMap<TKPulseListener,AccessControlContext>();
+        final Map<TKPulseListener,AccessControlContext> postScenePulseList =
+                new WeakHashMap<TKPulseListener,AccessControlContext>();
+
         synchronized (this) {
-            stagePulseList.addAll(stagePulseListeners.keySet());
-            scenePulseList.addAll(scenePulseListeners.keySet());
-            postScenePulseList.addAll(postScenePulseListeners.keySet());
+            stagePulseList.putAll(stagePulseListeners);
+            scenePulseList.putAll(scenePulseListeners);
+            postScenePulseList.putAll(postScenePulseListeners);
         }
-        for (TKPulseListener listener: stagePulseList) {
-            listener.pulse();
+        for (Map.Entry<TKPulseListener,AccessControlContext> entry : stagePulseList.entrySet()) {
+            runPulse(entry.getKey(), entry.getValue());
         }
-        for (TKPulseListener listener: scenePulseList) {
-            listener.pulse();
+        for (Map.Entry<TKPulseListener,AccessControlContext> entry : scenePulseList.entrySet()) {
+            runPulse(entry.getKey(), entry.getValue());
         }
-        for (TKPulseListener listener: postScenePulseList) {
-            listener.pulse();
+        for (Map.Entry<TKPulseListener,AccessControlContext> entry : postScenePulseList.entrySet()) {
+            runPulse(entry.getKey(), entry.getValue());
         }
+
         if (lastTkPulseListener != null) {
-            lastTkPulseListener.pulse();
+            runPulse(lastTkPulseListener, lastTkPulseAcc);
         }
     }
     public void addStageTkPulseListener(TKPulseListener listener) {
         synchronized (this) {
-            stagePulseListeners.put(listener, null);
+            AccessControlContext acc = AccessController.getContext();
+            stagePulseListeners.put(listener, acc);
         }
     }
     public void removeStageTkPulseListener(TKPulseListener listener) {
@@ -347,7 +368,8 @@
     }
     public void addSceneTkPulseListener(TKPulseListener listener) {
         synchronized (this) {
-            scenePulseListeners.put(listener, null);
+            AccessControlContext acc = AccessController.getContext();
+            scenePulseListeners.put(listener, acc);
         }
     }
     public void removeSceneTkPulseListener(TKPulseListener listener) {
@@ -357,7 +379,8 @@
     }
     public void addPostSceneTkPulseListener(TKPulseListener listener) {
         synchronized (this) {
-            postScenePulseListeners.put(listener, null);
+            AccessControlContext acc = AccessController.getContext();
+            postScenePulseListeners.put(listener, acc);
         }
     }
     public void removePostSceneTkPulseListener(TKPulseListener listener) {
@@ -367,7 +390,8 @@
     }
 
     public void addTkListener(TKListener listener) {
-        toolkitListeners.put(listener, null);
+        AccessControlContext acc = AccessController.getContext();
+        toolkitListeners.put(listener, acc);
     }
 
     public void removeTkListener(TKListener listener) {
@@ -375,7 +399,9 @@
     }
 
     private TKPulseListener lastTkPulseListener = null;
+    private AccessControlContext lastTkPulseAcc = null;
     public void setLastTkPulseListener(TKPulseListener listener) {
+        lastTkPulseAcc = AccessController.getContext();
         lastTkPulseListener = listener;
     }
 
@@ -403,9 +429,20 @@
         }
     }
 
-    public void notifyWindowListeners(List<TKStage> windows) {
-        for (TKListener listener: toolkitListeners.keySet()) {
-            listener.changedTopLevelWindows(windows);
+    public void notifyWindowListeners(final List<TKStage> windows) {
+        for (Map.Entry<TKListener,AccessControlContext> entry : toolkitListeners.entrySet()) {
+            final TKListener listener = entry.getKey();
+            final AccessControlContext acc = entry.getValue();
+            if (acc == null) {
+                throw new IllegalStateException("Invalid AccessControlContext");
+            }
+
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                @Override public Void run() {
+                    listener.changedTopLevelWindows(windows);
+                    return null;
+                }
+            }, acc);
         }
     }
 
--- a/modules/graphics/src/main/java/com/sun/scenario/animation/AbstractMasterTimer.java	Tue Sep 10 20:25:41 2013 -0700
+++ b/modules/graphics/src/main/java/com/sun/scenario/animation/AbstractMasterTimer.java	Thu Sep 12 11:39:33 2013 -0700
@@ -26,12 +26,12 @@
 package com.sun.scenario.animation;
 
 import java.util.Arrays;
-import javafx.animation.AnimationTimer;
 import javafx.util.Callback;
 import com.sun.javafx.animation.TickCalculation;
 import com.sun.scenario.DelayedRunnable;
 import com.sun.scenario.Settings;
 import com.sun.scenario.animation.shared.PulseReceiver;
+import com.sun.scenario.animation.shared.TimerReceiver;
 
 public abstract class AbstractMasterTimer {
 
@@ -95,7 +95,7 @@
     private boolean receiversLocked;
 
     // synchronize to update frameJobList and frameJobs
-    private AnimationTimer animationTimers[] = new AnimationTimer[2]; // frameJobList
+    private TimerReceiver animationTimers[] = new TimerReceiver[2]; // frameJobList
                                                                      // snapshot
     private int animationTimersLength;
     private boolean animationTimersLocked;
@@ -200,7 +200,7 @@
         }
     }
 
-    public void addAnimationTimer(AnimationTimer timer) {
+    public void addAnimationTimer(TimerReceiver timer) {
         boolean needMoreSize = animationTimersLength == animationTimers.length;
         if (animationTimersLocked || needMoreSize) {
             animationTimers = Arrays.copyOf(animationTimers, needMoreSize ? animationTimers.length * 3 / 2 + 1 : animationTimers.length);
@@ -212,7 +212,7 @@
         }
     }
 
-    public void removeAnimationTimer(AnimationTimer timer) {
+    public void removeAnimationTimer(TimerReceiver timer) {
         if (animationTimersLocked) {
             animationTimers = animationTimers.clone();
             animationTimersLocked = false;
@@ -348,7 +348,7 @@
         }
         recordAnimationEnd();
 
-        final AnimationTimer animationTimersSnapshot[] = animationTimers;
+        final TimerReceiver animationTimersSnapshot[] = animationTimers;
         final int aTLength = animationTimersLength;
         try {
             animationTimersLocked = true;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/graphics/src/main/java/com/sun/scenario/animation/shared/TimerReceiver.java	Thu Sep 12 11:39:33 2013 -0700
@@ -0,0 +1,41 @@
+/*
+ * 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.scenario.animation.shared;
+
+/**
+ * A TimerReceiver receives per-frame pulses from the MasterTimer.
+ */
+public interface TimerReceiver {
+    /**
+     * Callback triggered to send a message to the TimerReceiver
+     *
+     * @param now
+     *            The timestamp of the current frame given in nanoseconds. This
+     *            value will be the same for all {@code AnimationTimers} called
+     *            during one frame.
+     */
+    public void handle(long now);
+}
--- a/modules/graphics/src/main/java/javafx/animation/Animation.java	Tue Sep 10 20:25:41 2013 -0700
+++ b/modules/graphics/src/main/java/javafx/animation/Animation.java	Thu Sep 12 11:39:33 2013 -0700
@@ -52,6 +52,9 @@
 import com.sun.scenario.animation.shared.PulseReceiver;
 
 import static com.sun.javafx.animation.TickCalculation.*;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 
 /**
  * The class {@code Animation} provides the core functionality of all animations
@@ -130,14 +133,25 @@
     private boolean paused = false;
     private final AbstractMasterTimer timer;
 
+    // Access control context, captured whenever we add this pulse reciever to
+    // the master timer (which is called when an animation is played or resumed)
+    private AccessControlContext accessCtrlCtx = null;
+
     private long now() {
         return TickCalculation.fromNano(timer.nanos());
     }
 
+    private void addPulseReceiver() {
+        // Capture the Access Control Context to be used during the animation pulse
+        accessCtrlCtx = AccessController.getContext();
+
+        timer.addPulseReceiver(pulseReceiver);
+    }
+
     void startReceiver(long delay) {
         paused = false;
         startTime = now() + delay;
-        timer.addPulseReceiver(pulseReceiver);
+        addPulseReceiver();
     }
 
     void pauseReceiver() {
@@ -153,7 +167,7 @@
             final long deltaTime = now() - pauseTime;
             startTime += deltaTime;
             paused = false;
-            timer.addPulseReceiver(pulseReceiver);
+            addPulseReceiver();
         }
     }
 
@@ -164,8 +178,16 @@
             if (elapsedTime < 0) {
                 return;
             }
+            if (accessCtrlCtx == null) {
+                throw new IllegalStateException("Error: AccessControlContext not captured");
+            }
 
-            impl_timePulse(elapsedTime);
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                @Override public Void run() {
+                    impl_timePulse(elapsedTime);
+                    return null;
+                }
+            }, accessCtrlCtx);
         }
     };
 
--- a/modules/graphics/src/main/java/javafx/animation/AnimationTimer.java	Tue Sep 10 20:25:41 2013 -0700
+++ b/modules/graphics/src/main/java/javafx/animation/AnimationTimer.java	Thu Sep 12 11:39:33 2013 -0700
@@ -27,6 +27,10 @@
 
 import com.sun.javafx.tk.Toolkit;
 import com.sun.scenario.animation.AbstractMasterTimer;
+import com.sun.scenario.animation.shared.TimerReceiver;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 
 /**
  * The class {@code AnimationTimer} allows to create a timer, that is called in
@@ -43,9 +47,28 @@
  */
 public abstract class AnimationTimer {
 
+    private class AnimationTimerReceiver implements TimerReceiver {
+        @Override public void handle(final long now) {
+            if (accessCtrlCtx == null) {
+                throw new IllegalStateException("Error: AccessControlContext not captured");
+            }
+
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                @Override public Void run() {
+                    AnimationTimer.this.handle(now);
+                    return null;
+                }
+            }, accessCtrlCtx);
+        }
+    }
+
     private final AbstractMasterTimer timer;
+    private final AnimationTimerReceiver timerReceiver = new AnimationTimerReceiver();
     private boolean active;
 
+    // Access control context, captured in start()
+    private AccessControlContext accessCtrlCtx = null;
+
     /**
      * Creates a new timer.
      */
@@ -78,7 +101,9 @@
      */
     public void start() {
         if (!active) {
-            timer.addAnimationTimer(this);
+            // Capture the Access Control Context to be used during the animation pulse
+            accessCtrlCtx = AccessController.getContext();
+            timer.addAnimationTimer(timerReceiver);
             active = true;
         }
     }
@@ -89,7 +114,7 @@
      */
     public void stop() {
         if (active) {
-            timer.removeAnimationTimer(this);
+            timer.removeAnimationTimer(timerReceiver);
             active = false;
         }
     }
--- a/modules/graphics/src/main/java/javafx/scene/Scene.java	Tue Sep 10 20:25:41 2013 -0700
+++ b/modules/graphics/src/main/java/javafx/scene/Scene.java	Thu Sep 12 11:39:33 2013 -0700
@@ -1235,7 +1235,7 @@
     private static List<Runnable> snapshotRunnableListB;
     private static List<Runnable> snapshotRunnableList;
 
-    static void addSnapshotRunnable(Runnable r) {
+    static void addSnapshotRunnable(final Runnable runnable) {
         Toolkit.getToolkit().checkFxUserThread();
 
         if (snapshotPulseListener == null) {
@@ -1269,7 +1269,18 @@
             // had layout and CSS processing, and have been synced
             Toolkit.getToolkit().addPostSceneTkPulseListener(snapshotPulseListener);
         }
-        snapshotRunnableList.add(r);
+
+        final AccessControlContext acc = AccessController.getContext();
+        snapshotRunnableList.add(new Runnable() {
+            @Override public void run() {
+                AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                    @Override public Void run() {
+                        runnable.run();
+                        return null;
+                    }
+                }, acc);
+            }
+        });
         Toolkit.getToolkit().requestNextPulse();
     }
 
--- a/modules/graphics/src/test/java/com/sun/scenario/animation/AbstractMasterTimerTest.java	Tue Sep 10 20:25:41 2013 -0700
+++ b/modules/graphics/src/test/java/com/sun/scenario/animation/AbstractMasterTimerTest.java	Thu Sep 12 11:39:33 2013 -0700
@@ -28,6 +28,7 @@
 import javafx.animation.AnimationTimer;
 import com.sun.scenario.DelayedRunnable;
 import com.sun.scenario.animation.shared.PulseReceiver;
+import com.sun.scenario.animation.shared.TimerReceiver;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -123,15 +124,21 @@
                 flag.flag();
             }
         };
+
+        final TimerReceiver timerReceiver = new TimerReceiver() {
+            @Override public void handle(long l) {
+                animationTimer.handle(l);
+            }
+        };
         
         // add AnimationTimer
-        timer.addAnimationTimer(animationTimer);
+        timer.addAnimationTimer(timerReceiver);
         timer.simulatePulse();
         assertTrue(flag.isFlagged());
         
         // remove AnimationTimer
         flag.unflag();
-        timer.removeAnimationTimer(animationTimer);
+        timer.removeAnimationTimer(timerReceiver);
         timer.simulatePulse();
         assertFalse(flag.isFlagged());
     }