changeset 7614:d1aac088a16b

RT-37551: loading user-agent stylesheets one at a time from PlatformImpl is inefficient
author David Grieve<david.grieve@oracle.com>
date Fri, 01 Aug 2014 12:44:55 -0400
parents 1f4093002756
children 859783c709ed
files modules/graphics/src/main/java/com/sun/javafx/application/PlatformImpl.java modules/graphics/src/main/java/com/sun/javafx/css/StyleManager.java modules/graphics/src/test/java/com/sun/javafx/css/StyleManagerTest.java
diffstat 3 files changed, 235 insertions(+), 101 deletions(-) [+]
line wrap: on
line diff
--- a/modules/graphics/src/main/java/com/sun/javafx/application/PlatformImpl.java	Fri Aug 01 10:43:52 2014 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/application/PlatformImpl.java	Fri Aug 01 12:44:55 2014 -0400
@@ -32,6 +32,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.security.AccessControlContext;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -549,10 +550,23 @@
 
     private static String accessibilityTheme;
     public static boolean setAccessibilityTheme(String platformTheme) {
+
         if (accessibilityTheme != null) {
             StyleManager.getInstance().removeUserAgentStylesheet(accessibilityTheme);
             accessibilityTheme = null;
         }
+        
+        _setAccessibilityTheme(platformTheme);
+
+        if (accessibilityTheme != null) {
+            StyleManager.getInstance().addUserAgentStylesheet(accessibilityTheme);
+            return true;
+        }
+        return false;
+
+    }
+
+    private static void _setAccessibilityTheme(String platformTheme) {
 
         // check to see if there is an override to enable a high-contrast theme
         final String userTheme = AccessController.doPrivileged(
@@ -597,11 +611,6 @@
                 }   
             }
         }
