]> source.dussan.org Git - vaadin-framework.git/commitdiff
Optimize resetting of state when detaching components (#10899, #11284)
authorHenri Sara <hesara@vaadin.com>
Thu, 22 Aug 2013 08:03:47 +0000 (11:03 +0300)
committerVaadin Code Review <review@vaadin.com>
Thu, 22 Aug 2013 08:57:09 +0000 (08:57 +0000)
This has a significant impact on some older browsers, especially IE8.
This change also adds hierarchy update profiling statements to
help find hotspots.

Change-Id: Id7025776f8606794ba1d7aef99e2c37832b0c6bb

client/src/com/vaadin/client/ApplicationConnection.java

index 0b64f56c3e517028f64338c665cc3bde9943bcf8..dc3378560e9804324242403ad5aede9d6c86bb1d 100644 (file)
@@ -1701,7 +1701,9 @@ public class ApplicationConnection {
                         // hierarchy, unregister it and any possible
                         // children. The UIConnector should never be
                         // unregistered even though it has no parent.
+                        Profiler.enter("unregisterRemovedConnectors unregisterConnector");
                         connectorMap.unregisterConnector(c);
+                        Profiler.leave("unregisterRemovedConnectors unregisterConnector");
                         unregistered++;
                     }
 
@@ -1984,6 +1986,8 @@ public class ApplicationConnection {
                 JsArrayString hierarchyKeys = hierarchies.getKeyArray();
                 for (int i = 0; i < hierarchyKeys.length(); i++) {
                     try {
+                        Profiler.enter("updateConnectorHierarchy hierarchy entry");
+
                         String connectorId = hierarchyKeys.get(i);
                         ServerConnector parentConnector = connectorMap
                                 .getConnector(connectorId);
@@ -1991,6 +1995,8 @@ public class ApplicationConnection {
                                 .getJSStringArray(connectorId);
                         int childConnectorSize = childConnectorIds.length();
 
+                        Profiler.enter("updateConnectorHierarchy find new connectors");
+
                         List<ServerConnector> newChildren = new ArrayList<ServerConnector>();
                         List<ComponentConnector> newComponents = new ArrayList<ComponentConnector>();
                         for (int connectorIndex = 0; connectorIndex < childConnectorSize; connectorIndex++) {
@@ -2029,6 +2035,8 @@ public class ApplicationConnection {
                             }
                         }
 
+                        Profiler.leave("updateConnectorHierarchy find new connectors");
+
                         // TODO This check should be done on the server side in
                         // the future so the hierarchy update is only sent when
                         // something actually has changed
@@ -2041,6 +2049,8 @@ public class ApplicationConnection {
                             continue;
                         }
 
+                        Profiler.enter("updateConnectorHierarchy handle HasComponentsConnector");
+
                         if (parentConnector instanceof HasComponentsConnector) {
                             HasComponentsConnector ccc = (HasComponentsConnector) parentConnector;
                             List<ComponentConnector> oldComponents = ccc
@@ -2062,7 +2072,13 @@ public class ApplicationConnection {
                                     + " has component children even though it isn't a HasComponentsConnector");
                         }
 
+                        Profiler.leave("updateConnectorHierarchy handle HasComponentsConnector");
+
+                        Profiler.enter("updateConnectorHierarchy setChildren");
                         parentConnector.setChildren(newChildren);
+                        Profiler.leave("updateConnectorHierarchy setChildren");
+
+                        Profiler.enter("updateConnectorHierarchy find removed children");
 
                         /*
                          * Find children removed from this parent and mark for
@@ -2084,11 +2100,17 @@ public class ApplicationConnection {
                                 maybeDetached.add(oldChild.getConnectorId());
                             }
                         }
+
+                        Profiler.leave("updateConnectorHierarchy find removed children");
                     } catch (final Throwable e) {
                         VConsole.error(e);
+                    } finally {
+                        Profiler.leave("updateConnectorHierarchy hierarchy entry");
                     }
                 }
 
+                Profiler.enter("updateConnectorHierarchy detach removed connectors");
+
                 /*
                  * Connector is in maybeDetached at this point if it has been
                  * removed from its parent but not added to any other parent
@@ -2100,6 +2122,8 @@ public class ApplicationConnection {
                     recursivelyDetach(removed, result.events);
                 }
 
+                Profiler.leave("updateConnectorHierarchy detach removed connectors");
+
                 Profiler.leave("updateConnectorHierarchy");
 
                 return result;
@@ -2116,27 +2140,44 @@ public class ApplicationConnection {
                  * is the closest we can get without data from the server.
                  * #10151
                  */
+                Profiler.enter("ApplicationConnection recursivelyDetach reset state");
                 try {
+                    Profiler.enter("ApplicationConnection recursivelyDetach reset state - getStateType");
                     Type stateType = AbstractConnector.getStateType(connector);
+                    Profiler.leave("ApplicationConnection recursivelyDetach reset state - getStateType");
 
                     // Empty state instance to get default property values from
+                    Profiler.enter("ApplicationConnection recursivelyDetach reset state - createInstance");
                     Object defaultState = stateType.createInstance();
+                    Profiler.leave("ApplicationConnection recursivelyDetach reset state - createInstance");
 
-                    SharedState state = connector.getState();
-
-                    JsArrayObject<Property> properties = stateType
-                            .getPropertiesAsArray();
-                    int size = properties.size();
-                    for (int i = 0; i < size; i++) {
-                        Property property = properties.get(i);
-                        property.setValue(state,
-                                property.getValue(defaultState));
+                    if (connector instanceof AbstractConnector) {
+                        // optimization as the loop setting properties is very
+                        // slow, especially on IE8
+                        replaceState((AbstractConnector) connector,
+                                defaultState);
+                    } else {
+                        SharedState state = connector.getState();
+
+                        Profiler.enter("ApplicationConnection recursivelyDetach reset state - properties");
+                        JsArrayObject<Property> properties = stateType
+                                .getPropertiesAsArray();
+                        int size = properties.size();
+                        for (int i = 0; i < size; i++) {
+                            Property property = properties.get(i);
+                            property.setValue(state,
+                                    property.getValue(defaultState));
+                        }
+                        Profiler.leave("ApplicationConnection recursivelyDetach reset state - properties");
                     }
                 } catch (NoDataException e) {
                     throw new RuntimeException("Can't reset state for "
                             + Util.getConnectorString(connector), e);
+                } finally {
+                    Profiler.leave("ApplicationConnection recursivelyDetach reset state");
                 }
 
+                Profiler.enter("ApplicationConnection recursivelyDetach perform detach");
                 /*
                  * Recursively detach children to make sure they get
                  * setParent(null) and hierarchy change events as needed.
@@ -2153,18 +2194,22 @@ public class ApplicationConnection {
                     }
                     recursivelyDetach(child, events);
                 }
+                Profiler.leave("ApplicationConnection recursivelyDetach perform detach");
 
                 /*
                  * Clear child list and parent
                  */
+                Profiler.enter("ApplicationConnection recursivelyDetach clear children and parent");
                 connector
                         .setChildren(Collections.<ServerConnector> emptyList());
                 connector.setParent(null);
+                Profiler.leave("ApplicationConnection recursivelyDetach clear children and parent");
 
                 /*
                  * Create an artificial hierarchy event for containers to give
                  * it a chance to clean up after its children if it has any
                  */
+                Profiler.enter("ApplicationConnection recursivelyDetach create hierarchy event");
                 if (connector instanceof HasComponentsConnector) {
                     HasComponentsConnector ccc = (HasComponentsConnector) connector;
                     List<ComponentConnector> oldChildren = ccc
@@ -2185,8 +2230,15 @@ public class ApplicationConnection {
                         events.add(event);
                     }
                 }
+                Profiler.leave("ApplicationConnection recursivelyDetach create hierarchy event");
             }
 
+            private native void replaceState(AbstractConnector connector,
+                    Object defaultState)
+            /*-{
+                connector.@com.vaadin.client.ui.AbstractConnector::state = defaultState;
+            }-*/;
+
             private void handleRpcInvocations(ValueMap json) {
                 if (json.containsKey("rpc")) {
                     Profiler.enter("handleRpcInvocations");