-        if (accessibilityTheme != null) {
-            StyleManager.getInstance().addUserAgentStylesheet(accessibilityTheme);
-            return true;
-        }
-        return false;
     }
 
     private static void _setPlatformUserAgentStylesheet(String stylesheetUrl) {
@@ -613,68 +622,80 @@
         if (overrideStylesheetUrl != null) {
             stylesheetUrl = overrideStylesheetUrl;
         }
+        
+        final List<String> uaStylesheets = new ArrayList<>();
 
         // check for named theme constants for modena and caspian
         if (Application.STYLESHEET_CASPIAN.equalsIgnoreCase(stylesheetUrl)) {
             isCaspian = true;
-            AccessController.doPrivileged(
-                    (PrivilegedAction) () -> {
-                        StyleManager.getInstance().setDefaultUserAgentStylesheet("com/sun/javafx/scene/control/skin/caspian/caspian.css");
 
-                        if (isSupported(ConditionalFeature.INPUT_TOUCH)) {
-                            StyleManager.getInstance().addUserAgentStylesheet("com/sun/javafx/scene/control/skin/caspian/embedded.css");
-                            if (com.sun.javafx.Utils.isQVGAScreen()) {
-                                StyleManager.getInstance().addUserAgentStylesheet("com/sun/javafx/scene/control/skin/caspian/embedded-qvga.css");
-                            }
-                            if (PlatformUtil.isAndroid()) {
-                                StyleManager.getInstance().addUserAgentStylesheet("com/sun/javafx/scene/control/skin/caspian/android.css");
-                            }
-                        }
+            uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/caspian.css");
 
-                        if (isSupported(ConditionalFeature.TWO_LEVEL_FOCUS)) {
-                            StyleManager.getInstance().addUserAgentStylesheet("com/sun/javafx/scene/control/skin/caspian/two-level-focus.css");
-                        }
+            if (isSupported(ConditionalFeature.INPUT_TOUCH)) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/embedded.css");
+                if (com.sun.javafx.Utils.isQVGAScreen()) {
+                    uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/embedded-qvga.css");
+                }
+                if (PlatformUtil.isAndroid()) {
+                    uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/android.css");
+                }
+            }
 
-                        if (isSupported(ConditionalFeature.VIRTUAL_KEYBOARD)) {
-                            StyleManager.getInstance().addUserAgentStylesheet("com/sun/javafx/scene/control/skin/caspian/fxvk.css");
-                        }
-                        return null;
-                    }
-            );
+            if (isSupported(ConditionalFeature.TWO_LEVEL_FOCUS)) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/two-level-focus.css");
+            }
+
+            if (isSupported(ConditionalFeature.VIRTUAL_KEYBOARD)) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/fxvk.css");
+            }
+
+            if (!isSupported(ConditionalFeature.TRANSPARENT_WINDOW)) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/caspian-no-transparency.css");
+            }            
+
         } else if (Application.STYLESHEET_MODENA.equalsIgnoreCase(stylesheetUrl)) {
             isModena = true;
-            AccessController.doPrivileged(
-                    (PrivilegedAction) () -> {
-                        StyleManager.getInstance().setDefaultUserAgentStylesheet("com/sun/javafx/scene/control/skin/modena/modena.css");
 
-                        if (isSupported(ConditionalFeature.INPUT_TOUCH)) {
-                            StyleManager.getInstance().addUserAgentStylesheet(
-                                    "com/sun/javafx/scene/control/skin/modena/touch.css");
-                        }
-                        // when running on embedded add a extra stylesheet to tune performance of modena theme
-                        if (PlatformUtil.isEmbedded()) {
-                            StyleManager.getInstance().addUserAgentStylesheet(
-                                    "com/sun/javafx/scene/control/skin/modena/modena-embedded-performance.css");
-                        }
-                        if (PlatformUtil.isAndroid()) {
-                            StyleManager.getInstance().addUserAgentStylesheet("com/sun/javafx/scene/control/skin/modena/android.css");
-                        }
+            uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/modena.css");
 
-                        if (isSupported(ConditionalFeature.TWO_LEVEL_FOCUS)) {
-                            StyleManager.getInstance().addUserAgentStylesheet("com/sun/javafx/scene/control/skin/modena/two-level-focus.css");
-                        }
+            if (isSupported(ConditionalFeature.INPUT_TOUCH)) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/touch.css");
+            }
+            // when running on embedded add a extra stylesheet to tune performance of modena theme
+            if (PlatformUtil.isEmbedded()) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/modena-embedded-performance.css");
+            }
+            if (PlatformUtil.isAndroid()) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/android.css");
+            }
 
-                        if (isSupported(ConditionalFeature.VIRTUAL_KEYBOARD)) {
-                            StyleManager.getInstance().addUserAgentStylesheet("com/sun/javafx/scene/control/skin/caspian/fxvk.css");
-                        }
-                        return null;
-                    }
-            );
+            if (isSupported(ConditionalFeature.TWO_LEVEL_FOCUS)) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/two-level-focus.css");
+            }
+
+            if (isSupported(ConditionalFeature.VIRTUAL_KEYBOARD)) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/caspian/fxvk.css");
+            }
+
+            if (!isSupported(ConditionalFeature.TRANSPARENT_WINDOW)) {
+                uaStylesheets.add("com/sun/javafx/scene/control/skin/modena/modena-no-transparency.css");
+            }
+
         } else {
-            StyleManager.getInstance().setDefaultUserAgentStylesheet(stylesheetUrl);
+            uaStylesheets.add(stylesheetUrl);
         }
+
         // Ensure that accessibility starts right
-        setAccessibilityTheme(Toolkit.getToolkit().getThemeName());
+        _setAccessibilityTheme(Toolkit.getToolkit().getThemeName());
+        if (accessibilityTheme != null) {
+            uaStylesheets.add(accessibilityTheme);
+        }
+
+        AccessController.doPrivileged((PrivilegedAction) () -> {
+            StyleManager.getInstance().setUserAgentStylesheets(uaStylesheets);
+            return null;
+        });
+
     }
 
     public static void addNoTransparencyStylesheetToScene(final Scene scene) {
--- a/modules/graphics/src/main/java/com/sun/javafx/css/StyleManager.java	Fri Aug 01 10:43:52 2014 -0400
+++ b/modules/graphics/src/main/java/com/sun/javafx/css/StyleManager.java	Fri Aug 01 12:44:55 2014 -0400
@@ -857,15 +857,15 @@
         if (fname == null || fname.isEmpty()) return new byte[0];
 
         try {
-            URL url = getURL(fname);
+            final URL url = getURL(fname);
 
             // We only care about stylesheets from file: URLs.
             if (url != null && "file".equals(url.getProtocol())) {
 
-                try (InputStream stream = url.openStream()) {
-
-                    // not looking for security, just a checksum. MD5 should be faster than SHA
-                    final DigestInputStream dis = new DigestInputStream(stream, MessageDigest.getInstance("MD5"));
+                // not looking for security, just a checksum. MD5 should be faster than SHA
+                try (final InputStream stream = url.openStream();
+                    final DigestInputStream dis = new DigestInputStream(stream, MessageDigest.getInstance("MD5")); ) {
+                    dis.getMessageDigest().reset();
                     while (dis.read() != -1) { /* empty loop body is intentional */ }
                     return dis.getMessageDigest().digest();
                 }
@@ -1121,6 +1121,66 @@
     //
     ////////////////////////////////////////////////////////////////////////////
 
+
+    /**
+     * Set a bunch of user agent stylesheets all at once. The order of the stylesheets in the list
+     * is the order of their styles in the cascade. Passing null, an empty list, or a list full of empty
+     * strings does nothing.
+     *
+     * @param urls The list of stylesheet URLs as Strings.
+     */
+    public void setUserAgentStylesheets(List<String> urls) {
+
+        if (urls == null || urls.size() == 0) return;
+
+        // Avoid resetting user agent stylesheets if they haven't changed.
+        if (urls.size() == platformUserAgentStylesheetContainers.size()) {
+            boolean isSame = true;
+            for (int n=0, nMax=urls.size(); n < nMax && isSame; n++) {
+
+                final String url = urls.get(n);
+                final String fname = (url != null) ? url.trim() : null;
+
+                if (fname == null || fname.isEmpty()) break;
+
+                StylesheetContainer container = platformUserAgentStylesheetContainers.get(n);
+                // assignment in this conditional is intentional!
+                if(isSame = fname.equals(container.fname)) {
+                    byte[] checksum = calculateCheckSum(fname);
+                    isSame = Arrays.equals(checksum, container.checksum);
+                }
+            }
+            if (isSame) return;
+        }
+
+        boolean modified = false;
+
+        for (int n=0, nMax=urls.size(); n < nMax; n++) {
+
+            final String url = urls.get(n);
+            final String fname = (url != null) ? url.trim() : null;
+
+            if (fname == null || fname.isEmpty()) continue;
+
+            if (!modified) {
+                // we have at least one non null or non-empty url
+                platformUserAgentStylesheetContainers.clear();
+                modified = true;
+            }
+
+            if (n==0) {
+                _setDefaultUserAgentStylesheet(fname);
+            } else {
+                _addUserAgentStylesheet(fname);
+            }
+        }
+
+        if (modified) {
+            userAgentStylesheetsChanged();
+        }
+
+    }
+
     /**
      * Add a user agent stylesheet, possibly overriding styles in the default
      * user agent stylesheet.
@@ -1140,37 +1200,40 @@
     // For RT-20643
     public void addUserAgentStylesheet(Scene scene, String url) {
 
-        if (url == null ) {
-            throw new IllegalArgumentException("null arg url");
+        final String fname = (url != null) ? url.trim() : null;
+        if (fname == null || fname.isEmpty()) {
+            return;
         }
 
-        final String fname = url.trim();
-        if (fname.isEmpty()) {
-            return;
+        // RT-20643
+        CssError.setCurrentScene(scene);
+
+        if (_addUserAgentStylesheet(fname)) {
+            userAgentStylesheetsChanged();
         }
 
+        // RT-20643
+        CssError.setCurrentScene(null);
+    }
+
+    // fname is assumed to be non null and non empty
+    private boolean _addUserAgentStylesheet(String fname) {
+
         // if we already have this stylesheet, bail
         for (int n=0, nMax= platformUserAgentStylesheetContainers.size(); n < nMax; n++) {
             StylesheetContainer container = platformUserAgentStylesheetContainers.get(n);
             if (fname.equals(container.fname)) {
-                return;
+                return false;
             }
         }
 
-        // RT-20643
-        CssError.setCurrentScene(scene);
+        final Stylesheet ua_stylesheet = loadStylesheet(fname);
 
-        final Stylesheet ua_stylesheet = loadStylesheet(fname);
+        if (ua_stylesheet == null) return false;
+
+        ua_stylesheet.setOrigin(StyleOrigin.USER_AGENT);
         platformUserAgentStylesheetContainers.add(new StylesheetContainer(fname, ua_stylesheet));
-
-        if (ua_stylesheet != null) {
-            ua_stylesheet.setOrigin(StyleOrigin.USER_AGENT);
-        }
-        userAgentStylesheetsChanged();
-
-        // RT-20643
-        CssError.setCurrentScene(null);
-
+        return true;
     }
 
     /**
@@ -1231,8 +1294,22 @@
 
         final String fname = (url != null) ? url.trim() : null;
         if (fname == null || fname.isEmpty()) {
-            throw new IllegalArgumentException("null arg url");
+            return;
         }
+        // RT-20643
+
+        CssError.setCurrentScene(scene);
+
+        if(_setDefaultUserAgentStylesheet(fname)) {
+            userAgentStylesheetsChanged();
+        }
+
+        // RT-20643
+        CssError.setCurrentScene(null);
+    }
+
+    // fname is expected to be non null and non empty
+    private boolean _setDefaultUserAgentStylesheet(String fname) {
 
         // if we already have this stylesheet, make sure it is the first element
         for (int n=0, nMax= platformUserAgentStylesheetContainers.size(); n < nMax; n++) {
@@ -1246,46 +1323,41 @@
                         platformUserAgentStylesheetContainers.add(0, container);
                     }
                 }
-                return;
+                // return true only if platformUserAgentStylesheetContainers was modified
+                return n > 0;
             }
         }
 
-        // RT-20643
-        CssError.setCurrentScene(scene);
+        final Stylesheet ua_stylesheet = loadStylesheet(fname);
 
-        final Stylesheet ua_stylesheet = loadStylesheet(fname);
+        if (ua_stylesheet == null) return false;
+
+        ua_stylesheet.setOrigin(StyleOrigin.USER_AGENT);
         final StylesheetContainer sc = new StylesheetContainer(fname, ua_stylesheet);
+
         if (platformUserAgentStylesheetContainers.size() == 0) {
             platformUserAgentStylesheetContainers.add(sc);
-        } else if (hasDefaultUserAgentStylesheet) {
+        }
+        else if (hasDefaultUserAgentStylesheet) {
             platformUserAgentStylesheetContainers.set(0,sc);
-        } else {
+        }
+        else {
             platformUserAgentStylesheetContainers.add(0,sc);
         }
         hasDefaultUserAgentStylesheet = true;
 
-        if (ua_stylesheet != null) {
-            ua_stylesheet.setOrigin(StyleOrigin.USER_AGENT);
-        }
-        userAgentStylesheetsChanged();
+        return true;
+    }
 
-        // RT-20643
-        CssError.setCurrentScene(null);
-
-    }
-    
     /**
      * Removes the specified stylesheet from the application default user agent
      * stylesheet list.
      * @param url  The file URL, either relative or absolute, as a String.
      */
     public void removeUserAgentStylesheet(String url) {
-        if (url == null ) {
-            throw new IllegalArgumentException("null arg url");
-        }
- 
-        final String fname = url.trim();
-        if (fname.isEmpty()) {
+
+        final String fname = (url != null) ? url.trim() : null;
+        if (fname == null || fname.isEmpty()) {
             return;
         }
  
@@ -1314,9 +1386,8 @@
      * the platform
      */
     public void setDefaultUserAgentStylesheet(Stylesheet ua_stylesheet) {
-
         if (ua_stylesheet == null ) {
-            throw new IllegalArgumentException("null arg ua_stylesheet");
+            return;
         }
 
         // null url is ok, just means that it is a stylesheet not loaded from a file
@@ -1352,8 +1423,6 @@
         ua_stylesheet.setOrigin(StyleOrigin.USER_AGENT);
         userAgentStylesheetsChanged();
 
-        // RT-20643
-        CssError.setCurrentScene(null);
     }
 
     /*
--- a/modules/graphics/src/test/java/com/sun/javafx/css/StyleManagerTest.java	Fri Aug 01 10:43:52 2014 -0400
+++ b/modules/graphics/src/test/java/com/sun/javafx/css/StyleManagerTest.java	Fri Aug 01 12:44:55 2014 -0400
@@ -39,6 +39,8 @@
 import org.junit.Test;
 
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -980,4 +982,46 @@
 
     }
 
+    @Test
+    public void test_setUserAgentStylesheets() {
+
+        List<String> uaStylesheets = new ArrayList<>();
+        Collections.addAll(uaStylesheets, "/com/sun/javafx/css/ua0.css", "/com/sun/javafx/css/ua1.css");
+
+        final StyleManager sm = StyleManager.getInstance();
+        sm.setUserAgentStylesheets(uaStylesheets);
+
+        assertEquals(2, sm.platformUserAgentStylesheetContainers.size());
+        assertEquals("/com/sun/javafx/css/ua0.css", sm.platformUserAgentStylesheetContainers.get(0).fname);
+        assertEquals("/com/sun/javafx/css/ua1.css", sm.platformUserAgentStylesheetContainers.get(1).fname);
+    }
+
+    @Test
+    public void test_setUserAgentStylesheets_overwrites_existing() {
+
+        List<String> uaStylesheets = new ArrayList<>();
+        Collections.addAll(uaStylesheets, "/com/sun/javafx/css/ua0.css");
+
+        final StyleManager sm = StyleManager.getInstance();
+
+        // 1 - overwrite default user agent stylesheet
+        sm.platformUserAgentStylesheetContainers.clear();;
+        sm.setDefaultUserAgentStylesheet("/com/sun/javafx/css/ua1.css");
+        assertEquals(1, sm.platformUserAgentStylesheetContainers.size());
+        assertEquals("/com/sun/javafx/css/ua1.css", sm.platformUserAgentStylesheetContainers.get(0).fname);
+
+        sm.setUserAgentStylesheets(uaStylesheets);
+        assertEquals(1, sm.platformUserAgentStylesheetContainers.size());
+        assertEquals("/com/sun/javafx/css/ua0.css", sm.platformUserAgentStylesheetContainers.get(0).fname);
+
+        // 2 - overwrite other user-agent stylesheets
+        sm.platformUserAgentStylesheetContainers.clear();;
+        sm.addUserAgentStylesheet("/com/sun/javafx/css/ua1.css");
+        assertEquals(1, sm.platformUserAgentStylesheetContainers.size());
+
+        sm.setUserAgentStylesheets(uaStylesheets);
+        assertEquals(1, sm.platformUserAgentStylesheetContainers.size());
+        assertEquals("/com/sun/javafx/css/ua0.css", sm.platformUserAgentStylesheetContainers.get(0).fname);
+    }
+
 }